diff options
Diffstat (limited to 'chrome')
1696 files changed, 70234 insertions, 23275 deletions
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS index 3aad30f..3135229 100644 --- a/chrome/browser/DEPS +++ b/chrome/browser/DEPS @@ -9,7 +9,10 @@ include_rules = [ "+chrome/views", "+cros", "+grit", # For generated headers - "+sandbox/src", + "+rlz", + "+sandbox/linux", + "+sandbox/src", # The path doesn't say it, but this is the Windows sandbox. + "+skia/ext", "+skia/include", "+webkit/database", "+webkit/glue", # Defines some types that are marshalled over IPC. @@ -18,15 +21,18 @@ include_rules = [ # Other libraries. "+chrome/third_party/hunspell", "+chrome/third_party/mozilla_security_manager", - "+libxml", # For search engine definition parsing. - "+media/audio", # Chrome's lightweight audio library. + "+libxml", # For search engine definition parsing. + "+media/audio", # Chrome's lightweight audio library. "+media/base", + "+third_party/apple", # Apple code ImageAndTextCell. "+third_party/expat", "+third_party/gpsd", "+third_party/sqlite", - "+third_party/libevent", # For the remote V8 debugging server + "+third_party/libevent", # For the remote V8 debugging server + "+third_party/libjingle", "+third_party/cld", - "+v8/include", # Browser uses V8 to get the version and run the debugger. + "+third_party/undoview", + "+v8/include", # Browser uses V8 to get the version and run the debugger. # FIXME: this should probably not be here, we need to find a better # structure for these includes. diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index fcbabe7..f1cebb1 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -22,31 +22,7 @@ namespace about_flags { namespace { -enum { kOsMac = 1 << 0, kOsWin = 1 << 1, kOsLinux = 1 << 2 , kOsCrOS = 1 << 3 }; - -unsigned kOsAll = kOsMac | kOsWin | kOsLinux | kOsCrOS; - -struct Experiment { - // The internal name of the experiment. This is never shown to the user. - // It _is_ however stored in the prefs file, so you shouldn't change the - // name of existing flags. - const char* internal_name; - - // String id of the message containing the experiment's name. - int visible_name_id; - - // String id of the message containing the experiment's description. - int visible_description_id; - - // The platforms the experiment is available on - // Needs to be more than a compile-time #ifdef because of profile sync. - unsigned supported_platforms; // bitmask - - // The commandline parameter that's added when this lab is active. This is - // different from |internal_name| so that the commandline flag can be - // renamed without breaking the prefs file. - const char* command_line; -}; +const unsigned kOsAll = kOsMac | kOsWin | kOsLinux | kOsCrOS; const Experiment kExperiments[] = { { @@ -115,6 +91,13 @@ const Experiment kExperiments[] = { switches::kEnableBackgroundMode }, { + "conflicting-modules-check", // Do not change; see above. + IDS_FLAGS_CONFLICTS_CHECK_NAME, + IDS_FLAGS_CONFLICTS_CHECK_DESCRIPTION, + kOsWin, + switches::kConflictingModulesCheck + }, + { "cloud-print-proxy", // Do not change; see above. IDS_FLAGS_CLOUD_PRINT_PROXY_NAME, IDS_FLAGS_CLOUD_PRINT_PROXY_DESCRIPTION, @@ -123,10 +106,17 @@ const Experiment kExperiments[] = { }, { "match-preview", // Do not change; see above. - IDS_FLAGS_INSTANT_NAME, - IDS_FLAGS_INSTANT_DESCRIPTION, + IDS_FLAGS_PREDICTIVE_INSTANT_NAME, + IDS_FLAGS_PREDICTIVE_INSTANT_DESCRIPTION, kOsMac, - switches::kEnableMatchPreview + switches::kEnablePredictiveInstant + }, + { + "verbatim-instant", // Do not change; see above. + IDS_FLAGS_VERBATIM_INSTANT_NAME, + IDS_FLAGS_VERBATIM_INSTANT_DESCRIPTION, + kOsMac | kOsLinux | kOsWin, + switches::kEnableVerbatimInstant }, // FIXME(scheib): Add Flags entry for accelerated Compositing, // or pull it and the strings in generated_resources.grd by Dec 2010 @@ -161,6 +151,13 @@ const Experiment kExperiments[] = { switches::kEnablePrintPreview }, { + "enable-nacl", // Do not change; see above. + IDS_FLAGS_ENABLE_NACL_NAME, + IDS_FLAGS_ENABLE_NACL_DESCRIPTION, + kOsAll, + switches::kEnableNaCl + }, + { "dns-server", IDS_FLAGS_DNS_SERVER_NAME, IDS_FLAGS_DNS_SERVER_DESCRIPTION, @@ -168,14 +165,24 @@ const Experiment kExperiments[] = { switches::kDnsServer }, { - "secure-infobars", // Do not change; see above - IDS_FLAGS_SECURE_INFOBARS_NAME, - IDS_FLAGS_SECURE_INFOBARS_DESCRIPTION, + "page-prerender", + IDS_FLAGS_PAGE_PRERENDER_NAME, + IDS_FLAGS_PAGE_PRERENDER_DESCRIPTION, kOsAll, - switches::kEnableSecureInfoBars - } + switches::kEnablePagePrerender + }, + { + "confirm-to-quit", // Do not change; see above. + IDS_FLAGS_CONFIRM_TO_QUIT_NAME, + IDS_FLAGS_CONFIRM_TO_QUIT_DESCRIPTION, + kOsMac, + switches::kEnableConfirmToQuit + }, }; +const Experiment* experiments = kExperiments; +size_t num_experiments = arraysize(kExperiments); + // Stores and encapsulates the little state that about:flags has. class FlagsState { public: @@ -183,7 +190,7 @@ class FlagsState { void ConvertFlagsToSwitches(PrefService* prefs, CommandLine* command_line); bool IsRestartNeededToCommitChanges(); void SetExperimentEnabled( - PrefService* prefs, const std::string& internal_name, bool enable); + PrefService* prefs, const std::string& internal_name, bool enable); void RemoveFlagsSwitches( std::map<std::string, CommandLine::StringType>* switch_list); void reset(); @@ -241,8 +248,8 @@ void SetEnabledFlags( // and removed. void SanitizeList(PrefService* prefs) { std::set<std::string> known_experiments; - for (size_t i = 0; i < arraysize(kExperiments); ++i) - known_experiments.insert(kExperiments[i].internal_name); + for (size_t i = 0; i < num_experiments; ++i) + known_experiments.insert(experiments[i].internal_name); std::set<std::string> enabled_experiments; GetEnabledFlags(prefs, &enabled_experiments); @@ -262,18 +269,29 @@ void GetSanitizedEnabledFlags( GetEnabledFlags(prefs, result); } -int GetCurrentPlatform() { -#if defined(OS_MACOSX) - return kOsMac; -#elif defined(OS_WIN) - return kOsWin; -#elif defined(OS_CHROMEOS) // Needs to be before the OS_LINUX check. - return kOsCrOS; -#elif defined(OS_LINUX) - return kOsLinux; -#else -#error Unknown platform -#endif +// Variant of GetSanitizedEnabledFlags that also removes any flags that aren't +// enabled on the current platform. +void GetSanitizedEnabledFlagsForCurrentPlatform( + PrefService* prefs, std::set<std::string>* result) { + GetSanitizedEnabledFlags(prefs, result); + + // Filter out any experiments that aren't enabled on the current platform. We + // don't remove these from prefs else syncing to a platform with a different + // set of experiments would be lossy. + std::set<std::string> platform_experiments; + int current_platform = GetCurrentPlatform(); + for (size_t i = 0; i < num_experiments; ++i) { + if (experiments[i].supported_platforms & current_platform) + platform_experiments.insert(experiments[i].internal_name); + } + + std::set<std::string> new_enabled_experiments; + std::set_intersection( + platform_experiments.begin(), platform_experiments.end(), + result->begin(), result->end(), + std::inserter(new_enabled_experiments, new_enabled_experiments.begin())); + + result->swap(new_enabled_experiments); } } // namespace @@ -289,8 +307,8 @@ ListValue* GetFlagsExperimentsData(PrefService* prefs) { int current_platform = GetCurrentPlatform(); ListValue* experiments_data = new ListValue(); - for (size_t i = 0; i < arraysize(kExperiments); ++i) { - const Experiment& experiment = kExperiments[i]; + for (size_t i = 0; i < num_experiments; ++i) { + const Experiment& experiment = experiments[i]; if (!(experiment.supported_platforms & current_platform)) continue; @@ -323,6 +341,20 @@ void RemoveFlagsSwitches( FlagsState::instance()->RemoveFlagsSwitches(switch_list); } +int GetCurrentPlatform() { +#if defined(OS_MACOSX) + return kOsMac; +#elif defined(OS_WIN) + return kOsWin; +#elif defined(OS_CHROMEOS) // Needs to be before the OS_LINUX check. + return kOsCrOS; +#elif defined(OS_LINUX) + return kOsLinux; +#else +#error Unknown platform +#endif +} + ////////////////////////////////////////////////////////////////////////////// // FlagsState implementation. @@ -334,11 +366,11 @@ void FlagsState::ConvertFlagsToSwitches( return; std::set<std::string> enabled_experiments; - GetSanitizedEnabledFlags(prefs, &enabled_experiments); + GetSanitizedEnabledFlagsForCurrentPlatform(prefs, &enabled_experiments); - std::map<std::string, const Experiment*> experiments; - for (size_t i = 0; i < arraysize(kExperiments); ++i) - experiments[kExperiments[i].internal_name] = &kExperiments[i]; + std::map<std::string, const Experiment*> experiment_map; + for (size_t i = 0; i < num_experiments; ++i) + experiment_map[experiments[i].internal_name] = &experiments[i]; command_line->AppendSwitch(switches::kFlagSwitchesBegin); flags_switches_.insert(switches::kFlagSwitchesBegin); @@ -347,9 +379,9 @@ void FlagsState::ConvertFlagsToSwitches( ++it) { const std::string& experiment_name = *it; std::map<std::string, const Experiment*>::iterator experiment = - experiments.find(experiment_name); - DCHECK(experiment != experiments.end()); - if (experiment == experiments.end()) + experiment_map.find(experiment_name); + DCHECK(experiment != experiment_map.end()); + if (experiment == experiment_map.end()) continue; command_line->AppendSwitch(experiment->second->command_line); @@ -398,6 +430,17 @@ namespace testing { void ClearState() { FlagsState::instance()->reset(); } + +void SetExperiments(const Experiment* e, size_t count) { + if (!e) { + experiments = kExperiments; + num_experiments = arraysize(kExperiments); + } else { + experiments = e; + num_experiments = count; + } +} + } // namespace testing } // namespace about_flags diff --git a/chrome/browser/about_flags.h b/chrome/browser/about_flags.h index 81b0c2a..0fcb3b9 100644 --- a/chrome/browser/about_flags.h +++ b/chrome/browser/about_flags.h @@ -16,6 +16,35 @@ class PrefService; namespace about_flags { +// Enumeration of OSs. +// This is exposed only for testing. +enum { kOsMac = 1 << 0, kOsWin = 1 << 1, kOsLinux = 1 << 2 , kOsCrOS = 1 << 3 }; + +// Experiment is used internally by about_flags to describe an experiment (and +// for testing). +// This is exposed only for testing. +struct Experiment { + // The internal name of the experiment. This is never shown to the user. + // It _is_ however stored in the prefs file, so you shouldn't change the + // name of existing flags. + const char* internal_name; + + // String id of the message containing the experiment's name. + int visible_name_id; + + // String id of the message containing the experiment's description. + int visible_description_id; + + // The platforms the experiment is available on + // Needs to be more than a compile-time #ifdef because of profile sync. + unsigned supported_platforms; // bitmask + + // The commandline parameter that's added when this lab is active. This is + // different from |internal_name| so that the commandline flag can be + // renamed without breaking the prefs file. + const char* command_line; +}; + // Reads the Labs |prefs| (called "Labs" for historical reasons) and adds the // commandline flags belonging to the active experiments to |command_line|. void ConvertFlagsToSwitches(PrefService* prefs, CommandLine* command_line); @@ -35,9 +64,18 @@ void SetExperimentEnabled( void RemoveFlagsSwitches( std::map<std::string, CommandLine::StringType>* switch_list); +// Returns the value for the current platform. This is one of the values defined +// by the OS enum above. +// This is exposed only for testing. +int GetCurrentPlatform(); + namespace testing { // Clears internal global state, for unit tests. void ClearState(); + +// Sets the list of experiments. Pass in NULL to use the default set. This does +// NOT take ownership of the supplied Experiments. +void SetExperiments(const Experiment* e, size_t count); } // namespace testing } // namespace about_flags diff --git a/chrome/browser/about_flags_unittest.cc b/chrome/browser/about_flags_unittest.cc index 3122cc9..6d86cc9 100644 --- a/chrome/browser/about_flags_unittest.cc +++ b/chrome/browser/about_flags_unittest.cc @@ -4,20 +4,50 @@ #include "chrome/browser/about_flags.h" +#include "base/values.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/test/testing_pref_service.h" +#include "grit/chromium_strings.h" #include "testing/gtest/include/gtest/gtest.h" -// These two should be two arbitrary, but valid names from about_flags.cc. -const char kFlags1[] = "remoting"; -const char kFlags2[] = "print-preview"; +const char kFlags1[] = "flag1"; +const char kFlags2[] = "flag2"; +const char kFlags3[] = "flag3"; -// The command line flag corresponding to |kFlags1|. -const char* const kFlag1CommandLine = switches::kEnableRemoting; +const char kSwitch1[] = "switch"; +const char kSwitch2[] = "switch2"; +const char kSwitch3[] = "switch3"; namespace about_flags { +// The experiments that are set for these tests. The first two experiments are +// supported on the current platform, but the last is only supported on a +// platform other than the current. +static Experiment kExperiments[] = { + { + kFlags1, + IDS_PRODUCT_NAME, + IDS_PRODUCT_NAME, + 0, // Ends up being mapped to the current platform. + kSwitch1 + }, + { + kFlags2, + IDS_PRODUCT_NAME, + IDS_PRODUCT_NAME, + 0, // Ends up being mapped to the current platform. + kSwitch2 + }, + { + kFlags3, + IDS_PRODUCT_NAME, + IDS_PRODUCT_NAME, + 0, // This ends up enabling for an OS other than the current. + kSwitch3 + }, +}; + class AboutFlagsTest : public ::testing::Test { protected: AboutFlagsTest() { @@ -25,6 +55,22 @@ class AboutFlagsTest : public ::testing::Test { testing::ClearState(); } + virtual void SetUp() { + for (size_t i = 0; i < arraysize(kExperiments); ++i) + kExperiments[i].supported_platforms = GetCurrentPlatform(); + + int os_other_than_current = 1; + while (os_other_than_current == GetCurrentPlatform()) + os_other_than_current <<= 1; + kExperiments[2].supported_platforms = os_other_than_current; + + testing::SetExperiments(kExperiments, arraysize(kExperiments)); + } + + virtual void TearDown() { + testing::SetExperiments(NULL, 0); + } + TestingPrefService prefs_; }; @@ -85,17 +131,17 @@ TEST_F(AboutFlagsTest, ConvertFlagsToSwitches) { command_line.AppendSwitch("foo"); EXPECT_TRUE(command_line.HasSwitch("foo")); - EXPECT_FALSE(command_line.HasSwitch(kFlag1CommandLine)); + EXPECT_FALSE(command_line.HasSwitch(kSwitch1)); ConvertFlagsToSwitches(&prefs_, &command_line); EXPECT_TRUE(command_line.HasSwitch("foo")); - EXPECT_TRUE(command_line.HasSwitch(kFlag1CommandLine)); + EXPECT_TRUE(command_line.HasSwitch(kSwitch1)); } TEST_F(AboutFlagsTest, RemoveFlagSwitches) { std::map<std::string, CommandLine::StringType> switch_list; - switch_list[kFlag1CommandLine] = CommandLine::StringType(); + switch_list[kSwitch1] = CommandLine::StringType(); switch_list[switches::kFlagSwitchesBegin] = CommandLine::StringType(); switch_list[switches::kFlagSwitchesEnd] = CommandLine::StringType(); switch_list["foo"] = CommandLine::StringType(); @@ -105,7 +151,7 @@ TEST_F(AboutFlagsTest, RemoveFlagSwitches) { // This shouldn't do anything before ConvertFlagsToSwitches() wasn't called. RemoveFlagsSwitches(&switch_list); ASSERT_EQ(4u, switch_list.size()); - EXPECT_TRUE(switch_list.find(kFlag1CommandLine) != switch_list.end()); + EXPECT_TRUE(switch_list.find(kSwitch1) != switch_list.end()); EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesBegin) != switch_list.end()); EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesEnd) != @@ -123,4 +169,25 @@ TEST_F(AboutFlagsTest, RemoveFlagSwitches) { EXPECT_TRUE(switch_list.find("foo") != switch_list.end()); } +// Tests enabling experiments that aren't supported on the current platform. +TEST_F(AboutFlagsTest, PersistAndPrune) { + // Enable exerpiement 1 and experiment 3. + SetExperimentEnabled(&prefs_, kFlags1, true); + SetExperimentEnabled(&prefs_, kFlags3, true); + CommandLine command_line(CommandLine::NO_PROGRAM); + EXPECT_FALSE(command_line.HasSwitch(kSwitch1)); + EXPECT_FALSE(command_line.HasSwitch(kSwitch3)); + + // Convert the flags to switches. Experiment 3 shouldn't be among the switches + // as it is not applicable to the current platform. + ConvertFlagsToSwitches(&prefs_, &command_line); + EXPECT_TRUE(command_line.HasSwitch(kSwitch1)); + EXPECT_FALSE(command_line.HasSwitch(kSwitch3)); + + // Experiment 3 should show still be persisted in preferences though. + scoped_ptr<ListValue> switch_prefs(GetFlagsExperimentsData(&prefs_)); + ASSERT_TRUE(switch_prefs.get()); + EXPECT_EQ(2u, switch_prefs->GetSize()); +} + } // namespace about_flags diff --git a/chrome/browser/accessibility/accessibility_win_browsertest.cc b/chrome/browser/accessibility/accessibility_win_browsertest.cc index e49041c..9d97988 100644 --- a/chrome/browser/accessibility/accessibility_win_browsertest.cc +++ b/chrome/browser/accessibility/accessibility_win_browsertest.cc @@ -563,13 +563,11 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check the browser's copy of the renderer accessibility tree. - AccessibleChecker static_text_checker(L"", ROLE_SYSTEM_TEXT, L"old value"); AccessibleChecker text_field_div_checker(L"", L"div", L""); AccessibleChecker text_field_checker(L"", ROLE_SYSTEM_TEXT, L"old value"); text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); AccessibleChecker body_checker(L"", L"body", L""); AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); - text_field_div_checker.AppendExpectedChild(&static_text_checker); text_field_checker.AppendExpectedChild(&text_field_div_checker); body_checker.AppendExpectedChild(&text_field_checker); document_checker.AppendExpectedChild(&body_checker); @@ -582,7 +580,6 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, // Check that the accessibility tree of the browser has been updated. text_field_checker.SetExpectedValue(L"new value"); - static_text_checker.SetExpectedValue(L"new value"); document_checker.CheckAccessible(GetRendererAccessible()); } diff --git a/chrome/browser/accessibility/browser_accessibility.cc b/chrome/browser/accessibility/browser_accessibility.cc index 9cd21b1..42cc878 100644 --- a/chrome/browser/accessibility/browser_accessibility.cc +++ b/chrome/browser/accessibility/browser_accessibility.cc @@ -5,6 +5,7 @@ #include "chrome/browser/accessibility/browser_accessibility.h" #include "base/logging.h" +#include "base/string_number_conversions.h" #include "chrome/browser/accessibility/browser_accessibility_manager.h" BrowserAccessibility::BrowserAccessibility() @@ -20,6 +21,14 @@ BrowserAccessibility::BrowserAccessibility() BrowserAccessibility::~BrowserAccessibility() { } +void BrowserAccessibility::ReplaceChild( + BrowserAccessibility* old_acc, BrowserAccessibility* new_acc) { + DCHECK_EQ(children_[old_acc->index_in_parent_], old_acc); + + old_acc = children_[old_acc->index_in_parent_]; + children_[old_acc->index_in_parent_] = new_acc; +} + void BrowserAccessibility::Initialize( BrowserAccessibilityManager* manager, BrowserAccessibility* parent, @@ -103,10 +112,64 @@ BrowserAccessibility* BrowserAccessibility::GetNextSibling() { return NULL; } -void BrowserAccessibility::ReplaceChild( - const BrowserAccessibility* old_acc, BrowserAccessibility* new_acc) { - DCHECK_EQ(children_[old_acc->index_in_parent_], old_acc); +gfx::Rect BrowserAccessibility::GetBoundsRect() { + gfx::Rect bounds = location_; + + // Adjust the bounds by the top left corner of the containing view's bounds + // in screen coordinates. + gfx::Point top_left = manager_->GetViewBounds().origin(); + bounds.Offset(top_left); + + // Adjust top left position by the root document's scroll offset. + BrowserAccessibility* root = manager_->GetRoot(); + int scroll_x = 0; + int scroll_y = 0; + root->GetAttributeAsInt( + WebAccessibility::ATTR_DOC_SCROLLX, &scroll_x); + root->GetAttributeAsInt( + WebAccessibility::ATTR_DOC_SCROLLY, &scroll_y); + bounds.Offset(-scroll_x, -scroll_y); + + return bounds; +} - old_acc = children_[old_acc->index_in_parent_]; - children_[old_acc->index_in_parent_] = new_acc; +BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint( + const gfx::Point& point) { + // Walk the children recursively looking for the BrowserAccessibility that + // most tightly encloses the specified point. + for (int i = children_.size() - 1; i >= 0; --i) { + BrowserAccessibility* child = children_[i]; + if (child->GetBoundsRect().Contains(point)) + return child->BrowserAccessibilityForPoint(point); + } + return this; +} + +bool BrowserAccessibility::HasAttribute( + WebAccessibility::Attribute attribute) { + return (attributes_.find(attribute) != attributes_.end()); +} + +bool BrowserAccessibility::GetAttribute( + WebAccessibility::Attribute attribute, string16* value) { + std::map<int32, string16>::iterator iter = attributes_.find(attribute); + if (iter != attributes_.end()) { + *value = iter->second; + return true; + } + + return false; +} + +bool BrowserAccessibility::GetAttributeAsInt( + WebAccessibility::Attribute attribute, int* value_int) { + string16 value_str; + + if (!GetAttribute(attribute, &value_str)) + return false; + + if (!base::StringToInt(value_str, value_int)) + return false; + + return true; } diff --git a/chrome/browser/accessibility/browser_accessibility.h b/chrome/browser/accessibility/browser_accessibility.h index 6e6b8fb..62b5966 100644 --- a/chrome/browser/accessibility/browser_accessibility.h +++ b/chrome/browser/accessibility/browser_accessibility.h @@ -55,6 +55,11 @@ class BrowserAccessibility { // than windows. virtual void ReleaseReference() = 0; + // Replace a child object. Used when updating the accessibility tree. + virtual void ReplaceChild( + BrowserAccessibility* old_acc, + BrowserAccessibility* new_acc); + // Initialize this object void Initialize(BrowserAccessibilityManager* manager, BrowserAccessibility* parent, @@ -85,10 +90,11 @@ class BrowserAccessibility { // of its parent. BrowserAccessibility* GetNextSibling(); - // Replace a child object. Used when updating the accessibility tree. - void ReplaceChild( - const BrowserAccessibility* old_acc, - BrowserAccessibility* new_acc); + // Returns the bounds of this object in screen coordinates. + gfx::Rect GetBoundsRect(); + + // Returns the deepest descendant that contains the specified point. + BrowserAccessibility* BrowserAccessibilityForPoint(const gfx::Point& point); // Accessors const std::map<int32, string16>& attributes() const { return attributes_; } @@ -115,6 +121,21 @@ class BrowserAccessibility { BrowserAccessibilityWin* toBrowserAccessibilityWin(); #endif + // BrowserAccessibilityCocoa needs access to these methods. + // Return true if this attribute is in the attributes map. + bool HasAttribute(WebAccessibility::Attribute attribute); + + // Retrieve the string value of an attribute from the attribute map and + // returns true if found. + bool GetAttribute(WebAccessibility::Attribute attribute, string16* value); + + // Retrieve the value of an attribute from the attribute map and + // if found and nonempty, try to convert it to an integer. + // Returns true only if both the attribute was found and it was successfully + // converted to an integer. + bool GetAttributeAsInt( + WebAccessibility::Attribute attribute, int* value_int); + protected: BrowserAccessibility(); diff --git a/chrome/browser/accessibility/browser_accessibility_cocoa.h b/chrome/browser/accessibility/browser_accessibility_cocoa.h index b6b32e0..ee04594 100644 --- a/chrome/browser/accessibility/browser_accessibility_cocoa.h +++ b/chrome/browser/accessibility/browser_accessibility_cocoa.h @@ -16,9 +16,12 @@ // object. The renderer converts webkit's accessibility tree into a // WebAccessibility tree and passes it to the browser process over IPC. // This class converts it into a format Cocoa can query. -@interface BrowserAccessibilityCocoa : NSObject { +// Inheriting from NSView rather than NSObject as clients cannot add +// observers to pure NSObject derived classes. +@interface BrowserAccessibilityCocoa : NSView { @private BrowserAccessibility* browserAccessibility_; + scoped_nsobject<NSMutableArray> children_; id<BrowserAccessibilityDelegateCocoa> delegate_; } @@ -29,6 +32,9 @@ - (id)initWithObject:(BrowserAccessibility*)accessibility delegate:(id<BrowserAccessibilityDelegateCocoa>)delegate; +// Invalidate children for a non-ignored ancestor (including self). +- (void)childrenChanged; + // Children is an array of BrowserAccessibility objects, representing // the accessibility children of this object. @property(nonatomic, readonly) NSArray* children; diff --git a/chrome/browser/accessibility/browser_accessibility_cocoa.mm b/chrome/browser/accessibility/browser_accessibility_cocoa.mm index ebfbf2c..0a202ce 100644 --- a/chrome/browser/accessibility/browser_accessibility_cocoa.mm +++ b/chrome/browser/accessibility/browser_accessibility_cocoa.mm @@ -36,23 +36,27 @@ struct RoleEntry { static const RoleEntry roles[] = { { WebAccessibility::ROLE_NONE, NSAccessibilityUnknownRole }, { WebAccessibility::ROLE_BUTTON, NSAccessibilityButtonRole }, - { WebAccessibility::ROLE_RADIO_BUTTON, NSAccessibilityRadioButtonRole }, { WebAccessibility::ROLE_CHECKBOX, NSAccessibilityCheckBoxRole }, - { WebAccessibility::ROLE_STATIC_TEXT, NSAccessibilityStaticTextRole}, - { WebAccessibility::ROLE_IMAGE, NSAccessibilityImageRole}, - { WebAccessibility::ROLE_TEXT_FIELD, NSAccessibilityTextFieldRole}, - { WebAccessibility::ROLE_TEXTAREA, NSAccessibilityTextAreaRole}, - { WebAccessibility::ROLE_LINK, NSAccessibilityLinkRole}, - { WebAccessibility::ROLE_SCROLLAREA, NSAccessibilityScrollAreaRole}, - { WebAccessibility::ROLE_SCROLLBAR, NSAccessibilityScrollBarRole}, - { WebAccessibility::ROLE_RADIO_GROUP, NSAccessibilityRadioGroupRole}, - { WebAccessibility::ROLE_TABLE, NSAccessibilityTableRole}, - { WebAccessibility::ROLE_TAB_GROUP, NSAccessibilityTabGroupRole}, - { WebAccessibility::ROLE_IGNORED, NSAccessibilityUnknownRole}, - { WebAccessibility::ROLE_WEB_AREA, @"AXWebArea"}, - { WebAccessibility::ROLE_GROUP, NSAccessibilityGroupRole}, - { WebAccessibility::ROLE_GRID, NSAccessibilityGridRole}, - { WebAccessibility::ROLE_WEBCORE_LINK, NSAccessibilityLinkRole}, + { WebAccessibility::ROLE_COLUMN, NSAccessibilityColumnRole }, + { WebAccessibility::ROLE_GRID, NSAccessibilityGridRole }, + { WebAccessibility::ROLE_GROUP, NSAccessibilityGroupRole }, + { WebAccessibility::ROLE_HEADING, @"AXHeading" }, + { WebAccessibility::ROLE_IGNORED, NSAccessibilityUnknownRole }, + { WebAccessibility::ROLE_IMAGE, NSAccessibilityImageRole }, + { WebAccessibility::ROLE_LINK, NSAccessibilityLinkRole }, + { WebAccessibility::ROLE_LIST, NSAccessibilityListRole }, + { WebAccessibility::ROLE_RADIO_BUTTON, NSAccessibilityRadioButtonRole }, + { WebAccessibility::ROLE_RADIO_GROUP, NSAccessibilityRadioGroupRole }, + { WebAccessibility::ROLE_ROW, NSAccessibilityRowRole }, + { WebAccessibility::ROLE_SCROLLAREA, NSAccessibilityScrollAreaRole }, + { WebAccessibility::ROLE_SCROLLBAR, NSAccessibilityScrollBarRole }, + { WebAccessibility::ROLE_STATIC_TEXT, NSAccessibilityStaticTextRole }, + { WebAccessibility::ROLE_TABLE, NSAccessibilityTableRole }, + { WebAccessibility::ROLE_TAB_GROUP, NSAccessibilityTabGroupRole }, + { WebAccessibility::ROLE_TEXT_FIELD, NSAccessibilityTextFieldRole }, + { WebAccessibility::ROLE_TEXTAREA, NSAccessibilityTextAreaRole }, + { WebAccessibility::ROLE_WEB_AREA, @"AXWebArea" }, + { WebAccessibility::ROLE_WEBCORE_LINK, NSAccessibilityLinkRole }, }; // GetState checks the bitmask used in webaccessibility.h to check @@ -87,21 +91,36 @@ bool GetState(BrowserAccessibility* accessibility, int state) { // Returns an array of BrowserAccessibilityCocoa objects, representing the // accessibility children of this object. - (NSArray*)children { - NSMutableArray* ret = [[[NSMutableArray alloc] - initWithCapacity:browserAccessibility_->GetChildCount()] autorelease]; - for (uint32 index = 0; - index < browserAccessibility_->GetChildCount(); - ++index) { - [ret addObject: - browserAccessibility_->GetChild(index)->toBrowserAccessibilityCocoa()]; + if (!children_.get()) { + children_.reset([[NSMutableArray alloc] + initWithCapacity:browserAccessibility_->GetChildCount()] ); + for (uint32 index = 0; + index < browserAccessibility_->GetChildCount(); + ++index) { + BrowserAccessibilityCocoa* child = + browserAccessibility_->GetChild(index)->toBrowserAccessibilityCocoa(); + if ([child isIgnored]) + [children_ addObjectsFromArray:[child children]]; + else + [children_ addObject:child]; } - return ret; + } + return children_; +} + +- (void)childrenChanged { + if (![self isIgnored]) { + children_.reset(); + } else { + [browserAccessibility_->GetParent()->toBrowserAccessibilityCocoa() + childrenChanged]; + } } // Returns whether or not this node should be ignored in the // accessibility tree. - (BOOL)isIgnored { - return browserAccessibility_->role() == WebAccessibility::ROLE_IGNORED; + return [self role] == NSAccessibilityUnknownRole; } // The origin of this accessibility object in the page's document. @@ -191,7 +210,21 @@ bool GetState(BrowserAccessibility* accessibility, int state) { WebAccessibility::ATTR_HELP); } if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { - return base::SysUTF16ToNSString(browserAccessibility_->value()); + if ([self role] == @"AXHeading") { + NSString* headingLevel = + NSStringForWebAccessibilityAttribute( + browserAccessibility_->attributes(), + WebAccessibility::ATTR_HTML_TAG); + if ([headingLevel length] >= 2) { + return [NSNumber numberWithInt: + [[headingLevel substringFromIndex:1] intValue]]; + } + } else if ([self role] == NSAccessibilityCheckBoxRole) { + return [NSNumber numberWithInt:GetState( + browserAccessibility_, WebAccessibility::STATE_CHECKED) ? 1 : 0]; + } else { + return base::SysUTF16ToNSString(browserAccessibility_->value()); + } } if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) { return [self roleDescription]; @@ -207,13 +240,53 @@ bool GetState(BrowserAccessibility* accessibility, int state) { [attribute isEqualToString:@"AXLoaded"]) { return [NSNumber numberWithBool:YES]; } + + // Text related attributes. + if ([attribute isEqualToString: + NSAccessibilityNumberOfCharactersAttribute]) { + return [NSNumber numberWithInt:browserAccessibility_->value().length()]; + } + if ([attribute isEqualToString: + NSAccessibilityVisibleCharacterRangeAttribute]) { + return [NSValue valueWithRange: + NSMakeRange(0, browserAccessibility_->value().length())]; + } + + int selStart, selEnd; + if (browserAccessibility_-> + GetAttributeAsInt(WebAccessibility::ATTR_TEXT_SEL_START, &selStart) && + browserAccessibility_-> + GetAttributeAsInt(WebAccessibility::ATTR_TEXT_SEL_END, &selEnd)) { + if (selStart > selEnd) + std::swap(selStart, selEnd); + int selLength = selEnd - selStart; + if ([attribute isEqualToString: + NSAccessibilityInsertionPointLineNumberAttribute]) { + return [NSNumber numberWithInt:0]; + } + if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) { + return base::SysUTF16ToNSString(browserAccessibility_->value().substr( + selStart, selLength)); + } + if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { + return [NSValue valueWithRange:NSMakeRange(selStart, selLength)]; + } + } return nil; } // Returns an array of action names that this object will respond to. - (NSArray*)accessibilityActionNames { - return [NSArray arrayWithObjects: - NSAccessibilityPressAction, NSAccessibilityShowMenuAction, nil]; + NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; + + // General actions. + [ret addObject:NSAccessibilityShowMenuAction]; + + // TODO(dtseng): this should only get set when there's a default action. + if ([self role] != NSAccessibilityStaticTextRole) + [ret addObject:NSAccessibilityPressAction]; + + return ret; } // Returns a sub-array of values for the given attribute value, starting at @@ -248,7 +321,10 @@ bool GetState(BrowserAccessibility* accessibility, int state) { // Returns the list of accessibility attributes that this object supports. - (NSArray*)accessibilityAttributeNames { - return [NSArray arrayWithObjects: + NSMutableArray* ret = [[NSMutableArray alloc] init]; + + // General attributes. + [ret addObjectsFromArray:[NSArray arrayWithObjects: NSAccessibilityChildrenAttribute, NSAccessibilityDescriptionAttribute, NSAccessibilityEnabledAttribute, @@ -263,7 +339,19 @@ bool GetState(BrowserAccessibility* accessibility, int state) { NSAccessibilityTopLevelUIElementAttribute, NSAccessibilityValueAttribute, NSAccessibilityWindowAttribute, - nil]; + nil]]; + + // Specific role attributes. + if ([self role] == NSAccessibilityTextFieldRole) { + [ret addObjectsFromArray:[NSArray arrayWithObjects: + NSAccessibilityInsertionPointLineNumberAttribute, + NSAccessibilityNumberOfCharactersAttribute, + NSAccessibilitySelectedTextAttribute, + NSAccessibilitySelectedTextRangeAttribute, + NSAccessibilityVisibleCharacterRangeAttribute, + nil]]; + } + return ret; } // Returns the index of the child in this objects array of children. @@ -272,8 +360,7 @@ bool GetState(BrowserAccessibility* accessibility, int state) { for (BrowserAccessibilityCocoa* childToCheck in [self children]) { if ([child isEqual:childToCheck]) return index; - if (![childToCheck isIgnored]) - ++index; + ++index; } return NSNotFound; } @@ -353,6 +440,9 @@ bool GetState(BrowserAccessibility* accessibility, int state) { } - (NSUInteger)hash { + // Potentially called during dealloc. + if (!browserAccessibility_) + return [super hash]; return browserAccessibility_->renderer_id(); } diff --git a/chrome/browser/accessibility/browser_accessibility_mac.h b/chrome/browser/accessibility/browser_accessibility_mac.h index eed09fc..ff1eedd 100644 --- a/chrome/browser/accessibility/browser_accessibility_mac.h +++ b/chrome/browser/accessibility/browser_accessibility_mac.h @@ -21,6 +21,12 @@ class BrowserAccessibilityMac : public BrowserAccessibility { virtual void Initialize(); virtual void ReleaseReference(); + // Overrides from BrowserAccessibility. + // Used to know when to update the cocoa children. + virtual void ReplaceChild( + BrowserAccessibility* old_acc, + BrowserAccessibility* new_acc); + // The BrowserAccessibilityCocoa associated with us. BrowserAccessibilityCocoa* native_view() const { return browser_accessibility_cocoa_; diff --git a/chrome/browser/accessibility/browser_accessibility_mac.mm b/chrome/browser/accessibility/browser_accessibility_mac.mm index 3048b60..e8698a0 100644 --- a/chrome/browser/accessibility/browser_accessibility_mac.mm +++ b/chrome/browser/accessibility/browser_accessibility_mac.mm @@ -45,6 +45,13 @@ void BrowserAccessibilityMac::ReleaseReference() { } } +void BrowserAccessibilityMac::ReplaceChild( + BrowserAccessibility* old_acc, + BrowserAccessibility* new_acc) { + BrowserAccessibility::ReplaceChild(old_acc, new_acc); + [browser_accessibility_cocoa_ childrenChanged]; +} + BrowserAccessibilityCocoa* BrowserAccessibility::toBrowserAccessibilityCocoa() { return static_cast<BrowserAccessibilityMac*>(this)-> native_view(); diff --git a/chrome/browser/accessibility/browser_accessibility_mac_unittest.mm b/chrome/browser/accessibility/browser_accessibility_mac_unittest.mm index 821b9af..a9a0a5d 100644 --- a/chrome/browser/accessibility/browser_accessibility_mac_unittest.mm +++ b/chrome/browser/accessibility/browser_accessibility_mac_unittest.mm @@ -51,6 +51,7 @@ class BrowserAccessibilityTest : public CocoaTest { root.location.y = 0; root.location.width = 500; root.location.height = 100; + root.role = WebAccessibility::ROLE_WEB_AREA; root.attributes[WebAccessibility::ATTR_HELP] = ASCIIToUTF16("HelpText"); WebAccessibility child1; @@ -59,12 +60,14 @@ class BrowserAccessibilityTest : public CocoaTest { child1.location.y = 0; child1.location.width = 250; child1.location.height = 100; + child1.role = WebAccessibility::ROLE_BUTTON; WebAccessibility child2; child2.location.x = 250; child2.location.y = 0; child2.location.width = 250; child2.location.height = 100; + child2.role = WebAccessibility::ROLE_HEADING; root.children.push_back(child1); root.children.push_back(child2); diff --git a/chrome/browser/accessibility/browser_accessibility_manager.cc b/chrome/browser/accessibility/browser_accessibility_manager.cc index eb17e6e..00c5221 100644 --- a/chrome/browser/accessibility/browser_accessibility_manager.cc +++ b/chrome/browser/accessibility/browser_accessibility_manager.cc @@ -109,7 +109,7 @@ void BrowserAccessibilityManager::OnAccessibilityNotifications( void BrowserAccessibilityManager::OnAccessibilityObjectStateChange( const WebAccessibility& acc_obj) { - BrowserAccessibility* new_browser_acc = UpdateTree(acc_obj); + BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); if (!new_browser_acc) return; @@ -121,7 +121,7 @@ void BrowserAccessibilityManager::OnAccessibilityObjectStateChange( void BrowserAccessibilityManager::OnAccessibilityObjectChildrenChange( const WebAccessibility& acc_obj) { - BrowserAccessibility* new_browser_acc = UpdateTree(acc_obj); + BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, true); if (!new_browser_acc) return; @@ -133,7 +133,7 @@ void BrowserAccessibilityManager::OnAccessibilityObjectChildrenChange( void BrowserAccessibilityManager::OnAccessibilityObjectFocusChange( const WebAccessibility& acc_obj) { - BrowserAccessibility* new_browser_acc = UpdateTree(acc_obj); + BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); if (!new_browser_acc) return; @@ -169,26 +169,26 @@ void BrowserAccessibilityManager::OnAccessibilityObjectLoadComplete( void BrowserAccessibilityManager::OnAccessibilityObjectValueChange( const WebAccessibility& acc_obj) { - BrowserAccessibility* new_browser_acc = UpdateTree(acc_obj); + BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); if (!new_browser_acc) return; NotifyAccessibilityEvent( ViewHostMsg_AccessibilityNotification_Params:: NOTIFICATION_TYPE_VALUE_CHANGED, - root_); + new_browser_acc); } void BrowserAccessibilityManager::OnAccessibilityObjectTextChange( const WebAccessibility& acc_obj) { - BrowserAccessibility* new_browser_acc = UpdateTree(acc_obj); + BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); if (!new_browser_acc) return; NotifyAccessibilityEvent( ViewHostMsg_AccessibilityNotification_Params:: NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED, - root_); + new_browser_acc); } void BrowserAccessibilityManager::GotFocus() { @@ -226,6 +226,12 @@ void BrowserAccessibilityManager::DoDefaultAction( delegate_->AccessibilityDoDefaultAction(node.renderer_id()); } +gfx::Rect BrowserAccessibilityManager::GetViewBounds() { + if (delegate_) + return delegate_->GetViewBounds(); + return gfx::Rect(); +} + bool BrowserAccessibilityManager::CanModifyTreeInPlace( BrowserAccessibility* current_root, const WebAccessibility& new_root) { @@ -257,10 +263,10 @@ void BrowserAccessibilityManager::ModifyTreeInPlace( new_root); } - -BrowserAccessibility* BrowserAccessibilityManager::UpdateTree( - const WebAccessibility& acc_obj) { - base::hash_map<int, int32>::iterator iter = +BrowserAccessibility* BrowserAccessibilityManager::UpdateNode( + const WebAccessibility& acc_obj, + bool include_children) { + base::hash_map<int32, int32>::iterator iter = renderer_id_to_child_id_map_.find(acc_obj.id); if (iter == renderer_id_to_child_id_map_.end()) return NULL; @@ -270,30 +276,41 @@ BrowserAccessibility* BrowserAccessibilityManager::UpdateTree( if (!old_browser_acc) return NULL; + if (!include_children) { + DCHECK_EQ(0U, acc_obj.children.size()); + old_browser_acc->Initialize( + this, + old_browser_acc->GetParent(), + old_browser_acc->child_id(), + old_browser_acc->index_in_parent(), + acc_obj); + return old_browser_acc; + } + if (CanModifyTreeInPlace(old_browser_acc, acc_obj)) { ModifyTreeInPlace(old_browser_acc, acc_obj); return old_browser_acc; } BrowserAccessibility* new_browser_acc = CreateAccessibilityTree( - old_browser_acc->GetParent(), - child_id, - acc_obj, - old_browser_acc->index_in_parent()); - - if (old_browser_acc->GetParent()) { - old_browser_acc->GetParent()->ReplaceChild( - old_browser_acc, - new_browser_acc); - } else { - DCHECK_EQ(old_browser_acc, root_); - root_ = new_browser_acc; - } - old_browser_acc->ReleaseTree(); - old_browser_acc->ReleaseReference(); - child_id_map_[child_id] = new_browser_acc; + old_browser_acc->GetParent(), + child_id, + acc_obj, + old_browser_acc->index_in_parent()); + + if (old_browser_acc->GetParent()) { + old_browser_acc->GetParent()->ReplaceChild( + old_browser_acc, + new_browser_acc); + } else { + DCHECK_EQ(old_browser_acc, root_); + root_ = new_browser_acc; + } + old_browser_acc->ReleaseTree(); + old_browser_acc->ReleaseReference(); + child_id_map_[child_id] = new_browser_acc; - return new_browser_acc; + return new_browser_acc; } BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree( diff --git a/chrome/browser/accessibility/browser_accessibility_manager.h b/chrome/browser/accessibility/browser_accessibility_manager.h index ff96e56..0abcd1c 100644 --- a/chrome/browser/accessibility/browser_accessibility_manager.h +++ b/chrome/browser/accessibility/browser_accessibility_manager.h @@ -15,7 +15,6 @@ #include "gfx/native_widget_types.h" #include "webkit/glue/webaccessibility.h" - class BrowserAccessibility; #if defined(OS_WIN) class BrowserAccessibilityManagerWin; @@ -30,6 +29,7 @@ class BrowserAccessibilityDelegate { virtual void SetAccessibilityFocus(int acc_obj_id) = 0; virtual void AccessibilityDoDefaultAction(int acc_obj_id) = 0; virtual bool HasFocus() = 0; + virtual gfx::Rect GetViewBounds() const = 0; }; class BrowserAccessibilityFactory { @@ -81,6 +81,9 @@ class BrowserAccessibilityManager { // Tell the renderer to do the default action for this node. void DoDefaultAction(const BrowserAccessibility& node); + // Retrieve the bounds of the parent View in screen coordinates. + gfx::Rect GetViewBounds(); + // Called when the renderer process has notified us of about tree changes. // Send a notification to MSAA clients of the change. void OnAccessibilityNotifications( @@ -130,12 +133,14 @@ class BrowserAccessibilityManager { BrowserAccessibility* current_root, const WebAccessibility& new_root); - // Update the accessibility tree with an updated WebAccessibility tree or - // subtree received from the renderer process. First attempts to modify - // the tree in place, and if that fails, replaces the entire subtree. - // Returns the updated node or NULL if no node was updated. - BrowserAccessibility* UpdateTree( - const WebAccessibility& acc_obj); + // Update an accessibility node with an updated WebAccessibility node + // received from the renderer process. When |include_children| is true + // the node's children will also be updated, otherwise only the node + // itself is updated. Returns the updated node or NULL if no node was + // updated. + BrowserAccessibility* UpdateNode( + const WebAccessibility& acc_obj, + bool include_children); // Recursively build a tree of BrowserAccessibility objects from // the WebAccessibility tree received from the renderer process. @@ -165,7 +170,7 @@ class BrowserAccessibilityManager { // A mapping from the IDs of objects in the renderer, to the child IDs // we use internally here. - base::hash_map<int, int32> renderer_id_to_child_id_map_; + base::hash_map<int32, int32> renderer_id_to_child_id_map_; // A mapping from child IDs to BrowserAccessibility objects. base::hash_map<int32, BrowserAccessibility*> child_id_map_; diff --git a/chrome/browser/accessibility/browser_accessibility_win.cc b/chrome/browser/accessibility/browser_accessibility_win.cc index 8817dfa..442d644 100644 --- a/chrome/browser/accessibility/browser_accessibility_win.cc +++ b/chrome/browser/accessibility/browser_accessibility_win.cc @@ -53,15 +53,31 @@ HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) { return S_OK; } -STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left, LONG y_top, - VARIANT* child) { +STDMETHODIMP BrowserAccessibilityWin::accHitTest( + LONG x_left, LONG y_top, VARIANT* child) { if (!instance_active_) return E_FAIL; if (!child) return E_INVALIDARG; - return E_NOTIMPL; + gfx::Point point(x_left, y_top); + if (!GetBoundsRect().Contains(point)) { + // Return S_FALSE and VT_EMPTY when the outside the object's boundaries. + child->vt = VT_EMPTY; + return S_FALSE; + } + + BrowserAccessibility* result = BrowserAccessibilityForPoint(point); + if (result == this) { + // Point is within this object. + child->vt = VT_I4; + child->lVal = CHILDID_SELF; + } else { + child->vt = VT_DISPATCH; + child->pdispVal = result->toBrowserAccessibilityWin()->NewReference(); + } + return S_OK; } STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left, LONG* y_top, @@ -77,16 +93,11 @@ STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left, LONG* y_top, if (!target) return E_INVALIDARG; - // Find the top left corner of the containing window in screen coords, and - // adjust the output position by this amount. - HWND parent_hwnd = manager_->GetParentView(); - POINT top_left = {0, 0}; - ::ClientToScreen(parent_hwnd, &top_left); - - *x_left = target->location_.x + top_left.x; - *y_top = target->location_.y + top_left.y; - *width = target->location_.width; - *height = target->location_.height; + gfx::Rect bounds = target->GetBoundsRect(); + *x_left = bounds.x(); + *y_top = bounds.y(); + *width = bounds.width(); + *height = bounds.height(); return S_OK; } @@ -1107,6 +1118,11 @@ void BrowserAccessibilityWin::Initialize() { html_attributes_.push_back(std::make_pair(L"level", role_name_.substr(1))); } + // Expose the "display" object attribute. + string16 display; + if (GetAttribute(WebAccessibility::ATTR_DISPLAY, &display)) + html_attributes_.push_back(std::make_pair(L"display", display)); + // If this object doesn't have a name but it does have a description, // use the description as its name - because some screen readers only // announce the name. @@ -1156,22 +1172,6 @@ BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID( return manager_->GetFromChildID(child_id)->toBrowserAccessibilityWin(); } -bool BrowserAccessibilityWin::HasAttribute( - WebAccessibility::Attribute attribute) { - return (attributes_.find(attribute) != attributes_.end()); -} - -bool BrowserAccessibilityWin::GetAttribute( - WebAccessibility::Attribute attribute, string16* value) { - std::map<int32, string16>::iterator iter = attributes_.find(attribute); - if (iter != attributes_.end()) { - *value = iter->second; - return true; - } - - return false; -} - HRESULT BrowserAccessibilityWin::GetAttributeAsBstr( WebAccessibility::Attribute attribute, BSTR* value_bstr) { string16 str; @@ -1188,19 +1188,6 @@ HRESULT BrowserAccessibilityWin::GetAttributeAsBstr( return S_OK; } -bool BrowserAccessibilityWin::GetAttributeAsInt( - WebAccessibility::Attribute attribute, int* value_int) { - string16 value_str; - - if (!GetAttribute(attribute, &value_str)) - return false; - - if (!base::StringToInt(value_str, value_int)) - return false; - - return true; -} - string16 BrowserAccessibilityWin::Escape(string16 str) { return EscapeQueryParamValueUTF8(str, false); } diff --git a/chrome/browser/accessibility/browser_accessibility_win.h b/chrome/browser/accessibility/browser_accessibility_win.h index 16ea136..6352b98 100644 --- a/chrome/browser/accessibility/browser_accessibility_win.h +++ b/chrome/browser/accessibility/browser_accessibility_win.h @@ -457,26 +457,12 @@ class BrowserAccessibilityWin // bitmasks defined in webkit/glue/webaccessibility.h. void InitRoleAndState(); - // Return true if this attribute is in the attributes map. - bool HasAttribute(WebAccessibility::Attribute attribute); - - // Retrieve the string value of an attribute from the attribute map and - // returns true if found. - bool GetAttribute(WebAccessibility::Attribute attribute, string16* value); - // Retrieve the string value of an attribute from the attribute map and // if found and nonempty, allocate a new BSTR (with SysAllocString) // and return S_OK. If not found or empty, return S_FALSE. HRESULT GetAttributeAsBstr( WebAccessibility::Attribute attribute, BSTR* value_bstr); - // Retrieve the value of an attribute from the attribute map and - // if found and nonempty, try to convert it to an integer. - // Returns true only if both the attribute was found and it was successfully - // converted to an integer. - bool GetAttributeAsInt( - WebAccessibility::Attribute attribute, int* value_int); - // Escape a string like it would be escaped for a URL or HTML form. string16 Escape(string16 str); diff --git a/chrome/browser/app_controller_mac.h b/chrome/browser/app_controller_mac.h index dc555e0..721aefc 100644 --- a/chrome/browser/app_controller_mac.h +++ b/chrome/browser/app_controller_mac.h @@ -9,6 +9,7 @@ #import <Cocoa/Cocoa.h> #include <vector> +#include "base/cocoa_protocols_mac.h" #include "base/scoped_nsobject.h" #include "base/scoped_ptr.h" @@ -23,7 +24,8 @@ class Profile; // The application controller object, created by loading the MainMenu nib. // This handles things like responding to menus when there are no windows // open, etc and acts as the NSApplication delegate. -@interface AppController : NSObject<NSUserInterfaceValidations> { +@interface AppController : NSObject<NSUserInterfaceValidations, + NSApplicationDelegate> { @private scoped_ptr<CommandUpdater> menuState_; // Management of the bookmark menu which spans across all windows diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm index 22de42f..6c8a9f1 100644 --- a/chrome/browser/app_controller_mac.mm +++ b/chrome/browser/app_controller_mac.mm @@ -13,7 +13,8 @@ #include "base/message_loop.h" #include "base/string_number_conversions.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#import "base/worker_pool_mac.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_init.h" #include "chrome/browser/browser_list.h" @@ -27,6 +28,7 @@ #import "chrome/browser/cocoa/browser_window_controller.h" #import "chrome/browser/cocoa/bug_report_window_controller.h" #import "chrome/browser/cocoa/clear_browsing_data_controller.h" +#import "chrome/browser/cocoa/confirm_quit_panel_controller.h" #import "chrome/browser/cocoa/encoding_menu_controller_delegate_mac.h" #import "chrome/browser/cocoa/history_menu_bridge.h" #import "chrome/browser/cocoa/import_settings_dialog.h" @@ -221,6 +223,15 @@ void RecordLastRunAppBundlePath() { if (parsed_command_line.HasSwitch(switches::kActivateOnLaunch)) { [NSApp activateIgnoringOtherApps:YES]; } + + // Temporary flag to revert to the old WorkerPool implementation. + // This will be removed once we either fix the Mac WorkerPool + // implementation, or completely switch to the shared (with Linux) + // implementation. + // http://crbug.com/44392 + if (parsed_command_line.HasSwitch(switches::kDisableLinuxWorkerPool)) { + worker_pool_mac::SetUseLinuxWorkerPool(false); + } } // (NSApplicationDelegate protocol) This is the Apple-approved place to override @@ -242,6 +253,12 @@ void RecordLastRunAppBundlePath() { // them in, but I'm not sure about UX; we'd also want to disable other things // though.) http://crbug.com/40861 + // Check if the user really wants to quit by employing the confirm-to-quit + // mechanism. + if (!browser_shutdown::IsTryingToQuit() && + [self applicationShouldTerminate:app] != NSTerminateNow) + return NO; + size_t num_browsers = BrowserList::size(); // Give any print jobs in progress time to finish. @@ -267,6 +284,92 @@ void RecordLastRunAppBundlePath() { } } +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app { + // Check if the experiment is enabled. + const CommandLine* commandLine(CommandLine::ForCurrentProcess()); + if (!commandLine->HasSwitch(switches::kEnableConfirmToQuit)) + return NSTerminateNow; + + // If the application is going to terminate as the result of a Cmd+Q + // invocation, use the special sauce to prevent accidental quitting. + // http://dev.chromium.org/developers/design-documents/confirm-to-quit-experiment + NSEvent* currentEvent = [app currentEvent]; + if ([currentEvent type] == NSKeyDown) { + ConfirmQuitPanelController* quitPanel = + [[ConfirmQuitPanelController alloc] init]; // Releases self. + // Show the info panel that explains what the user must to do confirm quit. + [quitPanel showWindow:self]; + + // How long the user must hold down Cmd+Q to confirm the quit. + const NSTimeInterval kTimeToConfirmQuit = 1.5; + // Leeway between the |targetDate| and the current time that will confirm a + // quit. + const NSTimeInterval kTimeDeltaFuzzFactor = 1.0; + // Duration of the window fade out animation. + const NSTimeInterval kWindowFadeAnimationDuration = 0.2; + + // Spin a nested run loop until the |targetDate| is reached or a KeyUp event + // is sent. + NSDate* targetDate = + [NSDate dateWithTimeIntervalSinceNow:kTimeToConfirmQuit]; + BOOL willQuit = NO; + NSEvent* nextEvent = nil; + do { + // Dequeue events until a key up is received. + nextEvent = [app nextEventMatchingMask:NSKeyUpMask + untilDate:nil + inMode:NSEventTrackingRunLoopMode + dequeue:YES]; + + // Wait for the time expiry to happen. Once past the hold threshold, + // commit to quitting and hide all the open windows. + if (!willQuit) { + NSDate* now = [NSDate date]; + NSTimeInterval difference = [targetDate timeIntervalSinceDate:now]; + if (difference < kTimeDeltaFuzzFactor) { + willQuit = YES; + + // At this point, the quit has been confirmed and windows should all + // fade out to convince the user to release the key combo to finalize + // the quit. + [NSAnimationContext beginGrouping]; + [[NSAnimationContext currentContext] setDuration: + kWindowFadeAnimationDuration]; + for (NSWindow* aWindow in [app windows]) { + // Windows that are set to animate and have a delegate do not + // expect to be animated by other things and could result in an + // invalid state. If a window is set up like so, just force the + // alpha value to 0. Otherwise, animate all pretty and stuff. + if (![[aWindow animationForKey:@"alphaValue"] delegate]) { + [[aWindow animator] setAlphaValue:0.0]; + } else { + [aWindow setAlphaValue:0.0]; + } + } + [NSAnimationContext endGrouping]; + } + } + } while (!nextEvent); + + // The user has released the key combo. Discard any events (i.e. the + // repeated KeyDown Cmd+Q). + [app discardEventsMatchingMask:NSAnyEventMask beforeEvent:nextEvent]; + if (willQuit) { + // The user held down the combination long enough that quitting should + // happen. + return NSTerminateNow; + } else { + // Slowly fade the confirm window out in case the user doesn't + // understand what they have to do to quit. + [quitPanel dismissPanel]; + return NSTerminateCancel; + } + } // if event type is KeyDown + + // Default case: terminate. + return NSTerminateNow; +} + // Called when the app is shutting down. Clean-up as appropriate. - (void)applicationWillTerminate:(NSNotification*)aNotification { NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; diff --git a/chrome/browser/app_controller_mac_unittest.mm b/chrome/browser/app_controller_mac_unittest.mm index 39cc77e..6af8e13 100644 --- a/chrome/browser/app_controller_mac_unittest.mm +++ b/chrome/browser/app_controller_mac_unittest.mm @@ -5,7 +5,7 @@ #import <Cocoa/Cocoa.h> #include "base/scoped_nsobject.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #import "chrome/browser/app_controller_mac.h" #include "testing/platform_test.h" diff --git a/chrome/browser/app_launched_animation.h b/chrome/browser/app_launched_animation.h index 278b228..91c029f 100644 --- a/chrome/browser/app_launched_animation.h +++ b/chrome/browser/app_launched_animation.h @@ -18,7 +18,7 @@ class AppLaunchedAnimation { public: // Starts an animation of the |extension| being launched. The |rect| is the // rect of the app icon. - static void Show(Extension* extension, const gfx::Rect& rect); + static void Show(const Extension* extension, const gfx::Rect& rect); private: AppLaunchedAnimation() { } diff --git a/chrome/browser/appcache/appcache_ui_test.cc b/chrome/browser/appcache/appcache_ui_test.cc index c1000bd..a4814cf 100644 --- a/chrome/browser/appcache/appcache_ui_test.cc +++ b/chrome/browser/appcache/appcache_ui_test.cc @@ -10,6 +10,7 @@ class AppCacheUITest : public UILayoutTest { virtual ~AppCacheUITest() {} }; +// Flaky: http://crbug.com/54717 TEST_F(AppCacheUITest, FLAKY_AppCacheLayoutTests) { static const char* kLayoutTestFiles[] = { "404-manifest.html", diff --git a/chrome/browser/autocomplete/autocomplete.cc b/chrome/browser/autocomplete/autocomplete.cc index d5b6cc6..007fd2b 100644 --- a/chrome/browser/autocomplete/autocomplete.cc +++ b/chrome/browser/autocomplete/autocomplete.cc @@ -13,6 +13,7 @@ #include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/history_quick_provider.h" #include "chrome/browser/autocomplete/history_url_provider.h" #include "chrome/browser/autocomplete/history_contents_provider.h" @@ -406,187 +407,6 @@ void AutocompleteInput::Clear() { prefer_keyword_ = false; } -// AutocompleteMatch ---------------------------------------------------------- - -AutocompleteMatch::AutocompleteMatch() - : provider(NULL), - relevance(0), - deletable(false), - inline_autocomplete_offset(std::wstring::npos), - transition(PageTransition::GENERATED), - is_history_what_you_typed_match(false), - type(SEARCH_WHAT_YOU_TYPED), - template_url(NULL), - starred(false) { -} - -AutocompleteMatch::AutocompleteMatch(AutocompleteProvider* provider, - int relevance, - bool deletable, - Type type) - : provider(provider), - relevance(relevance), - deletable(deletable), - inline_autocomplete_offset(std::wstring::npos), - transition(PageTransition::TYPED), - is_history_what_you_typed_match(false), - type(type), - template_url(NULL), - starred(false) { -} - -AutocompleteMatch::~AutocompleteMatch() { -} - -// static -std::string AutocompleteMatch::TypeToString(Type type) { - const char* strings[NUM_TYPES] = { - "url-what-you-typed", - "history-url", - "history-title", - "history-body", - "history-keyword", - "navsuggest", - "search-what-you-typed", - "search-history", - "search-suggest", - "search-other-engine", - "open-history-page", - }; - DCHECK(arraysize(strings) == NUM_TYPES); - return strings[type]; -} - -// static -int AutocompleteMatch::TypeToIcon(Type type) { - int icons[NUM_TYPES] = { - IDR_OMNIBOX_HTTP, - IDR_OMNIBOX_HTTP, - IDR_OMNIBOX_HISTORY, - IDR_OMNIBOX_HISTORY, - IDR_OMNIBOX_HISTORY, - IDR_OMNIBOX_HTTP, - IDR_OMNIBOX_SEARCH, - IDR_OMNIBOX_SEARCH, - IDR_OMNIBOX_SEARCH, - IDR_OMNIBOX_SEARCH, - IDR_OMNIBOX_MORE, - }; - DCHECK(arraysize(icons) == NUM_TYPES); - return icons[type]; -} - -// static -bool AutocompleteMatch::MoreRelevant(const AutocompleteMatch& elem1, - const AutocompleteMatch& elem2) { - // For equal-relevance matches, we sort alphabetically, so that providers - // who return multiple elements at the same priority get a "stable" sort - // across multiple updates. - if (elem1.relevance == elem2.relevance) - return elem1.contents > elem2.contents; - - // A negative relevance indicates the real relevance can be determined by - // negating the value. If both relevances are negative, negate the result - // so that we end up with positive relevances, then negative relevances with - // the negative relevances sorted by absolute values. - const bool result = elem1.relevance > elem2.relevance; - return (elem1.relevance < 0 && elem2.relevance < 0) ? !result : result; -} - -// static -bool AutocompleteMatch::DestinationSortFunc(const AutocompleteMatch& elem1, - const AutocompleteMatch& elem2) { - // Sort identical destination_urls together. Place the most relevant matches - // first, so that when we call std::unique(), these are the ones that get - // preserved. - return (elem1.destination_url != elem2.destination_url) ? - (elem1.destination_url < elem2.destination_url) : - MoreRelevant(elem1, elem2); -} - -// static -bool AutocompleteMatch::DestinationsEqual(const AutocompleteMatch& elem1, - const AutocompleteMatch& elem2) { - return elem1.destination_url == elem2.destination_url; -} - -// static -void AutocompleteMatch::ClassifyMatchInString( - const std::wstring& find_text, - const std::wstring& text, - int style, - ACMatchClassifications* classification) { - ClassifyLocationInString(text.find(find_text), find_text.length(), - text.length(), style, classification); -} - -void AutocompleteMatch::ClassifyLocationInString( - size_t match_location, - size_t match_length, - size_t overall_length, - int style, - ACMatchClassifications* classification) { - classification->clear(); - - // Don't classify anything about an empty string - // (AutocompleteMatch::Validate() checks this). - if (overall_length == 0) - return; - - // Mark pre-match portion of string (if any). - if (match_location != 0) { - classification->push_back(ACMatchClassification(0, style)); - } - - // Mark matching portion of string. - if (match_location == std::wstring::npos) { - // No match, above classification will suffice for whole string. - return; - } - // Classifying an empty match makes no sense and will lead to validation - // errors later. - DCHECK(match_length > 0); - classification->push_back(ACMatchClassification(match_location, - (style | ACMatchClassification::MATCH) & ~ACMatchClassification::DIM)); - - // Mark post-match portion of string (if any). - const size_t after_match(match_location + match_length); - if (after_match < overall_length) { - classification->push_back(ACMatchClassification(after_match, style)); - } -} - -#ifndef NDEBUG -void AutocompleteMatch::Validate() const { - ValidateClassifications(contents, contents_class); - ValidateClassifications(description, description_class); -} - -void AutocompleteMatch::ValidateClassifications( - const std::wstring& text, - const ACMatchClassifications& classifications) const { - if (text.empty()) { - DCHECK(classifications.size() == 0); - return; - } - - // The classifications should always cover the whole string. - DCHECK(classifications.size() > 0) << "No classification for text"; - DCHECK(classifications[0].offset == 0) << "Classification misses beginning"; - if (classifications.size() == 1) - return; - - // The classifications should always be sorted. - size_t last_offset = classifications[0].offset; - for (ACMatchClassifications::const_iterator i(classifications.begin() + 1); - i != classifications.end(); ++i) { - DCHECK(i->offset > last_offset) << "Classification unsorted"; - DCHECK(i->offset < text.length()) << "Classification out of bounds"; - last_offset = i->offset; - } -} -#endif - // AutocompleteProvider ------------------------------------------------------- // static @@ -752,6 +572,36 @@ void AutocompleteResult::SortAndCull(const AutocompleteInput& input) { alternate_nav_url_ = input.canonicalized_url(); } +size_t AutocompleteResult::size() const { + return matches_.size(); +} + +bool AutocompleteResult::empty() const { + return matches_.empty(); +} + +AutocompleteResult::const_iterator AutocompleteResult::begin() const { + return matches_.begin(); +} + +AutocompleteResult::iterator AutocompleteResult::begin() { + return matches_.begin(); +} + +AutocompleteResult::const_iterator AutocompleteResult::end() const { + return matches_.end(); +} + +AutocompleteResult::iterator AutocompleteResult::end() { + return matches_.end(); +} + +// Returns the match at the given index. +const AutocompleteMatch& AutocompleteResult::match_at(size_t index) const { + DCHECK(index < matches_.size()); + return matches_[index]; +} + void AutocompleteResult::Reset() { matches_.clear(); default_match_ = end(); diff --git a/chrome/browser/autocomplete/autocomplete.h b/chrome/browser/autocomplete/autocomplete.h index 23bd053..25c8341 100644 --- a/chrome/browser/autocomplete/autocomplete.h +++ b/chrome/browser/autocomplete/autocomplete.h @@ -12,7 +12,6 @@ #include "base/logging.h" #include "base/ref_counted.h" #include "base/timer.h" -#include "chrome/common/page_transition_types.h" #include "googleurl/src/gurl.h" #include "googleurl/src/url_parse.h" @@ -271,195 +270,6 @@ class AutocompleteInput { bool synchronous_only_; }; -// AutocompleteMatch ---------------------------------------------------------- - -// A single result line with classified spans. The autocomplete popup displays -// the 'contents' and the 'description' (the description is optional) in the -// autocomplete dropdown, and fills in 'fill_into_edit' into the textbox when -// that line is selected. fill_into_edit may be the same as 'description' for -// things like URLs, but may be different for searches or other providers. For -// example, a search result may say "Search for asdf" as the description, but -// "asdf" should appear in the box. -struct AutocompleteMatch { - // Autocomplete matches contain strings that are classified according to a - // separate vector of styles. This vector associates flags with particular - // string segments, and must be in sorted order. All text must be associated - // with some kind of classification. Even if a match has no distinct - // segments, its vector should contain an entry at offset 0 with no flags. - // - // Example: The user typed "goog" - // http://www.google.com/ Google - // ^ ^ ^ ^ ^ - // 0, | 15, | 4, - // 11,match 0,match - // - // This structure holds the classification information for each span. - struct ACMatchClassification { - // The values in here are not mutually exclusive -- use them like a - // bitfield. This also means we use "int" instead of this enum type when - // passing the values around, so the compiler doesn't complain. - enum Style { - NONE = 0, - URL = 1 << 0, // A URL - MATCH = 1 << 1, // A match for the user's search term - DIM = 1 << 2, // "Helper text" - }; - - ACMatchClassification(size_t offset, int style) - : offset(offset), - style(style) { - } - - // Offset within the string that this classification starts - size_t offset; - - int style; - }; - - typedef std::vector<ACMatchClassification> ACMatchClassifications; - - // The type of this match. - enum Type { - URL_WHAT_YOU_TYPED = 0, // The input as a URL. - HISTORY_URL, // A past page whose URL contains the input. - HISTORY_TITLE, // A past page whose title contains the input. - HISTORY_BODY, // A past page whose body contains the input. - HISTORY_KEYWORD, // A past page whose keyword contains the input. - NAVSUGGEST, // A suggested URL. - SEARCH_WHAT_YOU_TYPED, // The input as a search query (with the default - // engine). - SEARCH_HISTORY, // A past search (with the default engine) - // containing the input. - SEARCH_SUGGEST, // A suggested search (with the default engine). - SEARCH_OTHER_ENGINE, // A search with a non-default engine. - OPEN_HISTORY_PAGE, // A synthetic result that opens the history page - // to search for the input. - NUM_TYPES, - }; - - AutocompleteMatch(); - AutocompleteMatch(AutocompleteProvider* provider, - int relevance, - bool deletable, - Type type); - ~AutocompleteMatch(); - - // Converts |type| to a string representation. Used in logging. - static std::string TypeToString(Type type); - - // Converts |type| to a resource identifier for the appropriate icon for this - // type. - static int TypeToIcon(Type type); - - // Comparison function for determining when one match is better than another. - static bool MoreRelevant(const AutocompleteMatch& elem1, - const AutocompleteMatch& elem2); - - // Comparison functions for removing matches with duplicate destinations. - static bool DestinationSortFunc(const AutocompleteMatch& elem1, - const AutocompleteMatch& elem2); - static bool DestinationsEqual(const AutocompleteMatch& elem1, - const AutocompleteMatch& elem2); - - // Helper functions for classes creating matches: - // Fills in the classifications for |text|, using |style| as the base style - // and marking the first instance of |find_text| as a match. (This match - // will also not be dimmed, if |style| has DIM set.) - static void ClassifyMatchInString(const std::wstring& find_text, - const std::wstring& text, - int style, - ACMatchClassifications* classifications); - - // Similar to ClassifyMatchInString(), but for cases where the range to mark - // as matching is already known (avoids calling find()). This can be helpful - // when find() would be misleading (e.g. you want to mark the second match in - // a string instead of the first). - static void ClassifyLocationInString(size_t match_location, - size_t match_length, - size_t overall_length, - int style, - ACMatchClassifications* classifications); - - // The provider of this match, used to remember which provider the user had - // selected when the input changes. This may be NULL, in which case there is - // no provider (or memory of the user's selection). - AutocompleteProvider* provider; - - // The relevance of this match. See table above for scores returned by - // various providers. This is used to rank matches among all responding - // providers, so different providers must be carefully tuned to supply - // matches with appropriate relevance. - // - // If the relevance is negative, it will only be displayed if there are not - // enough non-negative items in all the providers to max out the popup. In - // this case, the relevance of the additional items will be inverted so they - // can be mixed in with the rest of the relevances. This allows a provider - // to group its matches, having the added items appear intermixed with its - // other matches. - // - // TODO(pkasting): http://b/1111299 This should be calculated algorithmically, - // rather than being a fairly fixed value defined by the table above. - int relevance; - - // True if the user should be able to delete this match. - bool deletable; - - // This string is loaded into the location bar when the item is selected - // by pressing the arrow keys. This may be different than a URL, for example, - // for search suggestions, this would just be the search terms. - std::wstring fill_into_edit; - - // The position within fill_into_edit from which we'll display the inline - // autocomplete string. This will be std::wstring::npos if this match should - // not be inline autocompleted. - size_t inline_autocomplete_offset; - - // The URL to actually load when the autocomplete item is selected. This URL - // should be canonical so we can compare URLs with strcmp to avoid dupes. - // It may be empty if there is no possible navigation. - GURL destination_url; - - // The main text displayed in the address bar dropdown. - std::wstring contents; - ACMatchClassifications contents_class; - - // Additional helper text for each entry, such as a title or description. - std::wstring description; - ACMatchClassifications description_class; - - // The transition type to use when the user opens this match. By default - // this is TYPED. Providers whose matches do not look like URLs should set - // it to GENERATED. - PageTransition::Type transition; - - // True when this match is the "what you typed" match from the history - // system. - bool is_history_what_you_typed_match; - - // Type of this match. - Type type; - - // If this match corresponds to a keyword, this is the TemplateURL the - // keyword was obtained from. - const TemplateURL* template_url; - - // True if the user has starred the destination URL. - bool starred; - -#ifndef NDEBUG - // Does a data integrity check on this match. - void Validate() const; - - // Checks one text/classifications pair for valid values. - void ValidateClassifications( - const std::wstring& text, - const ACMatchClassifications& classifications) const; -#endif -}; - -typedef AutocompleteMatch::ACMatchClassification ACMatchClassification; -typedef std::vector<ACMatchClassification> ACMatchClassifications; - // AutocompleteProvider ------------------------------------------------------- // A single result provider for the autocomplete system. Given user input, the @@ -642,18 +452,15 @@ class AutocompleteResult { void SortAndCull(const AutocompleteInput& input); // Vector-style accessors/operators. - size_t size() const { return matches_.size(); } - bool empty() const { return matches_.empty(); } - const_iterator begin() const { return matches_.begin(); } - iterator begin() { return matches_.begin(); } - const_iterator end() const { return matches_.end(); } - iterator end() { return matches_.end(); } + size_t size() const; + bool empty() const; + const_iterator begin() const; + iterator begin(); + const_iterator end() const; + iterator end(); // Returns the match at the given index. - const AutocompleteMatch& match_at(size_t index) const { - DCHECK(index < matches_.size()); - return matches_[index]; - } + const AutocompleteMatch& match_at(size_t index) const; // Get the default match for the query (not necessarily the first). Returns // end() if there is no default match. diff --git a/chrome/browser/autocomplete/autocomplete_browsertest.cc b/chrome/browser/autocomplete/autocomplete_browsertest.cc index 0fcd9a8..877fe7a 100644 --- a/chrome/browser/autocomplete/autocomplete_browsertest.cc +++ b/chrome/browser/autocomplete/autocomplete_browsertest.cc @@ -7,6 +7,7 @@ #include "chrome/browser/autocomplete/autocomplete.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/autocomplete/autocomplete_edit_view.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_window.h" @@ -21,14 +22,6 @@ #include "chrome/test/ui_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" -// Basic test is crashy on Mac -// http://crbug.com/49324 -#if defined(OS_MAC) -#define MAYBE_Basic DISABLED_Basic -#else -#define MAYBE_Basic Basic -#endif - // Autocomplete test is flaky on ChromeOS. // http://crbug.com/52928 #if defined(OS_CHROMEOS) @@ -66,7 +59,7 @@ class AutocompleteBrowserTest : public InProcessBrowserTest { } }; -IN_PROC_BROWSER_TEST_F(AutocompleteBrowserTest, MAYBE_Basic) { +IN_PROC_BROWSER_TEST_F(AutocompleteBrowserTest, Basic) { LocationBar* location_bar = GetLocationBar(); EXPECT_EQ(std::wstring(), location_bar->GetInputString()); @@ -147,10 +140,8 @@ IN_PROC_BROWSER_TEST_F(AutocompleteBrowserTest, TabAwayRevertSelect) { EXPECT_EQ(UTF8ToWide(chrome::kAboutBlankURL), location_bar->location_entry()->GetText()); location_bar->location_entry()->SetUserText(L""); - Browser::AddTabWithURLParams params(GURL(chrome::kAboutBlankURL), - PageTransition::START_PAGE); - browser()->AddTabWithURL(¶ms); - EXPECT_EQ(browser(), params.target); + browser()->AddSelectedTabWithURL(GURL(chrome::kAboutBlankURL), + PageTransition::START_PAGE); ui_test_utils::WaitForNavigation( &browser()->GetSelectedTabContents()->controller()); EXPECT_EQ(UTF8ToWide(chrome::kAboutBlankURL), diff --git a/chrome/browser/autocomplete/autocomplete_classifier.cc b/chrome/browser/autocomplete/autocomplete_classifier.cc index 3e96ff5..88dc89c 100644 --- a/chrome/browser/autocomplete/autocomplete_classifier.cc +++ b/chrome/browser/autocomplete/autocomplete_classifier.cc @@ -5,6 +5,7 @@ #include "chrome/browser/autocomplete/autocomplete_classifier.h" #include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "googleurl/src/gurl.h" AutocompleteClassifier::AutocompleteClassifier(Profile* profile) diff --git a/chrome/browser/autocomplete/autocomplete_edit.cc b/chrome/browser/autocomplete/autocomplete_edit.cc index d5faec3..ed18232 100644 --- a/chrome/browser/autocomplete/autocomplete_edit.cc +++ b/chrome/browser/autocomplete/autocomplete_edit.cc @@ -10,15 +10,17 @@ #include "base/metrics/histogram.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/autocomplete/autocomplete_classifier.h" #include "chrome/browser/autocomplete/autocomplete_edit_view.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/autocomplete/keyword_provider.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/command_updater.h" #include "chrome/browser/extensions/extension_omnibox_api.h" #include "chrome/browser/google/google_url_tracker.h" +#include "chrome/browser/instant/instant_controller.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/net/predictor_api.h" #include "chrome/browser/net/url_fixer_upper.h" @@ -321,13 +323,17 @@ void AutocompleteEditModel::AcceptInput(WindowOpenDisposition disposition, if (!match.destination_url.is_valid()) return; - if (match.destination_url == - URLFixerUpper::FixupURL(WideToUTF8(permanent_text_), std::string())) { + if ((match.transition == PageTransition::TYPED) && (match.destination_url == + URLFixerUpper::FixupURL(WideToUTF8(permanent_text_), std::string()))) { // When the user hit enter on the existing permanent URL, treat it like a // reload for scoring purposes. We could detect this by just checking // user_input_in_progress_, but it seems better to treat "edits" that end // up leaving the URL unchanged (e.g. deleting the last character and then - // retyping it) as reloads too. + // retyping it) as reloads too. We exclude non-TYPED transitions because if + // the transition is GENERATED, the user input something that looked + // different from the current URL, even if it wound up at the same place + // (e.g. manually retyping the same search query), and it seems wrong to + // treat this as a reload. match.transition = PageTransition::RELOAD; } else if (for_drop || ((paste_state_ != NONE) && match.is_history_what_you_typed_match)) { @@ -679,6 +685,10 @@ void AutocompleteEditModel::PopupBoundsChangedTo(const gfx::Rect& bounds) { controller_->OnPopupBoundsChanged(bounds); } +void AutocompleteEditModel::ResultsUpdated() { + UpdateSuggestedSearchText(); +} + // Return true if the suggestion type warrants a TCP/IP preconnection. // i.e., it is now highly likely that the user will select the related domain. static bool IsPreconnectable(AutocompleteMatch::Type type) { @@ -766,3 +776,38 @@ void AutocompleteEditModel::GetInfoForCurrentText( alternate_nav_url); } } + +// Returns true if suggested search text should be shown for the specified match +// type. +static bool ShouldShowSuggestSearchTextFor(AutocompleteMatch::Type type) { + // TODO: add support for other engines when in keyword mode. + return ((type == AutocompleteMatch::SEARCH_HISTORY) || + (type == AutocompleteMatch::SEARCH_SUGGEST)); +} + +void AutocompleteEditModel::UpdateSuggestedSearchText() { + if (!InstantController::IsEnabled(profile_, InstantController::VERBATIM_TYPE)) + return; + + string16 suggested_text; + // The suggested text comes from the first search result. + if (popup_->IsOpen()) { + const AutocompleteResult& result = popup_->result(); + if ((result.size() > 1) && (popup_->selected_line() == 0) && + ((result.begin()->inline_autocomplete_offset == std::wstring::npos) || + (result.begin()->inline_autocomplete_offset == + result.begin()->fill_into_edit.size()))) { + for (AutocompleteResult::const_iterator i = result.begin() + 1; + i != result.end(); ++i) { + // TODO: add support for other engines when in keyword mode. + if (ShouldShowSuggestSearchTextFor(i->type) && + i->inline_autocomplete_offset != std::wstring::npos) { + suggested_text = WideToUTF16(i->fill_into_edit.substr( + i->inline_autocomplete_offset)); + break; + } + } + } + } + controller_->OnSetSuggestedSearchText(suggested_text); +} diff --git a/chrome/browser/autocomplete/autocomplete_edit.h b/chrome/browser/autocomplete/autocomplete_edit.h index a9cdf1a..9e94172 100644 --- a/chrome/browser/autocomplete/autocomplete_edit.h +++ b/chrome/browser/autocomplete/autocomplete_edit.h @@ -6,7 +6,8 @@ #define CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_H_ #pragma once -#include "chrome/browser/autocomplete/autocomplete.h" +#include "base/string16.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/page_transition_types.h" @@ -21,6 +22,7 @@ class SkBitmap; class AutocompleteEditController; class AutocompleteEditModel; class AutocompleteEditView; +class AutocompleteResult; namespace gfx { class Rect; @@ -48,6 +50,9 @@ class AutocompleteEditController { // autocomplete. Returns true if the text was committed. virtual bool OnCommitSuggestedText(const std::wstring& typed_text) = 0; + // Sets the suggested search text to |suggested_text|. + virtual void OnSetSuggestedSearchText(const string16& suggested_text) = 0; + // Invoked when the popup is going to change its bounds to |bounds|. virtual void OnPopupBoundsChanged(const gfx::Rect& bounds) = 0; @@ -318,6 +323,9 @@ class AutocompleteEditModel : public NotificationObserver { // Invoked when the popup is going to change its bounds to |bounds|. void PopupBoundsChangedTo(const gfx::Rect& bounds); + // Invoked when the autocomplete results may have changed in some way. + void ResultsUpdated(); + private: enum PasteState { NONE, // Most recent edit was not a paste that replaced all text. @@ -367,6 +375,10 @@ class AutocompleteEditModel : public NotificationObserver { void GetInfoForCurrentText(AutocompleteMatch* match, GURL* alternate_nav_url) const; + // Determines the suggested search text and invokes OnSetSuggestedSearchText + // on the controller. + void UpdateSuggestedSearchText(); + AutocompleteEditView* view_; AutocompletePopupModel* popup_; diff --git a/chrome/browser/autocomplete/autocomplete_edit_unittest.cc b/chrome/browser/autocomplete/autocomplete_edit_unittest.cc index 6451fa6..b0941ad 100644 --- a/chrome/browser/autocomplete/autocomplete_edit_unittest.cc +++ b/chrome/browser/autocomplete/autocomplete_edit_unittest.cc @@ -67,6 +67,7 @@ class TestingAutocompleteEditController : public AutocompleteEditController { virtual bool OnCommitSuggestedText(const std::wstring& typed_text) { return false; } + virtual void OnSetSuggestedSearchText(const string16& suggested_text) {} virtual void OnPopupBoundsChanged(const gfx::Rect& bounds) {} virtual void OnAutocompleteAccept(const GURL& url, WindowOpenDisposition disposition, diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_browsertest.cc b/chrome/browser/autocomplete/autocomplete_edit_view_browsertest.cc index 2890fdd..6a8ef27 100644 --- a/chrome/browser/autocomplete/autocomplete_edit_view_browsertest.cc +++ b/chrome/browser/autocomplete/autocomplete_edit_view_browsertest.cc @@ -10,9 +10,10 @@ #include "base/string_util.h" #include "base/time.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/autocomplete/autocomplete.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/autocomplete/autocomplete_edit_view.h" #include "chrome/browser/bookmarks/bookmark_model.h" @@ -658,3 +659,128 @@ IN_PROC_BROWSER_TEST_F(AutocompleteEditViewTest, MAYBE_EscapeToDefaultMatch) { EXPECT_EQ(old_text, edit_view->GetText()); EXPECT_EQ(old_selected_line, popup_model->selected_line()); } + +IN_PROC_BROWSER_TEST_F(AutocompleteEditViewTest, BasicTextOperations) { + ASSERT_NO_FATAL_FAILURE(SetupComponents()); + ui_test_utils::NavigateToURL(browser(), GURL(chrome::kAboutBlankURL)); + browser()->FocusLocationBar(); + + AutocompleteEditView* edit_view = NULL; + ASSERT_NO_FATAL_FAILURE(GetAutocompleteEditView(&edit_view)); + + std::wstring old_text = edit_view->GetText(); + EXPECT_EQ(UTF8ToWide(chrome::kAboutBlankURL), old_text); + EXPECT_TRUE(edit_view->IsSelectAll()); + + std::wstring::size_type start, end; + edit_view->GetSelectionBounds(&start, &end); + EXPECT_EQ(0U, start); + EXPECT_EQ(old_text.size(), end); + + // Move the cursor to the end. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_END, false, false, false)); + EXPECT_FALSE(edit_view->IsSelectAll()); + + // Make sure the cursor is placed correctly. + edit_view->GetSelectionBounds(&start, &end); + EXPECT_EQ(old_text.size(), start); + EXPECT_EQ(old_text.size(), end); + + // Insert one character at the end. Make sure we won't insert anything after + // the special ZWS mark used in gtk implementation. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_A, false, false, false)); + EXPECT_EQ(old_text + L"a", edit_view->GetText()); + + // Delete one character from the end. Make sure we won't delete the special + // ZWS mark used in gtk implementation. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_BACK, false, false, false)); + EXPECT_EQ(old_text, edit_view->GetText()); + + edit_view->SelectAll(true); + EXPECT_TRUE(edit_view->IsSelectAll()); + edit_view->GetSelectionBounds(&start, &end); + EXPECT_EQ(0U, start); + EXPECT_EQ(old_text.size(), end); + + // Delete the content + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_DELETE, false, false, false)); + EXPECT_TRUE(edit_view->IsSelectAll()); + edit_view->GetSelectionBounds(&start, &end); + EXPECT_EQ(0U, start); + EXPECT_EQ(0U, end); + EXPECT_TRUE(edit_view->GetText().empty()); + + // Check if RevertAll() can set text and cursor correctly. + edit_view->RevertAll(); + EXPECT_FALSE(edit_view->IsSelectAll()); + EXPECT_EQ(old_text, edit_view->GetText()); + edit_view->GetSelectionBounds(&start, &end); + EXPECT_EQ(old_text.size(), start); + EXPECT_EQ(old_text.size(), end); +} + +#if defined(OS_LINUX) +IN_PROC_BROWSER_TEST_F(AutocompleteEditViewTest, UndoRedoLinux) { + ASSERT_NO_FATAL_FAILURE(SetupComponents()); + ui_test_utils::NavigateToURL(browser(), GURL(chrome::kAboutBlankURL)); + browser()->FocusLocationBar(); + + AutocompleteEditView* edit_view = NULL; + ASSERT_NO_FATAL_FAILURE(GetAutocompleteEditView(&edit_view)); + + std::wstring old_text = edit_view->GetText(); + EXPECT_EQ(UTF8ToWide(chrome::kAboutBlankURL), old_text); + EXPECT_TRUE(edit_view->IsSelectAll()); + + // Undo should clear the omnibox. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_Z, true, false, false)); + EXPECT_TRUE(edit_view->GetText().empty()); + + // Nothing should happen if undo again. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_Z, true, false, false)); + EXPECT_TRUE(edit_view->GetText().empty()); + + // Redo should restore the original text. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_Z, true, true, false)); + EXPECT_EQ(old_text, edit_view->GetText()); + + // Looks like the undo manager doesn't support restoring selection. + EXPECT_FALSE(edit_view->IsSelectAll()); + + // The cursor should be at the end. + std::wstring::size_type start, end; + edit_view->GetSelectionBounds(&start, &end); + EXPECT_EQ(old_text.size(), start); + EXPECT_EQ(old_text.size(), end); + + // Delete two characters. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_BACK, false, false, false)); + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_BACK, false, false, false)); + EXPECT_EQ(old_text.substr(0, old_text.size() - 2), edit_view->GetText()); + + // Undo delete. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_Z, true, false, false)); + EXPECT_EQ(old_text, edit_view->GetText()); + + // Redo delete. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_Z, true, true, false)); + EXPECT_EQ(old_text.substr(0, old_text.size() - 2), edit_view->GetText()); + + // Delete everything. + edit_view->SelectAll(true); + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_BACK, false, false, false)); + EXPECT_TRUE(edit_view->GetText().empty()); + + // Undo delete everything. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_Z, true, false, false)); + EXPECT_EQ(old_text.substr(0, old_text.size() - 2), edit_view->GetText()); + + // Undo delete two characters. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_Z, true, false, false)); + EXPECT_EQ(old_text, edit_view->GetText()); + + // Undo again. + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_Z, true, false, false)); + EXPECT_TRUE(edit_view->GetText().empty()); +} +#endif diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc b/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc index ee94a96..8ba3b52 100644 --- a/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc +++ b/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc @@ -10,12 +10,12 @@ #include <algorithm> #include "app/l10n_util.h" -#include "base/gtk_util.h" #include "base/logging.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/bookmarks/bookmark_drag_data.h" #include "chrome/browser/browser_process.h" @@ -23,6 +23,7 @@ #include "chrome/browser/defaults.h" #include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/gtk/view_id_util.h" +#include "chrome/browser/platform_util.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/toolbar_model.h" #include "chrome/common/notification_service.h" @@ -43,8 +44,6 @@ #include "chrome/browser/gtk/location_bar_view_gtk.h" #endif -using gfx::SkColorToGdkColor; - namespace { const gchar* kAutocompleteEditViewGtkKey = "__ACE_VIEW_GTK__"; @@ -151,6 +150,10 @@ AutocompleteEditViewGtk::AutocompleteEditViewGtk( faded_text_tag_(NULL), secure_scheme_tag_(NULL), security_error_scheme_tag_(NULL), + normal_text_tag_(NULL), + instant_anchor_tag_(NULL), + instant_view_(NULL), + instant_mark_(NULL), model_(new AutocompleteEditModel(this, controller, profile)), #if defined(TOOLKIT_VIEWS) popup_view_(new AutocompletePopupContentsView( @@ -178,7 +181,8 @@ AutocompleteEditViewGtk::AutocompleteEditViewGtk( paste_clipboard_requested_(false), enter_was_inserted_(false), enable_tab_to_search_(true), - selection_suggested_(false) { + selection_suggested_(false), + going_to_focus_(NULL) { model_->SetPopupModel(popup_view_->GetModel()); } @@ -218,6 +222,15 @@ void AutocompleteEditViewGtk::Init() { tag_table_ = gtk_text_tag_table_new(); text_buffer_ = gtk_text_buffer_new(tag_table_); g_object_set_data(G_OBJECT(text_buffer_), kAutocompleteEditViewGtkKey, this); + + // We need to run this two handlers before undo manager's handlers, so that + // text iterators modified by these handlers can be passed down to undo + // manager's handlers. + g_signal_connect(text_buffer_, "delete-range", + G_CALLBACK(&HandleDeleteRangeThunk), this); + g_signal_connect(text_buffer_, "mark-set", + G_CALLBACK(&HandleMarkSetAlwaysThunk), this); + text_view_ = gtk_undo_view_new(text_buffer_); if (popup_window_mode_) gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view_), false); @@ -255,8 +268,6 @@ void AutocompleteEditViewGtk::Init() { G_CALLBACK(&HandleBeginUserActionThunk), this); g_signal_connect(text_buffer_, "end-user-action", G_CALLBACK(&HandleEndUserActionThunk), this); - g_signal_connect(text_buffer_, "insert-text", - G_CALLBACK(&HandleInsertTextThunk), this); // We connect to key press and release for special handling of a few keys. g_signal_connect(text_view_, "key-press-event", G_CALLBACK(&HandleKeyPressThunk), this); @@ -289,6 +300,10 @@ void AutocompleteEditViewGtk::Init() { text_buffer_, "mark-set", G_CALLBACK(&HandleMarkSetAfterThunk), this); g_signal_connect(text_view_, "drag-data-received", G_CALLBACK(&HandleDragDataReceivedThunk), this); + // Override the text_view_'s default drag-data-get handler by calling our own + // version after the normal call has happened. + g_signal_connect_after(text_view_, "drag-data-get", + G_CALLBACK(&HandleDragDataGetThunk), this); g_signal_connect(text_view_, "backspace", G_CALLBACK(&HandleBackSpaceThunk), this); g_signal_connect(text_view_, "copy-clipboard", @@ -303,6 +318,57 @@ void AutocompleteEditViewGtk::Init() { G_CALLBACK(&HandleWidgetDirectionChangedThunk), this); g_signal_connect(text_view_, "delete-from-cursor", G_CALLBACK(&HandleDeleteFromCursorThunk), this); + g_signal_connect(text_view_, "hierarchy-changed", + G_CALLBACK(&HandleHierarchyChangedThunk), this); +#if GTK_CHECK_VERSION(2, 20, 0) + g_signal_connect(text_view_, "preedit-changed", + G_CALLBACK(&HandlePreeditChangedThunk), this); +#endif + g_signal_connect(text_view_, "undo", G_CALLBACK(&HandleUndoRedoThunk), this); + g_signal_connect(text_view_, "redo", G_CALLBACK(&HandleUndoRedoThunk), this); + g_signal_connect_after(text_view_, "undo", + G_CALLBACK(&HandleUndoRedoAfterThunk), this); + g_signal_connect_after(text_view_, "redo", + G_CALLBACK(&HandleUndoRedoAfterThunk), this); + + // Setup for the Instant suggestion text view. + // GtkLabel is used instead of GtkTextView to get transparent background. + instant_view_ = gtk_label_new(NULL); + + GtkTextIter end_iter; + gtk_text_buffer_get_end_iter(text_buffer_, &end_iter); + + // Insert a Zero Width Space character just before the instant anchor. + // It's a hack to workaround a bug of GtkTextView which can not align the + // preedit string and a child anchor correctly when there is no other content + // around the preedit string. + gtk_text_buffer_insert(text_buffer_, &end_iter, "\342\200\213", -1); + GtkTextChildAnchor* instant_anchor = + gtk_text_buffer_create_child_anchor(text_buffer_, &end_iter); + + gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(text_view_), + instant_view_, + instant_anchor); + + instant_anchor_tag_ = gtk_text_buffer_create_tag(text_buffer_, NULL, NULL); + + GtkTextIter anchor_iter; + gtk_text_buffer_get_iter_at_child_anchor(text_buffer_, &anchor_iter, + instant_anchor); + gtk_text_buffer_apply_tag(text_buffer_, instant_anchor_tag_, + &anchor_iter, &end_iter); + + GtkTextIter start_iter; + gtk_text_buffer_get_start_iter(text_buffer_, &start_iter); + instant_mark_ = + gtk_text_buffer_create_mark(text_buffer_, NULL, &start_iter, FALSE); + + // Hooking up this handler after setting up above hacks for Instant view, so + // that we won't filter out the special ZWP mark itself. + g_signal_connect(text_buffer_, "insert-text", + G_CALLBACK(&HandleInsertTextThunk), this); + + AdjustVerticalAlignmentOfInstantView(); #if !defined(TOOLKIT_VIEWS) registrar_.Add(this, @@ -318,6 +384,17 @@ void AutocompleteEditViewGtk::Init() { ViewIDUtil::SetID(GetNativeView(), VIEW_ID_AUTOCOMPLETE); } +void AutocompleteEditViewGtk::HandleHierarchyChanged( + GtkWidget* sender, GtkWidget* old_toplevel) { + GtkWindow* new_toplevel = platform_util::GetTopLevel(sender); + if (!new_toplevel) + return; + + // Use |signals_| to make sure we don't get called back after destruction. + signals_.Connect(new_toplevel, "set-focus", + G_CALLBACK(&HandleWindowSetFocusThunk), this); +} + void AutocompleteEditViewGtk::SetFocus() { gtk_widget_grab_focus(text_view_); } @@ -334,6 +411,9 @@ int AutocompleteEditViewGtk::TextWidth() { GtkTextIter start, end; GdkRectangle first_char_bounds, last_char_bounds; gtk_text_buffer_get_start_iter(text_buffer_, &start); + + // Use the real end iterator here to take the width of instant suggestion + // text into account, so that location bar can layout its children correctly. gtk_text_buffer_get_end_iter(text_buffer_, &end); gtk_text_view_get_iter_location(GTK_TEXT_VIEW(text_view_), &start, &first_char_bounds); @@ -346,9 +426,8 @@ int AutocompleteEditViewGtk::TextWidth() { } int AutocompleteEditViewGtk::WidthOfTextAfterCursor() { - // TODO(sky): implement this. - NOTIMPLEMENTED(); - return TextWidth(); + // Not used. + return -1; } gfx::Font AutocompleteEditViewGtk::GetFont() { @@ -417,16 +496,26 @@ void AutocompleteEditViewGtk::OpenURL(const GURL& url, std::wstring AutocompleteEditViewGtk::GetText() const { GtkTextIter start, end; - gtk_text_buffer_get_bounds(text_buffer_, &start, &end); + GetTextBufferBounds(&start, &end); gchar* utf8 = gtk_text_buffer_get_text(text_buffer_, &start, &end, false); std::wstring out(UTF8ToWide(utf8)); g_free(utf8); + +#if GTK_CHECK_VERSION(2, 20, 0) + // We need to treat the text currently being composed by the input method as + // part of the text content, so that omnibox can work correctly in the middle + // of composition. + if (preedit_.size()) { + GtkTextMark* mark = gtk_text_buffer_get_insert(text_buffer_); + gtk_text_buffer_get_iter_at_mark(text_buffer_, &start, mark); + out.insert(gtk_text_iter_get_offset(&start), preedit_); + } +#endif return out; } bool AutocompleteEditViewGtk::IsEditingOrEmpty() const { - return model_->user_input_in_progress() || - (gtk_text_buffer_get_char_count(text_buffer_) == 0); + return model_->user_input_in_progress() || (GetTextLength() == 0); } int AutocompleteEditViewGtk::GetIcon() const { @@ -473,7 +562,7 @@ bool AutocompleteEditViewGtk::IsSelectAll() { gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end); GtkTextIter start, end; - gtk_text_buffer_get_bounds(text_buffer_, &start, &end); + GetTextBufferBounds(&start, &end); // Returns true if the |text_buffer_| is empty. return gtk_text_iter_equal(&start, &sel_start) && @@ -506,10 +595,14 @@ void AutocompleteEditViewGtk::UpdatePopup() { return; // Don't inline autocomplete when the caret/selection isn't at the end of - // the text. + // the text, or in the middle of composition. CharRange sel = GetSelection(); - model_->StartAutocomplete(sel.cp_min != sel.cp_max, - std::max(sel.cp_max, sel.cp_min) < GetTextLength()); + bool no_inline_autocomplete = + std::max(sel.cp_max, sel.cp_min) < GetTextLength(); +#if GTK_CHECK_VERSION(2, 20, 0) + no_inline_autocomplete = no_inline_autocomplete || preedit_.size(); +#endif + model_->StartAutocomplete(sel.cp_min != sel.cp_max, no_inline_autocomplete); } void AutocompleteEditViewGtk::ClosePopup() { @@ -650,6 +743,7 @@ void AutocompleteEditViewGtk::SetBaseColor() { gtk_widget_modify_text(text_view_, GTK_STATE_ACTIVE, NULL); gtk_util::UndoForceFontSize(text_view_); + gtk_util::UndoForceFontSize(instant_view_); // Grab the text colors out of the style and set our tags to use them. GtkStyle* style = gtk_rc_get_style(text_view_); @@ -662,6 +756,9 @@ void AutocompleteEditViewGtk::SetBaseColor() { g_object_set(faded_text_tag_, "foreground-gdk", &average_color, NULL); g_object_set(normal_text_tag_, "foreground-gdk", &style->text[GTK_STATE_NORMAL], NULL); + + // GtkLabel uses fg color instead of text color. + gtk_widget_modify_fg(instant_view_, GTK_STATE_NORMAL, &average_color); } else { const GdkColor* background_color_ptr; #if defined(TOOLKIT_VIEWS) @@ -672,35 +769,50 @@ void AutocompleteEditViewGtk::SetBaseColor() { #else background_color_ptr = &LocationBarViewGtk::kBackgroundColor; #endif - gtk_widget_modify_cursor(text_view_, &gfx::kGdkBlack, &gfx::kGdkGray); + gtk_widget_modify_cursor( + text_view_, >k_util::kGdkBlack, >k_util::kGdkGray); gtk_widget_modify_base(text_view_, GTK_STATE_NORMAL, background_color_ptr); + GdkColor c; #if !defined(TOOLKIT_VIEWS) // Override the selected colors so we don't leak colors from the current // gtk theme into the chrome-theme. - GdkColor c; - c = SkColorToGdkColor(theme_provider_->get_active_selection_bg_color()); + c = gfx::SkColorToGdkColor( + theme_provider_->get_active_selection_bg_color()); gtk_widget_modify_base(text_view_, GTK_STATE_SELECTED, &c); - c = SkColorToGdkColor(theme_provider_->get_active_selection_fg_color()); + c = gfx::SkColorToGdkColor( + theme_provider_->get_active_selection_fg_color()); gtk_widget_modify_text(text_view_, GTK_STATE_SELECTED, &c); - c = SkColorToGdkColor(theme_provider_->get_inactive_selection_bg_color()); + c = gfx::SkColorToGdkColor( + theme_provider_->get_inactive_selection_bg_color()); gtk_widget_modify_base(text_view_, GTK_STATE_ACTIVE, &c); - c = SkColorToGdkColor(theme_provider_->get_inactive_selection_fg_color()); + c = gfx::SkColorToGdkColor( + theme_provider_->get_inactive_selection_fg_color()); gtk_widget_modify_text(text_view_, GTK_STATE_ACTIVE, &c); #endif + gdk_color_parse(kTextBaseColor, &c); + gtk_widget_modify_fg(instant_view_, GTK_STATE_NORMAL, &c); + // Until we switch to vector graphics, force the font size. gtk_util::ForceFontSizePixels(text_view_, popup_window_mode_ ? browser_defaults::kAutocompleteEditFontPixelSizeInPopup : browser_defaults::kAutocompleteEditFontPixelSize); + gtk_util::ForceFontSizePixels(instant_view_, + popup_window_mode_ ? + browser_defaults::kAutocompleteEditFontPixelSizeInPopup : + browser_defaults::kAutocompleteEditFontPixelSize); + g_object_set(faded_text_tag_, "foreground", kTextBaseColor, NULL); g_object_set(normal_text_tag_, "foreground", "#000000", NULL); } + + AdjustVerticalAlignmentOfInstantView(); } void AutocompleteEditViewGtk::HandleBeginUserAction(GtkTextBuffer* sender) { @@ -915,7 +1027,7 @@ gboolean AutocompleteEditViewGtk::HandleViewButtonRelease( // NOTE: This function doesn't seem to like a count of 0, looking at the // code it will skip an important loop. Use -1 to achieve the same. GtkTextIter start, end; - gtk_text_buffer_get_bounds(text_buffer_, &start, &end); + GetTextBufferBounds(&start, &end); gtk_text_view_move_visually(GTK_TEXT_VIEW(text_view_), &start, -1); } #endif @@ -946,9 +1058,13 @@ gboolean AutocompleteEditViewGtk::HandleViewFocusIn(GtkWidget* sender, gboolean AutocompleteEditViewGtk::HandleViewFocusOut(GtkWidget* sender, GdkEventFocus* event) { + GtkWidget* view_getting_focus = NULL; + GtkWindow* toplevel = platform_util::GetTopLevel(sender); + if (gtk_window_is_active(toplevel)) + view_getting_focus = going_to_focus_; + // This must be invoked before ClosePopup. - // TODO: figure out who is getting focus. - controller_->OnAutocompleteLosingFocus(NULL); + controller_->OnAutocompleteLosingFocus(view_getting_focus); // Close the popup. ClosePopup(); @@ -972,24 +1088,41 @@ void AutocompleteEditViewGtk::HandleViewMoveCursor( gboolean has_selection = gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end); + bool handled = true; + // We want the GtkEntry behavior when you move the cursor while you have a // selection. GtkTextView just drops the selection and moves the cursor, but // instead we want to move the cursor to the appropiate end of the selection. - if (step == GTK_MOVEMENT_VISUAL_POSITIONS && !extend_selection && - (count == 1 || count == -1) && has_selection) { - // We have a selection and start / end are in ascending order. - // Cursor placement will remove the selection, so we need inform |model_| - // about this change by calling On{Before|After}PossibleChange() methods. - OnBeforePossibleChange(); - gtk_text_buffer_place_cursor(text_buffer_, - count == 1 ? &sel_end : &sel_start); - OnAfterPossibleChange(); + if (step == GTK_MOVEMENT_VISUAL_POSITIONS && !extend_selection) { + if ((count == 1 || count == -1) && has_selection) { + // We have a selection and start / end are in ascending order. + // Cursor placement will remove the selection, so we need inform |model_| + // about this change by calling On{Before|After}PossibleChange() methods. + OnBeforePossibleChange(); + gtk_text_buffer_place_cursor(text_buffer_, + count == 1 ? &sel_end : &sel_start); + OnAfterPossibleChange(); + } else if (count == 1 && !has_selection) { + gint cursor_pos; + g_object_get(G_OBJECT(text_buffer_), "cursor-position", &cursor_pos, + NULL); + if (cursor_pos == GetTextLength()) + controller_->OnCommitSuggestedText(GetText()); + else + handled = false; + } else { + handled = false; + } } else if (step == GTK_MOVEMENT_PAGES) { // Page up and down. // Multiply by count for the direction (if we move too much that's ok). model_->OnUpOrDownKeyPressed(model_->result().size() * count); } else if (step == GTK_MOVEMENT_DISPLAY_LINES) { // Arrow up and down. model_->OnUpOrDownKeyPressed(count); } else { + handled = false; + } + + if (!handled) { // Cursor movement may change the selection, we need to record the change // and report it to |model_|. if (has_selection || extend_selection) @@ -1025,7 +1158,7 @@ void AutocompleteEditViewGtk::HandlePopulatePopup(GtkWidget* sender, // Search Engine menu item. GtkWidget* search_engine_menuitem = gtk_menu_item_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8(IDS_EDIT_SEARCH_ENGINES)).c_str()); gtk_menu_shell_append(GTK_MENU_SHELL(menu), search_engine_menuitem); g_signal_connect(search_engine_menuitem, "activate", @@ -1045,7 +1178,7 @@ void AutocompleteEditViewGtk::HandlePopulatePopup(GtkWidget* sender, // Paste and Go menu item. GtkWidget* paste_go_menuitem = gtk_menu_item_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8(model_->is_paste_and_search() ? IDS_PASTE_AND_SEARCH : IDS_PASTE_AND_GO)).c_str()); gtk_menu_shell_append(GTK_MENU_SHELL(menu), paste_go_menuitem); @@ -1145,14 +1278,31 @@ void AutocompleteEditViewGtk::HandleDragDataReceived( } } +void AutocompleteEditViewGtk::HandleDragDataGet( + GtkWidget* widget, + GdkDragContext* context, + GtkSelectionData* selection_data, + guint target_type, + guint time) { + // If GTK put the normal textual version of the selection in our drag data, + // put our doctored selection that might have the 'http://' prefix. Also, GTK + // is confused about signedness of its datatypes, leading to the weird switch + // statement (no set of casts fixes this). + switch (target_type) { + case GTK_TEXT_BUFFER_TARGET_INFO_TEXT: { + gtk_selection_data_set_text(selection_data, selected_text_.c_str(), -1); + } + } +} + void AutocompleteEditViewGtk::HandleInsertText( GtkTextBuffer* buffer, GtkTextIter* location, const gchar* text, gint len) { std::string filtered_text; filtered_text.reserve(len); // Filter out new line and tab characters. - // |text| is guaranteed to be a valid UTF-8 string, so it's safe here to - // filter byte by byte. + // |text| is guaranteed to be a valid UTF-8 string, so we don't need to + // validate it here. // // If there was only a single character, then it might be generated by a key // event. In this case, we save the single character to help our @@ -1161,15 +1311,25 @@ void AutocompleteEditViewGtk::HandleInsertText( if (len == 1 && (text[0] == '\n' || text[0] == '\r')) enter_was_inserted_ = true; - for (gint i = 0; i < len; ++i) { - gchar c = text[i]; - if (c == '\n' || c == '\r' || c == '\t') - continue; + const gchar* p = text; + while(*p) { + gunichar c = g_utf8_get_char(p); + const gchar* next = g_utf8_next_char(p); - filtered_text.push_back(c); + // 0x200B is Zero Width Space, which is inserted just before the instant + // anchor for working around the GtkTextView's misalignment bug. + // This character might be captured and inserted into the content by undo + // manager, so we need to filter it out here. + if (c != L'\n' && c != L'\r' && c != L'\t' && c != 0x200B) + filtered_text.append(p, next); + + p = next; } if (filtered_text.length()) { + // Avoid inserting the text after the instant anchor. + ValidateTextBufferIter(location); + // Call the default handler to insert filtered text. GtkTextBufferClass* klass = GTK_TEXT_BUFFER_GET_CLASS(buffer); klass->insert_text(buffer, location, filtered_text.data(), @@ -1358,9 +1518,9 @@ void AutocompleteEditViewGtk::SelectAllInternal(bool reversed, bool update_primary_selection) { GtkTextIter start, end; if (reversed) { - gtk_text_buffer_get_bounds(text_buffer_, &end, &start); + GetTextBufferBounds(&end, &start); } else { - gtk_text_buffer_get_bounds(text_buffer_, &start, &end); + GetTextBufferBounds(&start, &end); } if (!update_primary_selection) StartUpdatingHighlightedText(); @@ -1407,6 +1567,11 @@ AutocompleteEditViewGtk::CharRange AutocompleteEditViewGtk::GetSelection() { mark = gtk_text_buffer_get_insert(text_buffer_); gtk_text_buffer_get_iter_at_mark(text_buffer_, &insert, mark); +#if GTK_CHECK_VERSION(2, 20, 0) + // Nothing should be selected when we are in the middle of composition. + DCHECK(preedit_.empty() || gtk_text_iter_equal(&start, &insert)); +#endif + return CharRange(gtk_text_iter_get_offset(&start), gtk_text_iter_get_offset(&insert)); } @@ -1418,13 +1583,28 @@ void AutocompleteEditViewGtk::ItersFromCharRange(const CharRange& range, gtk_text_buffer_get_iter_at_offset(text_buffer_, iter_max, range.cp_max); } -int AutocompleteEditViewGtk::GetTextLength() { - GtkTextIter start, end; - gtk_text_buffer_get_bounds(text_buffer_, &start, &end); +int AutocompleteEditViewGtk::GetTextLength() const { + GtkTextIter end; + gtk_text_buffer_get_iter_at_mark(text_buffer_, &end, instant_mark_); +#if GTK_CHECK_VERSION(2, 20, 0) + // We need to count the length of the text being composed, because we treat + // it as part of the content in GetText(). + return gtk_text_iter_get_offset(&end) + preedit_.size(); +#else return gtk_text_iter_get_offset(&end); +#endif } void AutocompleteEditViewGtk::EmphasizeURLComponents() { +#if GTK_CHECK_VERSION(2, 20, 0) + // We can't change the text style easily, if the preedit string (the text + // being composed by the input method) is not empty, which is not treated as + // a part of the text content inside GtkTextView. And it's ok to simply return + // in this case, as this method will be called again when the preedit string + // gets committed. + if (preedit_.size()) + return; +#endif // See whether the contents are a URL with a non-empty host portion, which we // should emphasize. To check for a URL, rather than using the type returned // by Parse(), ask the model, which will check the desired page transition for @@ -1439,7 +1619,7 @@ void AutocompleteEditViewGtk::EmphasizeURLComponents() { // Set the baseline emphasis. GtkTextIter start, end; - gtk_text_buffer_get_bounds(text_buffer_, &start, &end); + GetTextBufferBounds(&start, &end); gtk_text_buffer_remove_all_tags(text_buffer_, &start, &end); if (emphasize) { gtk_text_buffer_apply_tag(text_buffer_, faded_text_tag_, &start, &end); @@ -1481,6 +1661,28 @@ void AutocompleteEditViewGtk::EmphasizeURLComponents() { } } +void AutocompleteEditViewGtk::SetInstantSuggestion( + const std::string& suggestion) { + gtk_label_set_text(GTK_LABEL(instant_view_), suggestion.c_str()); + if (suggestion.empty()) { + gtk_widget_hide(instant_view_); + } else { + gtk_widget_show(instant_view_); + AdjustVerticalAlignmentOfInstantView(); + } +} + +bool AutocompleteEditViewGtk::CommitInstantSuggestion() { + const gchar* suggestion = gtk_label_get_text(GTK_LABEL(instant_view_)); + if (!suggestion || !*suggestion) + return false; + + OnBeforePossibleChange(); + SetUserText(GetText() + UTF8ToWide(suggestion)); + OnAfterPossibleChange(); + return true; +} + void AutocompleteEditViewGtk::TextChanged() { EmphasizeURLComponents(); controller_->OnChanged(); @@ -1572,6 +1774,42 @@ void AutocompleteEditViewGtk::HandleKeymapDirectionChanged(GdkKeymap* sender) { AdjustTextJustification(); } +void AutocompleteEditViewGtk::HandleDeleteRange(GtkTextBuffer* buffer, + GtkTextIter* start, + GtkTextIter* end) { + // Prevent the user from deleting the instant anchor. We can't simply set the + // instant anchor readonly by applying a tag with "editable" = FALSE, because + // it'll prevent the insert caret from blinking. + ValidateTextBufferIter(start); + ValidateTextBufferIter(end); + if (!gtk_text_iter_compare(start, end)) { + static guint signal_id = + g_signal_lookup("delete-range", GTK_TYPE_TEXT_BUFFER); + g_signal_stop_emission(buffer, signal_id, 0); + } +} + +void AutocompleteEditViewGtk::HandleMarkSetAlways(GtkTextBuffer* buffer, + GtkTextIter* location, + GtkTextMark* mark) { + if (mark == instant_mark_) + return; + + GtkTextIter new_iter = *location; + ValidateTextBufferIter(&new_iter); + + // "mark-set" signal is actually emitted after the mark's location is already + // set, so if the location is beyond the instant anchor, we need to move the + // mark again, which will emit the signal again. In order to prevent other + // signal handlers from being called twice, we need to stop signal emission + // before moving the mark again. + if (gtk_text_iter_compare(&new_iter, location)) { + static guint signal_id = g_signal_lookup("mark-set", GTK_TYPE_TEXT_BUFFER); + g_signal_stop_emission(buffer, signal_id, 0); + gtk_text_buffer_move_mark(buffer, mark, &new_iter); + } +} + // static void AutocompleteEditViewGtk::ClipboardGetSelectionThunk( GtkClipboard* clipboard, @@ -1622,3 +1860,69 @@ void AutocompleteEditViewGtk::UpdatePrimarySelectionIfValidURL() { OwnPrimarySelection(selected_text_); } } + +#if GTK_CHECK_VERSION(2, 20, 0) +void AutocompleteEditViewGtk::HandlePreeditChanged(GtkWidget* sender, + const gchar* preedit) { + // GtkTextView won't fire "begin-user-action" and "end-user-action" signals + // when changing the preedit string, so we need to call + // OnBeforePossibleChange() and OnAfterPossibleChange() by ourselves. + OnBeforePossibleChange(); + if (preedit && *preedit) { + // GtkTextView will only delete the selection range when committing the + // preedit string, which will cause very strange behavior, so we need to + // delete the selection range here explicitly. See http://crbug.com/18808. + if (preedit_.empty()) + gtk_text_buffer_delete_selection(text_buffer_, false, true); + preedit_ = UTF8ToWide(preedit); + } else { + preedit_.clear(); + } + OnAfterPossibleChange(); +} +#endif + +void AutocompleteEditViewGtk::HandleWindowSetFocus( + GtkWindow* sender, GtkWidget* focus) { + // This is actually a guess. If the focused widget changes in "focus-out" + // event handler, then the window will respect that and won't focus + // |focus|. I doubt that is likely to happen however. + going_to_focus_ = focus; +} + +void AutocompleteEditViewGtk::HandleUndoRedo(GtkWidget* sender) { + OnBeforePossibleChange(); +} + +void AutocompleteEditViewGtk::HandleUndoRedoAfter(GtkWidget* sender) { + OnAfterPossibleChange(); +} + +void AutocompleteEditViewGtk::GetTextBufferBounds(GtkTextIter* start, + GtkTextIter* end) const { + gtk_text_buffer_get_start_iter(text_buffer_, start); + gtk_text_buffer_get_iter_at_mark(text_buffer_, end, instant_mark_); +} + +void AutocompleteEditViewGtk::ValidateTextBufferIter(GtkTextIter* iter) const { + if (!instant_mark_) + return; + + GtkTextIter end; + gtk_text_buffer_get_iter_at_mark(text_buffer_, &end, instant_mark_); + if (gtk_text_iter_compare(iter, &end) > 0) + *iter = end; +} + +void AutocompleteEditViewGtk::AdjustVerticalAlignmentOfInstantView() { + // By default, GtkTextView layouts an anchored child widget just above the + // baseline, so we need to move the |instant_view_| down to make sure it + // has the same baseline as the |text_view_|. + PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(instant_view_)); + int height; + pango_layout_get_size(layout, NULL, &height); + PangoLayoutIter* iter = pango_layout_get_iter(layout); + int baseline = pango_layout_iter_get_baseline(iter); + pango_layout_iter_free(iter); + g_object_set(instant_anchor_tag_, "rise", baseline - height, NULL); +} diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_gtk.h b/chrome/browser/autocomplete/autocomplete_edit_view_gtk.h index 2ca1180..b63c2f2 100644 --- a/chrome/browser/autocomplete/autocomplete_edit_view_gtk.h +++ b/chrome/browser/autocomplete/autocomplete_edit_view_gtk.h @@ -12,6 +12,7 @@ #include <string> #include "app/gtk_signal.h" +#include "app/gtk_signal_registrar.h" #include "base/basictypes.h" #include "base/scoped_ptr.h" #include "base/string_util.h" @@ -145,6 +146,9 @@ class AutocompleteEditViewGtk : public AutocompleteEditView, void SetBaseColor(); + void SetInstantSuggestion(const std::string& suggestion); + bool CommitInstantSuggestion(); + // Used by LocationBarViewGtk to inform AutocompleteEditViewGtk if the tab to // search should be enabled or not. See the comment of |enable_tab_to_search_| // for details. @@ -170,6 +174,12 @@ class AutocompleteEditViewGtk : public AutocompleteEditView, GtkTextBuffer*, GtkTextIter*, const gchar*, gint); CHROMEG_CALLBACK_0(AutocompleteEditViewGtk, void, HandleKeymapDirectionChanged, GdkKeymap*); + CHROMEG_CALLBACK_2(AutocompleteEditViewGtk, void, HandleDeleteRange, + GtkTextBuffer*, GtkTextIter*, GtkTextIter*); + // Unlike above HandleMarkSet and HandleMarkSetAfter, this handler will always + // be connected to the signal. + CHROMEG_CALLBACK_2(AutocompleteEditViewGtk, void, HandleMarkSetAlways, + GtkTextBuffer*, GtkTextIter*, GtkTextMark*); CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleKeyPress, GdkEventKey*); @@ -196,6 +206,8 @@ class AutocompleteEditViewGtk : public AutocompleteEditView, CHROMEGTK_CALLBACK_6(AutocompleteEditViewGtk, void, HandleDragDataReceived, GdkDragContext*, gint, gint, GtkSelectionData*, guint, guint); + CHROMEGTK_CALLBACK_4(AutocompleteEditViewGtk, void, HandleDragDataGet, + GdkDragContext*, GtkSelectionData*, guint, guint); CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleBackSpace); CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleCopyClipboard); CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleCutClipboard); @@ -206,6 +218,23 @@ class AutocompleteEditViewGtk : public AutocompleteEditView, HandleWidgetDirectionChanged, GtkTextDirection); CHROMEGTK_CALLBACK_2(AutocompleteEditViewGtk, void, HandleDeleteFromCursor, GtkDeleteType, gint); + // We connect to this so we can determine our toplevel window, so we can + // listen to focus change events on it. + CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, HandleHierarchyChanged, + GtkWidget*); +#if GTK_CHECK_VERSION(2, 20, 0) + CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, HandlePreeditChanged, + const gchar*); +#endif + // Undo/redo operations won't trigger "begin-user-action" and + // "end-user-action" signals, so we need to hook into "undo" and "redo" + // signals and call OnBeforePossibleChange()/OnAfterPossibleChange() by + // ourselves. + CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleUndoRedo); + CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleUndoRedoAfter); + + CHROMEG_CALLBACK_1(AutocompleteEditViewGtk, void, HandleWindowSetFocus, + GtkWindow*, GtkWidget*); // Callback for the PRIMARY selection clipboard. static void ClipboardGetSelectionThunk(GtkClipboard* clipboard, @@ -252,7 +281,7 @@ class AutocompleteEditViewGtk : public AutocompleteEditView, GtkTextIter* iter_max); // Return the number of characers in the current buffer. - int GetTextLength(); + int GetTextLength() const; // Try to parse the current text as a URL and colorize the components. void EmphasizeURLComponents(); @@ -286,6 +315,18 @@ class AutocompleteEditViewGtk : public AutocompleteEditView, // If the selected text parses as a URL OwnPrimarySelection is invoked. void UpdatePrimarySelectionIfValidURL(); + // Retrieves the first and last iterators in the |text_buffer_|, but excludes + // the anchor holding the |instant_view_| widget. + void GetTextBufferBounds(GtkTextIter* start, GtkTextIter* end) const; + + // Validates an iterator in the |text_buffer_|, to make sure it doesn't go + // beyond the anchor for holding the |instant_view_| widget. + void ValidateTextBufferIter(GtkTextIter* iter) const; + + // Adjusts vertical alignment of the |instant_view_| in the |text_view_|, to + // make sure they have the same baseline. + void AdjustVerticalAlignmentOfInstantView(); + // The widget we expose, used for vertically centering the real text edit, // since the height will change based on the font / font size, etc. OwnedWidgetGtk alignment_; @@ -300,6 +341,18 @@ class AutocompleteEditViewGtk : public AutocompleteEditView, GtkTextTag* security_error_scheme_tag_; GtkTextTag* normal_text_tag_; + // Objects for the instant suggestion text view. + GtkTextTag* instant_anchor_tag_; + + // A widget for displaying instant suggestion text. It'll be attached to a + // child anchor in the |text_buffer_| object. + GtkWidget* instant_view_; + + // A mark to split the content and the instant anchor. Wherever the end + // iterator of the text buffer is required, the iterator to this mark should + // be used. + GtkTextMark* instant_mark_; + scoped_ptr<AutocompleteEditModel> model_; scoped_ptr<AutocompletePopupView> popup_view_; AutocompleteEditController* controller_; @@ -409,6 +462,17 @@ class AutocompleteEditViewGtk : public AutocompleteEditView, // is not suggested text, that means the user manually made the selection. bool selection_suggested_; +#if GTK_CHECK_VERSION(2, 20, 0) + // Stores the text being composed by the input method. + std::wstring preedit_; +#endif + + // The view that is going to be focused next. Only valid while handling + // "focus-out" events. + GtkWidget* going_to_focus_; + + GtkSignalRegistrar signals_; + DISALLOW_COPY_AND_ASSIGN(AutocompleteEditViewGtk); }; diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm b/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm index d902b85..aa4b9a6 100644 --- a/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm +++ b/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm @@ -14,6 +14,7 @@ #include "base/sys_string_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/autocomplete/autocomplete_popup_view_mac.h" #include "chrome/browser/browser_process.h" diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_win.cc b/chrome/browser/autocomplete/autocomplete_edit_view_win.cc index 5f26caa..e853c85 100644 --- a/chrome/browser/autocomplete/autocomplete_edit_view_win.cc +++ b/chrome/browser/autocomplete/autocomplete_edit_view_win.cc @@ -29,8 +29,9 @@ #include "base/ref_counted.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/autocomplete/autocomplete_accessibility.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/autocomplete/keyword_provider.h" #include "chrome/browser/browser_process.h" diff --git a/chrome/browser/autocomplete/autocomplete_match.cc b/chrome/browser/autocomplete/autocomplete_match.cc new file mode 100644 index 0000000..57d57cd --- /dev/null +++ b/chrome/browser/autocomplete/autocomplete_match.cc @@ -0,0 +1,188 @@ +// 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 "base/logging.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" +#include "grit/theme_resources.h" + +// AutocompleteMatch ---------------------------------------------------------- + +AutocompleteMatch::AutocompleteMatch() + : provider(NULL), + relevance(0), + deletable(false), + inline_autocomplete_offset(std::wstring::npos), + transition(PageTransition::GENERATED), + is_history_what_you_typed_match(false), + type(SEARCH_WHAT_YOU_TYPED), + template_url(NULL), + starred(false) { +} + +AutocompleteMatch::AutocompleteMatch(AutocompleteProvider* provider, + int relevance, + bool deletable, + Type type) + : provider(provider), + relevance(relevance), + deletable(deletable), + inline_autocomplete_offset(std::wstring::npos), + transition(PageTransition::TYPED), + is_history_what_you_typed_match(false), + type(type), + template_url(NULL), + starred(false) { +} + +AutocompleteMatch::~AutocompleteMatch() { +} + +// static +std::string AutocompleteMatch::TypeToString(Type type) { + const char* strings[NUM_TYPES] = { + "url-what-you-typed", + "history-url", + "history-title", + "history-body", + "history-keyword", + "navsuggest", + "search-what-you-typed", + "search-history", + "search-suggest", + "search-other-engine", + "open-history-page", + }; + DCHECK(arraysize(strings) == NUM_TYPES); + return strings[type]; +} + +// static +int AutocompleteMatch::TypeToIcon(Type type) { + int icons[NUM_TYPES] = { + IDR_OMNIBOX_HTTP, + IDR_OMNIBOX_HTTP, + IDR_OMNIBOX_HISTORY, + IDR_OMNIBOX_HISTORY, + IDR_OMNIBOX_HISTORY, + IDR_OMNIBOX_HTTP, + IDR_OMNIBOX_SEARCH, + IDR_OMNIBOX_SEARCH, + IDR_OMNIBOX_SEARCH, + IDR_OMNIBOX_SEARCH, + IDR_OMNIBOX_MORE, + }; + DCHECK(arraysize(icons) == NUM_TYPES); + return icons[type]; +} + +// static +bool AutocompleteMatch::MoreRelevant(const AutocompleteMatch& elem1, + const AutocompleteMatch& elem2) { + // For equal-relevance matches, we sort alphabetically, so that providers + // who return multiple elements at the same priority get a "stable" sort + // across multiple updates. + if (elem1.relevance == elem2.relevance) + return elem1.contents > elem2.contents; + + // A negative relevance indicates the real relevance can be determined by + // negating the value. If both relevances are negative, negate the result + // so that we end up with positive relevances, then negative relevances with + // the negative relevances sorted by absolute values. + const bool result = elem1.relevance > elem2.relevance; + return (elem1.relevance < 0 && elem2.relevance < 0) ? !result : result; +} + +// static +bool AutocompleteMatch::DestinationSortFunc(const AutocompleteMatch& elem1, + const AutocompleteMatch& elem2) { + // Sort identical destination_urls together. Place the most relevant matches + // first, so that when we call std::unique(), these are the ones that get + // preserved. + return (elem1.destination_url != elem2.destination_url) ? + (elem1.destination_url < elem2.destination_url) : + MoreRelevant(elem1, elem2); +} + +// static +bool AutocompleteMatch::DestinationsEqual(const AutocompleteMatch& elem1, + const AutocompleteMatch& elem2) { + return elem1.destination_url == elem2.destination_url; +} + +// static +void AutocompleteMatch::ClassifyMatchInString( + const std::wstring& find_text, + const std::wstring& text, + int style, + ACMatchClassifications* classification) { + ClassifyLocationInString(text.find(find_text), find_text.length(), + text.length(), style, classification); +} + +void AutocompleteMatch::ClassifyLocationInString( + size_t match_location, + size_t match_length, + size_t overall_length, + int style, + ACMatchClassifications* classification) { + classification->clear(); + + // Don't classify anything about an empty string + // (AutocompleteMatch::Validate() checks this). + if (overall_length == 0) + return; + + // Mark pre-match portion of string (if any). + if (match_location != 0) { + classification->push_back(ACMatchClassification(0, style)); + } + + // Mark matching portion of string. + if (match_location == std::wstring::npos) { + // No match, above classification will suffice for whole string. + return; + } + // Classifying an empty match makes no sense and will lead to validation + // errors later. + DCHECK(match_length > 0); + classification->push_back(ACMatchClassification(match_location, + (style | ACMatchClassification::MATCH) & ~ACMatchClassification::DIM)); + + // Mark post-match portion of string (if any). + const size_t after_match(match_location + match_length); + if (after_match < overall_length) { + classification->push_back(ACMatchClassification(after_match, style)); + } +} + +#ifndef NDEBUG +void AutocompleteMatch::Validate() const { + ValidateClassifications(contents, contents_class); + ValidateClassifications(description, description_class); +} + +void AutocompleteMatch::ValidateClassifications( + const std::wstring& text, + const ACMatchClassifications& classifications) const { + if (text.empty()) { + DCHECK(classifications.size() == 0); + return; + } + + // The classifications should always cover the whole string. + DCHECK(classifications.size() > 0) << "No classification for text"; + DCHECK(classifications[0].offset == 0) << "Classification misses beginning"; + if (classifications.size() == 1) + return; + + // The classifications should always be sorted. + size_t last_offset = classifications[0].offset; + for (ACMatchClassifications::const_iterator i(classifications.begin() + 1); + i != classifications.end(); ++i) { + DCHECK(i->offset > last_offset) << "Classification unsorted"; + DCHECK(i->offset < text.length()) << "Classification out of bounds"; + last_offset = i->offset; + } +} +#endif diff --git a/chrome/browser/autocomplete/autocomplete_match.h b/chrome/browser/autocomplete/autocomplete_match.h new file mode 100644 index 0000000..4ad4a6e --- /dev/null +++ b/chrome/browser/autocomplete/autocomplete_match.h @@ -0,0 +1,208 @@ +// 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_AUTOCOMPLETE_AUTOCOMPLETE_MATCH_H_ +#define CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_MATCH_H_ +#pragma once + +#include <vector> +#include <string> + +#include "chrome/common/page_transition_types.h" +#include "googleurl/src/gurl.h" + +class AutocompleteProvider; +class PageTransition; +class TemplateURL; + +// AutocompleteMatch ---------------------------------------------------------- + +// A single result line with classified spans. The autocomplete popup displays +// the 'contents' and the 'description' (the description is optional) in the +// autocomplete dropdown, and fills in 'fill_into_edit' into the textbox when +// that line is selected. fill_into_edit may be the same as 'description' for +// things like URLs, but may be different for searches or other providers. For +// example, a search result may say "Search for asdf" as the description, but +// "asdf" should appear in the box. +struct AutocompleteMatch { + // Autocomplete matches contain strings that are classified according to a + // separate vector of styles. This vector associates flags with particular + // string segments, and must be in sorted order. All text must be associated + // with some kind of classification. Even if a match has no distinct + // segments, its vector should contain an entry at offset 0 with no flags. + // + // Example: The user typed "goog" + // http://www.google.com/ Google + // ^ ^ ^ ^ ^ + // 0, | 15, | 4, + // 11,match 0,match + // + // This structure holds the classification information for each span. + struct ACMatchClassification { + // The values in here are not mutually exclusive -- use them like a + // bitfield. This also means we use "int" instead of this enum type when + // passing the values around, so the compiler doesn't complain. + enum Style { + NONE = 0, + URL = 1 << 0, // A URL + MATCH = 1 << 1, // A match for the user's search term + DIM = 1 << 2, // "Helper text" + }; + + ACMatchClassification(size_t offset, int style) + : offset(offset), + style(style) { + } + + // Offset within the string that this classification starts + size_t offset; + + int style; + }; + + typedef std::vector<ACMatchClassification> ACMatchClassifications; + + // The type of this match. + enum Type { + URL_WHAT_YOU_TYPED = 0, // The input as a URL. + HISTORY_URL, // A past page whose URL contains the input. + HISTORY_TITLE, // A past page whose title contains the input. + HISTORY_BODY, // A past page whose body contains the input. + HISTORY_KEYWORD, // A past page whose keyword contains the input. + NAVSUGGEST, // A suggested URL. + SEARCH_WHAT_YOU_TYPED, // The input as a search query (with the default + // engine). + SEARCH_HISTORY, // A past search (with the default engine) + // containing the input. + SEARCH_SUGGEST, // A suggested search (with the default engine). + SEARCH_OTHER_ENGINE, // A search with a non-default engine. + OPEN_HISTORY_PAGE, // A synthetic result that opens the history page + // to search for the input. + NUM_TYPES, + }; + + AutocompleteMatch(); + AutocompleteMatch(AutocompleteProvider* provider, + int relevance, + bool deletable, + Type type); + ~AutocompleteMatch(); + + // Converts |type| to a string representation. Used in logging. + static std::string TypeToString(Type type); + + // Converts |type| to a resource identifier for the appropriate icon for this + // type. + static int TypeToIcon(Type type); + + // Comparison function for determining when one match is better than another. + static bool MoreRelevant(const AutocompleteMatch& elem1, + const AutocompleteMatch& elem2); + + // Comparison functions for removing matches with duplicate destinations. + static bool DestinationSortFunc(const AutocompleteMatch& elem1, + const AutocompleteMatch& elem2); + static bool DestinationsEqual(const AutocompleteMatch& elem1, + const AutocompleteMatch& elem2); + + // Helper functions for classes creating matches: + // Fills in the classifications for |text|, using |style| as the base style + // and marking the first instance of |find_text| as a match. (This match + // will also not be dimmed, if |style| has DIM set.) + static void ClassifyMatchInString(const std::wstring& find_text, + const std::wstring& text, + int style, + ACMatchClassifications* classifications); + + // Similar to ClassifyMatchInString(), but for cases where the range to mark + // as matching is already known (avoids calling find()). This can be helpful + // when find() would be misleading (e.g. you want to mark the second match in + // a string instead of the first). + static void ClassifyLocationInString(size_t match_location, + size_t match_length, + size_t overall_length, + int style, + ACMatchClassifications* classifications); + + // The provider of this match, used to remember which provider the user had + // selected when the input changes. This may be NULL, in which case there is + // no provider (or memory of the user's selection). + AutocompleteProvider* provider; + + // The relevance of this match. See table above for scores returned by + // various providers. This is used to rank matches among all responding + // providers, so different providers must be carefully tuned to supply + // matches with appropriate relevance. + // + // If the relevance is negative, it will only be displayed if there are not + // enough non-negative items in all the providers to max out the popup. In + // this case, the relevance of the additional items will be inverted so they + // can be mixed in with the rest of the relevances. This allows a provider + // to group its matches, having the added items appear intermixed with its + // other matches. + // + // TODO(pkasting): http://b/1111299 This should be calculated algorithmically, + // rather than being a fairly fixed value defined by the table above. + int relevance; + + // True if the user should be able to delete this match. + bool deletable; + + // This string is loaded into the location bar when the item is selected + // by pressing the arrow keys. This may be different than a URL, for example, + // for search suggestions, this would just be the search terms. + std::wstring fill_into_edit; + + // The position within fill_into_edit from which we'll display the inline + // autocomplete string. This will be std::wstring::npos if this match should + // not be inline autocompleted. + size_t inline_autocomplete_offset; + + // The URL to actually load when the autocomplete item is selected. This URL + // should be canonical so we can compare URLs with strcmp to avoid dupes. + // It may be empty if there is no possible navigation. + GURL destination_url; + + // The main text displayed in the address bar dropdown. + std::wstring contents; + ACMatchClassifications contents_class; + + // Additional helper text for each entry, such as a title or description. + std::wstring description; + ACMatchClassifications description_class; + + // The transition type to use when the user opens this match. By default + // this is TYPED. Providers whose matches do not look like URLs should set + // it to GENERATED. + PageTransition::Type transition; + + // True when this match is the "what you typed" match from the history + // system. + bool is_history_what_you_typed_match; + + // Type of this match. + Type type; + + // If this match corresponds to a keyword, this is the TemplateURL the + // keyword was obtained from. + const TemplateURL* template_url; + + // True if the user has starred the destination URL. + bool starred; + +#ifndef NDEBUG + // Does a data integrity check on this match. + void Validate() const; + + // Checks one text/classifications pair for valid values. + void ValidateClassifications( + const std::wstring& text, + const ACMatchClassifications& classifications) const; +#endif +}; + +typedef AutocompleteMatch::ACMatchClassification ACMatchClassification; +typedef std::vector<ACMatchClassification> ACMatchClassifications; + +#endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_MATCH_H_ diff --git a/chrome/browser/autocomplete/autocomplete_popup_model.cc b/chrome/browser/autocomplete/autocomplete_popup_model.cc index a92d0d8..a14d19f 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_model.cc +++ b/chrome/browser/autocomplete/autocomplete_popup_model.cc @@ -8,6 +8,7 @@ #include "base/string_util.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_view.h" #include "chrome/browser/profile.h" #include "chrome/browser/extensions/extensions_service.h" @@ -220,6 +221,15 @@ bool AutocompletePopupModel::GetKeywordForMatch(const AutocompleteMatch& match, if (!TemplateURL::SupportsReplacement(template_url)) return false; + // Don't provide a hint if this is an extension keyword not enabled for + // incognito mode (and if this is an incognito profile). + if (template_url->IsExtensionKeyword() && profile_->IsOffTheRecord()) { + const Extension* extension = profile_->GetExtensionsService()-> + GetExtensionById(template_url->GetExtensionId(), false); + if (!profile_->GetExtensionsService()->IsIncognitoEnabled(extension)) + return false; + } + keyword->assign(keyword_hint); return true; } @@ -292,6 +302,7 @@ void AutocompletePopupModel::Observe(NotificationType type, SetHoveredLine(kNoMatch); view_->UpdatePopupAppearance(); + edit_model_->ResultsUpdated(); edit_model_->PopupBoundsChangedTo(view_->GetTargetBounds()); } diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_gtk.cc b/chrome/browser/autocomplete/autocomplete_popup_view_gtk.cc index aaf7e05..088c9ad 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_view_gtk.cc +++ b/chrome/browser/autocomplete/autocomplete_popup_view_gtk.cc @@ -17,6 +17,7 @@ #include "chrome/browser/autocomplete/autocomplete.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/defaults.h" #include "chrome/browser/gtk/gtk_theme_provider.h" @@ -109,9 +110,68 @@ size_t GetUTF8Offset(const std::wstring& wide_text, size_t wide_text_offset) { return WideToUTF8(wide_text.substr(0, wide_text_offset)).size(); } -void SetupLayoutForMatch(PangoLayout* layout, +// Generates the normal URL color, a green color used in unhighlighted URL +// text. It is a mix of |kURLTextColor| and the current text color. Unlike the +// selected text color, it is more important to match the qualities of the +// foreground typeface color instead of taking the background into account. +GdkColor NormalURLColor(GdkColor foreground) { + color_utils::HSL fg_hsl; + color_utils::SkColorToHSL(gfx::GdkColorToSkColor(foreground), &fg_hsl); + + color_utils::HSL hue_hsl; + color_utils::SkColorToHSL(gfx::GdkColorToSkColor(kURLTextColor), &hue_hsl); + + // Only allow colors that have a fair amount of saturation in them (color vs + // white). This means that our output color will always be fairly green. + double s = std::max(0.5, fg_hsl.s); + + // Make sure the luminance is at least as bright as the |kURLTextColor| green + // would be if we were to use that. + double l; + if (fg_hsl.l < hue_hsl.l) + l = hue_hsl.l; + else + l = (fg_hsl.l + hue_hsl.l) / 2; + + color_utils::HSL output = { hue_hsl.h, s, l }; + return gfx::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255)); +} + +// Generates the selected URL color, a green color used on URL text in the +// currently highlighted entry in the autocomplete popup. It's a mix of +// |kURLTextColor|, the current text color, and the background color (the +// select highlight). It is more important to contrast with the background +// saturation than to look exactly like the foreground color. +GdkColor SelectedURLColor(GdkColor foreground, GdkColor background) { + color_utils::HSL fg_hsl; + color_utils::SkColorToHSL(gfx::GdkColorToSkColor(foreground), &fg_hsl); + + color_utils::HSL bg_hsl; + color_utils::SkColorToHSL(gfx::GdkColorToSkColor(background), &bg_hsl); + + color_utils::HSL hue_hsl; + color_utils::SkColorToHSL(gfx::GdkColorToSkColor(kURLTextColor), &hue_hsl); + + // The saturation of the text should be opposite of the background, clamped + // to 0.2-0.8. We make sure it's greater than 0.2 so there's some color, but + // less than 0.8 so it's not the oversaturated neon-color. + double opposite_s = 1 - bg_hsl.s; + double s = std::max(0.2, std::min(0.8, opposite_s)); + + // The luminance should match the luminance of the foreground text. Again, + // we clamp so as to have at some amount of color (green) in the text. + double opposite_l = fg_hsl.l; + double l = std::max(0.1, std::min(0.9, opposite_l)); + + color_utils::HSL output = { hue_hsl.h, s, l }; + return gfx::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255)); +} +} // namespace + +void AutocompletePopupViewGtk::SetupLayoutForMatch( + PangoLayout* layout, const std::wstring& text, - AutocompleteMatch::ACMatchClassifications classifications, + const AutocompleteMatch::ACMatchClassifications& classifications, const GdkColor* base_color, const GdkColor* dim_color, const GdkColor* url_color, @@ -195,65 +255,6 @@ void SetupLayoutForMatch(PangoLayout* layout, pango_attr_list_unref(attrs); } -// Generates the normal URL color, a green color used in unhighlighted URL -// text. It is a mix of |kURLTextColor| and the current text color. Unlike the -// selected text color, It is more important to match the qualities of the -// foreground typeface color instead of taking the background into account. -GdkColor NormalURLColor(GdkColor foreground) { - color_utils::HSL fg_hsl; - color_utils::SkColorToHSL(gfx::GdkColorToSkColor(foreground), &fg_hsl); - - color_utils::HSL hue_hsl; - color_utils::SkColorToHSL(gfx::GdkColorToSkColor(kURLTextColor), &hue_hsl); - - // Only allow colors that have a fair amount of saturation in them (color vs - // white). This means that our output color will always be fairly green. - double s = std::max(0.5, fg_hsl.s); - - // Make sure the luminance is at least as bright as the |kURLTextColor| green - // would be if we were to use that. - double l; - if (fg_hsl.l < hue_hsl.l) - l = hue_hsl.l; - else - l = (fg_hsl.l + hue_hsl.l) / 2; - - color_utils::HSL output = { hue_hsl.h, s, l }; - return gfx::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255)); -} - -// Generates the selected URL color, a green color used on URL text in the -// currently highlighted entry in the autocomplete popup. It's a mix of -// |kURLTextColor|, the current text color, and the background color (the -// select highlight). It is more important to contrast with the background -// saturation than to look exactly like the foreground color. -GdkColor SelectedURLColor(GdkColor foreground, GdkColor background) { - color_utils::HSL fg_hsl; - color_utils::SkColorToHSL(gfx::GdkColorToSkColor(foreground), &fg_hsl); - - color_utils::HSL bg_hsl; - color_utils::SkColorToHSL(gfx::GdkColorToSkColor(background), &bg_hsl); - - color_utils::HSL hue_hsl; - color_utils::SkColorToHSL(gfx::GdkColorToSkColor(kURLTextColor), &hue_hsl); - - // The saturation of the text should be opposite of the background, clamped - // to 0.2-0.8. We make sure it's greater than 0.2 so there's some color, but - // less than 0.8 so it's not the oversaturated neon-color. - double opposite_s = 1 - bg_hsl.s; - double s = std::max(0.2, std::min(0.8, opposite_s)); - - // The luminance should match the luminance of the foreground text. Again, - // we clamp so as to have at some amount of color (green) in the text. - double opposite_l = fg_hsl.l; - double l = std::max(0.1, std::min(0.9, opposite_l)); - - color_utils::HSL output = { hue_hsl.h, s, l }; - return gfx::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255)); -} - -} // namespace - AutocompletePopupViewGtk::AutocompletePopupViewGtk( AutocompleteEditView* edit_view, AutocompleteEditModel* edit_model, @@ -356,9 +357,20 @@ void AutocompletePopupViewGtk::UpdatePopupAppearance() { } gfx::Rect AutocompletePopupViewGtk::GetTargetBounds() { - return gfx::Rect(); -} + if (!GTK_WIDGET_REALIZED(window_)) + return gfx::Rect(); + gfx::Rect retval = gtk_util::GetWidgetScreenBounds(window_); + + // The widget bounds don't update synchronously so may be out of sync with + // our last size request. + GtkRequisition req; + gtk_widget_size_request(window_, &req); + retval.set_width(req.width); + retval.set_height(req.height); + + return retval; +} void AutocompletePopupViewGtk::PaintUpdatesNow() { // Paint our queued invalidations now, synchronously. diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_gtk.h b/chrome/browser/autocomplete/autocomplete_popup_view_gtk.h index 89bd697..590cc41 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_view_gtk.h +++ b/chrome/browser/autocomplete/autocomplete_popup_view_gtk.h @@ -8,9 +8,11 @@ #include <gtk/gtk.h> #include <map> +#include <string> #include "base/basictypes.h" #include "base/scoped_ptr.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_view.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" @@ -18,7 +20,6 @@ class AutocompleteEditModel; class AutocompleteEditView; -struct AutocompleteMatch; class AutocompletePopupModel; class GtkThemeProvider; class Profile; @@ -48,6 +49,17 @@ class AutocompletePopupViewGtk : public AutocompletePopupView, const NotificationDetails& details); private: + // Be friendly for unit tests. + friend class AutocompletePopupViewGtkTest; + static void SetupLayoutForMatch( + PangoLayout* layout, + const std::wstring& text, + const AutocompleteMatch::ACMatchClassifications& classifications, + const GdkColor* base_color, + const GdkColor* dim_color, + const GdkColor* url_color, + const std::string& prefix_text); + void Show(size_t num_results); void Hide(); diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_gtk_unittest.cc b/chrome/browser/autocomplete/autocomplete_popup_view_gtk_unittest.cc new file mode 100644 index 0000000..dd84f26 --- /dev/null +++ b/chrome/browser/autocomplete/autocomplete_popup_view_gtk_unittest.cc @@ -0,0 +1,418 @@ +// 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/autocomplete/autocomplete_popup_view_gtk.h" + +#include <gtk/gtk.h> + +#include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" +#include "chrome/browser/gtk/gtk_util.h" +#include "testing/platform_test.h" + +namespace { + +static const float kLargeWidth = 10000; + +const GdkColor kContentTextColor = GDK_COLOR_RGB(0x00, 0x00, 0x00); +const GdkColor kDimContentTextColor = GDK_COLOR_RGB(0x80, 0x80, 0x80); +const GdkColor kURLTextColor = GDK_COLOR_RGB(0x00, 0x88, 0x00); + +} // namespace + +class AutocompletePopupViewGtkTest : public PlatformTest { + public: + AutocompletePopupViewGtkTest() { } + + virtual void SetUp() { + PlatformTest::SetUp(); + + window_ = gtk_window_new(GTK_WINDOW_POPUP); + layout_ = gtk_widget_create_pango_layout(window_, NULL); + } + + virtual void TearDown() { + g_object_unref(layout_); + gtk_widget_destroy(window_); + + PlatformTest::TearDown(); + } + + // The google C++ Testing Framework documentation suggests making + // accessors in the fixture so that each test doesn't need to be a + // friend of the class being tested. This method just proxies the + // call through after adding the fixture's layout_. + void SetupLayoutForMatch( + const std::wstring& text, + const AutocompleteMatch::ACMatchClassifications& classifications, + const GdkColor* base_color, + const GdkColor* dim_color, + const GdkColor* url_color, + const std::string& prefix_text) { + AutocompletePopupViewGtk::SetupLayoutForMatch(layout_, + text, + classifications, + base_color, + dim_color, + url_color, + prefix_text); + } + + struct RunInfo { + PangoAttribute* attr_; + guint length_; + RunInfo() : attr_(NULL), length_(0) { } + }; + + RunInfo RunInfoForAttrType(guint location, + guint end_location, + PangoAttrType type) { + RunInfo retval; + + PangoAttrList* attrs = pango_layout_get_attributes(layout_); + if (!attrs) + return retval; + + PangoAttrIterator* attr_iter = pango_attr_list_get_iterator(attrs); + if (!attr_iter) + return retval; + + for (gboolean more = true, findNextStart = false; + more; + more = pango_attr_iterator_next(attr_iter)) { + PangoAttribute* attr = pango_attr_iterator_get(attr_iter, type); + + // This iterator segment doesn't have any elements of the + // desired type; keep looking. + if (!attr) + continue; + + // Skip attribute ranges before the desired start point. + if (attr->end_index <= location) + continue; + + // If the matching type went past the iterator segment, then set + // the length to the next start - location. + if (findNextStart) { + // If the start is still less than the location, then reset + // the match. Otherwise, check that the new attribute is, in + // fact different before shortening the run length. + if (attr->start_index <= location) { + findNextStart = false; + } else if (!pango_attribute_equal(retval.attr_, attr)) { + retval.length_ = attr->start_index - location; + break; + } + } + + gint start_range, end_range; + pango_attr_iterator_range(attr_iter, + &start_range, + &end_range); + + // Now we have a match. May need to keep going to shorten + // length if we reach a new item of the same type. + retval.attr_ = attr; + if (attr->end_index > (guint)end_range) { + retval.length_ = end_location - location; + findNextStart = true; + } else { + retval.length_ = attr->end_index - location; + break; + } + } + + pango_attr_iterator_destroy(attr_iter); + return retval; + } + + guint RunLengthForAttrType(guint location, + guint end_location, + PangoAttrType type) { + RunInfo info = RunInfoForAttrType(location, + end_location, + type); + return info.length_; + } + + gboolean RunHasAttribute(guint location, + guint end_location, + PangoAttribute* attribute) { + RunInfo info = RunInfoForAttrType(location, + end_location, + attribute->klass->type); + + return info.attr_ && pango_attribute_equal(info.attr_, attribute); + } + + gboolean RunHasColor(guint location, + guint end_location, + const GdkColor& color) { + PangoAttribute* attribute = + pango_attr_foreground_new(color.red, + color.green, + color.blue); + + gboolean retval = RunHasAttribute(location, + end_location, + attribute); + + pango_attribute_destroy(attribute); + + return retval; + } + + gboolean RunHasWeight(guint location, + guint end_location, + PangoWeight weight) { + PangoAttribute* attribute = pango_attr_weight_new(weight); + + gboolean retval = RunHasAttribute(location, + end_location, + attribute); + + pango_attribute_destroy(attribute); + + return retval; + } + + GtkWidget* window_; + PangoLayout* layout_; + + private: + DISALLOW_COPY_AND_ASSIGN(AutocompletePopupViewGtkTest); +}; + +// Simple inputs with no matches should result in styled output who's +// text matches the input string, with the passed-in color, and +// nothing bolded. +TEST_F(AutocompletePopupViewGtkTest, DecorateMatchedStringNoMatch) { + const std::wstring kContents = L"This is a test"; + + AutocompleteMatch::ACMatchClassifications classifications; + + SetupLayoutForMatch(kContents, + classifications, + &kContentTextColor, + &kDimContentTextColor, + &kURLTextColor, + std::string()); + + EXPECT_EQ(kContents.size(), + RunLengthForAttrType(0U, + kContents.size(), + PANGO_ATTR_FOREGROUND)); + + EXPECT_TRUE(RunHasColor(0U, + kContents.size(), + kContentTextColor)); + + // This part's a little wacky - either we don't have a weight, or + // the weight run is the entire string and is NORMAL + guint weightLength = RunLengthForAttrType(0U, + kContents.size(), + PANGO_ATTR_WEIGHT); + if (weightLength) { + EXPECT_EQ(kContents.size(), weightLength); + EXPECT_TRUE(RunHasWeight(0U, + kContents.size(), + PANGO_WEIGHT_NORMAL)); + } +} + +// Identical to DecorateMatchedStringNoMatch, except test that URL +// style gets a different color than we passed in. +TEST_F(AutocompletePopupViewGtkTest, DecorateMatchedStringURLNoMatch) { + const std::wstring kContents = L"This is a test"; + AutocompleteMatch::ACMatchClassifications classifications; + + classifications.push_back( + ACMatchClassification(0U, ACMatchClassification::URL)); + + SetupLayoutForMatch(kContents, + classifications, + &kContentTextColor, + &kDimContentTextColor, + &kURLTextColor, + std::string()); + + EXPECT_EQ(kContents.size(), + RunLengthForAttrType(0U, + kContents.size(), + PANGO_ATTR_FOREGROUND)); + EXPECT_TRUE(RunHasColor(0U, + kContents.size(), + kURLTextColor)); + + // This part's a little wacky - either we don't have a weight, or + // the weight run is the entire string and is NORMAL + guint weightLength = RunLengthForAttrType(0U, + kContents.size(), + PANGO_ATTR_WEIGHT); + if (weightLength) { + EXPECT_EQ(kContents.size(), weightLength); + EXPECT_TRUE(RunHasWeight(0U, + kContents.size(), + PANGO_WEIGHT_NORMAL)); + } +} + +// Test that DIM works as expected. +TEST_F(AutocompletePopupViewGtkTest, DecorateMatchedStringDimNoMatch) { + const std::wstring kContents = L"This is a test"; + // Dim "is". + const guint runLength1 = 5, runLength2 = 2, runLength3 = 7; + // Make sure nobody messed up the inputs. + EXPECT_EQ(runLength1 + runLength2 + runLength3, kContents.size()); + + // Push each run onto classifications. + AutocompleteMatch::ACMatchClassifications classifications; + classifications.push_back( + ACMatchClassification(0U, ACMatchClassification::NONE)); + classifications.push_back( + ACMatchClassification(runLength1, ACMatchClassification::DIM)); + classifications.push_back( + ACMatchClassification(runLength1 + runLength2, + ACMatchClassification::NONE)); + + SetupLayoutForMatch(kContents, + classifications, + &kContentTextColor, + &kDimContentTextColor, + &kURLTextColor, + std::string()); + + // Check the runs have expected color and length. + EXPECT_EQ(runLength1, + RunLengthForAttrType(0U, + kContents.size(), + PANGO_ATTR_FOREGROUND)); + EXPECT_TRUE(RunHasColor(0U, + kContents.size(), + kContentTextColor)); + EXPECT_EQ(runLength2, + RunLengthForAttrType(runLength1, + kContents.size(), + PANGO_ATTR_FOREGROUND)); + EXPECT_TRUE(RunHasColor(runLength1, + kContents.size(), + kDimContentTextColor)); + EXPECT_EQ(runLength3, + RunLengthForAttrType(runLength1 + runLength2, + kContents.size(), + PANGO_ATTR_FOREGROUND)); + EXPECT_TRUE(RunHasColor(runLength1 + runLength2, + kContents.size(), + kContentTextColor)); + + // This part's a little wacky - either we don't have a weight, or + // the weight run is the entire string and is NORMAL + guint weightLength = RunLengthForAttrType(0U, + kContents.size(), + PANGO_ATTR_WEIGHT); + if (weightLength) { + EXPECT_EQ(kContents.size(), weightLength); + EXPECT_TRUE(RunHasWeight(0U, + kContents.size(), + PANGO_WEIGHT_NORMAL)); + } +} + +// Test that the matched run gets bold-faced, but keeps the same +// color. +TEST_F(AutocompletePopupViewGtkTest, DecorateMatchedStringMatch) { + const std::wstring kContents = L"This is a test"; + // Match "is". + const guint runLength1 = 5, runLength2 = 2, runLength3 = 7; + // Make sure nobody messed up the inputs. + EXPECT_EQ(runLength1 + runLength2 + runLength3, kContents.size()); + + // Push each run onto classifications. + AutocompleteMatch::ACMatchClassifications classifications; + classifications.push_back( + ACMatchClassification(0U, ACMatchClassification::NONE)); + classifications.push_back( + ACMatchClassification(runLength1, ACMatchClassification::MATCH)); + classifications.push_back( + ACMatchClassification(runLength1 + runLength2, + ACMatchClassification::NONE)); + + SetupLayoutForMatch(kContents, + classifications, + &kContentTextColor, + &kDimContentTextColor, + &kURLTextColor, + std::string()); + + // Check the runs have expected weight and length. + EXPECT_EQ(runLength1, + RunLengthForAttrType(0U, + kContents.size(), + PANGO_ATTR_WEIGHT)); + EXPECT_TRUE(RunHasWeight(0U, + kContents.size(), + PANGO_WEIGHT_NORMAL)); + EXPECT_EQ(runLength2, + RunLengthForAttrType(runLength1, + kContents.size(), + PANGO_ATTR_WEIGHT)); + EXPECT_TRUE(RunHasWeight(runLength1, + kContents.size(), + PANGO_WEIGHT_BOLD)); + EXPECT_EQ(runLength3, + RunLengthForAttrType(runLength1 + runLength2, + kContents.size(), + PANGO_ATTR_WEIGHT)); + EXPECT_TRUE(RunHasWeight(runLength1 + runLength2, + kContents.size(), + PANGO_WEIGHT_NORMAL)); + + // The entire string should be the same, normal color. + EXPECT_EQ(kContents.size(), + RunLengthForAttrType(0U, + kContents.size(), + PANGO_ATTR_FOREGROUND)); + EXPECT_TRUE(RunHasColor(0U, + kContents.size(), + kContentTextColor)); +} + +// Just like DecorateMatchedStringURLMatch, this time with URL style. +TEST_F(AutocompletePopupViewGtkTest, DecorateMatchedStringURLMatch) { + const std::wstring kContents = L"http://hello.world/"; + // Match "hello". + const guint runLength1 = 7, runLength2 = 5, runLength3 = 7; + // Make sure nobody messed up the inputs. + EXPECT_EQ(runLength1 + runLength2 + runLength3, kContents.size()); + + // Push each run onto classifications. + AutocompleteMatch::ACMatchClassifications classifications; + classifications.push_back( + ACMatchClassification(0U, ACMatchClassification::URL)); + const int kURLMatch = + ACMatchClassification::URL | ACMatchClassification::MATCH; + classifications.push_back( + ACMatchClassification(runLength1, + kURLMatch)); + classifications.push_back( + ACMatchClassification(runLength1 + runLength2, + ACMatchClassification::URL)); + + SetupLayoutForMatch(kContents, + classifications, + &kContentTextColor, + &kDimContentTextColor, + &kURLTextColor, + std::string()); + + // One color for the entire string, and it's not the one we passed + // in. + EXPECT_EQ(kContents.size(), + RunLengthForAttrType(0U, + kContents.size(), + PANGO_ATTR_FOREGROUND)); + EXPECT_TRUE(RunHasColor(0U, + kContents.size(), + kURLTextColor)); +} diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_mac.h b/chrome/browser/autocomplete/autocomplete_popup_view_mac.h index 70a1ba4..20af4f0 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_view_mac.h +++ b/chrome/browser/autocomplete/autocomplete_popup_view_mac.h @@ -14,6 +14,7 @@ #include "base/scoped_ptr.h" #include "base/scoped_nsobject.h" #include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_view.h" #include "gfx/font.h" #include "webkit/glue/window_open_disposition.h" diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm b/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm index 1e4f42f..6f26248 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm +++ b/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm @@ -13,6 +13,7 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/cocoa/event_utils.h" #include "chrome/browser/cocoa/image_utils.h" diff --git a/chrome/browser/autocomplete/autocomplete_unittest.cc b/chrome/browser/autocomplete/autocomplete_unittest.cc index fd0ebda..d78611c 100644 --- a/chrome/browser/autocomplete/autocomplete_unittest.cc +++ b/chrome/browser/autocomplete/autocomplete_unittest.cc @@ -8,6 +8,7 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" diff --git a/chrome/browser/autocomplete/history_contents_provider.cc b/chrome/browser/autocomplete/history_contents_provider.cc index 06e46d2..1d0c828 100644 --- a/chrome/browser/autocomplete/history_contents_provider.cc +++ b/chrome/browser/autocomplete/history_contents_provider.cc @@ -8,6 +8,7 @@ #include "base/metrics/histogram.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/bookmarks/bookmark_utils.h" #include "chrome/browser/history/query_parser.h" diff --git a/chrome/browser/autocomplete/history_contents_provider_unittest.cc b/chrome/browser/autocomplete/history_contents_provider_unittest.cc index 4a76f26..9f1119b 100644 --- a/chrome/browser/autocomplete/history_contents_provider_unittest.cc +++ b/chrome/browser/autocomplete/history_contents_provider_unittest.cc @@ -7,6 +7,7 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/history_contents_provider.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser_thread.h" diff --git a/chrome/browser/autocomplete/history_quick_provider.cc b/chrome/browser/autocomplete/history_quick_provider.cc index 04d1c7e..236c1c4 100644 --- a/chrome/browser/autocomplete/history_quick_provider.cc +++ b/chrome/browser/autocomplete/history_quick_provider.cc @@ -9,6 +9,7 @@ #include "base/string_util.h" #include "base/logging.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/history/history.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" @@ -192,4 +193,3 @@ int HistoryQuickProvider::CalculateRelevance(int raw_score, return 900 + static_cast<int>(match_number); } } - diff --git a/chrome/browser/autocomplete/history_quick_provider_unittest.cc b/chrome/browser/autocomplete/history_quick_provider_unittest.cc index 4344a2e..6de941a 100644 --- a/chrome/browser/autocomplete/history_quick_provider_unittest.cc +++ b/chrome/browser/autocomplete/history_quick_provider_unittest.cc @@ -13,6 +13,7 @@ #include "base/message_loop.h" #include "base/scoped_ptr.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/history/history.h" #include "chrome/browser/history/url_database.h" diff --git a/chrome/browser/autocomplete/history_url_provider.cc b/chrome/browser/autocomplete/history_url_provider.cc index 30d63fb..fd5b8fb 100644 --- a/chrome/browser/autocomplete/history_url_provider.cc +++ b/chrome/browser/autocomplete/history_url_provider.cc @@ -11,6 +11,7 @@ #include "base/metrics/histogram.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/history/history.h" #include "chrome/browser/history/history_backend.h" #include "chrome/browser/history/history_database.h" @@ -242,7 +243,7 @@ void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend, // give us far more than enough to work with. CullRedirects() will then // reduce the list to the best kMaxMatches results. db->AutocompleteForPrefix(WideToUTF16(i->prefix + params->input.text()), - kMaxMatches * 2, &url_matches); + kMaxMatches * 2, (backend == NULL), &url_matches); for (URLRowVector::const_iterator j(url_matches.begin()); j != url_matches.end(); ++j) { const Prefix* best_prefix = BestPrefix(j->url(), std::wstring()); diff --git a/chrome/browser/autocomplete/history_url_provider_unittest.cc b/chrome/browser/autocomplete/history_url_provider_unittest.cc index f330798..19f3053 100644 --- a/chrome/browser/autocomplete/history_url_provider_unittest.cc +++ b/chrome/browser/autocomplete/history_url_provider_unittest.cc @@ -7,6 +7,7 @@ #include "base/path_service.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/history_url_provider.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/history/history.h" diff --git a/chrome/browser/autocomplete/keyword_provider.cc b/chrome/browser/autocomplete/keyword_provider.cc index 4f3ee5e..af9c125 100644 --- a/chrome/browser/autocomplete/keyword_provider.cc +++ b/chrome/browser/autocomplete/keyword_provider.cc @@ -10,6 +10,7 @@ #include "app/l10n_util.h" #include "base/string16.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/extensions/extension_omnibox_api.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/profile.h" @@ -167,6 +168,26 @@ void KeywordProvider::Start(const AutocompleteInput& input, std::vector<std::wstring> keyword_matches; model->FindMatchingKeywords(keyword, !remaining_input.empty(), &keyword_matches); + + // Prune any extension keywords that are disallowed in incognito mode (if + // we're incognito), or disabled. + for (std::vector<std::wstring>::iterator i(keyword_matches.begin()); + i != keyword_matches.end(); ) { + const TemplateURL* template_url(model->GetTemplateURLForKeyword(*i)); + if (profile_ && + !input.synchronous_only() && template_url->IsExtensionKeyword()) { + ExtensionsService* service = profile_->GetExtensionsService(); + const Extension* extension = service->GetExtensionById( + template_url->GetExtensionId(), false); + bool enabled = extension && (!profile_->IsOffTheRecord() || + service->IsIncognitoEnabled(extension)); + if (!enabled) { + i = keyword_matches.erase(i); + continue; + } + } + ++i; + } if (keyword_matches.empty()) return; std::sort(keyword_matches.begin(), keyword_matches.end(), CompareQuality()); @@ -179,23 +200,16 @@ void KeywordProvider::Start(const AutocompleteInput& input, const TemplateURL* template_url(model->GetTemplateURLForKeyword(keyword)); // TODO(pkasting): We should probably check that if the user explicitly // typed a scheme, that scheme matches the one in |template_url|. + matches_.push_back(CreateAutocompleteMatch(model, keyword, input, + keyword.length(), + remaining_input, -1)); if (profile_ && !input.synchronous_only() && template_url->IsExtensionKeyword()) { - // If this extension keyword is disabled, make sure we don't add any - // matches (including the synchronous one below). - ExtensionsService* service = profile_->GetExtensionsService(); - Extension* extension = service->GetExtensionById( - template_url->GetExtensionId(), false); - bool enabled = extension && (!profile_->IsOffTheRecord() || - service->IsIncognitoEnabled(extension)); - if (!enabled) - return; - - if (extension->id() != current_keyword_extension_id_) + if (template_url->GetExtensionId() != current_keyword_extension_id_) MaybeEndExtensionKeywordMode(); if (current_keyword_extension_id_.empty()) - EnterExtensionKeywordMode(extension->id()); + EnterExtensionKeywordMode(template_url->GetExtensionId()); keyword_mode_toggle.StayInKeywordMode(); if (minimal_changes) { @@ -220,10 +234,6 @@ void KeywordProvider::Start(const AutocompleteInput& input, done_ = false; } } - - matches_.push_back(CreateAutocompleteMatch(model, keyword, input, - keyword.length(), - remaining_input, -1)); } else { if (keyword_matches.size() > kMaxMatches) { keyword_matches.erase(keyword_matches.begin() + kMaxMatches, diff --git a/chrome/browser/autocomplete/keyword_provider_unittest.cc b/chrome/browser/autocomplete/keyword_provider_unittest.cc index 5583519..e63d548 100644 --- a/chrome/browser/autocomplete/keyword_provider_unittest.cc +++ b/chrome/browser/autocomplete/keyword_provider_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/message_loop.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/keyword_provider.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc index a13ef3e..408e647 100644 --- a/chrome/browser/autocomplete/search_provider.cc +++ b/chrome/browser/autocomplete/search_provider.cc @@ -14,12 +14,14 @@ #include "base/string16.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete/keyword_provider.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/google/google_util.h" #include "chrome/browser/history/history.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" +#include "chrome/browser/history/in_memory_database.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/common/json_value_serializer.h" #include "chrome/common/pref_names.h" @@ -56,8 +58,6 @@ void SearchProvider::Providers::Set(const TemplateURL* default_provider, SearchProvider::SearchProvider(ACProviderListener* listener, Profile* profile) : AutocompleteProvider(listener, profile, "Search"), - have_history_results_(false), - history_request_pending_(false), suggest_results_pending_(0), have_suggest_results_(false) { } @@ -76,10 +76,8 @@ void SearchProvider::Start(const AutocompleteInput& input, const TemplateURL* keyword_provider = KeywordProvider::GetSubstitutingTemplateURLForInput(profile_, input, &keyword_input_text_); - if (!TemplateURL::SupportsReplacement(keyword_provider) || - keyword_input_text_.empty()) { + if (keyword_input_text_.empty()) keyword_provider = NULL; - } const TemplateURL* default_provider = profile_->GetTemplateURLModel()->GetDefaultSearchProvider(); @@ -126,7 +124,7 @@ void SearchProvider::Start(const AutocompleteInput& input, input_ = input; - StartOrStopHistoryQuery(minimal_changes); + DoHistoryQuery(minimal_changes); StartOrStopSuggestQuery(minimal_changes); ConvertResultsToAutocompleteMatches(); } @@ -154,7 +152,6 @@ void SearchProvider::Run() { } void SearchProvider::Stop() { - StopHistory(); StopSuggest(); done_ = true; } @@ -209,29 +206,36 @@ void SearchProvider::OnURLFetchComplete(const URLFetcher* source, SearchProvider::~SearchProvider() { } -void SearchProvider::StartOrStopHistoryQuery(bool minimal_changes) { - // For the minimal_changes case, if we finished the previous query and still - // have its results, or are allowed to keep running it, just do that, rather - // than starting a new query. - if (minimal_changes && - (have_history_results_ || (!done_ && !input_.synchronous_only()))) +void SearchProvider::DoHistoryQuery(bool minimal_changes) { + // The history query results are synchronous, so if minimal_changes is true, + // we still have the last results and don't need to do anything. + if (minimal_changes) return; - // We can't keep running any previous query, so halt it. - StopHistory(); + keyword_history_results_.clear(); + default_history_results_.clear(); - // We can't start a new query if we're only allowed synchronous results. - if (input_.synchronous_only()) + HistoryService* const history_service = + profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); + history::URLDatabase* url_db = history_service ? + history_service->InMemoryDatabase() : NULL; + if (!url_db) return; // Request history for both the keyword and default provider. if (providers_.valid_keyword_provider()) { - ScheduleHistoryQuery(providers_.keyword_provider().id(), - keyword_input_text_); + url_db->GetMostRecentKeywordSearchTerms( + providers_.keyword_provider().id(), + WideToUTF16(keyword_input_text_), + static_cast<int>(kMaxMatches), + &keyword_history_results_); } if (providers_.valid_default_provider()) { - ScheduleHistoryQuery(providers_.default_provider().id(), - input_.text()); + url_db->GetMostRecentKeywordSearchTerms( + providers_.default_provider().id(), + WideToUTF16(input_.text()), + static_cast<int>(kMaxMatches), + &default_history_results_); } } @@ -323,14 +327,6 @@ bool SearchProvider::IsQuerySuitableForSuggest() const { return true; } -void SearchProvider::StopHistory() { - history_request_consumer_.CancelAllRequests(); - history_request_pending_ = false; - keyword_history_results_.clear(); - default_history_results_.clear(); - have_history_results_ = false; -} - void SearchProvider::StopSuggest() { suggest_results_pending_ = 0; timer_.Stop(); @@ -344,47 +340,6 @@ void SearchProvider::StopSuggest() { have_suggest_results_ = false; } -void SearchProvider::ScheduleHistoryQuery(TemplateURLID search_id, - const std::wstring& text) { - DCHECK(!text.empty()); - HistoryService* const history_service = - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - HistoryService::Handle request_handle = - history_service->GetMostRecentKeywordSearchTerms( - search_id, WideToUTF16(text), static_cast<int>(kMaxMatches), - &history_request_consumer_, - NewCallback(this, - &SearchProvider::OnGotMostRecentKeywordSearchTerms)); - history_request_consumer_.SetClientData(history_service, request_handle, - search_id); - history_request_pending_ = true; -} - -void SearchProvider::OnGotMostRecentKeywordSearchTerms( - CancelableRequestProvider::Handle handle, - HistoryResults* results) { - HistoryService* history_service = - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - DCHECK(history_service); - if (providers_.valid_keyword_provider() && - (providers_.keyword_provider().id() == - history_request_consumer_.GetClientData(history_service, handle))) { - keyword_history_results_ = *results; - } else { - default_history_results_ = *results; - } - - if (history_request_consumer_.PendingRequestCount() == 1) { - // Requests are removed AFTER the callback is invoked. If the count == 1, - // it means no more history requests are pending. - history_request_pending_ = false; - have_history_results_ = true; - } - - ConvertResultsToAutocompleteMatches(); - listener_->OnProviderUpdate(!results->empty()); -} - URLFetcher* SearchProvider::CreateSuggestFetcher(int id, const TemplateURL& provider, const std::wstring& text) { @@ -539,13 +494,9 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { UpdateStarredStateOfMatches(); - // We're done when both asynchronous subcomponents have finished. We can't - // use CancelableRequestConsumer.HasPendingRequests() for history requests - // here. A pending request is not cleared until after the completion - // callback has returned, but we've reached here from inside that callback. - // HasPendingRequests() would therefore return true, and if this is the last - // thing left to calculate for this query, we'll never mark the query "done". - done_ = !history_request_pending_ && !suggest_results_pending_; + // We're done when there are no more suggest queries pending (this is set to 1 + // when the timer is started). + done_ = suggest_results_pending_ == 0; } void SearchProvider::AddNavigationResultsToMatches( diff --git a/chrome/browser/autocomplete/search_provider.h b/chrome/browser/autocomplete/search_provider.h index 73b70fe..f1d5ffd 100644 --- a/chrome/browser/autocomplete/search_provider.h +++ b/chrome/browser/autocomplete/search_provider.h @@ -22,7 +22,7 @@ #include "base/scoped_ptr.h" #include "chrome/browser/autocomplete/autocomplete.h" -#include "chrome/browser/cancelable_request.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/history/history_types.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_id.h" @@ -162,10 +162,13 @@ class SearchProvider : public AutocompleteProvider, // Called when timer_ expires. void Run(); + // Runs the history query, if necessary. The history query is synchronous. + // This does not update |done_|. + void DoHistoryQuery(bool minimal_changes); + // Determines whether an asynchronous subcomponent query should run for the // current input. If so, starts it if necessary; otherwise stops it. - // NOTE: These functions do not update |done_|. Callers must do so. - void StartOrStopHistoryQuery(bool minimal_changes); + // NOTE: This function does not update |done_|. Callers must do so. void StartOrStopSuggestQuery(bool minimal_changes); // Returns true when the current query can be sent to the Suggest service. @@ -173,22 +176,10 @@ class SearchProvider : public AutocompleteProvider, // potentially private data, etc. bool IsQuerySuitableForSuggest() const; - // Functions to stop the separate asynchronous subcomponents. - // NOTE: These functions do not update |done_|. Callers must do so. - void StopHistory(); + // Stops the suggest query. + // NOTE: This does not update |done_|. Callers must do so. void StopSuggest(); - // Schedules a history query requesting past searches against the engine - // whose id is |search_id| and whose text starts with |text|. - void ScheduleHistoryQuery(TemplateURLID search_id, - const std::wstring& text); - - // Called back by the history system to return searches that begin with the - // input text. - void OnGotMostRecentKeywordSearchTerms( - CancelableRequestProvider::Handle handle, - HistoryResults* results); - // Creates a URLFetcher requesting suggest results for the specified // TemplateURL. Ownership of the returned URLFetchet passes to the caller. URLFetcher* CreateSuggestFetcher(int id, @@ -274,24 +265,10 @@ class SearchProvider : public AutocompleteProvider, // Input text when searching against the keyword provider. std::wstring keyword_input_text_; - // An object we can use to cancel history requests. The client data - // corresponds to the id of the search engine and is used in the callback to - // determine whether the request corresponds to the keyword of default - // provider. - CancelableRequestConsumerTSimple<TemplateURLID> - history_request_consumer_; - // Searches in the user's history that begin with the input text. HistoryResults keyword_history_results_; HistoryResults default_history_results_; - // Whether history_results_ is valid (so we can tell invalid apart from - // empty). - bool have_history_results_; - - // Whether we are waiting for a history request to finish. - bool history_request_pending_; - // Number of suggest results that haven't yet arrived. If greater than 0 it // indicates either |timer_| or one of the URLFetchers is still running. int suggest_results_pending_; diff --git a/chrome/browser/autocomplete/search_provider_unittest.cc b/chrome/browser/autocomplete/search_provider_unittest.cc index 595b688..43ae43b 100644 --- a/chrome/browser/autocomplete/search_provider_unittest.cc +++ b/chrome/browser/autocomplete/search_provider_unittest.cc @@ -6,6 +6,7 @@ #include "base/time.h" #include "base/utf_string_conversions.h" #include "build/build_config.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/search_provider.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/history/history.h" @@ -132,6 +133,11 @@ void SearchProviderTest::SetUp() { history->SetKeywordSearchTermsForURL(keyword_url_, keyword_t_url_->id(), keyword_term_); + // Keywords are updated by the InMemoryHistoryBackend only after the message + // has been processed on the history thread. Block until history processes all + // requests to ensure the InMemoryDatabase is the state we expect it. + profile_.BlockUntilHistoryProcessesPendingRequests(); + provider_ = new SearchProvider(this, &profile_); URLFetcher::set_factory(&test_factory_); diff --git a/chrome/browser/autocomplete_history_manager.cc b/chrome/browser/autocomplete_history_manager.cc index b2c4d3c..2243bcb 100644 --- a/chrome/browser/autocomplete_history_manager.cc +++ b/chrome/browser/autocomplete_history_manager.cc @@ -31,8 +31,6 @@ const string16 kSSNSeparators = ASCIIToUTF16(" -"); bool IsSSN(const string16& text) { string16 number_string; RemoveChars(text, kSSNSeparators.c_str(), &number_string); - if (number_string.length() != 9 || !IsStringASCII(number_string)) - return false; // A SSN is of the form AAA-GG-SSSS (A = area number, G = group number, S = // serial number). The validation we do here is simply checking if the area, @@ -42,13 +40,13 @@ bool IsSSN(const string16& text) { // See: http://www.socialsecurity.gov/history/ssn/geocard.html // http://www.socialsecurity.gov/employer/stateweb.htm // http://www.socialsecurity.gov/employer/ssnvhighgroup.htm - - string16 area_string = number_string.substr(0, 3); - string16 group_string = number_string.substr(3, 2); - string16 serial_string = number_string.substr(5, 4); + if (number_string.length() != 9 || !IsStringASCII(number_string)) + return false; int area; - if (!base::StringToInt(area_string, &area)) + if (!base::StringToInt(number_string.begin(), + number_string.begin() + 3, + &area)) return false; if (area < 1 || area == 666 || @@ -57,11 +55,15 @@ bool IsSSN(const string16& text) { return false; int group; - if (!base::StringToInt(group_string, &group) || group == 0) + if (!base::StringToInt(number_string.begin() + 3, + number_string.begin() + 5, + &group) || group == 0) return false; int serial; - if (!base::StringToInt(serial_string, &serial) || serial == 0) + if (!base::StringToInt(number_string.begin() + 5, + number_string.begin() + 9, + &serial) || serial == 0) return false; return true; diff --git a/chrome/browser/autofill/address_field.cc b/chrome/browser/autofill/address_field.cc index 52e5193..41a1a1d 100644 --- a/chrome/browser/autofill/address_field.cc +++ b/chrome/browser/autofill/address_field.cc @@ -91,12 +91,10 @@ AddressField* AddressField::Parse( if (ParseCompany(&q, is_ecml, address_field.get()) || ParseAddressLines(&q, is_ecml, address_field.get()) || ParseCity(&q, is_ecml, address_field.get()) || + ParseState(&q, is_ecml, address_field.get()) || ParseZipCode(&q, is_ecml, address_field.get()) || ParseCountry(&q, is_ecml, address_field.get())) { continue; - } else if ((!address_field->state_ || address_field->state_->IsEmpty()) && - address_field->ParseState(&q, is_ecml, address_field.get())) { - continue; } else if (ParseText(&q, ASCIIToUTF16("attention|attn.")) || ParseText(&q, ASCIIToUTF16("province|region|other"))) { // We ignore the following: @@ -339,9 +337,13 @@ bool AddressField::ParseCity( return true; } +// static bool AddressField::ParseState( std::vector<AutoFillField*>::const_iterator* iter, bool is_ecml, AddressField* address_field) { + if (address_field->state_) + return false; + string16 pattern; if (is_ecml) pattern = GetEcmlPattern(kEcmlShipToStateProv, kEcmlBillToStateProv, '|'); diff --git a/chrome/browser/autofill/address_field.h b/chrome/browser/autofill/address_field.h index 6b93ead..51dd105 100644 --- a/chrome/browser/autofill/address_field.h +++ b/chrome/browser/autofill/address_field.h @@ -44,8 +44,8 @@ class AddressField : public FormField { bool is_ecml, AddressField* address_field); static bool ParseCity(std::vector<AutoFillField*>::const_iterator* iter, bool is_ecml, AddressField* address_field); - bool ParseState(std::vector<AutoFillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field); + static bool ParseState(std::vector<AutoFillField*>::const_iterator* iter, + bool is_ecml, AddressField* address_field); // Looks for an address type in the given text, which the caller must // convert to lowercase. diff --git a/chrome/browser/autofill/address_field_unittest.cc b/chrome/browser/autofill/address_field_unittest.cc index 5477b11..51509f7 100644 --- a/chrome/browser/autofill/address_field_unittest.cc +++ b/chrome/browser/autofill/address_field_unittest.cc @@ -344,6 +344,40 @@ TEST_F(AddressFieldTest, ParseZipEcml) { EXPECT_EQ(ADDRESS_HOME_ZIP, field_type_map_[ASCIIToUTF16("zip1")]); } +TEST_F(AddressFieldTest, ParseStateAndZipOneLabel) { + list_.push_back( + new AutoFillField( + webkit_glue::FormField( + ASCIIToUTF16("State/Province, Zip/Postal Code"), + ASCIIToUTF16("state"), + string16(), + ASCIIToUTF16("text"), + 0), + ASCIIToUTF16("state"))); + list_.push_back( + new AutoFillField( + webkit_glue::FormField( + ASCIIToUTF16("State/Province, Zip/Postal Code"), + ASCIIToUTF16("zip"), + string16(), + ASCIIToUTF16("text"), + 0), + ASCIIToUTF16("zip"))); + list_.push_back(NULL); + iter_ = list_.begin(); + field_.reset(AddressField::Parse(&iter_, false)); + ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); + EXPECT_EQ(kGenericAddress, field_->FindType()); + EXPECT_FALSE(field_->IsFullAddress()); + ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); + ASSERT_TRUE( + field_type_map_.find(ASCIIToUTF16("state")) != field_type_map_.end()); + EXPECT_EQ(ADDRESS_HOME_STATE, field_type_map_[ASCIIToUTF16("state")]); + ASSERT_TRUE( + field_type_map_.find(ASCIIToUTF16("zip")) != field_type_map_.end()); + EXPECT_EQ(ADDRESS_HOME_ZIP, field_type_map_[ASCIIToUTF16("zip")]); +} + TEST_F(AddressFieldTest, ParseCountry) { list_.push_back( new AutoFillField(webkit_glue::FormField(ASCIIToUTF16("Country"), diff --git a/chrome/browser/autofill/autofill_address_model_mac_unittest.mm b/chrome/browser/autofill/autofill_address_model_mac_unittest.mm index 3cf29c8..21ee8b5 100644 --- a/chrome/browser/autofill/autofill_address_model_mac_unittest.mm +++ b/chrome/browser/autofill/autofill_address_model_mac_unittest.mm @@ -18,14 +18,14 @@ typedef CocoaTest AutoFillAddressModelTest; TEST(AutoFillAddressModelTest, Basic) { // A basic test that creates a new instance and releases. // Aids valgrind leak detection. - AutoFillProfile profile(ASCIIToUTF16("Home"), 0); + AutoFillProfile profile; scoped_nsobject<AutoFillAddressModel> model([[AutoFillAddressModel alloc] initWithProfile:profile]); EXPECT_TRUE(model.get()); } TEST(AutoFillAddressModelTest, InitializationFromProfile) { - AutoFillProfile profile(ASCIIToUTF16("Home"), 0); + AutoFillProfile profile; autofill_test::SetProfileInfo( &profile, "Billing", @@ -59,7 +59,7 @@ TEST(AutoFillAddressModelTest, InitializationFromProfile) { } TEST(AutoFillAddressModelTest, CopyModelToProfile) { - AutoFillProfile profile(ASCIIToUTF16("Home"), 0); + AutoFillProfile profile; autofill_test::SetProfileInfo( &profile, "Billing", diff --git a/chrome/browser/autofill/autofill_address_sheet_controller_mac_unittest.mm b/chrome/browser/autofill/autofill_address_sheet_controller_mac_unittest.mm index 7c72255..2d6d34a 100644 --- a/chrome/browser/autofill/autofill_address_sheet_controller_mac_unittest.mm +++ b/chrome/browser/autofill/autofill_address_sheet_controller_mac_unittest.mm @@ -17,7 +17,7 @@ typedef CocoaTest AutoFillAddressSheetControllerTest; TEST(AutoFillAddressSheetControllerTest, Basic) { // A basic test that creates a new instance and releases. // Aids valgrind leak detection. - AutoFillProfile profile(ASCIIToUTF16("Home"), 0); + AutoFillProfile profile; scoped_nsobject<AutoFillAddressSheetController> controller( [[AutoFillAddressSheetController alloc] initWithProfile:profile diff --git a/chrome/browser/autofill/autofill_browsertest.cc b/chrome/browser/autofill/autofill_browsertest.cc index 527cccd..579f08c 100644 --- a/chrome/browser/autofill/autofill_browsertest.cc +++ b/chrome/browser/autofill/autofill_browsertest.cc @@ -32,7 +32,7 @@ class AutoFillTest : public InProcessBrowserTest { void SetUpProfile() { autofill_test::DisableSystemServices(browser()->profile()); - AutoFillProfile profile(string16(), 0); + AutoFillProfile profile; autofill_test::SetProfileInfo( &profile, "Office Space", "Milton", "C.", "Waddams", "red.swingline@initech.com", "Initech", "4120 Freidrich Lane", @@ -59,13 +59,7 @@ class AutoFillTest : public InProcessBrowserTest { }; // Test that basic form fill is working. -// FAILS on windows: http://crbug.com/57962 -#if defined(OS_WIN) -#define MAYBE_BasicFormFill DISABLED_BasicFormFill -#else -#define MAYBE_BasicFormFill BasicFormFill -#endif -IN_PROC_BROWSER_TEST_F(AutoFillTest, MAYBE_BasicFormFill) { +IN_PROC_BROWSER_TEST_F(AutoFillTest, BasicFormFill) { SetUpProfile(); ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); @@ -73,7 +67,9 @@ IN_PROC_BROWSER_TEST_F(AutoFillTest, MAYBE_BasicFormFill) { browser(), GURL("data:text/html;charset=utf-8," "<form action=\"http://www.google.com/\" method=\"POST\">" "<label for=\"firstname\">First name:</label>" - " <input type=\"text\" id=\"firstname\" /><br />" + " <input type=\"text\" id=\"firstname\"" + " onFocus=\"domAutomationController.send(true)\"" + " /><br />" "<label for=\"lastname\">Last name:</label>" " <input type=\"text\" id=\"lastname\" /><br />" "<label for=\"address1\">Address line 1:</label>" @@ -107,8 +103,11 @@ IN_PROC_BROWSER_TEST_F(AutoFillTest, MAYBE_BasicFormFill) { RenderViewHost* render_view_host = browser()->GetSelectedTabContents()->render_view_host(); - ASSERT_TRUE(ui_test_utils::ExecuteJavaScript( - render_view_host, L"", L"document.getElementById('firstname').focus();")); + bool result; + ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool( + render_view_host, L"", L"document.getElementById('firstname').focus();", + &result)); + ASSERT_TRUE(result); // Start filling the first name field with "M" and wait for the popup to be // shown. diff --git a/chrome/browser/autofill/autofill_common_test.cc b/chrome/browser/autofill/autofill_common_test.cc index 3c7c319..c4c8482 100644 --- a/chrome/browser/autofill/autofill_common_test.cc +++ b/chrome/browser/autofill/autofill_common_test.cc @@ -55,16 +55,13 @@ void SetProfileInfo(AutoFillProfile* profile, } void SetCreditCardInfo(CreditCard* credit_card, - const char* label, const char* name_on_card, const char* type, - const char* card_number, const char* expiration_month, - const char* expiration_year, int billing_address_id) { + const char* label, const char* name_on_card, const char* card_number, + const char* expiration_month, const char* expiration_year) { credit_card->set_label(ASCIIToUTF16(label)); check_and_set(credit_card, CREDIT_CARD_NAME, name_on_card); - check_and_set(credit_card, CREDIT_CARD_TYPE, type); check_and_set(credit_card, CREDIT_CARD_NUMBER, card_number); check_and_set(credit_card, CREDIT_CARD_EXP_MONTH, expiration_month); check_and_set(credit_card, CREDIT_CARD_EXP_4_DIGIT_YEAR, expiration_year); - credit_card->set_billing_address_id(billing_address_id); } void DisableSystemServices(Profile* profile) { diff --git a/chrome/browser/autofill/autofill_common_test.h b/chrome/browser/autofill/autofill_common_test.h index 3b7b83e..dfb7ac6 100644 --- a/chrome/browser/autofill/autofill_common_test.h +++ b/chrome/browser/autofill/autofill_common_test.h @@ -38,9 +38,8 @@ void SetProfileInfo(AutoFillProfile* profile, // tests. |SetCreditCardInfo| provides a quick way to populate a credit card // with c-strings. void SetCreditCardInfo(CreditCard* credit_card, - const char* label, const char* name_on_card, const char* type, - const char* card_number, const char* expiration_month, - const char* expiration_year, int billing_address_id); + const char* label, const char* name_on_card, const char* card_number, + const char* expiration_month, const char* expiration_year); // TODO(isherman): We should do this automatically for all tests, not manually // on a per-test basis: http://crbug.com/57221 diff --git a/chrome/browser/autofill/autofill_credit_card_model_mac.h b/chrome/browser/autofill/autofill_credit_card_model_mac.h index 277ff55..093e7cb 100644 --- a/chrome/browser/autofill/autofill_credit_card_model_mac.h +++ b/chrome/browser/autofill/autofill_credit_card_model_mac.h @@ -25,14 +25,12 @@ class CreditCard; NSString* creditCardNumber_; NSString* expirationMonth_; NSString* expirationYear_; - NSInteger billingAddressID_; } @property (nonatomic, copy) NSString* nameOnCard; @property (nonatomic, copy) NSString* creditCardNumber; @property (nonatomic, copy) NSString* expirationMonth; @property (nonatomic, copy) NSString* expirationYear; -@property (nonatomic) NSInteger billingAddressID; // Designated initializer. Initializes the property strings to values retrieved // from the |creditCard| object. diff --git a/chrome/browser/autofill/autofill_credit_card_model_mac.mm b/chrome/browser/autofill/autofill_credit_card_model_mac.mm index 5c49ab2..1eec96f 100644 --- a/chrome/browser/autofill/autofill_credit_card_model_mac.mm +++ b/chrome/browser/autofill/autofill_credit_card_model_mac.mm @@ -15,7 +15,6 @@ @synthesize creditCardNumber = creditCardNumber_; @synthesize expirationMonth = expirationMonth_; @synthesize expirationYear = expirationYear_; -@synthesize billingAddressID = billingAddressID_; - (id)initWithCreditCard:(const CreditCard&)creditCard { if ((self = [super init])) { @@ -27,7 +26,6 @@ creditCard.GetFieldText(AutoFillType(CREDIT_CARD_EXP_MONTH)))]; [self setExpirationYear:SysUTF16ToNSString( creditCard.GetFieldText(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR)))]; - [self setBillingAddressID:creditCard.billing_address_id()]; } return self; } @@ -50,7 +48,6 @@ base::SysNSStringToUTF16([self expirationMonth])); creditCard->SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), base::SysNSStringToUTF16([self expirationYear])); - creditCard->set_billing_address_id([self billingAddressID]); } @end diff --git a/chrome/browser/autofill/autofill_credit_card_model_mac_unittest.mm b/chrome/browser/autofill/autofill_credit_card_model_mac_unittest.mm index f90f7d1..e95c2dd 100644 --- a/chrome/browser/autofill/autofill_credit_card_model_mac_unittest.mm +++ b/chrome/browser/autofill/autofill_credit_card_model_mac_unittest.mm @@ -18,16 +18,16 @@ typedef CocoaTest AutoFillCreditCardModelTest; TEST(AutoFillCreditCardModelTest, Basic) { // A basic test that creates a new instance and releases. // Aids valgrind leak detection. - CreditCard credit_card(ASCIIToUTF16("myCC"), 0); + CreditCard credit_card; scoped_nsobject<AutoFillCreditCardModel> model( [[AutoFillCreditCardModel alloc] initWithCreditCard:credit_card]); EXPECT_TRUE(model.get()); } TEST(AutoFillCreditCardModelTest, InitializationFromCreditCard) { - CreditCard credit_card(string16(), 0); + CreditCard credit_card; autofill_test::SetCreditCardInfo(&credit_card, "Corporate", - "John Dillinger", "Visa", "123456789012", "01", "2010", 1); + "John Dillinger", "123456789012", "01", "2010"); scoped_nsobject<AutoFillCreditCardModel> model( [[AutoFillCreditCardModel alloc] initWithCreditCard:credit_card]); EXPECT_TRUE(model.get()); @@ -36,13 +36,12 @@ TEST(AutoFillCreditCardModelTest, InitializationFromCreditCard) { EXPECT_TRUE([[model creditCardNumber] isEqualToString:@"123456789012"]); EXPECT_TRUE([[model expirationMonth] isEqualToString:@"01"]); EXPECT_TRUE([[model expirationYear] isEqualToString:@"2010"]); - EXPECT_EQ(1, [model billingAddressID]); } TEST(AutoFillCreditCardModelTest, CopyModelToCreditCard) { - CreditCard credit_card(string16(), 0); + CreditCard credit_card; autofill_test::SetCreditCardInfo(&credit_card, "Corporate", - "John Dillinger", "Visa", "123456789012", "01", "2010", 1); + "John Dillinger", "123456789012", "01", "2010"); scoped_nsobject<AutoFillCreditCardModel> model( [[AutoFillCreditCardModel alloc] initWithCreditCard:credit_card]); EXPECT_TRUE(model.get()); @@ -51,7 +50,6 @@ TEST(AutoFillCreditCardModelTest, CopyModelToCreditCard) { [model setCreditCardNumber:@"223456789012"]; [model setExpirationMonth:@"11"]; [model setExpirationYear:@"2011"]; - [model setBillingAddressID:2]; [model copyModelToCreditCard:&credit_card]; @@ -64,7 +62,6 @@ TEST(AutoFillCreditCardModelTest, CopyModelToCreditCard) { EXPECT_EQ(ASCIIToUTF16("2011"), credit_card.GetFieldText( AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR))); - EXPECT_EQ(2, credit_card.billing_address_id()); } } // namespace diff --git a/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.h b/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.h index 76fa2ad..650fb1c 100644 --- a/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.h +++ b/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.h @@ -27,7 +27,6 @@ typedef NSInteger AutoFillCreditCardMode; // and transcribes it to |creditCardModel| to which the view is bound. @interface AutoFillCreditCardSheetController : NSWindowController { @private - IBOutlet NSPopUpButton* billingAddressPopup_; IBOutlet NSPopUpButton* expirationMonthPopup_; IBOutlet NSPopUpButton* expirationYearPopup_; @@ -44,40 +43,24 @@ typedef NSInteger AutoFillCreditCardMode; // because it is exposed as a KVO compliant property. AutoFillCreditCardModel* creditCardModel_; - // Array of strings that populate the |billingAddressPopup_| control. We - // do not hold this as scoped_nsobject because it is exposed as a KVO - // compliant property. The values of this array may change as the list - // of addresses change in the |parentController_|. - NSArray* billingAddressContents_; - - // Array of IDs corresponding to the strings in |billingAddressContents_|. - std::vector<int> billingAddressIDs_; - // Contents of the expiration month and year popups. Strongly owned. We do // not hold them as scoped_nsobjects because they are exposed as KVO compliant // properties. NSArray* expirationMonthContents_; NSArray* expirationYearContents_; - // A reference to our parent controller. Used for fetching billing address - // labels. May be not be nil. - // Weak reference, owns us. - AutoFillDialogController* parentController_; - // Either "Add" or "Edit" mode of sheet. AutoFillCreditCardMode mode_; } @property (nonatomic, retain) AutoFillCreditCardModel* creditCardModel; -@property (nonatomic, retain) NSArray* billingAddressContents; @property (nonatomic, retain) NSArray* expirationMonthContents; @property (nonatomic, retain) NSArray* expirationYearContents; // Designated initializer. Takes a copy of the data in |creditCard|, // it is not held as a reference. - (id)initWithCreditCard:(const CreditCard&)creditCard - mode:(AutoFillCreditCardMode)mode - controller:(AutoFillDialogController*)parentController; + mode:(AutoFillCreditCardMode)mode; // IBActions for save and cancel buttons. Both invoke |endSheet:|. - (IBAction)save:(id)sender; diff --git a/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.mm b/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.mm index 31fbff0..95eb140 100644 --- a/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.mm +++ b/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.mm @@ -21,7 +21,6 @@ // Private methods for the |AutoFillCreditCardSheetController| class. @interface AutoFillCreditCardSheetController (PrivateMethods) -- (void)buildBillingAddressContents; - (void)buildExpirationMonthContents; - (void)buildExpirationYearContents; @end @@ -29,13 +28,11 @@ @implementation AutoFillCreditCardSheetController @synthesize creditCardModel = creditCardModel_; -@synthesize billingAddressContents = billingAddressContents_; @synthesize expirationMonthContents = expirationMonthContents_; @synthesize expirationYearContents = expirationYearContents_; - (id)initWithCreditCard:(const CreditCard&)creditCard - mode:(AutoFillCreditCardMode)mode - controller:(AutoFillDialogController*)parentController { + mode:(AutoFillCreditCardMode)mode { NSString* nibPath = [mac_util::MainAppBundle() pathForResource:@"AutoFillCreditCardSheet" ofType:@"nib"]; @@ -45,9 +42,6 @@ [self setCreditCardModel:[[[AutoFillCreditCardModel alloc] initWithCreditCard:creditCard] autorelease]]; - // We keep track of our parent controller for model-update purposes. - parentController_ = parentController; - mode_ = mode; } return self; @@ -55,7 +49,6 @@ - (void)dealloc { [creditCardModel_ release]; - [billingAddressContents_ release]; [expirationMonthContents_ release]; [expirationYearContents_ release]; [super dealloc]; @@ -63,12 +56,10 @@ - (void)awakeFromNib { // Setup initial state of popups. - [self buildBillingAddressContents]; [self buildExpirationMonthContents]; [self buildExpirationYearContents]; // Turn menu autoenable off. We manually govern this. - [billingAddressPopup_ setAutoenablesItems:NO]; [expirationMonthPopup_ setAutoenablesItems:NO]; [expirationYearPopup_ setAutoenablesItems:NO]; @@ -97,55 +88,14 @@ - (void)copyModelToCreditCard:(CreditCard*)creditCard { // The model copies the popup values blindly. We need to clear the strings // in the case that our special menus are in effect. - if ([billingAddressPopup_ indexOfSelectedItem] <= 0) - [creditCardModel_ setBillingAddressID:0]; if ([expirationMonthPopup_ indexOfSelectedItem] <= 0) [creditCardModel_ setExpirationMonth:@""]; if ([expirationYearPopup_ indexOfSelectedItem] <= 0) [creditCardModel_ setExpirationYear:@""]; - // The view does not set the billing address directly. It relies on - // the controller to translate between popup index and the billing address - // ID. Note the -1 offset. This is due to the empty menu item in the popup. - if ([billingAddressPopup_ indexOfSelectedItem] > 0) { - [creditCardModel_ setBillingAddressID: - billingAddressIDs_[[billingAddressPopup_ indexOfSelectedItem]-1]]; - } [creditCardModel_ copyModelToCreditCard:creditCard]; } -// Builds the |billingAddressContents_| array of strings from the list of -// addresses returned by the |parentController_| and additional UI string. -// Ensures that current selection is valid. If not, reset it. -- (void)buildBillingAddressContents { - NSString* menuString = l10n_util::GetNSString( - IDS_AUTOFILL_DIALOG_CHOOSE_EXISTING_ADDRESS); - - // Build the menu array and set it. - NSArray* addressStrings = nil; - [parentController_ addressLabels:&addressStrings - addressIDs:&billingAddressIDs_]; - NSArray* newArray = [[NSArray arrayWithObject:menuString] - arrayByAddingObjectsFromArray:addressStrings]; - [self setBillingAddressContents:newArray]; - - // If the addresses no longer contain our billing address then reset the - // selection. - int distance = std::distance(billingAddressIDs_.begin(), - std::find(billingAddressIDs_.begin(), - billingAddressIDs_.end(), - [creditCardModel_ billingAddressID])); - if (distance >= static_cast<int>(billingAddressIDs_.size())) { - [billingAddressPopup_ selectItemAtIndex:0]; - } else { - [billingAddressPopup_ selectItemAtIndex:distance+1]; - } - - - // Disable first item in menu. "Choose existing address" is a non-item. - [[billingAddressPopup_ itemAtIndex:0] setEnabled:NO]; -} - // Builds array of valid months. Uses special @" " to indicate no selection. - (void)buildExpirationMonthContents { NSArray* newArray = [NSArray arrayWithObjects:@" ", diff --git a/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac_unittest.mm b/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac_unittest.mm index 9058070..6f943ea 100644 --- a/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac_unittest.mm +++ b/chrome/browser/autofill/autofill_credit_card_sheet_controller_mac_unittest.mm @@ -17,12 +17,11 @@ typedef CocoaTest AutoFillCreditCardSheetControllerTest; TEST(AutoFillCreditCardSheetControllerTest, Basic) { // A basic test that creates a new instance and releases. // Aids valgrind leak detection. - CreditCard credit_card(ASCIIToUTF16("myCC"), 0); + CreditCard credit_card; scoped_nsobject<AutoFillCreditCardSheetController> controller( [[AutoFillCreditCardSheetController alloc] initWithCreditCard:credit_card - mode:kAutoFillCreditCardAddMode - controller:nil]); + mode:kAutoFillCreditCardAddMode]); EXPECT_TRUE(controller.get()); } diff --git a/chrome/browser/autofill/autofill_dialog_controller_mac.h b/chrome/browser/autofill/autofill_dialog_controller_mac.h index cd5eec1..4ebf2d6 100644 --- a/chrome/browser/autofill/autofill_dialog_controller_mac.h +++ b/chrome/browser/autofill/autofill_dialog_controller_mac.h @@ -160,12 +160,6 @@ class Profile; - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView; -// Creates an array of labels and populates a vector of corresponding IDs -// representing the addresses and associated billing address IDs in the -// |profiles_| member. -// Only returns labels and IDs for which there exists address information. -- (void)addressLabels:(NSArray**)labels addressIDs:(std::vector<int>*)ids; - @end // Interface exposed for unit testing. diff --git a/chrome/browser/autofill/autofill_dialog_controller_mac.mm b/chrome/browser/autofill/autofill_dialog_controller_mac.mm index f48f952..c1f193c 100644 --- a/chrome/browser/autofill/autofill_dialog_controller_mac.mm +++ b/chrome/browser/autofill/autofill_dialog_controller_mac.mm @@ -32,20 +32,6 @@ namespace { // dialog. typedef std::map<Profile*, AutoFillDialogController*> ProfileControllerMap; -// Update profile labels passed as |input|. When profile data changes as a -// result of adding new profiles, edititing existing profiles, or deleting a -// profile, then the list of profiles need to have their derived labels -// recomputed. -void UpdateProfileLabels(std::vector<AutoFillProfile>* input) { - DCHECK(input); - std::vector<AutoFillProfile*> profiles; - profiles.resize(input->size()); - for (size_t i = 0; i < input->size(); ++i) { - profiles[i] = &(*input)[i]; - } - AutoFillProfile::AdjustInferredLabels(&profiles); -} - } // namespace // Delegate protocol that needs to be in place for the AutoFillTableView's @@ -295,8 +281,7 @@ class PreferenceObserver : public NotificationObserver { DCHECK(!addressSheetController.get()); // Create a new default address. - string16 newName = l10n_util::GetStringUTF16(IDS_AUTOFILL_NEW_ADDRESS); - AutoFillProfile newAddress(newName, 0); + AutoFillProfile newAddress; // Create a new address sheet controller in "Add" mode. addressSheetController.reset( @@ -318,15 +303,13 @@ class PreferenceObserver : public NotificationObserver { DCHECK(!creditCardSheetController.get()); // Create a new default credit card. - string16 newName = l10n_util::GetStringUTF16(IDS_AUTOFILL_NEW_CREDITCARD); - CreditCard newCreditCard(newName, 0); + CreditCard newCreditCard; // Create a new address sheet controller in "Add" mode. creditCardSheetController.reset( [[AutoFillCreditCardSheetController alloc] initWithCreditCard:newCreditCard - mode:kAutoFillCreditCardAddMode - controller:self]); + mode:kAutoFillCreditCardAddMode]); // Show the sheet. [NSApp beginSheet:[creditCardSheetController window] @@ -344,7 +327,7 @@ class PreferenceObserver : public NotificationObserver { if (returnCode) { // Create a new address and save it to the |profiles_| list. - AutoFillProfile newAddress(string16(), 0); + AutoFillProfile newAddress; [addressSheetController copyModelToProfile:&newAddress]; if (!newAddress.IsEmpty()) { profiles_.push_back(newAddress); @@ -371,7 +354,7 @@ class PreferenceObserver : public NotificationObserver { if (returnCode) { // Create a new credit card and save it to the |creditCards_| list. - CreditCard newCreditCard(string16(), 0); + CreditCard newCreditCard; [creditCardSheetController copyModelToCreditCard:&newCreditCard]; if (!newCreditCard.IsEmpty()) { creditCards_.push_back(newCreditCard); @@ -458,8 +441,7 @@ class PreferenceObserver : public NotificationObserver { creditCardSheetController.reset( [[AutoFillCreditCardSheetController alloc] initWithCreditCard:creditCards_[i] - mode:kAutoFillCreditCardEditMode - controller:self]); + mode:kAutoFillCreditCardEditMode]); // Show the sheet. [NSApp beginSheet:[creditCardSheetController window] @@ -619,33 +601,6 @@ class PreferenceObserver : public NotificationObserver { return 0; } -- (void)addressLabels:(NSArray**)labels addressIDs:(std::vector<int>*)ids { - NSUInteger capacity = profiles_.size(); - NSMutableArray* array = [NSMutableArray arrayWithCapacity:capacity]; - ids->clear(); - - std::vector<AutoFillProfile>::iterator i; - for (i = profiles_.begin(); i != profiles_.end(); ++i) { - FieldTypeSet fields; - i->GetAvailableFieldTypes(&fields); - if (fields.find(ADDRESS_HOME_LINE1) == fields.end() && - fields.find(ADDRESS_HOME_LINE2) == fields.end() && - fields.find(ADDRESS_HOME_APT_NUM) == fields.end() && - fields.find(ADDRESS_HOME_CITY) == fields.end() && - fields.find(ADDRESS_HOME_STATE) == fields.end() && - fields.find(ADDRESS_HOME_ZIP) == fields.end() && - fields.find(ADDRESS_HOME_COUNTRY) == fields.end()) { - // No address information in this profile; it's useless as a billing - // address. - continue; - } - [array addObject:SysUTF16ToNSString(i->Label())]; - ids->push_back(i->unique_id()); - } - - *labels = array; -} - // Accessor for |autoFillEnabled| preference state. Note: a checkbox in Nib // is bound to this via KVO. - (BOOL)autoFillEnabled { @@ -841,7 +796,6 @@ class PreferenceObserver : public NotificationObserver { iter != creditCards.end(); ++iter) creditCards_.push_back(**iter); - UpdateProfileLabels(&profiles_); [tableView_ reloadData]; } @@ -934,7 +888,7 @@ class PreferenceObserver : public NotificationObserver { // TODO(dhollowa): Using SetInfo() call to validate phone number. Should // have explicit validation method. More robust validation is needed as // well eventually. - AutoFillProfile profile(string16(), 0); + AutoFillProfile profile; profile.SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER), base::SysNSStringToUTF16(string)); if (profile.GetFieldText(AutoFillType(PHONE_HOME_WHOLE_NUMBER)).empty()) { diff --git a/chrome/browser/autofill/autofill_dialog_controller_mac_unittest.mm b/chrome/browser/autofill/autofill_dialog_controller_mac_unittest.mm index e8c7dab..13aeb4f 100644 --- a/chrome/browser/autofill/autofill_dialog_controller_mac_unittest.mm +++ b/chrome/browser/autofill/autofill_dialog_controller_mac_unittest.mm @@ -114,9 +114,8 @@ class AutoFillDialogObserverMock : public AutoFillDialogObserver { : hit_(false) {} virtual ~AutoFillDialogObserverMock() {} - virtual void OnAutoFillDialogApply( - std::vector<AutoFillProfile>* profiles, - std::vector<CreditCard>* credit_cards) { + virtual void OnAutoFillDialogApply(std::vector<AutoFillProfile>* profiles, + std::vector<CreditCard>* credit_cards) { hit_ = true; std::vector<AutoFillProfile>::iterator i; @@ -192,7 +191,7 @@ TEST_F(AutoFillDialogControllerTest, NoEditsDoNotChangeObserverProfiles) { } TEST_F(AutoFillDialogControllerTest, NoEditsDoNotChangeObserverCreditCards) { - CreditCard credit_card(ASCIIToUTF16("myCC"), 345); + CreditCard credit_card; credit_cards().push_back(&credit_card); LoadDialog(); [controller_ closeDialog]; @@ -205,7 +204,7 @@ TEST_F(AutoFillDialogControllerTest, NoEditsDoNotChangeObserverCreditCards) { } TEST_F(AutoFillDialogControllerTest, AutoFillDataMutation) { - AutoFillProfile profile(ASCIIToUTF16("Home"), 17); + AutoFillProfile profile; profile.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("John")); profile.SetInfo(AutoFillType(NAME_MIDDLE), ASCIIToUTF16("C")); profile.SetInfo(AutoFillType(NAME_LAST), ASCIIToUTF16("Smith")); @@ -250,22 +249,17 @@ TEST_F(AutoFillDialogControllerTest, AutoFillDataMutation) { ASSERT_TRUE(observer_.hit_); ASSERT_TRUE(observer_.profiles_.size() == 1); - profiles()[0]->set_unique_id(observer_.profiles_[0].unique_id()); - // Do not compare labels. Label is a derived field. - observer_.profiles_[0].set_label(string16()); - profiles()[0]->set_label(string16()); - ASSERT_EQ(observer_.profiles_[0], *profiles()[0]); + ASSERT_EQ(0, observer_.profiles_[0].Compare(*profiles()[0])); } TEST_F(AutoFillDialogControllerTest, CreditCardDataMutation) { - CreditCard credit_card(ASCIIToUTF16("myCC"), 345); + CreditCard credit_card; credit_card.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("DCH")); credit_card.SetInfo(AutoFillType(CREDIT_CARD_NUMBER), ASCIIToUTF16("1234 5678 9101 1121")); credit_card.SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), ASCIIToUTF16("01")); credit_card.SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), ASCIIToUTF16("2012")); - credit_card.set_billing_address_id(0); credit_cards().push_back(&credit_card); LoadDialog(); @@ -291,17 +285,14 @@ TEST_F(AutoFillDialogControllerTest, CreditCardDataMutation) { ASSERT_TRUE(observer_.hit_); ASSERT_TRUE(observer_.credit_cards_.size() == 1); - - // Don't compare unique ids. - credit_cards()[0]->set_unique_id(observer_.credit_cards_[0].unique_id()); - ASSERT_EQ(observer_.credit_cards_[0], *credit_cards()[0]); + ASSERT_EQ(0, observer_.credit_cards_[0].Compare(*credit_cards()[0])); } TEST_F(AutoFillDialogControllerTest, TwoProfiles) { - AutoFillProfile profile1(ASCIIToUTF16("One"), 1); + AutoFillProfile profile1; profile1.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Joe")); profiles().push_back(&profile1); - AutoFillProfile profile2(ASCIIToUTF16("Two"), 2); + AutoFillProfile profile2; profile2.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Bob")); profiles().push_back(&profile2); LoadDialog(); @@ -316,20 +307,15 @@ TEST_F(AutoFillDialogControllerTest, TwoProfiles) { // Contents should match. With the exception of the |unique_id|. for (size_t i = 0, count = profiles().size(); i < count; i++) { - profiles()[i]->set_unique_id([controller_ profiles][i].unique_id()); - - // Do not compare labels. Label is a derived field. - [controller_ profiles][i].set_label(string16()); - profiles()[i]->set_label(string16()); - ASSERT_EQ([controller_ profiles][i], *profiles()[i]); + ASSERT_EQ(0, [controller_ profiles][i].Compare(*profiles()[i])); } } TEST_F(AutoFillDialogControllerTest, TwoCreditCards) { - CreditCard credit_card1(ASCIIToUTF16("Visa"), 1); + CreditCard credit_card1; credit_card1.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Joe")); credit_cards().push_back(&credit_card1); - CreditCard credit_card2(ASCIIToUTF16("Mastercard"), 2); + CreditCard credit_card2; credit_card2.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Bob")); credit_cards().push_back(&credit_card2); LoadDialog(); @@ -344,13 +330,12 @@ TEST_F(AutoFillDialogControllerTest, TwoCreditCards) { // Contents should match. With the exception of the |unique_id|. for (size_t i = 0, count = credit_cards().size(); i < count; i++) { - credit_cards()[i]->set_unique_id([controller_ creditCards][i].unique_id()); - ASSERT_EQ([controller_ creditCards][i], *credit_cards()[i]); + ASSERT_EQ(0, [controller_ creditCards][i].Compare(*credit_cards()[i])); } } TEST_F(AutoFillDialogControllerTest, AddNewProfile) { - AutoFillProfile profile(ASCIIToUTF16("One"), 1); + AutoFillProfile profile; profile.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Joe")); profiles().push_back(&profile); LoadDialog(); @@ -373,12 +358,11 @@ TEST_F(AutoFillDialogControllerTest, AddNewProfile) { // New address should match. Don't compare labels. AutoFillProfile new_profile; new_profile.SetInfo(AutoFillType(NAME_FULL), ASCIIToUTF16("Don")); - observer_.profiles_[1].set_label(string16()); - ASSERT_EQ(observer_.profiles_[1], new_profile); + ASSERT_EQ(0, observer_.profiles_[1].Compare(new_profile)); } TEST_F(AutoFillDialogControllerTest, AddNewCreditCard) { - CreditCard credit_card(ASCIIToUTF16("Visa"), 1); + CreditCard credit_card; credit_card.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Joe")); credit_cards().push_back(&credit_card); LoadDialog(); @@ -402,13 +386,11 @@ TEST_F(AutoFillDialogControllerTest, AddNewCreditCard) { // New credit card should match. Don't compare labels. CreditCard new_credit_card; new_credit_card.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Don")); - new_credit_card.set_billing_address_id(0); - observer_.credit_cards_[1].set_label(string16()); - ASSERT_EQ(observer_.credit_cards_[1], new_credit_card); + ASSERT_EQ(0, observer_.credit_cards_[1].Compare(new_credit_card)); } TEST_F(AutoFillDialogControllerTest, AddNewEmptyProfile) { - AutoFillProfile profile(string16(), 1); + AutoFillProfile profile; profile.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Joe")); profiles().push_back(&profile); LoadDialog(); @@ -426,7 +408,7 @@ TEST_F(AutoFillDialogControllerTest, AddNewEmptyProfile) { } TEST_F(AutoFillDialogControllerTest, AddNewEmptyCreditCard) { - CreditCard credit_card(string16(), 1); + CreditCard credit_card; credit_card.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Joe")); credit_cards().push_back(&credit_card); LoadDialog(); @@ -445,7 +427,7 @@ TEST_F(AutoFillDialogControllerTest, AddNewEmptyCreditCard) { } TEST_F(AutoFillDialogControllerTest, DeleteProfile) { - AutoFillProfile profile(ASCIIToUTF16("One"), 1); + AutoFillProfile profile; profile.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Joe")); profiles().push_back(&profile); LoadDialog(); @@ -462,7 +444,7 @@ TEST_F(AutoFillDialogControllerTest, DeleteProfile) { } TEST_F(AutoFillDialogControllerTest, DeleteCreditCard) { - CreditCard credit_card(ASCIIToUTF16("Visa"), 1); + CreditCard credit_card; credit_card.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Joe")); credit_cards().push_back(&credit_card); LoadDialog(); @@ -479,10 +461,10 @@ TEST_F(AutoFillDialogControllerTest, DeleteCreditCard) { } TEST_F(AutoFillDialogControllerTest, TwoProfilesDeleteOne) { - AutoFillProfile profile(ASCIIToUTF16("One"), 1); + AutoFillProfile profile; profile.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Joe")); profiles().push_back(&profile); - AutoFillProfile profile2(ASCIIToUTF16("Two"), 2); + AutoFillProfile profile2; profile2.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Bob")); profiles().push_back(&profile2); LoadDialog(); @@ -496,21 +478,14 @@ TEST_F(AutoFillDialogControllerTest, TwoProfilesDeleteOne) { // Sizes should be different. New size should be 1. ASSERT_NE(observer_.profiles_.size(), profiles().size()); ASSERT_EQ(observer_.profiles_.size(), 1UL); - - // First address should match. - profiles()[0]->set_unique_id(observer_.profiles_[0].unique_id()); - - // Do not compare labels. Label is a derived field. - observer_.profiles_[0].set_label(string16()); - profile.set_label(string16()); - ASSERT_EQ(observer_.profiles_[0], profile); + ASSERT_EQ(0, observer_.profiles_[0].Compare(profile)); } TEST_F(AutoFillDialogControllerTest, TwoCreditCardsDeleteOne) { - CreditCard credit_card(ASCIIToUTF16("Visa"), 1); + CreditCard credit_card; credit_card.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Joe")); credit_cards().push_back(&credit_card); - CreditCard credit_card2(ASCIIToUTF16("Mastercard"), 2); + CreditCard credit_card2; credit_card2.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Bob")); credit_cards().push_back(&credit_card2); LoadDialog(); @@ -526,22 +501,21 @@ TEST_F(AutoFillDialogControllerTest, TwoCreditCardsDeleteOne) { ASSERT_EQ(observer_.credit_cards_.size(), 1UL); // First credit card should match. - credit_cards()[0]->set_unique_id(observer_.credit_cards_[0].unique_id()); - ASSERT_EQ(observer_.credit_cards_[0], credit_card); + ASSERT_EQ(0, observer_.credit_cards_[0].Compare(credit_card)); } TEST_F(AutoFillDialogControllerTest, DeleteMultiple) { - AutoFillProfile profile(ASCIIToUTF16("One"), 1); + AutoFillProfile profile; profile.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Joe")); profiles().push_back(&profile); - AutoFillProfile profile2(ASCIIToUTF16("Two"), 2); + AutoFillProfile profile2; profile2.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Bob")); profiles().push_back(&profile2); - CreditCard credit_card(ASCIIToUTF16("Visa"), 1); + CreditCard credit_card; credit_card.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Joe")); credit_cards().push_back(&credit_card); - CreditCard credit_card2(ASCIIToUTF16("Mastercard"), 2); + CreditCard credit_card2; credit_card2.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Bob")); credit_cards().push_back(&credit_card2); @@ -565,17 +539,11 @@ TEST_F(AutoFillDialogControllerTest, DeleteMultiple) { ASSERT_NE(observer_.credit_cards_.size(), credit_cards().size()); ASSERT_EQ(observer_.credit_cards_.size(), 1UL); - // First address should match. - profiles()[0]->set_unique_id(observer_.profiles_[0].unique_id()); - - // Do not compare labels. Label is a derived field. - observer_.profiles_[0].set_label(string16()); - profile.set_label(string16()); - ASSERT_EQ(observer_.profiles_[0], profile); + // Profiles should match. + ASSERT_EQ(0, observer_.profiles_[0].Compare(profile)); // Second credit card should match. - credit_cards()[0]->set_unique_id(observer_.credit_cards_[0].unique_id()); - ASSERT_EQ(observer_.credit_cards_[0], credit_card2); + ASSERT_EQ(0, observer_.credit_cards_[0].Compare(credit_card2)); } // Auxilliary profiles are enabled by default. @@ -621,9 +589,9 @@ TEST_F(AutoFillDialogControllerTest, AuxiliaryProfilesChanged) { } TEST_F(AutoFillDialogControllerTest, WaitForDataToLoad) { - AutoFillProfile profile(ASCIIToUTF16("Home"), 0); + AutoFillProfile profile; profiles().push_back(&profile); - CreditCard credit_card(ASCIIToUTF16("Visa"), 0); + CreditCard credit_card; credit_cards().push_back(&credit_card); helper_.test_profile_->test_manager_->test_data_is_loaded_ = false; LoadDialog(); diff --git a/chrome/browser/autofill/autofill_dialog_gtk.cc b/chrome/browser/autofill/autofill_dialog_gtk.cc index bc305ff..de74b94 100644 --- a/chrome/browser/autofill/autofill_dialog_gtk.cc +++ b/chrome/browser/autofill/autofill_dialog_gtk.cc @@ -612,7 +612,7 @@ void AutoFillDialog::AddAddressToTree(const AutoFillProfile& profile, list_store_, iter, COL_WEIGHT, PANGO_WEIGHT_NORMAL, COL_WEIGHT_SET, TRUE, - COL_TITLE, UTF16ToUTF8(profile.PreviewSummary()).c_str(), + COL_TITLE, UTF16ToUTF8(profile.Label()).c_str(), -1); } diff --git a/chrome/browser/autofill/autofill_download.cc b/chrome/browser/autofill/autofill_download.cc index a08794b..a1a83c5 100644 --- a/chrome/browser/autofill/autofill_download.cc +++ b/chrome/browser/autofill/autofill_download.cc @@ -71,15 +71,10 @@ bool AutoFillDownloadManager::StartQueryRequest( return false; } std::string form_xml; - FormStructure::EncodeQueryRequest(forms, &form_xml); - FormRequestData request_data; - request_data.form_signatures.reserve(forms.size()); - for (ScopedVector<FormStructure>::const_iterator it = forms.begin(); - it != forms.end(); - ++it) { - request_data.form_signatures.push_back((*it)->FormSignature()); - } + if (!FormStructure::EncodeQueryRequest(forms, &request_data.form_signatures, + &form_xml)) + return false; request_data.request_type = AutoFillDownloadManager::REQUEST_QUERY; @@ -102,7 +97,8 @@ bool AutoFillDownloadManager::StartUploadRequest( return false; } std::string form_xml; - form.EncodeUploadRequest(form_was_matched, &form_xml); + if (!form.EncodeUploadRequest(form_was_matched, &form_xml)) + return false; FormRequestData request_data; request_data.form_signatures.push_back(form.FormSignature()); @@ -238,8 +234,8 @@ void AutoFillDownloadManager::OnURLFetchComplete(const URLFetcher* source, } } - VLOG(1) << "AutoFillDownloadManager: " << type_of_request - << " request has failed with response" << response_code; + LOG(WARNING) << "AutoFillDownloadManager: " << type_of_request + << " request has failed with response" << response_code; if (observer_) { observer_->OnHeuristicsRequestError(it->second.form_signatures[0], it->second.request_type, diff --git a/chrome/browser/autofill/auto_fill_editor_gtk.cc b/chrome/browser/autofill/autofill_editor_gtk.cc index 09442d7..21f7ef3 100644 --- a/chrome/browser/autofill/auto_fill_editor_gtk.cc +++ b/chrome/browser/autofill/autofill_editor_gtk.cc @@ -201,7 +201,7 @@ class AutoFillProfileEditor { // If is_new_ is false this is the unique id of the profile the user is // editing. - const int profile_id_; + const std::string profile_guid_; AutoFillDialogObserver* observer_; @@ -231,7 +231,8 @@ AutoFillProfileEditor::AutoFillProfileEditor( Profile* profile, AutoFillProfile* auto_fill_profile) : is_new_(!auto_fill_profile ? true : false), - profile_id_(auto_fill_profile ? auto_fill_profile->unique_id() : 0), + profile_guid_(auto_fill_profile ? auto_fill_profile->guid() + : std::string()), observer_(observer), profile_(profile) { Init(); @@ -391,7 +392,7 @@ void AutoFillProfileEditor::ApplyEdits() { // The user is editing an existing profile, find it. for (std::vector<AutoFillProfile>::iterator i = profiles.begin(); i != profiles.end(); ++i) { - if (i->unique_id() == profile_id_) { + if (i->guid() == profile_guid_) { profile = &(*i); break; } @@ -530,25 +531,8 @@ class AutoFillCreditCardEditor { private: friend class DeleteTask<AutoFillCreditCardEditor>; - // Types of columns in the address_store_. - enum ColumnTypes { - // Unique if of the CreditCard. - COL_ID, - - // Title of the column. - COL_TITLE, - - COL_COUNT - }; - virtual ~AutoFillCreditCardEditor() {} - // Creates the GtkListStore used to show the billing addresses. - GtkListStore* CreateAddressStore(); - - // Creates the combobox used to show the billing addresses. - GtkWidget* CreateAddressWidget(); - // Creates the combobox for choosing the month. GtkWidget* CreateMonthWidget(); @@ -587,7 +571,7 @@ class AutoFillCreditCardEditor { // If is_new_ is false this is the unique id of the credit card the user is // editing. - const int credit_card_id_; + const std::string credit_card_guid_; AutoFillDialogObserver* observer_; @@ -601,14 +585,11 @@ class AutoFillCreditCardEditor { GtkWidget* dialog_; GtkWidget* name_; - GtkWidget* address_; GtkWidget* number_; GtkWidget* month_; GtkWidget* year_; GtkWidget* ok_button_; - GtkListStore* address_store_; - DISALLOW_COPY_AND_ASSIGN(AutoFillCreditCardEditor); }; @@ -617,7 +598,7 @@ AutoFillCreditCardEditor::AutoFillCreditCardEditor( Profile* profile, CreditCard* credit_card) : is_new_(!credit_card ? true : false), - credit_card_id_(credit_card ? credit_card->unique_id() : 0), + credit_card_guid_(credit_card ? credit_card->guid() : std::string()), observer_(observer), profile_(profile), base_year_(0), @@ -631,11 +612,7 @@ AutoFillCreditCardEditor::AutoFillCreditCardEditor( if (credit_card) { SetWidgetValues(credit_card); } else { - // We're creating a new credit card. Select a default billing address (if - // there are any) and select January of next year. - PersonalDataManager* data_manager = profile_->GetPersonalDataManager(); - if (!data_manager->profiles().empty()) - gtk_combo_box_set_active(GTK_COMBO_BOX(address_), 0); + // We're creating a new credit card. Select January of next year by default. gtk_combo_box_set_active(GTK_COMBO_BOX(month_), 0); gtk_combo_box_set_active(GTK_COMBO_BOX(year_), 1); } @@ -652,54 +629,6 @@ AutoFillCreditCardEditor::AutoFillCreditCardEditor( gtk_util::PresentWindow(dialog_, gtk_get_current_event_time()); } -GtkListStore* AutoFillCreditCardEditor::CreateAddressStore() { - GtkListStore* store = - gtk_list_store_new(COL_COUNT, G_TYPE_INT, G_TYPE_STRING); - - GtkTreeIter iter; - - PersonalDataManager* data_manager = profile_->GetPersonalDataManager(); - for (std::vector<AutoFillProfile*>::const_iterator i = - data_manager->profiles().begin(); - i != data_manager->profiles().end(); ++i) { - FieldTypeSet fields; - (*i)->GetAvailableFieldTypes(&fields); - if (fields.find(ADDRESS_HOME_LINE1) == fields.end() && - fields.find(ADDRESS_HOME_LINE2) == fields.end() && - fields.find(ADDRESS_HOME_APT_NUM) == fields.end() && - fields.find(ADDRESS_HOME_CITY) == fields.end() && - fields.find(ADDRESS_HOME_STATE) == fields.end() && - fields.find(ADDRESS_HOME_ZIP) == fields.end() && - fields.find(ADDRESS_HOME_COUNTRY) == fields.end()) { - // No address information in this profile; it's useless as a billing - // address. - continue; - } - - gtk_list_store_append(store, &iter); - gtk_list_store_set( - store, &iter, - COL_ID, (*i)->unique_id(), - COL_TITLE, UTF16ToUTF8((*i)->PreviewSummary()).c_str(), - -1); - } - return store; -} - -GtkWidget* AutoFillCreditCardEditor::CreateAddressWidget() { - address_store_ = CreateAddressStore(); - - GtkWidget* widget = gtk_combo_box_new_with_model( - GTK_TREE_MODEL(address_store_)); - g_object_unref(address_store_); - - GtkCellRenderer* cell = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), cell, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), cell, - "text", COL_TITLE, NULL); - return widget; -} - GtkWidget* AutoFillCreditCardEditor::CreateMonthWidget() { GtkWidget* combobox = gtk_combo_box_new_text(); for (int i = 1; i <= 12; ++i) { @@ -723,16 +652,12 @@ GtkWidget* AutoFillCreditCardEditor::CreateYearWidget() { } void AutoFillCreditCardEditor::Init() { - TableBuilder main_table_builder(8, 2); + TableBuilder main_table_builder(6, 2); main_table_builder.AddWidget( CreateLabel(IDS_AUTOFILL_DIALOG_NAME_ON_CARD), 2); name_ = main_table_builder.AddWidget(gtk_entry_new(), 2); - main_table_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_BILLING_ADDRESS), - 2); - address_ = main_table_builder.AddWidget(CreateAddressWidget(), 2); - main_table_builder.AddWidget( CreateLabel(IDS_AUTOFILL_DIALOG_CREDIT_CARD_NUMBER), 2); number_ = main_table_builder.AddWidget(gtk_entry_new(), 1); @@ -785,17 +710,6 @@ void AutoFillCreditCardEditor::RegisterForTextChanged() { void AutoFillCreditCardEditor::SetWidgetValues(CreditCard* card) { SetEntryText(name_, card, CREDIT_CARD_NAME); - PersonalDataManager* data_manager = profile_->GetPersonalDataManager(); - for (std::vector<AutoFillProfile*>::const_iterator i = - data_manager->profiles().begin(); - i != data_manager->profiles().end(); ++i) { - if ((*i)->unique_id() == card->billing_address_id()) { - int index = static_cast<int>(i - data_manager->profiles().begin()); - gtk_combo_box_set_active(GTK_COMBO_BOX(address_), index); - break; - } - } - gtk_entry_set_text(GTK_ENTRY(number_), UTF16ToUTF8(card->ObfuscatedNumber()).c_str()); @@ -834,7 +748,7 @@ void AutoFillCreditCardEditor::ApplyEdits() { // The user is editing an existing credit card, find it. for (std::vector<CreditCard>::iterator i = cards.begin(); i != cards.end(); ++i) { - if (i->unique_id() == credit_card_id_) { + if (i->guid() == credit_card_guid_) { card = &(*i); break; } @@ -857,32 +771,8 @@ void AutoFillCreditCardEditor::ApplyEdits() { void AutoFillCreditCardEditor::SetCreditCardValuesFromWidgets( CreditCard* card) { - PersonalDataManager* data_manager = profile_->GetPersonalDataManager(); - SetFormValue(name_, card, CREDIT_CARD_NAME); - card->set_billing_address_id(0); - int selected_address_index = - gtk_combo_box_get_active(GTK_COMBO_BOX(address_)); - if (selected_address_index != -1) { - GtkTreeIter iter; - gtk_tree_model_iter_nth_child( - GTK_TREE_MODEL(address_store_), &iter, NULL, selected_address_index); - GValue value = { 0 }; - gtk_tree_model_get_value( - GTK_TREE_MODEL(address_store_), &iter, COL_ID, &value); - int id = g_value_get_int(&value); - for (std::vector<AutoFillProfile*>::const_iterator i = - data_manager->profiles().begin(); - i != data_manager->profiles().end(); ++i) { - if ((*i)->unique_id() == id) { - card->set_billing_address_id(id); - break; - } - } - g_value_unset(&value); - } - if (edited_number_) SetFormValue(number_, card, CREDIT_CARD_NUMBER); diff --git a/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc b/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc index dcfe0a5..0165f1c 100644 --- a/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc +++ b/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc @@ -9,6 +9,7 @@ #include "base/win/registry.h" #include "chrome/browser/autofill/autofill_profile.h" #include "chrome/browser/autofill/credit_card.h" +#include "chrome/browser/autofill/crypto/rc4_decryptor.h" #include "chrome/browser/autofill/field_types.h" #include "chrome/browser/autofill/personal_data_manager.h" #include "chrome/browser/sync/util/data_encryption.h" @@ -21,17 +22,6 @@ bool ImportCurrentUserProfiles(std::vector<AutoFillProfile>* profiles, std::vector<CreditCard>* credit_cards); namespace { -#if defined(GOOGLE_CHROME_BUILD) -#include "chrome/browser/autofill/internal/autofill_ie_toolbar_decryption.h" -#else // defined(GOOGLE_CHROME_BUILD) -inline std::wstring DecryptCCNumber(const std::wstring& data) { - return std::wstring(); -} -inline bool IsEmptySalt(const std::wstring& salt) { - return false; -} -#endif // defined(GOOGLE_CHROME_BUILD) - const wchar_t* const kProfileKey = L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Profiles"; const wchar_t* const kCreditCardKey = @@ -39,6 +29,31 @@ const wchar_t* const kCreditCardKey = const wchar_t* const kPasswordHashValue = L"password_hash"; const wchar_t* const kSaltValue = L"salt"; +// This is RC4 decryption for Toolbar credit card data. This is necessary +// because it is not standard, so Crypto api cannot be used. +std::wstring DecryptCCNumber(const std::wstring& data) { + const wchar_t* kEmptyKey = + L"\x3605\xCEE5\xCE49\x44F7\xCF4E\xF6CC\x604B\xFCBE\xC70A\x08FD"; + const size_t kMacLen = 10; + + if (data.length() <= kMacLen) + return std::wstring(); + + RC4Decryptor rc4_algorithm(kEmptyKey); + return rc4_algorithm.Run(data.substr(kMacLen)); +} + +bool IsEmptySalt(std::wstring const& salt) { + // Empty salt in IE Toolbar is \x1\x2...\x14 + if (salt.length() != 20) + return false; + for (size_t i = 0; i < salt.length(); ++i) { + if (salt[i] != i + 1) + return false; + } + return true; +} + string16 ReadAndDecryptValue(RegKey* key, const wchar_t* value_name) { DWORD data_type = REG_BINARY; DWORD data_size = 0; diff --git a/chrome/browser/autofill/autofill_ie_toolbar_import_win_unittest.cc b/chrome/browser/autofill/autofill_ie_toolbar_import_win_unittest.cc index 0512139..5b23ee5 100644 --- a/chrome/browser/autofill/autofill_ie_toolbar_import_win_unittest.cc +++ b/chrome/browser/autofill/autofill_ie_toolbar_import_win_unittest.cc @@ -198,8 +198,7 @@ TEST_F(AutofillIeToolbarImportTest, TestAutoFillImport) { L"5556666"); EXPECT_EQ(profiles[0].GetFieldText(AutoFillType(PHONE_FAX_WHOLE_NUMBER)), L"27775556666"); -#if defined(GOOGLE_CHROME_BUILD) - // We have the ability to export credit cards only in chrome build. + ASSERT_EQ(credit_cards.size(), 1); EXPECT_EQ(credit_cards[0].GetFieldText(AutoFillType(CREDIT_CARD_NAME)), credit_card[0].value); @@ -225,9 +224,5 @@ TEST_F(AutofillIeToolbarImportTest, TestAutoFillImport) { EXPECT_EQ(profiles.size(), 2); // Credit cards are. EXPECT_EQ(credit_cards.size(), 0); -#else // defined(GOOGLE_CHROME_BUILD) - // Cannot decrypt CC in non-chrome build. - EXPECT_EQ(credit_cards.size(), 0); -#endif // defined(GOOGLE_CHROME_BUILD) } diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index 8e80752..ef0fee9 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -16,6 +16,7 @@ #include "chrome/browser/autofill/autofill_dialog.h" #include "chrome/browser/autofill/form_structure.h" #include "chrome/browser/autofill/select_control_handler.h" +#include "chrome/browser/guid.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" #ifndef ANDROID @@ -51,18 +52,6 @@ const int kAutoFillPhoneNumberSuffixCount = 4; const string16::value_type kCreditCardPrefix[] = {'*',0}; const string16::value_type kLabelSeparator[] = {';',' ','*',0}; -// Combines the |label| string with the last four digits of the credit card -// |cc|. If one, the other, or both are empty strings we omit the separator. -string16 CombineLabelAndCreditCard(const string16& label, - const CreditCard* cc) { - if (label.empty()) - return kCreditCardPrefix + cc->LastFourDigits(); - else if (cc->LastFourDigits().empty()) - return label; - else - return label + kLabelSeparator + cc->LastFourDigits(); -} - // Removes duplicate elements whilst preserving original order of |elements| and // |unique_ids|. void RemoveDuplicateElements( @@ -168,7 +157,7 @@ void AutoFillManager::FormsSeen(const std::vector<FormData>& forms) { } bool AutoFillManager::GetAutoFillSuggestions(int query_id, - bool form_autofilled, + bool field_autofilled, const FormField& field) { if (!IsAutoFillEnabled()) return false; @@ -223,19 +212,13 @@ bool AutoFillManager::GetAutoFillSuggestions(int query_id, std::vector<int> unique_ids; AutoFillType type(autofill_field->type()); - // If this form is non-HTTPS, treat billing address fields as regular address - // fields. - bool handle_billing = FormIsHTTPS(form); - - if (type.group() == AutoFillType::CREDIT_CARD) + if (type.group() == AutoFillType::CREDIT_CARD) { GetCreditCardSuggestions( form, field, type, &values, &labels, &icons, &unique_ids); - else if (type.group() == AutoFillType::ADDRESS_BILLING) - GetBillingProfileSuggestions( + } else { + GetProfileSuggestions( form, field, type, &values, &labels, &icons, &unique_ids); - else - GetProfileSuggestions(form, field, type, handle_billing, - &values, &labels, &icons, &unique_ids); + } DCHECK_EQ(values.size(), labels.size()); DCHECK_EQ(values.size(), icons.size()); @@ -246,17 +229,19 @@ bool AutoFillManager::GetAutoFillSuggestions(int query_id, return false; // If the form is auto-filled and the renderer is querying for suggestions, - // then the user is editing the value of a field. In this case, don't display - // labels, as that information is redundant. In addition, remove duplicate - // values. - if (form_autofilled) { + // then the user is editing the value of a field. In this case, mimick + // autocomplete. In particular, don't display labels, as that information is + // redundant. In addition, remove duplicate values. + if (field_autofilled) { RemoveDuplicateElements(&values, &unique_ids); labels.resize(values.size()); icons.resize(values.size()); + unique_ids.resize(values.size()); for (size_t i = 0; i < labels.size(); ++i) { labels[i] = string16(); icons[i] = string16(); + unique_ids[i] = 0; } } @@ -306,32 +291,35 @@ bool AutoFillManager::FillAutoFillFormData(int query_id, return false; // Unpack the |unique_id| into component parts. - int cc_id = 0; - int profile_id = 0; - UnpackIDs(unique_id, &cc_id, &profile_id); + std::string cc_guid; + std::string profile_guid; + UnpackGUIDs(unique_id, &cc_guid, &profile_guid); + DCHECK(!guid::IsValidGUID(cc_guid) || !guid::IsValidGUID(profile_guid)); // Find the profile that matches the |profile_id|, if one is specified. const AutoFillProfile* profile = NULL; - if (profile_id != 0) { + if (guid::IsValidGUID(profile_guid)) { for (std::vector<AutoFillProfile*>::const_iterator iter = profiles.begin(); iter != profiles.end(); ++iter) { - if ((*iter)->unique_id() == profile_id) { + if ((*iter)->guid() == profile_guid) { profile = *iter; break; } } + DCHECK(profile); } // Find the credit card that matches the |cc_id|, if one is specified. const CreditCard* credit_card = NULL; - if (cc_id != 0) { + if (guid::IsValidGUID(cc_guid)) { for (std::vector<CreditCard*>::const_iterator iter = credit_cards.begin(); iter != credit_cards.end(); ++iter) { - if ((*iter)->unique_id() == cc_id) { + if ((*iter)->guid() == cc_guid) { credit_card = *iter; break; } } + DCHECK(credit_card); } if (!profile && !credit_card) @@ -364,9 +352,6 @@ bool AutoFillManager::FillAutoFillFormData(int query_id, if (credit_card && autofill_type.group() == AutoFillType::CREDIT_CARD) { FillCreditCardFormField(credit_card, autofill_type, &result.fields[j]); - } else if (credit_card && - autofill_type.group() == AutoFillType::ADDRESS_BILLING) { - FillBillingFormField(credit_card, autofill_type, &result.fields[j]); } else if (profile) { FillFormField(profile, autofill_type, &result.fields[j]); } @@ -520,7 +505,6 @@ AutoFillManager::AutoFillManager(TabContents* tab_contents, void AutoFillManager::GetProfileSuggestions(FormStructure* form, const FormField& field, AutoFillType type, - bool include_cc_labels, std::vector<string16>* values, std::vector<string16>* labels, std::vector<string16>* icons, @@ -538,113 +522,26 @@ void AutoFillManager::GetProfileSuggestions(FormStructure* form, StartsWith(profile_field_value, field.value(), false)) { matched_profiles.push_back(profile); values->push_back(profile_field_value); - unique_ids->push_back(PackIDs(0, profile->unique_id())); - } - } - - AutoFillProfile::CreateInferredLabels(&matched_profiles, labels, 0, - type.field_type()); - - if (!include_cc_labels || !form->HasBillingFields() || !FormIsHTTPS(form)) { - icons->resize(values->size()); // No CC, so no icons. - return; - } - - size_t i = 0; - std::vector<string16> expanded_values; - std::vector<string16> expanded_labels; - std::vector<int> expanded_ids; - for (std::vector<AutoFillProfile*>::const_iterator iter = - matched_profiles.begin(); iter != matched_profiles.end(); - ++iter, ++i) { - AutoFillProfile* profile = *iter; - for (std::vector<CreditCard*>::const_iterator cc = - personal_data_->credit_cards().begin(); - cc != personal_data_->credit_cards().end(); ++cc) { - expanded_values.push_back((*values)[i]); - string16 label = CombineLabelAndCreditCard((*labels)[i], *cc); - expanded_labels.push_back(label); - icons->push_back((*cc)->type()); - expanded_ids.push_back(PackIDs((*cc)->unique_id(), profile->unique_id())); + unique_ids->push_back(PackGUIDs(std::string(), profile->guid())); } } - expanded_labels.swap(*labels); - expanded_values.swap(*values); - expanded_ids.swap(*unique_ids); -} - -void AutoFillManager::GetBillingProfileSuggestions( - FormStructure* form, - const FormField& field, - AutoFillType type, - std::vector<string16>* values, - std::vector<string16>* labels, - std::vector<string16>* icons, - std::vector<int>* unique_ids) { - - // If the form is non-HTTPS, no CC suggestions are provided; however, give the - // user the option of filling the billing address fields with regular address - // data. - if (!FormIsHTTPS(form)) { - GetProfileSuggestions( - form, field, type, false, values, icons, labels, unique_ids); - return; + std::vector<AutoFillFieldType> form_fields; + form_fields.reserve(form->field_count()); + for (std::vector<AutoFillField*>::const_iterator iter = form->begin(); + iter != form->end(); ++iter) { + // The field list is terminated with a NULL AutoFillField, so don't try to + // dereference it. + if (!*iter) + break; + form_fields.push_back((*iter)->type()); } - std::vector<CreditCard*> matching_creditcards; - std::vector<AutoFillProfile*> matching_profiles; - - // Collect matching pairs of credit cards and related profiles, where profile - // field value matches form field value. - for (std::vector<CreditCard*>::const_iterator cc = - personal_data_->credit_cards().begin(); - cc != personal_data_->credit_cards().end(); ++cc) { - int billing_address_id = (*cc)->billing_address_id(); - AutoFillProfile* billing_profile = NULL; - - // The value of the stored data for this field type in the |profile|. - string16 profile_field_value; - - for (std::vector<AutoFillProfile*>::const_iterator iter = - personal_data_->profiles().begin(); - iter != personal_data_->profiles().end(); ++iter) { - AutoFillProfile* profile = *iter; - - // This assumes that labels are unique. - if (profile->unique_id() == billing_address_id && - !profile->GetFieldText(type).empty() && - StartsWith(profile->GetFieldText(type), field.value(), false)) { - billing_profile = profile; - break; - } - } - - if (!billing_profile) - continue; - - matching_creditcards.push_back(*cc); - matching_profiles.push_back(billing_profile); - } + AutoFillProfile::CreateInferredLabels(&matched_profiles, labels, 0, + type.field_type(), &form_fields); - std::vector<string16> inferred_labels; - AutoFillProfile::CreateInferredLabels(&matching_profiles, &inferred_labels, 0, - type.field_type()); - - DCHECK_EQ(matching_profiles.size(), matching_creditcards.size()); - DCHECK_EQ(matching_profiles.size(), inferred_labels.size()); - - // Process the matching pairs into suggested |values|, |labels|, and - // |unique_ids|. - for (size_t i = 0; i < matching_profiles.size(); ++i) { - values->push_back(matching_profiles[i]->GetFieldText(type)); - string16 label = CombineLabelAndCreditCard(inferred_labels[i], - matching_creditcards[i]); - labels->push_back(label); - icons->push_back(matching_creditcards[i]->type()); - unique_ids->push_back(PackIDs(matching_creditcards[i]->unique_id(), - matching_profiles[i]->unique_id())); - } + // No icons for profile suggestions. + icons->resize(values->size()); } void AutoFillManager::GetCreditCardSuggestions(FormStructure* form, @@ -670,57 +567,10 @@ void AutoFillManager::GetCreditCardSuggestions(FormStructure* form, if (type.field_type() == CREDIT_CARD_NUMBER) creditcard_field_value = credit_card->ObfuscatedNumber(); - if (!form->HasNonBillingFields()) { - values->push_back(creditcard_field_value); - labels->push_back(CombineLabelAndCreditCard(string16(), credit_card)); - icons->push_back(credit_card->type()); - unique_ids->push_back(PackIDs(credit_card->unique_id(), 0)); - } else { - const std::vector<AutoFillProfile*>& profiles - = personal_data_->profiles(); - std::vector<string16> inferred_labels; - AutoFillProfile::CreateInferredLabels(&profiles, - &inferred_labels, - 0, - type.field_type()); - DCHECK_EQ(profiles.size(), inferred_labels.size()); - - for (size_t i = 0; i < profiles.size(); ++i) { - values->push_back(creditcard_field_value); - - string16 label = CombineLabelAndCreditCard(inferred_labels[i], - credit_card); - labels->push_back(label); - icons->push_back(credit_card->type()); - unique_ids->push_back( - PackIDs(credit_card->unique_id(), profiles[i]->unique_id())); - } - } - } - } -} - -void AutoFillManager::FillBillingFormField(const CreditCard* credit_card, - AutoFillType type, - webkit_glue::FormField* field) { - DCHECK(credit_card); - DCHECK(type.group() == AutoFillType::ADDRESS_BILLING); - DCHECK(field); - - int billing_address_id = credit_card->billing_address_id(); - if (billing_address_id != 0) { - AutoFillProfile* profile = NULL; - const std::vector<AutoFillProfile*>& profiles = personal_data_->profiles(); - for (std::vector<AutoFillProfile*>::const_iterator iter = profiles.begin(); - iter != profiles.end(); ++iter) { - if ((*iter)->unique_id() == billing_address_id) { - profile = *iter; - break; - } - } - - if (profile) { - FillFormField(profile, type, field); + values->push_back(creditcard_field_value); + labels->push_back(kCreditCardPrefix + credit_card->LastFourDigits()); + icons->push_back(credit_card->type()); + unique_ids->push_back(PackGUIDs(credit_card->guid(), std::string())); } } } @@ -796,8 +646,11 @@ void AutoFillManager::ParseForms( // When sending IDs (across processes) to the renderer we pack credit card and // profile IDs into a single integer. Credit card IDs are sent in the high // word and profile IDs are sent in the low word. -// static -int AutoFillManager::PackIDs(int cc_id, int profile_id) { +int AutoFillManager::PackGUIDs(const std::string& cc_guid, + const std::string& profile_guid) { + int cc_id = GUIDToID(cc_guid); + int profile_id = GUIDToID(profile_guid); + DCHECK(cc_id <= std::numeric_limits<unsigned short>::max()); DCHECK(profile_id <= std::numeric_limits<unsigned short>::max()); @@ -807,9 +660,42 @@ int AutoFillManager::PackIDs(int cc_id, int profile_id) { // When receiving IDs (across processes) from the renderer we unpack credit card // and profile IDs from a single integer. Credit card IDs are stored in the // high word and profile IDs are stored in the low word. -// static -void AutoFillManager::UnpackIDs(int id, int* cc_id, int* profile_id) { - *cc_id = id >> std::numeric_limits<unsigned short>::digits & +void AutoFillManager::UnpackGUIDs(int id, + std::string* cc_guid, + std::string* profile_guid) { + int cc_id = id >> std::numeric_limits<unsigned short>::digits & std::numeric_limits<unsigned short>::max(); - *profile_id = id & std::numeric_limits<unsigned short>::max(); + int profile_id = id & std::numeric_limits<unsigned short>::max(); + + *cc_guid = IDToGUID(cc_id); + *profile_guid = IDToGUID(profile_id); +} + +int AutoFillManager::GUIDToID(const std::string& guid) { + static int last_id = 1; + + if (!guid::IsValidGUID(guid)) + return 0; + + std::map<std::string, int>::const_iterator iter = guid_id_map_.find(guid); + if (iter == guid_id_map_.end()) { + guid_id_map_[guid] = last_id; + id_guid_map_[last_id] = guid; + return last_id++; + } else { + return iter->second; + } +} + +const std::string AutoFillManager::IDToGUID(int id) { + if (id == 0) + return std::string(); + + std::map<int, std::string>::const_iterator iter = id_guid_map_.find(id); + if (iter == id_guid_map_.end()) { + NOTREACHED(); + return std::string(); + } + + return iter->second; } diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h index 51c322f..6e4859d 100644 --- a/chrome/browser/autofill/autofill_manager.h +++ b/chrome/browser/autofill/autofill_manager.h @@ -58,7 +58,7 @@ class AutoFillManager : virtual void FormSubmitted(const webkit_glue::FormData& form); virtual void FormsSeen(const std::vector<webkit_glue::FormData>& forms); virtual bool GetAutoFillSuggestions(int query_id, - bool form_autofilled, + bool field_autofilled, const webkit_glue::FormField& field); virtual bool FillAutoFillFormData(int query_id, const webkit_glue::FormData& form, @@ -106,30 +106,15 @@ class AutoFillManager : private: // Returns a list of values from the stored profiles that match |type| and the // value of |field| and returns the labels of the matching profiles. |labels| - // is filled with the Profile label and possibly the last four digits of a - // corresponding credit card: 'Home; *1258' - Home is the Profile label and - // 1258 is the last four digits of the credit card. If |include_cc_labels| is - // true, check for billing fields and append CC digits to the labels; - // otherwise, regular profiles are returned for billing address fields. + // is filled with the Profile label. void GetProfileSuggestions(FormStructure* form, const webkit_glue::FormField& field, AutoFillType type, - bool include_cc_labels, std::vector<string16>* values, std::vector<string16>* labels, std::vector<string16>* icons, std::vector<int>* unique_ids); - // Same as GetProfileSuggestions, but the list of stored profiles is limited - // to the linked billing addresses from the list of credit cards. - void GetBillingProfileSuggestions(FormStructure* form, - const webkit_glue::FormField& field, - AutoFillType type, - std::vector<string16>* values, - std::vector<string16>* labels, - std::vector<string16>* icons, - std::vector<int>* unique_ids); - // Returns a list of values from the stored credit cards that match |type| and // the value of |field| and returns the labels of the matching credit cards. void GetCreditCardSuggestions(FormStructure* form, @@ -141,14 +126,6 @@ class AutoFillManager : std::vector<int>* unique_ids); // Set |field| argument's value based on |type| and contents of the - // |credit_card|. The |type| field is expected to have main group type of - // ADDRESS_BILLING. The address information is retrieved from the billing - // profile associated with the |credit_card|, if there is one set. - void FillBillingFormField(const CreditCard* credit_card, - AutoFillType type, - webkit_glue::FormField* field); - - // Set |field| argument's value based on |type| and contents of the // |credit_card|. void FillCreditCardFormField(const CreditCard* credit_card, AutoFillType type, @@ -169,8 +146,13 @@ class AutoFillManager : // Methods for packing and unpacking credit card and profile IDs for sending // and receiving to and from the renderer process. - static int PackIDs(int cc_id, int profile_id); - static void UnpackIDs(int id, int* cc_id, int* profile_id); + int PackGUIDs(const std::string& cc_guid, const std::string& profile_guid); + void UnpackGUIDs(int id, std::string* cc_guid, std::string* profile_guid); + + // Maps GUIDs to and from IDs that are used to identify profiles and credit + // cards sent to and from the renderer process. + int GUIDToID(const std::string& guid); + const std::string IDToGUID(int id); // The following function is meant to be called from unit-test only. void set_disable_download_manager_requests(bool value) { @@ -212,10 +194,17 @@ class AutoFillManager : AutoFillCCInfoBarDelegate* cc_infobar_; #endif + // GUID to ID mapping. We keep two maps to convert back and forth. + std::map<std::string, int> guid_id_map_; + std::map<int, std::string> id_guid_map_; + friend class TestAutoFillManager; FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillCreditCardForm); - FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillNonBillingFormSemicolon); - FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillBillFormSemicolon); + FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillAddressForm); + FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillAddressAndCreditCardForm); + FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillPhoneNumber); + FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FormChangesRemoveField); + FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FormChangesAddField); DISALLOW_COPY_AND_ASSIGN(AutoFillManager); }; diff --git a/chrome/browser/autofill/autofill_manager_unittest.cc b/chrome/browser/autofill/autofill_manager_unittest.cc index 7cc9769..264dc80 100644 --- a/chrome/browser/autofill/autofill_manager_unittest.cc +++ b/chrome/browser/autofill/autofill_manager_unittest.cc @@ -67,7 +67,7 @@ class TestPersonalDataManager : public PersonalDataManager { "3734 Elvis Presley Blvd.", "Apt. 10", "Memphis", "Tennessee", "38116", "USA", "12345678901", ""); - profile->set_unique_id(1); + profile->set_guid("00000000-0000-0000-0000-000000000001"); profiles->push_back(profile); profile = new AutoFillProfile; autofill_test::SetProfileInfo(profile, "Work", "Charles", "Hardin", @@ -75,32 +75,31 @@ class TestPersonalDataManager : public PersonalDataManager { "123 Apple St.", "unit 6", "Lubbock", "Texas", "79401", "USA", "23456789012", ""); - profile->set_unique_id(2); + profile->set_guid("00000000-0000-0000-0000-000000000002"); profiles->push_back(profile); profile = new AutoFillProfile; autofill_test::SetProfileInfo(profile, "Empty", "", "", "", "", "", "", "", "", "", "", "", "", ""); - profile->set_unique_id(3); + profile->set_guid("00000000-0000-0000-0000-000000000003"); profiles->push_back(profile); } void CreateTestCreditCards(ScopedVector<CreditCard>* credit_cards) { CreditCard* credit_card = new CreditCard; autofill_test::SetCreditCardInfo(credit_card, "First", "Elvis Presley", - "Visa", "1234567890123456", - "04", "2012", 1); - credit_card->set_unique_id(4); + "4234567890123456", // Visa + "04", "2012"); + credit_card->set_guid("00000000-0000-0000-0000-000000000004"); credit_cards->push_back(credit_card); credit_card = new CreditCard; autofill_test::SetCreditCardInfo(credit_card, "Second", "Buddy Holly", - "Mastercard", "0987654321098765", - "10", "2014", 2); - credit_card->set_unique_id(5); + "5187654321098765", // Mastercard + "10", "2014"); + credit_card->set_guid("00000000-0000-0000-0000-000000000005"); credit_cards->push_back(credit_card); credit_card = new CreditCard; - autofill_test::SetCreditCardInfo(credit_card, "Empty", "", "", "", "", "", - 3); - credit_card->set_unique_id(6); + autofill_test::SetCreditCardInfo(credit_card, "Empty", "", "", "", ""); + credit_card->set_guid("00000000-0000-0000-0000-000000000006"); credit_cards->push_back(credit_card); } @@ -135,7 +134,10 @@ class TestAutoFillManager : public AutoFillManager { DISALLOW_COPY_AND_ASSIGN(TestAutoFillManager); }; -void CreateTestFormData(FormData* form) { +// Populates |form| with data corresponding to a simple address form. +// Note that this actually appends fields to the form data, which can be useful +// for building up more complex test forms. +void CreateTestAddressFormData(FormData* form) { form->name = ASCIIToUTF16("MyForm"); form->method = ASCIIToUTF16("POST"); form->origin = GURL("http://myform.com/form.html"); @@ -178,48 +180,23 @@ void CreateTestFormData(FormData* form) { form->fields.push_back(field); } -void CreateTestFormDataBilling(FormData* form) { +// Populates |form| with data corresponding to a simple credit card form, with. +// Note that this actually appends fields to the form data, which can be useful +// for building up more complex test forms. +void CreateTestCreditCardFormData(FormData* form, bool is_https) { form->name = ASCIIToUTF16("MyForm"); form->method = ASCIIToUTF16("POST"); - form->origin = GURL("https://myform.com/form.html"); - form->action = GURL("https://myform.com/submit.html"); + if (is_https) { + form->origin = GURL("https://myform.com/form.html"); + form->action = GURL("https://myform.com/submit.html"); + } else { + form->origin = GURL("http://myform.com/form.html"); + form->action = GURL("http://myform.com/submit.html"); + } form->user_submitted = true; webkit_glue::FormField field; autofill_test::CreateTestFormField( - "First Name", "firstname", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( - "Middle Name", "middlename", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( - "Last Name", "lastname", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( - "Address Line 1", "billingAddr1", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( - "Address Line 2", "billingAddr2", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( - "City", "billingCity", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( - "State", "billingState", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( - "Postal Code", "billingZipcode", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( - "Country", "billingCountry", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( - "Phone Number", "phonenumber", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( - "Email", "email", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( "Name on Card", "nameoncard", "", "text", &field); form->fields.push_back(field); autofill_test::CreateTestFormField( @@ -254,7 +231,8 @@ class AutoFillManagerTest : public RenderViewHostTestHarness { bool GetAutoFillSuggestionsMessage(int* page_id, std::vector<string16>* values, - std::vector<string16>* labels) { + std::vector<string16>* labels, + std::vector<string16>* icons) { const uint32 kMsgID = ViewMsg_AutoFillSuggestionsReturned::ID; const IPC::Message* message = process()->sink().GetFirstMessageMatching(kMsgID); @@ -269,6 +247,8 @@ class AutoFillManagerTest : public RenderViewHostTestHarness { *values = autofill_param.b; if (labels) *labels = autofill_param.c; + if (icons) + *icons = autofill_param.d; return true; } @@ -295,9 +275,11 @@ class AutoFillManagerTest : public RenderViewHostTestHarness { DISALLOW_COPY_AND_ASSIGN(AutoFillManagerTest); }; +// Test that we return all address profile suggestions when all form fields are +// empty. TEST_F(AutoFillManagerTest, GetProfileSuggestionsEmptyValue) { FormData form; - CreateTestFormData(&form); + CreateTestAddressFormData(&form); // Set up our FormStructures. std::vector<FormData> forms; @@ -321,7 +303,9 @@ TEST_F(AutoFillManagerTest, GetProfileSuggestionsEmptyValue) { int page_id = 0; std::vector<string16> values; std::vector<string16> labels; - EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + std::vector<string16> icons; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); EXPECT_EQ(kPageID, page_id); ASSERT_EQ(2U, values.size()); EXPECT_EQ(ASCIIToUTF16("Elvis"), values[0]); @@ -331,11 +315,16 @@ TEST_F(AutoFillManagerTest, GetProfileSuggestionsEmptyValue) { // the address #1. EXPECT_EQ(ASCIIToUTF16("3734 Elvis Presley Blvd."), labels[0]); EXPECT_EQ(ASCIIToUTF16("123 Apple St."), labels[1]); + ASSERT_EQ(2U, icons.size()); + EXPECT_EQ(string16(), icons[0]); + EXPECT_EQ(string16(), icons[1]); } +// Test that we return only matching address profile suggestions when the +// selected form field has been partially filled out. TEST_F(AutoFillManagerTest, GetProfileSuggestionsMatchCharacter) { FormData form; - CreateTestFormData(&form); + CreateTestAddressFormData(&form); // Set up our FormStructures. std::vector<FormData> forms; @@ -359,17 +348,61 @@ TEST_F(AutoFillManagerTest, GetProfileSuggestionsMatchCharacter) { int page_id = 0; std::vector<string16> values; std::vector<string16> labels; - EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + std::vector<string16> icons; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); EXPECT_EQ(kPageID, page_id); ASSERT_EQ(1U, values.size()); EXPECT_EQ(ASCIIToUTF16("Elvis"), values[0]); ASSERT_EQ(1U, labels.size()); EXPECT_EQ(ASCIIToUTF16("3734 Elvis Presley Blvd."), labels[0]); + ASSERT_EQ(1U, icons.size()); + EXPECT_EQ(string16(), icons[0]); +} + +// Test that we return no suggestions when the form has no relevant fields. +TEST_F(AutoFillManagerTest, GetProfileSuggestionsUnknownFields) { + FormData form; + form.name = ASCIIToUTF16("MyForm"); + form.method = ASCIIToUTF16("POST"); + form.origin = GURL("http://myform.com/form.html"); + form.action = GURL("http://myform.com/submit.html"); + form.user_submitted = true; + + webkit_glue::FormField field; + autofill_test::CreateTestFormField( + "Username", "username", "", "text", &field); + form.fields.push_back(field); + autofill_test::CreateTestFormField( + "Password", "password", "", "password", &field); + form.fields.push_back(field); + autofill_test::CreateTestFormField( + "Quest", "quest", "", "quest", &field); + form.fields.push_back(field); + autofill_test::CreateTestFormField( + "Color", "color", "", "text", &field); + form.fields.push_back(field); + + // Set up our FormStructures. + std::vector<FormData> forms; + forms.push_back(form); + autofill_manager_->FormsSeen(forms); + + // The page ID sent to the AutoFillManager from the RenderView, used to send + // an IPC message back to the renderer. + const int kPageID = 1; + + autofill_test::CreateTestFormField( + "Username", "username", "", "text", &field); + EXPECT_FALSE( + autofill_manager_->GetAutoFillSuggestions(kPageID, false, field)); } +// Test that we return all credit card profile suggestions when all form fields +// are empty. TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsEmptyValue) { FormData form; - CreateTestFormDataBilling(&form); + CreateTestCreditCardFormData(&form, true); // Set up our FormStructures. std::vector<FormData> forms; @@ -393,27 +426,26 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsEmptyValue) { int page_id = 0; std::vector<string16> values; std::vector<string16> labels; - EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + std::vector<string16> icons; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); EXPECT_EQ(kPageID, page_id); - ASSERT_EQ(6U, values.size()); + ASSERT_EQ(2U, values.size()); EXPECT_EQ(ASCIIToUTF16("************3456"), values[0]); - EXPECT_EQ(ASCIIToUTF16("************3456"), values[1]); - EXPECT_EQ(ASCIIToUTF16("************3456"), values[2]); - EXPECT_EQ(ASCIIToUTF16("************8765"), values[3]); - EXPECT_EQ(ASCIIToUTF16("************8765"), values[4]); - EXPECT_EQ(ASCIIToUTF16("************8765"), values[5]); - ASSERT_EQ(6U, labels.size()); - EXPECT_EQ(ASCIIToUTF16("Elvis Aaron Presley; *3456"), labels[0]); - EXPECT_EQ(ASCIIToUTF16("Charles Hardin Holley; *3456"), labels[1]); - EXPECT_EQ(ASCIIToUTF16("*3456"), labels[2]); - EXPECT_EQ(ASCIIToUTF16("Elvis Aaron Presley; *8765"), labels[3]); - EXPECT_EQ(ASCIIToUTF16("Charles Hardin Holley; *8765"), labels[4]); - EXPECT_EQ(ASCIIToUTF16("*8765"), labels[5]); + EXPECT_EQ(ASCIIToUTF16("************8765"), values[1]); + ASSERT_EQ(2U, labels.size()); + EXPECT_EQ(ASCIIToUTF16("*3456"), labels[0]); + EXPECT_EQ(ASCIIToUTF16("*8765"), labels[1]); + ASSERT_EQ(2U, icons.size()); + EXPECT_EQ(ASCIIToUTF16("visaCC"), icons[0]); + EXPECT_EQ(ASCIIToUTF16("masterCardCC"), icons[1]); } +// Test that we return only matching credit card profile suggestions when the +// selected form field has been partially filled out. TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsMatchCharacter) { FormData form; - CreateTestFormDataBilling(&form); + CreateTestCreditCardFormData(&form, true); // Set up our FormStructures. std::vector<FormData> forms; @@ -426,7 +458,7 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsMatchCharacter) { webkit_glue::FormField field; autofill_test::CreateTestFormField( - "Card Number", "cardnumber", "1", "text", &field); + "Card Number", "cardnumber", "4", "text", &field); EXPECT_TRUE(autofill_manager_->GetAutoFillSuggestions(kPageID, false, field)); // No suggestions provided, so send an empty vector as the results. @@ -437,21 +469,23 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsMatchCharacter) { int page_id = 0; std::vector<string16> values; std::vector<string16> labels; - EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + std::vector<string16> icons; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); EXPECT_EQ(kPageID, page_id); - ASSERT_EQ(3U, values.size()); + ASSERT_EQ(1U, values.size()); EXPECT_EQ(ASCIIToUTF16("************3456"), values[0]); - EXPECT_EQ(ASCIIToUTF16("************3456"), values[1]); - EXPECT_EQ(ASCIIToUTF16("************3456"), values[2]); - ASSERT_EQ(3U, labels.size()); - EXPECT_EQ(ASCIIToUTF16("Elvis Aaron Presley; *3456"), labels[0]); - EXPECT_EQ(ASCIIToUTF16("Charles Hardin Holley; *3456"), labels[1]); - EXPECT_EQ(ASCIIToUTF16("*3456"), labels[2]); + ASSERT_EQ(1U, labels.size()); + EXPECT_EQ(ASCIIToUTF16("*3456"), labels[0]); + ASSERT_EQ(1U, icons.size()); + EXPECT_EQ(ASCIIToUTF16("visaCC"), icons[0]); } +// Test that we return credit card profile suggestions when the selected form +// field is not the credit card number field. TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsNonCCNumber) { FormData form; - CreateTestFormDataBilling(&form); + CreateTestCreditCardFormData(&form, true); // Set up our FormStructures. std::vector<FormData> forms; @@ -475,36 +509,48 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsNonCCNumber) { int page_id = 0; std::vector<string16> values; std::vector<string16> labels; - EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + std::vector<string16> icons; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); EXPECT_EQ(kPageID, page_id); - ASSERT_EQ(6U, values.size()); + ASSERT_EQ(2U, values.size()); EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[0]); - EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[1]); - EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[2]); - EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[3]); - EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[4]); - EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[5]); - ASSERT_EQ(6U, labels.size()); - EXPECT_EQ(ASCIIToUTF16("Elvis Aaron Presley; *3456"), labels[0]); - EXPECT_EQ(ASCIIToUTF16("Charles Hardin Holley; *3456"), labels[1]); - EXPECT_EQ(ASCIIToUTF16("*3456"), labels[2]); - EXPECT_EQ(ASCIIToUTF16("Elvis Aaron Presley; *8765"), labels[3]); - EXPECT_EQ(ASCIIToUTF16("Charles Hardin Holley; *8765"), labels[4]); - EXPECT_EQ(ASCIIToUTF16("*8765"), labels[5]); + EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[1]); + ASSERT_EQ(2U, labels.size()); + EXPECT_EQ(ASCIIToUTF16("*3456"), labels[0]); + EXPECT_EQ(ASCIIToUTF16("*8765"), labels[1]); + ASSERT_EQ(2U, icons.size()); + EXPECT_EQ(ASCIIToUTF16("visaCC"), icons[0]); + EXPECT_EQ(ASCIIToUTF16("masterCardCC"), icons[1]); } -TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsSemicolon) { - // |profile| will be owned by the mock PersonalDataManager. - AutoFillProfile* profile = new AutoFillProfile; - autofill_test::SetProfileInfo(profile, "Home; 8765", "Joe", "", "Ely", - "flatlander@gmail.com", "MCA", - "916 16th St.", "Apt. 6", "Lubbock", - "Texas", "79401", "USA", - "12345678901", ""); - autofill_manager_->AddProfile(profile); +// Test that we return no credit card profile suggestions when the form is not +// https. +TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsNonHTTPS) { + FormData form; + CreateTestCreditCardFormData(&form, false); + + // Set up our FormStructures. + std::vector<FormData> forms; + forms.push_back(form); + autofill_manager_->FormsSeen(forms); + + // The page ID sent to the AutoFillManager from the RenderView, used to send + // an IPC message back to the renderer. + const int kPageID = 1; + + webkit_glue::FormField field; + autofill_test::CreateTestFormField( + "Card Number", "cardnumber", "", "text", &field); + EXPECT_FALSE( + autofill_manager_->GetAutoFillSuggestions(kPageID, false, field)); +} +// Test that we return profile and credit card suggestions for combined forms. +TEST_F(AutoFillManagerTest, GetAddressAndCreditCardSuggestions) { FormData form; - CreateTestFormDataBilling(&form); + CreateTestAddressFormData(&form); + CreateTestCreditCardFormData(&form, true); // Set up our FormStructures. std::vector<FormData> forms; @@ -517,43 +563,64 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsSemicolon) { webkit_glue::FormField field; autofill_test::CreateTestFormField( - "Name on Card", "nameoncard", "", "text", &field); + "First Name", "firstname", "", "text", &field); EXPECT_TRUE(autofill_manager_->GetAutoFillSuggestions(kPageID, false, field)); // No suggestions provided, so send an empty vector as the results. // This triggers the combined message send. rvh()->AutocompleteSuggestionsReturned(kPageID, std::vector<string16>()); - // Test that we sent the right message to the renderer. + // Test that we sent the right address suggestions to the renderer. int page_id = 0; std::vector<string16> values; std::vector<string16> labels; - EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + std::vector<string16> icons; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); EXPECT_EQ(kPageID, page_id); - ASSERT_EQ(8U, values.size()); - EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[0]); - EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[1]); - EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[2]); - EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[3]); - EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[4]); - EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[5]); - EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[6]); - EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[7]); - ASSERT_EQ(8U, labels.size()); - EXPECT_EQ(ASCIIToUTF16("Elvis Aaron Presley; *3456"), labels[0]); - EXPECT_EQ(ASCIIToUTF16("Charles Hardin Holley; *3456"), labels[1]); - EXPECT_EQ(ASCIIToUTF16("*3456"), labels[2]); - EXPECT_EQ(ASCIIToUTF16("Joe Ely; *3456"), labels[3]); - EXPECT_EQ(ASCIIToUTF16("Elvis Aaron Presley; *8765"), labels[4]); - EXPECT_EQ(ASCIIToUTF16("Charles Hardin Holley; *8765"), labels[5]); - EXPECT_EQ(ASCIIToUTF16("*8765"), labels[6]); - EXPECT_EQ(ASCIIToUTF16("Joe Ely; *8765"), labels[7]); + ASSERT_EQ(2U, values.size()); + EXPECT_EQ(ASCIIToUTF16("Elvis"), values[0]); + EXPECT_EQ(ASCIIToUTF16("Charles"), values[1]); + ASSERT_EQ(2U, labels.size()); + // Inferred labels now include full first relevant field, which in this case + // the address #1. + EXPECT_EQ(ASCIIToUTF16("3734 Elvis Presley Blvd."), labels[0]); + EXPECT_EQ(ASCIIToUTF16("123 Apple St."), labels[1]); + ASSERT_EQ(2U, icons.size()); + EXPECT_EQ(string16(), icons[0]); + EXPECT_EQ(string16(), icons[1]); + + process()->sink().ClearMessages(); + autofill_test::CreateTestFormField( + "Card Number", "cardnumber", "", "text", &field); + EXPECT_TRUE(autofill_manager_->GetAutoFillSuggestions(kPageID, false, field)); + + // No suggestions provided, so send an empty vector as the results. + // This triggers the combined message send. + rvh()->AutocompleteSuggestionsReturned(kPageID, std::vector<string16>()); + + // Test that we sent the credit card suggestions to the renderer. + page_id = 0; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); + EXPECT_EQ(kPageID, page_id); + ASSERT_EQ(2U, values.size()); + EXPECT_EQ(ASCIIToUTF16("************3456"), values[0]); + EXPECT_EQ(ASCIIToUTF16("************8765"), values[1]); + ASSERT_EQ(2U, labels.size()); + EXPECT_EQ(ASCIIToUTF16("*3456"), labels[0]); + EXPECT_EQ(ASCIIToUTF16("*8765"), labels[1]); + ASSERT_EQ(2U, icons.size()); + EXPECT_EQ(ASCIIToUTF16("visaCC"), icons[0]); + EXPECT_EQ(ASCIIToUTF16("masterCardCC"), icons[1]); } -TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsNonHTTPS) { +// Test that for non-https forms with both address and credit card fields, we +// only return address suggestions. +TEST_F(AutoFillManagerTest, GetAddressAndCreditCardSuggestionsNonHttps) { FormData form; - CreateTestFormDataBilling(&form); - form.origin = GURL("http://myform.com/form.html"); + CreateTestAddressFormData(&form); + CreateTestCreditCardFormData(&form, false); // Set up our FormStructures. std::vector<FormData> forms; @@ -566,14 +633,43 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsNonHTTPS) { webkit_glue::FormField field; autofill_test::CreateTestFormField( + "First Name", "firstname", "", "text", &field); + EXPECT_TRUE(autofill_manager_->GetAutoFillSuggestions(kPageID, false, field)); + + // No suggestions provided, so send an empty vector as the results. + // This triggers the combined message send. + rvh()->AutocompleteSuggestionsReturned(kPageID, std::vector<string16>()); + + // Test that we sent the right address suggestions to the renderer. + int page_id = 0; + std::vector<string16> values; + std::vector<string16> labels; + std::vector<string16> icons; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); + EXPECT_EQ(kPageID, page_id); + ASSERT_EQ(2U, values.size()); + EXPECT_EQ(ASCIIToUTF16("Elvis"), values[0]); + EXPECT_EQ(ASCIIToUTF16("Charles"), values[1]); + ASSERT_EQ(2U, labels.size()); + // Inferred labels now include full first relevant field, which in this case + // the address #1. + EXPECT_EQ(ASCIIToUTF16("3734 Elvis Presley Blvd."), labels[0]); + EXPECT_EQ(ASCIIToUTF16("123 Apple St."), labels[1]); + ASSERT_EQ(2U, icons.size()); + EXPECT_EQ(string16(), icons[0]); + EXPECT_EQ(string16(), icons[1]); + + autofill_test::CreateTestFormField( "Card Number", "cardnumber", "", "text", &field); EXPECT_FALSE( autofill_manager_->GetAutoFillSuggestions(kPageID, false, field)); } +// Test that we correctly combine autofill and autocomplete suggestions. TEST_F(AutoFillManagerTest, GetCombinedAutoFillAndAutocompleteSuggestions) { FormData form; - CreateTestFormData(&form); + CreateTestAddressFormData(&form); // Set up our FormStructures. std::vector<FormData> forms; @@ -600,7 +696,9 @@ TEST_F(AutoFillManagerTest, GetCombinedAutoFillAndAutocompleteSuggestions) { int page_id = 0; std::vector<string16> values; std::vector<string16> labels; - EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + std::vector<string16> icons; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); EXPECT_EQ(kPageID, page_id); ASSERT_EQ(4U, values.size()); EXPECT_EQ(ASCIIToUTF16("Elvis"), values[0]); @@ -612,11 +710,18 @@ TEST_F(AutoFillManagerTest, GetCombinedAutoFillAndAutocompleteSuggestions) { EXPECT_EQ(ASCIIToUTF16("123 Apple St."), labels[1]); EXPECT_EQ(string16(), labels[2]); EXPECT_EQ(string16(), labels[3]); + ASSERT_EQ(4U, icons.size()); + EXPECT_EQ(string16(), icons[0]); + EXPECT_EQ(string16(), icons[1]); + EXPECT_EQ(string16(), icons[2]); + EXPECT_EQ(string16(), icons[3]); } -TEST_F(AutoFillManagerTest, GetFieldSuggestionsFormIsAutoFilled) { +// Test that we return autocomplete-like suggestions when trying to autofill +// already filled fields. +TEST_F(AutoFillManagerTest, GetFieldSuggestionsFieldIsAutoFilled) { FormData form; - CreateTestFormData(&form); + CreateTestAddressFormData(&form); // Set up our FormStructures. std::vector<FormData> forms; @@ -640,7 +745,9 @@ TEST_F(AutoFillManagerTest, GetFieldSuggestionsFormIsAutoFilled) { int page_id = 0; std::vector<string16> values; std::vector<string16> labels; - EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + std::vector<string16> icons; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); EXPECT_EQ(kPageID, page_id); ASSERT_EQ(2U, values.size()); EXPECT_EQ(ASCIIToUTF16("Elvis"), values[0]); @@ -648,11 +755,16 @@ TEST_F(AutoFillManagerTest, GetFieldSuggestionsFormIsAutoFilled) { ASSERT_EQ(2U, labels.size()); EXPECT_EQ(string16(), labels[0]); EXPECT_EQ(string16(), labels[1]); + ASSERT_EQ(2U, icons.size()); + EXPECT_EQ(string16(), icons[0]); + EXPECT_EQ(string16(), icons[1]); } +// Test that nothing breaks when there are autocomplete suggestions but no +// autofill suggestions. TEST_F(AutoFillManagerTest, GetFieldSuggestionsForAutocompleteOnly) { FormData form; - CreateTestFormData(&form); + CreateTestAddressFormData(&form); // Set up our FormStructures. std::vector<FormData> forms; @@ -668,10 +780,8 @@ TEST_F(AutoFillManagerTest, GetFieldSuggestionsForAutocompleteOnly) { "Some Field", "somefield", "", "text", &field); EXPECT_FALSE(autofill_manager_->GetAutoFillSuggestions(kPageID, true, field)); - // No suggestions provided, so send an empty vector as the results. + // Add some Autocomplete suggestions. // This triggers the combined message send. - // In this case, we're simulating a cancel of Autocomplete with a different - // page ID and an empty vector of suggestions. std::vector<string16> suggestions; suggestions.push_back(ASCIIToUTF16("one")); suggestions.push_back(ASCIIToUTF16("two")); @@ -681,7 +791,9 @@ TEST_F(AutoFillManagerTest, GetFieldSuggestionsForAutocompleteOnly) { int page_id = 0; std::vector<string16> values; std::vector<string16> labels; - EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + std::vector<string16> icons; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); EXPECT_EQ(kPageID, page_id); ASSERT_EQ(2U, values.size()); ASSERT_EQ(2U, labels.size()); @@ -689,11 +801,16 @@ TEST_F(AutoFillManagerTest, GetFieldSuggestionsForAutocompleteOnly) { EXPECT_EQ(string16(), labels[0]); EXPECT_EQ(ASCIIToUTF16("two"), values[1]); EXPECT_EQ(string16(), labels[1]); + ASSERT_EQ(2U, icons.size()); + EXPECT_EQ(string16(), icons[0]); + EXPECT_EQ(string16(), icons[1]); } +// Test that we do not return duplicate values drawn from multiple profiles when +// filling an already filled field. TEST_F(AutoFillManagerTest, GetFieldSuggestionsWithDuplicateValues) { FormData form; - CreateTestFormData(&form); + CreateTestAddressFormData(&form); // Set up our FormStructures. std::vector<FormData> forms; @@ -723,7 +840,9 @@ TEST_F(AutoFillManagerTest, GetFieldSuggestionsWithDuplicateValues) { int page_id = 0; std::vector<string16> values; std::vector<string16> labels; - EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + std::vector<string16> icons; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels, + &icons)); EXPECT_EQ(kPageID, page_id); ASSERT_EQ(2U, values.size()); EXPECT_EQ(ASCIIToUTF16("Elvis"), values[0]); @@ -731,47 +850,24 @@ TEST_F(AutoFillManagerTest, GetFieldSuggestionsWithDuplicateValues) { ASSERT_EQ(2U, labels.size()); EXPECT_EQ(string16(), labels[0]); EXPECT_EQ(string16(), labels[1]); + ASSERT_EQ(2U, icons.size()); + EXPECT_EQ(string16(), icons[0]); + EXPECT_EQ(string16(), icons[1]); } -TEST_F(AutoFillManagerTest, GetBillingSuggestionsAddress1) { - FormData form; - CreateTestFormDataBilling(&form); - - // Set up our FormStructures. - std::vector<FormData> forms; - forms.push_back(form); - autofill_manager_->FormsSeen(forms); - - // The page ID sent to the AutoFillManager from the RenderView, used to send - // an IPC message back to the renderer. - const int kPageID = 1; - - webkit_glue::FormField field; - autofill_test::CreateTestFormField( - "Address Line 1", "billingAddr1", "", "text", &field); - EXPECT_TRUE(autofill_manager_->GetAutoFillSuggestions(kPageID, false, field)); - - // No suggestions provided, so send an empty vector as the results. - // This triggers the combined message send. - rvh()->AutocompleteSuggestionsReturned(kPageID, std::vector<string16>()); - - // Test that we sent the right message to the renderer. - int page_id = 0; - std::vector<string16> values; - std::vector<string16> labels; - EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); - EXPECT_EQ(kPageID, page_id); - ASSERT_EQ(2U, values.size()); - EXPECT_EQ(ASCIIToUTF16("3734 Elvis Presley Blvd."), values[0]); - EXPECT_EQ(ASCIIToUTF16("123 Apple St."), values[1]); - ASSERT_EQ(2U, labels.size()); - EXPECT_EQ(ASCIIToUTF16("Elvis Aaron Presley; *3456"), labels[0]); - EXPECT_EQ(ASCIIToUTF16("Charles Hardin Holley; *8765"), labels[1]); -} +// Test that we correctly fill an address form. +TEST_F(AutoFillManagerTest, FillAddressForm) { + // |profile| will be owned by the mock PersonalDataManager. + AutoFillProfile* profile = new AutoFillProfile; + autofill_test::SetProfileInfo(profile, "Home; 8765", "Joe", "", "Ely", + "flatlander@gmail.com", "MCA", + "916 16th St.", "Apt. 6", "Lubbock", + "Texas", "79401", "USA", + "12345678901", ""); + autofill_manager_->AddProfile(profile); -TEST_F(AutoFillManagerTest, FillCreditCardForm) { FormData form; - CreateTestFormDataBilling(&form); + CreateTestAddressFormData(&form); // Set up our FormStructures. std::vector<FormData> forms; @@ -782,67 +878,98 @@ TEST_F(AutoFillManagerTest, FillCreditCardForm) { // an IPC message back to the renderer. const int kPageID = 1; EXPECT_TRUE(autofill_manager_->FillAutoFillFormData( - kPageID, form, AutoFillManager::PackIDs(4, 1))); + kPageID, form, autofill_manager_->PackGUIDs(std::string(), + profile->guid()))); int page_id = 0; FormData results; EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); EXPECT_EQ(ASCIIToUTF16("MyForm"), results.name); EXPECT_EQ(ASCIIToUTF16("POST"), results.method); - EXPECT_EQ(GURL("https://myform.com/form.html"), results.origin); - EXPECT_EQ(GURL("https://myform.com/submit.html"), results.action); - ASSERT_EQ(15U, results.fields.size()); + EXPECT_EQ(GURL("http://myform.com/form.html"), results.origin); + EXPECT_EQ(GURL("http://myform.com/submit.html"), results.action); + ASSERT_EQ(11U, results.fields.size()); webkit_glue::FormField field; autofill_test::CreateTestFormField( - "First Name", "firstname", "Elvis", "text", &field); + "First Name", "firstname", "Joe", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[0])); autofill_test::CreateTestFormField( - "Middle Name", "middlename", "Aaron", "text", &field); + "Middle Name", "middlename", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[1])); autofill_test::CreateTestFormField( - "Last Name", "lastname", "Presley", "text", &field); + "Last Name", "lastname", "Ely", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[2])); autofill_test::CreateTestFormField( - "Address Line 1", "billingAddr1", - "3734 Elvis Presley Blvd.", "text", &field); + "Address Line 1", "addr1", "916 16th St.", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[3])); autofill_test::CreateTestFormField( - "Address Line 2", "billingAddr2", "Apt. 10", "text", &field); + "Address Line 2", "addr2", "Apt. 6", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[4])); autofill_test::CreateTestFormField( - "City", "billingCity", "Memphis", "text", &field); + "City", "city", "Lubbock", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[5])); autofill_test::CreateTestFormField( - "State", "billingState", "Tennessee", "text", &field); + "State", "state", "Texas", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[6])); autofill_test::CreateTestFormField( - "Postal Code", "billingZipcode", "38116", "text", &field); + "Postal Code", "zipcode", "79401", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[7])); autofill_test::CreateTestFormField( - "Country", "billingCountry", "USA", "text", &field); + "Country", "country", "USA", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[8])); autofill_test::CreateTestFormField( "Phone Number", "phonenumber", "12345678901", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[9])); autofill_test::CreateTestFormField( - "Email", "email", "theking@gmail.com", "text", &field); + "Email", "email", "flatlander@gmail.com", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[10])); +} + +// Test that we correctly fill a credit card form. +TEST_F(AutoFillManagerTest, FillCreditCardForm) { + FormData form; + CreateTestCreditCardFormData(&form, true); + + // Set up our FormStructures. + std::vector<FormData> forms; + forms.push_back(form); + autofill_manager_->FormsSeen(forms); + + // The page ID sent to the AutoFillManager from the RenderView, used to send + // an IPC message back to the renderer. + const int kPageID = 1; + EXPECT_TRUE(autofill_manager_->FillAutoFillFormData( + kPageID, form, + autofill_manager_->PackGUIDs("00000000-0000-0000-0000-000000000004", + std::string()))); + + int page_id = 0; + FormData results; + EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); + EXPECT_EQ(ASCIIToUTF16("MyForm"), results.name); + EXPECT_EQ(ASCIIToUTF16("POST"), results.method); + EXPECT_EQ(GURL("https://myform.com/form.html"), results.origin); + EXPECT_EQ(GURL("https://myform.com/submit.html"), results.action); + ASSERT_EQ(4U, results.fields.size()); + + webkit_glue::FormField field; autofill_test::CreateTestFormField( "Name on Card", "nameoncard", "Elvis Presley", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[11])); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[0])); autofill_test::CreateTestFormField( - "Card Number", "cardnumber", "1234567890123456", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[12])); + "Card Number", "cardnumber", "4234567890123456", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[1])); autofill_test::CreateTestFormField( "Expiration Date", "ccmonth", "04", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[13])); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[2])); autofill_test::CreateTestFormField( "", "ccyear", "2012", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[14])); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[3])); } -TEST_F(AutoFillManagerTest, FillNonBillingFormSemicolon) { +// Test that we correctly fill a combined address and credit card form. +TEST_F(AutoFillManagerTest, FillAddressAndCreditCardForm) { // |profile| will be owned by the mock PersonalDataManager. AutoFillProfile* profile = new AutoFillProfile; autofill_test::SetProfileInfo(profile, "Home; 8765", "Joe", "", "Ely", @@ -850,31 +977,35 @@ TEST_F(AutoFillManagerTest, FillNonBillingFormSemicolon) { "916 16th St.", "Apt. 6", "Lubbock", "Texas", "79401", "USA", "12345678901", ""); - profile->set_unique_id(7); + profile->set_guid("00000000-0000-0000-0000-000000000008"); autofill_manager_->AddProfile(profile); FormData form; - CreateTestFormData(&form); + CreateTestAddressFormData(&form); + CreateTestCreditCardFormData(&form, true); // Set up our FormStructures. std::vector<FormData> forms; forms.push_back(form); autofill_manager_->FormsSeen(forms); + // First fill the address data. // The page ID sent to the AutoFillManager from the RenderView, used to send // an IPC message back to the renderer. const int kPageID = 1; EXPECT_TRUE(autofill_manager_->FillAutoFillFormData( - kPageID, form, AutoFillManager::PackIDs(4, 7))); + kPageID, form, + autofill_manager_->PackGUIDs(std::string(), + "00000000-0000-0000-0000-000000000008"))); int page_id = 0; FormData results; EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); EXPECT_EQ(ASCIIToUTF16("MyForm"), results.name); EXPECT_EQ(ASCIIToUTF16("POST"), results.method); - EXPECT_EQ(GURL("http://myform.com/form.html"), results.origin); - EXPECT_EQ(GURL("http://myform.com/submit.html"), results.action); - ASSERT_EQ(11U, results.fields.size()); + EXPECT_EQ(GURL("https://myform.com/form.html"), results.origin); + EXPECT_EQ(GURL("https://myform.com/submit.html"), results.action); + ASSERT_EQ(15U, results.fields.size()); webkit_glue::FormField field; autofill_test::CreateTestFormField( @@ -910,35 +1041,28 @@ TEST_F(AutoFillManagerTest, FillNonBillingFormSemicolon) { autofill_test::CreateTestFormField( "Email", "email", "flatlander@gmail.com", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[10])); -} - -TEST_F(AutoFillManagerTest, FillBillFormSemicolon) { - // |profile| will be owned by the mock PersonalDataManager. - AutoFillProfile* profile = new AutoFillProfile; - autofill_test::SetProfileInfo(profile, "Home; 8765", "Joe", "", "Ely", - "flatlander@gmail.com", "MCA", - "916 16th St.", "Apt. 6", "Lubbock", - "Texas", "79401", "USA", - "12345678901", ""); - profile->set_unique_id(7); - autofill_manager_->AddProfile(profile); - - FormData form; - CreateTestFormDataBilling(&form); - - // Set up our FormStructures. - std::vector<FormData> forms; - forms.push_back(form); - autofill_manager_->FormsSeen(forms); + autofill_test::CreateTestFormField( + "Name on Card", "nameoncard", "", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[11])); + autofill_test::CreateTestFormField( + "Card Number", "cardnumber", "", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[12])); + autofill_test::CreateTestFormField( + "Expiration Date", "ccmonth", "", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[13])); + autofill_test::CreateTestFormField( + "", "ccyear", "", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[14])); - // The page ID sent to the AutoFillManager from the RenderView, used to send - // an IPC message back to the renderer. - const int kPageID = 1; + // Now fill the credit card data. + process()->sink().ClearMessages(); EXPECT_TRUE(autofill_manager_->FillAutoFillFormData( - kPageID, form, AutoFillManager::PackIDs(4, 7))); + kPageID, form, + autofill_manager_->PackGUIDs("00000000-0000-0000-0000-000000000004", + std::string()))); - int page_id = 0; - FormData results; + + page_id = 0; EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); EXPECT_EQ(ASCIIToUTF16("MyForm"), results.name); EXPECT_EQ(ASCIIToUTF16("POST"), results.method); @@ -946,46 +1070,44 @@ TEST_F(AutoFillManagerTest, FillBillFormSemicolon) { EXPECT_EQ(GURL("https://myform.com/submit.html"), results.action); ASSERT_EQ(15U, results.fields.size()); - webkit_glue::FormField field; autofill_test::CreateTestFormField( - "First Name", "firstname", "Joe", "text", &field); + "First Name", "firstname", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[0])); autofill_test::CreateTestFormField( "Middle Name", "middlename", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[1])); autofill_test::CreateTestFormField( - "Last Name", "lastname", "Ely", "text", &field); + "Last Name", "lastname", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[2])); autofill_test::CreateTestFormField( - "Address Line 1", "billingAddr1", - "3734 Elvis Presley Blvd.", "text", &field); + "Address Line 1", "addr1", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[3])); autofill_test::CreateTestFormField( - "Address Line 2", "billingAddr2", "Apt. 10", "text", &field); + "Address Line 2", "addr2", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[4])); autofill_test::CreateTestFormField( - "City", "billingCity", "Memphis", "text", &field); + "City", "city", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[5])); autofill_test::CreateTestFormField( - "State", "billingState", "Tennessee", "text", &field); + "State", "state", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[6])); autofill_test::CreateTestFormField( - "Postal Code", "billingZipcode", "38116", "text", &field); + "Postal Code", "zipcode", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[7])); autofill_test::CreateTestFormField( - "Country", "billingCountry", "USA", "text", &field); + "Country", "country", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[8])); autofill_test::CreateTestFormField( - "Phone Number", "phonenumber", "12345678901", "text", &field); + "Phone Number", "phonenumber", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[9])); autofill_test::CreateTestFormField( - "Email", "email", "flatlander@gmail.com", "text", &field); + "Email", "email", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[10])); autofill_test::CreateTestFormField( "Name on Card", "nameoncard", "Elvis Presley", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[11])); autofill_test::CreateTestFormField( - "Card Number", "cardnumber", "1234567890123456", "text", &field); + "Card Number", "cardnumber", "4234567890123456", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[12])); autofill_test::CreateTestFormField( "Expiration Date", "ccmonth", "04", "text", &field); @@ -995,6 +1117,7 @@ TEST_F(AutoFillManagerTest, FillBillFormSemicolon) { EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[14])); } +// Test that we correctly fill a phone number split across multiple fields. TEST_F(AutoFillManagerTest, FillPhoneNumber) { FormData form; @@ -1045,10 +1168,9 @@ TEST_F(AutoFillManagerTest, FillPhoneNumber) { // an IPC message back to the renderer. int page_id = 100 - i; process()->sink().ClearMessages(); - EXPECT_TRUE( - autofill_manager_->FillAutoFillFormData(page_id, - form, - work_profile->unique_id())); + EXPECT_TRUE(autofill_manager_->FillAutoFillFormData( + page_id, form, + autofill_manager_->PackGUIDs(std::string(), work_profile->guid()))); page_id = 0; FormData results; EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); @@ -1066,6 +1188,7 @@ TEST_F(AutoFillManagerTest, FillPhoneNumber) { work_profile->SetInfo(phone_type, saved_phone); } +// Test that we can still fill a form when a field has been removed from it. TEST_F(AutoFillManagerTest, FormChangesRemoveField) { FormData form; form.name = ASCIIToUTF16("MyForm"); @@ -1103,7 +1226,10 @@ TEST_F(AutoFillManagerTest, FormChangesRemoveField) { // The page ID sent to the AutoFillManager from the RenderView, used to send // an IPC message back to the renderer. const int kPageID = 1; - EXPECT_TRUE(autofill_manager_->FillAutoFillFormData(kPageID, form, 1)); + EXPECT_TRUE(autofill_manager_->FillAutoFillFormData( + kPageID, form, + autofill_manager_->PackGUIDs(std::string(), + "00000000-0000-0000-0000-000000000001"))); int page_id = 0; FormData results; @@ -1129,6 +1255,7 @@ TEST_F(AutoFillManagerTest, FormChangesRemoveField) { EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[3])); } +// Test that we can still fill a form when a field has been added to it. TEST_F(AutoFillManagerTest, FormChangesAddField) { FormData form; form.name = ASCIIToUTF16("MyForm"); @@ -1166,7 +1293,10 @@ TEST_F(AutoFillManagerTest, FormChangesAddField) { // The page ID sent to the AutoFillManager from the RenderView, used to send // an IPC message back to the renderer. const int kPageID = 1; - EXPECT_TRUE(autofill_manager_->FillAutoFillFormData(kPageID, form, 1)); + EXPECT_TRUE(autofill_manager_->FillAutoFillFormData( + kPageID, form, + autofill_manager_->PackGUIDs(std::string(), + "00000000-0000-0000-0000-000000000001"))); int page_id = 0; FormData results; diff --git a/chrome/browser/autofill/autofill_profile.cc b/chrome/browser/autofill/autofill_profile.cc index f718d5a..68117ae 100644 --- a/chrome/browser/autofill/autofill_profile.cc +++ b/chrome/browser/autofill/autofill_profile.cc @@ -35,21 +35,13 @@ void InitPersonalInfo(FormGroupMap* personal_info) { } // namespace -AutoFillProfile::AutoFillProfile(const string16& label, int unique_id) - : label_(label), - unique_id_(unique_id), - guid_(guid::GenerateGUID()) { - InitPersonalInfo(&personal_info_); -} - AutoFillProfile::AutoFillProfile(const std::string& guid) - : unique_id_(0), - guid_(guid) { + : guid_(guid) { InitPersonalInfo(&personal_info_); } AutoFillProfile::AutoFillProfile() - : unique_id_(0) { + : guid_(guid::GenerateGUID()) { InitPersonalInfo(&personal_info_); } @@ -159,7 +151,6 @@ void AutoFillProfile::SetInfo(const AutoFillType& type, const string16& value) { FormGroup* AutoFillProfile::Clone() const { AutoFillProfile* profile = new AutoFillProfile(); profile->label_ = label_; - profile->unique_id_ = unique_id(); profile->guid_ = guid(); FormGroupMap::const_iterator iter; @@ -172,61 +163,17 @@ FormGroup* AutoFillProfile::Clone() const { return profile; } -const string16& AutoFillProfile::Label() const { +const string16 AutoFillProfile::Label() const { return label_; } -string16 AutoFillProfile::PreviewSummary() const { - // Fetch the components of the summary string. Any or all of these - // may be an empty string. - string16 first_name = GetFieldText(AutoFillType(NAME_FIRST)); - string16 last_name = GetFieldText(AutoFillType(NAME_LAST)); - string16 address = GetFieldText(AutoFillType(ADDRESS_HOME_LINE1)); - - // String separators depend (below) on the existence of the various fields. - bool have_first_name = first_name.length() > 0; - bool have_last_name = last_name.length() > 0; - bool have_address = address.length() > 0; - - // Name separator defaults to "". Space if we have first and last name. - string16 name_separator; - - if (have_first_name && have_last_name) { - name_separator = l10n_util::GetStringUTF16( - IDS_AUTOFILL_DIALOG_ADDRESS_NAME_SEPARATOR); - } - - // E.g. "John Smith", or "John", or "Smith", or "". - string16 name_format = l10n_util::GetStringFUTF16( - IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_NAME_FORMAT, - first_name, - name_separator, - last_name); - - // Summary separator defaults to "". ", " if we have name and address. - string16 summary_separator; - if ((have_first_name || have_last_name) && have_address) { - summary_separator = l10n_util::GetStringUTF16( - IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_SEPARATOR); - } - - // E.g. "John Smith, 123 Main Street". - string16 summary_format = l10n_util::GetStringFUTF16( - IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_FORMAT, - name_format, - summary_separator, - address); - - return summary_format; -} - // static bool AutoFillProfile::AdjustInferredLabels( std::vector<AutoFillProfile*>* profiles) { std::vector<string16> created_labels; const size_t kMinimalFieldsShown = 2; CreateInferredLabels(profiles, &created_labels, kMinimalFieldsShown, - UNKNOWN_TYPE); + UNKNOWN_TYPE, NULL); DCHECK(profiles->size() == created_labels.size()); bool updated_labels = false; for (size_t i = 0; i < profiles->size(); ++i) { @@ -243,10 +190,12 @@ void AutoFillProfile::CreateInferredLabels( const std::vector<AutoFillProfile*>* profiles, std::vector<string16>* created_labels, size_t minimal_fields_shown, - AutoFillFieldType exclude_field) { - // These fields are use to distinguish between two profiles in the order of - // importance, e. g. if both EMAIL_ADDRESS and COMPANY_NAME are different, - // EMAIL_ADDRESS will be used to distinguish them. + AutoFillFieldType exclude_field, + const std::vector<AutoFillFieldType>* suggested_fields) { + // |suggested_fields| may be NULL. + // The following fields are use to distinguish between two profiles in the + // order of importance, e. g. if both EMAIL_ADDRESS and COMPANY_NAME are + // different, EMAIL_ADDRESS will be used to distinguish them. const AutoFillFieldType distinguishing_fields[] = { // First non empty data are always present in the label, even if it matches. NAME_FULL, @@ -260,15 +209,47 @@ void AutoFillProfile::CreateInferredLabels( PHONE_FAX_WHOLE_NUMBER, COMPANY_NAME, }; - if (exclude_field == NAME_FIRST || exclude_field == NAME_LAST) - exclude_field = NAME_FULL; + DCHECK(profiles); DCHECK(created_labels); + + std::vector<AutoFillFieldType> fields_to_use; + if (suggested_fields) { + // Limiting the number of possible fields simplifies the following code, + // and 10 fields more than enough to create clear label. + fields_to_use.reserve(std::min(suggested_fields->size(), + arraysize(distinguishing_fields))); + bool name_selected = false; + for (size_t i = 0; i < suggested_fields->size() && + fields_to_use.size() < arraysize(distinguishing_fields); ++i) { + if (suggested_fields->at(i) == NAME_FIRST || + suggested_fields->at(i) == NAME_LAST || + suggested_fields->at(i) == NAME_MIDDLE || + suggested_fields->at(i) == NAME_FULL) { + if (name_selected) + continue; + name_selected = true; + fields_to_use.push_back(NAME_FULL); + } else { + fields_to_use.push_back(suggested_fields->at(i)); + } + } + } else { + fields_to_use.resize(arraysize(distinguishing_fields)); + for (size_t i = 0; i < arraysize(distinguishing_fields); ++i) { + fields_to_use[i] = distinguishing_fields[i]; + } + } + + if (exclude_field == NAME_FIRST || exclude_field == NAME_LAST || + exclude_field == NAME_MIDDLE) + exclude_field = NAME_FULL; created_labels->resize(profiles->size()); std::map<string16, std::list<size_t> > labels; for (size_t it = 0; it < profiles->size(); ++it) { - labels[ - profiles->at(it)->GetFieldText(AutoFillType(NAME_FULL))].push_back(it); + labels[profiles->at(it)->GetFieldText( + AutoFillType(fields_to_use.size() ? fields_to_use[0] : + NAME_FULL))].push_back(it); } std::map<string16, std::list<size_t> >::iterator label_iterator; for (label_iterator = labels.begin(); label_iterator != labels.end(); @@ -277,13 +258,14 @@ void AutoFillProfile::CreateInferredLabels( // We have more than one item with the same preview, add differentiating // data. std::list<size_t>::iterator similar_profiles; + DCHECK(fields_to_use.size() <= arraysize(distinguishing_fields)); std::map<string16, int> tested_fields[arraysize(distinguishing_fields)]; for (similar_profiles = label_iterator->second.begin(); similar_profiles != label_iterator->second.end(); ++similar_profiles) { - for (size_t i = 0; i < arraysize(distinguishing_fields); ++i) { + for (size_t i = 0; i < fields_to_use.size(); ++i) { string16 key = profiles->at(*similar_profiles)->GetFieldText( - AutoFillType(distinguishing_fields[i])); + AutoFillType(fields_to_use[i])); std::map<string16, int>::iterator tested_field = tested_fields[i].find(key); if (tested_field == tested_fields[i].end()) @@ -297,7 +279,7 @@ void AutoFillProfile::CreateInferredLabels( size_t added_fields = 0; bool matched_necessary = false; // Leave it as a candidate if it is not the same for everybody. - for (size_t i = 0; i < arraysize(distinguishing_fields); ++i) { + for (size_t i = 0; i < fields_to_use.size(); ++i) { if (tested_fields[i].size() == label_iterator->second.size()) { // This field is different for everybody. if (!matched_necessary) { @@ -314,26 +296,26 @@ void AutoFillProfile::CreateInferredLabels( } else { ++added_fields; } - fields.push_back(distinguishing_fields[i]); + fields.push_back(fields_to_use[i]); if (added_fields >= minimal_fields_shown) break; } else if (tested_fields[i].size() != 1) { // this field is different for some. if (added_fields < minimal_fields_shown) { - first_non_empty_fields.push_back(distinguishing_fields[i]); + first_non_empty_fields.push_back(fields_to_use[i]); ++added_fields; if (added_fields == minimal_fields_shown && matched_necessary) break; } - fields.push_back(distinguishing_fields[i]); + fields.push_back(fields_to_use[i]); } else if (added_fields < minimal_fields_shown && - exclude_field != distinguishing_fields[i] && + exclude_field != fields_to_use[i] && !label_iterator->first.empty()) { - fields.push_back(distinguishing_fields[i]); - first_non_empty_fields.push_back(distinguishing_fields[i]); + fields.push_back(fields_to_use[i]); + first_non_empty_fields.push_back(fields_to_use[i]); ++added_fields; if (added_fields == minimal_fields_shown && matched_necessary) - break; + break; } } // Update labels if needed. @@ -342,12 +324,12 @@ void AutoFillProfile::CreateInferredLabels( ++similar_profiles) { size_t field_it = 0; for (size_t field_id = 0; - field_id < arraysize(distinguishing_fields) && + field_id < fields_to_use.size() && field_it < fields.size(); ++field_id) { - if (fields[field_it] == distinguishing_fields[field_id]) { + if (fields[field_it] == fields_to_use[field_id]) { if ((tested_fields[field_id])[ profiles->at(*similar_profiles)->GetFieldText( - AutoFillType(distinguishing_fields[field_id]))] == 1) { + AutoFillType(fields_to_use[field_id]))] == 1) { // this field is unique among the subset. break; } @@ -374,12 +356,12 @@ void AutoFillProfile::CreateInferredLabels( std::vector<AutoFillFieldType> non_empty_fields; size_t include_fields = minimal_fields_shown ? minimal_fields_shown : 1; non_empty_fields.reserve(include_fields); - for (size_t i = 0; i < arraysize(distinguishing_fields); ++i) { - if (exclude_field == distinguishing_fields[i]) + for (size_t i = 0; i < fields_to_use.size(); ++i) { + if (exclude_field == fields_to_use[i]) continue; if (!profiles->at(label_iterator->second.front())->GetFieldText( - AutoFillType(distinguishing_fields[i])).empty()) { - non_empty_fields.push_back(distinguishing_fields[i]); + AutoFillType(fields_to_use[i])).empty()) { + non_empty_fields.push_back(fields_to_use[i]); if (non_empty_fields.size() >= include_fields) break; } @@ -400,7 +382,6 @@ bool AutoFillProfile::IsEmpty() const { void AutoFillProfile::operator=(const AutoFillProfile& source) { label_ = source.label_; - unique_id_ = source.unique_id_; guid_ = source.guid_; STLDeleteContainerPairSecondPointers(personal_info_.begin(), @@ -443,34 +424,10 @@ int AutoFillProfile::Compare(const AutoFillProfile& profile) const { } bool AutoFillProfile::operator==(const AutoFillProfile& profile) const { - // The following AutoFill field types are the only types we store in the WebDB - // so far, so we're only concerned with matching these types in the profile. - const AutoFillFieldType types[] = { NAME_FIRST, - NAME_MIDDLE, - NAME_LAST, - EMAIL_ADDRESS, - COMPANY_NAME, - ADDRESS_HOME_LINE1, - ADDRESS_HOME_LINE2, - ADDRESS_HOME_CITY, - ADDRESS_HOME_STATE, - ADDRESS_HOME_ZIP, - ADDRESS_HOME_COUNTRY, - PHONE_HOME_NUMBER, - PHONE_FAX_NUMBER }; - - if (label_ != profile.label_ || - unique_id_ != profile.unique_id_) { + if (label_ != profile.label_ || guid_ != profile.guid_) return false; - } - for (size_t index = 0; index < arraysize(types); ++index) { - if (GetFieldText(AutoFillType(types[index])) != - profile.GetFieldText(AutoFillType(types[index]))) - return false; - } - - return true; + return Compare(profile) == 0; } bool AutoFillProfile::operator!=(const AutoFillProfile& profile) const { @@ -519,8 +476,6 @@ std::ostream& operator<<(std::ostream& os, const AutoFillProfile& profile) { return os << UTF16ToUTF8(profile.Label()) << " " - << profile.unique_id() - << " " << profile.guid() << " " << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_FIRST))) diff --git a/chrome/browser/autofill/autofill_profile.h b/chrome/browser/autofill/autofill_profile.h index 4336139..7244641 100644 --- a/chrome/browser/autofill/autofill_profile.h +++ b/chrome/browser/autofill/autofill_profile.h @@ -21,9 +21,6 @@ typedef std::map<FieldTypeGroup, FormGroup*> FormGroupMap; // to the requested form group type. class AutoFillProfile : public FormGroup { public: - // DEPRECATED - // TODO(dhollowa): Remove unique ID and label. http://crbug.com/58813 - AutoFillProfile(const string16& label, int unique_id); explicit AutoFillProfile(const std::string& guid); // For use in STL containers. @@ -46,25 +43,15 @@ class AutoFillProfile : public FormGroup { // Returns a copy of the profile it is called on. The caller is responsible // for deleting profile when they are done with it. virtual FormGroup* Clone() const; - virtual const string16& Label() const; - - int unique_id() const { return unique_id_; } - void set_unique_id(int id) { unique_id_ = id; } + // The user-visible label of the profile, generated in relation to other + // profiles. Shows at least 2 fields that differentiate profile from other + // profiles. See AdjustInferredLabels() further down for more description. + virtual const string16 Label() const; // This guid is the primary identifier for |AutoFillProfile| objects. const std::string guid() const { return guid_; } void set_guid(const std::string& guid) { guid_ = guid; } - // Profile summary string for UI. - // Constructs a summary string based on NAME_FIRST, NAME_LAST, and - // ADDRESS_HOME_LINE1 fields of the profile. The summary string is of the - // form: - // L"<first_name> <last_name>, <address_line_1>" - // but may omit any or all of the fields if they are not present in the - // profile. - // The form of the string is governed by generated resources. - string16 PreviewSummary() const; - // Adjusts the labels according to profile data. // Labels contain minimal different combination of: // 1. Full name. @@ -83,12 +70,14 @@ class AutoFillProfile : public FormGroup { // Created inferred labels for |profiles|, according to the rules above and // stores them in |created_labels|. |minimal_fields_shown| minimal number of // fields that need to be shown for the label. |exclude_field| is excluded - // from the label. + // from the label. If |suggested_fields| is not NULL it is used to generate + // labels appropriate to the actual fields in a given form. static void CreateInferredLabels( const std::vector<AutoFillProfile*>* profiles, std::vector<string16>* created_labels, size_t minimal_fields_shown, - AutoFillFieldType exclude_field); + AutoFillFieldType exclude_field, + const std::vector<AutoFillFieldType>* suggested_fields); // Returns true if there are no values (field types) set. bool IsEmpty() const; @@ -98,7 +87,8 @@ class AutoFillProfile : public FormGroup { // Comparison for Sync. Returns 0 if the profile is the same as |this|, // or < 0, or > 0 if it is different. The implied ordering can be used for - // culling duplicates. + // culling duplicates. The ordering is based on collation order of the + // textual contents of the fields. // GUIDs, labels, and unique IDs are not compared, only the values of the // profiles themselves. int Compare(const AutoFillProfile& profile) const; @@ -124,9 +114,6 @@ class AutoFillProfile : public FormGroup { // The label presented to the user when selecting a profile. string16 label_; - // The unique ID of this profile. - int unique_id_; - // The guid of this profile. std::string guid_; diff --git a/chrome/browser/autofill/autofill_profile_unittest.cc b/chrome/browser/autofill/autofill_profile_unittest.cc index 5a1f27b..3c500a3 100644 --- a/chrome/browser/autofill/autofill_profile_unittest.cc +++ b/chrome/browser/autofill/autofill_profile_unittest.cc @@ -15,82 +15,119 @@ namespace { +bool UpdateProfileLabel(AutoFillProfile *profile) { + std::vector<AutoFillProfile*> profiles; + profiles.push_back(profile); + return AutoFillProfile::AdjustInferredLabels(&profiles); +} + // Tests different possibilities for summary string generation. // Based on existence of first name, last name, and address line 1. TEST(AutoFillProfileTest, PreviewSummaryString) { // Case 0/null: "" - AutoFillProfile profile0(string16(), 0); - string16 summary0 = profile0.PreviewSummary(); + AutoFillProfile profile0; + // Empty profile - nothing to update. + EXPECT_FALSE(UpdateProfileLabel(&profile0)); + string16 summary0 = profile0.Label(); EXPECT_EQ(string16(), summary0); - // Case 0/empty: "" - AutoFillProfile profile00(string16(), 0); + // Case 0a/empty name and address, so the first two fields of the rest of the + // data is used: "Hollywood, CA" + AutoFillProfile profile00; autofill_test::SetProfileInfo(&profile00, "Billing", "", "Mitchell", "", "johnwayne@me.xyz", "Fox", "", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - string16 summary00 = profile00.PreviewSummary(); - EXPECT_EQ(string16(), summary00); + EXPECT_TRUE(UpdateProfileLabel(&profile00)); + string16 summary00 = profile00.Label(); + EXPECT_EQ(string16(ASCIIToUTF16("Hollywood, CA")), summary00); // Case 1: "<address>" - AutoFillProfile profile1(string16(), 0); + AutoFillProfile profile1; autofill_test::SetProfileInfo(&profile1, "Billing", "", "Mitchell", "", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - string16 summary1 = profile1.PreviewSummary(); - EXPECT_EQ(string16(ASCIIToUTF16("123 Zoo St.")), summary1); + EXPECT_TRUE(UpdateProfileLabel(&profile1)); + string16 summary1 = profile1.Label(); + EXPECT_EQ(string16(ASCIIToUTF16("123 Zoo St., Hollywood")), summary1); // Case 2: "<lastname>" - AutoFillProfile profile2(string16(), 0); + AutoFillProfile profile2; autofill_test::SetProfileInfo(&profile2, "Billing", "", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - string16 summary2 = profile2.PreviewSummary(); - EXPECT_EQ(string16(ASCIIToUTF16("Morrison")), summary2); + EXPECT_TRUE(UpdateProfileLabel(&profile2)); + string16 summary2 = profile2.Label(); + // Summary does include full name which is empty if the first name is empty. + EXPECT_EQ(string16(ASCIIToUTF16("Hollywood, CA")), summary2); // Case 3: "<lastname>, <address>" - AutoFillProfile profile3(string16(), 0); + AutoFillProfile profile3; autofill_test::SetProfileInfo(&profile3, "Billing", "", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - string16 summary3 = profile3.PreviewSummary(); - EXPECT_EQ(string16(ASCIIToUTF16("Morrison, 123 Zoo St.")), summary3); + EXPECT_TRUE(UpdateProfileLabel(&profile3)); + string16 summary3 = profile3.Label(); + EXPECT_EQ(string16(ASCIIToUTF16("123 Zoo St., Hollywood")), summary3); // Case 4: "<firstname>" - AutoFillProfile profile4(string16(), 0); + AutoFillProfile profile4; autofill_test::SetProfileInfo(&profile4, "Billing", "Marion", "Mitchell", "", "johnwayne@me.xyz", "Fox", "", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - string16 summary4 = profile4.PreviewSummary(); - EXPECT_EQ(string16(ASCIIToUTF16("Marion")), summary4); + EXPECT_TRUE(UpdateProfileLabel(&profile4)); + string16 summary4 = profile4.Label(); + EXPECT_EQ(string16(ASCIIToUTF16("Marion Mitchell, Hollywood")), summary4); // Case 5: "<firstname>, <address>" - AutoFillProfile profile5(string16(), 0); + AutoFillProfile profile5; autofill_test::SetProfileInfo(&profile5, "Billing", "Marion", "Mitchell", "", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - string16 summary5 = profile5.PreviewSummary(); - EXPECT_EQ(string16(ASCIIToUTF16("Marion, 123 Zoo St.")), summary5); + EXPECT_TRUE(UpdateProfileLabel(&profile5)); + string16 summary5 = profile5.Label(); + EXPECT_EQ(string16(ASCIIToUTF16("Marion Mitchell, 123 Zoo St.")), summary5); // Case 6: "<firstname> <lastname>" - AutoFillProfile profile6(string16(), 0); + AutoFillProfile profile6; autofill_test::SetProfileInfo(&profile6, "Billing", "Marion", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - string16 summary6 = profile6.PreviewSummary(); - EXPECT_EQ(string16(ASCIIToUTF16("Marion Morrison")), summary6); + EXPECT_TRUE(UpdateProfileLabel(&profile6)); + string16 summary6 = profile6.Label(); + EXPECT_EQ(string16(ASCIIToUTF16("Marion Mitchell Morrison, Hollywood")), + summary6); // Case 7: "<firstname> <lastname>, <address>" - AutoFillProfile profile7(string16(), 0); + AutoFillProfile profile7; autofill_test::SetProfileInfo(&profile7, "Billing", "Marion", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - string16 summary7 = profile7.PreviewSummary(); - EXPECT_EQ(string16(ASCIIToUTF16("Marion Morrison, 123 Zoo St.")), summary7); + EXPECT_TRUE(UpdateProfileLabel(&profile7)); + string16 summary7 = profile7.Label(); + EXPECT_EQ(string16(ASCIIToUTF16("Marion Mitchell Morrison, 123 Zoo St.")), + summary7); + + // Case 7a: "<firstname> <lastname>, <address>" - same as #7, except for + // e-mail. + AutoFillProfile profile7a; + autofill_test::SetProfileInfo(&profile7a, "Billing", "Marion", "Mitchell", + "Morrison", "marion@me.xyz", "Fox", "123 Zoo St.", "unit 5", + "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); + std::vector<AutoFillProfile*> profiles; + profiles.push_back(&profile7); + profiles.push_back(&profile7a); + EXPECT_TRUE(AutoFillProfile::AdjustInferredLabels(&profiles)); + summary7 = profile7.Label(); + string16 summary7a = profile7a.Label(); + EXPECT_EQ(string16(ASCIIToUTF16( + "Marion Mitchell Morrison, 123 Zoo St., johnwayne@me.xyz")), summary7); + EXPECT_EQ(string16(ASCIIToUTF16( + "Marion Mitchell Morrison, 123 Zoo St., marion@me.xyz")), summary7a); } TEST(AutoFillProfileTest, AdjustInferredLabels) { std::vector<AutoFillProfile*> profiles; - profiles.push_back(new AutoFillProfile(string16(), 0)); + profiles.push_back(new AutoFillProfile); autofill_test::SetProfileInfo( profiles[0], "", @@ -106,7 +143,7 @@ TEST(AutoFillProfileTest, AdjustInferredLabels) { "US", "11111111111", "22222222222"); - profiles.push_back(new AutoFillProfile(string16(), 0)); + profiles.push_back(new AutoFillProfile); autofill_test::SetProfileInfo( profiles[1], "", @@ -131,7 +168,7 @@ TEST(AutoFillProfileTest, AdjustInferredLabels) { EXPECT_EQ(string16(ASCIIToUTF16("Jane Doe, 123 Letha Shore.")), profiles[1]->Label()); - profiles.push_back(new AutoFillProfile(string16(), 0)); + profiles.push_back(new AutoFillProfile); autofill_test::SetProfileInfo( profiles[2], "", @@ -162,7 +199,7 @@ TEST(AutoFillProfileTest, AdjustInferredLabels) { delete profiles[2]; profiles.pop_back(); - profiles.push_back(new AutoFillProfile(string16(), 0)); + profiles.push_back(new AutoFillProfile); autofill_test::SetProfileInfo( profiles[2], "", @@ -191,7 +228,7 @@ TEST(AutoFillProfileTest, AdjustInferredLabels) { "John Doe, 666 Erebus St., fax:#33333333333")), profiles[2]->Label()); - profiles.push_back(new AutoFillProfile(string16(), 0)); + profiles.push_back(new AutoFillProfile); autofill_test::SetProfileInfo( profiles[3], "", @@ -223,7 +260,7 @@ TEST(AutoFillProfileTest, AdjustInferredLabels) { EXPECT_EQ(string16(ASCIIToUTF16("John Doe, 666 Erebus St., 44444444444")), profiles[3]->Label()); - profiles.push_back(new AutoFillProfile(string16(), 0)); + profiles.push_back(new AutoFillProfile); autofill_test::SetProfileInfo( profiles[4], "", @@ -268,6 +305,101 @@ TEST(AutoFillProfileTest, AdjustInferredLabels) { STLDeleteContainerPointers(profiles.begin(), profiles.end()); } +TEST(AutoFillProfileTest, CreateInferredLabels) { + std::vector<AutoFillProfile*> profiles; + profiles.push_back(new AutoFillProfile); + autofill_test::SetProfileInfo(profiles[0], + "", + "John", + "", + "Doe", + "johndoe@hades.com", + "Underworld", + "666 Erebus St.", + "", + "Elysium", "CA", + "91111", + "US", + "11111111111", + "22222222222"); + profiles.push_back(new AutoFillProfile); + autofill_test::SetProfileInfo(profiles[1], + "", + "Jane", + "", + "Doe", + "janedoe@tertium.com", + "Pluto Inc.", + "123 Letha Shore.", + "", + "Dis", "CA", + "91222", + "US", + "12345678910", + "01987654321"); + std::vector<string16> labels; + // Two fields at least - no filter. + AutoFillProfile::CreateInferredLabels(&profiles, &labels, 2, UNKNOWN_TYPE, + NULL); + EXPECT_EQ(string16(ASCIIToUTF16("John Doe, 666 Erebus St.")), labels[0]); + EXPECT_EQ(string16(ASCIIToUTF16("Jane Doe, 123 Letha Shore.")), labels[1]); + + // Three fields at least - no filter. + AutoFillProfile::CreateInferredLabels(&profiles, &labels, 3, UNKNOWN_TYPE, + NULL); + EXPECT_EQ(string16(ASCIIToUTF16("John Doe, 666 Erebus St., Elysium")), + labels[0]); + EXPECT_EQ(string16(ASCIIToUTF16("Jane Doe, 123 Letha Shore., Dis")), + labels[1]); + + // Two fields at least - filter out the name. + AutoFillProfile::CreateInferredLabels(&profiles, &labels, 2, NAME_FULL, NULL); + EXPECT_EQ(string16(ASCIIToUTF16("666 Erebus St., Elysium")), labels[0]); + EXPECT_EQ(string16(ASCIIToUTF16("123 Letha Shore., Dis")), labels[1]); + + std::vector<AutoFillFieldType> suggested_fields; + suggested_fields.push_back(ADDRESS_HOME_CITY); + suggested_fields.push_back(ADDRESS_HOME_STATE); + suggested_fields.push_back(ADDRESS_HOME_ZIP); + + // Two fields at least, from suggested fields - no filter. + AutoFillProfile::CreateInferredLabels(&profiles, &labels, 2, UNKNOWN_TYPE, + &suggested_fields); + EXPECT_EQ(string16(ASCIIToUTF16("Elysium, CA")), labels[0]); + EXPECT_EQ(string16(ASCIIToUTF16("Dis, CA")), labels[1]); + + // Three fields at least, from suggested fields - no filter. + AutoFillProfile::CreateInferredLabels(&profiles, &labels, 3, UNKNOWN_TYPE, + &suggested_fields); + EXPECT_EQ(string16(ASCIIToUTF16("Elysium, CA, 91111")), labels[0]); + EXPECT_EQ(string16(ASCIIToUTF16("Dis, CA, 91222")), labels[1]); + + // Three fields at least, from suggested fields - but filter reduces available + // fields to two. + AutoFillProfile::CreateInferredLabels(&profiles, &labels, 3, + ADDRESS_HOME_STATE, &suggested_fields); + EXPECT_EQ(string16(ASCIIToUTF16("Elysium, 91111")), labels[0]); + EXPECT_EQ(string16(ASCIIToUTF16("Dis, 91222")), labels[1]); + + suggested_fields.clear(); + // In our implementation we always display NAME_FULL for all NAME* fields... + suggested_fields.push_back(NAME_MIDDLE); + // One field at least, from suggested fields - no filter. + AutoFillProfile::CreateInferredLabels(&profiles, &labels, 1, UNKNOWN_TYPE, + &suggested_fields); + EXPECT_EQ(string16(ASCIIToUTF16("John Doe")), labels[0]); + EXPECT_EQ(string16(ASCIIToUTF16("Jane Doe")), labels[1]); + + // One field at least, from suggested fields - filter the same as suggested + // field. + AutoFillProfile::CreateInferredLabels(&profiles, &labels, 1, NAME_MIDDLE, + &suggested_fields); + EXPECT_EQ(string16(ASCIIToUTF16("")), labels[0]); + EXPECT_EQ(string16(ASCIIToUTF16("")), labels[1]); + // Clean up. + STLDeleteContainerPointers(profiles.begin(), profiles.end()); +} + TEST(AutoFillProfileTest, IsSubsetOf) { scoped_ptr<AutoFillProfile> a, b; @@ -359,8 +491,8 @@ TEST(AutoFillProfileTest, MergeWith) { "constitutionalist@gmail.com", "United States Government", "Monticello", NULL, "Charlottesville", "Virginia", "22902", NULL, "12134759123", "19384284720"); - EXPECT_EQ(expected_a, *a); - EXPECT_EQ(expected_b, *b); + EXPECT_EQ(0, expected_a.Compare(*a)); + EXPECT_EQ(0, expected_b.Compare(*b)); } TEST(AutoFillProfileTest, Compare) { diff --git a/chrome/browser/autofill/credit_card.cc b/chrome/browser/autofill/credit_card.cc index be05c50..3c4f8b2 100644 --- a/chrome/browser/autofill/credit_card.cc +++ b/chrome/browser/autofill/credit_card.cc @@ -41,14 +41,14 @@ const int kAutoFillCreditCardLength = arraysize(kAutoFillCreditCardTypes); // These values must match the values in WebKitClientImpl in webkit/glue. We // send these strings to WK, which then asks WebKitClientImpl to load the image // data. -const char* kAmericanExpressCard = "americanExpressCC"; -const char* kDinersCard = "dinersCC"; -const char* kDiscoverCard = "discoverCC"; -const char* kGenericCard = "genericCC"; -const char* kJCBCard = "jcbCC"; -const char* kMasterCard = "masterCardCC"; -const char* kSoloCard = "soloCC"; -const char* kVisaCard = "visaCC"; +const char* const kAmericanExpressCard = "americanExpressCC"; +const char* const kDinersCard = "dinersCC"; +const char* const kDiscoverCard = "discoverCC"; +const char* const kGenericCard = "genericCC"; +const char* const kJCBCard = "jcbCC"; +const char* const kMasterCard = "masterCardCC"; +const char* const kSoloCard = "soloCC"; +const char* const kVisaCard = "visaCC"; std::string GetCreditCardType(const string16& number) { // Credit card number specifications taken from: @@ -132,34 +132,20 @@ std::string GetCreditCardType(const string16& number) { } // namespace -CreditCard::CreditCard(const string16& label, - int unique_id) - : expiration_month_(0), - expiration_year_(0), - label_(label), - billing_address_id_(0), - unique_id_(unique_id), - guid_(guid::GenerateGUID()) { -} - CreditCard::CreditCard(const std::string& guid) : expiration_month_(0), expiration_year_(0), - billing_address_id_(0), - unique_id_(0), guid_(guid) { } CreditCard::CreditCard() : expiration_month_(0), expiration_year_(0), - billing_address_id_(0), - unique_id_(0), guid_(guid::GenerateGUID()) { } -CreditCard::CreditCard(const CreditCard& card) : FormGroup() { - operator=(card); +CreditCard::CreditCard(const CreditCard& credit_card) : FormGroup() { + operator=(credit_card); } CreditCard::~CreditCard() {} @@ -389,46 +375,44 @@ string16 CreditCard::LastFourDigits() const { return number().substr(number().size() - kNumLastDigits, kNumLastDigits); } -void CreditCard::operator=(const CreditCard& source) { - number_ = source.number_; - name_on_card_ = source.name_on_card_; - type_ = source.type_; - last_four_digits_ = source.last_four_digits_; - expiration_month_ = source.expiration_month_; - expiration_year_ = source.expiration_year_; - label_ = source.label_; - billing_address_id_ = source.billing_address_id_; - unique_id_ = source.unique_id_; - guid_ = source.guid_; +void CreditCard::operator=(const CreditCard& credit_card) { + number_ = credit_card.number_; + name_on_card_ = credit_card.name_on_card_; + type_ = credit_card.type_; + last_four_digits_ = credit_card.last_four_digits_; + expiration_month_ = credit_card.expiration_month_; + expiration_year_ = credit_card.expiration_year_; + label_ = credit_card.label_; + guid_ = credit_card.guid_; } -bool CreditCard::operator==(const CreditCard& creditcard) const { +int CreditCard::Compare(const CreditCard& credit_card) const { // The following CreditCard field types are the only types we store in the // WebDB so far, so we're only concerned with matching these types in the - // profile. + // credit card. const AutoFillFieldType types[] = { CREDIT_CARD_NAME, - CREDIT_CARD_TYPE, CREDIT_CARD_NUMBER, CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_4_DIGIT_YEAR }; - - if (label_ != creditcard.label_ || - unique_id_ != creditcard.unique_id_ || - billing_address_id_ != creditcard.billing_address_id_) { - return false; - } - for (size_t index = 0; index < arraysize(types); ++index) { - if (GetFieldText(AutoFillType(types[index])) != - creditcard.GetFieldText(AutoFillType(types[index]))) - return false; + int comparison = GetFieldText(AutoFillType(types[index])).compare( + credit_card.GetFieldText(AutoFillType(types[index]))); + if (comparison != 0) + return comparison; } - return true; + return 0; } -bool CreditCard::operator!=(const CreditCard& creditcard) const { - return !operator==(creditcard); +bool CreditCard::operator==(const CreditCard& credit_card) const { + if (label_ != credit_card.label_ || guid_ != credit_card.guid_) + return false; + + return Compare(credit_card) == 0; +} + +bool CreditCard::operator!=(const CreditCard& credit_card) const { + return !operator==(credit_card); } // Use the Luhn formula to validate the number. @@ -460,10 +444,9 @@ bool CreditCard::IsCreditCardNumber(const string16& text) { bool CreditCard::IsEmpty() const { FieldTypeSet types; GetAvailableFieldTypes(&types); - return types.empty() && billing_address_id_ == 0; + return types.empty(); } - string16 CreditCard::ExpirationMonthAsString() const { if (expiration_month_ == 0) return string16(); @@ -630,25 +613,21 @@ bool CreditCard::ConvertDate(const string16& date, int* num) const { } // So we can compare CreditCards with EXPECT_EQ(). -std::ostream& operator<<(std::ostream& os, const CreditCard& creditcard) { +std::ostream& operator<<(std::ostream& os, const CreditCard& credit_card) { return os - << UTF16ToUTF8(creditcard.Label()) - << " " - << creditcard.unique_id() - << " " - << creditcard.guid() + << UTF16ToUTF8(credit_card.Label()) << " " - << creditcard.billing_address_id() + << credit_card.guid() << " " - << UTF16ToUTF8(creditcard.GetFieldText(AutoFillType(CREDIT_CARD_NAME))) + << UTF16ToUTF8(credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NAME))) << " " - << UTF16ToUTF8(creditcard.GetFieldText(AutoFillType(CREDIT_CARD_TYPE))) + << UTF16ToUTF8(credit_card.GetFieldText(AutoFillType(CREDIT_CARD_TYPE))) << " " - << UTF16ToUTF8(creditcard.GetFieldText(AutoFillType(CREDIT_CARD_NUMBER))) + << UTF16ToUTF8(credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NUMBER))) << " " - << UTF16ToUTF8(creditcard.GetFieldText( + << UTF16ToUTF8(credit_card.GetFieldText( AutoFillType(CREDIT_CARD_EXP_MONTH))) << " " - << UTF16ToUTF8(creditcard.GetFieldText( + << UTF16ToUTF8(credit_card.GetFieldText( AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR))); } diff --git a/chrome/browser/autofill/credit_card.h b/chrome/browser/autofill/credit_card.h index f437a90..91fc6ce 100644 --- a/chrome/browser/autofill/credit_card.h +++ b/chrome/browser/autofill/credit_card.h @@ -14,14 +14,11 @@ // A form group that stores credit card information. class CreditCard : public FormGroup { public: - // DEPRECATED - // TODO(dhollowa): Remove unique ID and label. http://crbug.com/58813 - CreditCard(const string16& label, int unique_id); explicit CreditCard(const std::string& guid); // For use in STL containers. CreditCard(); - CreditCard(const CreditCard& card); + CreditCard(const CreditCard& credit_card); virtual ~CreditCard(); // FormGroup implementation: @@ -35,7 +32,7 @@ class CreditCard : public FormGroup { virtual string16 GetFieldText(const AutoFillType& type) const; virtual string16 GetPreviewText(const AutoFillType& type) const; virtual void SetInfo(const AutoFillType& type, const string16& value); - const string16& Label() const { return label_; } + virtual const string16 Label() const { return label_; } // The number altered for display, for example: ******1234 string16 ObfuscatedNumber() const; @@ -45,26 +42,25 @@ class CreditCard : public FormGroup { string16 LastFourDigits() const; const string16& type() const { return type_; } - int billing_address_id() const { return billing_address_id_; } - - int unique_id() const { return unique_id_; } - void set_unique_id(int id) { unique_id_ = id; } // The guid is the primary identifier for |CreditCard| objects. const std::string guid() const { return guid_; } void set_guid(const std::string& guid) { guid_ = guid; } - // The caller should verify that the corresponding AutoFillProfile exists. - void set_billing_address_id(int address_id) { - billing_address_id_ = address_id; - } - // For use in STL containers. - void operator=(const CreditCard&); + void operator=(const CreditCard& credit_card); + + // Comparison for Sync. Returns 0 if the credit card is the same as |this|, + // or < 0, or > 0 if it is different. The implied ordering can be used for + // culling duplicates. The ordering is based on collation order of the + // textual contents of the fields. + // GUIDs, labels, and unique IDs are not compared, only the values of the + // credit cards themselves. + int Compare(const CreditCard& credit_card) const; // Used by tests. - bool operator==(const CreditCard& creditcard) const; - bool operator!=(const CreditCard& creditcard) const; + bool operator==(const CreditCard& credit_card) const; + bool operator!=(const CreditCard& credit_card) const; void set_label(const string16& label) { label_ = label; } // Returns true if |value| is a credit card number. Uses the Luhn formula to @@ -147,18 +143,11 @@ class CreditCard : public FormGroup { // This is the display name of the card set by the user, e.g., Amazon Visa. string16 label_; - // The billing address. This is the unique ID of the AutoFillProfile that - // contains the corresponding billing address. - int billing_address_id_; - - // The unique ID of this credit card. - int unique_id_; - // The guid of this credit card. std::string guid_; }; // So we can compare CreditCards with EXPECT_EQ(). -std::ostream& operator<<(std::ostream& os, const CreditCard& creditcard); +std::ostream& operator<<(std::ostream& os, const CreditCard& credit_card); #endif // CHROME_BROWSER_AUTOFILL_CREDIT_CARD_H_ diff --git a/chrome/browser/autofill/credit_card_unittest.cc b/chrome/browser/autofill/credit_card_unittest.cc index 5a9a732..507379b 100644 --- a/chrome/browser/autofill/credit_card_unittest.cc +++ b/chrome/browser/autofill/credit_card_unittest.cc @@ -15,52 +15,52 @@ namespace { // existence of credit card number, month, and year fields. TEST(CreditCardTest, PreviewSummaryAndObfuscatedNumberStrings) { // Case 0: empty credit card. - CreditCard credit_card0(string16(), 0); + CreditCard credit_card0; string16 summary0 = credit_card0.PreviewSummary(); EXPECT_EQ(string16(), summary0); string16 obfuscated0 = credit_card0.ObfuscatedNumber(); EXPECT_EQ(string16(), obfuscated0); // Case 00: Empty credit card with empty strings. - CreditCard credit_card00(string16(), 0); + CreditCard credit_card00; autofill_test::SetCreditCardInfo(&credit_card00, "Corporate", - "John Dillinger", "Visa", "", "", "", 1); + "John Dillinger", "", "", ""); string16 summary00 = credit_card00.PreviewSummary(); EXPECT_EQ(string16(), summary00); string16 obfuscated00 = credit_card00.ObfuscatedNumber(); EXPECT_EQ(string16(), obfuscated00); // Case 1: No credit card number. - CreditCard credit_card1(string16(), 0); + CreditCard credit_card1; autofill_test::SetCreditCardInfo(&credit_card1, "Corporate", - "John Dillinger", "Visa", "", "01", "2010", 1); + "John Dillinger", "", "01", "2010"); string16 summary1 = credit_card1.PreviewSummary(); EXPECT_EQ(string16(), summary1); string16 obfuscated1 = credit_card1.ObfuscatedNumber(); EXPECT_EQ(string16(), obfuscated1); // Case 2: No month. - CreditCard credit_card2(string16(), 0); + CreditCard credit_card2; autofill_test::SetCreditCardInfo(&credit_card2, "Corporate", - "John Dillinger", "Visa", "123456789012", "", "2010", 1); + "John Dillinger", "123456789012", "", "2010"); string16 summary2 = credit_card2.PreviewSummary(); EXPECT_EQ(string16(ASCIIToUTF16("************9012")), summary2); string16 obfuscated2 = credit_card2.ObfuscatedNumber(); EXPECT_EQ(string16(ASCIIToUTF16("************9012")), obfuscated2); // Case 3: No year. - CreditCard credit_card3(string16(), 0); + CreditCard credit_card3; autofill_test::SetCreditCardInfo(&credit_card3, "Corporate", - "John Dillinger", "Visa", "123456789012", "01", "", 1); + "John Dillinger", "123456789012", "01", ""); string16 summary3 = credit_card3.PreviewSummary(); EXPECT_EQ(string16(ASCIIToUTF16("************9012")), summary3); string16 obfuscated3 = credit_card3.ObfuscatedNumber(); EXPECT_EQ(string16(ASCIIToUTF16("************9012")), obfuscated3); // Case 4: Have everything. - CreditCard credit_card4(string16(), 0); + CreditCard credit_card4; autofill_test::SetCreditCardInfo(&credit_card4, "Corporate", - "John Dillinger", "Visa", "123456789012", "01", "2010", 1); + "John Dillinger", "123456789012", "01", "2010"); string16 summary4 = credit_card4.PreviewSummary(); EXPECT_EQ(string16(ASCIIToUTF16("************9012, Exp: 01/2010")), summary4); string16 obfuscated4 = credit_card4.ObfuscatedNumber(); diff --git a/chrome/browser/autofill/crypto/rc4_decryptor.h b/chrome/browser/autofill/crypto/rc4_decryptor.h new file mode 100644 index 0000000..2fb18ac --- /dev/null +++ b/chrome/browser/autofill/crypto/rc4_decryptor.h @@ -0,0 +1,103 @@ +// 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_AUTOFILL_CRYPTO_RC4_DECRYPTOR_H_ +#define CHROME_BROWSER_AUTOFILL_CRYPTO_RC4_DECRYPTOR_H_ + +#include <string> +#include "base/basictypes.h" +#include "base/scoped_ptr.h" + +// This is modified RC4 decryption used for import of Toolbar autofill data +// only. The difference from the Crypto Api implementation is twofold: +// First, it uses a non-standard key size (160 bit), not supported by Microsoft +// (it supports only 40 and 128 bit for RC4). Second, it codes 128 words with +// value 0x0020 at the beginning of the code to enhance security. +// This class used in chrome/browser/autofill/autofill_ie_toolbar_import_win.cc. +// This class should not be used anywhere else!!! +class RC4Decryptor { + public: + explicit RC4Decryptor(wchar_t const* password) { + PrepareKey(reinterpret_cast<const uint8 *>(password), + wcslen(password) * sizeof(wchar_t)); + std::wstring data; + // First 128 bytes should be spaces. + data.resize(128, L' '); + Run(data.c_str()); + } + + // Run the algorithm + std::wstring Run(const std::wstring& data) { + int data_size = data.length() * sizeof(wchar_t); + + scoped_array<wchar_t> buffer(new wchar_t[data.length() + 1]); + memset(buffer.get(), 0, (data.length() + 1) * sizeof(wchar_t)); + memcpy(buffer.get(), data.c_str(), data_size); + + RunInternal(reinterpret_cast<uint8 *>(buffer.get()), data_size); + + std::wstring result(buffer.get()); + + // Clear the memory + memset(buffer.get(), 0, data_size); + return result; + } + + private: + static const int kKeyDataSize = 256; + struct Rc4Key { + uint8 state[kKeyDataSize]; + uint8 x; + uint8 y; + }; + + void SwapByte(uint8* byte1, uint8* byte2) { + uint8 temp = *byte1; + *byte1 = *byte2; + *byte2 = temp; + } + + void PrepareKey(const uint8 *key_data, int key_data_len) { + uint8 index1 = 0; + uint8 index2 = 0; + uint8* state; + short counter; + + state = &key_.state[0]; + for (counter = 0; counter < kKeyDataSize; ++counter) + state[counter] = static_cast<uint8>(counter); + + key_.x = key_.y = 0; + + for (counter = 0; counter < kKeyDataSize; counter++) { + index2 = (key_data[index1] + state[counter] + index2) % kKeyDataSize; + SwapByte(&state[counter], &state[index2]); + index1 = (index1 + 1) % key_data_len; + } + } + + void RunInternal(uint8 *buffer, int buffer_len) { + uint8 x, y; + uint8 xor_index = 0; + uint8* state; + int counter; + + x = key_.x; + y = key_.y; + state = &key_.state[0]; + for (counter = 0; counter < buffer_len; ++counter) { + x = (x + 1) % kKeyDataSize; + y = (state[x] + y) % kKeyDataSize; + SwapByte(&state[x], &state[y]); + xor_index = (state[x] + state[y]) % kKeyDataSize; + buffer[counter] ^= state[xor_index]; + } + key_.x = x; + key_.y = y; + } + + Rc4Key key_; +}; + +#endif // CHROME_BROWSER_AUTOFILL_CRYPTO_RC4_DECRYPTOR_H_ diff --git a/chrome/browser/autofill/form_field.cc b/chrome/browser/autofill/form_field.cc index 24b45ed..95b1e55 100644 --- a/chrome/browser/autofill/form_field.cc +++ b/chrome/browser/autofill/form_field.cc @@ -207,6 +207,12 @@ bool FormField::ParseText(std::vector<AutoFillField*>::const_iterator* iter, const string16& pattern, AutoFillField** dest, bool match_label_only) { + // Some forms have one or more hidden fields before each visible input; skip + // past these. + while (**iter && LowerCaseEqualsASCII((**iter)->form_control_type(), + kControlTypeHidden)) + (*iter)++; + AutoFillField* field = **iter; if (!field) return false; diff --git a/chrome/browser/autofill/form_field.h b/chrome/browser/autofill/form_field.h index cf57350..38b2ec2 100644 --- a/chrome/browser/autofill/form_field.h +++ b/chrome/browser/autofill/form_field.h @@ -126,7 +126,7 @@ class FormField { // Checkout field name limitation. All ECML compliant web forms will be // recognized correctly as such however the restrictions on having exactly // ECML compliant names have been loosened to only require that field names - // be prefixed with an ECML compiant name in order to accommodate checkout. + // be prefixed with an ECML compliant name in order to accommodate checkout. // Additionally we allow the use of '.' as a word delimiter in addition to the // ECML standard '_' (see FormField::FormField for details). static string16 GetEcmlPattern(const char* ecml_name); diff --git a/chrome/browser/autofill/form_group.cc b/chrome/browser/autofill/form_group.cc index e99059b..7e91bc3 100644 --- a/chrome/browser/autofill/form_group.cc +++ b/chrome/browser/autofill/form_group.cc @@ -8,7 +8,7 @@ string16 FormGroup::GetPreviewText(const AutoFillType& type) const { return GetFieldText(type); } -const string16& FormGroup::Label() const { return EmptyString16(); } +const string16 FormGroup::Label() const { return string16(); } bool FormGroup::operator!=(const FormGroup& form_group) const { FieldTypeSet a, b, symmetric_difference; diff --git a/chrome/browser/autofill/form_group.h b/chrome/browser/autofill/form_group.h index 68a7d3a..5f16276 100644 --- a/chrome/browser/autofill/form_group.h +++ b/chrome/browser/autofill/form_group.h @@ -53,7 +53,7 @@ class FormGroup { // Returns the label for this FormGroup item. This should be overridden for // form group items that implement a label. - virtual const string16& Label() const; + virtual const string16 Label() const; // Returns true if the field data in |form_group| does not match the field // data in this FormGroup. diff --git a/chrome/browser/autofill/form_structure.cc b/chrome/browser/autofill/form_structure.cc index 299c58b..d9ff45b 100644 --- a/chrome/browser/autofill/form_structure.cc +++ b/chrome/browser/autofill/form_structure.cc @@ -115,6 +115,8 @@ FormStructure::~FormStructure() {} bool FormStructure::EncodeUploadRequest(bool auto_fill_used, std::string* encoded_xml) const { + DCHECK(encoded_xml); + encoded_xml->clear(); bool auto_fillable = IsAutoFillable(); DCHECK(auto_fillable); // Caller should've checked for search pages. if (!auto_fillable) @@ -139,7 +141,8 @@ bool FormStructure::EncodeUploadRequest(bool auto_fill_used, // personaldata_manager_->GetDataPresent(); autofil_request_xml.SetAttr(buzz::QName(kAttributeDataPresent), ""); - EncodeFormRequest(FormStructure::UPLOAD, &autofil_request_xml); + if (!EncodeFormRequest(FormStructure::UPLOAD, &autofil_request_xml)) + return false; // Malformed form, skip it. // Obtain the XML structure as a string. *encoded_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; @@ -150,7 +153,13 @@ bool FormStructure::EncodeUploadRequest(bool auto_fill_used, // static bool FormStructure::EncodeQueryRequest(const ScopedVector<FormStructure>& forms, - std::string* encoded_xml) { + std::vector<std::string>* encoded_signatures, + std::string* encoded_xml) { + DCHECK(encoded_signatures); + DCHECK(encoded_xml); + encoded_xml->clear(); + encoded_signatures->clear(); + encoded_signatures->reserve(forms.size()); buzz::XmlElement autofil_request_xml(buzz::QName("autofillquery")); // Attributes for the <autofillquery> element. // @@ -158,19 +167,32 @@ bool FormStructure::EncodeQueryRequest(const ScopedVector<FormStructure>& forms, // For now these values are hacked from the toolbar code. autofil_request_xml.SetAttr(buzz::QName(kAttributeClientVersion), "6.1.1715.1442/en (GGLL)"); + // Some badly formatted web sites repeat forms - detect that and encode only + // one form as returned data would be the same for all the repeated forms. + std::set<std::string> processed_forms; for (ScopedVector<FormStructure>::const_iterator it = forms.begin(); it != forms.end(); ++it) { - buzz::XmlElement* encompassing_xml_element = - new buzz::XmlElement(buzz::QName("form")); + std::string signature((*it)->FormSignature()); + if (processed_forms.find(signature) != processed_forms.end()) + continue; + processed_forms.insert(signature); + scoped_ptr<buzz::XmlElement> encompassing_xml_element( + new buzz::XmlElement(buzz::QName("form"))); encompassing_xml_element->SetAttr(buzz::QName(kAttributeSignature), - (*it)->FormSignature()); + signature); - (*it)->EncodeFormRequest(FormStructure::QUERY, encompassing_xml_element); + if (!(*it)->EncodeFormRequest(FormStructure::QUERY, + encompassing_xml_element.get())) + continue; // Malformed form, skip it. - autofil_request_xml.AddElement(encompassing_xml_element); + autofil_request_xml.AddElement(encompassing_xml_element.release()); + encoded_signatures->push_back(signature); } + if (!encoded_signatures->size()) + return false; + // Obtain the XML structure as a string. *encoded_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; *encoded_xml += autofil_request_xml.Str().c_str(); @@ -402,8 +424,16 @@ bool FormStructure::EncodeFormRequest( buzz::XmlElement* encompassing_xml_element) const { if (!field_count()) // Nothing to add. return false; + // Some badly formatted web sites repeat fields - limit number of fields to + // 48, which is far larger than any valid form and XML still fits into 2K. + const size_t kMaxFieldsOnTheForm = 48; + if (field_count() > kMaxFieldsOnTheForm) { + // This is not a valid form, most certainly. Do not send request for the + // wrongly formatted forms. + return false; + } // Add the child nodes for the form fields. - for (size_t index = 0; index < field_count(); index++) { + for (size_t index = 0; index < field_count(); ++index) { const AutoFillField* field = fields_[index]; if (request_type == FormStructure::UPLOAD) { FieldTypeSet types = field->possible_types(); diff --git a/chrome/browser/autofill/form_structure.h b/chrome/browser/autofill/form_structure.h index 7000056..77e106f 100644 --- a/chrome/browser/autofill/form_structure.h +++ b/chrome/browser/autofill/form_structure.h @@ -48,6 +48,7 @@ class FormStructure { // fields, first two of which would be for the first form, next 4 for the // second, and the rest is for the third. static bool EncodeQueryRequest(const ScopedVector<FormStructure>& forms, + std::vector<std::string>* encoded_signatures, std::string* encoded_xml); // Parses the field types from the server query response. |forms| must be the diff --git a/chrome/browser/autofill/form_structure_unittest.cc b/chrome/browser/autofill/form_structure_unittest.cc index f9f808a..b5881d6 100644 --- a/chrome/browser/autofill/form_structure_unittest.cc +++ b/chrome/browser/autofill/form_structure_unittest.cc @@ -319,6 +319,124 @@ TEST(FormStructureTest, HeuristicsContactInfo) { EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(8)->heuristic_type()); } +TEST(FormStructureTest, HeuristicsHiddenFields) { + scoped_ptr<FormStructure> form_structure; + FormData form; + + form.method = ASCIIToUTF16("post"); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("First Name"), + ASCIIToUTF16("firstname"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(string16(), + ASCIIToUTF16("hidden1"), + string16(), + ASCIIToUTF16("hidden"), + 0)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Last Name"), + ASCIIToUTF16("lastname"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(string16(), + ASCIIToUTF16("hidden2"), + string16(), + ASCIIToUTF16("hidden"), + 0)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("EMail"), + ASCIIToUTF16("email"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(string16(), + ASCIIToUTF16("hidden3"), + string16(), + ASCIIToUTF16("hidden"), + 0)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Phone"), + ASCIIToUTF16("phone"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(string16(), + ASCIIToUTF16("hidden4"), + string16(), + ASCIIToUTF16("hidden"), + 0)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Fax"), + ASCIIToUTF16("fax"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(string16(), + ASCIIToUTF16("hidden5"), + string16(), + ASCIIToUTF16("hidden"), + 0)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Address"), + ASCIIToUTF16("address"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(string16(), + ASCIIToUTF16("hidden6"), + string16(), + ASCIIToUTF16("hidden"), + 0)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("City"), + ASCIIToUTF16("city"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(string16(), + ASCIIToUTF16("hidden7"), + string16(), + ASCIIToUTF16("hidden"), + 0)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Zip code"), + ASCIIToUTF16("zipcode"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(string16(), + ASCIIToUTF16("hidden8"), + string16(), + ASCIIToUTF16("hidden"), + 0)); + form.fields.push_back(webkit_glue::FormField(string16(), + ASCIIToUTF16("Submit"), + string16(), + ASCIIToUTF16("submit"), + 0)); + form_structure.reset(new FormStructure(form)); + EXPECT_TRUE(form_structure->IsAutoFillable()); + + // Expect the correct number of fields. + ASSERT_EQ(17U, form_structure->field_count()); + ASSERT_EQ(8U, form_structure->autofill_count()); + + // First name. + EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type()); + // Last name. + EXPECT_EQ(NAME_LAST, form_structure->field(2)->heuristic_type()); + // Email. + EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(4)->heuristic_type()); + // Phone. + EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER, + form_structure->field(6)->heuristic_type()); + // Fax. + EXPECT_EQ(PHONE_FAX_WHOLE_NUMBER, form_structure->field(8)->heuristic_type()); + // Address. + EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(10)->heuristic_type()); + // City. + EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(12)->heuristic_type()); + // Zip. + EXPECT_EQ(ADDRESS_HOME_ZIP, form_structure->field(14)->heuristic_type()); + // Submit. + EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(16)->heuristic_type()); +} + TEST(FormStructureTest, HeuristicsSample8) { scoped_ptr<FormStructure> form_structure; FormData form; @@ -1167,4 +1285,264 @@ TEST(FormStructureTest, HeuristicsInfernoCC) { form_structure->field(4)->heuristic_type()); } +TEST(FormStructureTest, EncodeQueryRequest) { + FormData form; + form.method = ASCIIToUTF16("post"); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Name on Card"), + ASCIIToUTF16("name_on_card"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Address"), + ASCIIToUTF16("billing_address"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Card Number"), + ASCIIToUTF16("card_number"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Expiration Date"), + ASCIIToUTF16("expiration_month"), + string16(), + ASCIIToUTF16("text"), + 0)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Expiration Year"), + ASCIIToUTF16("expiration_year"), + string16(), + ASCIIToUTF16("text"), + 0)); + ScopedVector<FormStructure> forms; + forms.push_back(new FormStructure(form)); + std::vector<std::string> encoded_signatures; + std::string encoded_xml; + const char * const kSignature1 = "11337937696949187602"; + const char * const kResponse1 = + "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><autofillquery " + "clientversion=\"6.1.1715.1442/en (GGLL)\"><form signature=\"" + "11337937696949187602\"><field signature=\"412125936\"/><field " + "signature=\"1917667676\"/><field signature=\"2226358947\"/><field " + "signature=\"747221617\"/><field signature=\"4108155786\"/></form>" + "</autofillquery>"; + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, + &encoded_xml)); + ASSERT_EQ(encoded_signatures.size(), 1U); + EXPECT_EQ(encoded_signatures[0], kSignature1); + EXPECT_EQ(encoded_xml, kResponse1); + + // Add the same form, only one will be encoded, so EncodeQueryRequest() should + // return the same data. + forms.push_back(new FormStructure(form)); + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, + &encoded_xml)); + ASSERT_EQ(encoded_signatures.size(), 1U); + EXPECT_EQ(encoded_signatures[0], kSignature1); + EXPECT_EQ(encoded_xml, kResponse1); + // Add 5 address fields - this should be still a valid form. + for (size_t i = 0; i < 5; ++i) { + form.fields.push_back( + webkit_glue::FormField(ASCIIToUTF16("Address"), + ASCIIToUTF16("address"), + string16(), + ASCIIToUTF16("text"), + 0)); + } + + forms.push_back(new FormStructure(form)); + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, + &encoded_xml)); + ASSERT_EQ(encoded_signatures.size(), 2U); + EXPECT_EQ(encoded_signatures[0], kSignature1); + const char * const kSignature2 = "8308881815906226214"; + EXPECT_EQ(encoded_signatures[1], kSignature2); + const char * const kResponse2 = + "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><autofillquery " + "clientversion=\"6.1.1715.1442/en (GGLL)\"><form signature=\"" + "11337937696949187602\"><field signature=\"412125936\"/><field signature=" + "\"1917667676\"/><field signature=\"2226358947\"/><field signature=\"" + "747221617\"/><field signature=\"4108155786\"/></form><form signature=\"" + "8308881815906226214\"><field signature=\"412125936\"/><field signature=" + "\"1917667676\"/><field signature=\"2226358947\"/><field signature=\"" + "747221617\"/><field signature=\"4108155786\"/><field signature=\"" + "509334676\"/><field signature=\"509334676\"/><field signature=\"" + "509334676\"/><field signature=\"509334676\"/><field signature=\"" + "509334676\"/></form></autofillquery>"; + EXPECT_EQ(encoded_xml, kResponse2); + + // Add 50 address fields - the form is not valid anymore, but previous ones + // are. The result should be the same as in previous test. + for (size_t i = 0; i < 50; ++i) { + form.fields.push_back( + webkit_glue::FormField(ASCIIToUTF16("Address"), + ASCIIToUTF16("address"), + string16(), + ASCIIToUTF16("text"), + 0)); + } + + forms.push_back(new FormStructure(form)); + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, + &encoded_xml)); + ASSERT_EQ(encoded_signatures.size(), 2U); + EXPECT_EQ(encoded_signatures[0], kSignature1); + EXPECT_EQ(encoded_signatures[1], kSignature2); + EXPECT_EQ(encoded_xml, kResponse2); + + // Check that we fail if there are only bad form(s). + ScopedVector<FormStructure> bad_forms; + bad_forms.push_back(new FormStructure(form)); + EXPECT_FALSE(FormStructure::EncodeQueryRequest(bad_forms, &encoded_signatures, + &encoded_xml)); + EXPECT_EQ(encoded_signatures.size(), 0U); + EXPECT_EQ(encoded_xml, ""); +} + +TEST(FormStructureTest, EncodeUploadRequest) { + scoped_ptr<FormStructure> form_structure; + std::vector<FieldTypeSet> possible_field_types; + FormData form; + form.method = ASCIIToUTF16("post"); + form_structure.reset(new FormStructure(form)); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("First Name"), + ASCIIToUTF16("firstname"), + string16(), + ASCIIToUTF16("text"), + 0)); + possible_field_types.push_back(FieldTypeSet()); + possible_field_types.back().insert(NAME_FIRST); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Last Name"), + ASCIIToUTF16("lastname"), + string16(), + ASCIIToUTF16("text"), + 0)); + possible_field_types.push_back(FieldTypeSet()); + possible_field_types.back().insert(NAME_LAST); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("EMail"), + ASCIIToUTF16("email"), + string16(), + ASCIIToUTF16("email"), + 0)); + possible_field_types.push_back(FieldTypeSet()); + possible_field_types.back().insert(EMAIL_ADDRESS); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Phone"), + ASCIIToUTF16("phone"), + string16(), + ASCIIToUTF16("number"), + 0)); + possible_field_types.push_back(FieldTypeSet()); + possible_field_types.back().insert(PHONE_HOME_WHOLE_NUMBER); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Country"), + ASCIIToUTF16("country"), + string16(), + ASCIIToUTF16("select-one"), + 0)); + possible_field_types.push_back(FieldTypeSet()); + possible_field_types.back().insert(ADDRESS_HOME_COUNTRY); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Fax"), + ASCIIToUTF16("fax"), + string16(), + ASCIIToUTF16("tel"), + 0)); + possible_field_types.push_back(FieldTypeSet()); + possible_field_types.back().insert(PHONE_FAX_WHOLE_NUMBER); + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Address"), + ASCIIToUTF16("address"), + string16(), + ASCIIToUTF16("radio"), + 0)); + possible_field_types.push_back(FieldTypeSet()); + possible_field_types.back().insert(ADDRESS_HOME_LINE1); + form_structure.reset(new FormStructure(form)); + std::string encoded_xml; + ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); + for (size_t i = 0; i < form_structure->field_count(); ++i) + form_structure->set_possible_types(i, possible_field_types[i]); + EXPECT_TRUE(form_structure->EncodeUploadRequest(false, &encoded_xml)); + EXPECT_EQ(encoded_xml, + "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><autofillupload " + "clientversion=\"6.1.1715.1442/en (GGLL)\" formsignature=\"" + "8269229441054798720\" autofillused=\"false\" datapresent=\"\"><field " + "signature=\"3763331450\" autofilltype=\"3\"/><field signature=\"" + "3494530716\" autofilltype=\"5\"/><field signature=\"1029417091\" " + "autofilltype=\"9\"/><field signature=\"466116101\" autofilltype=" + "\"14\"/><field signature=\"2799270304\" autofilltype=\"36\"/><field " + "signature=\"1876771436\" autofilltype=\"24\"/><field signature=" + "\"263446779\" autofilltype=\"30\"/></autofillupload>"); + EXPECT_TRUE(form_structure->EncodeUploadRequest(true, &encoded_xml)); + EXPECT_EQ(encoded_xml, + "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><autofillupload " + "clientversion=\"6.1.1715.1442/en (GGLL)\" formsignature=\"" + "8269229441054798720\" autofillused=\"true\" datapresent=\"\"><field " + "signature=\"3763331450\" autofilltype=\"3\"/><field signature=\"" + "3494530716\" autofilltype=\"5\"/><field signature=\"1029417091\" " + "autofilltype=\"9\"/><field signature=\"466116101\" autofilltype=" + "\"14\"/><field signature=\"2799270304\" autofilltype=\"36\"/><field " + "signature=\"1876771436\" autofilltype=\"24\"/><field signature=" + "\"263446779\" autofilltype=\"30\"/></autofillupload>"); + // Add 5 address fields - this should be still a valid form. + for (size_t i = 0; i < 5; ++i) { + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Address"), + ASCIIToUTF16("address"), + string16(), + ASCIIToUTF16("text"), + 0)); + possible_field_types.push_back(FieldTypeSet()); + possible_field_types.back().insert(ADDRESS_HOME_LINE1); + possible_field_types.back().insert(ADDRESS_HOME_LINE2); + possible_field_types.back().insert(ADDRESS_BILLING_LINE1); + possible_field_types.back().insert(ADDRESS_BILLING_LINE2); + } + form_structure.reset(new FormStructure(form)); + ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); + for (size_t i = 0; i < form_structure->field_count(); ++i) + form_structure->set_possible_types(i, possible_field_types[i]); + EXPECT_TRUE(form_structure->EncodeUploadRequest(false, &encoded_xml)); + EXPECT_EQ(encoded_xml, + "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><autofillupload " + "clientversion=\"6.1.1715.1442/en (GGLL)\" formsignature=\"" + "2027360543766157144\" autofillused=\"false\" datapresent=\"\"><field " + "signature=\"3763331450\" autofilltype=\"3\"/><field signature=\"" + "3494530716\" autofilltype=\"5\"/><field signature=\"1029417091\" " + "autofilltype=\"9\"/><field signature=\"466116101\" autofilltype=\"14\"/>" + "<field signature=\"2799270304\" autofilltype=\"36\"/><field signature=\"" + "1876771436\" autofilltype=\"24\"/><field signature=\"263446779\" " + "autofilltype=\"30\"/><field signature=\"509334676\" autofilltype=" + "\"30\"/><field signature=\"509334676\" autofilltype=\"31\"/><field " + "signature=\"509334676\" autofilltype=\"37\"/><field signature=" + "\"509334676\" autofilltype=\"38\"/><field signature=\"509334676\" " + "autofilltype=\"30\"/><field signature=\"509334676\" autofilltype=" + "\"31\"/><field signature=\"509334676\" autofilltype=\"37\"/><field " + "signature=\"509334676\" autofilltype=\"38\"/><field signature=\"" + "509334676\" autofilltype=\"30\"/><field signature=\"509334676\" " + "autofilltype=\"31\"/><field signature=\"509334676\" " + "autofilltype=\"37\"/><field signature=\"509334676\" autofilltype=" + "\"38\"/><field signature=\"509334676\" autofilltype=\"30\"/><field " + "signature=\"509334676\" autofilltype=\"31\"/><field signature=" + "\"509334676\" autofilltype=\"37\"/><field signature=\"509334676\" " + "autofilltype=\"38\"/><field signature=\"509334676\" autofilltype=" + "\"30\"/><field signature=\"509334676\" autofilltype=\"31\"/><field " + "signature=\"509334676\" autofilltype=\"37\"/><field signature=" + "\"509334676\" autofilltype=\"38\"/></autofillupload>"); + // Add 50 address fields - now the form is invalid. + for (size_t i = 0; i < 50; ++i) { + form.fields.push_back(webkit_glue::FormField(ASCIIToUTF16("Address"), + ASCIIToUTF16("address"), + string16(), + ASCIIToUTF16("text"), + 0)); + possible_field_types.push_back(FieldTypeSet()); + possible_field_types.back().insert(ADDRESS_HOME_LINE1); + possible_field_types.back().insert(ADDRESS_HOME_LINE2); + possible_field_types.back().insert(ADDRESS_BILLING_LINE1); + possible_field_types.back().insert(ADDRESS_BILLING_LINE2); + } + form_structure.reset(new FormStructure(form)); + ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); + for (size_t i = 0; i < form_structure->field_count(); ++i) + form_structure->set_possible_types(i, possible_field_types[i]); + EXPECT_FALSE(form_structure->EncodeUploadRequest(false, &encoded_xml)); + EXPECT_EQ(encoded_xml, ""); +} + } // namespace diff --git a/chrome/browser/autofill/personal_data_manager.cc b/chrome/browser/autofill/personal_data_manager.cc index 8ea110e..db65b02 100644 --- a/chrome/browser/autofill/personal_data_manager.cc +++ b/chrome/browser/autofill/personal_data_manager.cc @@ -26,19 +26,17 @@ namespace { // before AutoFill will attempt to import the data into a profile. const int kMinImportSize = 3; -const char kUnlabeled[] = "Unlabeled"; - template<typename T> -class FormGroupIDMatchesFunctor { +class FormGroupGUIDMatchesFunctor { public: - explicit FormGroupIDMatchesFunctor(int id) : id_(id) {} + explicit FormGroupGUIDMatchesFunctor(const std::string& guid) : guid_(guid) {} bool operator()(const T& form_group) { - return form_group.unique_id() == id_; + return form_group.guid() == guid_; } private: - int id_; + std::string guid_; }; template<typename T> @@ -55,6 +53,51 @@ T* address_of(T& v) { return &v; } +bool FindInProfilesByGUID(const std::vector<AutoFillProfile>& profiles, + const std::string& guid) { + for (std::vector<AutoFillProfile>::const_iterator iter = profiles.begin(); + iter != profiles.end(); + ++iter) { + if (iter->guid() == guid) + return true; + } + return false; +} + +bool FindInScopedProfilesByGUID(const ScopedVector<AutoFillProfile>& profiles, + const std::string& guid) { + for (std::vector<AutoFillProfile*>::const_iterator iter = profiles.begin(); + iter != profiles.end(); + ++iter) { + if ((*iter)->guid() == guid) + return true; + } + return false; +} + +bool FindInCreditCardsByGUID(const std::vector<CreditCard>& credit_cards, + const std::string& guid) { + for (std::vector<CreditCard>::const_iterator iter = credit_cards.begin(); + iter != credit_cards.end(); + ++iter) { + if (iter->guid() == guid) + return true; + } + return false; +} + +bool FindInScopedCreditCardsByGUID( + const ScopedVector<CreditCard>& credit_cards, const std::string& guid) { + for (std::vector<CreditCard*>::const_iterator iter = + credit_cards.begin(); + iter != credit_cards.end(); + ++iter) { + if ((*iter)->guid() == guid) + return true; + } + return false; +} + } // namespace PersonalDataManager::~PersonalDataManager() { @@ -87,6 +130,10 @@ void PersonalDataManager::OnWebDataServiceRequestDone( // If both requests have responded, then all personal data is loaded. if (pending_profiles_query_ == 0 && pending_creditcards_query_ == 0) { is_data_loaded_ = true; + std::vector<AutoFillProfile*> profile_pointers(web_profiles_.size()); + std::copy(web_profiles_.begin(), web_profiles_.end(), + profile_pointers.begin()); + AutoFillProfile::AdjustInferredLabels(&profile_pointers); FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataLoaded()); } } @@ -124,19 +171,22 @@ void PersonalDataManager::RemoveObserver( bool PersonalDataManager::ImportFormData( const std::vector<FormStructure*>& form_structures, AutoFillManager* autofill_manager) { +<<<<<<< HEAD #ifdef ANDROID // TODO: Is this the funcionality that tries to create a profile for the user // based on what they've entered into forms? return false; #else AutoLock lock(unique_ids_lock_); +======= +>>>>>>> chromium.org at r65505 // Parse the form and construct a profile based on the information that is // possible to import. int importable_fields = 0; int importable_credit_card_fields = 0; - imported_profile_.reset(new AutoFillProfile(string16(), 0)); + imported_profile_.reset(new AutoFillProfile); // TODO(jhawkins): Use a hash of the CC# instead of a list of unique IDs? - imported_credit_card_.reset(new CreditCard(string16(), 0)); + imported_credit_card_.reset(new CreditCard); bool billing_address_info = false; std::vector<FormStructure*>::const_iterator iter; @@ -215,17 +265,8 @@ bool PersonalDataManager::ImportFormData( if (importable_credit_card_fields == 0) imported_credit_card_.reset(); - { - // We're now done with the unique IDs, and SaveImportedProfile() needs the - // lock, so release it. - AutoUnlock unlock(unique_ids_lock_); - - // We always save imported profiles. - SaveImportedProfile(); - - // We never save an imported credit card at this point. If there was one we - // found, we'll be asked to save it later once the user gives their OK. - } + // We always save imported profiles. + SaveImportedProfile(); return true; #endif @@ -236,14 +277,7 @@ void PersonalDataManager::GetImportedFormData(AutoFillProfile** profile, DCHECK(profile); DCHECK(credit_card); - if (imported_profile_.get()) { - imported_profile_->set_label(ASCIIToUTF16(kUnlabeled)); - } *profile = imported_profile_.get(); - - if (imported_credit_card_.get()) { - imported_credit_card_->set_label(ASCIIToUTF16(kUnlabeled)); - } *credit_card = imported_credit_card_.get(); } @@ -257,11 +291,24 @@ void PersonalDataManager::SetProfiles(std::vector<AutoFillProfile>* profiles) { std::mem_fun_ref(&AutoFillProfile::IsEmpty)), profiles->end()); +<<<<<<< HEAD #ifndef ANDROID +======= + // Ensure that profile labels are up to date. Currently, sync relies on + // labels to identify a profile. + // TODO(dhollowa): We need to deprecate labels and update the way sync + // identifies profiles. + std::vector<AutoFillProfile*> profile_pointers(profiles->size()); + std::transform(profiles->begin(), profiles->end(), profile_pointers.begin(), + address_of<AutoFillProfile>); + AutoFillProfile::AdjustInferredLabels(&profile_pointers); + +>>>>>>> chromium.org at r65505 WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!wds) return; +<<<<<<< HEAD // ANDROID FIXME: AutoLock does not build on Android as of the initial checkin. AutoLock lock(unique_ids_lock_); @@ -282,46 +329,33 @@ void PersonalDataManager::SetProfiles(std::vector<AutoFillProfile>* profiles) { // Also remove these IDs from the total set of unique IDs. unique_ids_.erase(*iter); +======= + // Any profiles that are not in the new profile list should be removed from + // the web database. + for (std::vector<AutoFillProfile*>::const_iterator iter = + web_profiles_.begin(); + iter != web_profiles_.end(); ++iter) { + if (!FindInProfilesByGUID(*profiles, (*iter)->guid())) + wds->RemoveAutoFillProfileGUID((*iter)->guid()); +>>>>>>> chromium.org at r65505 } - // Clear the unique IDs. The set of unique IDs is updated for each profile - // added to |web_profiles_| below. - unique_profile_ids_.clear(); - - // Update the web database with the existing profiles. We need to handle - // these first so that |unique_profile_ids_| is reset with the IDs of the - // existing profiles; otherwise, new profiles added before older profiles can - // take their unique ID. + // Update the web database with the existing profiles. for (std::vector<AutoFillProfile>::iterator iter = profiles->begin(); iter != profiles->end(); ++iter) { - if (iter->unique_id() != 0) { - unique_profile_ids_.insert(iter->unique_id()); - wds->UpdateAutoFillProfile(*iter); - } + if (FindInScopedProfilesByGUID(web_profiles_, iter->guid())) + wds->UpdateAutoFillProfileGUID(*iter); } - // Ensure that profile labels are up to date. Currently, sync relies on - // labels to identify a profile. - // TODO(dhollowa): We need to deprecate labels and update the way sync - // identifies profiles. - std::vector<AutoFillProfile*> profile_pointers(profiles->size()); - std::transform(profiles->begin(), profiles->end(), profile_pointers.begin(), - address_of<AutoFillProfile>); - AutoFillProfile::AdjustInferredLabels(&profile_pointers); - // Add the new profiles to the web database. for (std::vector<AutoFillProfile>::iterator iter = profiles->begin(); iter != profiles->end(); ++iter) { - // The profile was added by the AutoFill dialog, so we need to set the - // unique ID. This also means we need to add this profile to the web - // database. - if (iter->unique_id() == 0) { - iter->set_unique_id(CreateNextUniqueIDFor(&unique_profile_ids_)); - wds->AddAutoFillProfile(*iter); - } + if (!FindInScopedProfilesByGUID(web_profiles_, iter->guid())) + wds->AddAutoFillProfileGUID(*iter); } #endif + // Copy in the new profiles. web_profiles_.reset(); for (std::vector<AutoFillProfile>::iterator iter = profiles->begin(); iter != profiles->end(); ++iter) { @@ -331,6 +365,7 @@ void PersonalDataManager::SetProfiles(std::vector<AutoFillProfile>* profiles) { // Read our writes to ensure consistency with the database. Refresh(); +<<<<<<< HEAD #if !defined(ANDROID) { // We're now done with the unique IDs, and observers might call into a @@ -342,6 +377,9 @@ void PersonalDataManager::SetProfiles(std::vector<AutoFillProfile>* profiles) { FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); } #endif +======= + FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); +>>>>>>> chromium.org at r65505 } void PersonalDataManager::SetCreditCards( @@ -362,6 +400,7 @@ void PersonalDataManager::SetCreditCards( if (!wds) return; +<<<<<<< HEAD #ifndef ANDROID AutoLock lock(unique_ids_lock_); @@ -383,37 +422,32 @@ void PersonalDataManager::SetCreditCards( // Also remove these IDs from the total set of unique IDs. unique_ids_.erase(*iter); +======= + // Any credit cards that are not in the new credit card list should be + // removed. + for (std::vector<CreditCard*>::const_iterator iter = credit_cards_.begin(); + iter != credit_cards_.end(); ++iter) { + if (!FindInCreditCardsByGUID(*credit_cards, (*iter)->guid())) + wds->RemoveCreditCardGUID((*iter)->guid()); +>>>>>>> chromium.org at r65505 } - // Clear the unique IDs. The set of unique IDs is updated for each credit - // card added to |credit_cards_| below. - unique_creditcard_ids_.clear(); - - // Update the web database with the existing credit cards. We need to handle - // these first so that |unique_creditcard_ids_| is reset with the IDs of the - // existing credit cards; otherwise, new credit cards added before older - // credit cards can take their unique ID. + // Update the web database with the existing credit cards. for (std::vector<CreditCard>::iterator iter = credit_cards->begin(); iter != credit_cards->end(); ++iter) { - if (iter->unique_id() != 0) { - unique_creditcard_ids_.insert(iter->unique_id()); - wds->UpdateCreditCard(*iter); - } + if (FindInScopedCreditCardsByGUID(credit_cards_, iter->guid())) + wds->UpdateCreditCardGUID(*iter); } // Add the new credit cards to the web database. for (std::vector<CreditCard>::iterator iter = credit_cards->begin(); iter != credit_cards->end(); ++iter) { - // The credit card was added by the AutoFill dialog, so we need to set the - // unique ID. This also means we need to add this credit card to the web - // database. - if (iter->unique_id() == 0) { - iter->set_unique_id(CreateNextUniqueIDFor(&unique_creditcard_ids_)); - wds->AddCreditCard(*iter); - } + if (!FindInScopedCreditCardsByGUID(credit_cards_, iter->guid())) + wds->AddCreditCardGUID(*iter); } #endif + // Copy in the new credit cards. credit_cards_.reset(); for (std::vector<CreditCard>::iterator iter = credit_cards->begin(); iter != credit_cards->end(); ++iter) { @@ -423,6 +457,7 @@ void PersonalDataManager::SetCreditCards( // Read our writes to ensure consistency with the database. Refresh(); +<<<<<<< HEAD #if !defined(ANDROID) { // We're now done with the unique IDs, and observers might call into a @@ -434,6 +469,9 @@ void PersonalDataManager::SetCreditCards( FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); } #endif +======= + FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); +>>>>>>> chromium.org at r65505 } // TODO(jhawkins): Refactor SetProfiles so this isn't so hacky. @@ -483,38 +521,42 @@ void PersonalDataManager::UpdateProfile(const AutoFillProfile& profile) { // Update the cached profile. for (std::vector<AutoFillProfile*>::iterator iter = web_profiles_->begin(); iter != web_profiles_->end(); ++iter) { - if ((*iter)->unique_id() == profile.unique_id()) { + if ((*iter)->guid() == profile.guid()) { delete *iter; *iter = new AutoFillProfile(profile); break; } } - wds->UpdateAutoFillProfile(profile); + // Ensure that profile labels are up to date. + AutoFillProfile::AdjustInferredLabels(&web_profiles_.get()); + + wds->UpdateAutoFillProfileGUID(profile); FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); #endif } -void PersonalDataManager::RemoveProfile(int unique_id) { +void PersonalDataManager::RemoveProfile(const std::string& guid) { // TODO(jhawkins): Refactor SetProfiles so this isn't so hacky. std::vector<AutoFillProfile> profiles(web_profiles_.size()); std::transform(web_profiles_.begin(), web_profiles_.end(), profiles.begin(), DereferenceFunctor<AutoFillProfile>()); - // Remove the profile that matches |unique_id|. + // Remove the profile that matches |guid|. profiles.erase( std::remove_if(profiles.begin(), profiles.end(), - FormGroupIDMatchesFunctor<AutoFillProfile>(unique_id)), + FormGroupGUIDMatchesFunctor<AutoFillProfile>(guid)), profiles.end()); SetProfiles(&profiles); } -AutoFillProfile* PersonalDataManager::GetProfileById(int unique_id) { +AutoFillProfile* PersonalDataManager::GetProfileByGUID( + const std::string& guid) { for (std::vector<AutoFillProfile*>::iterator iter = web_profiles_->begin(); iter != web_profiles_->end(); ++iter) { - if ((*iter)->unique_id() == unique_id) + if ((*iter)->guid() == guid) return *iter; } return NULL; @@ -540,34 +582,43 @@ void PersonalDataManager::UpdateCreditCard(const CreditCard& credit_card) { // Update the cached credit card. for (std::vector<CreditCard*>::iterator iter = credit_cards_->begin(); iter != credit_cards_->end(); ++iter) { - if ((*iter)->unique_id() == credit_card.unique_id()) { + if ((*iter)->guid() == credit_card.guid()) { delete *iter; *iter = new CreditCard(credit_card); break; } } - wds->UpdateCreditCard(credit_card); + wds->UpdateCreditCardGUID(credit_card); FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); #endif } -void PersonalDataManager::RemoveCreditCard(int unique_id) { +void PersonalDataManager::RemoveCreditCard(const std::string& guid) { // TODO(jhawkins): Refactor SetCreditCards so this isn't so hacky. std::vector<CreditCard> credit_cards(credit_cards_.size()); std::transform(credit_cards_.begin(), credit_cards_.end(), credit_cards.begin(), DereferenceFunctor<CreditCard>()); - // Remove the credit card that matches |unique_id|. + // Remove the credit card that matches |guid|. credit_cards.erase( std::remove_if(credit_cards.begin(), credit_cards.end(), - FormGroupIDMatchesFunctor<CreditCard>(unique_id)), + FormGroupGUIDMatchesFunctor<CreditCard>(guid)), credit_cards.end()); SetCreditCards(&credit_cards); } +CreditCard* PersonalDataManager::GetCreditCardByGUID(const std::string& guid) { + for (std::vector<CreditCard*>::iterator iter = credit_cards_.begin(); + iter != credit_cards_.end(); ++iter) { + if ((*iter)->guid() == guid) + return *iter; + } + return NULL; +} + void PersonalDataManager::GetPossibleFieldTypes(const string16& text, FieldTypeSet* possible_types) { string16 clean_info = StringToLowerASCII(CollapseWhitespace(text, false)); @@ -647,9 +698,8 @@ AutoFillProfile* PersonalDataManager::CreateNewEmptyAutoFillProfileForDBThread( #else // See comment in header for thread details. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - AutoLock lock(unique_ids_lock_); - AutoFillProfile* p = new AutoFillProfile(label, - CreateNextUniqueIDFor(&unique_profile_ids_)); + AutoFillProfile* p = new AutoFillProfile; + p->set_label(label); return p; #endif } @@ -672,6 +722,7 @@ void PersonalDataManager::Init(Profile* profile) { LoadCreditCards(); } +<<<<<<< HEAD int PersonalDataManager::CreateNextUniqueIDFor(std::set<int>* id_set) { // Profile IDs MUST start at 1 to allow 0 as an error value when reading // the ID from the WebDB (see LoadData()). @@ -688,6 +739,8 @@ int PersonalDataManager::CreateNextUniqueIDFor(std::set<int>* id_set) { #endif } +======= +>>>>>>> chromium.org at r65505 void PersonalDataManager::LoadProfiles() { #ifdef ANDROID // This shoud request the profile(s) from java land on Android. @@ -733,11 +786,14 @@ void PersonalDataManager::LoadCreditCards() { void PersonalDataManager::ReceiveLoadedProfiles(WebDataService::Handle h, const WDTypedResult* result) { DCHECK_EQ(pending_profiles_query_, h); - pending_profiles_query_ = 0; +<<<<<<< HEAD #ifndef ANDROID AutoLock lock(unique_ids_lock_); unique_profile_ids_.clear(); +======= + pending_profiles_query_ = 0; +>>>>>>> chromium.org at r65505 web_profiles_.reset(); const WDResult<std::vector<AutoFillProfile*> >* r = @@ -746,8 +802,6 @@ void PersonalDataManager::ReceiveLoadedProfiles(WebDataService::Handle h, std::vector<AutoFillProfile*> profiles = r->GetValue(); for (std::vector<AutoFillProfile*>::iterator iter = profiles.begin(); iter != profiles.end(); ++iter) { - unique_profile_ids_.insert((*iter)->unique_id()); - unique_ids_.insert((*iter)->unique_id()); web_profiles_.push_back(*iter); } #endif @@ -756,11 +810,14 @@ void PersonalDataManager::ReceiveLoadedProfiles(WebDataService::Handle h, void PersonalDataManager::ReceiveLoadedCreditCards( WebDataService::Handle h, const WDTypedResult* result) { DCHECK_EQ(pending_creditcards_query_, h); - pending_creditcards_query_ = 0; +<<<<<<< HEAD #ifndef ANDROID AutoLock lock(unique_ids_lock_); unique_creditcard_ids_.clear(); +======= + pending_creditcards_query_ = 0; +>>>>>>> chromium.org at r65505 credit_cards_.reset(); const WDResult<std::vector<CreditCard*> >* r = @@ -769,8 +826,6 @@ void PersonalDataManager::ReceiveLoadedCreditCards( std::vector<CreditCard*> credit_cards = r->GetValue(); for (std::vector<CreditCard*>::iterator iter = credit_cards.begin(); iter != credit_cards.end(); ++iter) { - unique_creditcard_ids_.insert((*iter)->unique_id()); - unique_ids_.insert((*iter)->unique_id()); credit_cards_.push_back(*iter); } #endif @@ -840,8 +895,6 @@ void PersonalDataManager::SaveImportedCreditCard() { // Set to true if |imported_credit_card_| is merged into the profile list. bool merged = false; - imported_credit_card_->set_label(ASCIIToUTF16(kUnlabeled)); - std::vector<CreditCard> creditcards; for (std::vector<CreditCard*>::const_iterator iter = credit_cards_.begin(); diff --git a/chrome/browser/autofill/personal_data_manager.h b/chrome/browser/autofill/personal_data_manager.h index daf913b..a89f92b 100644 --- a/chrome/browser/autofill/personal_data_manager.h +++ b/chrome/browser/autofill/personal_data_manager.h @@ -82,8 +82,7 @@ class PersonalDataManager void SaveImportedCreditCard(); // Sets |web_profiles_| to the contents of |profiles| and updates the web - // database by adding, updating and removing profiles. Sets the unique ID of - // newly-added profiles. + // database by adding, updating and removing profiles. // // The relationship between this and Refresh is subtle. // A call to |SetProfiles| could include out-of-date data that may conflict @@ -96,8 +95,7 @@ class PersonalDataManager void SetProfiles(std::vector<AutoFillProfile>* profiles); // Sets |credit_cards_| to the contents of |credit_cards| and updates the web - // database by adding, updating and removing credit cards. Sets the unique - // ID of newly-added profiles. + // database by adding, updating and removing credit cards. void SetCreditCards(std::vector<CreditCard>* credit_cards); // Adds |profile| to the web database. @@ -106,12 +104,12 @@ class PersonalDataManager // Updates |profile| which already exists in the web database. void UpdateProfile(const AutoFillProfile& profile); - // Removes the profile represented by |unique_id|. - void RemoveProfile(int unique_id); + // Removes the profile represented by |guid|. + void RemoveProfile(const std::string& guid); - // Returns the profile with the specified |unique_id|, or NULL if there is no - // profile with the specified |unique_id|. - AutoFillProfile* GetProfileById(int unique_id); + // Returns the profile with the specified |guid|, or NULL if there is no + // profile with the specified |guid|. + AutoFillProfile* GetProfileByGUID(const std::string& guid); // Adds |credit_card| to the web database. void AddCreditCard(const CreditCard& credit_card); @@ -119,8 +117,12 @@ class PersonalDataManager // Updates |credit_card| which already exists in the web database. void UpdateCreditCard(const CreditCard& credit_card); - // Removes the credit card represented by |unique_id|. - void RemoveCreditCard(int unique_id); + // Removes the credit card represented by |guid|. + void RemoveCreditCard(const std::string& guid); + + // Returns the credit card with the specified |guid|, or NULL if there is + // no credit card with the specified |guid|. + CreditCard* GetCreditCardByGUID(const std::string& guid); // Gets the possible field types for the given text, determined by matching // the text with all known personal information and returning matching types. @@ -143,7 +145,7 @@ class PersonalDataManager return credit_cards_.get(); } - // Creates a profile labeled |label|, with it's own locally unique ID. + // Creates a profile labeled |label|. // This must be called on the DB thread with the expectation that the // returned form will be synchronously persisted to the WebDatabase. See // Refresh and SetProfiles for details. @@ -157,15 +159,6 @@ class PersonalDataManager // engine processed a change from the cloud, we will learn of these as a // result of this call. // - // Note that there is a subtle relationship with ID generation. IDs can be - // generated by CreateNewEmptyAutoFillProfileForDBThread (in a synchronized - // way), meaning that it is possible we are aware of this new profile only - // by having it's ID tracked in unique_profile_ids_ for a period of time. - // Because the expectation of that call is that the ID we generate will be - // synchronously persisted to the DB, we are guaranteed to read it via - // the next call to Refresh. It could get deleted before we - // manage, but this is safe (we just hold on to the ID a bit longer). - // // Also see SetProfile for more details. virtual void Refresh(); @@ -189,12 +182,6 @@ class PersonalDataManager // Returns the profile of the tab contents. Profile* profile(); - // This will create and reserve a new unique ID for the id pool |id_set|. - // The |id_set| is typically |unique_profile_ids_| or - // |unique_creditcard_ids_|. The global pool |unique_ids_| is used to ensure - // uniqueness of ids across all pools. The new (next) unique id is returned. - int CreateNextUniqueIDFor(std::set<int>* id_set); - // Loads the saved profiles from the web database. virtual void LoadProfiles(); @@ -233,6 +220,7 @@ class PersonalDataManager // True if personal data has been loaded from the web database. bool is_data_loaded_; +<<<<<<< HEAD // The set of already created unique IDs, shared by both profiles and credit // cards, since IDs must be unique among the two groups. std::set<int> unique_ids_; @@ -254,6 +242,8 @@ class PersonalDataManager Lock unique_ids_lock_; #endif +======= +>>>>>>> chromium.org at r65505 // The loaded web profiles. ScopedVector<AutoFillProfile> web_profiles_; diff --git a/chrome/browser/autofill/personal_data_manager_mac.mm b/chrome/browser/autofill/personal_data_manager_mac.mm index b2bf60d..4a9fb25 100644 --- a/chrome/browser/autofill/personal_data_manager_mac.mm +++ b/chrome/browser/autofill/personal_data_manager_mac.mm @@ -7,11 +7,13 @@ #import <AddressBook/AddressBook.h> #include "app/l10n_util_mac.h" +#include "base/logging.h" #include "base/scoped_ptr.h" #include "base/scoped_vector.h" #include "base/sys_string_conversions.h" #include "chrome/browser/autofill/autofill_profile.h" #include "chrome/browser/autofill/phone_number.h" +#include "chrome/browser/guid.h" #include "grit/generated_resources.h" namespace { @@ -69,12 +71,16 @@ void AuxiliaryProfilesImpl::GetAddressBookMeCard() { for (NSUInteger i = 0, count = [addresses count]; i < count; i++) { NSDictionary* address = [addresses valueAtIndex:i]; NSString* addressLabelRaw = [addresses labelAtIndex:i]; - NSString* addressLabel = ABLocalizedPropertyOrLabel(addressLabelRaw); - // Create a new profile where the label is set to the localized label - // from the "me" address. - scoped_ptr<AutoFillProfile> profile( - new AutoFillProfile(base::SysNSStringToUTF16(addressLabel), 0)); + // Create a new profile where the guid is set to the guid portion of the + // |kABUIDProperty| taken from from the "me" address. The format of + // the |kABUIDProperty| is "<guid>:ABPerson", so we're stripping off the + // raw guid here and using it directly. + const size_t kGUIDLength = 36U; + std::string guid = base::SysNSStringToUTF8( + [me valueForProperty:kABUIDProperty]).substr(0, kGUIDLength); + scoped_ptr<AutoFillProfile> profile(new AutoFillProfile(guid)); + DCHECK(guid::IsValidGUID(profile->guid())); // Fill in name and company information. GetAddressBookNames(me, addressLabelRaw, profile.get()); @@ -250,28 +256,6 @@ void AuxiliaryProfilesImpl::GetAddressBookPhoneNumbers( // Populate |auxiliary_profiles_| with the Address Book data. void PersonalDataManager::LoadAuxiliaryProfiles() { - AutoLock lock(unique_ids_lock_); - - // Before loading new auxiliary profiles remove the unique ids from the - // id pools. The |GetAddressBookMeCard()| call below clears the - // |auxiliary_profiles_|. - unique_auxiliary_profile_ids_.clear(); - for (ScopedVector<AutoFillProfile>::iterator iter - = auxiliary_profiles_.begin(); - iter != auxiliary_profiles_.end(); ++iter) { - if ((*iter)->unique_id() != 0) { - unique_ids_.erase((*iter)->unique_id()); - } - } - AuxiliaryProfilesImpl impl(&auxiliary_profiles_); impl.GetAddressBookMeCard(); - - // For newly fetched auxiliary profiles, ensure that we have unique ids set. - for (ScopedVector<AutoFillProfile>::iterator iter - = auxiliary_profiles_.begin(); - iter != auxiliary_profiles_.end(); ++iter) { - (*iter)->set_unique_id( - CreateNextUniqueIDFor(&unique_auxiliary_profile_ids_)); - } } diff --git a/chrome/browser/autofill/personal_data_manager_unittest.cc b/chrome/browser/autofill/personal_data_manager_unittest.cc index 267933f..2947e08 100644 --- a/chrome/browser/autofill/personal_data_manager_unittest.cc +++ b/chrome/browser/autofill/personal_data_manager_unittest.cc @@ -12,6 +12,7 @@ #include "chrome/browser/autofill/form_structure.h" #include "chrome/browser/autofill/personal_data_manager.h" #include "chrome/browser/browser_thread.h" +#include "chrome/browser/guid.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/password_manager/encryptor.h" #include "chrome/common/notification_details.h" @@ -72,13 +73,6 @@ class PersonalDataManagerTest : public testing::Test { personal_data_->SetObserver(&personal_data_observer_); } - AutoFillProfile* MakeProfile() { - AutoLock lock(personal_data_->unique_ids_lock_); - return new AutoFillProfile(string16(), - personal_data_->CreateNextUniqueIDFor( - &personal_data_->unique_profile_ids_)); - } - MessageLoopForUI message_loop_; BrowserThread ui_thread_; BrowserThread db_thread_; @@ -91,19 +85,19 @@ class PersonalDataManagerTest : public testing::Test { // TODO(jhawkins): Test SetProfiles w/out a WebDataService in the profile. TEST_F(PersonalDataManagerTest, SetProfiles) { - AutoFillProfile profile0(string16(), 0); + AutoFillProfile profile0; autofill_test::SetProfileInfo(&profile0, "Billing", "Marion", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - AutoFillProfile profile1(string16(), 0); + AutoFillProfile profile1; autofill_test::SetProfileInfo(&profile1, "Home", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "903 Apple Ct.", NULL, "Orlando", "FL", "32801", "US", "19482937549", "13502849239"); - AutoFillProfile profile2(string16(), 0); + AutoFillProfile profile2; autofill_test::SetProfileInfo(&profile2, "Work", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", @@ -123,18 +117,10 @@ TEST_F(PersonalDataManagerTest, SetProfiles) { update.push_back(profile1); personal_data_->SetProfiles(&update); - // The PersonalDataManager will update the unique IDs when saving the - // profiles, so we have to update the expectations. - // Same for labels. - profile0.set_unique_id(update[0].unique_id()); - profile1.set_unique_id(update[1].unique_id()); - profile0.set_label(update[0].Label()); - profile1.set_label(update[1].Label()); - const std::vector<AutoFillProfile*>& results1 = personal_data_->profiles(); ASSERT_EQ(2U, results1.size()); - EXPECT_EQ(profile0, *results1.at(0)); - EXPECT_EQ(profile1, *results1.at(1)); + EXPECT_EQ(0, profile0.Compare(*results1.at(0))); + EXPECT_EQ(0, profile1.Compare(*results1.at(1))); // Three operations in one: // - Update profile0 @@ -146,20 +132,10 @@ TEST_F(PersonalDataManagerTest, SetProfiles) { update.push_back(profile2); personal_data_->SetProfiles(&update); - // Set the expected unique ID for profile2. - // Same for labels. - profile0.set_label(update[0].Label()); - profile2.set_unique_id(update[1].unique_id()); - profile2.set_label(update[1].Label()); - - // AutoFillProfile IDs are re-used, so the third profile to be added will have - // a unique ID that matches the old unique ID of the removed profile1, even - // though that ID has already been used. const std::vector<AutoFillProfile*>& results2 = personal_data_->profiles(); ASSERT_EQ(2U, results2.size()); - EXPECT_EQ(profile0, *results2.at(0)); - EXPECT_EQ(profile2, *results2.at(1)); - EXPECT_EQ(profile1.unique_id(), profile2.unique_id()); + EXPECT_EQ(0, profile0.Compare(*results2.at(0))); + EXPECT_EQ(0, profile2.Compare(*results2.at(1))); // Reset the PersonalDataManager. This tests that the personal data was saved // to the web database, and that we can load the profiles from the web @@ -177,25 +153,23 @@ TEST_F(PersonalDataManagerTest, SetProfiles) { // Verify that we've loaded the profiles from the web database. const std::vector<AutoFillProfile*>& results3 = personal_data_->profiles(); ASSERT_EQ(2U, results3.size()); - profile0.set_label(results3.at(0)->Label()); - EXPECT_EQ(profile0, *results3.at(0)); - profile2.set_label(results3.at(1)->Label()); - EXPECT_EQ(profile2, *results3.at(1)); + EXPECT_EQ(0, profile0.Compare(*results3.at(0))); + EXPECT_EQ(0, profile2.Compare(*results3.at(1))); } // TODO(jhawkins): Test SetCreditCards w/out a WebDataService in the profile. TEST_F(PersonalDataManagerTest, SetCreditCards) { - CreditCard creditcard0(string16(), 0); + CreditCard creditcard0; autofill_test::SetCreditCardInfo(&creditcard0, "Corporate", - "John Dillinger", "Visa", "123456789012", "01", "2010", 1); + "John Dillinger", "423456789012" /* Visa */, "01", "2010"); - CreditCard creditcard1(string16(), 0); + CreditCard creditcard1; autofill_test::SetCreditCardInfo(&creditcard1, "Personal", - "Bonnie Parker", "Mastercard", "098765432109", "12", "2012", 2); + "Bonnie Parker", "518765432109" /* Mastercard */, "12", "2012"); - CreditCard creditcard2(string16(), 0); + CreditCard creditcard2; autofill_test::SetCreditCardInfo(&creditcard2, "Savings", - "Clyde Barrow", "American Express", "777666888555", "04", "2015", 3); + "Clyde Barrow", "347666888555" /* American Express */, "04", "2015"); // This will verify that the web database has been loaded and the notification // sent out. @@ -211,18 +185,10 @@ TEST_F(PersonalDataManagerTest, SetCreditCards) { update.push_back(creditcard1); personal_data_->SetCreditCards(&update); - // The PersonalDataManager will update the unique IDs when saving the - // credit cards, so we have to update the expectations. - // Same for labels. - creditcard0.set_unique_id(update[0].unique_id()); - creditcard1.set_unique_id(update[1].unique_id()); - creditcard0.set_label(update[0].Label()); - creditcard1.set_label(update[1].Label()); - const std::vector<CreditCard*>& results1 = personal_data_->credit_cards(); ASSERT_EQ(2U, results1.size()); - EXPECT_EQ(creditcard0, *results1.at(0)); - EXPECT_EQ(creditcard1, *results1.at(1)); + EXPECT_EQ(0, creditcard0.Compare(*results1.at(0))); + EXPECT_EQ(0, creditcard1.Compare(*results1.at(1))); // Three operations in one: // - Update creditcard0 @@ -234,19 +200,10 @@ TEST_F(PersonalDataManagerTest, SetCreditCards) { update.push_back(creditcard2); personal_data_->SetCreditCards(&update); - // Set the expected unique ID for creditcard2. - // Same for labels. - creditcard2.set_unique_id(update[1].unique_id()); - creditcard2.set_label(update[1].Label()); - - // CreditCard IDs are re-used, so the third credit card to be added will have - // a unique ID that matches the old unique ID of the removed creditcard1, even - // though that ID has already been used. const std::vector<CreditCard*>& results2 = personal_data_->credit_cards(); ASSERT_EQ(2U, results2.size()); EXPECT_EQ(creditcard0, *results2.at(0)); EXPECT_EQ(creditcard2, *results2.at(1)); - EXPECT_EQ(creditcard2.unique_id(), creditcard1.unique_id()); // Reset the PersonalDataManager. This tests that the personal data was saved // to the web database, and that we can load the credit cards from the web @@ -269,25 +226,25 @@ TEST_F(PersonalDataManagerTest, SetCreditCards) { } TEST_F(PersonalDataManagerTest, SetProfilesAndCreditCards) { - AutoFillProfile profile0(string16(), 0); + AutoFillProfile profile0; autofill_test::SetProfileInfo(&profile0, "Billing", "Marion", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - AutoFillProfile profile1(string16(), 0); + AutoFillProfile profile1; autofill_test::SetProfileInfo(&profile1, "Home", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "903 Apple Ct.", NULL, "Orlando", "FL", "32801", "US", "19482937549", "13502849239"); - CreditCard creditcard0(string16(), 0); + CreditCard creditcard0; autofill_test::SetCreditCardInfo(&creditcard0, "Corporate", - "John Dillinger", "Visa", "123456789012", "01", "2010", 1); + "John Dillinger", "423456789012" /* Visa */, "01", "2010"); - CreditCard creditcard1(string16(), 0); + CreditCard creditcard1; autofill_test::SetCreditCardInfo(&creditcard1, "Personal", - "Bonnie Parker", "Mastercard", "098765432109", "12", "2012", 2); + "Bonnie Parker", "518765432109" /* Mastercard */, "12", "2012"); // This will verify that the web database has been loaded and the notification // sent out. @@ -304,18 +261,10 @@ TEST_F(PersonalDataManagerTest, SetProfilesAndCreditCards) { update.push_back(profile1); personal_data_->SetProfiles(&update); - // The PersonalDataManager will update the unique IDs when saving the - // profiles, so we have to update the expectations. - // Same for labels. - profile0.set_unique_id(update[0].unique_id()); - profile1.set_unique_id(update[1].unique_id()); - profile0.set_label(update[0].Label()); - profile1.set_label(update[1].Label()); - const std::vector<AutoFillProfile*>& results1 = personal_data_->profiles(); ASSERT_EQ(2U, results1.size()); - EXPECT_EQ(profile0, *results1.at(0)); - EXPECT_EQ(profile1, *results1.at(1)); + EXPECT_EQ(0, profile0.Compare(*results1.at(0))); + EXPECT_EQ(0, profile1.Compare(*results1.at(1))); // Add two test credit cards to the database. std::vector<CreditCard> update_cc; @@ -323,33 +272,26 @@ TEST_F(PersonalDataManagerTest, SetProfilesAndCreditCards) { update_cc.push_back(creditcard1); personal_data_->SetCreditCards(&update_cc); - // The PersonalDataManager will update the unique IDs when saving the - // credit cards, so we have to update the expectations. - // Same for labels. - creditcard0.set_unique_id(update_cc[0].unique_id()); - creditcard1.set_unique_id(update_cc[1].unique_id()); - creditcard0.set_label(update_cc[0].Label()); - creditcard1.set_label(update_cc[1].Label()); const std::vector<CreditCard*>& results2 = personal_data_->credit_cards(); ASSERT_EQ(2U, results2.size()); EXPECT_EQ(creditcard0, *results2.at(0)); EXPECT_EQ(creditcard1, *results2.at(1)); - // Determine uniqueness by inserting all of the IDs into a set and verifying - // the size of the set matches the number of IDs. - std::set<int> ids; - ids.insert(profile0.unique_id()); - ids.insert(profile1.unique_id()); - ids.insert(creditcard0.unique_id()); - ids.insert(creditcard1.unique_id()); - EXPECT_EQ(4U, ids.size()); + // Determine uniqueness by inserting all of the GUIDs into a set and verifying + // the size of the set matches the number of GUIDs. + std::set<std::string> guids; + guids.insert(profile0.guid()); + guids.insert(profile1.guid()); + guids.insert(creditcard0.guid()); + guids.insert(creditcard1.guid()); + EXPECT_EQ(4U, guids.size()); } // Test care for 50047. Makes sure that unique_ids_ is populated correctly on // load. TEST_F(PersonalDataManagerTest, PopulateUniqueIDsOnLoad) { - AutoFillProfile profile0(string16(), 0); + AutoFillProfile profile0; autofill_test::SetProfileInfo(&profile0, "", "y", "", "", "", "", "", "", "", "", "", "", "", ""); @@ -378,7 +320,7 @@ TEST_F(PersonalDataManagerTest, PopulateUniqueIDsOnLoad) { ASSERT_EQ(1U, results2.size()); // Add a new profile. - AutoFillProfile profile1(string16(), 0); + AutoFillProfile profile1; autofill_test::SetProfileInfo(&profile1, "", "y", "", "", "", "", "", "", "", "", "", "", "", ""); update.clear(); @@ -390,13 +332,13 @@ TEST_F(PersonalDataManagerTest, PopulateUniqueIDsOnLoad) { // which is an invalid id). const std::vector<AutoFillProfile*>& results3 = personal_data_->profiles(); ASSERT_EQ(2U, results3.size()); - EXPECT_NE(results3[0]->unique_id(), results3[1]->unique_id()); - EXPECT_NE(0, results3[0]->unique_id()); - EXPECT_NE(0, results3[1]->unique_id()); + EXPECT_NE(results3[0]->guid(), results3[1]->guid()); + EXPECT_TRUE(guid::IsValidGUID(results3[0]->guid())); + EXPECT_TRUE(guid::IsValidGUID(results3[1]->guid())); } TEST_F(PersonalDataManagerTest, SetEmptyProfile) { - AutoFillProfile profile0(string16(), 0); + AutoFillProfile profile0; autofill_test::SetProfileInfo(&profile0, "", "", "", "", "", "", "", "", "", "", "", "", "", ""); @@ -436,9 +378,8 @@ TEST_F(PersonalDataManagerTest, SetEmptyProfile) { } TEST_F(PersonalDataManagerTest, SetEmptyCreditCard) { - CreditCard creditcard0(string16(), 0); - autofill_test::SetCreditCardInfo(&creditcard0, - "", "", "", "", "", "", 0); + CreditCard creditcard0; + autofill_test::SetCreditCardInfo(&creditcard0, "", "", "", "", ""); // This will verify that the web database has been loaded and the notification // sent out. @@ -476,13 +417,13 @@ TEST_F(PersonalDataManagerTest, SetEmptyCreditCard) { } TEST_F(PersonalDataManagerTest, Refresh) { - AutoFillProfile profile0(string16(), 0); + AutoFillProfile profile0; autofill_test::SetProfileInfo(&profile0, "Billing", "Marion", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - AutoFillProfile profile1(string16(), 0); + AutoFillProfile profile1; autofill_test::SetProfileInfo(&profile1, "Home", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "903 Apple Ct.", NULL, "Orlando", "FL", "32801", @@ -499,10 +440,11 @@ TEST_F(PersonalDataManagerTest, Refresh) { update.push_back(profile1); personal_data_->SetProfiles(&update); - profile0.set_unique_id(update[0].unique_id()); - profile1.set_unique_id(update[1].unique_id()); - profile0.set_label(update[0].Label()); - profile1.set_label(update[1].Label()); + // Labels depend on other profiles in the list - update labels manually. + std::vector<AutoFillProfile *> profile_pointers; + profile_pointers.push_back(&profile0); + profile_pointers.push_back(&profile1); + AutoFillProfile::AdjustInferredLabels(&profile_pointers); // Wait for the refresh. EXPECT_CALL(personal_data_observer_, @@ -515,15 +457,19 @@ TEST_F(PersonalDataManagerTest, Refresh) { EXPECT_EQ(profile0, *results1.at(0)); EXPECT_EQ(profile1, *results1.at(1)); - scoped_ptr<AutoFillProfile> profile2(MakeProfile()); - autofill_test::SetProfileInfo(profile2.get(), + AutoFillProfile profile2; + autofill_test::SetProfileInfo(&profile2, "Work", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", "32801", "US", "19482937549", "13502849239"); + // Adjust all labels. + profile_pointers.push_back(&profile2); + AutoFillProfile::AdjustInferredLabels(&profile_pointers); + WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); ASSERT_TRUE(wds); - wds->AddAutoFillProfile(*profile2.get()); + wds->AddAutoFillProfileGUID(profile2); personal_data_->Refresh(); @@ -537,21 +483,21 @@ TEST_F(PersonalDataManagerTest, Refresh) { ASSERT_EQ(3U, results2.size()); EXPECT_EQ(profile0, *results2.at(0)); EXPECT_EQ(profile1, *results2.at(1)); - EXPECT_EQ(*profile2.get(), *results2.at(2)); + EXPECT_EQ(profile2, *results2.at(2)); - wds->RemoveAutoFillProfile(profile1.unique_id()); - wds->RemoveAutoFillProfile(profile2->unique_id()); + wds->RemoveAutoFillProfileGUID(profile1.guid()); + wds->RemoveAutoFillProfileGUID(profile2.guid()); // Before telling the PDM to refresh, simulate an edit to one of the profiles // via a SetProfile update (this would happen if the AutoFill window was // open with a previous snapshot of the profiles, and something [e.g. sync] // removed a profile from the browser. In this edge case, we will end up // in a consistent state by dropping the write). - profile2->SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Jo")); + profile2.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Jo")); update.clear(); update.push_back(profile0); update.push_back(profile1); - update.push_back(*profile2.get()); + update.push_back(profile2); personal_data_->SetProfiles(&update); // And wait for the refresh. @@ -588,28 +534,33 @@ TEST_F(PersonalDataManagerTest, ImportFormData) { MessageLoop::current()->Run(); - AutoFillProfile expected(string16(), 1); + AutoFillProfile expected; autofill_test::SetProfileInfo(&expected, NULL, "George", NULL, "Washington", "theprez@gmail.com", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); const std::vector<AutoFillProfile*>& results = personal_data_->profiles(); ASSERT_EQ(1U, results.size()); - expected.set_label(results[0]->Label()); - EXPECT_EQ(expected, *results[0]); + EXPECT_EQ(0, expected.Compare(*results[0])); } TEST_F(PersonalDataManagerTest, SetUniqueCreditCardLabels) { - CreditCard credit_card0(ASCIIToUTF16("Home"), 0); + CreditCard credit_card0; + credit_card0.set_label(ASCIIToUTF16("Home")); credit_card0.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("John")); - CreditCard credit_card1(ASCIIToUTF16("Home"), 0); + CreditCard credit_card1; + credit_card1.set_label(ASCIIToUTF16("Home")); credit_card1.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Paul")); - CreditCard credit_card2(ASCIIToUTF16("Home"), 0); + CreditCard credit_card2; + credit_card2.set_label(ASCIIToUTF16("Home")); credit_card2.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Ringo")); - CreditCard credit_card3(ASCIIToUTF16("NotHome"), 0); + CreditCard credit_card3; + credit_card3.set_label(ASCIIToUTF16("NotHome")); credit_card3.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Other")); - CreditCard credit_card4(ASCIIToUTF16("Work"), 0); + CreditCard credit_card4; + credit_card4.set_label(ASCIIToUTF16("Work")); credit_card4.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Ozzy")); - CreditCard credit_card5(ASCIIToUTF16("Work"), 0); + CreditCard credit_card5; + credit_card5.set_label(ASCIIToUTF16("Work")); credit_card5.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Dio")); // This will verify that the web database has been loaded and the notification @@ -680,14 +631,13 @@ TEST_F(PersonalDataManagerTest, AggregateProfileData) { MessageLoop::current()->Run(); scoped_ptr<AutoFillProfile> expected( - new AutoFillProfile(string16(), 1)); + new AutoFillProfile); autofill_test::SetProfileInfo(expected.get(), NULL, "George", NULL, "Washington", "theprez@gmail.com", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); const std::vector<AutoFillProfile*>& results = personal_data_->profiles(); ASSERT_EQ(1U, results.size()); - expected->set_label(results[0]->Label()); - EXPECT_EQ(*expected, *results[0]); + EXPECT_EQ(0, expected->Compare(*results[0])); // Now create a completely different profile. form.reset(new FormData); @@ -715,19 +665,17 @@ TEST_F(PersonalDataManagerTest, AggregateProfileData) { const std::vector<AutoFillProfile*>& results2 = personal_data_->profiles(); ASSERT_EQ(2U, results2.size()); - expected.reset(new AutoFillProfile(string16(), 1)); + expected.reset(new AutoFillProfile); autofill_test::SetProfileInfo(expected.get(), NULL, "George", NULL, "Washington", "theprez@gmail.com", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - expected->set_label(results2[0]->Label()); - EXPECT_EQ(*expected, *results2[0]); + EXPECT_EQ(0, expected->Compare(*results2[0])); - expected.reset(new AutoFillProfile(string16(), 2)); + expected.reset(new AutoFillProfile); autofill_test::SetProfileInfo(expected.get(), NULL, "John", NULL, "Adams", "second@gmail.com", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - expected->set_label(results2[1]->Label()); - EXPECT_EQ(*expected, *results2[1]); + EXPECT_EQ(0, expected->Compare(*results2[1])); // Submit a form with new data for the first profile. form.reset(new FormData); @@ -764,17 +712,15 @@ TEST_F(PersonalDataManagerTest, AggregateProfileData) { const std::vector<AutoFillProfile*>& results3 = personal_data_->profiles(); ASSERT_EQ(2U, results3.size()); - expected.reset(new AutoFillProfile(string16(), 1)); + expected.reset(new AutoFillProfile); autofill_test::SetProfileInfo(expected.get(), NULL, "George", NULL, "Washington", "theprez@gmail.com", NULL, "190 High Street", NULL, "Philadelphia", "Pennsylvania", "19106", NULL, NULL, NULL); - expected->set_label(results3[0]->Label()); - EXPECT_EQ(*expected, *results3[0]); + EXPECT_EQ(0, expected->Compare(*results3[0])); - expected.reset(new AutoFillProfile(string16(), 2)); + expected.reset(new AutoFillProfile); autofill_test::SetProfileInfo(expected.get(), NULL, "John", NULL, "Adams", "second@gmail.com", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - expected->set_label(results3[1]->Label()); - EXPECT_EQ(*expected, *results3[1]); + EXPECT_EQ(0, expected->Compare(*results3[1])); } diff --git a/chrome/browser/autofill/select_control_handler.cc b/chrome/browser/autofill/select_control_handler.cc index cbf5292..f7f2282 100644 --- a/chrome/browser/autofill/select_control_handler.cc +++ b/chrome/browser/autofill/select_control_handler.cc @@ -485,9 +485,9 @@ void FillSelectControl(const FormGroup* form_group, string16 value; string16 field_text = form_group->GetFieldText(type); for (size_t i = 0; i < field->option_strings().size(); ++i) { - if (form_group->GetFieldText(type) == field->option_strings()[i]) { + if (field_text == field->option_strings()[i]) { // An exact match, use it. - value = form_group->GetFieldText(type); + value = field_text; break; } diff --git a/chrome/browser/automation/automation_extension_tracker.cc b/chrome/browser/automation/automation_extension_tracker.cc index cd816d3..8ab734d 100644 --- a/chrome/browser/automation/automation_extension_tracker.cc +++ b/chrome/browser/automation/automation_extension_tracker.cc @@ -10,7 +10,7 @@ AutomationExtensionTracker::AutomationExtensionTracker( IPC::Message::Sender* automation) - : AutomationResourceTracker<Extension*>(automation) { + : AutomationResourceTracker<const Extension*>(automation) { registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, NotificationService::AllSources()); registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED, @@ -20,9 +20,9 @@ AutomationExtensionTracker::AutomationExtensionTracker( AutomationExtensionTracker::~AutomationExtensionTracker() { } -void AutomationExtensionTracker::AddObserver(Extension* resource) {} +void AutomationExtensionTracker::AddObserver(const Extension* resource) {} -void AutomationExtensionTracker::RemoveObserver(Extension* resource) {} +void AutomationExtensionTracker::RemoveObserver(const Extension* resource) {} void AutomationExtensionTracker::Observe(NotificationType type, const NotificationSource& source, @@ -31,7 +31,7 @@ void AutomationExtensionTracker::Observe(NotificationType type, type != NotificationType::EXTENSION_UNLOADED_DISABLED) return; - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); Profile* profile = Source<Profile>(source).ptr(); if (profile) { ExtensionsService* service = profile->GetExtensionsService(); diff --git a/chrome/browser/automation/automation_extension_tracker.h b/chrome/browser/automation/automation_extension_tracker.h index c1b7ed7..05f16b5 100644 --- a/chrome/browser/automation/automation_extension_tracker.h +++ b/chrome/browser/automation/automation_extension_tracker.h @@ -12,7 +12,7 @@ class Extension; // Tracks an Extension. An Extension is removed on uninstall, not on disable. class AutomationExtensionTracker - : public AutomationResourceTracker<Extension*> { + : public AutomationResourceTracker<const Extension*> { public: explicit AutomationExtensionTracker(IPC::Message::Sender* automation); @@ -23,10 +23,10 @@ class AutomationExtensionTracker // extension, is the one who sends the notification about extension // uninstalls. Instead of using this method, one observer is added for all // extensions in the constructor. - virtual void AddObserver(Extension* resource); + virtual void AddObserver(const Extension* resource); // See related comment above as to why this method is empty. - virtual void RemoveObserver(Extension* resource); + virtual void RemoveObserver(const Extension* resource); // Overriding AutomationResourceTracker Observe. AutomationResourceTracker's // Observe expects the NotificationSource to be the object that is closing. diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index c37c1f7..cbbc786 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -8,6 +8,7 @@ #include "app/message_box_flags.h" #include "base/callback.h" +#include "base/debug/trace_event.h" #include "base/file_path.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" @@ -19,12 +20,11 @@ #include "base/string_util.h" #include "base/task.h" #include "base/thread.h" -#include "base/trace_event.h" #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "base/waitable_event.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/app_modal_dialog.h" #include "chrome/browser/app_modal_dialog_queue.h" #include "chrome/browser/autofill/autofill_manager.h" @@ -161,14 +161,18 @@ void AutomationProvider::ConnectToChannel(const std::string& channel_id) { automation_resource_message_filter_, g_browser_process->io_thread()->message_loop(), true, g_browser_process->shutdown_event())); - chrome::VersionInfo version_info; // Send a hello message with our current automation protocol version. - channel_->Send(new AutomationMsg_Hello(0, version_info.Version().c_str())); + channel_->Send(new AutomationMsg_Hello(0, GetProtocolVersion().c_str())); TRACE_EVENT_END("AutomationProvider::ConnectToChannel", 0, ""); } +std::string AutomationProvider::GetProtocolVersion() { + chrome::VersionInfo version_info; + return version_info.Version().c_str(); +} + void AutomationProvider::SetExpectedTabCount(size_t expected_tabs) { if (expected_tabs == 0) { Send(new AutomationMsg_InitialLoadsComplete(0)); @@ -254,7 +258,7 @@ int AutomationProvider::GetIndexForNavigationController( return parent->GetIndexOfController(controller); } -int AutomationProvider::AddExtension(Extension* extension) { +int AutomationProvider::AddExtension(const Extension* extension) { DCHECK(extension); return extension_tracker_->Add(extension); } @@ -278,8 +282,10 @@ DictionaryValue* AutomationProvider::GetDictionaryFromDownloadItem( dl_item_value->SetInteger("id", static_cast<int>(download->id())); dl_item_value->SetString("url", download->url().spec()); dl_item_value->SetString("referrer_url", download->referrer_url().spec()); - dl_item_value->SetString("file_name", download->GetFileName().value()); - dl_item_value->SetString("full_path", download->full_path().value()); + dl_item_value->SetString("file_name", + download->GetFileNameToReportUser().value()); + dl_item_value->SetString("full_path", + download->GetTargetFilePath().value()); dl_item_value->SetBoolean("is_paused", download->is_paused()); dl_item_value->SetBoolean("open_when_complete", download->open_when_complete()); @@ -295,12 +301,13 @@ DictionaryValue* AutomationProvider::GetDictionaryFromDownloadItem( return dl_item_value; } -Extension* AutomationProvider::GetExtension(int extension_handle) { +const Extension* AutomationProvider::GetExtension(int extension_handle) { return extension_tracker_->GetResource(extension_handle); } -Extension* AutomationProvider::GetEnabledExtension(int extension_handle) { - Extension* extension = extension_tracker_->GetResource(extension_handle); +const Extension* AutomationProvider::GetEnabledExtension(int extension_handle) { + const Extension* extension = + extension_tracker_->GetResource(extension_handle); ExtensionsService* service = profile_->GetExtensionsService(); if (extension && service && service->GetExtensionById(extension->id(), false)) @@ -308,8 +315,10 @@ Extension* AutomationProvider::GetEnabledExtension(int extension_handle) { return NULL; } -Extension* AutomationProvider::GetDisabledExtension(int extension_handle) { - Extension* extension = extension_tracker_->GetResource(extension_handle); +const Extension* AutomationProvider::GetDisabledExtension( + int extension_handle) { + const Extension* extension = + extension_tracker_->GetResource(extension_handle); ExtensionsService* service = profile_->GetExtensionsService(); if (extension && service && service->GetExtensionById(extension->id(), true) && @@ -434,7 +443,7 @@ void AutomationProvider::HandleUnused(const IPC::Message& message, int handle) { } void AutomationProvider::OnChannelError() { - LOG(INFO) << "AutomationProxy went away, shutting down app."; + VLOG(1) << "AutomationProxy went away, shutting down app."; AutomationProviderList::GetInstance()->RemoveProvider(this); } @@ -795,7 +804,7 @@ void AutomationProvider::GetEnabledExtensions( const ExtensionList* extensions = service->extensions(); DCHECK(extensions); for (size_t i = 0; i < extensions->size(); ++i) { - Extension* extension = (*extensions)[i]; + const Extension* extension = (*extensions)[i]; DCHECK(extension); if (extension->location() == Extension::INTERNAL || extension->location() == Extension::LOAD) { @@ -844,7 +853,7 @@ void AutomationProvider::InstallExtensionAndGetHandle( void AutomationProvider::UninstallExtension(int extension_handle, bool* success) { *success = false; - Extension* extension = GetExtension(extension_handle); + const Extension* extension = GetExtension(extension_handle); ExtensionsService* service = profile_->GetExtensionsService(); if (extension && service) { ExtensionUnloadNotificationObserver observer; @@ -857,7 +866,7 @@ void AutomationProvider::UninstallExtension(int extension_handle, void AutomationProvider::EnableExtension(int extension_handle, IPC::Message* reply_message) { - Extension* extension = GetDisabledExtension(extension_handle); + const Extension* extension = GetDisabledExtension(extension_handle); ExtensionsService* service = profile_->GetExtensionsService(); ExtensionProcessManager* manager = profile_->GetExtensionProcessManager(); // Only enable if this extension is disabled. @@ -878,7 +887,7 @@ void AutomationProvider::EnableExtension(int extension_handle, void AutomationProvider::DisableExtension(int extension_handle, bool* success) { *success = false; - Extension* extension = GetEnabledExtension(extension_handle); + const Extension* extension = GetEnabledExtension(extension_handle); ExtensionsService* service = profile_->GetExtensionsService(); if (extension && service) { ExtensionUnloadNotificationObserver observer; @@ -893,7 +902,7 @@ void AutomationProvider::ExecuteExtensionActionInActiveTabAsync( int extension_handle, int browser_handle, IPC::Message* reply_message) { bool success = false; - Extension* extension = GetEnabledExtension(extension_handle); + const Extension* extension = GetEnabledExtension(extension_handle); ExtensionsService* service = profile_->GetExtensionsService(); ExtensionMessageService* message_service = profile_->GetExtensionMessageService(); @@ -918,7 +927,7 @@ void AutomationProvider::ExecuteExtensionActionInActiveTabAsync( void AutomationProvider::MoveExtensionBrowserAction( int extension_handle, int index, bool* success) { *success = false; - Extension* extension = GetEnabledExtension(extension_handle); + const Extension* extension = GetEnabledExtension(extension_handle); ExtensionsService* service = profile_->GetExtensionsService(); if (extension && service) { ExtensionToolbarModel* toolbar = service->toolbar_model(); @@ -939,7 +948,7 @@ void AutomationProvider::GetExtensionProperty( bool* success, std::string* value) { *success = false; - Extension* extension = GetExtension(extension_handle); + const Extension* extension = GetExtension(extension_handle); ExtensionsService* service = profile_->GetExtensionsService(); if (extension && service) { ExtensionToolbarModel* toolbar = service->toolbar_model(); diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h index 3063d2b..280d916 100644 --- a/chrome/browser/automation/automation_provider.h +++ b/chrome/browser/automation/automation_provider.h @@ -150,7 +150,7 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, // Adds the extension passed in to the extension tracker, and returns // the associated handle. If the tracker already contains the extension, // the handle is simply returned. - int AddExtension(Extension* extension); + int AddExtension(const Extension* extension); #if defined(OS_WIN) // Adds the external tab passed in to the tab tracker. @@ -175,6 +175,9 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, // is not of the TabContents type. TabContents* GetTabContentsForHandle(int handle, NavigationController** tab); + // Returns the protocol version which typically is the module version. + virtual std::string GetProtocolVersion(); + scoped_ptr<AutomationAutocompleteEditTracker> autocomplete_edit_tracker_; scoped_ptr<AutomationBrowserTracker> browser_tracker_; scoped_ptr<InitialLoadObserver> initial_load_observer_; @@ -320,15 +323,15 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, // Returns the extension for the given handle. Returns NULL if there is // no extension for the handle. - Extension* GetExtension(int extension_handle); + const Extension* GetExtension(int extension_handle); // Returns the extension for the given handle, if the handle is valid and // the associated extension is enabled. Returns NULL otherwise. - Extension* GetEnabledExtension(int extension_handle); + const Extension* GetEnabledExtension(int extension_handle); // Returns the extension for the given handle, if the handle is valid and // the associated extension is disabled. Returns NULL otherwise. - Extension* GetDisabledExtension(int extension_handle); + const Extension* GetDisabledExtension(int extension_handle); // Method called by the popup menu tracker when a popup menu is opened. void NotifyPopupMenuOpened(); @@ -354,14 +357,16 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, void CreateExternalTab(const IPC::ExternalTabSettings& settings, gfx::NativeWindow* tab_container_window, gfx::NativeWindow* tab_window, - int* tab_handle); + int* tab_handle, + int* session_id); void ConnectExternalTab(uint64 cookie, bool allow, gfx::NativeWindow parent_window, gfx::NativeWindow* tab_container_window, gfx::NativeWindow* tab_window, - int* tab_handle); + int* tab_handle, + int* session_id); void NavigateInExternalTab( int handle, const GURL& url, const GURL& referrer, diff --git a/chrome/browser/automation/automation_provider_json.cc b/chrome/browser/automation/automation_provider_json.cc index 2ffbeaa..c58b622 100644 --- a/chrome/browser/automation/automation_provider_json.cc +++ b/chrome/browser/automation/automation_provider_json.cc @@ -6,6 +6,7 @@ #include "base/json/json_writer.h" #include "base/json/string_escape.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/automation/automation_provider.h" #include "chrome/test/automation/automation_messages.h" @@ -53,4 +54,3 @@ void AutomationJSONReply::SendError(const std::string& error_message) { provider_->Send(message_); message_ = NULL; } - diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc index b98d78c..d5fb64f 100644 --- a/chrome/browser/automation/automation_provider_observers.cc +++ b/chrome/browser/automation/automation_provider_observers.cc @@ -4,12 +4,15 @@ #include "chrome/browser/automation/automation_provider_observers.h" +#include <deque> + #include "base/basictypes.h" +#include "base/callback.h" #include "base/json/json_writer.h" #include "base/scoped_ptr.h" #include "base/string_util.h" #include "base/values.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/automation/automation_provider.h" #include "chrome/browser/automation/automation_provider_json.h" #include "chrome/browser/bookmarks/bookmark_model.h" @@ -23,6 +26,8 @@ #include "chrome/browser/extensions/extension_updater.h" #include "chrome/browser/login_prompt.h" #include "chrome/browser/metrics/metric_event_duration_details.h" +#include "chrome/browser/notifications/balloon.h" +#include "chrome/browser/notifications/balloon_collection.h" #include "chrome/browser/printing/print_job.h" #include "chrome/browser/profile.h" #include "chrome/browser/search_engines/template_url_model.h" @@ -489,7 +494,7 @@ void ExtensionReadyNotificationObserver::Observe( success = true; break; case NotificationType::EXTENSION_LOADED: - extension_ = Details<Extension>(details).ptr(); + extension_ = Details<const Extension>(details).ptr(); if (!DidExtensionHostsStopLoading(manager_)) return; success = true; @@ -854,7 +859,7 @@ void FindInPageNotificationObserver::Observe( const NotificationDetails& details) { Details<FindNotificationDetails> find_details(details); if (!(find_details->final_update() && reply_message_ != NULL)) { - DLOG(INFO) << "Ignoring, since we only care about the final message"; + DVLOG(1) << "Ignoring, since we only care about the final message"; return; } // We get multiple responses and one of those will contain the ordinal. @@ -1346,3 +1351,22 @@ void AutocompleteEditFocusedObserver::Observe( delete this; } +OnNotificationBalloonCountObserver::OnNotificationBalloonCountObserver( + AutomationProvider* provider, + IPC::Message* reply_message, + BalloonCollection* collection, + int count) + : reply_(provider, reply_message), + collection_(collection), + count_(count) { + collection->set_on_collection_changed_callback(NewCallback( + this, &OnNotificationBalloonCountObserver::OnBalloonCollectionChanged)); +} + +void OnNotificationBalloonCountObserver::OnBalloonCollectionChanged() { + if (static_cast<int>(collection_->GetActiveBalloons().size()) == count_) { + collection_->set_on_collection_changed_callback(NULL); + reply_.SendSuccess(NULL); + delete this; + } +} diff --git a/chrome/browser/automation/automation_provider_observers.h b/chrome/browser/automation/automation_provider_observers.h index 5c7b77b..7b76638 100644 --- a/chrome/browser/automation/automation_provider_observers.h +++ b/chrome/browser/automation/automation_provider_observers.h @@ -10,6 +10,7 @@ #include <map> #include <set> +#include "chrome/browser/automation/automation_provider_json.h" #include "chrome/browser/bookmarks/bookmark_model_observer.h" #include "chrome/browser/browsing_data_remover.h" #include "chrome/browser/download/download_item.h" @@ -27,6 +28,7 @@ class AutocompleteEditModel; class AutomationProvider; +class BalloonCollection; class Browser; class Extension; class ExtensionProcessManager; @@ -271,7 +273,7 @@ class ExtensionReadyNotificationObserver : public NotificationObserver { scoped_refptr<AutomationProvider> automation_; int id_; IPC::Message* reply_message_; - Extension* extension_; + const Extension* extension_; DISALLOW_COPY_AND_ASSIGN(ExtensionReadyNotificationObserver); }; @@ -888,4 +890,24 @@ class AutocompleteEditFocusedObserver : public NotificationObserver { DISALLOW_COPY_AND_ASSIGN(AutocompleteEditFocusedObserver); }; +// Allows the automation provider to wait for a given number of +// notification balloons. +class OnNotificationBalloonCountObserver { + public: + OnNotificationBalloonCountObserver(AutomationProvider* provider, + IPC::Message* reply_message, + BalloonCollection* collection, + int count); + + void OnBalloonCollectionChanged(); + + private: + AutomationJSONReply reply_; + BalloonCollection* collection_; + int count_; + + DISALLOW_COPY_AND_ASSIGN(OnNotificationBalloonCountObserver); +}; + + #endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_OBSERVERS_H_ diff --git a/chrome/browser/automation/automation_provider_win.cc b/chrome/browser/automation/automation_provider_win.cc index 3a30d25..a988a2f 100644 --- a/chrome/browser/automation/automation_provider_win.cc +++ b/chrome/browser/automation/automation_provider_win.cc @@ -5,8 +5,8 @@ #include "chrome/browser/automation/automation_provider.h" #include "app/keyboard_codes.h" +#include "base/debug/trace_event.h" #include "base/json/json_reader.h" -#include "base/trace_event.h" #include "base/utf_string_conversions.h" #include "chrome/browser/automation/automation_browser_tracker.h" #include "chrome/browser/automation/automation_extension_function.h" @@ -241,12 +241,13 @@ void AutomationProvider::GetTabHWND(int handle, HWND* tab_hwnd) { void AutomationProvider::CreateExternalTab( const IPC::ExternalTabSettings& settings, gfx::NativeWindow* tab_container_window, gfx::NativeWindow* tab_window, - int* tab_handle) { + int* tab_handle, int* session_id) { TRACE_EVENT_BEGIN("AutomationProvider::CreateExternalTab", 0, ""); *tab_handle = 0; *tab_container_window = NULL; *tab_window = NULL; + *session_id = -1; scoped_refptr<ExternalTabContainer> external_tab_container = new ExternalTabContainer(this, automation_resource_message_filter_); @@ -266,6 +267,7 @@ void AutomationProvider::CreateExternalTab( *tab_handle = external_tab_container->tab_handle(); *tab_container_window = external_tab_container->GetNativeView(); *tab_window = tab_contents->GetNativeView(); + *session_id = tab_contents->controller().session_id().id(); } else { external_tab_container->Uninitialize(); } @@ -378,12 +380,14 @@ void AutomationProvider::ConnectExternalTab( gfx::NativeWindow parent_window, gfx::NativeWindow* tab_container_window, gfx::NativeWindow* tab_window, - int* tab_handle) { + int* tab_handle, + int* session_id) { TRACE_EVENT_BEGIN("AutomationProvider::ConnectExternalTab", 0, ""); *tab_handle = 0; *tab_container_window = NULL; *tab_window = NULL; + *session_id = -1; scoped_refptr<ExternalTabContainer> external_tab_container = ExternalTabContainer::RemovePendingTab(static_cast<uintptr_t>(cookie)); @@ -400,6 +404,7 @@ void AutomationProvider::ConnectExternalTab( *tab_handle = external_tab_container->tab_handle(); *tab_container_window = external_tab_container->GetNativeView(); *tab_window = tab_contents->GetNativeView(); + *session_id = tab_contents->controller().session_id().id(); } else { external_tab_container->Uninitialize(); } diff --git a/chrome/browser/automation/automation_resource_message_filter.cc b/chrome/browser/automation/automation_resource_message_filter.cc index bdb082c..ecbbddc 100644 --- a/chrome/browser/automation/automation_resource_message_filter.cc +++ b/chrome/browser/automation/automation_resource_message_filter.cc @@ -45,7 +45,7 @@ class AutomationCookieStore : public net::CookieStore { } virtual ~AutomationCookieStore() { - DLOG(INFO) << "In " << __FUNCTION__; + DVLOG(1) << "In " << __FUNCTION__; } // CookieStore implementation. @@ -294,8 +294,8 @@ void AutomationResourceMessageFilter::RegisterRenderViewInIOThread( bool pending_view) { RendererId renderer_key(renderer_pid, renderer_id); - scoped_refptr<net::CookieStore> cookie_store = - new AutomationCookieStore(filter, tab_handle); + scoped_refptr<net::CookieStore> cookie_store( + new AutomationCookieStore(filter, tab_handle)); RenderViewMap::iterator automation_details_iter( filtered_render_views_.Get().find(renderer_key)); @@ -318,7 +318,7 @@ void AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread( renderer_id))); if (automation_details_iter == filtered_render_views_.Get().end()) { - LOG(INFO) << "UnRegisterRenderViewInIOThread: already unregistered"; + VLOG(1) << "UnRegisterRenderViewInIOThread: already unregistered"; return; } @@ -350,8 +350,8 @@ bool AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread( DCHECK(automation_details_iter->second.is_pending_render_view); - scoped_refptr<net::CookieStore> cookie_store = - new AutomationCookieStore(filter, tab_handle); + scoped_refptr<net::CookieStore> cookie_store( + new AutomationCookieStore(filter, tab_handle)); AutomationResourceMessageFilter* old_filter = automation_details_iter->second.filter; diff --git a/chrome/browser/automation/automation_resource_tracker.cc b/chrome/browser/automation/automation_resource_tracker.cc index c233b41..a82265d 100644 --- a/chrome/browser/automation/automation_resource_tracker.cc +++ b/chrome/browser/automation/automation_resource_tracker.cc @@ -15,7 +15,7 @@ AutomationResourceTrackerImpl::AutomationResourceTrackerImpl( AutomationResourceTrackerImpl::~AutomationResourceTrackerImpl() { } -int AutomationResourceTrackerImpl::AddImpl(void* resource) { +int AutomationResourceTrackerImpl::AddImpl(const void* resource) { if (ContainsResourceImpl(resource)) return resource_to_handle_[resource]; @@ -30,7 +30,7 @@ int AutomationResourceTrackerImpl::AddImpl(void* resource) { return handle; } -void AutomationResourceTrackerImpl::RemoveImpl(void* resource) { +void AutomationResourceTrackerImpl::RemoveImpl(const void* resource) { if (!ContainsResourceImpl(resource)) return; @@ -48,7 +48,7 @@ int AutomationResourceTrackerImpl::GenerateHandle() { return ++handle; } -bool AutomationResourceTrackerImpl::ContainsResourceImpl(void* resource) { +bool AutomationResourceTrackerImpl::ContainsResourceImpl(const void* resource) { return resource_to_handle_.find(resource) != resource_to_handle_.end(); } @@ -56,7 +56,7 @@ bool AutomationResourceTrackerImpl::ContainsHandleImpl(int handle) { return handle_to_resource_.find(handle) != handle_to_resource_.end(); } -void* AutomationResourceTrackerImpl::GetResourceImpl(int handle) { +const void* AutomationResourceTrackerImpl::GetResourceImpl(int handle) { HandleToResourceMap::const_iterator iter = handle_to_resource_.find(handle); if (iter == handle_to_resource_.end()) return NULL; @@ -64,7 +64,7 @@ void* AutomationResourceTrackerImpl::GetResourceImpl(int handle) { return iter->second; } -int AutomationResourceTrackerImpl::GetHandleImpl(void* resource) { +int AutomationResourceTrackerImpl::GetHandleImpl(const void* resource) { ResourceToHandleMap::const_iterator iter = resource_to_handle_.find(resource); if (iter == resource_to_handle_.end()) @@ -73,7 +73,8 @@ int AutomationResourceTrackerImpl::GetHandleImpl(void* resource) { return iter->second; } -void AutomationResourceTrackerImpl::HandleCloseNotification(void* resource) { +void AutomationResourceTrackerImpl::HandleCloseNotification( + const void* resource) { if (!ContainsResourceImpl(resource)) return; diff --git a/chrome/browser/automation/automation_resource_tracker.h b/chrome/browser/automation/automation_resource_tracker.h index 671f72b..2d6b0cd 100644 --- a/chrome/browser/automation/automation_resource_tracker.h +++ b/chrome/browser/automation/automation_resource_tracker.h @@ -38,21 +38,21 @@ class AutomationResourceTrackerImpl { // These need to be implemented in AutomationResourceTracker, // since it needs to call the subclass's type-specific notification // registration functions. - virtual void AddObserverTypeProxy(void* resource) = 0; - virtual void RemoveObserverTypeProxy(void* resource) = 0; + virtual void AddObserverTypeProxy(const void* resource) = 0; + virtual void RemoveObserverTypeProxy(const void* resource) = 0; - int AddImpl(void* resource); - void RemoveImpl(void* resource); + int AddImpl(const void* resource); + void RemoveImpl(const void* resource); int GenerateHandle(); - bool ContainsResourceImpl(void* resource); + bool ContainsResourceImpl(const void* resource); bool ContainsHandleImpl(int handle); - void* GetResourceImpl(int handle); - int GetHandleImpl(void* resource); - void HandleCloseNotification(void* resource); + const void* GetResourceImpl(int handle); + int GetHandleImpl(const void* resource); + void HandleCloseNotification(const void* resource); protected: - typedef std::map<void*, int> ResourceToHandleMap; - typedef std::map<int, void*> HandleToResourceMap; + typedef std::map<const void*, int> ResourceToHandleMap; + typedef std::map<int, const void*> HandleToResourceMap; ResourceToHandleMap resource_to_handle_; HandleToResourceMap handle_to_resource_; @@ -109,8 +109,9 @@ class AutomationResourceTracker : public NotificationObserver, // Returns the resource pointer associated with a given handle, or NULL // if that handle is not present in the mapping. + // The casts here allow this to compile with both T = Foo and T = const Foo. T GetResource(int handle) { - return static_cast<T>(GetResourceImpl(handle)); + return static_cast<T>(const_cast<void*>(GetResourceImpl(handle))); } // Returns the handle associated with a given resource pointer, or 0 if @@ -144,11 +145,12 @@ class AutomationResourceTracker : public NotificationObserver, private: // These proxy calls from the base Impl class to the template's subclss. - virtual void AddObserverTypeProxy(void* resource) { - AddObserver(static_cast<T>(resource)); + // The casts here allow this to compile with both T = Foo and T = const Foo. + virtual void AddObserverTypeProxy(const void* resource) { + AddObserver(static_cast<T>(const_cast<void*>(resource))); } - virtual void RemoveObserverTypeProxy(void* resource) { - RemoveObserver(static_cast<T>(resource)); + virtual void RemoveObserverTypeProxy(const void* resource) { + RemoveObserver(static_cast<T>(const_cast<void*>(resource))); } DISALLOW_COPY_AND_ASSIGN(AutomationResourceTracker); diff --git a/chrome/browser/automation/extension_port_container.cc b/chrome/browser/automation/extension_port_container.cc index f6b502e..f5a1d75 100644 --- a/chrome/browser/automation/extension_port_container.cc +++ b/chrome/browser/automation/extension_port_container.cc @@ -119,7 +119,7 @@ void ExtensionPortContainer::OnExtensionMessageInvoke( if (args.GetString(0, &message) && args.GetInteger(1, &source_port_id)) OnExtensionHandleMessage(message, source_port_id); } else if (function_name == ExtensionMessageService::kDispatchOnDisconnect) { - DCHECK_EQ(args.GetSize(), 1u); + DCHECK_EQ(args.GetSize(), 2u); int port_id; if (args.GetInteger(0, &port_id)) OnExtensionPortDisconnected(port_id); diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc index 109c13b..4c2fcce 100644 --- a/chrome/browser/automation/testing_automation_provider.cc +++ b/chrome/browser/automation/testing_automation_provider.cc @@ -10,12 +10,15 @@ #include "base/json/json_writer.h" #include "base/json/string_escape.h" #include "base/path_service.h" +#include "base/stringprintf.h" #include "base/time.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/app_modal_dialog.h" #include "chrome/browser/app_modal_dialog_queue.h" +#include "chrome/browser/autocomplete/autocomplete.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autofill/autofill_manager.h" #include "chrome/browser/automation/automation_autocomplete_edit_tracker.h" #include "chrome/browser/automation/automation_browser_tracker.h" @@ -59,6 +62,10 @@ #include "chrome/common/net/url_request_context_getter.h" #include "chrome/common/notification_service.h" #include "chrome/common/url_constants.h" +#include "chrome/browser/notifications/balloon.h" +#include "chrome/browser/notifications/balloon_collection.h" +#include "chrome/browser/notifications/notification.h" +#include "chrome/browser/notifications/notification_ui_manager.h" #include "chrome/test/automation/automation_messages.h" #include "net/base/cookie_store.h" #include "net/url_request/url_request_context.h" @@ -419,6 +426,7 @@ void TestingAutomationProvider::OnMessageReceived( IPC_MESSAGE_HANDLER(AutomationMsg_ShutdownSessionService, ShutdownSessionService) IPC_MESSAGE_HANDLER(AutomationMsg_SetContentSetting, SetContentSetting) + IPC_MESSAGE_HANDLER(AutomationMsg_LoadBlockedPlugins, LoadBlockedPlugins) IPC_MESSAGE_HANDLER(AutomationMsg_ResetToDefaultTheme, ResetToDefaultTheme) IPC_MESSAGE_UNHANDLED(AutomationProvider::OnMessageReceived(message)); @@ -555,8 +563,9 @@ void TestingAutomationProvider::DeleteCookie(const GURL& url, NavigationController* tab = tab_tracker_->GetResource(handle); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - new DeleteCookieTask(url, cookie_name, - tab->profile()->GetRequestContext())); + new DeleteCookieTask( + url, cookie_name, + make_scoped_refptr(tab->profile()->GetRequestContext()))); *success = true; } } @@ -809,9 +818,12 @@ void TestingAutomationProvider::GetLastActiveBrowserWindow(int* handle) { } void TestingAutomationProvider::GetActiveWindow(int* handle) { - gfx::NativeWindow window = - BrowserList::GetLastActive()->window()->GetNativeHandle(); - *handle = window_tracker_->Add(window); + *handle = 0; + Browser* browser = BrowserList::GetLastActive(); + if (browser) { + gfx::NativeWindow window = browser->window()->GetNativeHandle(); + *handle = window_tracker_->Add(window); + } } void TestingAutomationProvider::ExecuteBrowserCommandAsync(int handle, @@ -1166,9 +1178,9 @@ void TestingAutomationProvider::ExecuteJavascript( // communication while sending back the response of // this javascript execution. std::wstring set_automation_id; - SStringPrintf(&set_automation_id, - L"window.domAutomationController.setAutomationId(%d);", - reply_message->routing_id()); + base::SStringPrintf(&set_automation_id, + L"window.domAutomationController.setAutomationId(%d);", + reply_message->routing_id()); DCHECK(reply_message_ == NULL); reply_message_ = reply_message; @@ -1539,9 +1551,9 @@ void TestingAutomationProvider::GetBookmarksAsJSON( if (!browser->profile()->GetBookmarkModel()->IsLoaded()) { return; } - scoped_refptr<BookmarkStorage> storage = new BookmarkStorage( + scoped_refptr<BookmarkStorage> storage(new BookmarkStorage( browser->profile(), - browser->profile()->GetBookmarkModel()); + browser->profile()->GetBookmarkModel())); *success = storage->SerializeData(bookmarks_as_json); } } @@ -2095,6 +2107,22 @@ void TestingAutomationProvider::SendJSONRequest(int handle, handler_map["FillAutoFillProfile"] = &TestingAutomationProvider::FillAutoFillProfile; + handler_map["GetActiveNotifications"] = + &TestingAutomationProvider::GetActiveNotifications; + handler_map["CloseNotification"] = + &TestingAutomationProvider::CloseNotification; + handler_map["WaitForNotificationCount"] = + &TestingAutomationProvider::WaitForNotificationCount; + + handler_map["SignInToSync"] = &TestingAutomationProvider::SignInToSync; + handler_map["GetSyncInfo"] = &TestingAutomationProvider::GetSyncInfo; + handler_map["AwaitSyncCycleCompletion"] = + &TestingAutomationProvider::AwaitSyncCycleCompletion; + handler_map["EnableSyncForDatatypes"] = + &TestingAutomationProvider::EnableSyncForDatatypes; + handler_map["DisableSyncForDatatypes"] = + &TestingAutomationProvider::DisableSyncForDatatypes; + if (handler_map.find(std::string(command)) != handler_map.end()) { (this->*handler_map[command])(browser, dict_value, reply_message); } else { @@ -3655,7 +3683,7 @@ void TestingAutomationProvider::GetThemeInfo( DictionaryValue* args, IPC::Message* reply_message) { scoped_ptr<DictionaryValue> return_value(new DictionaryValue); - Extension* theme = browser->profile()->GetTheme(); + const Extension* theme = browser->profile()->GetTheme(); if (theme) { return_value->SetString("name", theme->name()); return_value->Set("images", theme->GetThemeImages()->DeepCopy()); @@ -3812,16 +3840,230 @@ void TestingAutomationProvider::FillAutoFillProfile( reply.SendSuccess(NULL); } +// Sample json output: { "success": true } +void TestingAutomationProvider::SignInToSync(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + AutomationJSONReply reply(this, reply_message); + std::string username; + std::string password; + if (!args->GetString("username", &username) || + !args->GetString("password", &password)) { + reply.SendError("Invalid or missing args"); + return; + } + if (sync_waiter_.get() == NULL) { + sync_waiter_.reset(new ProfileSyncServiceHarness( + browser->profile(), username, password, 0)); + } else { + sync_waiter_->SetCredentials(username, password); + } + if (sync_waiter_->SetupSync()) { + DictionaryValue* return_value = new DictionaryValue; + return_value->SetBoolean("success", true); + reply.SendSuccess(return_value); + } else { + reply.SendError("Signing in to sync was unsuccessful"); + } +} + +// Sample json output: +// {u'summary': u'SYNC DISABLED'} +// +// { u'authenticated': True, +// u'last synced': u'Just now', +// u'summary': u'READY', +// u'sync url': u'clients4.google.com', +// u'synced datatypes': [ u'Bookmarks', +// u'Preferences', +// u'Passwords', +// u'Autofill', +// u'Themes', +// u'Extensions', +// u'Apps']} +void TestingAutomationProvider::GetSyncInfo(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + AutomationJSONReply reply(this, reply_message); + DictionaryValue* sync_info = new DictionaryValue; + DictionaryValue* return_value = new DictionaryValue; + if (sync_waiter_.get() == NULL) { + sync_waiter_.reset( + ProfileSyncServiceHarness::CreateAndAttach(browser->profile())); + } + if (!sync_waiter_->IsSyncAlreadySetup()) { + sync_info->SetString("summary", "SYNC DISABLED"); + } else { + ProfileSyncService* service = sync_waiter_->service(); + ProfileSyncService::Status status = sync_waiter_->GetStatus(); + sync_info->SetString("summary", + ProfileSyncService::BuildSyncStatusSummaryText(status.summary)); + sync_info->SetString("sync url", service->sync_service_url().host()); + sync_info->SetBoolean("authenticated", status.authenticated); + sync_info->SetString("last synced", service->GetLastSyncedTimeString()); + ListValue* synced_datatype_list = new ListValue; + syncable::ModelTypeSet synced_datatypes; + service->GetPreferredDataTypes(&synced_datatypes); + for (syncable::ModelTypeSet::iterator it = synced_datatypes.begin(); + it != synced_datatypes.end(); ++it) { + synced_datatype_list->Append( + new StringValue(syncable::ModelTypeToString(*it))); + } + sync_info->Set("synced datatypes", synced_datatype_list); + } + return_value->Set("sync_info", sync_info); + reply.SendSuccess(return_value); +} + +// Sample json output: { "success": true } +void TestingAutomationProvider::AwaitSyncCycleCompletion( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + AutomationJSONReply reply(this, reply_message); + if (sync_waiter_.get() == NULL) { + sync_waiter_.reset( + ProfileSyncServiceHarness::CreateAndAttach(browser->profile())); + } + if (!sync_waiter_->IsSyncAlreadySetup()) { + reply.SendError("Not signed in to sync"); + return; + } + sync_waiter_->AwaitSyncCycleCompletion("Waiting for sync cycle"); + ProfileSyncService::Status status = sync_waiter_->GetStatus(); + if (status.summary == ProfileSyncService::Status::READY) { + scoped_ptr<DictionaryValue> return_value(new DictionaryValue); + return_value->SetBoolean("success", true); + reply.SendSuccess(return_value.get()); + } else { + reply.SendError("Wait for sync cycle was unsuccessful"); + } +} + +// Refer to EnableSyncForDatatypes() in chrome/test/pyautolib/pyauto.py for +// sample json input. Sample json output: { "success": true } +void TestingAutomationProvider::EnableSyncForDatatypes( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + AutomationJSONReply reply(this, reply_message); + if (sync_waiter_.get() == NULL) { + sync_waiter_.reset( + ProfileSyncServiceHarness::CreateAndAttach(browser->profile())); + } + if (!sync_waiter_->IsSyncAlreadySetup()) { + reply.SendError("Not signed in to sync"); + return; + } + ListValue* datatypes = NULL; + if (!args->GetList("datatypes", &datatypes)) { + reply.SendError("Invalid or missing args"); + return; + } + std::string first_datatype; + datatypes->GetString(0, &first_datatype); + if (first_datatype == "All") { + sync_waiter_->EnableSyncForAllDatatypes(); + } else { + int num_datatypes = datatypes->GetSize(); + for (int i = 0; i < num_datatypes; ++i) { + std::string datatype_string; + datatypes->GetString(i, &datatype_string); + syncable::ModelType datatype = + syncable::ModelTypeFromString(datatype_string); + if (datatype == syncable::UNSPECIFIED) { + AutomationJSONReply(this, reply_message).SendError(StringPrintf( + "Invalid datatype string: %s.", datatype_string.c_str())); + return; + } + sync_waiter_->EnableSyncForDatatype(datatype); + sync_waiter_->AwaitSyncCycleCompletion(StringPrintf( + "Enabling datatype: %s", datatype_string.c_str())); + } + } + ProfileSyncService::Status status = sync_waiter_->GetStatus(); + if (status.summary == ProfileSyncService::Status::READY || + status.summary == ProfileSyncService::Status::SYNCING) { + DictionaryValue* return_value = new DictionaryValue; + return_value->SetBoolean("success", true); + reply.SendSuccess(return_value); + } else { + reply.SendError("Enabling sync for given datatypes was unsuccessful"); + } +} + +// Refer to DisableSyncForDatatypes() in chrome/test/pyautolib/pyauto.py for +// sample json input. Sample json output: { "success": true } +void TestingAutomationProvider::DisableSyncForDatatypes( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + AutomationJSONReply reply(this, reply_message); + if (sync_waiter_.get() == NULL) { + sync_waiter_.reset( + ProfileSyncServiceHarness::CreateAndAttach(browser->profile())); + } + if (!sync_waiter_->IsSyncAlreadySetup()) { + reply.SendError("Not signed in to sync"); + return; + } + ListValue* datatypes = NULL; + if (!args->GetList("datatypes", &datatypes)) { + reply.SendError("Invalid or missing args"); + return; + } + std::string first_datatype; + datatypes->GetString(0, &first_datatype); + if (first_datatype == "All") { + sync_waiter_->DisableSyncForAllDatatypes(); + ProfileSyncService::Status status = sync_waiter_->GetStatus(); + if (status.summary != ProfileSyncService::Status::READY && + status.summary != ProfileSyncService::Status::SYNCING) { + DictionaryValue* return_value = new DictionaryValue; + return_value->SetBoolean("success", true); + reply.SendSuccess(return_value); + } else { + reply.SendError("Disabling all sync datatypes was unsuccessful"); + } + } else { + int num_datatypes = datatypes->GetSize(); + for (int i = 0; i < num_datatypes; i++) { + std::string datatype_string; + datatypes->GetString(i, &datatype_string); + syncable::ModelType datatype = + syncable::ModelTypeFromString(datatype_string); + if (datatype == syncable::UNSPECIFIED) { + AutomationJSONReply(this, reply_message).SendError(StringPrintf( + "Invalid datatype string: %s.", datatype_string.c_str())); + return; + } + sync_waiter_->DisableSyncForDatatype(datatype); + sync_waiter_->AwaitSyncCycleCompletion(StringPrintf( + "Disabling datatype: %s", datatype_string.c_str())); + } + ProfileSyncService::Status status = sync_waiter_->GetStatus(); + if (status.summary == ProfileSyncService::Status::READY || + status.summary == ProfileSyncService::Status::SYNCING) { + DictionaryValue* return_value = new DictionaryValue; + return_value->SetBoolean("success", true); + reply.SendSuccess(return_value); + } else { + reply.SendError("Disabling sync for given datatypes was unsuccessful"); + } + } +} + /* static */ ListValue* TestingAutomationProvider::GetListFromAutoFillProfiles( - std::vector<AutoFillProfile*> autofill_profiles) { + const std::vector<AutoFillProfile*>& autofill_profiles) { ListValue* profiles = new ListValue; std::map<AutoFillFieldType, std::wstring> autofill_type_to_string = GetAutoFillFieldToStringMap(); // For each AutoFillProfile, transform it to a dictionary object to return. - for (std::vector<AutoFillProfile*>::iterator it = autofill_profiles.begin(); + for (std::vector<AutoFillProfile*>::const_iterator it = + autofill_profiles.begin(); it != autofill_profiles.end(); ++it) { AutoFillProfile* profile = *it; DictionaryValue* profile_info = new DictionaryValue; @@ -3841,14 +4083,15 @@ ListValue* TestingAutomationProvider::GetListFromAutoFillProfiles( /* static */ ListValue* TestingAutomationProvider::GetListFromCreditCards( - std::vector<CreditCard*> credit_cards) { + const std::vector<CreditCard*>& credit_cards) { ListValue* cards = new ListValue; std::map<AutoFillFieldType, std::wstring> credit_card_type_to_string = GetCreditCardFieldToStringMap(); // For each AutoFillProfile, transform it to a dictionary object to return. - for (std::vector<CreditCard*>::iterator it = credit_cards.begin(); + for (std::vector<CreditCard*>::const_iterator it = + credit_cards.begin(); it != credit_cards.end(); ++it) { CreditCard* card = *it; DictionaryValue* card_info = new DictionaryValue; @@ -3881,8 +4124,7 @@ TestingAutomationProvider::GetAutoFillProfilesFromList( int num_profiles = profiles.GetSize(); for (int i = 0; i < num_profiles; i++) { profiles.GetDictionary(i, &profile_info); - // Choose an id of 0 so that a unique id will be created. - AutoFillProfile profile(string16(), 0); + AutoFillProfile profile; // Loop through the possible profile types and add those provided. for (std::map<AutoFillFieldType, std::wstring>::iterator type_it = autofill_type_to_string.begin(); @@ -3915,7 +4157,7 @@ std::vector<CreditCard> TestingAutomationProvider::GetCreditCardsFromList( int num_credit_cards = cards.GetSize(); for (int i = 0; i < num_credit_cards; i++) { cards.GetDictionary(i, &card_info); - CreditCard card(string16(), 0); + CreditCard card; // Loop through the possible credit card fields and add those provided. for (std::map<AutoFillFieldType, std::wstring>::iterator type_it = credit_card_type_to_string.begin(); @@ -3968,6 +4210,83 @@ std::map<AutoFillFieldType, std::wstring> return credit_card_type_to_string; } +// Refer to GetActiveNotifications() in chrome/test/pyautolib/pyauto.py for +// sample json input/output. +void TestingAutomationProvider::GetActiveNotifications( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + NotificationUIManager* manager = g_browser_process->notification_ui_manager(); + const BalloonCollection::Balloons& balloons = + manager->balloon_collection()->GetActiveBalloons(); + scoped_ptr<DictionaryValue> return_value(new DictionaryValue); + ListValue* list = new ListValue; + return_value->Set("notifications", list); + BalloonCollection::Balloons::const_iterator iter; + for (iter = balloons.begin(); iter != balloons.end(); ++iter) { + const Notification& notification = (*iter)->notification(); + DictionaryValue* balloon = new DictionaryValue; + balloon->SetString("content_url", notification.content_url().spec()); + balloon->SetString("origin_url", notification.origin_url().spec()); + balloon->SetString("display_source", notification.display_source()); + list->Append(balloon); + } + AutomationJSONReply(this, reply_message).SendSuccess(return_value.get()); +} + +// Refer to CloseNotification() in chrome/test/pyautolib/pyauto.py for +// sample json input. +// Returns empty json message. +void TestingAutomationProvider::CloseNotification( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + int index; + if (!args->GetInteger("index", &index)) { + AutomationJSONReply(this, reply_message).SendError( + "'index' missing or invalid."); + return; + } + NotificationUIManager* manager = g_browser_process->notification_ui_manager(); + BalloonCollection* collection = manager->balloon_collection(); + const BalloonCollection::Balloons& balloons = collection->GetActiveBalloons(); + int balloon_count = static_cast<int>(balloons.size()); + if (index < 0 || index >= balloon_count) { + AutomationJSONReply(this, reply_message).SendError( + StringPrintf("No notification at index %d", index)); + return; + } + // This will delete itself when finished. + new OnNotificationBalloonCountObserver( + this, reply_message, collection, balloon_count - 1); + manager->Cancel(balloons[index]->notification()); +} + +// Refer to WaitForNotificationCount() in chrome/test/pyautolib/pyauto.py for +// sample json input. +// Returns empty json message. +void TestingAutomationProvider::WaitForNotificationCount( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + int count; + if (!args->GetInteger("count", &count)) { + AutomationJSONReply(this, reply_message).SendError( + "'count' missing or invalid."); + return; + } + NotificationUIManager* manager = g_browser_process->notification_ui_manager(); + BalloonCollection* collection = manager->balloon_collection(); + const BalloonCollection::Balloons& balloons = collection->GetActiveBalloons(); + if (static_cast<int>(balloons.size()) == count) { + AutomationJSONReply(this, reply_message).SendSuccess(NULL); + return; + } + // This will delete itself when finished. + new OnNotificationBalloonCountObserver( + this, reply_message, collection, count); +} + void TestingAutomationProvider::WaitForTabCountToBecome( int browser_handle, int target_tab_count, @@ -4052,6 +4371,21 @@ void TestingAutomationProvider::SetContentSetting( } } +void TestingAutomationProvider::LoadBlockedPlugins(int tab_handle, + bool* success) { + *success = false; + if (tab_tracker_->ContainsHandle(tab_handle)) { + NavigationController* nav = tab_tracker_->GetResource(tab_handle); + if (!nav) + return; + TabContents* contents = nav->tab_contents(); + if (!contents) + return; + contents->render_view_host()->LoadBlockedPlugins(); + *success = true; + } +} + void TestingAutomationProvider::ResetToDefaultTheme() { profile_->ClearTheme(); } diff --git a/chrome/browser/automation/testing_automation_provider.h b/chrome/browser/automation/testing_automation_provider.h index 838a079..8cd999d 100644 --- a/chrome/browser/automation/testing_automation_provider.h +++ b/chrome/browser/automation/testing_automation_provider.h @@ -7,9 +7,11 @@ #pragma once #include "base/basictypes.h" +#include "base/scoped_ptr.h" #include "chrome/browser/automation/automation_provider.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/history/history.h" +#include "chrome/browser/sync/profile_sync_service_harness.h" #include "chrome/common/notification_registrar.h" class DictionaryValue; @@ -612,6 +614,36 @@ class TestingAutomationProvider : public AutomationProvider, DictionaryValue* args, IPC::Message* reply_message); + // Signs in to sync using the given username and password. + // Uses the JSON interface for input/output. + void SignInToSync(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + + // Returns info about sync. + // Uses the JSON interface for input/output. + void GetSyncInfo(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + + // Waits for the ongoing sync cycle to complete. + // Uses the JSON interface for input/output. + void AwaitSyncCycleCompletion(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + + // Enables sync for one or more sync datatypes. + // Uses the JSON interface for input/output. + void EnableSyncForDatatypes(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + + // Disables sync for one or more sync datatypes. + // Uses the JSON interface for input/output. + void DisableSyncForDatatypes(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + // Translate DictionaryValues of autofill profiles and credit cards to the // data structure used in the browser. // Args: @@ -626,9 +658,9 @@ class TestingAutomationProvider : public AutomationProvider, // for profiles and credit cards to a ListValue of DictionaryValues. The // caller owns the returned object. static ListValue* GetListFromAutoFillProfiles( - std::vector<AutoFillProfile*> autofill_profiles); + const std::vector<AutoFillProfile*>& autofill_profiles); static ListValue* GetListFromCreditCards( - std::vector<CreditCard*> credit_cards); + const std::vector<CreditCard*>& credit_cards); // Return the map from the internal data representation to the string value // of auto fill fields and credit card fields. @@ -637,6 +669,24 @@ class TestingAutomationProvider : public AutomationProvider, static std::map<AutoFillFieldType, std::wstring> GetCreditCardFieldToStringMap(); + // Get a list of active HTML5 notifications. + // Uses the JSON interface for input/output. + void GetActiveNotifications(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + + // Close an active HTML5 notification. + // Uses the JSON interface for input/output. + void CloseNotification(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + + // Waits for the number of active HTML5 notifications to reach a given count. + // Uses the JSON interface for input/output. + void WaitForNotificationCount(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + void WaitForTabCountToBecome(int browser_handle, int target_tab_count, IPC::Message* reply_message); @@ -656,6 +706,9 @@ class TestingAutomationProvider : public AutomationProvider, ContentSetting setting, bool* success); + // Load all plug-ins on the page. + void LoadBlockedPlugins(int tab_handle, bool* success); + // Resets to the default theme. void ResetToDefaultTheme(); @@ -681,6 +734,9 @@ class TestingAutomationProvider : public AutomationProvider, PopupMenuWaiter* popup_menu_waiter_; #endif // defined(TOOLKIT_VIEWS) + // Used to wait on various browser sync events. + scoped_ptr<ProfileSyncServiceHarness> sync_waiter_; + // Handle for an in-process redirect query. We expect only one redirect query // at a time (we should have only one caller, and it will block while waiting // for the results) so there is only one handle. When non-0, indicates a diff --git a/chrome/browser/automation/url_request_automation_job.cc b/chrome/browser/automation/url_request_automation_job.cc index 21624c3..fc4bd63 100644 --- a/chrome/browser/automation/url_request_automation_job.cc +++ b/chrome/browser/automation/url_request_automation_job.cc @@ -54,7 +54,7 @@ URLRequestAutomationJob::URLRequestAutomationJob(URLRequest* request, int tab, redirect_status_(0), request_id_(request_id), is_pending_(is_pending) { - DLOG(INFO) << "URLRequestAutomationJob create. Count: " << ++instance_count_; + DVLOG(1) << "URLRequestAutomationJob create. Count: " << ++instance_count_; DCHECK(message_filter_ != NULL); if (message_filter_) { @@ -64,7 +64,7 @@ URLRequestAutomationJob::URLRequestAutomationJob(URLRequest* request, int tab, } URLRequestAutomationJob::~URLRequestAutomationJob() { - DLOG(INFO) << "URLRequestAutomationJob delete. Count: " << --instance_count_; + DVLOG(1) << "URLRequestAutomationJob delete. Count: " << --instance_count_; Cleanup(); } @@ -91,8 +91,9 @@ URLRequestJob* URLRequestAutomationJob::Factory(URLRequest* request, // Returning null here just means that the built-in handler will be used. if (scheme_is_http || scheme_is_https) { - ResourceDispatcherHostRequestInfo* request_info = - ResourceDispatcherHost::InfoForRequest(request); + ResourceDispatcherHostRequestInfo* request_info = NULL; + if (request->GetUserData(NULL)) + request_info = ResourceDispatcherHost::InfoForRequest(request); if (request_info) { int child_id = request_info->child_id(); int route_id = request_info->route_id(); @@ -148,8 +149,8 @@ void URLRequestAutomationJob::Kill() { bool URLRequestAutomationJob::ReadRawData( net::IOBuffer* buf, int buf_size, int* bytes_read) { - DLOG(INFO) << "URLRequestAutomationJob: " << - request_->url().spec() << " - read pending: " << buf_size; + DVLOG(1) << "URLRequestAutomationJob: " << request_->url().spec() + << " - read pending: " << buf_size; // We should not receive a read request for a pending job. DCHECK(!is_pending()); @@ -259,8 +260,8 @@ void URLRequestAutomationJob::OnMessage(const IPC::Message& message) { void URLRequestAutomationJob::OnRequestStarted(int tab, int id, const IPC::AutomationURLResponse& response) { - DLOG(INFO) << "URLRequestAutomationJob: " << - request_->url().spec() << " - response started."; + DVLOG(1) << "URLRequestAutomationJob: " << request_->url().spec() + << " - response started."; set_expected_content_size(response.content_length); mime_type_ = response.mime_type; @@ -279,8 +280,8 @@ void URLRequestAutomationJob::OnRequestStarted(int tab, int id, void URLRequestAutomationJob::OnDataAvailable( int tab, int id, const std::string& bytes) { - DLOG(INFO) << "URLRequestAutomationJob: " << - request_->url().spec() << " - data available, Size: " << bytes.size(); + DVLOG(1) << "URLRequestAutomationJob: " << request_->url().spec() + << " - data available, Size: " << bytes.size(); DCHECK(!bytes.empty()); // The request completed, and we have all the data. @@ -307,8 +308,8 @@ void URLRequestAutomationJob::OnRequestEnd( std::string url; if (request_) url = request_->url().spec(); - DLOG(INFO) << "URLRequestAutomationJob: " - << url << " - request end. Status: " << status.status(); + DVLOG(1) << "URLRequestAutomationJob: " << url << " - request end. Status: " + << status.status(); #endif // TODO(tommi): When we hit certificate errors, notify the delegate via @@ -362,8 +363,8 @@ void URLRequestAutomationJob::Cleanup() { } void URLRequestAutomationJob::StartAsync() { - DLOG(INFO) << "URLRequestAutomationJob: start request: " << - (request_ ? request_->url().spec() : "NULL request"); + DVLOG(1) << "URLRequestAutomationJob: start request: " + << (request_ ? request_->url().spec() : "NULL request"); // If the job is cancelled before we got a chance to start it // we have nothing much to do here. @@ -411,8 +412,8 @@ void URLRequestAutomationJob::StartAsync() { // The referrer header must be suppressed if the preceding URL was // a secure one and the new one is not. if (referrer.SchemeIsSecure() && !request_->url().SchemeIsSecure()) { - DLOG(INFO) << - "Suppressing referrer header since going from secure to non-secure"; + DVLOG(1) << "Suppressing referrer header since going from secure to " + "non-secure"; referrer = GURL(); } diff --git a/chrome/browser/background_application_list_model.cc b/chrome/browser/background_application_list_model.cc new file mode 100644 index 0000000..63f2b94 --- /dev/null +++ b/chrome/browser/background_application_list_model.cc @@ -0,0 +1,308 @@ +// 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/background_application_list_model.h" + +#include <algorithm> + +#include "app/l10n_util_collator.h" +#include "base/stl_util-inl.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/background_mode_manager.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/extensions/image_loading_tracker.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profile.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_resource.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/pref_names.h" + +class ExtensionNameComparator { + public: + explicit ExtensionNameComparator(icu::Collator* collator); + bool operator()(const Extension* x, const Extension* y); + + private: + icu::Collator* collator_; +}; + +ExtensionNameComparator::ExtensionNameComparator(icu::Collator* collator) + : collator_(collator) { +} + +bool ExtensionNameComparator::operator()(const Extension* x, + const Extension* y) { + return l10n_util::StringComparator<string16>(collator_)( + UTF8ToUTF16(x->name()), + UTF8ToUTF16(y->name())); +} + +// Background application representation, private to the +// BackgroundApplicationListModel class. +class BackgroundApplicationListModel::Application + : public ImageLoadingTracker::Observer { + public: + Application(BackgroundApplicationListModel* model, + const Extension* an_extension); + + virtual ~Application(); + + // Invoked when a request icon is available. + virtual void OnImageLoaded(SkBitmap* image, + ExtensionResource resource, + int index); + + // Uses the FILE thread to request this extension's icon, sized + // appropriately. + void RequestIcon(Extension::Icons size); + + const Extension* extension_; + scoped_ptr<SkBitmap> icon_; + BackgroundApplicationListModel* model_; + ImageLoadingTracker tracker_; +}; + +namespace { +void GetServiceApplications(ExtensionsService* service, + ExtensionList* applications_result) { + const ExtensionList* extensions = service->extensions(); + + for (ExtensionList::const_iterator cursor = extensions->begin(); + cursor != extensions->end(); + ++cursor) { + const Extension* extension = *cursor; + if (BackgroundApplicationListModel::IsBackgroundApp(*extension)) + applications_result->push_back(extension); + } + std::string locale = g_browser_process->GetApplicationLocale(); + icu::Locale loc(locale.c_str()); + UErrorCode error = U_ZERO_ERROR; + scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(loc, error)); + sort(applications_result->begin(), applications_result->end(), + ExtensionNameComparator(collator.get())); +} + +bool HasBackgroundAppPermission( + const std::set<std::string>& api_permissions) { + return Extension::HasApiPermission( + api_permissions, Extension::kBackgroundPermission); +} +} // namespace + +void +BackgroundApplicationListModel::Observer::OnApplicationDataChanged( + const Extension* extension) { +} + +void +BackgroundApplicationListModel::Observer::OnApplicationListChanged() { +} + +BackgroundApplicationListModel::Observer::~Observer() { +} + +BackgroundApplicationListModel::Application::~Application() { +} + +BackgroundApplicationListModel::Application::Application( + BackgroundApplicationListModel* model, + const Extension* extension) + : extension_(extension), + icon_(NULL), + model_(model), + ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) { +} + +void BackgroundApplicationListModel::Application::OnImageLoaded( + SkBitmap* image, + ExtensionResource resource, + int index) { + if (!image) + return; + icon_.reset(new SkBitmap(*image)); + model_->OnApplicationDataChanged(extension_); +} + +void BackgroundApplicationListModel::Application::RequestIcon( + Extension::Icons size) { + ExtensionResource resource = extension_->GetIconResource( + size, ExtensionIconSet::MATCH_BIGGER); + tracker_.LoadImage(extension_, resource, gfx::Size(size, size), + ImageLoadingTracker::CACHE); +} + +BackgroundApplicationListModel::~BackgroundApplicationListModel() { + STLDeleteContainerPairSecondPointers(applications_.begin(), + applications_.end()); +} + +BackgroundApplicationListModel::BackgroundApplicationListModel(Profile* profile) + : profile_(profile) { + DCHECK(profile_); + registrar_.Add(this, + NotificationType::EXTENSION_LOADED, + Source<Profile>(profile)); + registrar_.Add(this, + NotificationType::EXTENSION_UNLOADED, + Source<Profile>(profile)); + registrar_.Add(this, + NotificationType::EXTENSION_UNLOADED_DISABLED, + Source<Profile>(profile)); + registrar_.Add(this, + NotificationType::EXTENSIONS_READY, + Source<Profile>(profile)); +} + +void BackgroundApplicationListModel::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void BackgroundApplicationListModel::AssociateApplicationData( + const Extension* extension) { + DCHECK(IsBackgroundApp(*extension)); + Application* application = FindApplication(extension); + if (!application) { + application = new Application(this, extension); + applications_[extension->id()] = application; + application->RequestIcon(Extension::EXTENSION_ICON_BITTY); + } +} + +void BackgroundApplicationListModel::DissociateApplicationData( + const Extension* extension) { + ApplicationMap::iterator found = applications_.find(extension->id()); + if (found != applications_.end()) { + delete found->second; + applications_.erase(found); + } +} + +const Extension* BackgroundApplicationListModel::GetExtension( + int position) const { + DCHECK(position >= 0 && static_cast<size_t>(position) < extensions_.size()); + return extensions_[position]; +} + +const BackgroundApplicationListModel::Application* +BackgroundApplicationListModel::FindApplication( + const Extension* extension) const { + const std::string& id = extension->id(); + ApplicationMap::const_iterator found = applications_.find(id); + return (found == applications_.end()) ? NULL : found->second; +} + +BackgroundApplicationListModel::Application* +BackgroundApplicationListModel::FindApplication(const Extension* extension) { + const std::string& id = extension->id(); + ApplicationMap::iterator found = applications_.find(id); + return (found == applications_.end()) ? NULL : found->second; +} + +const SkBitmap* BackgroundApplicationListModel::GetIcon( + const Extension* extension) { + const Application* application = FindApplication(extension); + if (application) + return application->icon_.get(); + AssociateApplicationData(extension); + return NULL; +} + +int BackgroundApplicationListModel::GetPosition( + const Extension* extension) const { + int position = 0; + const std::string& id = extension->id(); + for (ExtensionList::const_iterator cursor = extensions_.begin(); + cursor != extensions_.end(); + ++cursor, ++position) { + if (id == cursor->get()->id()) + return position; + } + NOTREACHED(); + return -1; +} + +// static +bool BackgroundApplicationListModel::IsBackgroundApp( + const Extension& extension) { + return HasBackgroundAppPermission(extension.api_permissions()); +} + +void BackgroundApplicationListModel::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::EXTENSIONS_READY) { + Update(); + return; + } + ExtensionsService* service = profile_->GetExtensionsService(); + if (!service || !service->is_ready()) + return; + switch (type.value) { + case NotificationType::EXTENSION_LOADED: + OnExtensionLoaded(Details<Extension>(details).ptr()); + break; + case NotificationType::EXTENSION_UNLOADED: + // Handle extension unload uniformly, falling through to next case. + case NotificationType::EXTENSION_UNLOADED_DISABLED: + OnExtensionUnloaded(Details<Extension>(details).ptr()); + break; + default: + NOTREACHED() << "Received unexpected notification"; + } +} + +void BackgroundApplicationListModel::OnApplicationDataChanged( + const Extension* extension) { + FOR_EACH_OBSERVER(Observer, observers_, OnApplicationDataChanged(extension)); +} + +void BackgroundApplicationListModel::OnExtensionLoaded(Extension* extension) { + // We only care about extensions that are background applications + if (!IsBackgroundApp(*extension)) + return; + AssociateApplicationData(extension); + Update(); +} + +void BackgroundApplicationListModel::OnExtensionUnloaded(Extension* extension) { + if (!IsBackgroundApp(*extension)) + return; + Update(); + DissociateApplicationData(extension); +} + +void BackgroundApplicationListModel::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +// Update queries the extensions service of the profile with which the model was +// initialized to determine the current set of background applications. If that +// differs from the old list, it generates OnApplicationListChanged events for +// each observer. +void BackgroundApplicationListModel::Update() { + ExtensionsService* service = profile_->GetExtensionsService(); + DCHECK(service->is_ready()); + + // Discover current background applications, compare with previous list, which + // is consistently sorted, and notify observers if they differ. + ExtensionList extensions; + GetServiceApplications(service, &extensions); + ExtensionList::const_iterator old_cursor = extensions_.begin(); + ExtensionList::const_iterator new_cursor = extensions.begin(); + while (old_cursor != extensions_.end() && + new_cursor != extensions.end() && + (*old_cursor)->name() == (*new_cursor)->name() && + (*old_cursor)->id() == (*new_cursor)->id()) { + ++old_cursor; + ++new_cursor; + } + if (old_cursor != extensions_.end() || new_cursor != extensions.end()) { + extensions_ = extensions; + FOR_EACH_OBSERVER(Observer, observers_, OnApplicationListChanged()); + } +} diff --git a/chrome/browser/background_application_list_model.h b/chrome/browser/background_application_list_model.h new file mode 100644 index 0000000..63c356e --- /dev/null +++ b/chrome/browser/background_application_list_model.h @@ -0,0 +1,138 @@ +// 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_BACKGROUND_APPLICATION_LIST_MODEL_H_ +#define CHROME_BROWSER_BACKGROUND_APPLICATION_LIST_MODEL_H_ +#pragma once + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/observer_list.h" +#include "chrome/browser/profile.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" + +class ExtensionsService; +class PrefService; + +// Model for list of Background Applications, that is, Extensions with +// kBackgroundPermission set, associated with a Profile. +class BackgroundApplicationListModel : public NotificationObserver { + public: + // Observer is informed of changes to the model. Users of the + // BackgroundApplicationListModel should anticipate that associated data, + // e. g. the Icon, may exist and yet not be immediately available. When the + // data becomes available, OnApplicationDataChanged will be invoked for all + // Observers of the model. + class Observer { + public: + // Invoked when data that the model associates with the extension, such as + // the Icon, has changed. + virtual void OnApplicationDataChanged(const Extension* extension); + + // Invoked when the model detects a previously unknown extension and/or when + // it no longer detects a previously known extension. + virtual void OnApplicationListChanged(); + + protected: + virtual ~Observer(); + }; + + // Create a new model associated with profile. + explicit BackgroundApplicationListModel(Profile* profile); + + ~BackgroundApplicationListModel(); + + // Associate observer with this model. + void AddObserver(Observer* observer); + + // Return the icon associated with |extension| or NULL. NULL indicates either + // that there is no icon associated with the extension, or that a pending + // task to retrieve the icon has not completed. See the Observer class above. + // + // NOTE: The model manages the SkBitmap result, that is it "owns" the memory, + // releasing it if the associated background application is unloaded. + // NOTE: All icons are currently sized as Extension::EXTENSION_ICON_BITTY. + const SkBitmap* GetIcon(const Extension* extension); + + // Return the position of |extension| within this list model. + int GetPosition(const Extension* extension) const; + + // Return the extension at the specified |position| in this list model. + const Extension* GetExtension(int position) const; + + // Returns true if the passed extension is a background app. + static bool IsBackgroundApp(const Extension& extension); + + // Dissociate observer from this model. + void RemoveObserver(Observer* observer); + + ExtensionList::const_iterator begin() const { + return extensions_.begin(); + } + + ExtensionList::const_iterator end() const { + return extensions_.end(); + } + + size_t size() const { + return extensions_.size(); + } + + private: + // Contains data associated with a background application that is not + // represented by the Extension class. + class Application; + + // Associates extension id strings with Application objects. + typedef std::map<std::string, Application*> ApplicationMap; + + // Identifies and caches data related to the extension. + void AssociateApplicationData(const Extension* extension); + + // Clears cached data related to |extension|. + void DissociateApplicationData(const Extension* extension); + + // Returns the Application associated with |extension| or NULL. + const Application* FindApplication(const Extension* extension) const; + + // Returns the Application associated with |extension| or NULL. + Application* FindApplication(const Extension* extension); + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Notifies observers that some of the data associated with this background + // application, e. g. the Icon, has changed. + void OnApplicationDataChanged(const Extension* extension); + + // Notifies observers that at least one background application has been added + // or removed. + void OnApplicationListChanged(); + + // Invoked by Observe for EXTENSION_LOADED notifications. + void OnExtensionLoaded(Extension* extension); + + // Invoked by Observe for EXTENSION_UNLOADED* notifications. + void OnExtensionUnloaded(Extension* extension); + + // Refresh the list of background applications and generates ApplicationAdded + // and ApplicationRemoved events. + void Update(); + + ApplicationMap applications_; + ExtensionList extensions_; + ObserverList<Observer> observers_; + Profile* profile_; + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundApplicationListModel); +}; + +#endif // CHROME_BROWSER_BACKGROUND_APPLICATION_LIST_MODEL_H_ diff --git a/chrome/browser/background_contents_service.cc b/chrome/browser/background_contents_service.cc index 1c9b71e..8517d97 100644 --- a/chrome/browser/background_contents_service.cc +++ b/chrome/browser/background_contents_service.cc @@ -110,7 +110,7 @@ void BackgroundContentsService::Observe(NotificationType type, break; case NotificationType::EXTENSION_UNLOADED: ShutdownAssociatedBackgroundContents( - ASCIIToUTF16(Details<Extension>(details)->id())); + ASCIIToUTF16(Details<const Extension>(details)->id())); break; default: NOTREACHED(); @@ -157,7 +157,7 @@ void BackgroundContentsService::CreateBackgroundContents( // Check to make sure that the parent extension is still enabled. ExtensionsService* extensions_service = profile->GetExtensionsService(); - Extension* extension = extensions_service->GetExtensionById( + const Extension* extension = extensions_service->GetExtensionById( UTF16ToASCII(application_id), false); if (!extension) { diff --git a/chrome/browser/background_contents_service.h b/chrome/browser/background_contents_service.h index e9d7df0..08165d8 100644 --- a/chrome/browser/background_contents_service.h +++ b/chrome/browser/background_contents_service.h @@ -16,7 +16,6 @@ #include "googleurl/src/gurl.h" #include "webkit/glue/window_open_disposition.h" -class BackgroundContents; class CommandLine; class PrefService; class Profile; diff --git a/chrome/browser/background_mode_manager.cc b/chrome/browser/background_mode_manager.cc index 080e3b2..06e5439 100644 --- a/chrome/browser/background_mode_manager.cc +++ b/chrome/browser/background_mode_manager.cc @@ -9,9 +9,12 @@ #include "base/file_path.h" #include "base/logging.h" #include "base/path_service.h" -#include "chrome/app/chrome_dll_resource.h" +#include "base/utf_string_conversions.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/background_application_list_model.h" #include "chrome/browser/background_mode_manager.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" @@ -32,9 +35,9 @@ #include <unistd.h> #include "base/environment.h" #include "base/file_util.h" +#include "base/nix/xdg_util.h" #include "base/task.h" #include "base/utf_string_conversions.h" -#include "base/nix/xdg_util.h" #include "chrome/common/chrome_version_info.h" #endif @@ -127,10 +130,22 @@ class EnableLaunchOnStartupTask : public Task { }; #endif // defined(OS_LINUX) +void BackgroundModeManager::OnApplicationDataChanged( + const Extension* extension) { + UpdateContextMenuEntryIcon(extension); +} + +void BackgroundModeManager::OnApplicationListChanged() { + UpdateStatusTrayIconContextMenu(); +} + BackgroundModeManager::BackgroundModeManager(Profile* profile, CommandLine* command_line) : profile_(profile), + applications_(profile), background_app_count_(0), + context_menu_(NULL), + context_menu_application_offset_(0), in_background_mode_(false), keep_alive_for_startup_(false), status_tray_(NULL), @@ -184,9 +199,13 @@ BackgroundModeManager::BackgroundModeManager(Profile* profile, // Listen for changes to the background mode preference. pref_registrar_.Init(profile_->GetPrefs()); pref_registrar_.Add(prefs::kBackgroundModeEnabled, this); + + applications_.AddObserver(this); } BackgroundModeManager::~BackgroundModeManager() { + applications_.RemoveObserver(this); + // We're going away, so exit background mode (does nothing if we aren't in // background mode currently). This is primarily needed for unit tests, // because in an actual running system we'd get an APP_TERMINATING @@ -207,20 +226,6 @@ void BackgroundModeManager::SetLaunchOnStartupResetAllowed(bool allowed) { allowed); } -namespace { - -bool HasBackgroundAppPermission( - const std::set<std::string>& api_permissions) { - return Extension::HasApiPermission( - api_permissions, Extension::kBackgroundPermission); -} - -bool IsBackgroundApp(const Extension& extension) { - return HasBackgroundAppPermission(extension.api_permissions()); -} - -} // namespace - void BackgroundModeManager::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { @@ -239,22 +244,30 @@ void BackgroundModeManager::Observe(NotificationType type, #endif break; case NotificationType::EXTENSION_LOADED: - if (IsBackgroundApp(*Details<Extension>(details).ptr())) + if (BackgroundApplicationListModel::IsBackgroundApp( + *Details<Extension>(details).ptr())) { OnBackgroundAppLoaded(); + } break; case NotificationType::EXTENSION_UNLOADED: - if (IsBackgroundApp(*Details<Extension>(details).ptr())) + if (BackgroundApplicationListModel::IsBackgroundApp( + *Details<Extension>(details).ptr())) { OnBackgroundAppUnloaded(); + } break; case NotificationType::EXTENSION_INSTALLED: - if (IsBackgroundApp(*Details<Extension>(details).ptr())) + if (BackgroundApplicationListModel::IsBackgroundApp( + *Details<Extension>(details).ptr())) { OnBackgroundAppInstalled(); + } break; case NotificationType::EXTENSION_UNINSTALLED: - if (HasBackgroundAppPermission( - Details<UninstalledExtensionInfo>(details).ptr()-> - extension_api_permissions)) + if (Extension::HasApiPermission( + Details<UninstalledExtensionInfo>(details).ptr()-> + extension_api_permissions, + Extension::kBackgroundPermission)) { OnBackgroundAppUninstalled(); + } break; case NotificationType::APP_TERMINATING: // Make sure we aren't still keeping the app alive (only happens if we @@ -447,9 +460,30 @@ void BackgroundModeManager::CreateStatusTrayIcon() { IDR_STATUS_TRAY_ICON); status_icon_->SetImage(*bitmap); status_icon_->SetToolTip(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); + UpdateStatusTrayIconContextMenu(); +} + +void BackgroundModeManager::UpdateContextMenuEntryIcon( + const Extension* extension) { + if (!context_menu_) + return; + context_menu_->SetIcon( + context_menu_application_offset_ + applications_.GetPosition(extension), + *(applications_.GetIcon(extension))); + status_icon_->SetContextMenu(context_menu_); // for Update effect +} + +void BackgroundModeManager::UpdateStatusTrayIconContextMenu() { + if (!status_icon_) + return; // Create a context menu item for Chrome. menus::SimpleMenuModel* menu = new menus::SimpleMenuModel(this); + // Add About item + menu->AddItem(IDC_ABOUT, l10n_util::GetStringFUTF16(IDS_ABOUT, + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); + + // Add Preferences item if (CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableTabbedOptions)) { menu->AddItemWithStringId(IDC_OPTIONS, IDS_SETTINGS); @@ -464,11 +498,23 @@ void BackgroundModeManager::CreateStatusTrayIcon() { menu->AddItemWithStringId(IDC_OPTIONS, IDS_OPTIONS); #endif } - menu->AddItem(IDC_ABOUT, l10n_util::GetStringFUTF16(IDS_ABOUT, - l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); + menu->AddSeparator(); + int application_position = 0; + context_menu_application_offset_ = menu->GetItemCount(); + for (ExtensionList::const_iterator cursor = applications_.begin(); + cursor != applications_.end(); + ++cursor, ++application_position) { + const SkBitmap* icon = applications_.GetIcon(*cursor); + int sort_position = applications_.GetPosition(*cursor); + DCHECK(sort_position == application_position); + const std::string& name = (*cursor)->name(); + menu->AddItem(sort_position, ASCIIToUTF16(name)); + if (icon) + menu->SetIcon(menu->GetItemCount() - 1, *icon); + } menu->AddSeparator(); menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT); - + context_menu_ = menu; status_icon_->SetContextMenu(menu); } @@ -494,6 +540,16 @@ void BackgroundModeManager::RemoveStatusTrayIcon() { status_icon_ = NULL; } +void BackgroundModeManager::ExecuteApplication(int item) { + DCHECK(item > 0 && item < static_cast<int>(applications_.size())); + Browser* browser = BrowserList::GetLastActive(); + if (!browser) { + Browser::OpenEmptyWindow(profile_); + browser = BrowserList::GetLastActive(); + } + const Extension* extension = applications_.GetExtension(item); + browser->OpenApplicationTab(profile_, extension, NULL); +} void BackgroundModeManager::ExecuteCommand(int item) { switch (item) { @@ -508,7 +564,7 @@ void BackgroundModeManager::ExecuteCommand(int item) { GetBrowserWindow()->OpenOptionsDialog(); break; default: - NOTREACHED(); + ExecuteApplication(item); break; } } diff --git a/chrome/browser/background_mode_manager.h b/chrome/browser/background_mode_manager.h index e662ca7..666b4f7 100644 --- a/chrome/browser/background_mode_manager.h +++ b/chrome/browser/background_mode_manager.h @@ -8,12 +8,14 @@ #include "app/menus/simple_menu_model.h" #include "base/gtest_prod_util.h" +#include "chrome/browser/background_application_list_model.h" #include "chrome/browser/prefs/pref_change_registrar.h" #include "chrome/browser/status_icons/status_icon.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" class Browser; +class CommandLine; class Extension; class PrefService; class Profile; @@ -36,13 +38,15 @@ class StatusTray; // no open windows to allow apps with the "background" permission to run in the // background. class BackgroundModeManager - : private NotificationObserver, - private menus::SimpleMenuModel::Delegate { + : public NotificationObserver, + public menus::SimpleMenuModel::Delegate, + public BackgroundApplicationListModel::Observer { public: BackgroundModeManager(Profile* profile, CommandLine* command_line); virtual ~BackgroundModeManager(); static void RegisterUserPrefs(PrefService* prefs); + private: friend class TestBackgroundModeManager; friend class BackgroundModeManagerTest; @@ -69,6 +73,13 @@ class BackgroundModeManager menus::Accelerator* accelerator); virtual void ExecuteCommand(int command_id); + // Open an application in a new tab, opening a new window if needed. + virtual void ExecuteApplication(int application_id); + + // BackgroundApplicationListModel::Observer implementation. + virtual void OnApplicationDataChanged(const Extension* extension); + virtual void OnApplicationListChanged(); + // Called when an extension is loaded to manage count of background apps. void OnBackgroundAppLoaded(); @@ -121,6 +132,16 @@ class BackgroundModeManager // Virtual to enable testing. virtual void RemoveStatusTrayIcon(); + // Updates the status icon's context menu entry corresponding to |extension| + // to use the icon associated with |extension| in the + // BackgroundApplicationListModel. + void UpdateContextMenuEntryIcon(const Extension* extension); + + // Create a context menu, or replace/update an existing context menu, for the + // status tray icon which, among other things, allows the user to shutdown + // Chrome when running in background mode. + virtual void UpdateStatusTrayIconContextMenu(); + // Returns a browser window, or creates one if none are open. Used by // operations (like displaying the preferences dialog) that require a Browser // window. @@ -131,9 +152,20 @@ class BackgroundModeManager // The parent profile for this object. Profile* profile_; + // The cached list of BackgroundApplications. + BackgroundApplicationListModel applications_; + // The number of background apps currently loaded. int background_app_count_; + // Reference to our status icon's context menu (if any) - owned by the + // status_icon_ + menus::SimpleMenuModel* context_menu_; + + // Set to the position of the first application entry in the status icon's + // context menu. + int context_menu_application_offset_; + // Set to true when we are running in background mode. Allows us to track our // current background state so we can take the appropriate action when the // user disables/enables background mode via preferences. diff --git a/chrome/browser/blocked_content_container.cc b/chrome/browser/blocked_content_container.cc index 37767ca..bbe712a 100644 --- a/chrome/browser/blocked_content_container.cc +++ b/chrome/browser/blocked_content_container.cc @@ -47,6 +47,9 @@ void BlockedContentContainer::AddTabContents(TabContents* tab_contents, blocked_contents_.push_back( BlockedContent(tab_contents, disposition, bounds, user_gesture)); tab_contents->set_delegate(this); + // Since the new tab_contents will not be showed, call WasHidden to change + // its status on both RenderViewHost and RenderView. + tab_contents->WasHidden(); if (blocked_contents_.size() == 1) owner_->PopupNotificationVisibilityChanged(true); } @@ -62,6 +65,8 @@ void BlockedContentContainer::LaunchForContents(TabContents* tab_contents) { blocked_contents_.erase(i); i = blocked_contents_.end(); tab_contents->set_delegate(NULL); + // We needn't call WasRestored to change its status because the + // TabContents::AddNewContents will do it. owner_->AddNewContents(tab_contents, content.disposition, content.bounds, content.user_gesture); break; diff --git a/chrome/browser/bookmarks/bookmark_html_writer.cc b/chrome/browser/bookmarks/bookmark_html_writer.cc index d66c35f..38899bf 100644 --- a/chrome/browser/bookmarks/bookmark_html_writer.cc +++ b/chrome/browser/bookmarks/bookmark_html_writer.cc @@ -249,7 +249,7 @@ class Writer : public Task { BookmarkFaviconFetcher::URLFaviconMap::iterator itr = favicons_map_->find(url_string); if (itr != favicons_map_->end()) { - scoped_refptr<RefCountedMemory> data = itr->second.get(); + scoped_refptr<RefCountedMemory> data(itr->second.get()); std::string favicon_data; favicon_data.assign(reinterpret_cast<const char*>(data->front()), data->size()); diff --git a/chrome/browser/bookmarks/bookmark_storage.cc b/chrome/browser/bookmarks/bookmark_storage.cc index 6cbe433..ee50a71 100644 --- a/chrome/browser/bookmarks/bookmark_storage.cc +++ b/chrome/browser/bookmarks/bookmark_storage.cc @@ -6,6 +6,7 @@ #include "base/compiler_specific.h" #include "base/file_util.h" +#include "base/file_util_proxy.h" #include "base/metrics/histogram.h" #include "base/time.h" #include "chrome/browser/bookmarks/bookmark_codec.h" @@ -43,21 +44,6 @@ class BackupTask : public Task { DISALLOW_COPY_AND_ASSIGN(BackupTask); }; -class FileDeleteTask : public Task { - public: - explicit FileDeleteTask(const FilePath& path) : path_(path) { - } - - virtual void Run() { - file_util::Delete(path_, true); - } - - private: - const FilePath path_; - - DISALLOW_COPY_AND_ASSIGN(FileDeleteTask); -}; - } // namespace class BookmarkStorage::LoadTask : public Task { @@ -244,8 +230,11 @@ void BookmarkStorage::OnLoadFinished(bool file_exists, const FilePath& path) { SaveNow(); // Clean up after migration from history. - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, new FileDeleteTask(tmp_history_path_)); + base::FileUtilProxy::Delete( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), + tmp_history_path_, + false, + NULL); } } diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h index a040498..a08661d 100644 --- a/chrome/browser/browser.h +++ b/chrome/browser/browser.h @@ -6,1130 +6,8 @@ #define CHROME_BROWSER_BROWSER_H_ #pragma once -#include <map> -#include <set> -#include <string> -#include <vector> - -#include "base/basictypes.h" -#include "base/gtest_prod_util.h" -#include "base/scoped_ptr.h" -#include "base/string16.h" -#include "base/task.h" -#include "chrome/browser/command_updater.h" -#include "chrome/browser/debugger/devtools_toggle_action.h" -#include "chrome/browser/instant/instant_delegate.h" -#include "chrome/browser/prefs/pref_member.h" -#include "chrome/browser/sessions/session_id.h" -#include "chrome/browser/sessions/tab_restore_service_observer.h" -#include "chrome/browser/shell_dialogs.h" -#include "chrome/browser/sync/profile_sync_service_observer.h" -#include "chrome/browser/tabs/tab_handler.h" -#include "chrome/browser/tabs/tab_strip_model_delegate.h" // TODO(beng): remove -#include "chrome/browser/tabs/tab_strip_model_observer.h" // TODO(beng): remove -#include "chrome/browser/tab_contents/page_navigator.h" -#include "chrome/browser/tab_contents/tab_contents_delegate.h" -#include "chrome/browser/toolbar_model.h" -#include "chrome/common/extensions/extension_constants.h" -#include "chrome/common/notification_registrar.h" -#include "chrome/common/page_transition_types.h" -#include "chrome/common/page_zoom.h" -#include "gfx/rect.h" - -class BrowserWindow; -class Extension; -class FindBarController; -class InstantController; -class PrefService; -class Profile; -class SessionStorageNamespace; -class SkBitmap; -class StatusBubble; -class TabNavigation; -class TabStripModel; -namespace gfx { -class Point; -} - -class Browser : public TabHandlerDelegate, - public TabContentsDelegate, - public PageNavigator, - public CommandUpdater::CommandUpdaterDelegate, - public NotificationObserver, - public SelectFileDialog::Listener, - public TabRestoreServiceObserver, - public ProfileSyncServiceObserver, - public InstantDelegate { - public: - // If you change the values in this enum you'll need to update browser_proxy. - // TODO(sky): move into a common place that is referenced by both ui_tests - // and chrome. - enum Type { - TYPE_NORMAL = 1, - TYPE_POPUP = 2, - // The old-style app created via "Create application shortcuts". - TYPE_APP = 4, - // The new-style app created by installing a crx. This kinda needs to be - // separate because we require larger icons and an application name that - // are found in the crx. If we ever decide to create this kind of app - // using some other system (eg some web standard), maybe we should - // generalize this name to TYPE_MULTITAB or something. - TYPE_EXTENSION_APP = 8, - TYPE_APP_POPUP = TYPE_APP | TYPE_POPUP, - TYPE_DEVTOOLS = TYPE_APP | 16, - - // TODO(skerner): crbug/56776: Until the panel UI is complete on all - // platforms, apps that set app.launch.container = "panel" have type - // APP_POPUP. (see Browser::CreateForApp) - // NOTE: TYPE_APP_PANEL is a superset of TYPE_APP_POPUP. - TYPE_APP_PANEL = TYPE_APP | TYPE_POPUP | 32, - TYPE_ANY = TYPE_NORMAL | - TYPE_POPUP | - TYPE_APP | - TYPE_EXTENSION_APP | - TYPE_DEVTOOLS | - TYPE_APP_PANEL - }; - - // Possible elements of the Browser window. - enum WindowFeature { - FEATURE_NONE = 0, - FEATURE_TITLEBAR = 1, - FEATURE_TABSTRIP = 2, - FEATURE_TOOLBAR = 4, - FEATURE_LOCATIONBAR = 8, - FEATURE_BOOKMARKBAR = 16, - FEATURE_INFOBAR = 32, - FEATURE_SIDEBAR = 64, - FEATURE_DOWNLOADSHELF = 128 - }; - - // Maximized state on creation. - enum MaximizedState { - // The maximized state is set to the default, which varies depending upon - // what the user has done. - MAXIMIZED_STATE_DEFAULT, - - // Maximized state is explicitly maximized. - MAXIMIZED_STATE_MAXIMIZED, - - // Maximized state is explicitly not maximized (normal). - MAXIMIZED_STATE_UNMAXIMIZED - }; - - // Constructors, Creation, Showing ////////////////////////////////////////// - - // Creates a new browser of the given |type| and for the given |profile|. The - // Browser has a NULL window after its construction, CreateBrowserWindow must - // be called after configuration for window() to be valid. - // Avoid using this constructor directly if you can use one of the Create*() - // methods below. This applies to almost all non-testing code. - Browser(Type type, Profile* profile); - virtual ~Browser(); - - // Creates a normal tabbed browser with the specified profile. The Browser's - // window is created by this function call. - static Browser* Create(Profile* profile); - - // Like Create, but creates a browser of the specified (popup) type, with the - // specified contents, in a popup window of the specified size/position. - static Browser* CreateForPopup(Type type, Profile* profile, - TabContents* new_contents, - const gfx::Rect& initial_bounds); - - // Like Create, but creates a browser of the specified type. - static Browser* CreateForType(Type type, Profile* profile); - - // Like Create, but creates a toolbar-less "app" window for the specified - // app. |app_name| is required and is used to identify the window to the - // shell. |extension| is optional. If supplied, we create a window with - // a bigger icon and title text, that supports tabs. - static Browser* CreateForApp(const std::string& app_name, - Extension* extension, - Profile* profile, - bool is_panel); - - // Like Create, but creates a tabstrip-less and toolbar-less - // DevTools "app" window. - static Browser* CreateForDevTools(Profile* profile); - - // Returns the extension app associated with this window, if any. - Extension* extension_app() { return extension_app_; } - - // Set overrides for the initial window bounds and maximized state. - void set_override_bounds(const gfx::Rect& bounds) { - override_bounds_ = bounds; - } - void set_maximized_state(MaximizedState state) { - maximized_state_ = state; - } - // Return true if the initial window bounds have been overridden. - bool bounds_overridden() const { - return !override_bounds_.IsEmpty(); - } - - // Creates the Browser Window. Prefer to use the static helpers above where - // possible. This does not show the window. You need to call window()->Show() - // to show it. - void CreateBrowserWindow(); - - // Accessors //////////////////////////////////////////////////////////////// - - Type type() const { return type_; } - Profile* profile() const { return profile_; } - const std::vector<std::wstring>& user_data_dir_profiles() const; - - // Returns the InstantController or NULL if there is no InstantController for - // this Browser. - InstantController* instant() const { return instant_.get(); } - -#if defined(UNIT_TEST) - // Sets the BrowserWindow. This is intended for testing and generally not - // useful outside of testing. Use CreateBrowserWindow outside of testing, or - // the static convenience methods that create a BrowserWindow for you. - void set_window(BrowserWindow* window) { - DCHECK(!window_); - window_ = window; - } -#endif - - BrowserWindow* window() const { return window_; } - ToolbarModel* toolbar_model() { return &toolbar_model_; } - const SessionID& session_id() const { return session_id_; } - CommandUpdater* command_updater() { return &command_updater_; } - - // Get the FindBarController for this browser, creating it if it does not - // yet exist. - FindBarController* GetFindBarController(); - - // Returns true if a FindBarController exists for this browser. - bool HasFindBarController() const; - - // Setters ///////////////////////////////////////////////////////////////// - - void set_user_data_dir_profiles(const std::vector<std::wstring>& profiles); - - // Browser Creation Helpers ///////////////////////////////////////////////// - - // Opens a new window with the default blank tab. - static void OpenEmptyWindow(Profile* profile); - - // Opens a new window with the tabs from |profile|'s TabRestoreService. - static void OpenWindowWithRestoredTabs(Profile* profile); - - // Opens the specified URL in a new browser window in an incognito session. - // If there is already an existing active incognito session for the specified - // |profile|, that session is re-used. - static void OpenURLOffTheRecord(Profile* profile, const GURL& url); - - // Open an application specified by |app_id| in the appropriate launch - // container. |existing_tab| is reused if it is not NULL and the launch - // container is a tab. Returns NULL if the app_id is invalid or if - // ExtensionsService isn't ready/available. - static TabContents* OpenApplication(Profile* profile, - const std::string& app_id, - TabContents* existing_tab); - - // Open |extension| in |container|, using |existing_tab| if not NULL and if - // the correct container type. Returns the TabContents* that was created or - // NULL. - static TabContents* OpenApplication( - Profile* profile, - Extension* extension, - extension_misc::LaunchContainer container, - TabContents* existing_tab); - - // Opens a new application window for the specified url. If |as_panel| - // is true, the application will be opened as a Browser::Type::APP_PANEL in - // app panel window, otherwise it will be opened as as either - // Browser::Type::APP a.k.a. "thin frame" (if |extension| is NULL) or - // Browser::Type::EXTENSION_APP (if |extension| is non-NULL). - static TabContents* OpenApplicationWindow( - Profile* profile, - Extension* extension, - extension_misc::LaunchContainer container, - const GURL& url); - - // Open an application for |extension| in a new application window or panel. - static TabContents* OpenApplicationWindow(Profile* profile, GURL& url); - - // Open an application for |extension| in a new application tab, or - // |existing_tab| if not NULL. Returns NULL if there are no appropriate - // existing browser windows for |profile|. - static TabContents* OpenApplicationTab(Profile* profile, - Extension* extension, - TabContents* existing_tab); - - // Opens a new window and opens the bookmark manager. - static void OpenBookmarkManagerWindow(Profile* profile); - -#if defined(OS_MACOSX) - // Open a new window with history/downloads/help/options (needed on Mac when - // there are no windows). - static void OpenHistoryWindow(Profile* profile); - static void OpenDownloadsWindow(Profile* profile); - static void OpenHelpWindow(Profile* profile); - static void OpenOptionsWindow(Profile* profile); -#endif - - // Opens a window with the extensions tab in it - needed by long-lived - // extensions which may run with no windows open. - static void OpenExtensionsWindow(Profile* profile); - - // State Storage and Retrieval for UI /////////////////////////////////////// - - // Save and restore the window position. - std::string GetWindowPlacementKey() const; - bool ShouldSaveWindowPlacement() const; - void SaveWindowPlacement(const gfx::Rect& bounds, bool maximized); - gfx::Rect GetSavedWindowBounds() const; - bool GetSavedMaximizedState() const; - - // Gets the FavIcon of the page in the selected tab. - SkBitmap GetCurrentPageIcon() const; - - // Gets the title of the window based on the selected tab's title. - string16 GetWindowTitleForCurrentTab() const; - - // Prepares a title string for display (removes embedded newlines, etc). - static void FormatTitleForDisplay(string16* title); - - // OnBeforeUnload handling ////////////////////////////////////////////////// - - // Gives beforeunload handlers the chance to cancel the close. - bool ShouldCloseWindow(); - - bool IsAttemptingToCloseBrowser() const { - return is_attempting_to_close_browser_; - } - - // Invoked when the window containing us is closing. Performs the necessary - // cleanup. - void OnWindowClosing(); - - // In-progress download termination handling ///////////////////////////////// - - // Called when the user has decided whether to proceed or not with the browser - // closure. |cancel_downloads| is true if the downloads should be canceled - // and the browser closed, false if the browser should stay open and the - // downloads running. - void InProgressDownloadResponse(bool cancel_downloads); - - // TabStripModel pass-thrus ///////////////////////////////////////////////// - - TabStripModel* tabstrip_model() const { - // TODO(beng): remove this accessor. It violates google style. - return tab_handler_->GetTabStripModel(); - } - - int tab_count() const; - int selected_index() const; - int GetIndexOfController(const NavigationController* controller) const; - TabContents* GetTabContentsAt(int index) const; - TabContents* GetSelectedTabContents() const; - void SelectTabContentsAt(int index, bool user_gesture); - void CloseAllTabs(); - - // Tab adding/showing functions ///////////////////////////////////////////// - - // Returns the index to insert a tab at during session restore and startup. - // |relative_index| gives the index of the url into the number of tabs that - // are going to be opened. For example, if three urls are passed in on the - // command line this is invoked three times with the values 0, 1 and 2. - int GetIndexForInsertionDuringRestore(int relative_index); - - // Adds a selected tab with the specified URL and transition, returns the - // created TabContents. - TabContents* AddSelectedTabWithURL(const GURL& url, - PageTransition::Type transition); - - // Parameters for AddTabWithURL. - struct AddTabWithURLParams { - AddTabWithURLParams(const GURL& a_url, PageTransition::Type a_transition); - ~AddTabWithURLParams(); - - // Basic configuration. - GURL url; - GURL referrer; - PageTransition::Type transition; - - // The index to open the tab at. This could be ignored by the tabstrip - // depending on the combination of |add_types| specified. - int index; - - // A bitmask of values defined in TabStripModel::AddTabTypes. - // The default is ADD_SELECTED. - int add_types; - - // If non-NULL, used to render the tab. - SiteInstance* instance; - - // If non-empty, the new tab is an app tab. - std::string extension_app_id; - - // The browser where the tab was added. - Browser* target; - - private: - AddTabWithURLParams(); - }; - - // Adds a tab to the browser (or another) and returns the created TabContents. - TabContents* AddTabWithURL(AddTabWithURLParams* params); - - // Add a new tab, given a TabContents. A TabContents appropriate to - // display the last committed entry is created and returned. - TabContents* AddTab(TabContents* tab_contents, PageTransition::Type type); - - // Add a tab with its session history restored from the SessionRestore - // system. If select is true, the tab is selected. |tab_index| gives the index - // to insert the tab at. |selected_navigation| is the index of the - // TabNavigation in |navigations| to select. If |extension_app_id| is - // non-empty the tab is an app tab and |extension_app_id| is the id of the - // extension. If |pin| is true and |tab_index|/ is the last pinned tab, then - // the newly created tab is pinned. If |from_last_session| is true, - // |navigations| are from the previous session. - TabContents* AddRestoredTab(const std::vector<TabNavigation>& navigations, - int tab_index, - int selected_navigation, - const std::string& extension_app_id, - bool select, - bool pin, - bool from_last_session, - SessionStorageNamespace* storage_namespace); - // Creates a new tab with the already-created TabContents 'new_contents'. - // The window for the added contents will be reparented correctly when this - // method returns. If |disposition| is NEW_POPUP, |pos| should hold the - // initial position. - void AddTabContents(TabContents* new_contents, - WindowOpenDisposition disposition, - const gfx::Rect& initial_pos, - bool user_gesture); - void CloseTabContents(TabContents* contents); - - // Show a dialog with HTML content. |delegate| contains a pointer to the - // delegate who knows how to display the dialog (which file URL and JSON - // string input to use during initialization). |parent_window| is the window - // that should be parent of the dialog, or NULL for the default. - void BrowserShowHtmlDialog(HtmlDialogUIDelegate* delegate, - gfx::NativeWindow parent_window); - - // Called when a popup select is about to be displayed. - void BrowserRenderWidgetShowing(); - - // Notification that some of our content has changed size as - // part of an animation. - void ToolbarSizeChanged(bool is_animating); - - // Replaces the state of the currently selected tab with the session - // history restored from the SessionRestore system. - void ReplaceRestoredTab( - const std::vector<TabNavigation>& navigations, - int selected_navigation, - bool from_last_session, - const std::string& extension_app_id, - SessionStorageNamespace* session_storage_namespace); - - // Navigate to an index in the tab history, opening a new tab depending on the - // disposition. - bool NavigateToIndexWithDisposition(int index, WindowOpenDisposition disp); - - // Show a given a URL. If a tab with the same URL (ignoring the ref) is - // already visible in this browser, it becomes selected. Otherwise a new tab - // is created. - void ShowSingletonTab(const GURL& url); - - // Update commands whose state depends on whether the window is in fullscreen - // mode. This is a public function because on Linux, fullscreen mode is an - // async call to X. Once we get the fullscreen callback, the browser window - // will call this method. - void UpdateCommandsForFullscreenMode(bool is_fullscreen); - - // Assorted browser commands //////////////////////////////////////////////// - - // NOTE: Within each of the following sections, the IDs are ordered roughly by - // how they appear in the GUI/menus (left to right, top to bottom, etc.). - - // Navigation commands - void GoBack(WindowOpenDisposition disposition); - void GoForward(WindowOpenDisposition disposition); - void Reload(WindowOpenDisposition disposition); - void ReloadIgnoringCache(WindowOpenDisposition disposition); // Shift-reload. - void Home(WindowOpenDisposition disposition); - void OpenCurrentURL(); - void Stop(); - // Window management commands - void NewWindow(); - void NewIncognitoWindow(); - void CloseWindow(); - void NewTab(); - void CloseTab(); - void SelectNextTab(); - void SelectPreviousTab(); - void OpenTabpose(); - void MoveTabNext(); - void MoveTabPrevious(); - void SelectNumberedTab(int index); - void SelectLastTab(); - void DuplicateTab(); - void WriteCurrentURLToClipboard(); - void ConvertPopupToTabbedBrowser(); - // In kiosk mode, the first toggle is valid, the rest is discarded. - void ToggleFullscreenMode(); - void Exit(); -#if defined(OS_CHROMEOS) - void ToggleCompactNavigationBar(); - void Search(); -#endif - - // Page-related commands - void BookmarkCurrentPage(); - void SavePage(); - void ViewSource(); - void ShowFindBar(); - - // Returns true if the Browser supports the specified feature. The value of - // this varies during the lifetime of the browser. For example, if the window - // is fullscreen this may return a different value. If you only care about - // whether or not it's possible for the browser to support a particular - // feature use |CanSupportWindowFeature|. - bool SupportsWindowFeature(WindowFeature feature) const; - - // Returns true if the Browser can support the specified feature. See comment - // in |SupportsWindowFeature| for details on this. - bool CanSupportWindowFeature(WindowFeature feature) const; - -// TODO(port): port these, and re-merge the two function declaration lists. - // Page-related commands. - void Print(); - void EmailPageLocation(); - void ToggleEncodingAutoDetect(); - void OverrideEncoding(int encoding_id); - - // Clipboard commands - void Cut(); - void Copy(); - void Paste(); - - // Find-in-page - void Find(); - void FindNext(); - void FindPrevious(); - - // Zoom - void Zoom(PageZoom::Function zoom_function); - - // Focus various bits of UI - void FocusToolbar(); - void FocusLocationBar(); // Also selects any existing text. - void FocusSearch(); - void FocusAppMenu(); - void FocusBookmarksToolbar(); - void FocusChromeOSStatus(); - void FocusNextPane(); - void FocusPreviousPane(); - - // Show various bits of UI - void OpenFile(); - void OpenCreateShortcutsDialog(); - void ToggleDevToolsWindow(DevToolsToggleAction action); - void OpenTaskManager(); - void OpenBugReportDialog(); - - void ToggleBookmarkBar(); - - void OpenBookmarkManager(); - void ShowAppMenu(); - void ShowBookmarkManagerTab(); - void ShowHistoryTab(); - void ShowDownloadsTab(); - void ShowExtensionsTab(); - void ShowBrokenPageTab(TabContents* contents); - void ShowOptionsTab(const std::string& sub_page); - void OpenClearBrowsingDataDialog(); - void OpenOptionsDialog(); - void OpenKeywordEditor(); - void OpenPasswordManager(); - void OpenSyncMyBookmarksDialog(); -#if defined(ENABLE_REMOTING) - void OpenRemotingSetupDialog(); -#endif - void OpenImportSettingsDialog(); - void OpenAboutChromeDialog(); - void OpenUpdateChromeDialog(); - void OpenHelpTab(); - // Used by the "Get themes" link in the options dialog. - void OpenThemeGalleryTabAndActivate(); - void OpenAutoFillHelpTabAndActivate(); - void OpenPrivacyDashboardTabAndActivate(); - void OpenSearchEngineOptionsDialog(); -#if defined(OS_CHROMEOS) - void OpenSystemOptionsDialog(); - void OpenInternetOptionsDialog(); - void OpenLanguageOptionsDialog(); - void OpenSystemTabAndActivate(); - void OpenMobilePlanTabAndActivate(); -#endif - void OpenPluginsTabAndActivate(); - - virtual void UpdateDownloadShelfVisibility(bool visible); - - // Overridden from TabStripModelDelegate: - virtual bool UseVerticalTabs() const; - - ///////////////////////////////////////////////////////////////////////////// - - // Sets the value of homepage related prefs to new values. Since we do not - // want to change these values for existing users, we can not change the - // default values under RegisterUserPrefs. Also if user already has an - // existing profile we do not want to override those preferences so we only - // set new values if they have not been set already. This method gets called - // during First Run. - static void SetNewHomePagePrefs(PrefService* prefs); - - static void RegisterPrefs(PrefService* prefs); - static void RegisterUserPrefs(PrefService* prefs); - - // Helper function to run unload listeners on a TabContents. - static bool RunUnloadEventsHelper(TabContents* contents); - - // Returns the Browser which contains the tab with the given - // NavigationController, also filling in |index| (if valid) with the tab's - // index in the tab strip. - // Returns NULL if not found. - // This call is O(N) in the number of tabs. - static Browser* GetBrowserForController( - const NavigationController* controller, int* index); - - // Retrieve the last active tabbed browser with a profile matching |profile|. - static Browser* GetTabbedBrowser(Profile* profile, bool match_incognito); - - // Retrieve the last active tabbed browser with a profile matching |profile|. - // Creates a new Browser if none are available. - static Browser* GetOrCreateTabbedBrowser(Profile* profile); - - // Calls ExecuteCommandWithDisposition with the given disposition. - void ExecuteCommandWithDisposition(int id, WindowOpenDisposition); - - // Returns whether the |id| is a reserved command, whose keyboard shortcuts - // should not be sent to the renderer. - bool IsReservedCommand(int id); - - // Sets if command execution shall be blocked. If |block| is true then - // following calls to ExecuteCommand() or ExecuteCommandWithDisposition() - // method will not execute the command, and the last blocked command will be - // recorded for retrieval. - void SetBlockCommandExecution(bool block); - - // Gets the last blocked command after calling SetBlockCommandExecution(true). - // Returns the command id or -1 if there is no command blocked. The - // disposition type of the command will be stored in |*disposition| if it's - // not null. - int GetLastBlockedCommand(WindowOpenDisposition* disposition); - - // Called by browser::Navigate() when a navigation has occurred in a tab in - // this Browser. Updates the UI for the start of this navigation. - void UpdateUIForNavigationInTab(TabContents* contents, - PageTransition::Type transition, - bool user_initiated); - - // Called by browser::Navigate() to retrieve the home page if no URL is - // specified. - GURL GetHomePage() const; - - // Interface implementations //////////////////////////////////////////////// - - // Overridden from PageNavigator: - virtual void OpenURL(const GURL& url, const GURL& referrer, - WindowOpenDisposition disposition, - PageTransition::Type transition); - - // Overridden from CommandUpdater::CommandUpdaterDelegate: - virtual void ExecuteCommand(int id); - - // Overridden from TabRestoreServiceObserver: - virtual void TabRestoreServiceChanged(TabRestoreService* service); - virtual void TabRestoreServiceDestroyed(TabRestoreService* service); - - - // Overridden from TabHandlerDelegate: - virtual Profile* GetProfile() const; - virtual Browser* AsBrowser(); - - // Overridden from TabStripModelDelegate: - virtual TabContents* AddBlankTab(bool foreground); - virtual TabContents* AddBlankTabAt(int index, bool foreground); - virtual Browser* CreateNewStripWithContents(TabContents* detached_contents, - const gfx::Rect& window_bounds, - const DockInfo& dock_info, - bool maximize); - virtual void ContinueDraggingDetachedTab(TabContents* contents, - const gfx::Rect& window_bounds, - const gfx::Rect& tab_bounds); - virtual int GetDragActions() const; - // Construct a TabContents for a given URL, profile and transition type. - // If instance is not null, its process will be used to render the tab. - virtual TabContents* CreateTabContentsForURL(const GURL& url, - const GURL& referrer, - Profile* profile, - PageTransition::Type transition, - bool defer_load, - SiteInstance* instance) const; - virtual bool CanDuplicateContentsAt(int index); - virtual void DuplicateContentsAt(int index); - virtual void CloseFrameAfterDragSession(); - virtual void CreateHistoricalTab(TabContents* contents); - virtual bool RunUnloadListenerBeforeClosing(TabContents* contents); - virtual bool CanCloseContentsAt(int index); - virtual bool CanBookmarkAllTabs() const; - virtual void BookmarkAllTabs(); - virtual bool CanCloseTab() const; - virtual void ToggleUseVerticalTabs(); - virtual bool CanRestoreTab(); - virtual void RestoreTab(); - virtual bool LargeIconsPermitted() const; - - // Overridden from TabStripModelObserver: - virtual void TabInsertedAt(TabContents* contents, - int index, - bool foreground); - virtual void TabClosingAt(TabStripModel* tab_strip_model, - TabContents* contents, - int index); - virtual void TabDetachedAt(TabContents* contents, int index); - virtual void TabDeselectedAt(TabContents* contents, int index); - virtual void TabSelectedAt(TabContents* old_contents, - TabContents* new_contents, - int index, - bool user_gesture); - virtual void TabMoved(TabContents* contents, - int from_index, - int to_index); - virtual void TabReplacedAt(TabContents* old_contents, - TabContents* new_contents, - int index); - virtual void TabPinnedStateChanged(TabContents* contents, int index); - virtual void TabStripEmpty(); - - private: - FRIEND_TEST_ALL_PREFIXES(BrowserTest, NoTabsInPopups); - - // Used to describe why a tab is being detached. This is used by - // TabDetachedAtImpl. - enum DetachType { - // Result of TabDetachedAt. - DETACH_TYPE_DETACH, - - // Result of TabReplacedAt. - DETACH_TYPE_REPLACE, - - // Result of the tab strip not having any significant tabs. - DETACH_TYPE_EMPTY - }; - - // Overridden from TabContentsDelegate: - virtual void OpenURLFromTab(TabContents* source, - const GURL& url, - const GURL& referrer, - WindowOpenDisposition disposition, - PageTransition::Type transition); - virtual void NavigationStateChanged(const TabContents* source, - unsigned changed_flags); - virtual void AddNewContents(TabContents* source, - TabContents* new_contents, - WindowOpenDisposition disposition, - const gfx::Rect& initial_pos, - bool user_gesture); - virtual void ActivateContents(TabContents* contents); - virtual void DeactivateContents(TabContents* contents); - virtual void LoadingStateChanged(TabContents* source); - virtual void CloseContents(TabContents* source); - virtual void MoveContents(TabContents* source, const gfx::Rect& pos); - virtual void DetachContents(TabContents* source); - virtual bool IsPopup(const TabContents* source) const; - virtual bool CanReloadContents(TabContents* source) const; - virtual void ToolbarSizeChanged(TabContents* source, bool is_animating); - virtual void URLStarredChanged(TabContents* source, bool starred); - virtual void UpdateTargetURL(TabContents* source, const GURL& url); - virtual void ContentsMouseEvent( - TabContents* source, const gfx::Point& location, bool motion); - virtual void ContentsZoomChange(bool zoom_in); - virtual void OnContentSettingsChange(TabContents* source); - virtual void SetTabContentBlocked(TabContents* contents, bool blocked); - virtual void TabContentsFocused(TabContents* tab_content); - virtual bool TakeFocus(bool reverse); - virtual bool IsApplication() const; - virtual void ConvertContentsToApplication(TabContents* source); - virtual bool ShouldDisplayURLField(); - virtual gfx::Rect GetRootWindowResizerRect() const; - virtual void ShowHtmlDialog(HtmlDialogUIDelegate* delegate, - gfx::NativeWindow parent_window); - virtual void BeforeUnloadFired(TabContents* source, - bool proceed, - bool* proceed_to_fire_unload); - virtual void SetFocusToLocationBar(bool select_all); - virtual void RenderWidgetShowing(); - virtual int GetExtraRenderViewHeight() const; - virtual void OnStartDownload(DownloadItem* download, TabContents* tab); - virtual void ConfirmSetDefaultSearchProvider( - TabContents* tab_contents, - TemplateURL* template_url, - TemplateURLModel* template_url_model); - virtual void ConfirmAddSearchProvider(const TemplateURL* template_url, - Profile* profile); - virtual void ShowPageInfo(Profile* profile, - const GURL& url, - const NavigationEntry::SSLStatus& ssl, - bool show_history); - virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, - bool* is_keyboard_shortcut); - virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event); - virtual void ShowRepostFormWarningDialog(TabContents* tab_contents); - virtual void ShowContentSettingsWindow(ContentSettingsType content_type); - virtual void ShowCollectedCookiesDialog(TabContents* tab_contents); - virtual bool ShouldAddNavigationToHistory( - const history::HistoryAddPageArgs& add_page_args, - NavigationType::Type navigation_type); - virtual void OnDidGetApplicationInfo(TabContents* tab_contents, - int32 page_id); - virtual void ContentRestrictionsChanged(TabContents* source); - - // Overridden from SelectFileDialog::Listener: - virtual void FileSelected(const FilePath& path, int index, void* params); - - // Overridden from NotificationObserver: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - - // Overridden from ProfileSyncServiceObserver: - virtual void OnStateChanged(); - - // Overriden from InstantDelegate: - virtual void ShowInstant(TabContents* preview_contents); - virtual void HideInstant(); - virtual void CommitInstant(TabContents* preview_contents); - virtual void SetSuggestedText(const string16& text); - virtual gfx::Rect GetInstantBounds(); - - // Command and state updating /////////////////////////////////////////////// - - // Initialize state for all browser commands. - void InitCommandState(); - - // Update commands whose state depends on the tab's state. - void UpdateCommandsForTabState(); - - // Updates commands when the content's restrictions change. - void UpdateCommandsForContentRestrictionState(); - - // Updates the printing command state. - void UpdatePrintingState(int content_restrictions); - - // Ask the Reload/Stop button to change its icon, and update the Stop command - // state. |is_loading| is true if the current TabContents is loading. - // |force| is true if the button should change its icon immediately. - void UpdateReloadStopState(bool is_loading, bool force); - - // UI update coalescing and handling //////////////////////////////////////// - - // Asks the toolbar (and as such the location bar) to update its state to - // reflect the current tab's current URL, security state, etc. - // If |should_restore_state| is true, we're switching (back?) to this tab and - // should restore any previous location bar state (such as user editing) as - // well. - void UpdateToolbar(bool should_restore_state); - - // Does one or both of the following for each bit in |changed_flags|: - // . If the update should be processed immediately, it is. - // . If the update should processed asynchronously (to avoid lots of ui - // updates), then scheduled_updates_ is updated for the |source| and update - // pair and a task is scheduled (assuming it isn't running already) - // that invokes ProcessPendingUIUpdates. - void ScheduleUIUpdate(const TabContents* source, unsigned changed_flags); - - // Processes all pending updates to the UI that have been scheduled by - // ScheduleUIUpdate in scheduled_updates_. - void ProcessPendingUIUpdates(); - - // Removes all entries from scheduled_updates_ whose source is contents. - void RemoveScheduledUpdatesFor(TabContents* contents); - - // Getters for UI /////////////////////////////////////////////////////////// - - // TODO(beng): remove, and provide AutomationProvider a better way to access - // the LocationBarView's edit. - friend class AutomationProvider; - friend class TestingAutomationProvider; - - // Returns the StatusBubble from the current toolbar. It is possible for - // this to return NULL if called before the toolbar has initialized. - // TODO(beng): remove this. - StatusBubble* GetStatusBubble(); - - // Session restore functions //////////////////////////////////////////////// - - // Notifies the history database of the index for all tabs whose index is - // >= index. - void SyncHistoryWithTabs(int index); - - // OnBeforeUnload handling ////////////////////////////////////////////////// - - typedef std::set<TabContents*> UnloadListenerSet; - - // Processes the next tab that needs it's beforeunload/unload event fired. - void ProcessPendingTabs(); - - // Whether we've completed firing all the tabs' beforeunload/unload events. - bool HasCompletedUnloadProcessing() const; - - // Clears all the state associated with processing tabs' beforeunload/unload - // events since the user cancelled closing the window. - void CancelWindowClose(); - - // Removes |tab| from the passed |set|. - // Returns whether the tab was in the set in the first place. - // TODO(beng): this method needs a better name! - bool RemoveFromSet(UnloadListenerSet* set, TabContents* tab); - - // Cleans up state appropriately when we are trying to close the browser and - // the tab has finished firing its unload handler. We also use this in the - // cases where a tab crashes or hangs even if the beforeunload/unload haven't - // successfully fired. - void ClearUnloadState(TabContents* tab); - - // In-progress download termination handling ///////////////////////////////// - - // Called when the window is closing to check if potential in-progress - // downloads should prevent it from closing. - // Returns true if the window can close, false otherwise. - bool CanCloseWithInProgressDownloads(); - - // Assorted utility functions /////////////////////////////////////////////// - - // Checks whether |source| is about to navigate across extension extents, and - // if so, navigates in the correct window. For example if this is a normal - // browser and we're about to navigate into an extent, this method will - // navigate the app's window instead. If we're in an app window and - // navigating out of the app, this method will find and navigate a normal - // browser instead. - // - // Returns true if the navigation was handled, eg, it was opened in some other - // browser. - // - // Returns false if it was not handled. In this case, the method may also - // modify |disposition| to a more suitable value. - bool HandleCrossAppNavigation(TabContents* source, - const GURL& url, - const GURL& referrer, - WindowOpenDisposition *disposition, - PageTransition::Type transition); - - // The low-level function that other OpenURL...() functions call. This - // determines the appropriate SiteInstance to pass to AddTabWithURL(), focuses - // the newly created tab as needed, and does other miscellaneous housekeeping. - // |add_types| is a bitmask of TabStripModel::AddTabTypes and may be used to - // pass specific flags to the TabStripModel. |add_types| is only used if - // the disposition is NEW_WINDOW or SUPPRESS_OPEN. - void OpenURLAtIndex(TabContents* source, - const GURL& url, - const GURL& referrer, - WindowOpenDisposition disposition, - PageTransition::Type transition, - int index, - int add_types); - - // Shows the Find Bar, optionally selecting the next entry that matches the - // existing search string for that Tab. |forward_direction| controls the - // search direction. - void FindInPage(bool find_next, bool forward_direction); - - // Closes the frame. - // TODO(beng): figure out if we need this now that the frame itself closes - // after a return to the message loop. - void CloseFrame(); - - void TabDetachedAtImpl(TabContents* contents, int index, DetachType type); - - // Create a preference dictionary for the provided application name. This is - // done only once per application name / per session. - static void RegisterAppPrefs(const std::string& app_name); - - // Shared code between Reload() and ReloadIgnoringCache(). - void ReloadInternal(WindowOpenDisposition disposition, bool ignore_cache); - - // Return true if the window dispositions means opening a new tab. - bool ShouldOpenNewTabForWindowDisposition(WindowOpenDisposition disposition); - - // Depending on the disposition, return the current tab or a clone of the - // current tab. - TabContents* GetOrCloneTabForDisposition(WindowOpenDisposition disposition); - - // Sets the insertion policy of the tabstrip based on whether vertical tabs - // are enabled. - void UpdateTabStripModelInsertionPolicy(); - - // Invoked when the use vertical tabs preference changes. Resets the insertion - // policy of the tab strip model and notifies the window. - void UseVerticalTabsChanged(); - - // Implementation of SupportsWindowFeature and CanSupportWindowFeature. If - // |check_fullscreen| is true, the set of features reflect the actual state of - // the browser, otherwise the set of features reflect the possible state of - // the browser. - bool SupportsWindowFeatureImpl(WindowFeature feature, - bool check_fullscreen) const; - - // Determines if closing of browser can really be permitted after normal - // sequence of downloads and unload handlers have given the go-ahead to close. - // It is called from ShouldCloseWindow. It checks with - // TabCloseableStateWatcher to confirm if browser can really be closed. - // Appropriate action is taken by watcher as it sees fit. - // If watcher denies closing of browser, CancelWindowClose is called to - // cancel closing of window. - bool IsClosingPermitted(); - - // Commits the current instant, returning true on success. This is intended - // for use from OpenCurrentURL. - bool OpenInstant(WindowOpenDisposition disposition); - - // If this browser should have instant one is created, otherwise does nothing. - void CreateInstantIfNecessary(); - - // Data members ///////////////////////////////////////////////////////////// - - NotificationRegistrar registrar_; - - // This Browser's type. - const Type type_; - - // This Browser's profile. - Profile* const profile_; - - // This Browser's window. - BrowserWindow* window_; - - // This Browser's current TabHandler. - scoped_ptr<TabHandler> tab_handler_; - - // The CommandUpdater that manages the browser window commands. - CommandUpdater command_updater_; - - // An optional application name which is used to retrieve and save window - // positions. - std::string app_name_; - - // Unique identifier of this browser for session restore. This id is only - // unique within the current session, and is not guaranteed to be unique - // across sessions. - const SessionID session_id_; - - // The model for the toolbar view. - ToolbarModel toolbar_model_; - - // UI update coalescing and handling //////////////////////////////////////// - - typedef std::map<const TabContents*, int> UpdateMap; - - // Maps from TabContents to pending UI updates that need to be processed. - // We don't update things like the URL or tab title right away to avoid - // flickering and extra painting. - // See ScheduleUIUpdate and ProcessPendingUIUpdates. - UpdateMap scheduled_updates_; - - // The following factory is used for chrome update coalescing. - ScopedRunnableMethodFactory<Browser> chrome_updater_factory_; - - // OnBeforeUnload handling ////////////////////////////////////////////////// - - // Tracks tabs that need there beforeunload event fired before we can - // close the browser. Only gets populated when we try to close the browser. - UnloadListenerSet tabs_needing_before_unload_fired_; - - // Tracks tabs that need there unload event fired before we can - // close the browser. Only gets populated when we try to close the browser. - UnloadListenerSet tabs_needing_unload_fired_; - - // Whether we are processing the beforeunload and unload events of each tab - // in preparation for closing the browser. - bool is_attempting_to_close_browser_; - - // In-progress download termination handling ///////////////////////////////// - - enum CancelDownloadConfirmationState { - NOT_PROMPTED, // We have not asked the user. - WAITING_FOR_RESPONSE, // We have asked the user and have not received a - // reponse yet. - RESPONSE_RECEIVED // The user was prompted and made a decision already. - }; - - // State used to figure-out whether we should prompt the user for confirmation - // when the browser is closed with in-progress downloads. - CancelDownloadConfirmationState cancel_download_confirmation_state_; - - ///////////////////////////////////////////////////////////////////////////// - - // Override values for the bounds of the window and its maximized state. - // These are supplied by callers that don't want to use the default values. - // The default values are typically loaded from local state (last session), - // obtained from the last window of the same type, or obtained from the - // shell shortcut's startup info. - gfx::Rect override_bounds_; - MaximizedState maximized_state_; - - // The following factory is used to close the frame at a later time. - ScopedRunnableMethodFactory<Browser> method_factory_; - - // The Find Bar. This may be NULL if there is no Find Bar, and if it is - // non-NULL, it may or may not be visible. - scoped_ptr<FindBarController> find_bar_controller_; - - // Dialog box used for opening and saving files. - scoped_refptr<SelectFileDialog> select_file_dialog_; - - // Keep track of the encoding auto detect pref. - BooleanPrefMember encoding_auto_detect_; - - // Keep track of the printing enabled pref. - BooleanPrefMember printing_enabled_; - - // Keep track of when instant enabled changes. - BooleanPrefMember instant_enabled_; - - // Indicates if command execution is blocked. - bool block_command_execution_; - - // Stores the last blocked command id when |block_command_execution_| is true. - int last_blocked_command_id_; - - // Stores the disposition type of the last blocked command. - WindowOpenDisposition last_blocked_command_disposition_; - - // Different types of action when web app info is available. - // OnDidGetApplicationInfo uses this to dispatch calls. - enum WebAppAction { - NONE, // No action at all. - CREATE_SHORTCUT, // Bring up create application shortcut dialog. - UPDATE_SHORTCUT // Update icon for app shortcut. - }; - - // Which deferred action to perform when OnDidGetApplicationInfo is notified - // from a TabContents. Currently, only one pending action is allowed. - WebAppAction pending_web_app_action_; - - // The extension app associated with this window, if any. - Extension* extension_app_; - - // Tracks the display mode of the tabstrip. - mutable BooleanPrefMember use_vertical_tabs_; - - // The profile's tab restore service. The service is owned by the profile, - // and we install ourselves as an observer. - TabRestoreService* tab_restore_service_; - - scoped_ptr<InstantController> instant_; - - DISALLOW_COPY_AND_ASSIGN(Browser); -}; +#include "chrome/browser/ui/browser.h" +// TODO(beng): remove this file once all includes have been updated. #endif // CHROME_BROWSER_BROWSER_H_ + diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc index 9590b87..79775e6 100644 --- a/chrome/browser/browser_about_handler.cc +++ b/chrome/browser/browser_about_handler.cc @@ -64,6 +64,7 @@ #endif #if defined(OS_WIN) +#include "chrome/browser/enumerate_modules_model_win.h" #include "chrome/browser/views/about_ipc_dialog.h" #elif defined(OS_CHROMEOS) #include "chrome/browser/chromeos/cros/cros_library.h" @@ -101,6 +102,9 @@ const char kAppCacheInternalsPath[] = "appcache-internals"; const char kBlobInternalsPath[] = "blob-internals"; const char kCreditsPath[] = "credits"; const char kCachePath[] = "view-http-cache"; +#if defined(OS_WIN) +const char kConflictsPath[] = "conflicts"; +#endif const char kDnsPath[] = "dns"; const char kFlagsPath[] = "flags"; const char kGpuPath[] = "gpu"; @@ -134,6 +138,9 @@ const char *kAllAboutPaths[] = { kBlobInternalsPath, kCachePath, kCreditsPath, +#if defined(OS_WIN) + kConflictsPath, +#endif kDnsPath, kFlagsPath, kGpuPath, @@ -263,6 +270,9 @@ std::string AboutAbout() { if (kAllAboutPaths[i] == kAppCacheInternalsPath || kAllAboutPaths[i] == kBlobInternalsPath || kAllAboutPaths[i] == kCachePath || +#if defined(OS_WIN) + kAllAboutPaths[i] == kConflictsPath || +#endif kAllAboutPaths[i] == kFlagsPath || kAllAboutPaths[i] == kNetInternalsPath || kAllAboutPaths[i] == kPluginsPath) { @@ -305,8 +315,8 @@ std::string AboutNetwork(const std::string& query) { class AboutDnsHandler : public base::RefCountedThreadSafe<AboutDnsHandler> { public: static void Start(AboutSource* source, int request_id) { - scoped_refptr<AboutDnsHandler> handler = - new AboutDnsHandler(source, request_id); + scoped_refptr<AboutDnsHandler> handler( + new AboutDnsHandler(source, request_id)); handler->StartOnUIThread(); } @@ -762,28 +772,38 @@ std::string AboutGpu() { html.append("</body></html> "); } else { html.append("<html><head><title>About GPU</title></head><body>\n"); - html.append("<h2>GPU Information</h2><ul>\n"); - html.append("<li><strong>Vendor ID:</strong> "); + html.append("<h2>GPU Information</h2>\n"); + html.append("<table><tr>"); + html.append("<td><strong>Initialization time</strong></td><td>"); + html.append(base::Int64ToString( + gpu_info.initialization_time().InMilliseconds())); + html.append("</td></tr><tr><td>"); + html.append("<strong>Vendor ID</strong></td><td>"); html.append(base::StringPrintf("0x%04x", gpu_info.vendor_id())); - html.append("<li><strong>Device ID:</strong> "); + html.append("</td></tr><tr><td>"); + html.append("<strong>Device ID</strong></td><td>"); html.append(base::StringPrintf("0x%04x", gpu_info.device_id())); - html.append("<li><strong>Driver Version:</strong> "); + html.append("</td></tr><tr><td>"); + html.append("<strong>Driver Version</strong></td><td>"); html.append(WideToASCII(gpu_info.driver_version()).c_str()); - html.append("<li><strong>Pixel Shader Version:</strong> "); + html.append("</td></tr><tr><td>"); + html.append("<strong>Pixel Shader Version</strong></td><td>"); + html.append(VersionNumberToString(gpu_info.pixel_shader_version()).c_str()); + html.append("</td></tr><tr><td>"); + html.append("<strong>Vertex Shader Version</strong></td><td>"); html.append(VersionNumberToString( - gpu_info.pixel_shader_version()).c_str()); - html.append("<li><strong>Vertex Shader Version:</strong> "); - html.append(VersionNumberToString( - gpu_info.vertex_shader_version()).c_str()); - html.append("<li><strong>GL Version:</strong> "); + gpu_info.vertex_shader_version()).c_str()); + html.append("</td></tr><tr><td>"); + html.append("<strong>GL Version</strong></td><td>"); html.append(VersionNumberToString(gpu_info.gl_version()).c_str()); + html.append("</td></tr></table>"); #if defined(OS_WIN) - html.append("<li><strong>DirectX Diagnostics:</strong> "); + html.append("<h2>DirectX Diagnostics</h2>"); DxDiagNodeToHTML(&html, gpu_info.dx_diagnostics()); #endif - html.append("</ul></body></html> "); + html.append("</body></html>"); } return html; } @@ -1085,6 +1105,14 @@ bool WillHandleBrowserAboutURL(GURL* url, Profile* profile) { return true; } +#if defined(OS_WIN) + // Rewrite about:conflicts/* URLs to chrome://conflicts/* + if (StartsWithAboutSpecifier(*url, chrome::kAboutConflicts)) { + *url = GURL(chrome::kChromeUIConflictsURL); + return true; + } +#endif + // Rewrite about:flags and about:vaporware to chrome://flags/. if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutFlagsURL) || LowerCaseEqualsASCII(url->spec(), chrome::kAboutVaporwareURL)) { @@ -1120,11 +1148,11 @@ bool WillHandleBrowserAboutURL(GURL* url, Profile* profile) { // Handle URLs to wreck the gpu process. if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutGpuCrashURL)) { - GpuProcessHost::SendAboutGpuCrash(); + GpuProcessHostUIShim::Get()->SendAboutGpuCrash(); return true; } if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutGpuHangURL)) { - GpuProcessHost::SendAboutGpuHang(); + GpuProcessHostUIShim::Get()->SendAboutGpuHang(); return true; } diff --git a/chrome/browser/browser_browsertest.cc b/chrome/browser/browser_browsertest.cc index 981d07c..c63d6b8 100644 --- a/chrome/browser/browser_browsertest.cc +++ b/chrome/browser/browser_browsertest.cc @@ -9,11 +9,12 @@ #include "base/file_path.h" #include "base/sys_info.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/app_modal_dialog.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_init.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/defaults.h" @@ -156,7 +157,7 @@ class BrowserTest : public ExtensionBrowserTest { } // Returns the app extension aptly named "App Test". - Extension* GetExtension() { + const Extension* GetExtension() { const ExtensionList* extensions = browser()->profile()->GetExtensionsService()->extensions(); for (size_t i = 0; i < extensions->size(); ++i) { @@ -206,10 +207,7 @@ IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_JavascriptAlertActivatesTab) { GURL url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory), FilePath(kTitle1File))); ui_test_utils::NavigateToURL(browser(), url); - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - params.index = 0; - browser()->AddTabWithURL(¶ms); - EXPECT_EQ(browser(), params.target); + AddTabAtIndex(0, url, PageTransition::TYPED); EXPECT_EQ(2, browser()->tab_count()); EXPECT_EQ(0, browser()->selected_index()); TabContents* second_tab = browser()->GetTabContentsAt(1); @@ -233,12 +231,8 @@ IN_PROC_BROWSER_TEST_F(BrowserTest, ThirtyFourTabs) { FilePath(kTitle2File))); // There is one initial tab. - for (int ix = 0; ix != 33; ++ix) { - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - params.index = 0; - browser()->AddTabWithURL(¶ms); - EXPECT_EQ(browser(), params.target); - } + for (int ix = 0; ix != 33; ++ix) + browser()->AddSelectedTabWithURL(url, PageTransition::TYPED); EXPECT_EQ(34, browser()->tab_count()); // See browser\renderer_host\render_process_host.cc for the algorithm to @@ -426,7 +420,7 @@ IN_PROC_BROWSER_TEST_F(BrowserTest, TabClosingWhenRemovingExtension) { ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/"))); - Extension* extension_app = GetExtension(); + const Extension* extension_app = GetExtension(); ui_test_utils::NavigateToURL(browser(), url); @@ -509,7 +503,7 @@ IN_PROC_BROWSER_TEST_F(BrowserTest, RestorePinnedTabs) { GURL url(test_server()->GetURL("empty.html")); TabStripModel* model = browser()->tabstrip_model(); ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/"))); - Extension* extension_app = GetExtension(); + const Extension* extension_app = GetExtension(); ui_test_utils::NavigateToURL(browser(), url); TabContents* app_contents = new TabContents(browser()->profile(), NULL, MSG_ROUTING_NONE, NULL, NULL); @@ -564,15 +558,9 @@ IN_PROC_BROWSER_TEST_F(BrowserTest, RestorePinnedTabs) { } #endif // !defined(OS_CHROMEOS) -#if defined(OS_CHROMEOS) -// crosbug.com/7773 -#define MAYBE_CloseWithAppMenuOpen DISABLED_CloseWithAppMenuOpen -#else -#define MAYBE_CloseWithAppMenuOpen CloseWithAppMenuOpen -#endif // This test verifies we don't crash when closing the last window and the app // menu is showing. -IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_CloseWithAppMenuOpen) { +IN_PROC_BROWSER_TEST_F(BrowserTest, CloseWithAppMenuOpen) { if (browser_defaults::kBrowserAliveWithNoWindows) return; diff --git a/chrome/browser/browser_commands_unittest.cc b/chrome/browser/browser_commands_unittest.cc index c5d0f57..667ee35 100644 --- a/chrome/browser/browser_commands_unittest.cc +++ b/chrome/browser/browser_commands_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" diff --git a/chrome/browser/browser_focus_uitest.cc b/chrome/browser/browser_focus_uitest.cc index ea67967..d9771db 100644 --- a/chrome/browser/browser_focus_uitest.cc +++ b/chrome/browser/browser_focus_uitest.cc @@ -47,9 +47,9 @@ // TODO(jcampan): http://crbug.com/23683 #define MAYBE_TabsRememberFocusFindInPage FAILS_TabsRememberFocusFindInPage #elif defined(OS_MACOSX) -// TODO(suzhe): http://crbug.com/49738 (following two tests) -#define MAYBE_FocusTraversal FAILS_FocusTraversal -#define MAYBE_FocusTraversalOnInterstitial FAILS_FocusTraversalOnInterstitial +// TODO(suzhe): http://crbug.com/60973 (following two tests) +#define MAYBE_FocusTraversal DISABLED_FocusTraversal +#define MAYBE_FocusTraversalOnInterstitial DISABLED_FocusTraversalOnInterstitial // TODO(suzhe): http://crbug.com/49737 #define MAYBE_TabsRememberFocusFindInPage FAILS_TabsRememberFocusFindInPage #elif defined(OS_WIN) @@ -217,11 +217,8 @@ IN_PROC_BROWSER_TEST_F(BrowserFocusTest, TabsRememberFocus) { ui_test_utils::NavigateToURL(browser(), url); // Create several tabs. - for (int i = 0; i < 4; ++i) { - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - browser()->AddTabWithURL(¶ms); - EXPECT_EQ(browser(), params.target); - } + for (int i = 0; i < 4; ++i) + browser()->AddSelectedTabWithURL(url, PageTransition::TYPED); // Alternate focus for the tab. const bool kFocusPage[3][5] = { @@ -296,9 +293,7 @@ IN_PROC_BROWSER_TEST_F(BrowserFocusTest, MAYBE_TabsRememberFocusFindInPage) { browser()->FocusLocationBar(); // Create a 2nd tab. - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - browser()->AddTabWithURL(¶ms); - EXPECT_EQ(browser(), params.target); + browser()->AddSelectedTabWithURL(url, PageTransition::TYPED); // Focus should be on the recently opened tab page. ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW)); @@ -760,15 +755,8 @@ IN_PROC_BROWSER_TEST_F(BrowserFocusTest, FocusOnReload) { ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW)); } -#if (defined(OS_CHROMEOS) || defined(OS_LINUX)) && !defined(NDEBUG) -// Hangy, http://crbug.com/50025. -#define MAYBE_FocusOnReloadCrashedTab DISABLED_FocusOnReloadCrashedTab -#else -#define MAYBE_FocusOnReloadCrashedTab FocusOnReloadCrashedTab -#endif - // Tests that focus goes where expected when using reload on a crashed tab. -IN_PROC_BROWSER_TEST_F(BrowserFocusTest, MAYBE_FocusOnReloadCrashedTab) { +IN_PROC_BROWSER_TEST_F(BrowserFocusTest, DISABLED_FocusOnReloadCrashedTab) { ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); ASSERT_TRUE(test_server()->Start()); diff --git a/chrome/browser/browser_init.cc b/chrome/browser/browser_init.cc index 731e405..f28029f 100644 --- a/chrome/browser/browser_init.cc +++ b/chrome/browser/browser_init.cc @@ -15,12 +15,14 @@ #include "base/path_service.h" #include "base/scoped_ptr.h" #include "base/string_number_conversions.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/automation/automation_provider.h" #include "chrome/browser/automation/automation_provider_list.h" #include "chrome/browser/automation/chrome_frame_automation_provider.h" #include "chrome/browser/automation/testing_automation_provider.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/browser_window.h" @@ -402,6 +404,10 @@ bool BrowserInit::LaunchBrowser(const CommandLine& command_line, // of what window has focus. chromeos::WmMessageListener::instance(); + // Create the SystemKeyEventListener so it can listen for system keyboard + // messages regardless of focus. + chromeos::SystemKeyEventListener::instance(); + // Create the WmOverviewController so it can register with the listener. chromeos::WmOverviewController::instance(); @@ -435,15 +441,13 @@ bool BrowserInit::LaunchBrowser(const CommandLine& command_line, static chromeos::NetworkMessageObserver* network_message_observer = new chromeos::NetworkMessageObserver(profile); - chromeos::CrosLibrary::Get()->GetNetworkLibrary()->AddObserver( - network_message_observer); - - chromeos::CrosLibrary::Get()->GetNetworkLibrary()->AddObserver( - chromeos::NetworkStateNotifier::Get()); + chromeos::CrosLibrary::Get()->GetNetworkLibrary() + ->AddNetworkManagerObserver(network_message_observer); + chromeos::CrosLibrary::Get()->GetNetworkLibrary() + ->AddCellularDataPlanObserver(network_message_observer); - // Creates the SystemKeyEventListener to listen for keypress messages - // regardless of what window has focus. - chromeos::SystemKeyEventListener::instance(); + chromeos::CrosLibrary::Get()->GetNetworkLibrary() + ->AddNetworkManagerObserver(chromeos::NetworkStateNotifier::Get()); } #endif return true; @@ -764,16 +768,17 @@ Browser* BrowserInit::LaunchWithProfile::OpenTabsInBrowser( add_types |= TabStripModel::ADD_PINNED; int index = browser->GetIndexForInsertionDuringRestore(i); - Browser::AddTabWithURLParams params(tabs[i].url, - PageTransition::START_PAGE); - params.index = index; - params.add_types = add_types; + browser::NavigateParams params(browser, tabs[i].url, + PageTransition::START_PAGE); + params.disposition = first_tab ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; + params.tabstrip_index = index; + params.tabstrip_add_types = add_types; params.extension_app_id = tabs[i].app_id; - TabContents* tab = browser->AddTabWithURL(¶ms); + browser::Navigate(¶ms); if (profile_ && first_tab && process_startup) { - AddCrashedInfoBarIfNecessary(tab); - AddBadFlagsInfoBarIfNecessary(tab); + AddCrashedInfoBarIfNecessary(params.target_contents); + AddBadFlagsInfoBarIfNecessary(params.target_contents); } first_tab = false; @@ -851,7 +856,14 @@ std::vector<GURL> BrowserInit::LaunchWithProfile::GetURLsFromCommandLine( TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring()))); } else { // This will create a file URL or a regular URL. - GURL url(URLFixerUpper::FixupRelativeFile(cur_dir_, param)); + // This call can (in rare circumstances) block the UI thread. + // Allow it until this bug is fixed. + // http://code.google.com/p/chromium/issues/detail?id=60641 + GURL url; + { + base::ThreadRestrictions::ScopedAllowIO allow_io; + url = URLFixerUpper::FixupRelativeFile(cur_dir_, param); + } // Exclude dangerous schemes. if (url.is_valid()) { ChildProcessSecurityPolicy *policy = diff --git a/chrome/browser/browser_init.h b/chrome/browser/browser_init.h index d5cb393..28ede89 100644 --- a/chrome/browser/browser_init.h +++ b/chrome/browser/browser_init.h @@ -6,194 +6,8 @@ #define CHROME_BROWSER_BROWSER_INIT_H_ #pragma once -#include <string> -#include <vector> - -#include "base/basictypes.h" -#include "base/file_path.h" -#include "base/gtest_prod_util.h" -#include "googleurl/src/gurl.h" - -class Browser; -class CommandLine; -class GURL; -class Profile; -class TabContents; - -// class containing helpers for BrowserMain to spin up a new instance and -// initialize the profile. -class BrowserInit { - public: - BrowserInit(); - ~BrowserInit(); - - // Adds a url to be opened during first run. This overrides the standard - // tabs shown at first run. - void AddFirstRunTab(const GURL& url); - - // This function is equivalent to ProcessCommandLine but should only be - // called during actual process startup. - bool Start(const CommandLine& cmd_line, const FilePath& cur_dir, - Profile* profile, int* return_code) { - return ProcessCmdLineImpl(cmd_line, cur_dir, true, profile, return_code, - this); - } - - // This function performs command-line handling and is invoked when process - // starts as well as when we get a start request from another process (via the - // WM_COPYDATA message). |command_line| holds the command line we need to - // process - either from this process or from some other one (if - // |process_startup| is true and we are being called from - // ProcessSingleton::OnCopyData). - static bool ProcessCommandLine(const CommandLine& cmd_line, - const FilePath& cur_dir, bool process_startup, - Profile* profile, int* return_code) { - return ProcessCmdLineImpl(cmd_line, cur_dir, process_startup, profile, - return_code, NULL); - } - - template <class AutomationProviderClass> - static void CreateAutomationProvider(const std::string& channel_id, - Profile* profile, - size_t expected_tabs); - - // Returns true if the browser is coming up. - static bool InProcessStartup(); - - // Launches a browser window associated with |profile|. |command_line| should - // be the command line passed to this process. |cur_dir| can be empty, which - // implies that the directory of the executable should be used. - // |process_startup| indicates whether this is the first browser. - bool LaunchBrowser(const CommandLine& command_line, Profile* profile, - const FilePath& cur_dir, bool process_startup, - int* return_code); - - // LaunchWithProfile --------------------------------------------------------- - // - // Assists launching the application and appending the initial tabs for a - // browser window. - - class LaunchWithProfile { - public: - // Used by OpenTabsInBrowser. - struct Tab { - Tab(); - ~Tab(); - - // The url to load. - GURL url; - - // If true, the tab corresponds to an app an |app_id| gives the id of the - // app. - bool is_app; - - // True if the is tab pinned. - bool is_pinned; - - // Id of the app. - std::string app_id; - }; - - // There are two ctors. The first one implies a NULL browser_init object - // and thus no access to distribution-specific first-run behaviors. The - // second one is always called when the browser starts even if it is not - // the first run. - LaunchWithProfile(const FilePath& cur_dir, const CommandLine& command_line); - LaunchWithProfile(const FilePath& cur_dir, const CommandLine& command_line, - BrowserInit* browser_init); - ~LaunchWithProfile(); - - // Creates the necessary windows for startup. Returns true on success, - // false on failure. process_startup is true if Chrome is just - // starting up. If process_startup is false, it indicates Chrome was - // already running and the user wants to launch another instance. - bool Launch(Profile* profile, bool process_startup); - - // Convenience for OpenTabsInBrowser that converts |urls| into a set of - // Tabs. - Browser* OpenURLsInBrowser(Browser* browser, - bool process_startup, - const std::vector<GURL>& urls); - - // Creates a tab for each of the Tabs in |tabs|. If browser is non-null - // and a tabbed browser, the tabs are added to it. Otherwise a new tabbed - // browser is created and the tabs are added to it. The browser the tabs - // are added to is returned, which is either |browser| or the newly created - // browser. - Browser* OpenTabsInBrowser(Browser* browser, - bool process_startup, - const std::vector<Tab>& tabs); - - private: - FRIEND_TEST_ALL_PREFIXES(BrowserTest, RestorePinnedTabs); - - // If the process was launched with the web application command line flags, - // e.g. --app=http://www.google.com/ or --app_id=... return true. - // In this case |app_url| or |app_id| are populated if they're non-null. - bool IsAppLaunch(std::string* app_url, std::string* app_id); - - // If IsAppLaunch is true, tries to open an application window. - // If the app is specified to start in a tab, or IsAppLaunch is false, - // returns false to specify default processing. - bool OpenApplicationWindow(Profile* profile); - - // Invoked from OpenURLsInBrowser to handle processing of urls. This may - // do any of the following: - // . Invoke ProcessStartupURLs if |process_startup| is true. - // . Restore the last session if necessary. - // . Open the urls directly. - void ProcessLaunchURLs(bool process_startup, - const std::vector<GURL>& urls_to_open); - - // Does the following: - // . If the user's startup pref is to restore the last session (or the - // command line flag is present to force using last session), it is - // restored, and true is returned. - // . Attempts to restore any pinned tabs from last run of chrome and: - // . If urls_to_open is non-empty, they are opened and true is returned. - // . If the user's startup pref is to launch a specific set of URLs they - // are opened. - // - // Otherwise false is returned, which indicates the caller must create a - // new browser. - bool ProcessStartupURLs(const std::vector<GURL>& urls_to_open); - - // If the last session didn't exit cleanly and tab is a web contents tab, - // an infobar is added allowing the user to restore the last session. - void AddCrashedInfoBarIfNecessary(TabContents* tab); - - // If we have been started with unsupported flags like --single-process, - // politely nag the user about it. - void AddBadFlagsInfoBarIfNecessary(TabContents* tab); - - // Returns the list of URLs to open from the command line. The returned - // vector is empty if the user didn't specify any URLs on the command line. - std::vector<GURL> GetURLsFromCommandLine(Profile* profile); - - // Adds additional startup URLs to the specified vector. - void AddStartupURLs(std::vector<GURL>* startup_urls) const; - - // Checks whether Chrome is still the default browser (unless the user - // previously instructed not to do so) and warns the user if it is not. - void CheckDefaultBrowser(Profile* profile); - - const FilePath cur_dir_; - const CommandLine& command_line_; - Profile* profile_; - BrowserInit* browser_init_; - DISALLOW_COPY_AND_ASSIGN(LaunchWithProfile); - }; - - private: - static bool ProcessCmdLineImpl(const CommandLine& command_line, - const FilePath& cur_dir, bool process_startup, - Profile* profile, int* return_code, - BrowserInit* browser_init); - - // Additional tabs to open during first run. - std::vector<GURL> first_run_tabs_; - - DISALLOW_COPY_AND_ASSIGN(BrowserInit); -}; +#include "chrome/browser/ui/browser_init.h" +// TODO(beng): remove this file once all includes have been updated. #endif // CHROME_BROWSER_BROWSER_INIT_H_ + diff --git a/chrome/browser/browser_keyevents_browsertest.cc b/chrome/browser/browser_keyevents_browsertest.cc index 6e55f3e..e992cea 100644 --- a/chrome/browser/browser_keyevents_browsertest.cc +++ b/chrome/browser/browser_keyevents_browsertest.cc @@ -501,14 +501,7 @@ IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, CommandKeyEvents) { } #endif -#if defined(OS_WIN) -// Tests may fail on windows: http://crbug.com/55713 -#define MAYBE_AccessKeys FLAKY_AccessKeys -#else -#define MAYBE_AccessKeys AccessKeys -#endif - -IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, MAYBE_AccessKeys) { +IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, AccessKeys) { #if defined(OS_MACOSX) // On Mac, access keys use ctrl+alt modifiers. static const KeyEventTestData kTestAccessA = { diff --git a/chrome/browser/browser_list.h b/chrome/browser/browser_list.h index 0d680ae..5ddc907 100644 --- a/chrome/browser/browser_list.h +++ b/chrome/browser/browser_list.h @@ -6,230 +6,8 @@ #define CHROME_BROWSER_BROWSER_LIST_H_ #pragma once -#include <vector> - -#include "base/observer_list.h" -#include "chrome/browser/browser.h" - -// Stores a list of all Browser objects. -class BrowserList { - public: - typedef std::vector<Browser*> BrowserVector; - typedef BrowserVector::iterator iterator; - typedef BrowserVector::const_iterator const_iterator; - typedef BrowserVector::const_reverse_iterator const_reverse_iterator; - - // It is not allowed to change the global window list (add or remove any - // browser windows while handling observer callbacks. - class Observer { - public: - // Called immediately after a browser is added to the list - virtual void OnBrowserAdded(const Browser* browser) = 0; - - // Called immediately after a browser is removed from the list - virtual void OnBrowserRemoved(const Browser* browser) = 0; - - // Called immediately after a browser is set active (SetLastActive) - virtual void OnBrowserSetLastActive(const Browser* browser) {} - - protected: - virtual ~Observer() {} - }; - - // Adds and removes browsers from the global list. The browser object should - // be valid BEFORE these calls (for the benefit of observers), so notify and - // THEN delete the object. - static void AddBrowser(Browser* browser); - static void RemoveBrowser(Browser* browser); - - static void AddObserver(Observer* observer); - static void RemoveObserver(Observer* observer); - - // Called by Browser objects when their window is activated (focused). This - // allows us to determine what the last active Browser was. - static void SetLastActive(Browser* browser); - - // Returns the Browser object whose window was most recently active. If the - // most recently open Browser's window was closed, returns the first Browser - // in the list. If no Browsers exist, returns NULL. - // - // WARNING: this is NULL until a browser becomes active. If during startup - // a browser does not become active (perhaps the user launches Chrome, then - // clicks on another app before the first browser window appears) then this - // returns NULL. - // WARNING #2: this will always be NULL in unit tests run on the bots. - static Browser* GetLastActive(); - - // Identical in behavior to GetLastActive(), except that the most recently - // open browser owned by |profile| is returned. If none exist, returns NULL. - // WARNING: see warnings in GetLastActive(). - static Browser* GetLastActiveWithProfile(Profile *profile); - - // Find an existing browser window with the provided type. Searches in the - // order of last activation. Only browsers that have been active can be - // returned. If |match_incognito| is true, will match a browser with either - // a regular or incognito profile that matches the given one. Returns NULL if - // no such browser currently exists. - static Browser* FindBrowserWithType(Profile* p, Browser::Type t, - bool match_incognito); - - // Find an existing browser window that can provide the specified type (this - // uses Browser::CanSupportsWindowFeature, not - // Browser::SupportsWindowFeature). This searches in the order of last - // activation. Only browsers that have been active can be returned. Returns - // NULL if no such browser currently exists. - static Browser* FindBrowserWithFeature(Profile* p, - Browser::WindowFeature feature); - - // Find an existing browser window with the provided profile. Searches in the - // order of last activation. Only browsers that have been active can be - // returned. Returns NULL if no such browser currently exists. - static Browser* FindBrowserWithProfile(Profile* p); - - // Find an existing browser with the provided ID. Returns NULL if no such - // browser currently exists. - static Browser* FindBrowserWithID(SessionID::id_type desired_id); - - // Checks if the browser can be automatically restarted to install upgrades - // The browser can be automatically restarted when: - // 1. It's in the background mode (no visible windows). - // 2. An update exe is present in the install folder. - static bool CanRestartForUpdate(); - - // Closes all browsers and exits. This is equivalent to - // CloseAllBrowsers(true) on platforms where the application exits when no - // more windows are remaining. On other platforms (the Mac), this will - // additionally exit the application. - static void CloseAllBrowsersAndExit(); - - // Closes all browsers. If the session is ending the windows are closed - // directly. Otherwise the windows are closed by way of posting a WM_CLOSE - // message. - static void CloseAllBrowsers(); - - // Begins shutdown of the application when the Windows session is ending. - static void WindowsSessionEnding(); - - // Returns true if there is at least one Browser with the specified profile. - static bool HasBrowserWithProfile(Profile* profile); - - // Tells the BrowserList to keep the application alive after the last Browser - // closes. This is implemented as a count, so callers should pair their calls - // to StartKeepAlive() with matching calls to EndKeepAlive() when they no - // longer need to keep the application running. - static void StartKeepAlive(); - - // Stops keeping the application alive after the last Browser is closed. - // Should match a previous call to StartKeepAlive(). - static void EndKeepAlive(); - - // Returns true if application will continue running after the last Browser - // closes. - static bool WillKeepAlive(); - - static const_iterator begin() { return browsers_.begin(); } - static const_iterator end() { return browsers_.end(); } - - static bool empty() { return browsers_.empty(); } - static size_t size() { return browsers_.size(); } - - // Returns iterated access to list of open browsers ordered by when - // they were last active. The underlying data structure is a vector - // and we push_back on recent access so a reverse iterator gives the - // latest accessed browser first. - static const_reverse_iterator begin_last_active() { - return last_active_browsers_.rbegin(); - } - - static const_reverse_iterator end_last_active() { - return last_active_browsers_.rend(); - } - - // Return the number of browsers with the following profile which are - // currently open. - static size_t GetBrowserCount(Profile* p); - - // Return the number of browsers with the following profile and type which are - // currently open. - static size_t GetBrowserCountForType(Profile* p, Browser::Type type); - - // Returns true if at least one off the record session is active. - static bool IsOffTheRecordSessionActive(); - - // Called once there are no more browsers open and the application is exiting. - static void AllBrowsersClosedAndAppExiting(); - - private: - // Helper method to remove a browser instance from a list of browsers - static void RemoveBrowserFrom(Browser* browser, BrowserVector* browser_list); - - static BrowserVector browsers_; - static BrowserVector last_active_browsers_; - static ObserverList<Observer> observers_; - - // Counter of calls to StartKeepAlive(). If non-zero, the application will - // continue running after the last browser has exited. - static int keep_alive_count_; -}; - -class TabContents; - -// Iterates through all web view hosts in all browser windows. Because the -// renderers act asynchronously, getting a host through this interface does -// not guarantee that the renderer is ready to go. Doing anything to affect -// browser windows or tabs while iterating may cause incorrect behavior. -// -// Example: -// for (TabContentsIterator iterator; !iterator.done(); ++iterator) { -// TabContents* cur = *iterator; -// -or- -// iterator->operationOnTabContents(); -// ... -// } -class TabContentsIterator { - public: - TabContentsIterator(); - - // Returns true if we are past the last Browser. - bool done() const { - return cur_ == NULL; - } - - // Returns the current TabContents, valid as long as !Done() - TabContents* operator->() const { - return cur_; - } - TabContents* operator*() const { - return cur_; - } - - // Incrementing operators, valid as long as !Done() - TabContents* operator++() { // ++preincrement - Advance(); - return cur_; - } - TabContents* operator++(int) { // postincrement++ - TabContents* tmp = cur_; - Advance(); - return tmp; - } - - private: - // Loads the next host into Cur. This is designed so that for the initial - // call when browser_iterator_ points to the first browser and - // web_view_index_ is -1, it will fill the first host. - void Advance(); - - // iterator over all the Browser objects - BrowserList::const_iterator browser_iterator_; - - // tab index into the current Browser of the current web view - int web_view_index_; - - // Current TabContents, or NULL if we're at the end of the list. This can - // be extracted given the browser iterator and index, but it's nice to cache - // this since the caller may access the current host many times. - TabContents* cur_; -}; +#include "chrome/browser/ui/browser_list.h" +// TODO(beng): remove this file once all includes have been updated. #endif // CHROME_BROWSER_BROWSER_LIST_H_ + diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index eb1ce60..90f7ce3 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -14,6 +14,7 @@ #include "app/system_monitor.h" #include "base/at_exit.h" #include "base/command_line.h" +#include "base/debug/trace_event.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/mac/scoped_nsautorelease_pool.h" @@ -27,8 +28,8 @@ #include "base/string_split.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" +#include "base/thread_restrictions.h" #include "base/time.h" -#include "base/trace_event.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "build/build_config.h" @@ -62,9 +63,9 @@ #include "chrome/browser/profile.h" #include "chrome/browser/profile_manager.h" #include "chrome/browser/renderer_host/resource_dispatcher_host.h" +#include "chrome/browser/search_engines/search_engine_type.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" -#include "chrome/browser/search_engines/template_url_prepopulate_data.h" #include "chrome/browser/service/service_process_control.h" #include "chrome/browser/service/service_process_control_manager.h" #include "chrome/browser/shell_integration.h" @@ -93,6 +94,8 @@ #include "net/http/http_stream_factory.h" #include "net/socket/client_socket_pool_base.h" #include "net/socket/client_socket_pool_manager.h" +#include "net/socket/tcp_client_socket.h" +#include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" #if defined(USE_LINUX_BREAKPAD) @@ -194,6 +197,13 @@ void BrowserMainParts::EarlyInitialization() { net::SSLConfigService::AllowMITMProxies(); if (parsed_command_line().HasSwitch(switches::kEnableSnapStart)) net::SSLConfigService::EnableSnapStart(); + if (parsed_command_line().HasSwitch( + switches::kEnableDNSCertProvenanceChecking)) { + net::SSLConfigService::EnableDNSCertProvenanceChecking(); + } + + if (parsed_command_line().HasSwitch(switches::kEnableTcpFastOpen)) + net::set_tcp_fastopen_enabled(true); PostEarlyInitialization(); } @@ -208,8 +218,8 @@ void BrowserMainParts::ConnectionFieldTrial() { const base::FieldTrial::Probability kConnectDivisor = 100; const base::FieldTrial::Probability kConnectProbability = 1; // 1% prob. - scoped_refptr<base::FieldTrial> connect_trial = - new base::FieldTrial("ConnCountImpact", kConnectDivisor); + scoped_refptr<base::FieldTrial> connect_trial( + new base::FieldTrial("ConnCountImpact", kConnectDivisor)); const int connect_5 = connect_trial->AppendGroup("conn_count_5", kConnectProbability); @@ -253,8 +263,8 @@ void BrowserMainParts::SocketTimeoutFieldTrial() { // 1% probability for all experimental settings. const base::FieldTrial::Probability kSocketTimeoutProbability = 1; - scoped_refptr<base::FieldTrial> socket_timeout_trial = - new base::FieldTrial("IdleSktToImpact", kIdleSocketTimeoutDivisor); + scoped_refptr<base::FieldTrial> socket_timeout_trial( + new base::FieldTrial("IdleSktToImpact", kIdleSocketTimeoutDivisor)); const int socket_timeout_5 = socket_timeout_trial->AppendGroup("idle_timeout_5", @@ -289,8 +299,8 @@ void BrowserMainParts::ProxyConnectionsFieldTrial() { // 25% probability const base::FieldTrial::Probability kProxyConnectionProbability = 1; - scoped_refptr<base::FieldTrial> proxy_connection_trial = - new base::FieldTrial("ProxyConnectionImpact", kProxyConnectionsDivisor); + scoped_refptr<base::FieldTrial> proxy_connection_trial( + new base::FieldTrial("ProxyConnectionImpact", kProxyConnectionsDivisor)); // The number of max sockets per group cannot be greater than the max number // of sockets per proxy server. We tried using 8, and it can easily @@ -340,8 +350,8 @@ void BrowserMainParts::SpdyFieldTrial() { const base::FieldTrial::Probability kSpdyDivisor = 100; // 10% to preclude SPDY. base::FieldTrial::Probability npnhttp_probability = 10; - scoped_refptr<base::FieldTrial> trial = - new base::FieldTrial("SpdyImpact", kSpdyDivisor); + scoped_refptr<base::FieldTrial> trial( + new base::FieldTrial("SpdyImpact", kSpdyDivisor)); // npn with only http support, no spdy. int npn_http_grp = trial->AppendGroup("npn_with_http", npnhttp_probability); // npn with spdy support. @@ -358,6 +368,14 @@ void BrowserMainParts::SpdyFieldTrial() { CHECK(!is_spdy_trial); } } + if (parsed_command_line().HasSwitch(switches::kMaxSpdyConcurrentStreams)) { + int value = 0; + base::StringToInt(parsed_command_line().GetSwitchValueASCII( + switches::kMaxSpdyConcurrentStreams), + &value); + if (value > 0) + net::SpdySession::set_max_concurrent_streams(value); + } } // If neither --enable-content-prefetch or --disable-content-prefetch @@ -370,8 +388,8 @@ void BrowserMainParts::PrefetchFieldTrial() { } else { const base::FieldTrial::Probability kPrefetchDivisor = 1000; const base::FieldTrial::Probability no_prefetch_probability = 500; - scoped_refptr<base::FieldTrial> trial = - new base::FieldTrial("Prefetch", kPrefetchDivisor); + scoped_refptr<base::FieldTrial> trial( + new base::FieldTrial("Prefetch", kPrefetchDivisor)); trial->AppendGroup("ContentPrefetchDisabled", no_prefetch_probability); const int yes_prefetch_grp = trial->AppendGroup("ContentPrefetchEnabled", @@ -397,9 +415,9 @@ void BrowserMainParts::ConnectBackupJobsFieldTrial() { const base::FieldTrial::Probability kConnectBackupJobsDivisor = 100; // 1% probability. const base::FieldTrial::Probability kConnectBackupJobsProbability = 1; - scoped_refptr<base::FieldTrial> trial = + scoped_refptr<base::FieldTrial> trial( new base::FieldTrial("ConnnectBackupJobs", - kConnectBackupJobsDivisor); + kConnectBackupJobsDivisor)); trial->AppendGroup("ConnectBackupJobsDisabled", kConnectBackupJobsProbability); const int connect_backup_jobs_enabled = @@ -469,6 +487,14 @@ void HandleTestParameters(const CommandLine& command_line) { void RunUIMessageLoop(BrowserProcess* browser_process) { TRACE_EVENT_BEGIN("BrowserMain:MESSAGE_LOOP", 0, ""); +#if !defined(OS_CHROMEOS) + // If the UI thread blocks, the whole UI is unresponsive. + // Do not allow disk IO from the UI thread. + // TODO(evanm): turn this on for all platforms. + // http://code.google.com/p/chromium/issues/detail?id=60211 + base::ThreadRestrictions::SetIOAllowed(false); +#endif + #if defined(TOOLKIT_VIEWS) views::AcceleratorHandler accelerator_handler; MessageLoopForUI::current()->Run(&accelerator_handler); @@ -784,9 +810,10 @@ class StubLogin : public chromeos::LoginStatusConsumer { } void OnLoginSuccess(const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests) { - chromeos::LoginUtils::Get()->CompleteLogin(username, credentials); + chromeos::LoginUtils::Get()->CompleteLogin(username, password, credentials); delete this; } @@ -1061,8 +1088,8 @@ int BrowserMain(const MainFunctionParams& parameters) { // for posting tasks via NewRunnableMethod. Its deleted when it goes out of // scope. Even though NewRunnableMethod does AddRef and Release, the object // will not be deleted after the Task is executed. - scoped_refptr<HistogramSynchronizer> histogram_synchronizer = - new HistogramSynchronizer(); + scoped_refptr<HistogramSynchronizer> histogram_synchronizer( + new HistogramSynchronizer()); // Initialize the prefs of the local state. browser::RegisterLocalState(local_state); @@ -1169,6 +1196,10 @@ int BrowserMain(const MainFunctionParams& parameters) { } #endif +#if defined(USE_X11) + SetBrowserX11ErrorHandlers(); +#endif + // Profile creation ---------------------------------------------------------- #if defined(OS_CHROMEOS) @@ -1196,24 +1227,11 @@ int BrowserMain(const MainFunctionParams& parameters) { VLOG(1) << "Relaunching browser for user: " << username; chromeos::UserManager::Get()->UserLoggedIn(username); - // Redirect logs. - FilePath user_data_dir; - PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); - ProfileManager* profile_manager = g_browser_process->profile_manager(); - // The default profile will have been changed because the ProfileManager - // will process the notification that the UserManager sends out. - - logging::RedirectChromeLogging( - user_data_dir.Append(profile_manager->GetCurrentProfileDir()), - *(CommandLine::ForCurrentProcess()), - logging::DELETE_OLD_LOG_FILE); + // Redirects Chrome logging to the user data dir. + logging::RedirectChromeLogging(parsed_command_line); } #endif -#if defined(USE_X11) - SetBrowserX11ErrorHandlers(); -#endif - // Modifies the current command line based on active experiments on // about:flags. Profile* profile = CreateProfile(parameters, user_data_dir); @@ -1366,8 +1384,8 @@ int BrowserMain(const MainFunctionParams& parameters) { // layout globally. base::FieldTrial::Probability kSDCH_DIVISOR = 1000; base::FieldTrial::Probability kSDCH_DISABLE_PROBABILITY = 1; // 0.1% prob. - scoped_refptr<base::FieldTrial> sdch_trial = - new base::FieldTrial("GlobalSdch", kSDCH_DIVISOR); + scoped_refptr<base::FieldTrial> sdch_trial( + new base::FieldTrial("GlobalSdch", kSDCH_DIVISOR)); // Use default of "" so that all domains are supported. std::string sdch_supported_domain(""); @@ -1509,15 +1527,15 @@ int BrowserMain(const MainFunctionParams& parameters) { profile->GetTemplateURLModel()->GetDefaultSearchProvider(); // The default engine can be NULL if the administrator has disabled // default search. - TemplateURLPrepopulateData::SearchEngineType search_engine_type = + SearchEngineType search_engine_type = default_search_engine ? default_search_engine->search_engine_type() : - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER; + SEARCH_ENGINE_OTHER; // Record the search engine chosen. if (master_prefs.run_search_engine_experiment) { UMA_HISTOGRAM_ENUMERATION( "Chrome.SearchSelectExperiment", search_engine_type, - TemplateURLPrepopulateData::SEARCH_ENGINE_MAX); + SEARCH_ENGINE_MAX); // If the selection has been randomized, also record the winner by slot. if (master_prefs.randomize_search_engine_experiment) { size_t engine_pos = profile->GetTemplateURLModel()-> @@ -1529,7 +1547,7 @@ int BrowserMain(const MainFunctionParams& parameters) { UMA_HISTOGRAM_ENUMERATION( experiment_type, search_engine_type, - TemplateURLPrepopulateData::SEARCH_ENGINE_MAX); + SEARCH_ENGINE_MAX); } else { NOTREACHED() << "Invalid search engine selection slot."; } @@ -1538,7 +1556,7 @@ int BrowserMain(const MainFunctionParams& parameters) { UMA_HISTOGRAM_ENUMERATION( "Chrome.SearchSelectExempt", search_engine_type, - TemplateURLPrepopulateData::SEARCH_ENGINE_MAX); + SEARCH_ENGINE_MAX); } } #endif diff --git a/chrome/browser/browser_main_gtk.cc b/chrome/browser/browser_main_gtk.cc index 9f98f35..8790726 100644 --- a/chrome/browser/browser_main_gtk.cc +++ b/chrome/browser/browser_main_gtk.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// 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. @@ -7,7 +7,7 @@ #include "app/x11_util.h" #include "app/x11_util_internal.h" #include "base/command_line.h" -#include "base/debug_util.h" +#include "base/debug/debugger.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_main_gtk.h" #include "chrome/browser/browser_main_win.h" @@ -51,7 +51,7 @@ void RecordBreakpadStatusUMA(MetricsService* metrics) { #else metrics->RecordBreakpadRegistration(false); #endif - metrics->RecordBreakpadHasDebugger(DebugUtil::BeingDebugged()); + metrics->RecordBreakpadHasDebugger(base::debug::BeingDebugged()); } void WarnAboutMinimumSystemRequirements() { diff --git a/chrome/browser/browser_main_mac.mm b/chrome/browser/browser_main_mac.mm index a00c47b..37c5246 100644 --- a/chrome/browser/browser_main_mac.mm +++ b/chrome/browser/browser_main_mac.mm @@ -10,7 +10,7 @@ #include "app/l10n_util_mac.h" #include "app/resource_bundle.h" #include "base/command_line.h" -#include "base/debug_util.h" +#include "base/debug/debugger.h" #include "base/file_path.h" #include "base/mac_util.h" #include "base/nss_util.h" @@ -36,7 +36,7 @@ void DidEndMainMessageLoop() { void RecordBreakpadStatusUMA(MetricsService* metrics) { metrics->RecordBreakpadRegistration(IsCrashReporterEnabled()); - metrics->RecordBreakpadHasDebugger(DebugUtil::BeingDebugged()); + metrics->RecordBreakpadHasDebugger(base::debug::BeingDebugged()); } void WarnAboutMinimumSystemRequirements() { diff --git a/chrome/browser/browser_navigator.cc b/chrome/browser/browser_navigator.cc index 2950d1f..54edd14 100644 --- a/chrome/browser/browser_navigator.cc +++ b/chrome/browser/browser_navigator.cc @@ -22,6 +22,9 @@ namespace { // Returns the SiteInstance for |source_contents| if it represents the same // website as |url|, or NULL otherwise. |source_contents| cannot be NULL. SiteInstance* GetSiteInstance(TabContents* source_contents, const GURL& url) { + if (!source_contents) + return NULL; + // Don't use this logic when "--process-per-tab" is specified. if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerTab) && SiteInstance::IsSameWebSite(source_contents->profile(), @@ -98,41 +101,59 @@ Browser* GetBrowserForDisposition(browser::NavigateParams* params) { // If no source TabContents was specified, we use the selected one from the // target browser. This must happen first, before GetBrowserForDisposition() // has a chance to replace |params->browser| with another one. - if (!params->source_contents) + if (!params->source_contents && params->browser) params->source_contents = params->browser->GetSelectedTabContents(); + Profile* profile = + params->browser ? params->browser->profile() : params->profile; + switch (params->disposition) { case CURRENT_TAB: + if (!params->browser && profile) { + // We specified a profile instead of a browser; find or create one. + params->browser = Browser::GetOrCreateTabbedBrowser(profile); + } return params->browser; case SINGLETON_TAB: case NEW_FOREGROUND_TAB: case NEW_BACKGROUND_TAB: // See if we can open the tab in the window this navigator is bound to. - if (WindowCanOpenTabs(params->browser)) + if (params->browser && WindowCanOpenTabs(params->browser)) return params->browser; // Find a compatible window and re-execute this command in it. Otherwise // re-run with NEW_WINDOW. - return GetOrCreateBrowser(params->browser->profile()); + if (profile) + return GetOrCreateBrowser(profile); + return NULL; case NEW_POPUP: { // Make a new popup window. Coerce app-style if |params->browser| or the // |source| represents an app. Browser::Type type = Browser::TYPE_POPUP; - if (params->browser->type() == Browser::TYPE_APP || + if ((params->browser && params->browser->type() == Browser::TYPE_APP) || (params->source_contents && params->source_contents->is_app())) { type = Browser::TYPE_APP_POPUP; } - Browser* browser = new Browser(type, params->browser->profile()); - browser->set_override_bounds(params->window_bounds); - browser->CreateBrowserWindow(); - return browser; + if (profile) { + Browser* browser = new Browser(type, profile); + browser->set_override_bounds(params->window_bounds); + browser->CreateBrowserWindow(); + return browser; + } + return NULL; } case NEW_WINDOW: // Make a new normal browser window. - return Browser::Create(params->browser->profile()); + if (profile) { + Browser* browser = new Browser(Browser::TYPE_NORMAL, profile); + browser->CreateBrowserWindow(); + return browser; + } + return NULL; case OFF_THE_RECORD: // Make or find an incognito window. - return GetOrCreateBrowser( - params->browser->profile()->GetOffTheRecordProfile()); + if (profile) + return GetOrCreateBrowser(profile->GetOffTheRecordProfile()); + return NULL; // The following types all result in no navigation. case SUPPRESS_OPEN: case SAVE_TO_DISK: @@ -149,7 +170,9 @@ Browser* GetBrowserForDisposition(browser::NavigateParams* params) { void NormalizeDisposition(browser::NavigateParams* params) { // Calculate the WindowOpenDisposition if necessary. if (params->browser->tabstrip_model()->empty() && - params->disposition == NEW_BACKGROUND_TAB) { + (params->disposition == NEW_BACKGROUND_TAB || + params->disposition == CURRENT_TAB || + params->disposition == SINGLETON_TAB)) { params->disposition = NEW_FOREGROUND_TAB; } if (params->browser->profile()->IsOffTheRecord() && @@ -162,6 +185,13 @@ void NormalizeDisposition(browser::NavigateParams* params) { // background. if (params->disposition == NEW_BACKGROUND_TAB) params->tabstrip_add_types &= ~TabStripModel::ADD_SELECTED; + + // Code that wants to open a new window typically expects it to be shown + // automatically. + if (params->disposition == NEW_WINDOW || params->disposition == NEW_POPUP) { + params->show_window = true; + params->tabstrip_add_types |= TabStripModel::ADD_SELECTED; + } } // This class makes sure the Browser object held in |params| is made visible @@ -230,8 +260,8 @@ NavigateParams::NavigateParams( tabstrip_index(-1), tabstrip_add_types(TabStripModel::ADD_SELECTED), show_window(false), - browser(a_browser) { - DCHECK(browser); + browser(a_browser), + profile(NULL) { } NavigateParams::NavigateParams(Browser* a_browser, @@ -243,8 +273,8 @@ NavigateParams::NavigateParams(Browser* a_browser, tabstrip_index(-1), tabstrip_add_types(TabStripModel::ADD_SELECTED), show_window(false), - browser(a_browser) { - DCHECK(browser); + browser(a_browser), + profile(NULL) { } NavigateParams::~NavigateParams() { @@ -266,6 +296,13 @@ void Navigate(NavigateParams* params) { // Some dispositions need coercion to base types. NormalizeDisposition(params); + // Determine if the navigation was user initiated. If it was, we need to + // inform the target TabContents, and we may need to update the UI. + PageTransition::Type base_transition = + PageTransition::StripQualifier(params->transition); + bool user_initiated = base_transition == PageTransition::TYPED || + base_transition == PageTransition::AUTO_BOOKMARK; + // If no target TabContents was specified, we need to construct one if we are // supposed to target a new tab. if (!params->target_contents) { @@ -292,26 +329,25 @@ void Navigate(NavigateParams* params) { // ... otherwise if we're loading in the current tab, the target is the // same as the source. params->target_contents = params->source_contents; + DCHECK(params->target_contents); } - } - // Determine if the navigation was user initiated. If it was, we need to - // inform the target TabContents, and we may need to update the UI. - PageTransition::Type base_transition = - PageTransition::StripQualifier(params->transition); - bool user_initiated = base_transition == PageTransition::TYPED || - base_transition == PageTransition::AUTO_BOOKMARK; - if (user_initiated) { - RenderViewHostDelegate::BrowserIntegration* integration = - params->target_contents; - integration->OnUserGesture(); - } + if (user_initiated) { + RenderViewHostDelegate::BrowserIntegration* integration = + params->target_contents; + integration->OnUserGesture(); + } - // Perform the actual navigation. - GURL url = params->url.is_empty() ? params->browser->GetHomePage() - : params->url; - params->target_contents->controller().LoadURL(url, params->referrer, - params->transition); + // Perform the actual navigation. + GURL url = params->url.is_empty() ? params->browser->GetHomePage() + : params->url; + params->target_contents->controller().LoadURL(url, params->referrer, + params->transition); + } else { + // |target_contents| was specified non-NULL, and so we assume it has already + // been navigated appropriately. We need to do nothing more other than + // add it to the appropriate tabstrip. + } if (params->source_contents == params->target_contents) { // The navigation occurred in the source tab, so update the UI. @@ -325,6 +361,11 @@ void Navigate(NavigateParams* params) { // The navigation should re-select an existing tab in the target Browser. params->browser->SelectTabContentsAt(singleton_index, user_initiated); } else { + // If some non-default value is set for the index, we should tell the + // TabStripModel to respect it. + if (params->tabstrip_index != -1) + params->tabstrip_add_types |= TabStripModel::ADD_FORCE_INDEX; + // The navigation should insert a new tab into the target Browser. params->browser->tabstrip_model()->AddTabContents( params->target_contents, @@ -339,4 +380,3 @@ void Navigate(NavigateParams* params) { } } // namespace browser - diff --git a/chrome/browser/browser_navigator.h b/chrome/browser/browser_navigator.h index 277830e..eaeabbe 100644 --- a/chrome/browser/browser_navigator.h +++ b/chrome/browser/browser_navigator.h @@ -6,124 +6,8 @@ #define CHROME_BROWSER_BROWSER_NAVIGATOR_H_ #pragma once -#include <string> - -#include "chrome/common/page_transition_types.h" -#include "gfx/rect.h" -#include "googleurl/src/gurl.h" -#include "webkit/glue/window_open_disposition.h" - -class Browser; -class Profile; -class TabContents; - -namespace browser { - -// Parameters that tell Navigate() what to do. -// -// Some basic examples: -// -// Simple Navigate to URL in current tab: -// browser::NavigateParams params(browser, GURL("http://www.google.com/"), -// PageTransition::LINK); -// browser::Navigate(¶ms); -// -// Open bookmark in new background tab: -// browser::NavigateParams params(browser, url, PageTransition::AUTO_BOOKMARK); -// params.disposition = NEW_BACKGROUND_TAB; -// browser::Navigate(¶ms); -// -// Opens a popup TabContents: -// browser::NavigateParams params(browser, popup_contents); -// params.source_contents = source_contents; -// browser::Navigate(¶ms); -// -// See browser_navigator_browsertest.cc for more examples. -// -struct NavigateParams { - NavigateParams(Browser* browser, - const GURL& a_url, - PageTransition::Type a_transition); - NavigateParams(Browser* browser, TabContents* a_target_contents); - ~NavigateParams(); - - // The URL/referrer to be loaded. Can be empty if |contents| is specified - // non-NULL. - GURL url; - GURL referrer; - - // [in] A TabContents to be navigated or inserted into the target Browser's - // tabstrip. If NULL, |url| or the homepage will be used instead. - // Default is NULL. - // [out] The TabContents in which the navigation occurred or that was - // inserted. Guaranteed non-NULL except for note below: - // Note: If this field is set to NULL by the caller and Navigate() creates - // a new TabContents, this field will remain NULL and the TabContents - // deleted if the TabContents it created is not added to a TabStripModel - // before Navigate() returns. - TabContents* target_contents; - - // [in] The TabContents that initiated the Navigate() request if such context - // is necessary. Default is NULL, i.e. no context. - // [out] If NULL, this value will be set to the selected TabContents in the - // originating browser prior to the operation performed by Navigate(). - TabContents* source_contents; - - // The disposition requested by the navigation source. Default is - // CURRENT_TAB. - WindowOpenDisposition disposition; - - // The transition type of the navigation. Default is PageTransition::LINK - // when target_contents is specified in the constructor. - PageTransition::Type transition; - - // The index the caller would like the tab to be positioned at in the - // TabStrip. The actual index will be determined by the TabHandler in - // accordance with |add_types|. Defaults to -1 (allows the TabHandler to - // decide). - int tabstrip_index; - - // A bitmask of values defined in TabStripModel::AddTabTypes. Helps - // determine where to insert a new tab and whether or not it should be - // selected, among other properties. Default is ADD_SELECTED. - int tabstrip_add_types; - - // If non-empty, the new tab is an app tab. - std::string extension_app_id; - - // If non-empty, specifies the desired initial position and size of the - // window if |disposition| == NEW_POPUP. - // TODO(beng): Figure out if this can be used to create Browser windows - // for other callsites that use set_override_bounds, or - // remove this comment. - gfx::Rect window_bounds; - - // True if the target window should be made visible at the end of the call - // to Navigate(). Default is false. - bool show_window; - - // [in] Specifies a Browser object where the navigation could occur or the - // tab could be added. Navigate() is not obliged to use this Browser if - // it is not compatible with the operation being performed. Cannot be - // NULL since Navigate() uses this Browser's Profile. - // [out] Specifies the Browser object where the navigation occurred or the - // tab was added. Guaranteed non-NULL unless the disposition did not - // require a navigation, in which case this is set to NULL - // (SUPPRESS_OPEN, SAVE_TO_DISK, IGNORE_ACTION). - // Note: If |show_window| is set to false and a new Browser is created by - // Navigate(), the caller is responsible for showing it so that its - // window can assume responsibility for the Browser's lifetime (Browser - // objects are deleted when the user closes a visible browser window). - Browser* browser; - - private: - NavigateParams(); -}; - -// Navigates according to the configuration specified in |params|. -void Navigate(NavigateParams* params); - -} // namespace browser +#include "chrome/browser/ui/browser_navigator.h" +// TODO(beng): remove this file once all includes have been updated. #endif // CHROME_BROWSER_BROWSER_NAVIGATOR_H_ diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index b991455..6c384d8 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc @@ -615,7 +615,7 @@ void BrowserProcessImpl::CreateCacheThread() { created_cache_thread_ = true; scoped_ptr<base::Thread> thread( - new BrowserProcessSubThread(BrowserThread::CACHE)); + new BrowserThread(BrowserThread::CACHE)); base::Thread::Options options; options.message_loop_type = MessageLoop::TYPE_IO; if (!thread->StartWithOptions(options)) diff --git a/chrome/browser/browser_process_sub_thread.cc b/chrome/browser/browser_process_sub_thread.cc index 39729d0..0ddbf3b 100644 --- a/chrome/browser/browser_process_sub_thread.cc +++ b/chrome/browser/browser_process_sub_thread.cc @@ -28,7 +28,7 @@ void BrowserProcessSubThread::Init() { notification_service_ = new NotificationService; } -void BrowserProcessSubThread::CleanUp() { +void BrowserProcessSubThread::CleanUpAfterMessageLoopDestruction() { delete notification_service_; notification_service_ = NULL; diff --git a/chrome/browser/browser_process_sub_thread.h b/chrome/browser/browser_process_sub_thread.h index c2012cd..011cdf0 100644 --- a/chrome/browser/browser_process_sub_thread.h +++ b/chrome/browser/browser_process_sub_thread.h @@ -27,7 +27,7 @@ class BrowserProcessSubThread : public BrowserThread { protected: virtual void Init(); - virtual void CleanUp(); + virtual void CleanUpAfterMessageLoopDestruction(); private: // Each specialized thread has its own notification service. diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 6c6e7fe..da04f3a 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- This comment is only here because changes to resources are not picked up -without changes to the corresponding grd file. ete --> +without changes to the corresponding grd file. etaa --> <grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/browser_resources.h" type="rc_header"> @@ -11,6 +11,9 @@ without changes to the corresponding grd file. ete --> </outputs> <release seq="1"> <includes> + <if expr="os.find('win') != -1"> + <include name="IDR_ABOUT_CONFLICTS_HTML" file="resources\about_conflicts.html" flattenhtml="true" type="BINDATA" /> + </if> <if expr="os == 'linux2' or os.find('bsd') != -1"> <include name="IDR_ABOUT_MEMORY_HTML" file="resources\about_memory_linux.html" flattenhtml="true" type="BINDATA" /> </if> @@ -51,6 +54,7 @@ without changes to the corresponding grd file. ete --> <include name="IDR_SAFE_BROWSING_MALWARE_BLOCK" file="resources\safe_browsing_malware_block.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_SAFE_BROWSING_MULTIPLE_THREAT_BLOCK" file="resources\safe_browsing_multiple_threat_block.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_SAFE_BROWSING_PHISHING_BLOCK" file="resources\safe_browsing_phishing_block.html" flattenhtml="true" type="BINDATA" /> + <include name="IDR_SIGNIN_HTML" file="resources\browser_signin.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_SSL_ERROR_HTML" file="security\resources\ssl_error.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_SSL_ROAD_BLOCK_HTML" file="security\resources\ssl_roadblock.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_SYNC_CONFIGURE_HTML" file="sync\resources\configure.html" flattenhtml="true" type="BINDATA" /> @@ -60,6 +64,7 @@ without changes to the corresponding grd file. ete --> <include name="IDR_SYNC_SETUP_FLOW_HTML" file="sync\resources\setup_flow.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_REMOTING_SETUP_FLOW_HTML" file="remoting\resources\remoting_setup_flow.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_REMOTING_SETUP_DONE_HTML" file="remoting\resources\remoting_setup_done.html" flattenhtml="true" type="BINDATA" /> + <include name="IDR_TEXTFIELDS_HTML" file="resources\textfields.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_TRANSLATE_JS" file="resources\translate.js" type="BINDATA" /> <include name="IDR_BUGREPORT_HTML" file="resources\bug_report.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_BUGREPORT_HTML_INVALID" file="resources\bug_report_invalid.html" flattenhtml="true" type="BINDATA" /> @@ -69,6 +74,10 @@ without changes to the corresponding grd file. ete --> <include name="IDR_FILEBROWSE_HTML" file="resources\filebrowse.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_HOST_REGISTRATION_PAGE_HTML" file="resources\host_registration_page.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_IMAGEBURNER_HTML" file="resources\imageburner.html" flattenhtml="true" type="BINDATA" /> + <include name="IDR_KEYBOARD_OVERLAY_CSS" file="resources\keyboard_overlay.css" flattenhtml="true" type="BINDATA" /> + <include name="IDR_KEYBOARD_OVERLAY_DATA_JS" file="resources\keyboard_overlay_data.js" flattenhtml="true" type="BINDATA" /> + <include name="IDR_KEYBOARD_OVERLAY_HTML" file="resources\keyboard_overlay.html" flattenhtml="true" type="BINDATA" /> + <include name="IDR_KEYBOARD_OVERLAY_JS" file="resources\keyboard_overlay.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_MEDIAPLAYER_HTML" file="resources\mediaplayer.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_MEDIAPLAYERPLAYLIST_HTML" file="resources\playlist.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_MOBILE_SETUP_PAGE_HTML" file="resources\mobile_setup.html" flattenhtml="true" type="BINDATA" /> @@ -76,7 +85,6 @@ without changes to the corresponding grd file. ete --> <include name="IDR_OFFLINE_LOAD_HTML" file="resources\offline_load.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_OS_CREDITS_HTML" file="resources\about_os_credits.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_SLIDESHOW_HTML" file="resources\slideshow.html" flattenhtml="true" type="BINDATA" /> - <include name="IDR_TALK_APP_MANIFEST" file="resources\chat_manager\manifest.json" type="BINDATA" /> <include name="IDR_MENU_HTML" file="resources\menu.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_NETWORK_MENU_CSS" file="resources\network_menu.css" flattenhtml="true" type="BINDATA" /> <include name="IDR_NETWORK_MENU_JS" file="resources\network_menu.js" flattenhtml="true" type="BINDATA" /> diff --git a/chrome/browser/browser_shutdown.cc b/chrome/browser/browser_shutdown.cc index 98e0af7..8f1fb80 100644 --- a/chrome/browser/browser_shutdown.cc +++ b/chrome/browser/browser_shutdown.cc @@ -16,6 +16,7 @@ #include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/thread.h" +#include "base/thread_restrictions.h" #include "base/time.h" #include "build/build_config.h" #include "chrome/browser/about_flags.h" @@ -100,6 +101,12 @@ FilePath GetShutdownMsPath() { } void Shutdown() { + // During shutdown we will end up some blocking operations. But the + // work needs to get done and we're going to wait for them no matter + // what thread they're on, so don't worry about it slowing down + // shutdown. + base::ThreadRestrictions::SetIOAllowed(true); + // Unload plugins. This needs to happen on the IO thread. BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, diff --git a/chrome/browser/browser_signin.cc b/chrome/browser/browser_signin.cc new file mode 100644 index 0000000..0c5dc69 --- /dev/null +++ b/chrome/browser/browser_signin.cc @@ -0,0 +1,331 @@ +// 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/browser_signin.h" + +#include <string> +#include <vector> + +#include "app/resource_bundle.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/message_loop.h" +#include "base/singleton.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/dom_ui/chrome_url_data_manager.h" +#include "chrome/browser/dom_ui/constrained_html_ui.h" +#include "chrome/browser/dom_ui/dom_ui_util.h" +#include "chrome/browser/dom_ui/html_dialog_ui.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/sync_setup_flow.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" + +class BrowserSigninResourcesSource : public ChromeURLDataManager::DataSource { + public: + BrowserSigninResourcesSource() + : DataSource(chrome::kChromeUIDialogHost, MessageLoop::current()) { + } + + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + + virtual std::string GetMimeType(const std::string& path) const { + return "text/html"; + } + + private: + virtual ~BrowserSigninResourcesSource() {} + + DISALLOW_COPY_AND_ASSIGN(BrowserSigninResourcesSource); +}; + +void BrowserSigninResourcesSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + const char kSigninPath[] = "signin"; + + std::string response; + if (path == kSigninPath) { + const base::StringPiece html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_SIGNIN_HTML)); + DictionaryValue dict; + SetFontAndTextDirection(&dict); + response = jstemplate_builder::GetI18nTemplateHtml(html, &dict); + } + + scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); + html_bytes->data.resize(response.size()); + std::copy(response.begin(), response.end(), html_bytes->data.begin()); + SendResponse(request_id, html_bytes); +} + +class BrowserSigninHtml : public HtmlDialogUIDelegate, + public DOMMessageHandler { + public: + BrowserSigninHtml(BrowserSignin* signin, + string16 suggested_email, + string16 login_message); + virtual ~BrowserSigninHtml() {} + + // HtmlDialogUIDelegate implementation + virtual bool IsDialogModal() const { + return false; + }; + virtual std::wstring GetDialogTitle() const { + return L""; + } + virtual GURL GetDialogContentURL() const { + return GURL("chrome://dialog/signin"); + } + virtual void GetDOMMessageHandlers( + std::vector<DOMMessageHandler*>* handlers) const { + const DOMMessageHandler* handler = this; + handlers->push_back(const_cast<DOMMessageHandler*>(handler)); + } + virtual void GetDialogSize(gfx::Size* size) const { + size->set_width(600); + size->set_height(300); + } + virtual std::string GetDialogArgs() const { + return UTF16ToASCII(login_message_); + } + virtual void OnDialogClosed(const std::string& json_retval) { + closed_ = true; + signin_->Cancel(); + } + virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { + } + virtual bool ShouldShowDialogTitle() const { return true; } + + // DOMMessageHandler implementation. + virtual void RegisterMessages(); + + // Refreshes the UI, such as after an authentication error. + void ReloadUI(); + + // Method which calls into javascript to force the dialog to close. + void ForceDialogClose(); + + private: + // JS callback handlers. + void HandleSigninInit(const ListValue* args); + void HandleSubmitAuth(const ListValue* args); + + // Nonowned pointer; |signin_| owns this object. + BrowserSignin* signin_; + + string16 suggested_email_; + string16 login_message_; + + bool closed_; +}; + +BrowserSigninHtml::BrowserSigninHtml(BrowserSignin* signin, + string16 suggested_email, + string16 login_message) + : signin_(signin), + suggested_email_(suggested_email), + login_message_(login_message), + closed_(false) { +} + +void BrowserSigninHtml::RegisterMessages() { + dom_ui_->RegisterMessageCallback( + "SubmitAuth", NewCallback(this, &BrowserSigninHtml::HandleSubmitAuth)); + dom_ui_->RegisterMessageCallback( + "SigninInit", NewCallback(this, &BrowserSigninHtml::HandleSigninInit)); +} + +void BrowserSigninHtml::ReloadUI() { + HandleSigninInit(NULL); +} + +void BrowserSigninHtml::ForceDialogClose() { + if (!closed_ && dom_ui_) { + StringValue value("DialogClose"); + ListValue close_args; + close_args.Append(new StringValue("")); + dom_ui_->CallJavascriptFunction(L"chrome.send", value, close_args); + } +} + +void BrowserSigninHtml::HandleSigninInit(const ListValue* args) { + if (!dom_ui_) + return; + + RenderViewHost* rvh = dom_ui_->tab_contents()->render_view_host(); + rvh->ExecuteJavascriptInWebFrame(L"//iframe[@id='login']", L"hideBlurb();"); + + DictionaryValue json_args; + std::string json; + std::wstring javascript(L""); + SyncSetupFlow::GetArgsForGaiaLogin(signin_->GetProfileSyncService(), + &json_args); + + // Replace the suggested email, unless sync has already required a + // particular value. + bool is_editable; + std::string user; + json_args.GetBoolean("editable_user", &is_editable); + json_args.GetString("user", &user); + if (is_editable && user.empty() && !suggested_email_.empty()) + json_args.SetString("user", suggested_email_); + + base::JSONWriter::Write(&json_args, false, &json); + javascript += L"showGaiaLogin(" + UTF8ToWide(json) + L");"; + rvh->ExecuteJavascriptInWebFrame(L"//iframe[@id='login']", javascript); +} + +void BrowserSigninHtml::HandleSubmitAuth(const ListValue* args) { + std::string json(dom_ui_util::GetJsonResponseFromFirstArgumentInList(args)); + scoped_ptr<DictionaryValue> result(static_cast<DictionaryValue*>( + base::JSONReader::Read(json, false))); + std::string username; + std::string password; + std::string captcha; + std::string access_code; + if (!result.get() || + !result->GetString("user", &username) || + !result->GetString("pass", &password) || + !result->GetString("captcha", &captcha) || + !result->GetString("access_code", &access_code)) { + LOG(ERROR) << "Unintelligble format for authentication data from page."; + signin_->Cancel(); + } + signin_->GetProfileSyncService()->OnUserSubmittedAuth( + username, password, captcha, access_code); +} + +BrowserSignin::BrowserSignin(Profile* profile) + : profile_(profile), + delegate_(NULL), + html_dialog_ui_delegate_(NULL) { + BrowserSigninResourcesSource* source = new BrowserSigninResourcesSource(); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod(Singleton<ChromeURLDataManager>::get(), + &ChromeURLDataManager::AddDataSource, + make_scoped_refptr(source))); +} + +BrowserSignin::~BrowserSignin() { + delegate_ = NULL; +} + +void BrowserSignin::RequestSignin(TabContents* tab_contents, + const string16& suggested_email, + const string16& login_message, + SigninDelegate* delegate) { + CHECK(tab_contents); + CHECK(delegate); + // Cancel existing request. + if (delegate_) + Cancel(); + delegate_ = delegate; + suggested_email_ = suggested_email; + login_message_ = login_message; + RegisterAuthNotifications(); + ShowSigninTabModal(tab_contents); +} + +std::string BrowserSignin::GetSignedInUsername() const { + std::string username = + profile_->GetPrefs()->GetString(prefs::kGoogleServicesUsername); + VLOG(1) << "GetSignedInUsername: " << username; + return username; +} + +void BrowserSignin::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::GOOGLE_SIGNIN_SUCCESSFUL: { + VLOG(1) << "GOOGLE_SIGNIN_SUCCESSFUL"; + if (delegate_) + delegate_->OnLoginSuccess(); + // Close the dialog. + OnLoginFinished(); + break; + } + case NotificationType::GOOGLE_SIGNIN_FAILED: { + VLOG(1) << "GOOGLE_SIGNIN_FAILED"; + // The signin failed, refresh the UI with error information. + html_dialog_ui_delegate_->ReloadUI(); + break; + } + default: + NOTREACHED(); + } +} + +void BrowserSignin::Cancel() { + if (delegate_) { + delegate_->OnLoginFailure(GoogleServiceAuthError( + GoogleServiceAuthError::REQUEST_CANCELED)); + } + OnLoginFinished(); +} + +void BrowserSignin::OnLoginFinished() { + if (html_dialog_ui_delegate_) + html_dialog_ui_delegate_->ForceDialogClose(); + // The dialog will be deleted by DOMUI due to the dialog close, + // don't hold a reference. + html_dialog_ui_delegate_ = NULL; + + if (delegate_) { + UnregisterAuthNotifications(); + delegate_ = NULL; + } +} + +ProfileSyncService* BrowserSignin::GetProfileSyncService() const { + return profile_->GetProfileSyncService(); +} + +BrowserSigninHtml* BrowserSignin::CreateHtmlDialogUI() { + return new BrowserSigninHtml(this, suggested_email_, login_message_); +} + +void BrowserSignin::RegisterAuthNotifications() { + registrar_.Add(this, + NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, + Source<Profile>(profile_)); + registrar_.Add(this, + NotificationType::GOOGLE_SIGNIN_FAILED, + Source<Profile>(profile_)); +} + +void BrowserSignin::UnregisterAuthNotifications() { + registrar_.Remove(this, + NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, + Source<Profile>(profile_)); + registrar_.Remove(this, + NotificationType::GOOGLE_SIGNIN_FAILED, + Source<Profile>(profile_)); +} + +void BrowserSignin::ShowSigninTabModal(TabContents* tab_contents) { +// TODO(johnnyg): Need a linux views implementation for ConstrainedHtmlDialog. +#if defined(OS_WIN) || !defined(TOOLKIT_VIEWS) + html_dialog_ui_delegate_ = CreateHtmlDialogUI(); + ConstrainedHtmlUI::CreateConstrainedHtmlDialog(profile_, + html_dialog_ui_delegate_, + tab_contents); +#endif +} diff --git a/chrome/browser/browser_signin.h b/chrome/browser/browser_signin.h new file mode 100644 index 0000000..0da0d07 --- /dev/null +++ b/chrome/browser/browser_signin.h @@ -0,0 +1,116 @@ +// 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_BROWSER_SIGNIN_H_ +#define CHROME_BROWSER_BROWSER_SIGNIN_H_ +#pragma once + +#include <string> + +#include "base/scoped_ptr.h" +#include "chrome/common/net/gaia/google_service_auth_error.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" + +class BrowserSigninHtml; +class Profile; +class ProfileSyncService; +class TabContents; + +// The BrowserSignin class provides a login screen which allows the +// user to signin to the browser. Currently the signin is coordinated +// through the Chrome Sync logic. +// +// TODO(johnnyg): Separate this from the sync logic and make it the +// sole co-ordinator of browser signin. +// +// This class should only be accessed on the UI thread. +class BrowserSignin : public NotificationObserver { + public: + explicit BrowserSignin(Profile* profile); + virtual ~BrowserSignin(); + + // The delegate class is invoked on success and failure. + class SigninDelegate { + public: + virtual ~SigninDelegate() {} + + // The login was successful. + virtual void OnLoginSuccess() = 0; + + // The login failed. + virtual void OnLoginFailure(const GoogleServiceAuthError& error) = 0; + }; + + // Request that the user signin, modal to TabContents provided. + // If a user is already signed in, this will show a login dialog where + // the username is not editable. + // + // A custom HTML string can be provided that will be displayed next + // to the signin dialog. + // + // Only one sign-in can be in process at a time; if there is one in + // progress already it will be canceled in favor of this one. + // + // The delegate will eventually be called with OnLoginSuccess() or + // OnLoginFailure(), but never both. virtual for test override. + virtual void RequestSignin(TabContents* tab_contents, + const string16& suggested_email, + const string16& login_message, + SigninDelegate* delegate); + + // Returns the username of the user currently signed in. If no + // user is signed in, returns the empty string. virtual for test + // override. + virtual std::string GetSignedInUsername() const; + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + ProfileSyncService* GetProfileSyncService() const; + + // Close the dialog. Delegate's OnLoginFailure method will be called. + void Cancel(); + + private: + // Create the HTML Dialog content. + BrowserSigninHtml* CreateHtmlDialogUI(); + + // When the dialog is closed. + void OnLoginFinished(); + + // Turn auth notifications on. + void RegisterAuthNotifications(); + + // Turn auth notifications off. + void UnregisterAuthNotifications(); + + // Show the dialog Tab-Modal. + void ShowSigninTabModal(TabContents* tab_contents); + + // Non-owned pointer to the profile (which owns this object). + Profile* profile_; + + // Suggested email for the current login prompt. + string16 suggested_email_; + + // Current login message. + string16 login_message_; + + // Delegate for the current sign in request. + SigninDelegate* delegate_; + + // Current HTML Dialog information. Pointer is owned by the DOM_UI + // it will be attached to. + BrowserSigninHtml* html_dialog_ui_delegate_; + + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(BrowserSignin); +}; + + +#endif // CHROME_BROWSER_BROWSER_SIGNIN_H_ diff --git a/chrome/browser/browser_thread.cc b/chrome/browser/browser_thread.cc index 35c1308..7ba53e0 100644 --- a/chrome/browser/browser_thread.cc +++ b/chrome/browser/browser_thread.cc @@ -95,7 +95,7 @@ BrowserThread::~BrowserThread() { AutoLock lock(lock_); browser_threads_[identifier_] = NULL; #ifndef NDEBUG - // Double check that the threads are ordererd correctly in the enumeration. + // Double check that the threads are ordered correctly in the enumeration. for (int i = identifier_ + 1; i < ID_COUNT; ++i) { DCHECK(!browser_threads_[i]) << "Threads must be listed in the reverse order that they die"; @@ -176,8 +176,8 @@ bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) { scoped_refptr<base::MessageLoopProxy> BrowserThread::GetMessageLoopProxyForThread( ID identifier) { - scoped_refptr<base::MessageLoopProxy> proxy = - new BrowserThreadMessageLoopProxy(identifier); + scoped_refptr<base::MessageLoopProxy> proxy( + new BrowserThreadMessageLoopProxy(identifier)); return proxy; } diff --git a/chrome/browser/browser_thread.h b/chrome/browser/browser_thread.h index c034851..f6a022e 100644 --- a/chrome/browser/browser_thread.h +++ b/chrome/browser/browser_thread.h @@ -109,7 +109,7 @@ class BrowserThread : public base::Thread { template <class T> static bool DeleteSoon(ID identifier, const tracked_objects::Location& from_here, - T* object) { + const T* object) { return PostNonNestableTask( identifier, from_here, new DeleteTask<T>(object)); } @@ -117,7 +117,7 @@ class BrowserThread : public base::Thread { template <class T> static bool ReleaseSoon(ID identifier, const tracked_objects::Location& from_here, - T* object) { + const T* object) { return PostNonNestableTask( identifier, from_here, new ReleaseTask<T>(object)); } @@ -154,7 +154,7 @@ class BrowserThread : public base::Thread { template<ID thread> struct DeleteOnThread { template<typename T> - static void Destruct(T* x) { + static void Destruct(const T* x) { if (CurrentlyOn(thread)) { delete x; } else { diff --git a/chrome/browser/browser_thread_unittest.cc b/chrome/browser/browser_thread_unittest.cc index d2c19d8..948709a 100644 --- a/chrome/browser/browser_thread_unittest.cc +++ b/chrome/browser/browser_thread_unittest.cc @@ -11,7 +11,7 @@ class BrowserThreadTest : public testing::Test { public: - void Release() { + void Release() const { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); } @@ -77,7 +77,9 @@ class BrowserThreadTest : public testing::Test { private: scoped_ptr<BrowserThread> ui_thread_; scoped_ptr<BrowserThread> file_thread_; - MessageLoop loop_; + // It's kind of ugly to make this mutable - solely so we can post the Quit + // Task from Release(). This should be fixed. + mutable MessageLoop loop_; }; TEST_F(BrowserThreadTest, PostTask) { diff --git a/chrome/browser/browser_uitest.cc b/chrome/browser/browser_uitest.cc index 8ec30a2..5d2c448 100644 --- a/chrome/browser/browser_uitest.cc +++ b/chrome/browser/browser_uitest.cc @@ -10,7 +10,7 @@ #include "base/sys_info.h" #include "base/test/test_file_util.h" #include "base/values.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/platform_util.h" #include "chrome/common/chrome_switches.h" @@ -159,9 +159,11 @@ TEST_F(BrowserTest, MAYBE_OtherRedirectsDontForkProcess) { // Use JavaScript URL to almost fork a new tab, but not quite. (Leave the // opener non-null.) Should not fork a process. - std::string url_prefix("javascript:(function(){w=window.open();"); - GURL dont_fork_url(url_prefix + - "w.document.location=\"http://localhost:1337\";})()"); + std::string url_str = "javascript:(function(){w=window.open(); "; + url_str += "w.document.location=\""; + url_str += test_server.GetURL("").spec(); + url_str += "\";})()"; + GURL dont_fork_url(url_str); // Make sure that a new tab but not new process has been created. ASSERT_TRUE(tab->NavigateToURLAsync(dont_fork_url)); @@ -172,8 +174,11 @@ TEST_F(BrowserTest, MAYBE_OtherRedirectsDontForkProcess) { ASSERT_EQ(orig_tab_count + 1, new_tab_count); // Same thing if the current tab tries to redirect itself. - GURL dont_fork_url2(url_prefix + - "document.location=\"http://localhost:1337\";})()"); + url_str = "javascript:(function(){w=window.open(); "; + url_str += "document.location=\""; + url_str += test_server.GetURL("").spec(); + url_str += "\";})()"; + GURL dont_fork_url2(url_str); // Make sure that no new process has been created. ASSERT_TRUE(tab->NavigateToURLAsync(dont_fork_url2)); diff --git a/chrome/browser/browser_url_handler.cc b/chrome/browser/browser_url_handler.cc index 6707606..f4f9fd7 100644 --- a/chrome/browser/browser_url_handler.cc +++ b/chrome/browser/browser_url_handler.cc @@ -22,7 +22,8 @@ static bool HandleViewSource(GURL* url, Profile* profile) { // other kind of 'active' url scheme like 'javascript' or 'data'. static const char* const allowed_sub_schemes[] = { chrome::kHttpScheme, chrome::kHttpsScheme, chrome::kFtpScheme, - chrome::kChromeUIScheme, chrome::kFileScheme + chrome::kChromeDevToolsScheme, chrome::kChromeUIScheme, + chrome::kFileScheme }; bool is_sub_scheme_allowed = false; diff --git a/chrome/browser/browser_window.h b/chrome/browser/browser_window.h index d45f2c1..0b93616 100644 --- a/chrome/browser/browser_window.h +++ b/chrome/browser/browser_window.h @@ -6,379 +6,8 @@ #define CHROME_BROWSER_BROWSER_WINDOW_H_ #pragma once -#include "chrome/browser/tab_contents/navigation_entry.h" -#include "chrome/common/content_settings_types.h" -#include "gfx/native_widget_types.h" - -class Browser; -class BrowserWindowTesting; -class DownloadShelf; -class FindBar; -class GURL; -class HtmlDialogUIDelegate; -class LocationBar; -class Profile; -class StatusBubble; -class TabContents; -class TemplateURL; -class TemplateURLModel; -#if !defined(OS_MACOSX) -class ToolbarView; -#endif -struct NativeWebKeyboardEvent; - -namespace gfx { -class Rect; -} - -namespace views { -class Window; -} - -//////////////////////////////////////////////////////////////////////////////// -// BrowserWindow interface -// An interface implemented by the "view" of the Browser window. -// -// NOTE: All getters may return NULL. -class BrowserWindow { - public: - // Show the window, or activates it if it's already visible. - virtual void Show() = 0; - - // Sets the window's size and position to the specified values. - virtual void SetBounds(const gfx::Rect& bounds) = 0; - - // Closes the frame as soon as possible. If the frame is not in a drag - // session, it will close immediately; otherwise, it will move offscreen (so - // events are still fired) until the drag ends, then close. This assumes - // that the Browser is not immediately destroyed, but will be eventually - // destroyed by other means (eg, the tab strip going to zero elements). - // Bad things happen if the Browser dtor is called directly as a result of - // invoking this method. - virtual void Close() = 0; - - // Activates (brings to front) the window. Restores the window from minimized - // state if necessary. - virtual void Activate() = 0; - - // Deactivates the window, making the next window in the Z order the active - // window. - virtual void Deactivate() = 0; - - // Returns true if the window is currently the active/focused window. - virtual bool IsActive() const = 0; - - // Flashes the taskbar item associated with this frame. - virtual void FlashFrame() = 0; - - // Return a platform dependent identifier for this frame. On Windows, this - // returns an HWND. - virtual gfx::NativeWindow GetNativeHandle() = 0; - - // Returns a pointer to the testing interface to the Browser window, or NULL - // if there is none. - virtual BrowserWindowTesting* GetBrowserWindowTesting() = 0; - - // Return the status bubble associated with the frame - virtual StatusBubble* GetStatusBubble() = 0; - - // Inform the receiving frame that an animation has progressed in the - // selected tab. - // TODO(beng): Remove. Infobars/Boomarks bars should talk directly to - // BrowserView. - virtual void SelectedTabToolbarSizeChanged(bool is_animating) = 0; - - // Inform the frame that the selected tab favicon or title has changed. Some - // frames may need to refresh their title bar. - virtual void UpdateTitleBar() = 0; - - // Invoked when the visibility of the bookmark bar. - // NOTE: this is NOT sent when the user toggles the visibility of this, - // but rather when the user transitions from a page that forces - // it to be visibile to one that doesn't have it visible (or - // vice-versa). - // TODO(sky): see about routing visibility pref changing through here too. - virtual void ShelfVisibilityChanged() = 0; - - // Inform the frame that the dev tools window for the selected tab has - // changed. - virtual void UpdateDevTools() = 0; - - // Update any loading animations running in the window. |should_animate| is - // true if there are tabs loading and the animations should continue, false - // if there are no active loads and the animations should end. - virtual void UpdateLoadingAnimations(bool should_animate) = 0; - - // Sets the starred state for the current tab. - virtual void SetStarredState(bool is_starred) = 0; - - // Returns the nonmaximized bounds of the frame (even if the frame is - // currently maximized or minimized) in terms of the screen coordinates. - virtual gfx::Rect GetRestoredBounds() const = 0; - - // TODO(beng): REMOVE? - // Returns true if the frame is maximized (aka zoomed). - virtual bool IsMaximized() const = 0; - - // Accessors for fullscreen mode state. - virtual void SetFullscreen(bool fullscreen) = 0; - virtual bool IsFullscreen() const = 0; - - // Returns true if the fullscreen bubble is visible. - virtual bool IsFullscreenBubbleVisible() const = 0; - - // Returns the location bar. - virtual LocationBar* GetLocationBar() const = 0; - - // Tries to focus the location bar. Clears the window focus (to avoid - // inconsistent state) if this fails. - virtual void SetFocusToLocationBar(bool select_all) = 0; - - // Informs the view whether or not a load is in progress for the current tab. - // The view can use this notification to update the reload/stop button. - virtual void UpdateReloadStopState(bool is_loading, bool force) = 0; - - // Updates the toolbar with the state for the specified |contents|. - virtual void UpdateToolbar(TabContents* contents, - bool should_restore_state) = 0; - - // Focuses the toolbar (for accessibility). - virtual void FocusToolbar() = 0; - - // Focuses the app menu like it was a menu bar. - // - // Not used on the Mac, which has a "normal" menu bar. - virtual void FocusAppMenu() = 0; - - // Focuses the bookmarks toolbar (for accessibility). - virtual void FocusBookmarksToolbar() = 0; - - // Focuses the Chrome OS status view (for accessibility). - virtual void FocusChromeOSStatus() = 0; - - // Moves keyboard focus to the next pane. - virtual void RotatePaneFocus(bool forwards) = 0; - - // Returns whether the bookmark bar is visible or not. - virtual bool IsBookmarkBarVisible() const = 0; - - // Returns whether the bookmark bar is animating or not. - virtual bool IsBookmarkBarAnimating() const = 0; - - // Returns whether the tool bar is visible or not. - virtual bool IsToolbarVisible() const = 0; - - // Returns the rect where the resize corner should be drawn by the render - // widget host view (on top of what the renderer returns). We return an empty - // rect to identify that there shouldn't be a resize corner (in the cases - // where we take care of it ourselves at the browser level). - virtual gfx::Rect GetRootWindowResizerRect() const = 0; - - // Tells the frame not to render as inactive until the next activation change. - // This is required on Windows when dropdown selects are shown to prevent the - // select from deactivating the browser frame. A stub implementation is - // provided here since the functionality is Windows-specific. - virtual void DisableInactiveFrame() {} - - // Shows a confirmation dialog box for setting the default search engine - // described by |template_url|. Takes ownership of |template_url|. - virtual void ConfirmSetDefaultSearchProvider( - TabContents* tab_contents, - TemplateURL* template_url, - TemplateURLModel* template_url_model) { - // TODO(levin): Implement this for non-Windows platforms and make it pure. - } - - // Shows a confirmation dialog box for adding a search engine described by - // |template_url|. Takes ownership of |template_url|. - virtual void ConfirmAddSearchProvider(const TemplateURL* template_url, - Profile* profile) = 0; - - // Shows or hides the bookmark bar depending on its current visibility. - virtual void ToggleBookmarkBar() = 0; - - // Shows the About Chrome dialog box. - virtual views::Window* ShowAboutChromeDialog() = 0; - - // Shows the Update Recommended dialog box. - virtual void ShowUpdateChromeDialog() = 0; - - // Shows the Task manager. - virtual void ShowTaskManager() = 0; - - // Shows the Bookmark bubble. |url| is the URL being bookmarked, - // |already_bookmarked| is true if the url is already bookmarked. - virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) = 0; - - // Whether or not the shelf view is visible. - virtual bool IsDownloadShelfVisible() const = 0; - - // Returns the DownloadShelf. - virtual DownloadShelf* GetDownloadShelf() = 0; - - // Shows the Report a Bug dialog box. - virtual void ShowReportBugDialog() = 0; - - // Shows the Clear Browsing Data dialog box. - virtual void ShowClearBrowsingDataDialog() = 0; - - // Shows the Import Bookmarks & Settings dialog box. - virtual void ShowImportDialog() = 0; - - // Shows the Search Engines dialog box. - virtual void ShowSearchEnginesDialog() = 0; - - // Shows the Password Manager dialog box. - virtual void ShowPasswordManager() = 0; - - // Shows the repost form confirmation dialog box. - virtual void ShowRepostFormWarningDialog(TabContents* tab_contents) = 0; - - // Shows the Content Settings dialog box. - virtual void ShowContentSettingsWindow(ContentSettingsType content_type, - Profile* profile) = 0; - - // Shows the collected cookies dialog box. - virtual void ShowCollectedCookiesDialog(TabContents* tab_contents) = 0; - - // Shows a dialog to the user that something is wrong with the profile. - // |message_id| is the ID for a string in the string table which will be - // displayed in the dialog. - virtual void ShowProfileErrorDialog(int message_id) = 0; - - // Show the bubble that indicates to the user that a theme is being installed. - virtual void ShowThemeInstallBubble() = 0; - - // Shows the confirmation dialog box warning that the browser is closing with - // in-progress downloads. - // This method should call Browser::InProgressDownloadResponse once the user - // has confirmed. - virtual void ConfirmBrowserCloseWithPendingDownloads() = 0; - - // Shows a dialog box with HTML content, e.g. for Gears. |parent_window| is - // the window the dialog should be opened modal to and is a native window - // handle. - virtual void ShowHTMLDialog(HtmlDialogUIDelegate* delegate, - gfx::NativeWindow parent_window) = 0; - - // Asks the window to continue a drag operation begun in a different browser - // window. |tab_bounds| are the bounds of the Tab view that was dragged from - // the source window, in screen coordinates. The corresponding Tab view in - // this new window will be positioned at these bounds for a seamless - // appearance. - virtual void ContinueDraggingDetachedTab(const gfx::Rect& tab_bounds) {} - - // BrowserThemeProvider calls this when a user has changed his or her theme, - // indicating that it's time to redraw everything. - virtual void UserChangedTheme() = 0; - - // Get extra vertical height that the render view should add to its requests - // to webkit. This can help prevent sending extraneous layout/repaint requests - // when the delegate is in the process of resizing the tab contents view (e.g. - // during infobar animations). - virtual int GetExtraRenderViewHeight() const = 0; - - // Notification that |tab_contents| got the focus through user action (click - // on the page). - virtual void TabContentsFocused(TabContents* tab_contents) = 0; - - // Shows the page info using the specified information. - // |url| is the url of the page/frame the info applies to, |ssl| is the SSL - // information for that page/frame. If |show_history| is true, a section - // showing how many times that URL has been visited is added to the page info. - virtual void ShowPageInfo(Profile* profile, - const GURL& url, - const NavigationEntry::SSLStatus& ssl, - bool show_history) = 0; - - // Shows the app menu (for accessibility). - virtual void ShowAppMenu() = 0; - - // Allows the BrowserWindow object to handle the specified keyboard event - // before sending it to the renderer. - // Returns true if the |event| was handled. Otherwise, if the |event| would - // be handled in HandleKeyboardEvent() method as a normal keyboard shortcut, - // |*is_keyboard_shortcut| should be set to true. - virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, - bool* is_keyboard_shortcut) = 0; - - // Allows the BrowserWindow object to handle the specified keyboard event, - // if the renderer did not process it. - virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) = 0; - - // Shows the create web app shortcut dialog box. - virtual void ShowCreateShortcutsDialog(TabContents* tab_contents) = 0; - - // Clipboard commands applied to the whole browser window. - virtual void Cut() = 0; - virtual void Copy() = 0; - virtual void Paste() = 0; - - // Switches between available tabstrip display modes. - virtual void ToggleTabStripMode() = 0; - -#if defined(OS_MACOSX) - // Opens the tabpose view. - virtual void OpenTabpose() = 0; -#endif - - // Invoked when instant's tab contents should be shown. - virtual void ShowInstant(TabContents* preview_contents) = 0; - - // Invoked when the instant's tab contents should be hidden. - virtual void HideInstant() = 0; - - // Returns the desired bounds for instant in screen coordinates. Note that if - // instant isn't currently visible this returns the bounds instant would be - // placed at. - virtual gfx::Rect GetInstantBounds() = 0; - - // Construct a BrowserWindow implementation for the specified |browser|. - static BrowserWindow* CreateBrowserWindow(Browser* browser); - - // Construct a FindBar implementation for the specified |browser|. - static FindBar* CreateFindBar(Browser* browser_window); - - protected: - friend class BrowserList; - friend class BrowserView; - virtual void DestroyBrowser() = 0; - - virtual ~BrowserWindow() {} -}; - -#if defined(OS_WIN) || defined(TOOLKIT_VIEWS) -class BookmarkBarView; -class LocationBarView; - -namespace views { -class View; -} -#endif // defined(OS_WIN) - -// A BrowserWindow utility interface used for accessing elements of the browser -// UI used only by UI test automation. -class BrowserWindowTesting { - public: -#if defined(OS_WIN) || defined(TOOLKIT_VIEWS) - // Returns the BookmarkBarView. - virtual BookmarkBarView* GetBookmarkBarView() const = 0; - - // Returns the LocationBarView. - virtual LocationBarView* GetLocationBarView() const = 0; - - // Returns the TabContentsContainer. - virtual views::View* GetTabContentsContainerView() const = 0; - - // Returns the TabContentsContainer. - virtual views::View* GetSidebarContainerView() const = 0; - - // Returns the ToolbarView. - virtual ToolbarView* GetToolbarView() const = 0; -#endif - - protected: - virtual ~BrowserWindowTesting() {} -}; +#include "chrome/browser/ui/browser_window.h" +// TODO(beng): remove this file once all includes have been updated. #endif // CHROME_BROWSER_BROWSER_WINDOW_H_ + diff --git a/chrome/browser/browsing_data_appcache_helper_unittest.cc b/chrome/browser/browsing_data_appcache_helper_unittest.cc index 8373485..0e6dab3 100644 --- a/chrome/browser/browsing_data_appcache_helper_unittest.cc +++ b/chrome/browser/browsing_data_appcache_helper_unittest.cc @@ -34,8 +34,8 @@ TEST(CannedBrowsingDataAppCacheHelperTest, SetInfo) { GURL manifest2("http://example2.com/path1/manifest.xml"); GURL manifest3("http://example2.com/path2/manifest.xml"); - scoped_refptr<CannedBrowsingDataAppCacheHelper> helper = - new CannedBrowsingDataAppCacheHelper(&profile); + scoped_refptr<CannedBrowsingDataAppCacheHelper> helper( + new CannedBrowsingDataAppCacheHelper(&profile)); helper->AddAppCache(manifest1); helper->AddAppCache(manifest2); helper->AddAppCache(manifest3); @@ -67,8 +67,8 @@ TEST(CannedBrowsingDataAppCacheHelperTest, Unique) { GURL manifest("http://example.com/manifest.xml"); - scoped_refptr<CannedBrowsingDataAppCacheHelper> helper = - new CannedBrowsingDataAppCacheHelper(&profile); + scoped_refptr<CannedBrowsingDataAppCacheHelper> helper( + new CannedBrowsingDataAppCacheHelper(&profile)); helper->AddAppCache(manifest); helper->AddAppCache(manifest); @@ -91,8 +91,8 @@ TEST(CannedBrowsingDataAppCacheHelperTest, Empty) { GURL manifest("http://example.com/manifest.xml"); - scoped_refptr<CannedBrowsingDataAppCacheHelper> helper = - new CannedBrowsingDataAppCacheHelper(&profile); + scoped_refptr<CannedBrowsingDataAppCacheHelper> helper( + new CannedBrowsingDataAppCacheHelper(&profile)); ASSERT_TRUE(helper->empty()); helper->AddAppCache(manifest); diff --git a/chrome/browser/browsing_data_database_helper_unittest.cc b/chrome/browser/browsing_data_database_helper_unittest.cc index c8f1c18..ca23f39 100644 --- a/chrome/browser/browsing_data_database_helper_unittest.cc +++ b/chrome/browser/browsing_data_database_helper_unittest.cc @@ -46,8 +46,8 @@ TEST(CannedBrowsingDataDatabaseTest, AddDatabase) { const char db2[] = "db2"; const char db3[] = "db3"; - scoped_refptr<CannedBrowsingDataDatabaseHelper> helper = - new CannedBrowsingDataDatabaseHelper(&profile); + scoped_refptr<CannedBrowsingDataDatabaseHelper> helper( + new CannedBrowsingDataDatabaseHelper(&profile)); helper->AddDatabase(origin1, db1, ""); helper->AddDatabase(origin1, db2, ""); helper->AddDatabase(origin2, db3, ""); @@ -76,8 +76,8 @@ TEST(CannedBrowsingDataDatabaseTest, Unique) { const char origin_str[] = "http_host1_1"; const char db[] = "db1"; - scoped_refptr<CannedBrowsingDataDatabaseHelper> helper = - new CannedBrowsingDataDatabaseHelper(&profile); + scoped_refptr<CannedBrowsingDataDatabaseHelper> helper( + new CannedBrowsingDataDatabaseHelper(&profile)); helper->AddDatabase(origin, db, ""); helper->AddDatabase(origin, db, ""); @@ -100,8 +100,8 @@ TEST(CannedBrowsingDataDatabaseTest, Empty) { const GURL origin("http://host1:1/"); const char db[] = "db1"; - scoped_refptr<CannedBrowsingDataDatabaseHelper> helper = - new CannedBrowsingDataDatabaseHelper(&profile); + scoped_refptr<CannedBrowsingDataDatabaseHelper> helper( + new CannedBrowsingDataDatabaseHelper(&profile)); ASSERT_TRUE(helper->empty()); helper->AddDatabase(origin, db, ""); diff --git a/chrome/browser/browsing_data_indexed_db_helper.cc b/chrome/browser/browsing_data_indexed_db_helper.cc index 0ecdc2e..4979471 100644 --- a/chrome/browser/browsing_data_indexed_db_helper.cc +++ b/chrome/browser/browsing_data_indexed_db_helper.cc @@ -108,13 +108,9 @@ void BrowsingDataIndexedDBHelperImpl::FetchIndexedDBInfoInWebKitThread() { for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); file_path = file_enumerator.Next()) { if (file_path.Extension() == IndexedDBContext::kIndexedDBExtension) { - std::string name; - WebSecurityOrigin web_security_origin; - if (!IndexedDBContext::SplitIndexedDBFileName( - file_path, &name, &web_security_origin)) { - // Could not parse file name. - continue; - } + WebSecurityOrigin web_security_origin = + WebKit::WebSecurityOrigin::createFromDatabaseIdentifier( + webkit_glue::FilePathToWebString(file_path.BaseName())); if (EqualsASCII(web_security_origin.protocol(), chrome::kExtensionScheme)) { // Extension state is not considered browsing data. @@ -129,7 +125,6 @@ void BrowsingDataIndexedDBHelperImpl::FetchIndexedDBInfoInWebKitThread() { web_security_origin.port(), web_security_origin.databaseIdentifier().utf8(), web_security_origin.toString().utf8(), - name, file_path, file_info.size, file_info.last_modified)); @@ -176,7 +171,7 @@ CannedBrowsingDataIndexedDBHelper::CannedBrowsingDataIndexedDBHelper( } void CannedBrowsingDataIndexedDBHelper::AddIndexedDB( - const GURL& origin, const string16& name, const string16& description) { + const GURL& origin, const string16& description) { WebSecurityOrigin web_security_origin = WebSecurityOrigin::createFromString( UTF8ToUTF16(origin.spec())); @@ -195,9 +190,8 @@ void CannedBrowsingDataIndexedDBHelper::AddIndexedDB( web_security_origin.port(), web_security_origin.databaseIdentifier().utf8(), security_origin, - UTF16ToUTF8(name), profile_->GetWebKitContext()->indexed_db_context()-> - GetIndexedDBFilePath(name, web_security_origin), + GetIndexedDBFilePath(web_security_origin.databaseIdentifier()), 0, base::Time())); } diff --git a/chrome/browser/browsing_data_indexed_db_helper.h b/chrome/browser/browsing_data_indexed_db_helper.h index 11a9755..932c00b 100644 --- a/chrome/browser/browsing_data_indexed_db_helper.h +++ b/chrome/browser/browsing_data_indexed_db_helper.h @@ -35,7 +35,6 @@ class BrowsingDataIndexedDBHelper unsigned short port, const std::string& database_identifier, const std::string& origin, - const std::string& database_name, const FilePath& file_path, int64 size, base::Time last_modified) @@ -44,7 +43,6 @@ class BrowsingDataIndexedDBHelper port(port), database_identifier(database_identifier), origin(origin), - database_name(database_name), file_path(file_path), size(size), last_modified(last_modified) { @@ -59,7 +57,6 @@ class BrowsingDataIndexedDBHelper unsigned short port; std::string database_identifier; std::string origin; - std::string database_name; FilePath file_path; int64 size; base::Time last_modified; @@ -97,7 +94,6 @@ class CannedBrowsingDataIndexedDBHelper // Add a indexed database to the set of canned indexed databases that is // returned by this helper. void AddIndexedDB(const GURL& origin, - const string16& name, const string16& description); // Clear the list of canned indexed databases. diff --git a/chrome/browser/browsing_data_indexed_db_helper_unittest.cc b/chrome/browser/browsing_data_indexed_db_helper_unittest.cc index 0cf0f0a..10df224 100644 --- a/chrome/browser/browsing_data_indexed_db_helper_unittest.cc +++ b/chrome/browser/browsing_data_indexed_db_helper_unittest.cc @@ -36,22 +36,22 @@ class TestCompletionCallback { DISALLOW_COPY_AND_ASSIGN(TestCompletionCallback); }; -TEST(CannedBrowsingDataIndexedDBHelperTest, AddIndexedDB) { +// Functionality incomplete, needs further refactoring, http://crbug.com/60532. +TEST(CannedBrowsingDataIndexedDBHelperTest, DISABLED_AddIndexedDB) { TestingProfile profile; const GURL origin1("http://host1:1/"); const GURL origin2("http://host2:1/"); - const string16 name(ASCIIToUTF16("name")); const string16 description(ASCIIToUTF16("description")); const FilePath::CharType file1[] = - FILE_PATH_LITERAL("http_host1_1@name.indexeddb"); + FILE_PATH_LITERAL("http_host1_1.indexeddb"); const FilePath::CharType file2[] = - FILE_PATH_LITERAL("http_host2_1@name.indexeddb"); + FILE_PATH_LITERAL("http_host2_1.indexeddb"); scoped_refptr<CannedBrowsingDataIndexedDBHelper> helper( new CannedBrowsingDataIndexedDBHelper(&profile)); - helper->AddIndexedDB(origin1, name, description); - helper->AddIndexedDB(origin2, name, description); + helper->AddIndexedDB(origin1, description); + helper->AddIndexedDB(origin2, description); TestCompletionCallback callback; helper->StartFetching( @@ -66,19 +66,19 @@ TEST(CannedBrowsingDataIndexedDBHelperTest, AddIndexedDB) { EXPECT_EQ(FilePath(file2).value(), result[1].file_path.BaseName().value()); } -TEST(CannedBrowsingDataIndexedDBHelperTest, Unique) { +// Functionality incomplete, needs further refactoring, http://crbug.com/60532. +TEST(CannedBrowsingDataIndexedDBHelperTest, DISABLED_Unique) { TestingProfile profile; const GURL origin("http://host1:1/"); - const string16 name(ASCIIToUTF16("name")); const string16 description(ASCIIToUTF16("description")); const FilePath::CharType file[] = - FILE_PATH_LITERAL("http_host1_1@name.indexeddb"); + FILE_PATH_LITERAL("http_host1_1.indexeddb"); scoped_refptr<CannedBrowsingDataIndexedDBHelper> helper( new CannedBrowsingDataIndexedDBHelper(&profile)); - helper->AddIndexedDB(origin, name, description); - helper->AddIndexedDB(origin, name, description); + helper->AddIndexedDB(origin, description); + helper->AddIndexedDB(origin, description); TestCompletionCallback callback; helper->StartFetching( @@ -96,14 +96,13 @@ TEST(CannedBrowsingDataIndexedDBHelperTest, Empty) { TestingProfile profile; const GURL origin("http://host1:1/"); - const string16 name(ASCIIToUTF16("name")); const string16 description(ASCIIToUTF16("description")); scoped_refptr<CannedBrowsingDataIndexedDBHelper> helper( new CannedBrowsingDataIndexedDBHelper(&profile)); ASSERT_TRUE(helper->empty()); - helper->AddIndexedDB(origin, name, description); + helper->AddIndexedDB(origin, description); ASSERT_FALSE(helper->empty()); helper->Reset(); ASSERT_TRUE(helper->empty()); diff --git a/chrome/browser/browsing_data_local_storage_helper_unittest.cc b/chrome/browser/browsing_data_local_storage_helper_unittest.cc index fe110c3..6fbb1ee 100644 --- a/chrome/browser/browsing_data_local_storage_helper_unittest.cc +++ b/chrome/browser/browsing_data_local_storage_helper_unittest.cc @@ -45,8 +45,8 @@ TEST(CannedBrowsingDataLocalStorageTest, AddLocalStorage) { const FilePath::CharType file2[] = FILE_PATH_LITERAL("http_host2_1.localstorage"); - scoped_refptr<CannedBrowsingDataLocalStorageHelper> helper = - new CannedBrowsingDataLocalStorageHelper(&profile); + scoped_refptr<CannedBrowsingDataLocalStorageHelper> helper( + new CannedBrowsingDataLocalStorageHelper(&profile)); helper->AddLocalStorage(origin1); helper->AddLocalStorage(origin2); @@ -70,8 +70,8 @@ TEST(CannedBrowsingDataLocalStorageTest, Unique) { const FilePath::CharType file[] = FILE_PATH_LITERAL("http_host1_1.localstorage"); - scoped_refptr<CannedBrowsingDataLocalStorageHelper> helper = - new CannedBrowsingDataLocalStorageHelper(&profile); + scoped_refptr<CannedBrowsingDataLocalStorageHelper> helper( + new CannedBrowsingDataLocalStorageHelper(&profile)); helper->AddLocalStorage(origin); helper->AddLocalStorage(origin); @@ -92,8 +92,8 @@ TEST(CannedBrowsingDataLocalStorageTest, Empty) { const GURL origin("http://host1:1/"); - scoped_refptr<CannedBrowsingDataLocalStorageHelper> helper = - new CannedBrowsingDataLocalStorageHelper(&profile); + scoped_refptr<CannedBrowsingDataLocalStorageHelper> helper( + new CannedBrowsingDataLocalStorageHelper(&profile)); ASSERT_TRUE(helper->empty()); helper->AddLocalStorage(origin); diff --git a/chrome/browser/bug_report_data.cc b/chrome/browser/bug_report_data.cc new file mode 100644 index 0000000..9219e52 --- /dev/null +++ b/chrome/browser/bug_report_data.cc @@ -0,0 +1,34 @@ +// 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/bug_report_data.h" + +#include "chrome/browser/browser.h" + +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/notifications/system_notification.h" +#endif + + + +#if defined(OS_CHROMEOS) +// Called from the same thread as HandleGetDialogDefaults, i.e. the UI thread. +void BugReportData::SyslogsComplete(chromeos::LogDictionaryType* logs, + std::string* zip_content) { + if (sent_report_) { + // We already sent the report, just delete the data. + if (logs) + delete logs; + if (zip_content) + delete zip_content; + } else { + zip_content_ = zip_content; + sys_info_ = logs; // Will get deleted when SendReport() is called. + if (send_sys_info_) { + // We already prepared the report, send it now. + this->SendReport(); + } + } +} +#endif diff --git a/chrome/browser/bug_report_data.h b/chrome/browser/bug_report_data.h new file mode 100644 index 0000000..7752350 --- /dev/null +++ b/chrome/browser/bug_report_data.h @@ -0,0 +1,112 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// Author: rkc@google.com (Rahul Chaturvedi) + +#ifndef CHROME_BROWSER_BUG_REPORT_DATA_H_ +#define CHROME_BROWSER_BUG_REPORT_DATA_H_ + + +#include <string> +#include <vector> + +#include "base/utf_string_conversions.h" +#include "chrome/browser/bug_report_util.h" + +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/cros/syslogs_library.h" +#endif + +class BugReportData { + public: + // Make sure we initialize these flags to false since SyslogsComplete + // may be triggered before we've called update data; in which case, + // we do not want it to just delete the logs it just gathered, and we + // don't want it to send the report either - this will make sure that if + // SyslogsComplete gets called before UpdateData, we'll simply populate the + // sys_info and zip_content fields and exit without disturbing anything else + BugReportData() : profile_(NULL), + problem_type_(0) +#if defined(OS_CHROMEOS) + , sent_report_(false), send_sys_info_(false) +#endif + { + } + + // Defined in bug_report_ui.cc + void SendReport(); + + void UpdateData(Profile* profile + , const std::string& target_tab_url + , const string16& target_tab_title + , const int problem_type + , const std::string& page_url + , const std::string& description + , const std::vector<unsigned char>& image +#if defined(OS_CHROMEOS) + , const std::string& user_email + , const bool send_sys_info + , const bool sent_report +#endif + ) { + profile_ = profile; + target_tab_url_ = target_tab_url; + target_tab_title_ = target_tab_title; + problem_type_ = problem_type; + page_url_ = page_url; + description_ = description; + image_ = image; +#if defined(OS_CHROMEOS) + user_email_ = user_email; + send_sys_info_ = send_sys_info; + sent_report_ = sent_report; +#endif + } + +#if defined(OS_CHROMEOS) + void SyslogsComplete(chromeos::LogDictionaryType* logs, + std::string* zip_content); +#endif + + const std::string& target_tab_url() { return target_tab_url_; } + const string16& target_tab_title() { return target_tab_title_; } + + int problem_type() { return problem_type_; } + const std::string& page_url() { return page_url_; } + const std::string& description() { return description_; } + const std::vector<unsigned char>& image() { return image_; } +#if defined(OS_CHROMEOS) + const std::string& user_email() { return user_email_; } + const chromeos::LogDictionaryType* sys_info() { return sys_info_; } + const bool send_sys_info() { return send_sys_info_; } + const bool sent_report() { return sent_report_; } + const std::string* zip_content() { return zip_content_; } +#endif + + + private: + Profile* profile_; + + // Target tab url. + std::string target_tab_url_; + // Target tab page title. + string16 target_tab_title_; + + int problem_type_; + std::string page_url_; + std::string description_; + std::vector<unsigned char> image_; + +#if defined(OS_CHROMEOS) + // Chromeos specific values for SendReport. + std::string user_email_; + chromeos::LogDictionaryType* sys_info_; + // Content of the compressed system logs. + std::string* zip_content_; + // NOTE: Extra boolean sent_report_ is required because callback may + // occur before or after we call SendReport(). + bool sent_report_; + // Flag to indicate to SyslogsComplete that it should send the report + bool send_sys_info_; +#endif +}; + +#endif // CHROME_BROWSER_BUG_REPORT_DATA_H_ diff --git a/chrome/browser/bug_report_util.cc b/chrome/browser/bug_report_util.cc index db771cc..a726c74 100644 --- a/chrome/browser/bug_report_util.cc +++ b/chrome/browser/bug_report_util.cc @@ -72,42 +72,21 @@ const size_t kMaxLineCount = 10; const size_t kMaxSystemLogLength = 1024; #endif -} // namespace - +const int64 kInitialRetryDelay = 900000; // 15 minutes +const int64 kRetryDelayIncreaseFactor = 2; +const int64 kRetryDelayLimit = 14400000; // 4 hours -#if defined(OS_CHROMEOS) -class FeedbackNotification { - public: - // Note: notification will show only on one profile at a time. - void Show(Profile* profile, const string16& message, bool urgent) { - if (notification_.get()) { - notification_->Hide(); - } - notification_.reset( - new chromeos::SystemNotification(profile, kNotificationId, - IDR_STATUSBAR_FEEDBACK, - l10n_util::GetStringUTF16( - IDS_BUGREPORT_NOTIFICATION_TITLE))); - notification_->Show(message, urgent, false); - } - private: - FeedbackNotification() {} - friend struct DefaultSingletonTraits<FeedbackNotification>; +} // namespace - scoped_ptr<chromeos::SystemNotification> notification_; - DISALLOW_COPY_AND_ASSIGN(FeedbackNotification); -}; -#endif // Simple URLFetcher::Delegate to clean up URLFetcher on completion. class BugReportUtil::PostCleanup : public URLFetcher::Delegate { public: -#if defined(OS_CHROMEOS) - explicit PostCleanup(Profile* profile); -#else - PostCleanup(); -#endif + PostCleanup(Profile* profile, std::string* post_body, + int64 previous_delay) : profile_(profile), + post_body_(post_body), + previous_delay_(previous_delay) { } // Overridden from URLFetcher::Delegate. virtual void OnURLFetchComplete(const URLFetcher* source, const GURL& url, @@ -121,18 +100,15 @@ class BugReportUtil::PostCleanup : public URLFetcher::Delegate { private: Profile* profile_; + std::string* post_body_; + int64 previous_delay_; DISALLOW_COPY_AND_ASSIGN(PostCleanup); }; -#if defined(OS_CHROMEOS) - BugReportUtil::PostCleanup::PostCleanup(Profile* profile) - : profile_(profile) { -#else - BugReportUtil::PostCleanup::PostCleanup() : profile_(NULL) { -#endif -} - +// Don't use the data parameter, instead use the pointer we pass into every +// post cleanup object - that pointer will be deleted and deleted only on a +// successful post to the feedback server. void BugReportUtil::PostCleanup::OnURLFetchComplete( const URLFetcher* source, const GURL& url, @@ -143,34 +119,35 @@ void BugReportUtil::PostCleanup::OnURLFetchComplete( std::stringstream error_stream; if (response_code == kHttpPostSuccessNoContent) { + // We've sent our report, delete the report data + delete post_body_; + error_stream << "Success"; - } else if (response_code == kHttpPostFailNoConnection) { - error_stream << "No connection to server."; - } else if ((response_code > kHttpPostFailClientError) && - (response_code < kHttpPostFailServerError)) { - error_stream << "Client error: HTTP response code " << response_code; - } else if (response_code > kHttpPostFailServerError) { - error_stream << "Server error: HTTP response code " << response_code; } else { - error_stream << "Unknown error: HTTP response code " << response_code; - } - - LOG(WARNING) << "Submission to feedback server (" << url - << ") status: " << error_stream.str(); + // Uh oh, feedback failed, send it off to retry + if (previous_delay_) { + if (previous_delay_ < kRetryDelayLimit) + previous_delay_ *= kRetryDelayIncreaseFactor; + } else { + previous_delay_ = kInitialRetryDelay; + } + BugReportUtil::DispatchFeedback(profile_, post_body_, previous_delay_); -#if defined(OS_CHROMEOS) - // Show the notification to the user; this notification will stay active till - // either the user closes it, or we display another notification. - if (response_code == kHttpPostSuccessNoContent) { - Singleton<FeedbackNotification>()->Show(profile_, l10n_util::GetStringUTF16( - IDS_BUGREPORT_FEEDBACK_STATUS_SUCCESS), false); - } else { - Singleton<FeedbackNotification>()->Show(profile_, - l10n_util::GetStringFUTF16(IDS_BUGREPORT_FEEDBACK_STATUS_FAIL, - ASCIIToUTF16(error_stream.str())), - true); + // Process the error for debug output + if (response_code == kHttpPostFailNoConnection) { + error_stream << "No connection to server."; + } else if ((response_code > kHttpPostFailClientError) && + (response_code < kHttpPostFailServerError)) { + error_stream << "Client error: HTTP response code " << response_code; + } else if (response_code > kHttpPostFailServerError) { + error_stream << "Server error: HTTP response code " << response_code; + } else { + error_stream << "Unknown error: HTTP response code " << response_code; + } } -#endif + + LOG(WARNING) << "FEEDBACK: Submission to feedback server (" << url << + ") status: " << error_stream.str() << std::endl; // Delete the URLFetcher. delete source; @@ -213,13 +190,47 @@ void BugReportUtil::SetFeedbackServer(const std::string& server) { feedback_server_ = server; } +// static +void BugReportUtil::DispatchFeedback(Profile* profile, + std::string* post_body, + int64 delay) { + DCHECK(post_body); + + MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableFunction( + &BugReportUtil::SendFeedback, profile, post_body, delay), delay); +} + +// static +void BugReportUtil::SendFeedback(Profile* profile, + std::string* post_body, + int64 previous_delay) { + DCHECK(post_body); + + GURL post_url; + if (CommandLine::ForCurrentProcess()-> + HasSwitch(switches::kFeedbackServer)) + post_url = GURL(CommandLine::ForCurrentProcess()-> + GetSwitchValueASCII(switches::kFeedbackServer)); + else + post_url = GURL(kBugReportPostUrl); + + URLFetcher* fetcher = new URLFetcher(post_url, URLFetcher::POST, + new BugReportUtil::PostCleanup(profile, + post_body, + previous_delay)); + fetcher->set_request_context(profile->GetRequestContext()); + + fetcher->set_upload_data(std::string(kProtBufMimeType), *post_body); + fetcher->Start(); +} + // static void BugReportUtil::AddFeedbackData( userfeedback::ExternalExtensionSubmit* feedback_data, const std::string& key, const std::string& value) { - // We have no reason to log any empty values - gives us no data - if (value == "") return; + // Don't bother with empty keys or values + if (key=="" || value == "") return; // Create log_value object and add it to the web_data object userfeedback::ProductSpecificData log_value; log_value.set_key(key); @@ -263,15 +274,6 @@ void BugReportUtil::SendReport(Profile* profile, #else int png_height) { #endif - GURL post_url; - - if (CommandLine::ForCurrentProcess()-> - HasSwitch(switches::kFeedbackServer)) - post_url = GURL(CommandLine::ForCurrentProcess()-> - GetSwitchValueASCII(switches::kFeedbackServer)); - else - post_url = GURL(kBugReportPostUrl); - // Create google feedback protocol buffer objects userfeedback::ExternalExtensionSubmit feedback_data; // type id set to 0, unused field but needs to be initialized to 0 @@ -320,17 +322,6 @@ void BugReportUtil::SendReport(Profile* profile, SetOSVersion(&os_version); AddFeedbackData(&feedback_data, std::string(kOsVersionTag), os_version); -#if defined(OS_CHROMEOS) - if (sys_info) { - for (chromeos::LogDictionaryType::const_iterator i = sys_info->begin(); - i != sys_info->end(); ++i) - if (!CommandLine::ForCurrentProcess()->HasSwitch( - switches::kCompressSystemFeedback) || ValidFeedbackSize(i->second)) { - AddFeedbackData(&feedback_data, i->first, i->second); - } - } -#endif - // Include the page image if we have one. if (png_data) { userfeedback::PostedScreenshot screenshot; @@ -347,15 +338,24 @@ void BugReportUtil::SendReport(Profile* profile, } #if defined(OS_CHROMEOS) - // Include the page image if we have one. - if (zipped_logs_data && CommandLine::ForCurrentProcess()->HasSwitch( - switches::kCompressSystemFeedback)) { - userfeedback::ProductSpecificBinaryData attachment; - attachment.set_mime_type(kBZip2MimeType); - attachment.set_name(kLogsAttachmentName); - attachment.set_data(std::string(zipped_logs_data, zipped_logs_length)); + if (sys_info) { + // Add the product specific data + for (chromeos::LogDictionaryType::const_iterator i = sys_info->begin(); + i != sys_info->end(); ++i) + if (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kCompressSystemFeedback) || ValidFeedbackSize(i->second)) { + AddFeedbackData(&feedback_data, i->first, i->second); + } - *(feedback_data.add_product_specific_binary_data()) = attachment; + // If we have zipped logs, add them here + if (zipped_logs_data && CommandLine::ForCurrentProcess()->HasSwitch( + switches::kCompressSystemFeedback)) { + userfeedback::ProductSpecificBinaryData attachment; + attachment.set_mime_type(kBZip2MimeType); + attachment.set_name(kLogsAttachmentName); + attachment.set_data(std::string(zipped_logs_data, zipped_logs_length)); + *(feedback_data.add_product_specific_binary_data()) = attachment; + } } #endif @@ -379,19 +379,12 @@ void BugReportUtil::SendReport(Profile* profile, *(feedback_data.mutable_chrome_data()) = chrome_data; - // We have the body of our POST, so send it off to the server. - URLFetcher* fetcher = new URLFetcher(post_url, URLFetcher::POST, -#if defined(OS_CHROMEOS) - new BugReportUtil::PostCleanup(profile)); -#else - new BugReportUtil::PostCleanup()); -#endif - fetcher->set_request_context(profile->GetRequestContext()); + // Serialize our report to a string pointer we can pass around + std::string* post_body = new std::string; + feedback_data.SerializeToString(post_body); - std::string post_body; - feedback_data.SerializeToString(&post_body); - fetcher->set_upload_data(std::string(kProtBufMimeType), post_body); - fetcher->Start(); + // We have the body of our POST, so send it off to the server with 0 delay + DispatchFeedback(profile, post_body, 0); } // static diff --git a/chrome/browser/bug_report_util.h b/chrome/browser/bug_report_util.h index 7d5af5d..61835d6 100644 --- a/chrome/browser/bug_report_util.h +++ b/chrome/browser/bug_report_util.h @@ -30,18 +30,8 @@ class TabContents; class BugReportUtil { public: -#if defined(OS_CHROMEOS) - enum BugType { - CONNECTIVITY_ISSUE = 0, - SYNC_ISSUE, - CRASH_ISSUE, - PAGE_FORMATTING, - EXTENSION_ISSUE, - SUSPEND_ISSUE, - PHISHING_PAGE, - OTHER_PROBLEM - }; -#else + +#if defined(OS_MACOSX) enum BugType { PAGE_WONT_LOAD = 0, PAGE_LOOKS_ODD, @@ -54,6 +44,7 @@ class BugReportUtil { }; #endif + // SetOSVersion copies the maj.minor.build + servicePack_string // into a string. We currently have: // base::win::GetVersion returns WinVersion, which is just @@ -68,6 +59,11 @@ class BugReportUtil { // This sets the address of the feedback server to be used by SendReport static void SetFeedbackServer(const std::string& server); + // Send the feedback report after the specified delay + static void DispatchFeedback(Profile* profile, std::string* feedback_data, + int64 delay); + + // Generates bug report data. static void SendReport(Profile* profile, const std::string& page_title_text, @@ -99,6 +95,10 @@ class BugReportUtil { userfeedback::ExternalExtensionSubmit* feedback_data, const std::string& key, const std::string& value); + // Send the feedback report + static void SendFeedback(Profile* profile, std::string* feedback_data, + int64 previous_delay); + #if defined(OS_CHROMEOS) static bool ValidFeedbackSize(const std::string& content); #endif diff --git a/chrome/browser/cert_store.cc b/chrome/browser/cert_store.cc index 89c5ffe..e2f9c2c 100644 --- a/chrome/browser/cert_store.cc +++ b/chrome/browser/cert_store.cc @@ -55,6 +55,10 @@ int CertStore::StoreCert(net::X509Certificate* cert, int process_id) { ReverseCertMap::iterator cert_iter = cert_to_id_.find(cert); if (cert_iter == cert_to_id_.end()) { cert_id = next_cert_id_++; + // We use 0 as an invalid cert_id value. In the unlikely event that + // next_cert_id_ wraps around, we reset it to 1. + if (next_cert_id_ == 0) + next_cert_id_ = 1; cert->AddRef(); id_to_cert_[cert_id] = cert; cert_to_id_[cert] = cert_id; diff --git a/chrome/browser/certificate_manager_model.cc b/chrome/browser/certificate_manager_model.cc index eafb709..1c7cd4e 100644 --- a/chrome/browser/certificate_manager_model.cc +++ b/chrome/browser/certificate_manager_model.cc @@ -70,10 +70,6 @@ string16 CertificateManagerModel::GetColumnText( base::TimeFormatShortDateNumeric(cert.valid_expiry())); } break; - case COL_EMAIL_ADDRESS: - rv = UTF8ToUTF16( - x509_certificate_model::GetEmailAddress(cert.os_cert_handle())); - break; default: NOTREACHED(); } diff --git a/chrome/browser/certificate_manager_model.h b/chrome/browser/certificate_manager_model.h index 4ff0be7..2206370 100644 --- a/chrome/browser/certificate_manager_model.h +++ b/chrome/browser/certificate_manager_model.h @@ -27,7 +27,6 @@ class CertificateManagerModel { COL_CERTIFICATE_STORE, COL_SERIAL_NUMBER, COL_EXPIRES_ON, - COL_EMAIL_ADDRESS, }; class Observer { diff --git a/chrome/browser/character_encoding.cc b/chrome/browser/character_encoding.cc index 8e24b02..80bfc6b 100644 --- a/chrome/browser/character_encoding.cc +++ b/chrome/browser/character_encoding.cc @@ -14,7 +14,7 @@ #include "base/string_tokenizer.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "grit/generated_resources.h" #include "unicode/ucnv.h" diff --git a/chrome/browser/child_process_security_policy.cc b/chrome/browser/child_process_security_policy.cc index 67b8a7c..0aed464 100644 --- a/chrome/browser/child_process_security_policy.cc +++ b/chrome/browser/child_process_security_policy.cc @@ -273,12 +273,6 @@ void ChildProcessSecurityPolicy::GrantScheme(int renderer_id, state->second->GrantScheme(scheme); } -void ChildProcessSecurityPolicy::GrantInspectElement(int renderer_id) { - // The inspector is served from a chrome: URL. In order to run the - // inspector, the renderer needs to be able to load chrome: URLs. - GrantScheme(renderer_id, chrome::kChromeUIScheme); -} - void ChildProcessSecurityPolicy::GrantDOMUIBindings(int renderer_id) { AutoLock lock(lock_); diff --git a/chrome/browser/child_process_security_policy.h b/chrome/browser/child_process_security_policy.h index fc8814a..b70dc25 100644 --- a/chrome/browser/child_process_security_policy.h +++ b/chrome/browser/child_process_security_policy.h @@ -84,11 +84,6 @@ class ChildProcessSecurityPolicy { // scheme. void GrantScheme(int renderer_id, const std::string& scheme); - // Whenever the browser processes commands the renderer to run web inspector, - // it should call this method to grant the renderer process the capability to - // run the inspector. - void GrantInspectElement(int renderer_id); - // Grant this renderer the ability to use DOM UI Bindings. void GrantDOMUIBindings(int renderer_id); diff --git a/chrome/browser/child_process_security_policy_unittest.cc b/chrome/browser/child_process_security_policy_unittest.cc index 40b249b..26f794f 100644 --- a/chrome/browser/child_process_security_policy_unittest.cc +++ b/chrome/browser/child_process_security_policy_unittest.cc @@ -287,20 +287,6 @@ TEST_F(ChildProcessSecurityPolicyTest, FilePermissions) { p->Remove(kRendererID); } -TEST_F(ChildProcessSecurityPolicyTest, CanServiceInspectElement) { - ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance(); - - GURL url("chrome://devtools/devtools.html"); - - p->Add(kRendererID); - - EXPECT_FALSE(p->CanRequestURL(kRendererID, url)); - p->GrantInspectElement(kRendererID); - EXPECT_TRUE(p->CanRequestURL(kRendererID, url)); - - p->Remove(kRendererID); -} - TEST_F(ChildProcessSecurityPolicyTest, CanServiceDOMUIBindings) { ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance(); diff --git a/chrome/browser/chrome_plugin_host.cc b/chrome/browser/chrome_plugin_host.cc index 76d4756..a044306 100644 --- a/chrome/browser/chrome_plugin_host.cc +++ b/chrome/browser/chrome_plugin_host.cc @@ -327,6 +327,7 @@ class ModelessHtmlDialogDelegate : public HtmlDialogUIDelegate { this, &ModelessHtmlDialogDelegate::ReportResults, json_retval)); } virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { } + virtual bool ShouldShowDialogTitle() const { return true; } private: // Actually shows the dialog on the UI thread. diff --git a/chrome/browser/chrome_plugin_unittest.cc b/chrome/browser/chrome_plugin_unittest.cc index 38b4bd5..a7eee5b 100644 --- a/chrome/browser/chrome_plugin_unittest.cc +++ b/chrome/browser/chrome_plugin_unittest.cc @@ -33,7 +33,7 @@ class TestURLRequestContextGetter : public URLRequestContextGetter { context_ = new TestURLRequestContext(); return context_; } - virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() { + virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const { return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); } diff --git a/chrome/browser/chromeos/audio_handler.cc b/chrome/browser/chromeos/audio_handler.cc index f069b18..a463e29 100644 --- a/chrome/browser/chromeos/audio_handler.cc +++ b/chrome/browser/chromeos/audio_handler.cc @@ -58,7 +58,7 @@ void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) { if (!VerifyMixerConnection()) return; - DLOG(INFO) << "Adjusting Volume by " << adjust_by_percent << " percent"; + DVLOG(1) << "Adjusting Volume by " << adjust_by_percent << " percent"; double volume = mixer_->GetVolumeDb(); double pct = VolumeDbToPercent(volume); @@ -90,14 +90,14 @@ void AudioHandler::SetMute(bool do_mute) { if (!VerifyMixerConnection()) return; - DLOG(INFO) << "Setting Mute to " << do_mute; + DVLOG(1) << "Setting Mute to " << do_mute; mixer_->SetMute(do_mute); } void AudioHandler::OnMixerInitialized(bool success) { connected_ = success; - DLOG(INFO) << "OnMixerInitialized, success = " << success; + DVLOG(1) << "OnMixerInitialized, success = " << success; } AudioHandler::AudioHandler() @@ -132,8 +132,8 @@ bool AudioHandler::VerifyMixerConnection() { if (reconnect_tries_ < kMaxReconnectTries) { reconnect_tries_++; - LOG(INFO) << "Re-connecting to PulseAudio attempt " << reconnect_tries_ - << "/" << kMaxReconnectTries; + VLOG(1) << "Re-connecting to PulseAudio attempt " << reconnect_tries_ << "/" + << kMaxReconnectTries; mixer_.reset(new PulseAudioMixer()); connected_ = mixer_->InitSync(); if (connected_) { diff --git a/chrome/browser/chromeos/boot_times_loader.cc b/chrome/browser/chromeos/boot_times_loader.cc index 00151b8..5fc7414 100644 --- a/chrome/browser/chromeos/boot_times_loader.cc +++ b/chrome/browser/chromeos/boot_times_loader.cc @@ -17,6 +17,7 @@ #include "base/string_util.h" #include "base/stringprintf.h" #include "base/thread.h" +#include "base/thread_restrictions.h" #include "base/time.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" @@ -222,7 +223,7 @@ static void RecordStatsDelayed( void BootTimesLoader::WriteLoginTimes( const std::vector<TimeMarker> login_times) { const int kMinTimeMillis = 1; - const int kMaxTimeMillis = 30; + const int kMaxTimeMillis = 30000; const int kNumBuckets = 100; const char kUmaPrefix[] = "BootTime."; const FilePath log_path(kLogPath); @@ -281,7 +282,7 @@ BootTimesLoader::Stats BootTimesLoader::GetCurrentStats() { const FilePath kProcUptime("/proc/uptime"); const FilePath kDiskStat("/sys/block/sda/stat"); Stats stats; - + base::ThreadRestrictions::ScopedAllowIO allow_io; file_util::ReadFileToString(kProcUptime, &stats.uptime); file_util::ReadFileToString(kDiskStat, &stats.disk); return stats; @@ -334,7 +335,7 @@ void BootTimesLoader::Observe( // and the page is reloaded. if (NetworkStateNotifier::Get()->is_connected()) { // Post difference between first tab and login success time. - AddLoginTimeMarker("LoginDone", false); + AddLoginTimeMarker("LoginDone", true); RecordCurrentStats(kChromeFirstRender); // Post chrome first render stat. registrar_.Remove(this, NotificationType::LOAD_START, diff --git a/chrome/browser/chromeos/cros/cros_mock.cc b/chrome/browser/chromeos/cros/cros_mock.cc index ab33dc0..5e66df0 100644 --- a/chrome/browser/chromeos/cros/cros_mock.cc +++ b/chrome/browser/chromeos/cros/cros_mock.cc @@ -29,6 +29,8 @@ namespace chromeos { using ::testing::AnyNumber; +using ::testing::AtMost; +using ::testing::InSequence; using ::testing::InvokeWithoutArgs; using ::testing::Return; using ::testing::ReturnRef; @@ -268,24 +270,19 @@ void CrosMock::SetInputMethodLibraryStatusAreaExpectations() { } void CrosMock::SetNetworkLibraryStatusAreaExpectations() { - EXPECT_CALL(*mock_network_library_, AddObserver(_)) + EXPECT_CALL(*mock_network_library_, AddNetworkManagerObserver(_)) .Times(1) .RetiresOnSaturation(); - - // NetworkDropdownButton::NetworkChanged() calls: - EXPECT_CALL(*mock_network_library_, ethernet_connected()) - .Times(2) // also called by NetworkMenu::InitMenuItems() - .WillRepeatedly((Return(false))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, wifi_connected()) + EXPECT_CALL(*mock_network_library_, AddCellularDataPlanObserver(_)) .Times(1) - .WillRepeatedly((Return(false))) .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, wifi_connecting()) + + // NetworkMenuButton::OnNetworkManagerChanged() calls: + EXPECT_CALL(*mock_network_library_, active_network()) .Times(1) - .WillRepeatedly((Return(false))) + .WillRepeatedly((Return((const Network*)(NULL)))) .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, cellular_connected()) + EXPECT_CALL(*mock_network_library_, wifi_connecting()) .Times(1) .WillRepeatedly((Return(false))) .RetiresOnSaturation(); @@ -294,7 +291,7 @@ void CrosMock::SetNetworkLibraryStatusAreaExpectations() { .WillRepeatedly((Return(false))) .RetiresOnSaturation(); EXPECT_CALL(*mock_network_library_, Connected()) - .Times(2) // also called by NetworkMenu::InitMenuItems() + .Times(2) .WillRepeatedly((Return(false))) .RetiresOnSaturation(); EXPECT_CALL(*mock_network_library_, Connecting()) @@ -303,36 +300,38 @@ void CrosMock::SetNetworkLibraryStatusAreaExpectations() { .RetiresOnSaturation(); // NetworkMenu::InitMenuItems() calls: - EXPECT_CALL(*mock_network_library_, ethernet_connecting()) + EXPECT_CALL(*mock_network_library_, ethernet_available()) + .Times(1) + .WillRepeatedly((Return(true))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_network_library_, ethernet_connected()) .Times(1) .WillRepeatedly((Return(false))) .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, wifi_networks()) + EXPECT_CALL(*mock_network_library_, ethernet_connecting()) .Times(1) - .WillRepeatedly((ReturnRef(wifi_networks_))) + .WillRepeatedly((Return(false))) .RetiresOnSaturation(); EXPECT_CALL(*mock_network_library_, wifi_available()) .Times(1) .WillRepeatedly((Return(false))) .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, wifi_network()) + EXPECT_CALL(*mock_network_library_, cellular_available()) .Times(1) - .WillRepeatedly((ReturnRef(wifi_network_))) + .WillRepeatedly((Return(false))) .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, cellular_networks()) + EXPECT_CALL(*mock_network_library_, Connected()) .Times(1) - .WillRepeatedly((ReturnRef(cellular_networks_))) + .WillRepeatedly((Return(false))) .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, cellular_network()) + + EXPECT_CALL(*mock_network_library_, RemoveNetworkManagerObserver(_)) .Times(1) - .WillRepeatedly((ReturnRef(cellular_network_))) .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, cellular_available()) + EXPECT_CALL(*mock_network_library_, RemoveObserverForAllNetworks(_)) .Times(1) - .WillRepeatedly((Return(false))) .RetiresOnSaturation(); - - EXPECT_CALL(*mock_network_library_, RemoveObserver(_)) + EXPECT_CALL(*mock_network_library_, RemoveCellularDataPlanObserver(_)) .Times(1) .RetiresOnSaturation(); } @@ -378,17 +377,20 @@ void CrosMock::SetPowerLibraryExpectations() { } void CrosMock::SetSpeechSynthesisLibraryExpectations() { + InSequence s; + EXPECT_CALL(*mock_speech_synthesis_library_, StopSpeaking()) + .WillOnce(Return(true)) + .RetiresOnSaturation(); EXPECT_CALL(*mock_speech_synthesis_library_, Speak(_)) - .Times(1) .WillOnce(Return(true)) .RetiresOnSaturation(); EXPECT_CALL(*mock_speech_synthesis_library_, StopSpeaking()) - .Times(1) .WillOnce(Return(true)) .RetiresOnSaturation(); - EXPECT_CALL(*mock_speech_synthesis_library_, IsSpeaking()) - .Times(4) + EXPECT_CALL(*mock_speech_synthesis_library_, Speak(_)) .WillOnce(Return(true)) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_speech_synthesis_library_, IsSpeaking()) .WillOnce(Return(true)) .WillOnce(Return(true)) .WillOnce(Return(false)) diff --git a/chrome/browser/chromeos/cros/cryptohome_library.cc b/chrome/browser/chromeos/cros/cryptohome_library.cc index 52ce85c..cd9a31d 100644 --- a/chrome/browser/chromeos/cros/cryptohome_library.cc +++ b/chrome/browser/chromeos/cros/cryptohome_library.cc @@ -163,7 +163,7 @@ class CryptohomeLibraryImpl : public CryptohomeLibrary { LOG(ERROR) << error; return false; } - LOG(INFO) << "Adding handler for " << async_id; + VLOG(1) << "Adding handler for " << async_id; callback_map_[async_id] = d; return true; } diff --git a/chrome/browser/chromeos/cros/input_method_library.cc b/chrome/browser/chromeos/cros/input_method_library.cc index 62ffbb1..bdf3318 100644 --- a/chrome/browser/chromeos/cros/input_method_library.cc +++ b/chrome/browser/chromeos/cros/input_method_library.cc @@ -60,8 +60,7 @@ class InputMethodLibraryImpl : public InputMethodLibrary, defer_ime_startup_(false), should_change_input_method_(false), ibus_daemon_process_id_(0), - candidate_window_process_id_(0), - failure_count_(0) { + candidate_window_process_id_(0) { scoped_ptr<InputMethodDescriptors> input_method_descriptors( CreateFallbackInputMethodDescriptors()); current_input_method_ = input_method_descriptors->at(0); @@ -124,8 +123,7 @@ class InputMethodLibraryImpl : public InputMethodLibrary, if (input_method_id != chromeos::GetHardwareKeyboardLayoutName()) { StartInputMethodProcesses(); } - chromeos::ChangeInputMethod( - input_method_status_connection_, input_method_id.c_str()); + ChangeInputMethodInternal(input_method_id); } } @@ -139,7 +137,7 @@ class InputMethodLibraryImpl : public InputMethodLibrary, bool InputMethodIsActivated(const std::string& input_method_id) { scoped_ptr<InputMethodDescriptors> active_input_method_descriptors( - CrosLibrary::Get()->GetInputMethodLibrary()->GetActiveInputMethods()); + GetActiveInputMethods()); for (size_t i = 0; i < active_input_method_descriptors->size(); ++i) { if (active_input_method_descriptors->at(i).id == input_method_id) { return true; @@ -206,6 +204,39 @@ class InputMethodLibraryImpl : public InputMethodLibrary, } } + // Changes the current input method to |input_method_id|. If the id is not in + // the preload_engine list, this function changes the current method to the + // first preloaded engine. Returns true if the current engine is switched to + // |input_method_id| or the first one. + bool ChangeInputMethodInternal(const std::string& input_method_id) { + DCHECK(EnsureLoadedAndStarted()); + std::string input_method_id_to_switch = input_method_id; + + if (!InputMethodIsActivated(input_method_id)) { + // This path might be taken if prefs::kLanguageCurrentInputMethod (NOT + // synced with cloud) and kLanguagePreloadEngines (synced with cloud) are + // mismatched. e.g. the former is 'xkb:us::eng' and the latter (on the + // sync server) is 'xkb:jp::jpn,mozc'. + scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods()); + DCHECK(!input_methods->empty()); + if (!input_methods->empty()) { + input_method_id_to_switch = input_methods->at(0).id; + LOG(INFO) << "Can't change the current input method to " + << input_method_id << " since the engine is not preloaded. " + << "Switch to " << input_method_id_to_switch << " instead."; + } + } + + if (chromeos::ChangeInputMethod(input_method_status_connection_, + input_method_id_to_switch.c_str())) { + return true; + } + + // Not reached. + LOG(ERROR) << "Can't switch input method to " << input_method_id_to_switch; + return false; + } + // Flushes the input method config data. The config data is queued up in // |pending_config_requests_| until the config backend (ibus-memconf) // starts. Since there is no good way to get notified when the config @@ -213,7 +244,6 @@ class InputMethodLibraryImpl : public InputMethodLibrary, // config data to the config backend. void FlushImeConfig() { bool active_input_methods_are_changed = false; - bool completed = false; if (EnsureLoadedAndStarted()) { InputMethodConfigRequests::iterator iter = pending_config_requests_.begin(); @@ -241,41 +271,22 @@ class InputMethodLibraryImpl : public InputMethodLibrary, // yet been added to preload_engines. As such, the call is deferred // until after all config values have been sent to the IME process. if (should_change_input_method_) { - if (chromeos::ChangeInputMethod(input_method_status_connection_, - current_input_method_id_.c_str())) { - should_change_input_method_ = false; - completed = true; - active_input_methods_are_changed = true; - } - } else { - completed = true; + ChangeInputMethodInternal(current_input_method_id_); + should_change_input_method_ = false; + active_input_methods_are_changed = true; } } } - if (completed) { + if (pending_config_requests_.empty()) { timer_.Stop(); // no-op if it's not running. - } else { + } else if (!timer_.IsRunning()) { // Flush is not completed. Start a timer if it's not yet running. - if (!timer_.IsRunning()) { - static const int64 kTimerIntervalInMsec = 100; - failure_count_ = 0; - timer_.Start(base::TimeDelta::FromMilliseconds(kTimerIntervalInMsec), - this, &InputMethodLibraryImpl::FlushImeConfig); - } else { - // The timer is already running. We'll give up if it reaches the - // max retry count. - static const int kMaxRetries = 15; - ++failure_count_; - if (failure_count_ > kMaxRetries) { - LOG(ERROR) << "FlushImeConfig: Max retries exceeded. " - << "current_input_method_id: " << current_input_method_id_ - << " pending_config_requests.size: " - << pending_config_requests_.size(); - timer_.Stop(); - } - } + static const int64 kTimerIntervalInMsec = 100; + timer_.Start(base::TimeDelta::FromMilliseconds(kTimerIntervalInMsec), + this, &InputMethodLibraryImpl::FlushImeConfig); } + if (active_input_methods_are_changed) { FOR_EACH_OBSERVER(Observer, observers_, ActiveInputMethodsChanged(this)); } @@ -495,7 +506,7 @@ class InputMethodLibraryImpl : public InputMethodLibrary, } void SetDeferImeStartup(bool defer) { - LOG(INFO) << "Setting DeferImeStartup to " << defer; + VLOG(1) << "Setting DeferImeStartup to " << defer; defer_ime_startup_ = defer; } @@ -560,8 +571,6 @@ class InputMethodLibraryImpl : public InputMethodLibrary, int ibus_daemon_process_id_; // The process id of the candidate window. 0 if it's not running. int candidate_window_process_id_; - // The failure count of config flush attempts. - int failure_count_; DISALLOW_COPY_AND_ASSIGN(InputMethodLibraryImpl); }; diff --git a/chrome/browser/chromeos/cros/login_library.cc b/chrome/browser/chromeos/cros/login_library.cc index 73ae346..5af80e9 100644 --- a/chrome/browser/chromeos/cros/login_library.cc +++ b/chrome/browser/chromeos/cros/login_library.cc @@ -31,13 +31,27 @@ class LoginLibraryImpl : public LoginLibrary { bool CheckWhitelist(const std::string& email, std::vector<uint8>* OUT_signature) { - return chromeos::CheckWhitelist(email.c_str(), OUT_signature); + CryptoBlob* sig = NULL; + if (chromeos::CheckWhitelistSafe(email.c_str(), &sig)) { + OUT_signature->assign(sig->data, sig->data + sig->length); + chromeos::FreeCryptoBlob(sig); + return true; + } + return false; } bool RetrieveProperty(const std::string& name, std::string* OUT_value, std::vector<uint8>* OUT_signature) { - return chromeos::RetrieveProperty(name.c_str(), OUT_value, OUT_signature); + Property* prop = NULL; + if (chromeos::RetrievePropertySafe(name.c_str(), &prop)) { + OUT_value->assign(prop->value); + CryptoBlob* sig = prop->signature; + OUT_signature->assign(sig->data, sig->data + sig->length); + chromeos::FreeProperty(prop); + return true; + } + return false; } bool SetOwnerKeyAsync(const std::vector<uint8>& public_key_der, @@ -46,7 +60,11 @@ class LoginLibraryImpl : public LoginLibrary { if (set_owner_key_callback_) return false; set_owner_key_callback_ = callback; - return chromeos::SetOwnerKey(public_key_der); + CryptoBlob* key = chromeos::CreateCryptoBlob(&public_key_der[0], + public_key_der.size()); + bool rv = chromeos::SetOwnerKeySafe(key); + chromeos::FreeCryptoBlob(key); + return rv; } bool StorePropertyAsync(const std::string& name, @@ -57,7 +75,13 @@ class LoginLibraryImpl : public LoginLibrary { if (property_op_callback_) return false; property_op_callback_ = callback; - return chromeos::StoreProperty(name.c_str(), value.c_str(), signature); + Property* prop = chromeos::CreateProperty(name.c_str(), + value.c_str(), + &signature[0], + signature.size()); + bool rv = chromeos::StorePropertySafe(prop); + chromeos::FreeProperty(prop); + return rv; } bool UnwhitelistAsync(const std::string& email, @@ -67,7 +91,11 @@ class LoginLibraryImpl : public LoginLibrary { if (whitelist_op_callback_) return false; whitelist_op_callback_ = callback; - return chromeos::Unwhitelist(email.c_str(), signature); + CryptoBlob* sig = chromeos::CreateCryptoBlob(&signature[0], + signature.size()); + bool rv = chromeos::UnwhitelistSafe(email.c_str(), sig); + chromeos::FreeCryptoBlob(sig); + return rv; } bool WhitelistAsync(const std::string& email, @@ -77,11 +105,22 @@ class LoginLibraryImpl : public LoginLibrary { if (whitelist_op_callback_) return false; whitelist_op_callback_ = callback; - return chromeos::Whitelist(email.c_str(), signature); + CryptoBlob* sig = chromeos::CreateCryptoBlob(&signature[0], + signature.size()); + bool rv = chromeos::WhitelistSafe(email.c_str(), sig); + chromeos::FreeCryptoBlob(sig); + return rv; } bool EnumerateWhitelisted(std::vector<std::string>* whitelisted) { - return chromeos::EnumerateWhitelisted(whitelisted); + UserList* list = NULL; + if (chromeos::EnumerateWhitelistedSafe(&list)) { + for (int i = 0; i < list->num_users; i++) + whitelisted->push_back(std::string(list->users[i])); + chromeos::FreeUserList(list); + return true; + } + return false; } bool StartSession(const std::string& user_email, diff --git a/chrome/browser/chromeos/cros/mock_network_library.h b/chrome/browser/chromeos/cros/mock_network_library.h index 1e43e35..21b9bf4 100644 --- a/chrome/browser/chromeos/cros/mock_network_library.h +++ b/chrome/browser/chromeos/cros/mock_network_library.h @@ -17,16 +17,22 @@ class MockNetworkLibrary : public NetworkLibrary { public: MockNetworkLibrary() {} virtual ~MockNetworkLibrary() {} - MOCK_METHOD1(AddObserver, void(Observer*)); - MOCK_METHOD1(RemoveObserver, void(Observer*)); - MOCK_CONST_METHOD0(ethernet_network, const EthernetNetwork&(void)); + MOCK_METHOD1(AddNetworkManagerObserver, void(NetworkManagerObserver*)); + MOCK_METHOD1(RemoveNetworkManagerObserver, void(NetworkManagerObserver*)); + MOCK_METHOD2(AddNetworkObserver, void(const std::string&, NetworkObserver*)); + MOCK_METHOD2(RemoveNetworkObserver, void(const std::string&, + NetworkObserver*)); + MOCK_METHOD1(RemoveObserverForAllNetworks, void(NetworkObserver*)); + MOCK_METHOD1(AddCellularDataPlanObserver, void(CellularDataPlanObserver*)); + MOCK_METHOD1(RemoveCellularDataPlanObserver, void(CellularDataPlanObserver*)); + MOCK_METHOD0(ethernet_network, EthernetNetwork*(void)); MOCK_CONST_METHOD0(ethernet_connecting, bool(void)); MOCK_CONST_METHOD0(ethernet_connected, bool(void)); - MOCK_CONST_METHOD0(wifi_network, const WifiNetwork&(void)); + MOCK_METHOD0(wifi_network, WifiNetwork*(void)); MOCK_CONST_METHOD0(wifi_connecting, bool(void)); MOCK_CONST_METHOD0(wifi_connected, bool(void)); - MOCK_CONST_METHOD0(cellular_network, const CellularNetwork&(void)); + MOCK_METHOD0(cellular_network, CellularNetwork*(void)); MOCK_CONST_METHOD0(cellular_connecting, bool(void)); MOCK_CONST_METHOD0(cellular_connected, bool(void)); @@ -37,33 +43,29 @@ class MockNetworkLibrary : public NetworkLibrary { MOCK_CONST_METHOD0(wifi_networks, const WifiNetworkVector&(void)); MOCK_CONST_METHOD0(remembered_wifi_networks, const WifiNetworkVector&(void)); MOCK_CONST_METHOD0(cellular_networks, const CellularNetworkVector&(void)); - MOCK_CONST_METHOD0(remembered_cellular_networks, - const CellularNetworkVector&(void)); - MOCK_CONST_METHOD2(FindWifiNetworkByPath, bool(const std::string&, - WifiNetwork*)); - MOCK_CONST_METHOD2(FindCellularNetworkByPath, bool(const std::string&, - CellularNetwork*)); + MOCK_METHOD1(FindWifiNetworkByPath, WifiNetwork*(const std::string&)); + MOCK_METHOD1(FindCellularNetworkByPath, CellularNetwork*(const std::string&)); MOCK_METHOD0(RequestWifiScan, void(void)); - MOCK_METHOD0(UpdateSystemInfo, void(void)); MOCK_METHOD1(GetWifiAccessPoints, bool(WifiAccessPointVector*)); - MOCK_METHOD4(ConnectToWifiNetwork, void(WifiNetwork, + MOCK_METHOD4(ConnectToWifiNetwork, bool(const WifiNetwork*, const std::string&, const std::string&, const std::string&)); - MOCK_METHOD5(ConnectToWifiNetwork, void(const std::string&, + MOCK_METHOD6(ConnectToWifiNetwork, bool(ConnectionSecurity security, + const std::string&, const std::string&, const std::string&, const std::string&, bool)); - MOCK_METHOD1(ConnectToCellularNetwork, void(CellularNetwork)); - MOCK_METHOD1(RefreshCellularDataPlans, void(const CellularNetwork& network)); + MOCK_METHOD1(ConnectToCellularNetwork, bool(const CellularNetwork*)); + MOCK_METHOD1(RefreshCellularDataPlans, void(const CellularNetwork* network)); - MOCK_METHOD1(DisconnectFromWirelessNetwork, void(const WirelessNetwork&)); - MOCK_METHOD1(SaveCellularNetwork, void(const CellularNetwork&)); - MOCK_METHOD1(SaveWifiNetwork, void(const WifiNetwork&)); - MOCK_METHOD1(ForgetWirelessNetwork, void(const std::string&)); + MOCK_METHOD1(DisconnectFromWirelessNetwork, void(const WirelessNetwork*)); + MOCK_METHOD1(SaveCellularNetwork, void(const CellularNetwork*)); + MOCK_METHOD1(SaveWifiNetwork, void(const WifiNetwork*)); + MOCK_METHOD1(ForgetWifiNetwork, void(const std::string&)); MOCK_CONST_METHOD0(ethernet_available, bool(void)); MOCK_CONST_METHOD0(wifi_available, bool(void)); @@ -73,6 +75,7 @@ class MockNetworkLibrary : public NetworkLibrary { MOCK_CONST_METHOD0(wifi_enabled, bool(void)); MOCK_CONST_METHOD0(cellular_enabled, bool(void)); + MOCK_CONST_METHOD0(active_network, const Network*(void)); MOCK_CONST_METHOD0(offline_mode, bool(void)); MOCK_METHOD1(EnableEthernetNetworkDevice, void(bool)); diff --git a/chrome/browser/chromeos/cros/mock_update_library.h b/chrome/browser/chromeos/cros/mock_update_library.h index d4f14cb..1b82342 100644 --- a/chrome/browser/chromeos/cros/mock_update_library.h +++ b/chrome/browser/chromeos/cros/mock_update_library.h @@ -20,6 +20,8 @@ class MockUpdateLibrary : public UpdateLibrary { MOCK_METHOD1(RemoveObserver, void(Observer*)); // NOLINT MOCK_METHOD0(CheckForUpdate, bool(void)); MOCK_METHOD0(RebootAfterUpdate, bool(void)); + MOCK_METHOD1(SetReleaseTrack, bool(const std::string&)); + MOCK_METHOD0(GetReleaseTrack, std::string()); MOCK_CONST_METHOD0(status, const Status&(void)); private: @@ -29,4 +31,3 @@ class MockUpdateLibrary : public UpdateLibrary { } // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_CROS_MOCK_UPDATE_LIBRARY_H_ - diff --git a/chrome/browser/chromeos/cros/network_library.cc b/chrome/browser/chromeos/cros/network_library.cc index 896f35c..7e878dd 100644 --- a/chrome/browser/chromeos/cros/network_library.cc +++ b/chrome/browser/chromeos/cros/network_library.cc @@ -5,17 +5,145 @@ #include "chrome/browser/chromeos/cros/network_library.h" #include <algorithm> +#include <map> #include "app/l10n_util.h" +#include "base/stl_util-inl.h" #include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" +#include "base/values.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "grit/generated_resources.h" +namespace { + +// FlimFlam may send multiple notifications for single network change. +// We wait small amount of time before retrieving the status to +// avoid send multiple sync request to flim flam. +const int kNetworkUpdateDelayMs = 50; + +} // namespace + namespace chromeos { +namespace { +// TODO(ers) These string constants and Parse functions are copied +// straight out of libcros:chromeos_network.cc. Fix this by moving +// all handling of properties into libcros. +// Network service properties we are interested in monitoring +static const char* kIsActiveProperty = "IsActive"; +static const char* kStateProperty = "State"; +static const char* kSignalStrengthProperty = "Strength"; +static const char* kActivationStateProperty = "Cellular.ActivationState"; +static const char* kNetworkTechnologyProperty = "Cellular.NetworkTechnology"; +static const char* kPaymentURLProperty = "Cellular.OlpUrl"; +static const char* kRestrictedPoolProperty = "Cellular.RestrictedPool"; +static const char* kRoamingStateProperty = "Cellular.RoamingState"; + +// Connman state options. +static const char* kStateIdle = "idle"; +static const char* kStateCarrier = "carrier"; +static const char* kStateAssociation = "association"; +static const char* kStateConfiguration = "configuration"; +static const char* kStateReady = "ready"; +static const char* kStateDisconnect = "disconnect"; +static const char* kStateFailure = "failure"; +static const char* kStateActivationFailure = "activation-failure"; + +// Connman activation state options +static const char* kActivationStateActivated = "activated"; +static const char* kActivationStateActivating = "activating"; +static const char* kActivationStateNotActivated = "not-activated"; +static const char* kActivationStatePartiallyActivated = "partially-activated"; +static const char* kActivationStateUnknown = "unknown"; + +// Connman network technology options. +static const char* kNetworkTechnology1Xrtt = "1xRTT"; +static const char* kNetworkTechnologyEvdo = "EVDO"; +static const char* kNetworkTechnologyGprs = "GPRS"; +static const char* kNetworkTechnologyEdge = "EDGE"; +static const char* kNetworkTechnologyUmts = "UMTS"; +static const char* kNetworkTechnologyHspa = "HSPA"; +static const char* kNetworkTechnologyHspaPlus = "HSPA+"; +static const char* kNetworkTechnologyLte = "LTE"; +static const char* kNetworkTechnologyLteAdvanced = "LTE Advanced"; + +// Connman roaming state options +static const char* kRoamingStateHome = "home"; +static const char* kRoamingStateRoaming = "roaming"; +static const char* kRoamingStateUnknown = "unknown"; + +static ConnectionState ParseState(const std::string& state) { + if (state == kStateIdle) + return STATE_IDLE; + if (state == kStateCarrier) + return STATE_CARRIER; + if (state == kStateAssociation) + return STATE_ASSOCIATION; + if (state == kStateConfiguration) + return STATE_CONFIGURATION; + if (state == kStateReady) + return STATE_READY; + if (state == kStateDisconnect) + return STATE_DISCONNECT; + if (state == kStateFailure) + return STATE_FAILURE; + if (state == kStateActivationFailure) + return STATE_ACTIVATION_FAILURE; + return STATE_UNKNOWN; +} + +static ActivationState ParseActivationState( + const std::string& activation_state) { + if (activation_state == kActivationStateActivated) + return ACTIVATION_STATE_ACTIVATED; + if (activation_state == kActivationStateActivating) + return ACTIVATION_STATE_ACTIVATING; + if (activation_state == kActivationStateNotActivated) + return ACTIVATION_STATE_NOT_ACTIVATED; + if (activation_state == kActivationStateUnknown) + return ACTIVATION_STATE_UNKNOWN; + if (activation_state == kActivationStatePartiallyActivated) + return ACTIVATION_STATE_PARTIALLY_ACTIVATED; + return ACTIVATION_STATE_UNKNOWN; +} + +static NetworkTechnology ParseNetworkTechnology( + const std::string& technology) { + if (technology == kNetworkTechnology1Xrtt) + return NETWORK_TECHNOLOGY_1XRTT; + if (technology == kNetworkTechnologyEvdo) + return NETWORK_TECHNOLOGY_EVDO; + if (technology == kNetworkTechnologyGprs) + return NETWORK_TECHNOLOGY_GPRS; + if (technology == kNetworkTechnologyEdge) + return NETWORK_TECHNOLOGY_EDGE; + if (technology == kNetworkTechnologyUmts) + return NETWORK_TECHNOLOGY_UMTS; + if (technology == kNetworkTechnologyHspa) + return NETWORK_TECHNOLOGY_HSPA; + if (technology == kNetworkTechnologyHspaPlus) + return NETWORK_TECHNOLOGY_HSPA_PLUS; + if (technology == kNetworkTechnologyLte) + return NETWORK_TECHNOLOGY_LTE; + if (technology == kNetworkTechnologyLteAdvanced) + return NETWORK_TECHNOLOGY_LTE_ADVANCED; + return NETWORK_TECHNOLOGY_UNKNOWN; +} +static NetworkRoamingState ParseRoamingState( + const std::string& roaming_state) { + if (roaming_state == kRoamingStateHome) + return ROAMING_STATE_HOME; + if (roaming_state == kRoamingStateRoaming) + return ROAMING_STATE_ROAMING; + if (roaming_state == kRoamingStateUnknown) + return ROAMING_STATE_UNKNOWN; + return ROAMING_STATE_UNKNOWN; +} +} + // Helper function to wrap Html with <th> tag. static std::string WrapWithTH(std::string text) { return "<th>" + text + "</th>"; @@ -82,24 +210,35 @@ static bool EnsureCrosLoaded() { //////////////////////////////////////////////////////////////////////////////// // Network +Network::Network(const Network& network) { + service_path_ = network.service_path(); + device_path_ = network.device_path(); + ip_address_ = network.ip_address(); + type_ = network.type(); + state_ = network.state(); + error_ = network.error(); +} + void Network::Clear() { state_ = STATE_UNKNOWN; error_ = ERROR_UNKNOWN; service_path_.clear(); device_path_.clear(); ip_address_.clear(); + is_active_ = false; } -void Network::ConfigureFromService(const ServiceInfo& service) { - type_ = service.type; - state_ = service.state; - error_ = service.error; - service_path_ = SafeString(service.service_path); - device_path_ = SafeString(service.device_path); +Network::Network(const ServiceInfo* service) { + type_ = service->type; + state_ = service->state; + error_ = service->error; + service_path_ = SafeString(service->service_path); + device_path_ = SafeString(service->device_path); + is_active_ = service->is_active; ip_address_.clear(); // If connected, get ip config. - if (EnsureCrosLoaded() && connected() && service.device_path) { - IPConfigStatus* ipconfig_status = ListIPConfigs(service.device_path); + if (EnsureCrosLoaded() && connected() && service->device_path) { + IPConfigStatus* ipconfig_status = ListIPConfigs(service->device_path); if (ipconfig_status) { for (int i = 0; i < ipconfig_status->size; i++) { IPConfig ipconfig = ipconfig_status->ips[i]; @@ -179,6 +318,21 @@ std::string Network::GetErrorString() const { //////////////////////////////////////////////////////////////////////////////// // WirelessNetwork +WirelessNetwork::WirelessNetwork(const WirelessNetwork& network) + : Network(network) { + name_ = network.name(); + strength_ = network.strength(); + auto_connect_ = network.auto_connect(); + favorite_ = network.favorite(); +} + +WirelessNetwork::WirelessNetwork(const ServiceInfo* service) + : Network(service) { + name_ = SafeString(service->name); + strength_ = service->strength; + auto_connect_ = service->auto_connect; + favorite_ = service->favorite; +} void WirelessNetwork::Clear() { Network::Clear(); @@ -188,14 +342,6 @@ void WirelessNetwork::Clear() { favorite_ = false; } -void WirelessNetwork::ConfigureFromService(const ServiceInfo& service) { - Network::ConfigureFromService(service); - name_ = SafeString(service.name); - strength_ = service.strength; - auto_connect_ = service.auto_connect; - favorite_ = service.favorite; -} - //////////////////////////////////////////////////////////////////////////////// // CellularNetwork @@ -210,6 +356,65 @@ CellularNetwork::CellularNetwork() type_ = TYPE_CELLULAR; } +CellularNetwork::CellularNetwork(const CellularNetwork& network) + : WirelessNetwork(network) { + activation_state_ = network.activation_state(); + network_technology_ = network.network_technology(); + roaming_state_ = network.roaming_state(); + restricted_pool_ = network.restricted_pool(); + service_name_ = network.service_name(); + operator_name_ = network.operator_name(); + operator_code_ = network.operator_code(); + payment_url_ = network.payment_url(); + meid_ = network.meid(); + imei_ = network.imei(); + imsi_ = network.imsi(); + esn_ = network.esn(); + mdn_ = network.mdn(); + min_ = network.min(); + model_id_ = network.model_id(); + manufacturer_ = network.manufacturer(); + firmware_revision_ = network.firmware_revision(); + hardware_revision_ = network.hardware_revision(); + last_update_ = network.last_update(); + prl_version_ = network.prl_version(); + type_ = TYPE_CELLULAR; +} + +CellularNetwork::CellularNetwork(const ServiceInfo* service) + : WirelessNetwork(service) { + service_name_ = SafeString(service->name); + activation_state_ = service->activation_state; + network_technology_ = service->network_technology; + roaming_state_ = service->roaming_state; + restricted_pool_ = service->restricted_pool; + // Carrier Info + if (service->carrier_info) { + operator_name_ = SafeString(service->carrier_info->operator_name); + operator_code_ = SafeString(service->carrier_info->operator_code); + payment_url_ = SafeString(service->carrier_info->payment_url); + } + // Device Info + if (service->device_info) { + meid_ = SafeString(service->device_info->MEID); + imei_ = SafeString(service->device_info->IMEI); + imsi_ = SafeString(service->device_info->IMSI); + esn_ = SafeString(service->device_info->ESN); + mdn_ = SafeString(service->device_info->MDN); + min_ = SafeString(service->device_info->MIN); + model_id_ = SafeString(service->device_info->model_id); + manufacturer_ = SafeString(service->device_info->manufacturer); + firmware_revision_ = SafeString(service->device_info->firmware_revision); + hardware_revision_ = SafeString(service->device_info->hardware_revision); + last_update_ = SafeString(service->device_info->last_update); + prl_version_ = service->device_info->PRL_version; + } + type_ = TYPE_CELLULAR; +} + +CellularNetwork::~CellularNetwork() { +} + bool CellularNetwork::StartActivation() const { if (!EnsureCrosLoaded()) return false; @@ -240,36 +445,6 @@ void CellularNetwork::Clear() { prl_version_ = 0; } -void CellularNetwork::ConfigureFromService(const ServiceInfo& service) { - WirelessNetwork::ConfigureFromService(service); - service_name_ = SafeString(service.name); - activation_state_ = service.activation_state; - network_technology_ = service.network_technology; - roaming_state_ = service.roaming_state; - restricted_pool_ = service.restricted_pool; - // Carrier Info - if (service.carrier_info) { - operator_name_ = SafeString(service.carrier_info->operator_name); - operator_code_ = SafeString(service.carrier_info->operator_code); - payment_url_ = SafeString(service.carrier_info->payment_url); - } - // Device Info - if (service.device_info) { - meid_ = SafeString(service.device_info->MEID); - imei_ = SafeString(service.device_info->IMEI); - imsi_ = SafeString(service.device_info->IMSI); - esn_ = SafeString(service.device_info->ESN); - mdn_ = SafeString(service.device_info->MDN); - min_ = SafeString(service.device_info->MIN); - model_id_ = SafeString(service.device_info->model_id); - manufacturer_ = SafeString(service.device_info->manufacturer); - firmware_revision_ = SafeString(service.device_info->firmware_revision); - hardware_revision_ = SafeString(service.device_info->hardware_revision); - last_update_ = SafeString(service.device_info->last_update); - prl_version_ = service.device_info->PRL_version; - } -} - bool CellularNetwork::is_gsm() const { return network_technology_ != NETWORK_TECHNOLOGY_EVDO && network_technology_ != NETWORK_TECHNOLOGY_1XRTT && @@ -279,14 +454,15 @@ bool CellularNetwork::is_gsm() const { CellularNetwork::DataLeft CellularNetwork::data_left() const { if (data_plans_.empty()) return DATA_NORMAL; - CellularDataPlan plan = data_plans_[0]; + const CellularDataPlan& plan(data_plans_[0]); if (plan.plan_type == CELLULAR_DATA_PLAN_UNLIMITED) { - int64 remaining = plan.plan_end_time - plan.update_time; - if (remaining <= 0) + base::TimeDelta remaining = plan.plan_end_time - plan.update_time; + if (remaining <= base::TimeDelta::FromSeconds(0)) return DATA_NONE; - else if (remaining <= kCellularDataVeryLowSecs) + else if (remaining <= + base::TimeDelta::FromSeconds(kCellularDataVeryLowSecs)) return DATA_VERY_LOW; - else if (remaining <= kCellularDataLowSecs) + else if (remaining <= base::TimeDelta::FromSeconds(kCellularDataLowSecs)) return DATA_LOW; else return DATA_NORMAL; @@ -342,8 +518,9 @@ std::string CellularNetwork::GetNetworkTechnologyString() const { } } -std::string CellularNetwork::GetActivationStateString() const { - switch (this->activation_state_) { +std::string CellularNetwork::ActivationStateToString( + ActivationState activation_state) { + switch (activation_state) { case ACTIVATION_STATE_ACTIVATED: return l10n_util::GetStringUTF8( IDS_CHROMEOS_NETWORK_ACTIVATION_STATE_ACTIVATED); @@ -367,6 +544,10 @@ std::string CellularNetwork::GetActivationStateString() const { } } +std::string CellularNetwork::GetActivationStateString() const { + return ActivationStateToString(this->activation_state_); +} + std::string CellularNetwork::GetRoamingStateString() const { switch (this->roaming_state_) { case ROAMING_STATE_HOME: @@ -394,8 +575,28 @@ WifiNetwork::WifiNetwork() type_ = TYPE_WIFI; } -WifiNetwork::WifiNetwork(const ServiceInfo& service) : WirelessNetwork() { - ConfigureFromService(service); +WifiNetwork::WifiNetwork(const WifiNetwork& network) + : WirelessNetwork(network) { + encryption_ = network.encryption(); + passphrase_ = network.passphrase(); + passphrase_required_ = network.passphrase_required(); + identity_ = network.identity(); + cert_path_ = network.cert_path(); +} + +WifiNetwork::WifiNetwork(const ServiceInfo* service) + : WirelessNetwork(service) { + encryption_ = service->security; + passphrase_ = SafeString(service->passphrase); + // TODO(stevenjb): Remove this once flimflam is setting passphrase_required + // correctly: http://crosbug.com/8830. + if (service->state == chromeos::STATE_FAILURE && + service->security != chromeos::SECURITY_NONE) + passphrase_required_ = true; + else + passphrase_required_ = service->passphrase_required; + identity_ = SafeString(service->identity); + cert_path_ = SafeString(service->cert_path); type_ = TYPE_WIFI; } @@ -407,14 +608,6 @@ void WifiNetwork::Clear() { cert_path_.clear(); } -void WifiNetwork::ConfigureFromService(const ServiceInfo& service) { - WirelessNetwork::ConfigureFromService(service); - encryption_ = service.security; - passphrase_ = SafeString(service.passphrase); - identity_ = SafeString(service.identity); - cert_path_ = SafeString(service.cert_path); -} - std::string WifiNetwork::GetEncryptionString() { switch (encryption_) { case SECURITY_UNKNOWN: @@ -457,47 +650,145 @@ bool WifiNetwork::IsCertificateLoaded() const { class NetworkLibraryImpl : public NetworkLibrary { public: NetworkLibraryImpl() - : network_status_connection_(NULL), + : network_manager_monitor_(NULL), data_plan_monitor_(NULL), + ethernet_(NULL), + wifi_(NULL), + cellular_(NULL), available_devices_(0), enabled_devices_(0), connected_devices_(0), - offline_mode_(false) { + offline_mode_(false), + update_task_(NULL) { if (EnsureCrosLoaded()) { Init(); + network_manager_monitor_ = + MonitorNetworkManager(&NetworkManagerStatusChangedHandler, + this); + data_plan_monitor_ = MonitorCellularDataPlan(&DataPlanUpdateHandler, + this); } else { InitTestData(); } } ~NetworkLibraryImpl() { - if (network_status_connection_) { - DisconnectMonitorNetwork(network_status_connection_); - } - if (data_plan_monitor_) { + network_manager_observers_.Clear(); + if (network_manager_monitor_) + DisconnectPropertyChangeMonitor(network_manager_monitor_); + data_plan_observers_.Clear(); + if (data_plan_monitor_) DisconnectDataPlanUpdateMonitor(data_plan_monitor_); + STLDeleteValues(&network_observers_); + ClearNetworks(); + } + + virtual void AddNetworkManagerObserver(NetworkManagerObserver* observer) { + if (!network_manager_observers_.HasObserver(observer)) + network_manager_observers_.AddObserver(observer); + } + + void NetworkStatusChanged() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (update_task_) { + update_task_->Cancel(); + } + update_task_ = + NewRunnableMethod(this, + &NetworkLibraryImpl::UpdateNetworkManagerStatus); + BrowserThread::PostDelayedTask( + BrowserThread::UI, FROM_HERE, update_task_, + kNetworkUpdateDelayMs); + } + + virtual void RemoveNetworkManagerObserver(NetworkManagerObserver* observer) { + network_manager_observers_.RemoveObserver(observer); + } + + virtual void AddNetworkObserver(const std::string& service_path, + NetworkObserver* observer) { + DCHECK(observer); + if (!EnsureCrosLoaded()) + return; + // First, add the observer to the callback map. + NetworkObserverMap::iterator iter = network_observers_.find(service_path); + NetworkObserverList* oblist; + if (iter != network_observers_.end()) { + oblist = iter->second; + } else { + std::pair<NetworkObserverMap::iterator, bool> inserted = + network_observers_.insert( + std::make_pair<std::string, NetworkObserverList*>( + service_path, + new NetworkObserverList(this, service_path))); + oblist = inserted.first->second; + } + if (!oblist->HasObserver(observer)) + oblist->AddObserver(observer); + } + + virtual void RemoveNetworkObserver(const std::string& service_path, + NetworkObserver* observer) { + DCHECK(observer); + DCHECK(service_path.size()); + NetworkObserverMap::iterator map_iter = + network_observers_.find(service_path); + if (map_iter != network_observers_.end()) { + map_iter->second->RemoveObserver(observer); + if (!map_iter->second->size()) { + delete map_iter->second; + network_observers_.erase(map_iter++); + } } } - void AddObserver(Observer* observer) { - observers_.AddObserver(observer); + virtual void RemoveObserverForAllNetworks(NetworkObserver* observer) { + DCHECK(observer); + NetworkObserverMap::iterator map_iter = network_observers_.begin(); + while (map_iter != network_observers_.end()) { + map_iter->second->RemoveObserver(observer); + if (!map_iter->second->size()) { + delete map_iter->second; + network_observers_.erase(map_iter++); + } else { + ++map_iter; + } + } } - void RemoveObserver(Observer* observer) { - observers_.RemoveObserver(observer); + virtual void AddCellularDataPlanObserver(CellularDataPlanObserver* observer) { + if (!data_plan_observers_.HasObserver(observer)) + data_plan_observers_.AddObserver(observer); } - virtual const EthernetNetwork& ethernet_network() const { return ethernet_; } - virtual bool ethernet_connecting() const { return ethernet_.connecting(); } - virtual bool ethernet_connected() const { return ethernet_.connected(); } + virtual void RemoveCellularDataPlanObserver( + CellularDataPlanObserver* observer) { + data_plan_observers_.RemoveObserver(observer); + } - virtual const WifiNetwork& wifi_network() const { return wifi_; } - virtual bool wifi_connecting() const { return wifi_.connecting(); } - virtual bool wifi_connected() const { return wifi_.connected(); } + virtual EthernetNetwork* ethernet_network() { return ethernet_; } + virtual bool ethernet_connecting() const { + return ethernet_ ? ethernet_->connecting() : false; + } + virtual bool ethernet_connected() const { + return ethernet_ ? ethernet_->connected() : false; + } - virtual const CellularNetwork& cellular_network() const { return cellular_; } - virtual bool cellular_connecting() const { return cellular_.connecting(); } - virtual bool cellular_connected() const { return cellular_.connected(); } + virtual WifiNetwork* wifi_network() { return wifi_; } + virtual bool wifi_connecting() const { + return wifi_ ? wifi_->connecting() : false; + } + virtual bool wifi_connected() const { + return wifi_ ? wifi_->connected() : false; + } + + virtual CellularNetwork* cellular_network() { return cellular_; } + virtual bool cellular_connecting() const { + return cellular_ ? cellular_->connecting() : false; + } + virtual bool cellular_connected() const { + return cellular_ ? cellular_->connected() : false; + } bool Connected() const { return ethernet_connected() || wifi_connected() || cellular_connected(); @@ -508,14 +799,14 @@ class NetworkLibraryImpl : public NetworkLibrary { } const std::string& IPAddress() const { - // Returns highest priority IP address. - if (ethernet_connected()) - return ethernet_.ip_address(); - if (wifi_connected()) - return wifi_.ip_address(); - if (cellular_connected()) - return cellular_.ip_address(); - return ethernet_.ip_address(); + // Returns IP address for the active network. + const Network* active = active_network(); + if (active != NULL) + return active->ip_address(); + if (ethernet_) + return ethernet_->ip_address(); + static std::string null_address("0.0.0.0"); + return null_address; } virtual const WifiNetworkVector& wifi_networks() const { @@ -530,34 +821,16 @@ class NetworkLibraryImpl : public NetworkLibrary { return cellular_networks_; } - virtual const CellularNetworkVector& remembered_cellular_networks() const { - return remembered_cellular_networks_; - } - ///////////////////////////////////////////////////////////////////////////// - virtual bool FindWifiNetworkByPath( - const std::string& path, WifiNetwork* result) const { - const WifiNetwork* wifi = - GetWirelessNetworkByPath(wifi_networks_, path); - if (wifi) { - if (result) - *result = *wifi; - return true; - } - return false; + virtual WifiNetwork* FindWifiNetworkByPath( + const std::string& path) { + return GetWirelessNetworkByPath(wifi_networks_, path); } - virtual bool FindCellularNetworkByPath( - const std::string& path, CellularNetwork* result) const { - const CellularNetwork* cellular = - GetWirelessNetworkByPath(cellular_networks_, path); - if (cellular) { - if (result) - *result = *cellular; - return true; - } - return false; + virtual CellularNetwork* FindCellularNetworkByPath( + const std::string& path) { + return GetWirelessNetworkByPath(cellular_networks_, path); } virtual void RequestWifiScan() { @@ -591,148 +864,158 @@ class NetworkLibraryImpl : public NetworkLibrary { return true; } - virtual void ConnectToWifiNetwork(WifiNetwork network, + virtual bool ConnectToWifiNetwork(const WifiNetwork* network, const std::string& password, const std::string& identity, const std::string& certpath) { - if (EnsureCrosLoaded()) { - if (ConnectToNetworkWithCertInfo(network.service_path().c_str(), - password.empty() ? NULL : password.c_str(), - identity.empty() ? NULL : identity.c_str(), - certpath.empty() ? NULL : certpath.c_str())) { - // Update local cache and notify listeners. - WifiNetwork* wifi = GetWirelessNetworkByPath( - wifi_networks_, network.service_path()); - if (wifi) { - wifi->set_passphrase(password); - wifi->set_identity(identity); - wifi->set_cert_path(certpath); - wifi->set_connecting(true); - wifi_ = *wifi; - } - NotifyNetworkChanged(); + DCHECK(network); + if (!EnsureCrosLoaded()) + return true; // No library loaded, don't trigger a retry attempt. + // TODO(ers) make wifi the highest priority service type + if (ConnectToNetworkWithCertInfo(network->service_path().c_str(), + password.empty() ? NULL : password.c_str(), + identity.empty() ? NULL : identity.c_str(), + certpath.empty() ? NULL : certpath.c_str())) { + // Update local cache and notify listeners. + WifiNetwork* wifi = GetWirelessNetworkByPath( + wifi_networks_, network->service_path()); + if (wifi) { + wifi->set_passphrase(password); + wifi->set_identity(identity); + wifi->set_cert_path(certpath); + wifi->set_connecting(true); + wifi_ = wifi; } + NotifyNetworkManagerChanged(); + return true; + } else { + return false; // Immediate failure. } } - virtual void ConnectToWifiNetwork(const std::string& ssid, + virtual bool ConnectToWifiNetwork(ConnectionSecurity security, + const std::string& ssid, const std::string& password, const std::string& identity, const std::string& certpath, bool auto_connect) { - if (EnsureCrosLoaded()) { - // First create a service from hidden network. - ServiceInfo* service = GetWifiService(ssid.c_str(), - SECURITY_UNKNOWN); - if (service) { - // Set auto-connect. - SetAutoConnect(service->service_path, auto_connect); - // Now connect to that service. - ConnectToNetworkWithCertInfo(service->service_path, - password.empty() ? NULL : password.c_str(), - identity.empty() ? NULL : identity.c_str(), - certpath.empty() ? NULL : certpath.c_str()); - - // Clean up ServiceInfo object. - FreeServiceInfo(service); - } else { - LOG(WARNING) << "Cannot find hidden network: " << ssid; - // TODO(chocobo): Show error message. - } + if (!EnsureCrosLoaded()) + return true; // No library loaded, don't trigger a retry attempt. + // First create a service from hidden network. + ServiceInfo* service = GetWifiService(ssid.c_str(), security); + if (service) { + // Set auto-connect. + SetAutoConnect(service->service_path, auto_connect); + // Now connect to that service. + // TODO(ers) make wifi the highest priority service type + bool res = ConnectToNetworkWithCertInfo( + service->service_path, + password.empty() ? NULL : password.c_str(), + identity.empty() ? NULL : identity.c_str(), + certpath.empty() ? NULL : certpath.c_str()); + + // Clean up ServiceInfo object. + FreeServiceInfo(service); + return res; + } else { + LOG(WARNING) << "Cannot find hidden network: " << ssid; + // TODO(chocobo): Show error message. + return false; // Immediate failure. } } - virtual void ConnectToCellularNetwork(CellularNetwork network) { - if (EnsureCrosLoaded()) { - if (ConnectToNetwork(network.service_path().c_str(), NULL)) { - // Update local cache and notify listeners. - CellularNetwork* cellular = GetWirelessNetworkByPath( - cellular_networks_, network.service_path()); - if (cellular) { - cellular->set_connecting(true); - cellular_ = *cellular; - } - NotifyNetworkChanged(); + virtual bool ConnectToCellularNetwork(const CellularNetwork* network) { + DCHECK(network); + if (!EnsureCrosLoaded()) + return true; // No library loaded, don't trigger a retry attempt. + // TODO(ers) make cellular the highest priority service type + if (network && ConnectToNetwork(network->service_path().c_str(), NULL)) { + // Update local cache and notify listeners. + CellularNetwork* cellular = GetWirelessNetworkByPath( + cellular_networks_, network->service_path()); + if (cellular) { + cellular->set_connecting(true); + cellular_ = cellular; } + NotifyNetworkManagerChanged(); + return true; + } else { + return false; // Immediate failure. } } - virtual void RefreshCellularDataPlans(const CellularNetwork& network) { - if (!EnsureCrosLoaded()) + virtual void RefreshCellularDataPlans(const CellularNetwork* network) { + DCHECK(network); + if (!EnsureCrosLoaded() || !network) return; - RequestCellularDataPlanUpdate(network.service_path().c_str()); + RequestCellularDataPlanUpdate(network->service_path().c_str()); } - virtual void DisconnectFromWirelessNetwork(const WirelessNetwork& network) { - if (EnsureCrosLoaded()) { - if (DisconnectFromNetwork(network.service_path().c_str())) { - // Update local cache and notify listeners. - if (network.type() == TYPE_WIFI) { - WifiNetwork* wifi = GetWirelessNetworkByPath( - wifi_networks_, network.service_path()); - if (wifi) { - wifi->set_connected(false); - wifi_ = WifiNetwork(); - } - } else if (network.type() == TYPE_CELLULAR) { - CellularNetwork* cellular = GetWirelessNetworkByPath( - cellular_networks_, network.service_path()); - if (cellular) { - cellular->set_connected(false); - cellular_ = CellularNetwork(); - } + virtual void DisconnectFromWirelessNetwork(const WirelessNetwork* network) { + DCHECK(network); + if (!EnsureCrosLoaded() || !network) + return; + // TODO(ers) restore default service type priority ordering? + if (DisconnectFromNetwork(network->service_path().c_str())) { + // Update local cache and notify listeners. + if (network->type() == TYPE_WIFI) { + WifiNetwork* wifi = GetWirelessNetworkByPath( + wifi_networks_, network->service_path()); + if (wifi) { + wifi->set_connected(false); + wifi_ = NULL; + } + } else if (network->type() == TYPE_CELLULAR) { + CellularNetwork* cellular = GetWirelessNetworkByPath( + cellular_networks_, network->service_path()); + if (cellular) { + cellular->set_connected(false); + cellular_ = NULL; } - NotifyNetworkChanged(); } + NotifyNetworkManagerChanged(); } } - virtual void SaveCellularNetwork(const CellularNetwork& network) { - // Update the wifi network in the local cache. - CellularNetwork* cellular = GetWirelessNetworkByPath( - cellular_networks_, network.service_path()); - if (cellular) - *cellular = network; - + virtual void SaveCellularNetwork(const CellularNetwork* network) { + DCHECK(network); // Update the cellular network with libcros. - if (EnsureCrosLoaded()) { - SetAutoConnect(network.service_path().c_str(), network.auto_connect()); - } - } + if (!EnsureCrosLoaded() || !network) + return; - virtual void SaveWifiNetwork(const WifiNetwork& network) { - // Update the wifi network in the local cache. - WifiNetwork* wifi = GetWirelessNetworkByPath( - wifi_networks_, network.service_path()); - if (wifi) - *wifi = network; + SetAutoConnect(network->service_path().c_str(), network->auto_connect()); + } + virtual void SaveWifiNetwork(const WifiNetwork* network) { + DCHECK(network); // Update the wifi network with libcros. - if (EnsureCrosLoaded()) { - SetPassphrase( - network.service_path().c_str(), network.passphrase().c_str()); - SetIdentity(network.service_path().c_str(), network.identity().c_str()); - SetCertPath(network.service_path().c_str(), network.cert_path().c_str()); - SetAutoConnect(network.service_path().c_str(), network.auto_connect()); - } + if (!EnsureCrosLoaded() || !network) + return; + SetPassphrase( + network->service_path().c_str(), network->passphrase().c_str()); + SetIdentity(network->service_path().c_str(), + network->identity().c_str()); + SetCertPath(network->service_path().c_str(), + network->cert_path().c_str()); + SetAutoConnect(network->service_path().c_str(), network->auto_connect()); } - virtual void ForgetWirelessNetwork(const std::string& service_path) { - if (EnsureCrosLoaded()) { - if (DeleteRememberedService(service_path.c_str())) { - // Update local cache and notify listeners. - remembered_wifi_networks_.erase( - std::remove_if(remembered_wifi_networks_.begin(), - remembered_wifi_networks_.end(), - WirelessNetwork::ServicePathEq(service_path)), - remembered_wifi_networks_.end()); - remembered_cellular_networks_.erase( - std::remove_if(remembered_cellular_networks_.begin(), - remembered_cellular_networks_.end(), - WirelessNetwork::ServicePathEq(service_path)), - remembered_cellular_networks_.end()); - NotifyNetworkChanged(); + virtual void ForgetWifiNetwork(const std::string& service_path) { + if (!EnsureCrosLoaded()) + return; + if (DeleteRememberedService(service_path.c_str())) { + // Update local cache and notify listeners. + for (WifiNetworkVector::iterator iter = + remembered_wifi_networks_.begin(); + iter != remembered_wifi_networks_.end(); + ++iter) { + if ((*iter)->service_path() == service_path) { + delete (*iter); + remembered_wifi_networks_.erase(iter); + break; + } } + NotifyNetworkManagerChanged(); } } @@ -758,6 +1041,16 @@ class NetworkLibraryImpl : public NetworkLibrary { virtual bool offline_mode() const { return offline_mode_; } + virtual const Network* active_network() const { + if (ethernet_ && ethernet_->is_active()) + return ethernet_; + if (wifi_ && wifi_->is_active()) + return wifi_; + if (cellular_ && cellular_->is_active()) + return cellular_; + return NULL; + } + virtual void EnableEthernetNetworkDevice(bool enable) { EnableNetworkDeviceType(TYPE_ETHERNET, enable); } @@ -776,12 +1069,11 @@ class NetworkLibraryImpl : public NetworkLibrary { // If network device is already enabled/disabled, then don't do anything. if (enable && offline_mode_) { - LOG(INFO) << "Trying to enable offline mode when it's already enabled. "; + VLOG(1) << "Trying to enable offline mode when it's already enabled."; return; } if (!enable && !offline_mode_) { - LOG(INFO) << - "Trying to disable offline mode when it's already disabled. "; + VLOG(1) << "Trying to disable offline mode when it's already disabled."; return; } @@ -828,42 +1120,33 @@ class NetworkLibraryImpl : public NetworkLibrary { } output.append("<h3>Ethernet:</h3><table border=1>"); - if (ethernet_enabled()) { - output.append("<tr>" + ToHtmlTableHeader(ðernet_) + "</tr>"); - output.append("<tr>" + ToHtmlTableRow(ðernet_) + "</tr>"); + if (ethernet_ && ethernet_enabled()) { + output.append("<tr>" + ToHtmlTableHeader(ethernet_) + "</tr>"); + output.append("<tr>" + ToHtmlTableRow(ethernet_) + "</tr>"); } output.append("</table><h3>Wifi:</h3><table border=1>"); for (size_t i = 0; i < wifi_networks_.size(); ++i) { if (i == 0) - output.append("<tr>" + ToHtmlTableHeader(&wifi_networks_[i]) + "</tr>"); - output.append("<tr>" + ToHtmlTableRow(&wifi_networks_[i]) + "</tr>"); + output.append("<tr>" + ToHtmlTableHeader(wifi_networks_[i]) + "</tr>"); + output.append("<tr>" + ToHtmlTableRow(wifi_networks_[i]) + "</tr>"); } output.append("</table><h3>Cellular:</h3><table border=1>"); for (size_t i = 0; i < cellular_networks_.size(); ++i) { if (i == 0) - output.append("<tr>" + ToHtmlTableHeader(&cellular_networks_[i]) + + output.append("<tr>" + ToHtmlTableHeader(cellular_networks_[i]) + "</tr>"); - output.append("<tr>" + ToHtmlTableRow(&cellular_networks_[i]) + "</tr>"); + output.append("<tr>" + ToHtmlTableRow(cellular_networks_[i]) + "</tr>"); } output.append("</table><h3>Remembered Wifi:</h3><table border=1>"); for (size_t i = 0; i < remembered_wifi_networks_.size(); ++i) { if (i == 0) output.append( - "<tr>" + ToHtmlTableHeader(&remembered_wifi_networks_[i]) + + "<tr>" + ToHtmlTableHeader(remembered_wifi_networks_[i]) + "</tr>"); - output.append("<tr>" + ToHtmlTableRow(&remembered_wifi_networks_[i]) + - "</tr>"); - } - - output.append("</table><h3>Remembered Cellular:</h3><table border=1>"); - for (size_t i = 0; i < remembered_cellular_networks_.size(); ++i) { - if (i == 0) - output.append("<tr>" + - ToHtmlTableHeader(&remembered_cellular_networks_[i]) + "</tr>"); - output.append("<tr>" + ToHtmlTableRow(&remembered_cellular_networks_[i]) + + output.append("<tr>" + ToHtmlTableRow(remembered_wifi_networks_[i]) + "</tr>"); } @@ -872,157 +1155,175 @@ class NetworkLibraryImpl : public NetworkLibrary { } private: - static void NetworkStatusChangedHandler(void* object) { - NetworkLibraryImpl* network = static_cast<NetworkLibraryImpl*>(object); - DCHECK(network); - network->UpdateNetworkStatus(); + + class NetworkObserverList : public ObserverList<NetworkObserver> { + public: + NetworkObserverList(NetworkLibraryImpl* library, + const std::string& service_path) { + network_monitor_ = MonitorNetworkService(&NetworkStatusChangedHandler, + service_path.c_str(), + library); + } + + virtual ~NetworkObserverList() { + if (network_monitor_) + DisconnectPropertyChangeMonitor(network_monitor_); + } + + private: + static void NetworkStatusChangedHandler(void* object, + const char* path, + const char* key, + const Value* value) { + NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object); + DCHECK(networklib); + networklib->UpdateNetworkStatus(path, key, value); + } + PropertyChangeMonitor network_monitor_; + }; + + typedef std::map<std::string, NetworkObserverList*> NetworkObserverMap; + + static void NetworkManagerStatusChangedHandler(void* object, + const char* path, + const char* key, + const Value* value) { + NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object); + DCHECK(networklib); + networklib->NetworkStatusChanged(); } static void DataPlanUpdateHandler(void* object, const char* modem_service_path, const CellularDataPlanList* dataplan) { - NetworkLibraryImpl* network = static_cast<NetworkLibraryImpl*>(object); - DCHECK(network); + NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object); + if (!networklib || !networklib->cellular_network()) { + // This might happen if an update is received as we are shutting down. + return; + } // Store data plan for currently connected cellular network. - if (network->cellular_network().service_path() + if (networklib->cellular_network()->service_path() .compare(modem_service_path) == 0) { if (dataplan != NULL) { - network->UpdateCellularDataPlan(*dataplan); + networklib->UpdateCellularDataPlan(dataplan); } } } static void ParseSystem(SystemInfo* system, - EthernetNetwork* ethernet, + EthernetNetwork** ethernet, WifiNetworkVector* wifi_networks, CellularNetworkVector* cellular_networks, - WifiNetworkVector* remembered_wifi_networks, - CellularNetworkVector* remembered_cellular_networks) { - DLOG(INFO) << "ParseSystem:"; - ethernet->Clear(); + WifiNetworkVector* remembered_wifi_networks) { + DVLOG(1) << "ParseSystem:"; + DCHECK(!(*ethernet)); for (int i = 0; i < system->service_size; i++) { - const ServiceInfo service = *system->GetServiceInfo(i); - DLOG(INFO) << " (" << service.type << - ") " << service.name << - " mode=" << service.mode << - " state=" << service.state << - " sec=" << service.security << - " req=" << service.passphrase_required << - " pass=" << service.passphrase << - " id=" << service.identity << - " certpath=" << service.cert_path << - " str=" << service.strength << - " fav=" << service.favorite << - " auto=" << service.auto_connect << - " error=" << service.error; + const ServiceInfo* service = system->GetServiceInfo(i); + DVLOG(1) << " (" << service->type << ") " << service->name + << " mode=" << service->mode + << " state=" << service->state + << " sec=" << service->security + << " req=" << service->passphrase_required + << " pass=" << service->passphrase + << " id=" << service->identity + << " certpath=" << service->cert_path + << " str=" << service->strength + << " fav=" << service->favorite + << " auto=" << service->auto_connect + << " is_active=" << service->is_active + << " error=" << service->error; // Once a connected ethernet service is found, disregard other ethernet // services that are also found - if (service.type == TYPE_ETHERNET && !(ethernet->connected())) - ethernet->ConfigureFromService(service); - else if (service.type == TYPE_WIFI) - wifi_networks->push_back(WifiNetwork(service)); - else if (service.type == TYPE_CELLULAR) - cellular_networks->push_back(CellularNetwork(service)); + if (service->type == TYPE_ETHERNET) + (*ethernet) = new EthernetNetwork(service); + else if (service->type == TYPE_WIFI) { + wifi_networks->push_back(new WifiNetwork(service)); + } else if (service->type == TYPE_CELLULAR) { + cellular_networks->push_back(new CellularNetwork(service)); + } } - DLOG(INFO) << "Remembered networks:"; + + // Create placeholder network for ethernet even if the service is not + // detected at this moment. + if (!(*ethernet)) + (*ethernet) = new EthernetNetwork(); + + DVLOG(1) << "Remembered networks:"; for (int i = 0; i < system->remembered_service_size; i++) { - const ServiceInfo& service = *system->GetRememberedServiceInfo(i); - // Only serices marked as auto_connect are considered remembered networks. + const ServiceInfo* service = system->GetRememberedServiceInfo(i); + // Only services marked as auto_connect are considered remembered + // networks. // TODO(chocobo): Don't add to remembered service if currently available. - if (service.auto_connect) { - DLOG(INFO) << " (" << service.type << - ") " << service.name << - " mode=" << service.mode << - " sec=" << service.security << - " pass=" << service.passphrase << - " id=" << service.identity << - " certpath=" << service.cert_path << - " auto=" << service.auto_connect; - if (service.type == TYPE_WIFI) - remembered_wifi_networks->push_back(WifiNetwork(service)); - else if (service.type == TYPE_CELLULAR) - remembered_cellular_networks->push_back(CellularNetwork(service)); + if (service->auto_connect) { + DVLOG(1) << " (" << service->type << ") " << service->name + << " mode=" << service->mode + << " sec=" << service->security + << " pass=" << service->passphrase + << " id=" << service->identity + << " certpath=" << service->cert_path + << " auto=" << service->auto_connect; + if (service->type == TYPE_WIFI) { + remembered_wifi_networks->push_back(new WifiNetwork(service)); + } } } } void Init() { - // First, get the currently available networks. This data is cached + // First, get the currently available networks. This data is cached // on the connman side, so the call should be quick. - LOG(INFO) << "Getting initial CrOS network info."; + VLOG(1) << "Getting initial CrOS network info."; UpdateSystemInfo(); - - LOG(INFO) << "Registering for network status updates."; - // Now, register to receive updates on network status. - network_status_connection_ = MonitorNetwork(&NetworkStatusChangedHandler, - this); - LOG(INFO) << "Registering for cellular data plan updates."; - data_plan_monitor_ = MonitorCellularDataPlan(&DataPlanUpdateHandler, this); } void InitTestData() { - ethernet_.Clear(); - ethernet_.set_connected(true); - ethernet_.set_service_path("eth1"); + ethernet_ = new EthernetNetwork(); + ethernet_->set_connected(true); + ethernet_->set_service_path("eth1"); + STLDeleteElements(&wifi_networks_); wifi_networks_.clear(); - WifiNetwork wifi1 = WifiNetwork(); - wifi1.set_service_path("fw1"); - wifi1.set_name("Fake Wifi 1"); - wifi1.set_strength(90); - wifi1.set_connected(false); - wifi1.set_encryption(SECURITY_NONE); + WifiNetwork* wifi1 = new WifiNetwork(); + wifi1->set_service_path("fw1"); + wifi1->set_name("Fake Wifi 1"); + wifi1->set_strength(90); + wifi1->set_connected(false); + wifi1->set_encryption(SECURITY_NONE); wifi_networks_.push_back(wifi1); - WifiNetwork wifi2 = WifiNetwork(); - wifi2.set_service_path("fw2"); - wifi2.set_name("Fake Wifi 2"); - wifi2.set_strength(70); - wifi2.set_connected(true); - wifi2.set_encryption(SECURITY_WEP); + WifiNetwork* wifi2 = new WifiNetwork(); + wifi2->set_service_path("fw2"); + wifi2->set_name("Fake Wifi 2"); + wifi2->set_strength(70); + wifi2->set_connected(true); + wifi2->set_encryption(SECURITY_WEP); wifi_networks_.push_back(wifi2); - WifiNetwork wifi3 = WifiNetwork(); - wifi3.set_service_path("fw3"); - wifi3.set_name("Fake Wifi 3"); - wifi3.set_strength(50); - wifi3.set_connected(false); - wifi3.set_encryption(SECURITY_WEP); + WifiNetwork* wifi3 = new WifiNetwork(); + wifi3->set_service_path("fw3"); + wifi3->set_name("Fake Wifi 3"); + wifi3->set_strength(50); + wifi3->set_connected(false); + wifi3->set_encryption(SECURITY_WEP); wifi_networks_.push_back(wifi3); wifi_ = wifi2; + STLDeleteElements(&cellular_networks_); cellular_networks_.clear(); - cellular_networks_.clear(); - CellularNetwork cellular1 = CellularNetwork(); - cellular1.set_service_path("fc1"); - cellular1.set_name("Fake Cellular 1"); - cellular1.set_strength(90); - cellular1.set_connected(false); + CellularNetwork* cellular1 = new CellularNetwork(); + cellular1->set_service_path("fc1"); + cellular1->set_name("Fake Cellular 1"); + cellular1->set_strength(70); + cellular1->set_connected(true); + cellular1->set_activation_state(ACTIVATION_STATE_PARTIALLY_ACTIVATED); + cellular1->set_payment_url(std::string("http://www.google.com")); cellular_networks_.push_back(cellular1); - - CellularNetwork cellular2 = CellularNetwork(); - cellular2.set_service_path("fc2"); - cellular2.set_name("Fake Cellular 2"); - cellular2.set_strength(70); - cellular2.set_connected(true); - cellular_networks_.push_back(cellular2); - - CellularNetwork cellular3 = CellularNetwork(); - cellular3.set_service_path("fc3"); - cellular3.set_name("Fake Cellular 3"); - cellular3.set_strength(50); - cellular3.set_connected(false); - cellular_networks_.push_back(cellular3); - - cellular_ = cellular2; + cellular_ = cellular1; remembered_wifi_networks_.clear(); - remembered_wifi_networks_.push_back(wifi2); - - remembered_cellular_networks_.clear(); - remembered_cellular_networks_.push_back(cellular2); + remembered_wifi_networks_.push_back(new WifiNetwork(*wifi2)); int devices = (1 << TYPE_ETHERNET) | (1 << TYPE_WIFI) | (1 << TYPE_CELLULAR); @@ -1030,55 +1331,38 @@ class NetworkLibraryImpl : public NetworkLibrary { enabled_devices_ = devices; connected_devices_ = devices; offline_mode_ = false; - - chromeos::CellularDataPlan test_plan; - test_plan.plan_name = "Fake plan"; - test_plan.data_bytes_used = 5LL * 1024LL * 1024LL * 1024LL; - test_plan.plan_start_time = - (base::Time::Now() - base::TimeDelta::FromDays(15)).ToInternalValue() / - base::Time::kMicrosecondsPerSecond; - test_plan.plan_end_time = - (base::Time::Now() + base::TimeDelta::FromDays(12)).ToInternalValue() / - base::Time::kMicrosecondsPerSecond; - test_plan.plan_data_bytes = 20LL * 1024LL * 1024LL * 1024LL; - test_plan.plan_type = CELLULAR_DATA_PLAN_METERED_PAID; - test_plan.update_time = base::Time::Now().ToInternalValue() / - base::Time::kMicrosecondsPerSecond; - chromeos::CellularDataPlanList test_plans; - test_plans.push_back(test_plan); - cellular_.SetDataPlans(test_plans); } void UpdateSystemInfo() { if (EnsureCrosLoaded()) { - UpdateNetworkStatus(); + UpdateNetworkManagerStatus(); } } WifiNetwork* GetWifiNetworkByName(const std::string& name) { for (size_t i = 0; i < wifi_networks_.size(); ++i) { - if (wifi_networks_[i].name().compare(name) == 0) { - return &wifi_networks_[i]; + if (wifi_networks_[i]->name().compare(name) == 0) { + return wifi_networks_[i]; } } return NULL; } - template<typename T> T* GetWirelessNetworkByPath( + template<typename T> T GetWirelessNetworkByPath( std::vector<T>& networks, const std::string& path) { typedef typename std::vector<T>::iterator iter_t; iter_t iter = std::find_if(networks.begin(), networks.end(), WirelessNetwork::ServicePathEq(path)); - return (iter != networks.end()) ? &(*iter) : NULL; + return (iter != networks.end()) ? *iter : NULL; } // const version - template<typename T> const T* GetWirelessNetworkByPath( + template<typename T> const T GetWirelessNetworkByPath( const std::vector<T>& networks, const std::string& path) const { typedef typename std::vector<T>::const_iterator iter_t; iter_t iter = std::find_if(networks.begin(), networks.end(), WirelessNetwork::ServicePathEq(path)); - return (iter != networks.end()) ? &(*iter) : NULL; + return (iter != networks.end()) ? *iter : NULL; } void EnableNetworkDeviceType(ConnectionType device, bool enable) { @@ -1100,53 +1384,68 @@ class NetworkLibraryImpl : public NetworkLibrary { EnableNetworkDevice(device, enable); } - void NotifyNetworkChanged() { - FOR_EACH_OBSERVER(Observer, observers_, NetworkChanged(this)); + void NotifyNetworkManagerChanged() { + FOR_EACH_OBSERVER(NetworkManagerObserver, + network_manager_observers_, + OnNetworkManagerChanged(this)); + } + + void NotifyNetworkChanged(Network* network) { + DCHECK(network); + NetworkObserverMap::const_iterator iter = network_observers_.find( + network->service_path()); + if (iter != network_observers_.end()) { + FOR_EACH_OBSERVER(NetworkObserver, + *(iter->second), + OnNetworkChanged(this, network)); + } else { + NOTREACHED() << + "There weren't supposed to be any property change observers of " << + network->service_path(); + } } void NotifyCellularDataPlanChanged() { - FOR_EACH_OBSERVER(Observer, observers_, CellularDataPlanChanged(this)); + FOR_EACH_OBSERVER(CellularDataPlanObserver, + data_plan_observers_, + OnCellularDataPlanChanged(this)); } - void UpdateNetworkStatus() { + void UpdateNetworkManagerStatus() { // Make sure we run on UI thread. - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - NewRunnableMethod(this, - &NetworkLibraryImpl::UpdateNetworkStatus)); - return; - } + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + update_task_ = NULL; + VLOG(1) << "Updating Network Status"; SystemInfo* system = GetSystemInfo(); if (!system) return; - wifi_networks_.clear(); - cellular_networks_.clear(); - remembered_wifi_networks_.clear(); - remembered_cellular_networks_.clear(); + std::string prev_cellular_service_path = cellular_ ? + cellular_->service_path() : std::string(); + + ClearNetworks(); + ParseSystem(system, ðernet_, &wifi_networks_, &cellular_networks_, - &remembered_wifi_networks_, &remembered_cellular_networks_); + &remembered_wifi_networks_); - wifi_ = WifiNetwork(); + wifi_ = NULL; for (size_t i = 0; i < wifi_networks_.size(); i++) { - if (wifi_networks_[i].connecting_or_connected()) { + if (wifi_networks_[i]->connecting_or_connected()) { wifi_ = wifi_networks_[i]; break; // There is only one connected or connecting wifi network. } } - std::string prev_service_path = cellular_.service_path(); - cellular_ = CellularNetwork(); + cellular_ = NULL; for (size_t i = 0; i < cellular_networks_.size(); i++) { - if (cellular_networks_[i].connecting_or_connected()) { - // If new cellular, then update data plan list. - if (cellular_networks_[i].service_path() != prev_service_path) { - CellularDataPlanList list; - RetrieveCellularDataPlans(cellular_.service_path().c_str(), &list); - UpdateCellularDataPlan(list); - } + if (cellular_networks_[i]->connecting_or_connected()) { cellular_ = cellular_networks_[i]; + // If new cellular, then request update of the data plan list. + if (cellular_networks_[i]->service_path() != + prev_cellular_service_path) { + RefreshCellularDataPlans(cellular_); + } break; // There is only one connected or connecting cellular network. } } @@ -1156,31 +1455,122 @@ class NetworkLibraryImpl : public NetworkLibrary { connected_devices_ = system->connected_technologies; offline_mode_ = system->offline_mode; - NotifyNetworkChanged(); + NotifyNetworkManagerChanged(); FreeSystemInfo(system); } - void UpdateCellularDataPlan(const CellularDataPlanList& data_plans) { - cellular_.SetDataPlans(data_plans); + void UpdateNetworkStatus(const char* path, + const char* key, + const Value* value) { + if (key == NULL || value == NULL) + return; + // Make sure we run on UI thread. + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, + &NetworkLibraryImpl::UpdateNetworkStatus, + path, key, value)); + return; + } + + bool boolval = false; + int intval = 0; + std::string stringval; + Network* network; + if (ethernet_->service_path() == path) { + network = ethernet_; + } else { + CellularNetwork* cellular = + GetWirelessNetworkByPath(cellular_networks_, path); + WifiNetwork* wifi = + GetWirelessNetworkByPath(wifi_networks_, path); + if (cellular == NULL && wifi == NULL) + return; + + WirelessNetwork* wireless; + if (wifi != NULL) + wireless = static_cast<WirelessNetwork*>(wifi); + else + wireless = static_cast<WirelessNetwork*>(cellular); + + if (strcmp(key, kSignalStrengthProperty) == 0) { + if (value->GetAsInteger(&intval)) + wireless->set_strength(intval); + } else if (cellular != NULL) { + if (strcmp(key, kRestrictedPoolProperty) == 0) { + if (value->GetAsBoolean(&boolval)) + cellular->set_restricted_pool(boolval); + } else if (strcmp(key, kActivationStateProperty) == 0) { + if (value->GetAsString(&stringval)) + cellular->set_activation_state(ParseActivationState(stringval)); + } else if (strcmp(key, kPaymentURLProperty) == 0) { + if (value->GetAsString(&stringval)) + cellular->set_payment_url(stringval); + } else if (strcmp(key, kNetworkTechnologyProperty) == 0) { + if (value->GetAsString(&stringval)) + cellular->set_network_technology( + ParseNetworkTechnology(stringval)); + } else if (strcmp(key, kRoamingStateProperty) == 0) { + if (value->GetAsString(&stringval)) + cellular->set_roaming_state(ParseRoamingState(stringval)); + } + } + network = wireless; + } + if (strcmp(key, kIsActiveProperty) == 0) { + if (value->GetAsBoolean(&boolval)) + network->set_active(boolval); + } else if (strcmp(key, kStateProperty) == 0) { + if (value->GetAsString(&stringval)) + network->set_state(ParseState(stringval)); + } + NotifyNetworkChanged(network); + } + + void UpdateCellularDataPlan(const CellularDataPlanList* data_plans) { + DCHECK(cellular_); + cellular_->SetDataPlans(data_plans); NotifyCellularDataPlanChanged(); } - ObserverList<Observer> observers_; + void ClearNetworks() { + if (ethernet_) + delete ethernet_; + ethernet_ = NULL; + wifi_ = NULL; + cellular_ = NULL; + STLDeleteElements(&wifi_networks_); + wifi_networks_.clear(); + STLDeleteElements(&cellular_networks_); + cellular_networks_.clear(); + STLDeleteElements(&remembered_wifi_networks_); + remembered_wifi_networks_.clear(); + } + + // Network manager observer list + ObserverList<NetworkManagerObserver> network_manager_observers_; + + // Cellular data plan observer list + ObserverList<CellularDataPlanObserver> data_plan_observers_; - // The network status connection for monitoring network status changes. - MonitorNetworkConnection network_status_connection_; + // Network observer map + NetworkObserverMap network_observers_; + + // For monitoring network manager status changes. + PropertyChangeMonitor network_manager_monitor_; // For monitoring data plan changes to the connected cellular network. DataPlanUpdateMonitor data_plan_monitor_; // The ethernet network. - EthernetNetwork ethernet_; + EthernetNetwork* ethernet_; // The list of available wifi networks. WifiNetworkVector wifi_networks_; // The current connected (or connecting) wifi network. - WifiNetwork wifi_; + WifiNetwork* wifi_; // The remembered wifi networks. WifiNetworkVector remembered_wifi_networks_; @@ -1189,10 +1579,7 @@ class NetworkLibraryImpl : public NetworkLibrary { CellularNetworkVector cellular_networks_; // The current connected (or connecting) cellular network. - CellularNetwork cellular_; - - // The remembered cellular networks. - CellularNetworkVector remembered_cellular_networks_; + CellularNetwork* cellular_; // The current available network devices. Bitwise flag of ConnectionTypes. int available_devices_; @@ -1205,26 +1592,43 @@ class NetworkLibraryImpl : public NetworkLibrary { bool offline_mode_; + // Delayed task to retrieve the network information. + CancelableTask* update_task_; + DISALLOW_COPY_AND_ASSIGN(NetworkLibraryImpl); }; class NetworkLibraryStubImpl : public NetworkLibrary { public: - NetworkLibraryStubImpl() : ip_address_("1.1.1.1") {} - ~NetworkLibraryStubImpl() {} - void AddObserver(Observer* observer) {} - void RemoveObserver(Observer* observer) {} - virtual const EthernetNetwork& ethernet_network() const { + NetworkLibraryStubImpl() + : ip_address_("1.1.1.1"), + ethernet_(new EthernetNetwork()), + wifi_(NULL), + cellular_(NULL) { + } + ~NetworkLibraryStubImpl() { if (ethernet_) delete ethernet_; } + virtual void AddNetworkManagerObserver(NetworkManagerObserver* observer) {} + virtual void RemoveNetworkManagerObserver(NetworkManagerObserver* observer) {} + virtual void AddNetworkObserver(const std::string& service_path, + NetworkObserver* observer) {} + virtual void RemoveNetworkObserver(const std::string& service_path, + NetworkObserver* observer) {} + virtual void RemoveObserverForAllNetworks(NetworkObserver* observer) {} + virtual void AddCellularDataPlanObserver( + CellularDataPlanObserver* observer) {} + virtual void RemoveCellularDataPlanObserver( + CellularDataPlanObserver* observer) {} + virtual EthernetNetwork* ethernet_network() { return ethernet_; } virtual bool ethernet_connecting() const { return false; } virtual bool ethernet_connected() const { return true; } - virtual const WifiNetwork& wifi_network() const { + virtual WifiNetwork* wifi_network() { return wifi_; } virtual bool wifi_connecting() const { return false; } virtual bool wifi_connected() const { return false; } - virtual const CellularNetwork& cellular_network() const { + virtual CellularNetwork* cellular_network() { return cellular_; } virtual bool cellular_connecting() const { return false; } @@ -1242,42 +1646,49 @@ class NetworkLibraryStubImpl : public NetworkLibrary { virtual const CellularNetworkVector& cellular_networks() const { return cellular_networks_; } - virtual const CellularNetworkVector& remembered_cellular_networks() const { - return cellular_networks_; + virtual bool has_cellular_networks() const { + return cellular_networks_.begin() != cellular_networks_.end(); } - ///////////////////////////////////////////////////////////////////////////// - virtual bool FindWifiNetworkByPath( - const std::string& path, WifiNetwork* result) const { return false; } - virtual bool FindCellularNetworkByPath( - const std::string& path, CellularNetwork* result) const { return false; } + virtual WifiNetwork* FindWifiNetworkByPath( + const std::string& path) { return NULL; } + virtual CellularNetwork* FindCellularNetworkByPath( + const std::string& path) { return NULL; } virtual void RequestWifiScan() {} virtual bool GetWifiAccessPoints(WifiAccessPointVector* result) { return false; } - virtual void ConnectToWifiNetwork(WifiNetwork network, + virtual bool ConnectToWifiNetwork(const WifiNetwork* network, const std::string& password, const std::string& identity, - const std::string& certpath) {} - virtual void ConnectToWifiNetwork(const std::string& ssid, + const std::string& certpath) { + return true; + } + virtual bool ConnectToWifiNetwork(ConnectionSecurity security, + const std::string& ssid, const std::string& password, const std::string& identity, const std::string& certpath, - bool auto_connect) {} - virtual void ConnectToCellularNetwork(CellularNetwork network) {} - virtual void RefreshCellularDataPlans(const CellularNetwork& network) {} - virtual void DisconnectFromWirelessNetwork(const WirelessNetwork& network) {} - virtual void SaveCellularNetwork(const CellularNetwork& network) {} - virtual void SaveWifiNetwork(const WifiNetwork& network) {} - virtual void ForgetWirelessNetwork(const std::string& service_path) {} + bool auto_connect) { + return true; + } + virtual bool ConnectToCellularNetwork(const CellularNetwork* network) { + return true; + } + virtual void RefreshCellularDataPlans(const CellularNetwork* network) {} + virtual void DisconnectFromWirelessNetwork(const WirelessNetwork* network) {} + virtual void SaveCellularNetwork(const CellularNetwork* network) {} + virtual void SaveWifiNetwork(const WifiNetwork* network) {} + virtual void ForgetWifiNetwork(const std::string& service_path) {} virtual bool ethernet_available() const { return true; } virtual bool wifi_available() const { return false; } virtual bool cellular_available() const { return false; } virtual bool ethernet_enabled() const { return true; } virtual bool wifi_enabled() const { return false; } virtual bool cellular_enabled() const { return false; } + virtual const Network* active_network() const { return NULL; } virtual bool offline_mode() const { return false; } virtual void EnableEthernetNetworkDevice(bool enable) {} virtual void EnableWifiNetworkDevice(bool enable) {} @@ -1289,13 +1700,12 @@ class NetworkLibraryStubImpl : public NetworkLibrary { return NetworkIPConfigVector(); } virtual std::string GetHtmlInfo(int refresh) { return std::string(); } - virtual void UpdateSystemInfo() {} private: std::string ip_address_; - EthernetNetwork ethernet_; - WifiNetwork wifi_; - CellularNetwork cellular_; + EthernetNetwork* ethernet_; + WifiNetwork* wifi_; + CellularNetwork* cellular_; WifiNetworkVector wifi_networks_; CellularNetworkVector cellular_networks_; }; diff --git a/chrome/browser/chromeos/cros/network_library.h b/chrome/browser/chromeos/cros/network_library.h index 5426da1..8296660 100644 --- a/chrome/browser/chromeos/cros/network_library.h +++ b/chrome/browser/chromeos/cros/network_library.h @@ -15,6 +15,8 @@ #include "base/timer.h" #include "cros/chromeos_network.h" +class Value; + namespace chromeos { // Cellular network is considered low data when less than 60 minues. @@ -38,25 +40,22 @@ class Network { ConnectionState connection_state() const { return state_; } bool connecting() const { return state_ == STATE_ASSOCIATION || state_ == STATE_CONFIGURATION || state_ == STATE_CARRIER; } + bool configuring() const { return state_ == STATE_CONFIGURATION; } bool connected() const { return state_ == STATE_READY; } bool connecting_or_connected() const { return connecting() || connected(); } bool failed() const { return state_ == STATE_FAILURE; } + bool failed_or_disconnected() const { return failed() || + state_ == STATE_IDLE; } ConnectionError error() const { return error_; } ConnectionState state() const { return state_; } - - void set_service_path(const std::string& service_path) { - service_path_ = service_path; } - void set_connecting(bool connecting) { state_ = (connecting ? - STATE_ASSOCIATION : STATE_IDLE); } - void set_connected(bool connected) { state_ = (connected ? - STATE_READY : STATE_IDLE); } + // Is this the active network, i.e, the one through which + // network traffic is being routed? A network can be connected, + // but not be carrying traffic. + bool is_active() const { return is_active_; } // Clear the fields. virtual void Clear(); - // Configure the Network from a ServiceInfo object. - virtual void ConfigureFromService(const ServiceInfo& service); - // Return a string representation of the state code. std::string GetStateString() const; @@ -67,7 +66,10 @@ class Network { Network() : type_(TYPE_UNKNOWN), state_(STATE_UNKNOWN), - error_(ERROR_UNKNOWN) {} + error_(ERROR_UNKNOWN), + is_active_(false) {} + explicit Network(const Network& network); + explicit Network(const ServiceInfo* service); virtual ~Network() {} std::string service_path_; @@ -76,6 +78,19 @@ class Network { ConnectionType type_; ConnectionState state_; ConnectionError error_; + bool is_active_; + + private: + void set_service_path(const std::string& service_path) { + service_path_ = service_path; } + void set_connecting(bool connecting) { state_ = (connecting ? + STATE_ASSOCIATION : STATE_IDLE); } + void set_connected(bool connected) { state_ = (connected ? + STATE_READY : STATE_IDLE); } + void set_state(ConnectionState state) { state_ = state; } + void set_active(bool is_active) { is_active_ = is_active; } + + friend class NetworkLibraryImpl; }; class EthernetNetwork : public Network { @@ -83,6 +98,15 @@ class EthernetNetwork : public Network { EthernetNetwork() : Network() { type_ = TYPE_ETHERNET; } + + explicit EthernetNetwork(const EthernetNetwork& network) + : Network(network) { + type_ = TYPE_ETHERNET; + } + + explicit EthernetNetwork(const ServiceInfo* service) : Network(service) { + type_ = TYPE_ETHERNET; + } }; class WirelessNetwork : public Network { @@ -95,8 +119,8 @@ class WirelessNetwork : public Network { // We frequently want to compare networks by service path. struct ServicePathEq { explicit ServicePathEq(const std::string& path_in) : path(path_in) {} - bool operator()(const WirelessNetwork& a) { - return a.service_path().compare(path) == 0; + bool operator()(const WirelessNetwork* a) { + return a->service_path().compare(path) == 0; } const std::string& path; }; @@ -106,14 +130,11 @@ class WirelessNetwork : public Network { bool auto_connect() const { return auto_connect_; } bool favorite() const { return favorite_; } - void set_name(const std::string& name) { name_ = name; } - void set_strength(int strength) { strength_ = strength; } void set_auto_connect(bool auto_connect) { auto_connect_ = auto_connect; } void set_favorite(bool favorite) { favorite_ = favorite; } // Network overrides. virtual void Clear(); - virtual void ConfigureFromService(const ServiceInfo& service); protected: WirelessNetwork() @@ -121,13 +142,48 @@ class WirelessNetwork : public Network { strength_(0), auto_connect_(false), favorite_(false) {} - + explicit WirelessNetwork(const WirelessNetwork& network); + explicit WirelessNetwork(const ServiceInfo* service); + virtual ~WirelessNetwork() {} std::string name_; int strength_; bool auto_connect_; bool favorite_; + + private: + void set_name(const std::string& name) { name_ = name; } + void set_strength(int strength) { strength_ = strength; } + + friend class NetworkLibraryImpl; +}; + +class CellularDataPlan { + public: + CellularDataPlan() : + plan_name("Unknown"), + plan_type(CELLULAR_DATA_PLAN_UNLIMITED), + plan_data_bytes(0), + data_bytes_used(0) { } + explicit CellularDataPlan(const CellularDataPlanInfo &plan) : + plan_name(plan.plan_name?plan.plan_name:""), + plan_type(plan.plan_type), + update_time(base::Time::FromInternalValue(plan.update_time)), + plan_start_time(base::Time::FromInternalValue(plan.plan_start_time)), + plan_end_time(base::Time::FromInternalValue(plan.plan_end_time)), + plan_data_bytes(plan.plan_data_bytes), + data_bytes_used(plan.data_bytes_used) { } + + std::string plan_name; + CellularDataPlanType plan_type; + base::Time update_time; + base::Time plan_start_time; + base::Time plan_end_time; + int64 plan_data_bytes; + int64 data_bytes_used; }; +typedef std::vector<CellularDataPlan> CellularDataPlanVector; + class CellularNetwork : public WirelessNetwork { public: enum DataLeft { @@ -138,11 +194,9 @@ class CellularNetwork : public WirelessNetwork { }; CellularNetwork(); - explicit CellularNetwork(const ServiceInfo& service) - : WirelessNetwork() { - ConfigureFromService(service); - } - + explicit CellularNetwork(const CellularNetwork& network); + explicit CellularNetwork(const ServiceInfo* service); + virtual ~CellularNetwork(); // Starts device activation process. Returns false if the device state does // not permit activation. bool StartActivation() const; @@ -173,14 +227,17 @@ class CellularNetwork : public WirelessNetwork { // WirelessNetwork overrides. virtual void Clear(); - virtual void ConfigureFromService(const ServiceInfo& service); - const CellularDataPlanList& GetDataPlans() const { + const CellularDataPlanVector& GetDataPlans() const { return data_plans_; } - void SetDataPlans(const CellularDataPlanList& data_plans) { - data_plans_ = data_plans; + void SetDataPlans(const CellularDataPlanList* data_plan_list) { + data_plans_.clear(); + for (size_t i = 0; i < data_plan_list->plans_size; i++) { + const CellularDataPlanInfo* info(data_plan_list->GetCellularDataPlan(i)); + data_plans_.push_back(CellularDataPlan(*info)); + } } // Return a string representation of network technology. std::string GetNetworkTechnologyString() const; @@ -189,7 +246,11 @@ class CellularNetwork : public WirelessNetwork { // Return a string representation of roaming state. std::string GetRoamingStateString() const; + // Return a string representation of |activation_state|. + static std::string ActivationStateToString(ActivationState activation_state); + protected: + ActivationState activation_state_; NetworkTechnology network_technology_; NetworkRoamingState roaming_state_; @@ -212,17 +273,38 @@ class CellularNetwork : public WirelessNetwork { std::string hardware_revision_; std::string last_update_; unsigned int prl_version_; - CellularDataPlanList data_plans_; + CellularDataPlanVector data_plans_; + + private: + void set_activation_state(ActivationState state) { + activation_state_ = state; + } + void set_payment_url(const std::string& url) { + payment_url_ = url; + } + void set_network_technology(NetworkTechnology technology) { + network_technology_ = technology; + } + void set_roaming_state(NetworkRoamingState state) { + roaming_state_ = state; + } + void set_restricted_pool(bool restricted_pool) { + restricted_pool_ = restricted_pool; + } + + friend class NetworkLibraryImpl; }; class WifiNetwork : public WirelessNetwork { public: WifiNetwork(); - explicit WifiNetwork(const ServiceInfo& service); + explicit WifiNetwork(const WifiNetwork& network); + explicit WifiNetwork(const ServiceInfo* service); bool encrypted() const { return encryption_ != SECURITY_NONE; } ConnectionSecurity encryption() const { return encryption_; } const std::string& passphrase() const { return passphrase_; } + bool passphrase_required() const { return passphrase_required_; } const std::string& identity() const { return identity_; } const std::string& cert_path() const { return cert_path_; } @@ -241,7 +323,6 @@ class WifiNetwork : public WirelessNetwork { // WirelessNetwork overrides. virtual void Clear(); - virtual void ConfigureFromService(const ServiceInfo& service); // Return a string representation of the encryption code. // This not translated and should be only used for debugging purposes. @@ -253,12 +334,13 @@ class WifiNetwork : public WirelessNetwork { protected: ConnectionSecurity encryption_; std::string passphrase_; + bool passphrase_required_; std::string identity_; std::string cert_path_; }; -typedef std::vector<WifiNetwork> WifiNetworkVector; -typedef std::vector<CellularNetwork> CellularNetworkVector; +typedef std::vector<chromeos::WifiNetwork*> WifiNetworkVector; +typedef std::vector<chromeos::CellularNetwork*> CellularNetworkVector; struct CellTower { enum RadioType { @@ -318,30 +400,60 @@ typedef std::vector<NetworkIPConfig> NetworkIPConfigVector; // library like this: chromeos::CrosLibrary::Get()->GetNetworkLibrary() class NetworkLibrary { public: - class Observer { + class NetworkManagerObserver { + public: + // Called when the state of the network manager has changed, + // for example, networks have appeared or disappeared. + virtual void OnNetworkManagerChanged(NetworkLibrary* obj) = 0; + }; + + class NetworkObserver { + public: + // Called when the state of a single network has changed, + // for example signal strength or connection state. + virtual void OnNetworkChanged(NetworkLibrary* cros, + const Network* network) = 0; + }; + + class CellularDataPlanObserver { public: - // Called when the network has changed. (wifi networks, and ethernet) - virtual void NetworkChanged(NetworkLibrary* obj) = 0; // Called when the cellular data plan has changed. - virtual void CellularDataPlanChanged(NetworkLibrary* obj) {} + virtual void OnCellularDataPlanChanged(NetworkLibrary* obj) = 0; }; virtual ~NetworkLibrary() {} - virtual void AddObserver(Observer* observer) = 0; - virtual void RemoveObserver(Observer* observer) = 0; + + virtual void AddNetworkManagerObserver(NetworkManagerObserver* observer) = 0; + virtual void RemoveNetworkManagerObserver( + NetworkManagerObserver* observer) = 0; + + // An attempt to add an observer that has already been added for a + // give service path will be ignored. + virtual void AddNetworkObserver(const std::string& service_path, + NetworkObserver* observer) = 0; + // Remove an observer of a single network + virtual void RemoveNetworkObserver(const std::string& service_path, + NetworkObserver* observer) = 0; + // Stop |observer| from observing any networks + virtual void RemoveObserverForAllNetworks(NetworkObserver* observer) = 0; + + virtual void AddCellularDataPlanObserver( + CellularDataPlanObserver* observer) = 0; + virtual void RemoveCellularDataPlanObserver( + CellularDataPlanObserver* observer) = 0; // Return the active Ethernet network (or a default structure if inactive). - virtual const EthernetNetwork& ethernet_network() const = 0; + virtual EthernetNetwork* ethernet_network() = 0; virtual bool ethernet_connecting() const = 0; virtual bool ethernet_connected() const = 0; // Return the active Wifi network (or a default structure if none active). - virtual const WifiNetwork& wifi_network() const = 0; + virtual WifiNetwork* wifi_network() = 0; virtual bool wifi_connecting() const = 0; virtual bool wifi_connected() const = 0; // Return the active Cellular network (or a default structure if none active). - virtual const CellularNetwork& cellular_network() const = 0; + virtual CellularNetwork* cellular_network() = 0; virtual bool cellular_connecting() const = 0; virtual bool cellular_connected() const = 0; @@ -363,15 +475,12 @@ class NetworkLibrary { // Returns the current list of cellular networks. virtual const CellularNetworkVector& cellular_networks() const = 0; - // Returns the list of remembered cellular networks. - virtual const CellularNetworkVector& remembered_cellular_networks() const = 0; - // Search the current list of networks by path and if the network // is available, copy the result and return true. - virtual bool FindWifiNetworkByPath(const std::string& path, - WifiNetwork* result) const = 0; - virtual bool FindCellularNetworkByPath(const std::string& path, - CellularNetwork* result) const = 0; + virtual WifiNetwork* FindWifiNetworkByPath(const std::string& path) = 0; + virtual CellularNetwork* FindCellularNetworkByPath( + const std::string& path) = 0; + // Request a scan for new wifi networks. virtual void RequestWifiScan() = 0; @@ -386,39 +495,40 @@ class NetworkLibrary { // TODO(joth): Add GetCellTowers to retrieve a CellTowerVector. - // Force an update of the system info. - virtual void UpdateSystemInfo() = 0; - // Connect to the specified wireless network with password. - virtual void ConnectToWifiNetwork(WifiNetwork network, + // Returns false if the attempt fails immediately (e.g. passphrase too short). + virtual bool ConnectToWifiNetwork(const WifiNetwork* network, const std::string& password, const std::string& identity, const std::string& certpath) = 0; - // Connect to the specified wifi ssid with password. - virtual void ConnectToWifiNetwork(const std::string& ssid, + // Connect to the specified network with security, ssid, and password. + // Returns false if the attempt fails immediately (e.g. passphrase too short). + virtual bool ConnectToWifiNetwork(ConnectionSecurity security, + const std::string& ssid, const std::string& password, const std::string& identity, const std::string& certpath, bool auto_connect) = 0; // Connect to the specified cellular network. - virtual void ConnectToCellularNetwork(CellularNetwork network) = 0; + // Returns false if the attempt fails immediately. + virtual bool ConnectToCellularNetwork(const CellularNetwork* network) = 0; // Initiates cellular data plan refresh. Plan data will be passed through // Network::Observer::CellularDataPlanChanged callback. - virtual void RefreshCellularDataPlans(const CellularNetwork& network) = 0; + virtual void RefreshCellularDataPlans(const CellularNetwork* network) = 0; // Disconnect from the specified wireless (either cellular or wifi) network. virtual void DisconnectFromWirelessNetwork( - const WirelessNetwork& network) = 0; + const WirelessNetwork* network) = 0; // Save network information including passwords (wifi) and auto-connect. - virtual void SaveCellularNetwork(const CellularNetwork& network) = 0; - virtual void SaveWifiNetwork(const WifiNetwork& network) = 0; + virtual void SaveCellularNetwork(const CellularNetwork* network) = 0; + virtual void SaveWifiNetwork(const WifiNetwork* network) = 0; - // Forget the passed in wireless (either cellular or wifi) network. - virtual void ForgetWirelessNetwork(const std::string& service_path) = 0; + // Forget the wifi network corresponding to service_path. + virtual void ForgetWifiNetwork(const std::string& service_path) = 0; virtual bool ethernet_available() const = 0; virtual bool wifi_available() const = 0; @@ -428,6 +538,8 @@ class NetworkLibrary { virtual bool wifi_enabled() const = 0; virtual bool cellular_enabled() const = 0; + virtual const Network* active_network() const = 0; + virtual bool offline_mode() const = 0; // Enables/disables the ethernet network device. diff --git a/chrome/browser/chromeos/cros/power_library.cc b/chrome/browser/chromeos/cros/power_library.cc index 7caea61..bb5efcd 100644 --- a/chrome/browser/chromeos/cros/power_library.cc +++ b/chrome/browser/chromeos/cros/power_library.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -109,12 +109,11 @@ class PowerLibraryImpl : public PowerLibrary { return; } - DLOG(INFO) << "Power" << - " lpo=" << status.line_power_on << - " sta=" << status.battery_state << - " per=" << status.battery_percentage << - " tte=" << status.battery_time_to_empty << - " ttf=" << status.battery_time_to_full; + DVLOG(1) << "Power lpo=" << status.line_power_on + << " sta=" << status.battery_state + << " per=" << status.battery_percentage + << " tte=" << status.battery_time_to_empty + << " ttf=" << status.battery_time_to_full; status_ = status; FOR_EACH_OBSERVER(Observer, observers_, PowerChanged(this)); } diff --git a/chrome/browser/chromeos/cros/screen_lock_library.cc b/chrome/browser/chromeos/cros/screen_lock_library.cc index 3502c26..cd01302 100644 --- a/chrome/browser/chromeos/cros/screen_lock_library.cc +++ b/chrome/browser/chromeos/cros/screen_lock_library.cc @@ -35,54 +35,18 @@ class ScreenLockLibraryImpl : public ScreenLockLibrary { } void NotifyScreenLockRequested() { - // Make sure we run on IO thread. - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - NewRunnableMethod( - this, - &ScreenLockLibraryImpl::NotifyScreenLockRequested)); - return; - } chromeos::NotifyScreenLockRequested(); } void NotifyScreenLockCompleted() { - // Make sure we run on IO thread. - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - NewRunnableMethod( - this, - &ScreenLockLibraryImpl::NotifyScreenLockCompleted)); - return; - } chromeos::NotifyScreenLockCompleted(); } void NotifyScreenUnlockRequested() { - // Make sure we run on IO thread. - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - NewRunnableMethod( - this, - &ScreenLockLibraryImpl::NotifyScreenUnlockRequested)); - return; - } chromeos::NotifyScreenUnlockRequested(); } void NotifyScreenUnlockCompleted() { - // Make sure we run on IO thread. - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - NewRunnableMethod( - this, - &ScreenLockLibraryImpl::NotifyScreenUnlockCompleted)); - return; - } chromeos::NotifyScreenUnlockCompleted(); } @@ -175,4 +139,3 @@ ScreenLockLibrary* ScreenLockLibrary::GetImpl(bool stub) { // Allows InvokeLater without adding refcounting. This class is a Singleton and // won't be deleted until it's last InvokeLater is run. DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::ScreenLockLibraryImpl); - diff --git a/chrome/browser/chromeos/cros/syslogs_library.cc b/chrome/browser/chromeos/cros/syslogs_library.cc index 8d771b6..fbfa515 100644 --- a/chrome/browser/chromeos/cros/syslogs_library.cc +++ b/chrome/browser/chromeos/cros/syslogs_library.cc @@ -13,13 +13,17 @@ namespace chromeos { +const char kContextFeedback[] = "feedback"; +const char kContextSysInfo[] = "sysinfo"; + + class SyslogsLibraryImpl : public SyslogsLibrary { public: SyslogsLibraryImpl() {} virtual ~SyslogsLibraryImpl() {} virtual Handle RequestSyslogs( - bool compress_logs, + bool compress_logs, bool add_feedback_context, CancelableRequestConsumerBase* consumer, ReadCompleteCallback* callback); @@ -27,7 +31,7 @@ class SyslogsLibraryImpl : public SyslogsLibrary { // Called from FILE thread. void ReadSyslogs( scoped_refptr<CancelableRequest<ReadCompleteCallback> > request, - bool compress_logs); + bool compress_logs, bool add_feedback_context); void LoadCompressedLogs(const FilePath& zip_file, std::string* zip_content); @@ -40,7 +44,7 @@ class SyslogsLibraryStubImpl : public SyslogsLibrary { SyslogsLibraryStubImpl() {} virtual ~SyslogsLibraryStubImpl() {} - virtual Handle RequestSyslogs(bool compress_logs, + virtual Handle RequestSyslogs(bool compress_logs, bool add_feedback_context, CancelableRequestConsumerBase* consumer, ReadCompleteCallback* callback) { if (callback) @@ -60,7 +64,7 @@ SyslogsLibrary* SyslogsLibrary::GetImpl(bool stub) { CancelableRequestProvider::Handle SyslogsLibraryImpl::RequestSyslogs( - bool compress_logs, + bool compress_logs, bool add_feedback_context, CancelableRequestConsumerBase* consumer, ReadCompleteCallback* callback) { // Register the callback request. @@ -73,7 +77,8 @@ CancelableRequestProvider::Handle SyslogsLibraryImpl::RequestSyslogs( BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod( - this, &SyslogsLibraryImpl::ReadSyslogs, request, compress_logs)); + this, &SyslogsLibraryImpl::ReadSyslogs, request, + compress_logs, add_feedback_context)); return request->handle(); } @@ -81,7 +86,7 @@ CancelableRequestProvider::Handle SyslogsLibraryImpl::RequestSyslogs( // Called from FILE thread. void SyslogsLibraryImpl::ReadSyslogs( scoped_refptr<CancelableRequest<ReadCompleteCallback> > request, - bool compress_logs) { + bool compress_logs, bool add_feedback_context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); if (request->canceled()) @@ -101,7 +106,8 @@ void SyslogsLibraryImpl::ReadSyslogs( LogDictionaryType* logs = NULL; if (CrosLibrary::Get()->EnsureLoaded()) logs = chromeos::GetSystemLogs( - compress_logs ? &zip_file : NULL); + compress_logs ? &zip_file : NULL, + add_feedback_context ? kContextFeedback : kContextSysInfo); std::string* zip_content = NULL; if (compress_logs) { @@ -131,4 +137,3 @@ void SyslogsLibraryImpl::LoadCompressedLogs(const FilePath& zip_file, // Allows InvokeLater without adding refcounting. SyslogsLibraryImpl is a // Singleton and won't be deleted until it's last InvokeLater is run. DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::SyslogsLibraryImpl); - diff --git a/chrome/browser/chromeos/cros/syslogs_library.h b/chrome/browser/chromeos/cros/syslogs_library.h index 3850a31..6b8c441 100644 --- a/chrome/browser/chromeos/cros/syslogs_library.h +++ b/chrome/browser/chromeos/cros/syslogs_library.h @@ -29,7 +29,7 @@ class SyslogsLibrary : public CancelableRequestProvider { // Returns the request handle. Call CancelRequest(Handle) to cancel // the request before the callback gets called. virtual Handle RequestSyslogs( - bool compress_logs, + bool compress_logs, bool feedback_context, CancelableRequestConsumerBase* consumer, ReadCompleteCallback* callback) = 0; diff --git a/chrome/browser/chromeos/cros/system_library.cc b/chrome/browser/chromeos/cros/system_library.cc index b14e38e..6c902b4 100644 --- a/chrome/browser/chromeos/cros/system_library.cc +++ b/chrome/browser/chromeos/cros/system_library.cc @@ -32,7 +32,7 @@ class SystemLibraryImpl : public SystemLibrary { icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(id)); timezone_.reset(timezone); icu::TimeZone::setDefault(*timezone); - LOG(INFO) << "Timezone is " << id; + VLOG(1) << "Timezone is " << id; } void AddObserver(Observer* observer) { @@ -54,7 +54,7 @@ class SystemLibraryImpl : public SystemLibrary { timezone->getID(unicode); std::string id; UTF16ToUTF8(unicode.getBuffer(), unicode.length(), &id); - LOG(INFO) << "Setting timezone to " << id; + VLOG(1) << "Setting timezone to " << id; chromeos::SetTimezoneID(id); } icu::TimeZone::setDefault(*timezone); diff --git a/chrome/browser/chromeos/cros/update_library.cc b/chrome/browser/chromeos/cros/update_library.cc index 728cd4b..a201e0c 100644 --- a/chrome/browser/chromeos/cros/update_library.cc +++ b/chrome/browser/chromeos/cros/update_library.cc @@ -50,6 +50,20 @@ class UpdateLibraryImpl : public UpdateLibrary { return RebootIfUpdated(); } + bool SetReleaseTrack(const std::string& track) { + if (!CrosLibrary::Get()->EnsureLoaded()) + return false; + + return chromeos::SetTrack(track); + } + + std::string GetReleaseTrack() { + if (!CrosLibrary::Get()->EnsureLoaded()) + return ""; + + return chromeos::GetTrack(); + } + const UpdateLibrary::Status& status() const { return status_; } @@ -107,6 +121,8 @@ class UpdateLibraryStubImpl : public UpdateLibrary { void RemoveObserver(Observer* observer) {} bool CheckForUpdate() { return false; } bool RebootAfterUpdate() { return false; } + bool SetReleaseTrack(const std::string& track) { return false; } + std::string GetReleaseTrack() { return "beta-channel"; } const UpdateLibrary::Status& status() const { return status_; } diff --git a/chrome/browser/chromeos/cros/update_library.h b/chrome/browser/chromeos/cros/update_library.h index 7b95815..e8a9daa 100644 --- a/chrome/browser/chromeos/cros/update_library.h +++ b/chrome/browser/chromeos/cros/update_library.h @@ -68,6 +68,14 @@ class UpdateLibrary { // Reboots if update has been performed. virtual bool RebootAfterUpdate() = 0; + // Sets the release track (channel). |track| should look like + // "beta-channel" and "dev-channel". Returns true on success. + virtual bool SetReleaseTrack(const std::string& track) = 0; + + // Returns the release track (channel). On error, returns an empty + // string. + virtual std::string GetReleaseTrack() = 0; + virtual const Status& status() const = 0; // Factory function, creates a new instance and returns ownership. @@ -78,4 +86,3 @@ class UpdateLibrary { } // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_CROS_UPDATE_LIBRARY_H_ - diff --git a/chrome/browser/chromeos/cros_settings_provider.cc b/chrome/browser/chromeos/cros_settings_provider.cc new file mode 100644 index 0000000..1bdd78b --- /dev/null +++ b/chrome/browser/chromeos/cros_settings_provider.cc @@ -0,0 +1,23 @@ +// 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/chromeos/cros_settings_provider.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "chrome/common/chrome_switches.h" + +namespace chromeos { + +void CrosSettingsProvider::Set(const std::string& path, Value* value) { + // We don't allow changing any of the cros settings in the guest mode. + // It should not reach here from UI in the guest mode, but just in case. + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kGuestSession)) { + LOG(ERROR) << "Ignoring the guest request to change: " << path; + return; + } + DoSet(path, value); +} + +}; // namespace chromeos diff --git a/chrome/browser/chromeos/cros_settings_provider.h b/chrome/browser/chromeos/cros_settings_provider.h index c069ce1..36e856e 100644 --- a/chrome/browser/chromeos/cros_settings_provider.h +++ b/chrome/browser/chromeos/cros_settings_provider.h @@ -17,7 +17,7 @@ class CrosSettingsProvider { // Sets |in_value| to given |path| in cros settings. // Note that this takes ownership of |in_value|. - virtual void Set(const std::string& path, Value* in_value) = 0; + void Set(const std::string& path, Value* in_value); // Gets settings value of given |path| to |out_value|. // Note that |out_value| is still owned by this class. @@ -25,6 +25,10 @@ class CrosSettingsProvider { // Gets the namespace prefix provided by this provider virtual bool HandlesSetting(const std::string& path) = 0; + + private: + // Does the real job for Set(). + virtual void DoSet(const std::string& path, Value* in_value) = 0; }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros_settings_provider_proxy.cc b/chrome/browser/chromeos/cros_settings_provider_proxy.cc index 7017abb..5f113ee 100644 --- a/chrome/browser/chromeos/cros_settings_provider_proxy.cc +++ b/chrome/browser/chromeos/cros_settings_provider_proxy.cc @@ -7,64 +7,97 @@ #include "base/string_util.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/profile.h" +#include "chrome/browser/profile_manager.h" namespace chromeos { +static const char kProxyPacUrl[] = "cros.proxy.pacurl"; +static const char kProxySingleHttp[] = "cros.proxy.singlehttp"; +static const char kProxySingleHttpPort[] = "cros.proxy.singlehttpport"; +static const char kProxyHttpUrl[] = "cros.proxy.httpurl"; +static const char kProxyHttpPort[] = "cros.proxy.httpport"; +static const char kProxyHttpsUrl[] = "cros.proxy.httpsurl"; +static const char kProxyHttpsPort[] = "cros.proxy.httpsport"; +static const char kProxyType[] = "cros.proxy.type"; +static const char kProxySingle[] = "cros.proxy.single"; +static const char kProxyFtpUrl[] = "cros.proxy.ftpurl"; +static const char kProxyFtpPort[] = "cros.proxy.ftpport"; +static const char kProxySocks[] = "cros.proxy.socks"; +static const char kProxySocksPort[] = "cros.proxy.socksport"; +static const char kProxyIgnoreList[] = "cros.proxy.ignorelist"; + //------------------ CrosSettingsProviderProxy: public methods ----------------- CrosSettingsProviderProxy::CrosSettingsProviderProxy() { } -void CrosSettingsProviderProxy::Set(const std::string& path, - Value* in_value) { - Browser* browser = BrowserList::GetLastActive(); - if (!browser || !in_value) { +void CrosSettingsProviderProxy::DoSet(const std::string& path, + Value* in_value) { + if (!in_value) { return; } - chromeos::ProxyConfigServiceImpl* config_service = - browser->profile()->GetChromeOSProxyConfigServiceImpl(); + + // Keep whatever user inputs so that we could use it later. + SetCache(path, in_value); + + chromeos::ProxyConfigServiceImpl* config_service = GetConfigService(); chromeos::ProxyConfigServiceImpl::ProxyConfig config; config_service->UIGetProxyConfig(&config); - if (path == "cros.proxy.pacurl") { + if (path == kProxyPacUrl) { std::string val; if (in_value->GetAsString(&val)) { GURL url(val); config_service->UISetProxyConfigToPACScript(url); } - } else if (path == "cros.proxy.singlehttp") { + } else if (path == kProxySingleHttp) { std::string val; if (in_value->GetAsString(&val)) { std::string uri = val; - AppendPortIfValid(config.single_proxy, &uri); + AppendPortIfValid(kProxySingleHttpPort, &uri); config_service->UISetProxyConfigToSingleProxy( net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTP)); } - } else if (path == "cros.proxy.singlehttpport") { + } else if (path == kProxySingleHttpPort) { std::string val; if (in_value->GetAsString(&val)) { std::string uri; - if (FormServerUriIfValid(config.single_proxy, val, &uri)) { - config_service->UISetProxyConfigToSingleProxy( - net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTP)); - } + FormServerUriIfValid(kProxySingleHttp, val, &uri); + config_service->UISetProxyConfigToSingleProxy( + net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTP)); } - } else if (path == "cros.proxy.httpurl") { + } else if (path == kProxyHttpUrl) { std::string val; if (in_value->GetAsString(&val)) { std::string uri = val; - AppendPortIfValid(config.http_proxy, &uri); + AppendPortIfValid(kProxyHttpPort, &uri); config_service->UISetProxyConfigToProxyPerScheme("http", net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTP)); } - } else if (path == "cros.proxy.httpsurl") { + } else if (path == kProxyHttpPort) { + std::string val; + if (in_value->GetAsString(&val)) { + std::string uri; + FormServerUriIfValid(kProxyHttpUrl, val, &uri); + config_service->UISetProxyConfigToProxyPerScheme("http", + net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTP)); + } + } else if (path == kProxyHttpsUrl) { std::string val; if (in_value->GetAsString(&val)) { std::string uri = val; - AppendPortIfValid(config.https_proxy, &uri); + AppendPortIfValid(kProxyHttpsPort, &uri); config_service->UISetProxyConfigToProxyPerScheme("https", net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTPS)); } - } else if (path == "cros.proxy.type") { + } else if (path == kProxyHttpsPort) { + std::string val; + if (in_value->GetAsString(&val)) { + std::string uri; + FormServerUriIfValid(kProxyHttpsUrl, val, &uri); + config_service->UISetProxyConfigToProxyPerScheme("https", + net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTPS)); + } + } else if (path == kProxyType) { int val; if (in_value->GetAsInteger(&val)) { if (val == 3) { @@ -108,7 +141,7 @@ void CrosSettingsProviderProxy::Set(const std::string& path, config_service->UISetProxyConfigToDirect(); } } - } else if (path == "cros.proxy.single") { + } else if (path == kProxySingle) { bool val; if (in_value->GetAsBoolean(&val)) { if (val) @@ -118,59 +151,39 @@ void CrosSettingsProviderProxy::Set(const std::string& path, config_service->UISetProxyConfigToProxyPerScheme("http", config.http_proxy.server); } - } else if (path == "cros.proxy.ftpurl") { + } else if (path == kProxyFtpUrl) { std::string val; if (in_value->GetAsString(&val)) { std::string uri = val; - AppendPortIfValid(config.ftp_proxy, &uri); + AppendPortIfValid(kProxyFtpPort, &uri); config_service->UISetProxyConfigToProxyPerScheme("ftp", net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTP)); } - } else if (path == "cros.proxy.socks") { - std::string val; - if (in_value->GetAsString(&val)) { - std::string uri = val; - AppendPortIfValid(config.socks_proxy, &uri); - config_service->UISetProxyConfigToProxyPerScheme("socks", - net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_SOCKS4)); - } - } else if (path == "cros.proxy.httpport") { + } else if (path == kProxyFtpPort) { std::string val; if (in_value->GetAsString(&val)) { std::string uri; - if (FormServerUriIfValid(config.http_proxy, val, &uri)) { - config_service->UISetProxyConfigToProxyPerScheme("http", - net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTP)); - } - } - } else if (path == "cros.proxy.httpsport") { - std::string val; - if (in_value->GetAsString(&val)) { - std::string uri; - if (FormServerUriIfValid(config.https_proxy, val, &uri)) { - config_service->UISetProxyConfigToProxyPerScheme("https", - net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTPS)); - } + FormServerUriIfValid(kProxyFtpUrl, val, &uri); + config_service->UISetProxyConfigToProxyPerScheme("ftp", + net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTP)); } - } else if (path == "cros.proxy.ftpport") { + } else if (path == kProxySocks) { std::string val; if (in_value->GetAsString(&val)) { - std::string uri; - if (FormServerUriIfValid(config.ftp_proxy, val, &uri)) { - config_service->UISetProxyConfigToProxyPerScheme("ftp", - net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_HTTP)); - } + std::string uri = val; + AppendPortIfValid(kProxySocksPort, &uri); + config_service->UISetProxyConfigToProxyPerScheme("socks", + net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_SOCKS4)); } - } else if (path == "cros.proxy.socksport") { + } else if (path == kProxySocksPort) { std::string val; if (in_value->GetAsString(&val)) { std::string uri; - if (FormServerUriIfValid(config.socks_proxy, val, &uri)) { - config_service->UISetProxyConfigToProxyPerScheme("socks", - net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_SOCKS5)); - } + FormServerUriIfValid(kProxySocks, val, &uri); + config_service->UISetProxyConfigToProxyPerScheme("socks", + net::ProxyServer::FromURI(uri, net::ProxyServer::SCHEME_SOCKS4)); } - } else if (path == "cros.proxy.ignorelist") { + } else if (path == kProxyIgnoreList) { net::ProxyBypassRules bypass_rules; if (in_value->GetType() == Value::TYPE_LIST) { const ListValue* list_value = static_cast<const ListValue*>(in_value); @@ -187,32 +200,27 @@ void CrosSettingsProviderProxy::Set(const std::string& path, bool CrosSettingsProviderProxy::Get(const std::string& path, Value** out_value) const { - Browser* browser = BrowserList::GetLastActive(); bool found = false; bool managed = false; Value* data; - if (!browser) { - return false; - } - chromeos::ProxyConfigServiceImpl* config_service = - browser->profile()->GetChromeOSProxyConfigServiceImpl(); + chromeos::ProxyConfigServiceImpl* config_service = GetConfigService(); chromeos::ProxyConfigServiceImpl::ProxyConfig config; config_service->UIGetProxyConfig(&config); - if (path == "cros.proxy.pacurl") { + if (path == kProxyPacUrl) { if (config.automatic_proxy.pac_url.is_valid()) { data = Value::CreateStringValue(config.automatic_proxy.pac_url.spec()); found = true; } - } else if (path == "cros.proxy.singlehttp") { + } else if (path == kProxySingleHttp) { found = (data = CreateServerHostValue(config.single_proxy)); - } else if (path == "cros.proxy.singlehttpport") { + } else if (path == kProxySingleHttpPort) { found = (data = CreateServerPortValue(config.single_proxy)); - } else if (path == "cros.proxy.httpurl") { + } else if (path == kProxyHttpUrl) { found = (data = CreateServerHostValue(config.http_proxy)); - } else if (path == "cros.proxy.httpsurl") { + } else if (path == kProxyHttpsUrl) { found = (data = CreateServerHostValue(config.https_proxy)); - } else if (path == "cros.proxy.type") { + } else if (path == kProxyType) { if (config.mode == chromeos::ProxyConfigServiceImpl::ProxyConfig::MODE_AUTO_DETECT || config.mode == @@ -227,23 +235,23 @@ bool CrosSettingsProviderProxy::Get(const std::string& path, data = Value::CreateIntegerValue(1); } found = true; - } else if (path == "cros.proxy.single") { + } else if (path == kProxySingle) { data = Value::CreateBooleanValue(config.mode == chromeos::ProxyConfigServiceImpl::ProxyConfig::MODE_SINGLE_PROXY); found = true; - } else if (path == "cros.proxy.ftpurl") { + } else if (path == kProxyFtpUrl) { found = (data = CreateServerHostValue(config.ftp_proxy)); - } else if (path == "cros.proxy.socks") { + } else if (path == kProxySocks) { found = (data = CreateServerHostValue(config.socks_proxy)); - } else if (path == "cros.proxy.httpport") { + } else if (path == kProxyHttpPort) { found = (data = CreateServerPortValue(config.http_proxy)); - } else if (path == "cros.proxy.httpsport") { + } else if (path == kProxyHttpsPort) { found = (data = CreateServerPortValue(config.https_proxy)); - } else if (path == "cros.proxy.ftpport") { + } else if (path == kProxyFtpPort) { found = (data = CreateServerPortValue(config.ftp_proxy)); - } else if (path == "cros.proxy.socksport") { + } else if (path == kProxySocksPort) { found = (data = CreateServerPortValue(config.socks_proxy)); - } else if (path == "cros.proxy.ignorelist") { + } else if (path == kProxyIgnoreList) { ListValue* list = new ListValue(); net::ProxyBypassRules::RuleList bypass_rules = config.bypass_rules.rules(); for (size_t x = 0; x < bypass_rules.size(); x++) { @@ -270,20 +278,32 @@ bool CrosSettingsProviderProxy::HandlesSetting(const std::string& path) { //----------------- CrosSettingsProviderProxy: private methods ----------------- +chromeos::ProxyConfigServiceImpl* + CrosSettingsProviderProxy::GetConfigService() const { + Browser* browser = BrowserList::GetLastActive(); + // browser is NULL at OOBE/login stage. + Profile* profile = browser ? + browser->profile() : + ProfileManager::GetDefaultProfile(); + return profile->GetChromeOSProxyConfigServiceImpl(); +} + void CrosSettingsProviderProxy::AppendPortIfValid( - const ProxyConfigServiceImpl::ProxyConfig::ManualProxy& proxy, + const char* port_cache_key, std::string* server_uri) { - if (proxy.server.is_valid()) - *server_uri += ":" + proxy.server.host_port_pair().port(); + std::string port; + if (!server_uri->empty() && cache_.GetString(port_cache_key, &port) && + !port.empty()) { + *server_uri += ":" + port; + } } -bool CrosSettingsProviderProxy::FormServerUriIfValid( - const ProxyConfigServiceImpl::ProxyConfig::ManualProxy& proxy, +void CrosSettingsProviderProxy::FormServerUriIfValid( + const char* host_cache_key, const std::string& port_num, std::string* server_uri) { - if (!proxy.server.is_valid()) - return false; - *server_uri = proxy.server.host_port_pair().host() + ":" + port_num; - return true; + if (cache_.GetString(host_cache_key, server_uri) && !server_uri->empty() && + !port_num.empty()) + *server_uri += ":" + port_num; } Value* CrosSettingsProviderProxy::CreateServerHostValue( @@ -300,4 +320,9 @@ Value* CrosSettingsProviderProxy::CreateServerPortValue( NULL; } +void CrosSettingsProviderProxy::SetCache(const std::string& key, + const Value* value) { + cache_.Set(key, value->DeepCopy()); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/cros_settings_provider_proxy.h b/chrome/browser/chromeos/cros_settings_provider_proxy.h index ce6712f..1b1672b 100644 --- a/chrome/browser/chromeos/cros_settings_provider_proxy.h +++ b/chrome/browser/chromeos/cros_settings_provider_proxy.h @@ -16,17 +16,19 @@ namespace chromeos { class CrosSettingsProviderProxy : public CrosSettingsProvider { public: CrosSettingsProviderProxy(); - virtual void Set(const std::string& path, Value* in_value); + // CrosSettingsProvider implementation. virtual bool Get(const std::string& path, Value** out_value) const; virtual bool HandlesSetting(const std::string& path); private: - void AppendPortIfValid( - const ProxyConfigServiceImpl::ProxyConfig::ManualProxy& proxy, - std::string* server_uri); + // CrosSettingsProvider implementation. + virtual void DoSet(const std::string& path, Value* value); - bool FormServerUriIfValid( - const ProxyConfigServiceImpl::ProxyConfig::ManualProxy& proxy, + chromeos::ProxyConfigServiceImpl* GetConfigService() const; + + void AppendPortIfValid(const char* port_cache_key, std::string* server_uri); + + void FormServerUriIfValid(const char* host_cache_key, const std::string& port_num, std::string* server_uri); Value* CreateServerHostValue( @@ -35,6 +37,11 @@ class CrosSettingsProviderProxy : public CrosSettingsProvider { Value* CreateServerPortValue( const ProxyConfigServiceImpl::ProxyConfig::ManualProxy& proxy) const; + void SetCache(const std::string& key, const Value* value); + + // A cache to keep whatever user typed. + DictionaryValue cache_; + DISALLOW_COPY_AND_ASSIGN(CrosSettingsProviderProxy); }; diff --git a/chrome/browser/chromeos/cros_settings_provider_stats.cc b/chrome/browser/chromeos/cros_settings_provider_stats.cc index 6a49f59..6a78f61 100644 --- a/chrome/browser/chromeos/cros_settings_provider_stats.cc +++ b/chrome/browser/chromeos/cros_settings_provider_stats.cc @@ -17,7 +17,8 @@ namespace chromeos { -void MetricsCrosSettingsProvider::Set(const std::string& path, Value* value) { +void MetricsCrosSettingsProvider::DoSet(const std::string& path, + Value* value) { DCHECK(path == kStatsReportingPref); bool enabled = false; CHECK(value->GetAsBoolean(&enabled)); @@ -35,7 +36,7 @@ bool MetricsCrosSettingsProvider::Get(const std::string& path, // static bool MetricsCrosSettingsProvider::SetMetricsStatus(bool enabled) { - LOG(INFO) << "Setting cros stats/crash metric reporting to " << enabled; + VLOG(1) << "Setting cros stats/crash metric reporting to " << enabled; if (enabled != GoogleUpdateSettings::GetCollectStatsConsent()) { bool new_enabled = OptionsUtil::ResolveMetricsReportingEnabled(enabled); #if defined(USE_LINUX_BREAKPAD) diff --git a/chrome/browser/chromeos/cros_settings_provider_stats.h b/chrome/browser/chromeos/cros_settings_provider_stats.h index 1b89702..ff30f25 100644 --- a/chrome/browser/chromeos/cros_settings_provider_stats.h +++ b/chrome/browser/chromeos/cros_settings_provider_stats.h @@ -20,7 +20,6 @@ class MetricsCrosSettingsProvider : public CrosSettingsProvider { MetricsCrosSettingsProvider() {} // CrosSettingsProvider implementation. - virtual void Set(const std::string& path, Value* value); virtual bool Get(const std::string& path, Value** value) const; virtual bool HandlesSetting(const std::string& path); @@ -31,6 +30,9 @@ class MetricsCrosSettingsProvider : public CrosSettingsProvider { static bool GetMetricsStatus(); private: + // CrosSettingsProvider implementation. + virtual void DoSet(const std::string& path, Value* value); + DISALLOW_COPY_AND_ASSIGN(MetricsCrosSettingsProvider); }; diff --git a/chrome/browser/chromeos/cros_settings_provider_user.cc b/chrome/browser/chromeos/cros_settings_provider_user.cc index 3094d14..7b9c0c3 100644 --- a/chrome/browser/chromeos/cros_settings_provider_user.cc +++ b/chrome/browser/chromeos/cros_settings_provider_user.cc @@ -88,6 +88,7 @@ UserCrosSettingsProvider::~UserCrosSettingsProvider() { SignedSettingsHelper::Get()->CancelCallback(this); } +// static void UserCrosSettingsProvider::RegisterPrefs(PrefService* local_state) { // Cached signed settings values local_state->RegisterBooleanPref(kAccountsPrefAllowGuest, true); @@ -97,20 +98,24 @@ void UserCrosSettingsProvider::RegisterPrefs(PrefService* local_state) { local_state->RegisterStringPref(kDeviceOwner, ""); } +// static bool UserCrosSettingsProvider::cached_allow_guest() { return g_browser_process->local_state()->GetBoolean(kAccountsPrefAllowGuest); } +// static bool UserCrosSettingsProvider::cached_allow_new_user() { return g_browser_process->local_state()->GetBoolean( kAccountsPrefAllowNewUser); } +// static bool UserCrosSettingsProvider::cached_show_users_on_signin() { return g_browser_process->local_state()->GetBoolean( kAccountsPrefShowUserNamesOnSignIn); } +// static const ListValue* UserCrosSettingsProvider::cached_whitelist() { PrefService* prefs = g_browser_process->local_state(); const ListValue* cached_users = prefs->GetList(kAccountsPrefUsers); @@ -125,11 +130,28 @@ const ListValue* UserCrosSettingsProvider::cached_whitelist() { return cached_users; } +// static std::string UserCrosSettingsProvider::cached_owner() { return g_browser_process->local_state()->GetString(kDeviceOwner); } -void UserCrosSettingsProvider::Set(const std::string& path, Value* in_value) { +// static +bool UserCrosSettingsProvider::IsEmailInCachedWhitelist( + const std::string& email) { + const ListValue* whitelist = cached_whitelist(); + if (whitelist) { + StringValue email_value(email); + for (ListValue::const_iterator i(whitelist->begin()); + i != whitelist->end(); ++i) { + if ((*i)->Equals(&email_value)) + return true; + } + } + return false; +} + +void UserCrosSettingsProvider::DoSet(const std::string& path, + Value* in_value) { if (!UserManager::Get()->current_user_is_owner()) { LOG(WARNING) << "Changing settings from non-owner, setting=" << path; @@ -147,14 +169,14 @@ void UserCrosSettingsProvider::Set(const std::string& path, Value* in_value) { SignedSettingsHelper::Get()->StartStorePropertyOp(path, value, this); UpdateCacheBool(path.c_str(), bool_value); - LOG(INFO) << "Set cros setting " << path << "=" << value; + VLOG(1) << "Set cros setting " << path << "=" << value; } } else if (path == kDeviceOwner) { - LOG(INFO) << "Setting owner is not supported. " - << "Please use 'UpdateCachedOwner' instead."; + VLOG(1) << "Setting owner is not supported. Please use 'UpdateCachedOwner' " + "instead."; } else if (path == kAccountsPrefUsers) { - LOG(INFO) << "Setting user whitelist is not implemented." - << "Please use whitelist/unwhitelist instead."; + VLOG(1) << "Setting user whitelist is not implemented. Please use " + "whitelist/unwhitelist instead."; } else { LOG(WARNING) << "Try to set unhandled cros setting " << path; } @@ -185,7 +207,7 @@ bool UserCrosSettingsProvider::HandlesSetting(const std::string& path) { void UserCrosSettingsProvider::OnWhitelistCompleted(bool success, const std::string& email) { - LOG(INFO) << "Add " << email << " to whitelist, success=" << success; + VLOG(1) << "Add " << email << " to whitelist, success=" << success; // Reload the whitelist on settings op failure. if (!success) @@ -194,7 +216,7 @@ void UserCrosSettingsProvider::OnWhitelistCompleted(bool success, void UserCrosSettingsProvider::OnUnwhitelistCompleted(bool success, const std::string& email) { - LOG(INFO) << "Remove " << email << " from whitelist, success=" << success; + VLOG(1) << "Remove " << email << " from whitelist, success=" << success; // Reload the whitelist on settings op failure. if (!success) @@ -203,8 +225,8 @@ void UserCrosSettingsProvider::OnUnwhitelistCompleted(bool success, void UserCrosSettingsProvider::OnStorePropertyCompleted( bool success, const std::string& name, const std::string& value) { - LOG(INFO) << "Store cros setting " << name << "=" << value - << ", success=" << success; + VLOG(1) << "Store cros setting " << name << "=" << value << ", success=" + << success; // Reload the setting if store op fails. if (!success) @@ -218,7 +240,7 @@ void UserCrosSettingsProvider::OnRetrievePropertyCompleted( return; } - LOG(INFO) << "Retrieved cros setting " << name << "=" << value; + VLOG(1) << "Retrieved cros setting " << name << "=" << value; if (bool_settings_.count(name)) { UpdateCacheBool(name.c_str(), value == "true" ? true : false); diff --git a/chrome/browser/chromeos/cros_settings_provider_user.h b/chrome/browser/chromeos/cros_settings_provider_user.h index f0c5509..3fafa41 100644 --- a/chrome/browser/chromeos/cros_settings_provider_user.h +++ b/chrome/browser/chromeos/cros_settings_provider_user.h @@ -34,8 +34,12 @@ class UserCrosSettingsProvider : public CrosSettingsProvider, static const ListValue* cached_whitelist(); static std::string cached_owner(); + // Returns true if given email is in user whitelist. + // Note this function is for display purpose only and should use + // CheckWhitelist op for the real whitelist check. + static bool IsEmailInCachedWhitelist(const std::string& email); + // CrosSettingsProvider implementation. - virtual void Set(const std::string& path, Value* in_value); virtual bool Get(const std::string& path, Value** out_value) const; virtual bool HandlesSetting(const std::string& path); @@ -54,6 +58,9 @@ class UserCrosSettingsProvider : public CrosSettingsProvider, static void UpdateCachedOwner(const std::string& email); private: + // CrosSettingsProvider implementation. + virtual void DoSet(const std::string& path, Value* value); + void StartFetchingBoolSetting(const std::string& name); void StartFetchingStringSetting(const std::string& name); void StartFetchingSetting(const std::string& name); diff --git a/chrome/browser/chromeos/customization_document.cc b/chrome/browser/chromeos/customization_document.cc index b9a4f34..a9d438a 100644 --- a/chrome/browser/chromeos/customization_document.cc +++ b/chrome/browser/chromeos/customization_document.cc @@ -103,7 +103,9 @@ bool StartupCustomizationDocument::ParseFromJsonValue( if (!background_color_string.empty()) { if (background_color_string[0] == '#') { int background_int; - base::HexStringToInt(background_color_string.substr(1), &background_int); + base::HexStringToInt(background_color_string.begin() + 1, + background_color_string.end(), + &background_int); background_color_ = static_cast<SkColor>(0xff000000 | background_int); } else { // Literal color constants are not supported yet. diff --git a/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc b/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc index 0176d7e..93361c6 100644 --- a/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc +++ b/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc @@ -31,6 +31,8 @@ void AccountsOptionsHandler::RegisterMessages() { NewCallback(this, &AccountsOptionsHandler::UnwhitelistUser)); dom_ui_->RegisterMessageCallback("fetchUserPictures", NewCallback(this, &AccountsOptionsHandler::FetchUserPictures)); + dom_ui_->RegisterMessageCallback("whitelistExistingUsers", + NewCallback(this, &AccountsOptionsHandler::WhitelistExistingUsers)); } void AccountsOptionsHandler::GetLocalizedValues( @@ -97,4 +99,24 @@ void AccountsOptionsHandler::FetchUserPictures(const ListValue* args) { user_pictures); } +void AccountsOptionsHandler::WhitelistExistingUsers(const ListValue* args) { + ListValue whitelist_users; + + std::vector<UserManager::User> users = UserManager::Get()->GetUsers(); + for (std::vector<UserManager::User>::const_iterator it = users.begin(); + it < users.end(); ++it) { + const std::string& email = it->email(); + if (!UserCrosSettingsProvider::IsEmailInCachedWhitelist(email)) { + DictionaryValue* user_dict = new DictionaryValue; + user_dict->SetString("name", it->GetDisplayName()); + user_dict->SetString("email", email); + user_dict->SetBoolean("owner", false); + + whitelist_users.Append(user_dict); + } + } + + dom_ui_->CallJavascriptFunction(L"AccountsOptions.addUsers", whitelist_users); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/dom_ui/accounts_options_handler.h b/chrome/browser/chromeos/dom_ui/accounts_options_handler.h index 177b447..2fbf76d 100644 --- a/chrome/browser/chromeos/dom_ui/accounts_options_handler.h +++ b/chrome/browser/chromeos/dom_ui/accounts_options_handler.h @@ -34,6 +34,9 @@ class AccountsOptionsHandler : public CrosOptionsPageUIHandler { // Javascript callback to fetch known user pictures. void FetchUserPictures(const ListValue* args); + // Javascript callback to auto add existing users to white list. + void WhitelistExistingUsers(const ListValue* args); + DISALLOW_COPY_AND_ASSIGN(AccountsOptionsHandler); }; diff --git a/chrome/browser/chromeos/dom_ui/internet_options_handler.cc b/chrome/browser/chromeos/dom_ui/internet_options_handler.cc index 87db039..fa0245f 100644 --- a/chrome/browser/chromeos/dom_ui/internet_options_handler.cc +++ b/chrome/browser/chromeos/dom_ui/internet_options_handler.cc @@ -24,6 +24,7 @@ #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/login/ownership_service.h" #include "chrome/browser/chromeos/status/network_menu.h" #include "chrome/browser/dom_ui/dom_ui_util.h" #include "chrome/browser/tab_contents/tab_contents.h" @@ -56,11 +57,19 @@ std::string FormatHardwareAddress(const std::string& address) { } // namespace InternetOptionsHandler::InternetOptionsHandler() { - chromeos::CrosLibrary::Get()->GetNetworkLibrary()->AddObserver(this); + chromeos::NetworkLibrary* netlib = + chromeos::CrosLibrary::Get()->GetNetworkLibrary(); + netlib->AddNetworkManagerObserver(this); + netlib->AddCellularDataPlanObserver(this); + MonitorActiveNetwork(netlib); } InternetOptionsHandler::~InternetOptionsHandler() { - chromeos::CrosLibrary::Get()->GetNetworkLibrary()->RemoveObserver(this); + chromeos::NetworkLibrary *netlib = + chromeos::CrosLibrary::Get()->GetNetworkLibrary(); + netlib->RemoveNetworkManagerObserver(this); + netlib->RemoveCellularDataPlanObserver(this); + netlib->RemoveObserverForAllNetworks(this); } void InternetOptionsHandler::GetLocalizedValues( @@ -92,6 +101,12 @@ void InternetOptionsHandler::GetLocalizedValues( localized_strings->SetString("forget_button", l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_FORGET)); + localized_strings->SetString("activate_button", + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_ACTIVATE)); + localized_strings->SetString("buyplan_button", + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_BUY_PLAN)); localized_strings->SetString("wifiNetworkTabLabel", l10n_util::GetStringUTF16( @@ -140,9 +155,9 @@ void InternetOptionsHandler::GetLocalizedValues( localized_strings->SetString("inetCertPass", l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PRIVATE_KEY_PASSWORD)); - localized_strings->SetString("inetPass", + localized_strings->SetString("inetPassProtected", l10n_util::GetStringUTF16( - IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PASSPHRASE)); + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NET_PROTECTED)); localized_strings->SetString("inetRememberNetwork", l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_AUTO_CONNECT)); @@ -155,6 +170,26 @@ void InternetOptionsHandler::GetLocalizedValues( localized_strings->SetString("inetShowPass", l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SHOWPASSWORD)); + localized_strings->SetString("inetSecurityNone", + l10n_util::GetStringFUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_SELECT, + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_NONE))); + localized_strings->SetString("inetSecurityWEP", + l10n_util::GetStringFUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_SELECT, + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_WEP))); + localized_strings->SetString("inetSecurityWPA", + l10n_util::GetStringFUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_SELECT, + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_WPA))); + localized_strings->SetString("inetSecurityRSN", + l10n_util::GetStringFUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_SELECT, + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_RSN))); localized_strings->SetString("inetPassPrompt", l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PASSWORD)); @@ -244,7 +279,7 @@ void InternetOptionsHandler::GetLocalizedValues( l10n_util::GetStringFUTF16( IDS_STATUSBAR_NETWORK_DEVICE_DISABLE, l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_WIFI))); - localized_strings->SetString("enableCellular", + localized_strings->SetString("enableCellular", l10n_util::GetStringFUTF16( IDS_STATUSBAR_NETWORK_DEVICE_ENABLE, l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_CELLULAR))); @@ -256,6 +291,8 @@ void InternetOptionsHandler::GetLocalizedValues( l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_CONTROL_TITLE)); + localized_strings->SetString("detailsInternetOk", + l10n_util::GetStringUTF16(IDS_OK)); localized_strings->SetString("detailsInternetDismiss", l10n_util::GetStringUTF16(IDS_CANCEL)); @@ -265,6 +302,8 @@ void InternetOptionsHandler::GetLocalizedValues( chromeos::NetworkLibrary* cros = chromeos::CrosLibrary::Get()->GetNetworkLibrary(); + localized_strings->SetBoolean("wifiAvailable", cros->wifi_available()); + localized_strings->SetBoolean("wifiEnabled", cros->wifi_enabled()); localized_strings->SetBoolean("cellularAvailable", cros->cellular_available()); localized_strings->SetBoolean("cellularEnabled", cros->cellular_enabled()); @@ -291,7 +330,7 @@ void InternetOptionsHandler::RegisterMessages() { NewCallback(this, &InternetOptionsHandler::DisableWifiCallback)); dom_ui_->RegisterMessageCallback("enableCellular", NewCallback(this, &InternetOptionsHandler::EnableCellularCallback)); - dom_ui_->RegisterMessageCallback("disablCellular", + dom_ui_->RegisterMessageCallback("disableCellular", NewCallback(this, &InternetOptionsHandler::DisableCellularCallback)); dom_ui_->RegisterMessageCallback("buyDataPlan", NewCallback(this, &InternetOptionsHandler::BuyDataPlanCallback)); @@ -324,15 +363,16 @@ void InternetOptionsHandler::DisableCellularCallback(const ListValue* args) { } void InternetOptionsHandler::BuyDataPlanCallback(const ListValue* args) { - Browser* browser = BrowserList::GetLastActive(); + if (!dom_ui_) + return; + Browser* browser = BrowserList::FindBrowserWithFeature( + dom_ui_->GetProfile(), Browser::FEATURE_TABSTRIP); if (browser) browser->OpenMobilePlanTabAndActivate(); } -void InternetOptionsHandler::NetworkChanged(chromeos::NetworkLibrary* cros) { - if (!dom_ui_) - return; - +void InternetOptionsHandler::RefreshNetworkData( + chromeos::NetworkLibrary* cros) { DictionaryValue dictionary; dictionary.Set("wiredList", GetWiredList()); dictionary.Set("wirelessList", GetWirelessList()); @@ -345,20 +385,60 @@ void InternetOptionsHandler::NetworkChanged(chromeos::NetworkLibrary* cros) { L"options.InternetOptions.refreshNetworkData", dictionary); } -void InternetOptionsHandler::CellularDataPlanChanged( +void InternetOptionsHandler::OnNetworkManagerChanged( + chromeos::NetworkLibrary* cros) { + if (!dom_ui_) + return; + MonitorActiveNetwork(cros); + RefreshNetworkData(cros); +} + +void InternetOptionsHandler::OnNetworkChanged( + chromeos::NetworkLibrary* cros, + const chromeos::Network* network) { + if (dom_ui_) + RefreshNetworkData(cros); +} + +// Add an observer for the active network, if any, so +// that we can dynamically display the correct icon for +// that network's signal strength. +// TODO(ers) Ideally, on this page we'd monitor all networks for +// signal strength changes, not just the active network. +void InternetOptionsHandler::MonitorActiveNetwork( + chromeos::NetworkLibrary* cros) { + const chromeos::Network* network = cros->active_network(); + if (active_network_.empty() || network == NULL || + active_network_ != network->service_path()) { + if (!active_network_.empty()) { + cros->RemoveNetworkObserver(active_network_, this); + } + if (network != NULL) { + cros->AddNetworkObserver(network->service_path(), this); + } + } + if (network != NULL) + active_network_ = network->service_path(); + else + active_network_ = ""; +} + +void InternetOptionsHandler::OnCellularDataPlanChanged( chromeos::NetworkLibrary* obj) { if (!dom_ui_) return; - const chromeos::CellularNetwork cellular = obj->cellular_network(); - const chromeos::CellularDataPlanList& plans = cellular.GetDataPlans(); + chromeos::CellularNetwork* cellular = obj->cellular_network(); + if (!cellular) + return; + const chromeos::CellularDataPlanVector& plans = cellular->GetDataPlans(); DictionaryValue connection_plans; ListValue* plan_list = new ListValue(); - for (chromeos::CellularDataPlanList::const_iterator iter = plans.begin(); + for (chromeos::CellularDataPlanVector::const_iterator iter = plans.begin(); iter != plans.end(); ++iter) { plan_list->Append(CellularDataPlanToDictionary(*iter)); } - connection_plans.SetString("servicePath", cellular.service_path()); + connection_plans.SetString("servicePath", cellular->service_path()); connection_plans.Set("plans", plan_list); dom_ui_->CallJavascriptFunction( L"options.InternetOptions.updateCellularPlans", connection_plans); @@ -380,9 +460,8 @@ DictionaryValue* InternetOptionsHandler::CellularDataPlanToDictionary( case chromeos::CELLULAR_DATA_PLAN_UNLIMITED: { description = l10n_util::GetStringFUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PURCHASE_UNLIMITED_DATA, - WideToUTF16(base::TimeFormatFriendlyDate( - base::Time::FromInternalValue(plan.plan_start_time * - base::Time::kMicrosecondsPerSecond)))); + WideToUTF16(base::TimeFormatFriendlyDate(plan.plan_start_time))); + remaining = l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_UNLIMITED); break; @@ -394,8 +473,7 @@ DictionaryValue* InternetOptionsHandler::CellularDataPlanToDictionary( GetByteDisplayUnits(plan.plan_data_bytes), true), WideToUTF16(base::TimeFormatFriendlyDate( - base::Time::FromInternalValue(plan.plan_start_time * - base::Time::kMicrosecondsPerSecond)))); + plan.plan_start_time))); remaining = FormatBytes(plan.plan_data_bytes - plan.data_bytes_used, GetByteDisplayUnits(plan.plan_data_bytes - plan.data_bytes_used), true); @@ -408,8 +486,7 @@ DictionaryValue* InternetOptionsHandler::CellularDataPlanToDictionary( GetByteDisplayUnits(plan.plan_data_bytes), true), WideToUTF16(base::TimeFormatFriendlyDate( - base::Time::FromInternalValue(plan.plan_start_time * - base::Time::kMicrosecondsPerSecond)))); + plan.plan_start_time))); remaining = FormatBytes(plan.plan_data_bytes - plan.data_bytes_used, GetByteDisplayUnits(plan.plan_data_bytes - plan.data_bytes_used), true); @@ -417,10 +494,7 @@ DictionaryValue* InternetOptionsHandler::CellularDataPlanToDictionary( } } string16 expiration = TimeFormat::TimeRemaining( - base::TimeDelta::FromSeconds( - plan.plan_end_time - (base::Time::Now().ToInternalValue() / - base::Time::kMicrosecondsPerSecond))); - + plan.plan_end_time - base::Time::Now()); plan_dict->SetString("name", plan.plan_name); plan_dict->SetString("planSummary", description); plan_dict->SetString("dataRemaining", remaining); @@ -433,7 +507,8 @@ string16 InternetOptionsHandler::GetPlanWarning( const chromeos::CellularDataPlan& plan) { if (plan.plan_type == chromeos::CELLULAR_DATA_PLAN_UNLIMITED) { // Time based plan. Show nearing expiration and data expiration. - int64 time_left = plan.plan_end_time - plan.update_time; + int64 time_left = base::TimeDelta( + plan.plan_end_time - plan.update_time).InSeconds(); if (time_left <= 0) { return l10n_util::GetStringFUTF16( IDS_NETWORK_MINUTES_REMAINING_MESSAGE, ASCIIToUTF16("0")); @@ -459,79 +534,57 @@ string16 InternetOptionsHandler::GetPlanWarning( } void InternetOptionsHandler::SetDetailsCallback(const ListValue* args) { - std::string service_path; + std::string remember; - if (!args->GetString(0, &service_path)) { + if (args->GetSize() < 2 || + !args->GetString(0, &service_path) || + !args->GetString(1, &remember)) { NOTREACHED(); return; } - chromeos::NetworkLibrary* cros = - chromeos::CrosLibrary::Get()->GetNetworkLibrary(); - chromeos::WifiNetwork network; - - if (cros->FindWifiNetworkByPath(service_path, &network)) { - bool changed = false; - if (network.encrypted()) { - if (network.encryption() == chromeos::SECURITY_8021X) { - std::string certpath; - std::string ident; - std::string certpass; - bool remember; - - if (args->GetSize() != 5 || - !args->GetBoolean(1, &remember) || - !args->GetString(2, &ident) || - !args->GetString(3, &certpath) || - !args->GetString(4, &certpass)) { - NOTREACHED(); - return; - } - bool auto_connect = remember; - if (auto_connect != network.auto_connect()) { - network.set_auto_connect(auto_connect); - changed = true; - } - if (ident != network.identity()) { - network.set_identity(ident); - changed = true; - } - if (certpass != network.passphrase()) { - network.set_passphrase(certpass); - changed = true; - } - if (certpath != network.cert_path()) { - network.set_cert_path(certpath); - changed = true; - } - } else { - std::string password; - std::string remember; - - if (args->GetSize() != 5 || - !args->GetString(1, &remember) || - !args->GetString(4, &password)) { - NOTREACHED(); - return; - } + if (!chromeos::OwnershipService::GetSharedInstance()->CurrentUserIsOwner()) { + LOG(WARNING) << "Non-owner tried to change a network."; + return; + } - bool auto_connect = (remember == "true"); - if (auto_connect != network.auto_connect()) { - network.set_auto_connect(auto_connect); - changed = true; - } - if (password != network.passphrase()) { - network.set_passphrase(password); - changed = true; - } + chromeos::NetworkLibrary* cros = + chromeos::CrosLibrary::Get()->GetNetworkLibrary(); + chromeos::WifiNetwork* network = cros->FindWifiNetworkByPath(service_path); + if (!network) + return; + bool changed = false; + if (network->encrypted()) { + if (network->encrypted() && + network->encryption() == chromeos::SECURITY_8021X) { + std::string ident; + std::string certpath; + + if (!args->GetString(2, &ident) || + !args->GetString(3, &certpath)) { + NOTREACHED(); + return; + } + if (ident != network->identity()) { + network->set_identity(ident); + changed = true; + } + if (certpath != network->cert_path()) { + network->set_cert_path(certpath); + changed = true; } - } - if (changed) { - chromeos::CrosLibrary::Get()->GetNetworkLibrary()->SaveWifiNetwork( - network); } } + + bool auto_connect = remember == "true"; + if (auto_connect != network->auto_connect()) { + network->set_auto_connect(auto_connect); + changed = true; + } + + if (changed) + cros->SaveWifiNetwork(network); } // Parse 'path' to determine if the certificate is stored in a pkcs#11 device. @@ -552,12 +605,13 @@ bool InternetOptionsHandler::is_certificate_in_pkcs11(const std::string& path) { } void InternetOptionsHandler::PopulateDictionaryDetails( - const chromeos::Network& net, chromeos::NetworkLibrary* cros) { + const chromeos::Network* net, chromeos::NetworkLibrary* cros) { + DCHECK(net); DictionaryValue dictionary; - chromeos::ConnectionType type = net.type(); + chromeos::ConnectionType type = net->type(); std::string hardware_address; chromeos::NetworkIPConfigVector ipconfigs = - cros->GetIPConfigs(net.device_path(), &hardware_address); + cros->GetIPConfigs(net->device_path(), &hardware_address); scoped_ptr<ListValue> ipconfig_list(new ListValue()); for (chromeos::NetworkIPConfigVector::const_iterator it = ipconfigs.begin(); it != ipconfigs.end(); ++it) { @@ -571,75 +625,78 @@ void InternetOptionsHandler::PopulateDictionaryDetails( } dictionary.Set("ipconfigs", ipconfig_list.release()); dictionary.SetInteger("type", type); - dictionary.SetString("servicePath", net.service_path()); - dictionary.SetBoolean("connecting", net.connecting()); - dictionary.SetBoolean("connected", net.connected()); - dictionary.SetString("connectionState", net.GetStateString()); + dictionary.SetString("servicePath", net->service_path()); + dictionary.SetBoolean("connecting", net->connecting()); + dictionary.SetBoolean("connected", net->connected()); + dictionary.SetString("connectionState", net->GetStateString()); if (type == chromeos::TYPE_WIFI) { - chromeos::WifiNetwork wireless; - if (!cros->FindWifiNetworkByPath(net.service_path(), &wireless)) { - LOG(WARNING) << "Cannot find network " << net.service_path(); + chromeos::WifiNetwork* wireless = + cros->FindWifiNetworkByPath(net->service_path()); + if (!wireless) { + LOG(WARNING) << "Cannot find network " << net->service_path(); } else { - dictionary.SetString("ssid", wireless.name()); - dictionary.SetBoolean("autoConnect",wireless.auto_connect()); - if (wireless.encrypted()) { + dictionary.SetString("ssid", wireless->name()); + dictionary.SetBoolean("autoConnect", wireless->auto_connect()); + if (wireless->encrypted()) { dictionary.SetBoolean("encrypted", true); - if (wireless.encryption() == chromeos::SECURITY_8021X) { + if (wireless->encryption() == chromeos::SECURITY_8021X) { bool certificate_in_pkcs11 = - is_certificate_in_pkcs11(wireless.cert_path()); + is_certificate_in_pkcs11(wireless->cert_path()); if (certificate_in_pkcs11) { dictionary.SetBoolean("certInPkcs", true); } else { dictionary.SetBoolean("certInPkcs", false); } - dictionary.SetString("certPath",wireless.cert_path()); - dictionary.SetString("ident",wireless.identity()); + dictionary.SetString("certPath", wireless->cert_path()); + dictionary.SetString("ident", wireless->identity()); dictionary.SetBoolean("certNeeded", true); - dictionary.SetString("certPass",wireless.passphrase()); + dictionary.SetString("certPass", wireless->passphrase()); } else { dictionary.SetBoolean("certNeeded", false); - dictionary.SetString("pass", wireless.passphrase()); } } else { dictionary.SetBoolean("encrypted", false); } } } else if (type == chromeos::TYPE_CELLULAR) { - chromeos::CellularNetwork cellular; - if (!cros->FindCellularNetworkByPath(net.service_path(), &cellular)) { - LOG(WARNING) << "Cannot find network " << net.service_path(); + chromeos::CellularNetwork* cellular = + cros->FindCellularNetworkByPath(net->service_path()); + if (!cellular) { + LOG(WARNING) << "Cannot find network " << net->service_path(); } else { // Cellular network / connection settings. - dictionary.SetString("serviceName", cellular.service_name()); + dictionary.SetString("serviceName", cellular->service_name()); dictionary.SetString("networkTechnology", - cellular.GetNetworkTechnologyString()); - dictionary.SetString("operatorName", cellular.operator_name()); - dictionary.SetString("operatorCode", cellular.operator_code()); + cellular->GetNetworkTechnologyString()); + dictionary.SetString("operatorName", cellular->operator_name()); + dictionary.SetString("operatorCode", cellular->operator_code()); dictionary.SetString("activationState", - cellular.GetActivationStateString()); + cellular->GetActivationStateString()); dictionary.SetString("roamingState", - cellular.GetRoamingStateString()); + cellular->GetRoamingStateString()); dictionary.SetString("restrictedPool", - cellular.restricted_pool() ? + cellular->restricted_pool() ? l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL) : l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL)); - dictionary.SetString("errorState", cellular.GetErrorString()); + dictionary.SetString("errorState", cellular->GetErrorString()); + dictionary.SetString("supportUrl", + cellular->payment_url()); // Device settings. - dictionary.SetString("manufacturer", cellular.manufacturer()); - dictionary.SetString("modelId", cellular.model_id()); - dictionary.SetString("firmwareRevision", cellular.firmware_revision()); - dictionary.SetString("hardwareRevision", cellular.hardware_revision()); - dictionary.SetString("lastUpdate", cellular.last_update()); + dictionary.SetString("manufacturer", cellular->manufacturer()); + dictionary.SetString("modelId", cellular->model_id()); + dictionary.SetString("firmwareRevision", cellular->firmware_revision()); + dictionary.SetString("hardwareRevision", cellular->hardware_revision()); + dictionary.SetString("lastUpdate", cellular->last_update()); dictionary.SetString("prlVersion", StringPrintf("%u", - cellular.prl_version())); - dictionary.SetString("meid", cellular.meid()); - dictionary.SetString("imei", cellular.imei()); - dictionary.SetString("mdn", cellular.mdn()); - dictionary.SetString("imsi", cellular.imsi()); - dictionary.SetString("esn", cellular.esn()); - dictionary.SetString("min", cellular.min()); - - dictionary.SetBoolean("gsm", cellular.is_gsm()); + cellular->prl_version())); + dictionary.SetString("meid", cellular->meid()); + dictionary.SetString("imei", cellular->imei()); + dictionary.SetString("mdn", cellular->mdn()); + dictionary.SetString("imsi", cellular->imsi()); + dictionary.SetString("esn", cellular->esn()); + dictionary.SetString("min", cellular->min()); + + dictionary.SetBoolean("gsm", cellular->is_gsm()); } } if (!hardware_address.empty()) { @@ -651,24 +708,7 @@ void InternetOptionsHandler::PopulateDictionaryDetails( L"options.InternetOptions.showDetailedInfo", dictionary); } -void InternetOptionsHandler::PopupWirelessPassword( - const chromeos::WifiNetwork& network) { - DictionaryValue dictionary; - dictionary.SetString("servicePath",network.service_path()); - if (network.encryption() == chromeos::SECURITY_8021X) { - dictionary.SetBoolean("certNeeded", true); - dictionary.SetString("ident", network.identity()); - dictionary.SetString("cert", network.cert_path()); - } else { - dictionary.SetBoolean("certNeeded", false); - dictionary.SetString("pass", network.passphrase()); - } - dom_ui_->CallJavascriptFunction( - L"options.InternetOptions.showPasswordEntry", dictionary); -} - void InternetOptionsHandler::LoginCallback(const ListValue* args) { - std::string service_path; std::string password; @@ -681,61 +721,72 @@ void InternetOptionsHandler::LoginCallback(const ListValue* args) { chromeos::NetworkLibrary* cros = chromeos::CrosLibrary::Get()->GetNetworkLibrary(); - chromeos::WifiNetwork network; - - if (cros->FindWifiNetworkByPath(service_path, &network)) { + chromeos::WifiNetwork* network = cros->FindWifiNetworkByPath(service_path); + if (network) { cros->ConnectToWifiNetwork( network, password, std::string(), std::string()); } else { - // Must be an "other" login - cros->ConnectToWifiNetwork( - service_path, password, std::string(), std::string(), true); + // Network disappeared while the user is connecting to it. + // TODO(chocobo): Display error message. + LOG(WARNING) << "Cannot find network to connect " << service_path; } } void InternetOptionsHandler::LoginCertCallback(const ListValue* args) { - std::string service_path; std::string identity; std::string certpath; - std::string password; - - if (args->GetSize() != 4 || + if (args->GetSize() < 3 || !args->GetString(0, &service_path) || !args->GetString(1, &certpath) || - !args->GetString(2, &identity) || - !args->GetString(3, &password)) { - NOTREACHED(); + !args->GetString(2, &identity)) { return; } chromeos::NetworkLibrary* cros = chromeos::CrosLibrary::Get()->GetNetworkLibrary(); - chromeos::WifiNetwork network; - - if (cros->FindWifiNetworkByPath(service_path, &network)) { - cros->ConnectToWifiNetwork( - network, password, identity, certpath); - } else { - // TODO(dhg): Send error back to UI + chromeos::WifiNetwork* network = + cros->FindWifiNetworkByPath(service_path); + if (!network) + return; + // If password does not come from the input, use one saved with the + // network details. + std::string password; + if (args->GetSize() != 4 || !args->GetString(3, &password)) { + password = network->passphrase(); } + cros->ConnectToWifiNetwork( + network, password, identity, certpath); } void InternetOptionsHandler::LoginToOtherCallback(const ListValue* args) { + std::string security; std::string ssid; std::string password; - if (args->GetSize() != 2 || - !args->GetString(0, &ssid) || - !args->GetString(1, &password)) { + if (args->GetSize() != 3 || + !args->GetString(0, &security) || + !args->GetString(1, &ssid) || + !args->GetString(2, &password)) { NOTREACHED(); return; } + chromeos::ConnectionSecurity sec = chromeos::SECURITY_UNKNOWN; + if (security == "none") { + sec = chromeos::SECURITY_NONE; + } else if (security == "wep") { + sec = chromeos::SECURITY_WEP; + } else if (security == "wpa") { + sec = chromeos::SECURITY_WPA; + } else if (security == "rsn") { + sec = chromeos::SECURITY_RSN; + } + chromeos::NetworkLibrary* cros = chromeos::CrosLibrary::Get()->GetNetworkLibrary(); - cros->ConnectToWifiNetwork( - ssid, password, std::string(), std::string(), true); + cros->ConnectToWifiNetwork(sec, ssid, password, std::string(), std::string(), + true); } void InternetOptionsHandler::ButtonClickCallback(const ListValue* args) { @@ -750,25 +801,35 @@ void InternetOptionsHandler::ButtonClickCallback(const ListValue* args) { return; } + bool is_owner = + chromeos::OwnershipService::GetSharedInstance()->CurrentUserIsOwner(); + int type = atoi(str_type.c_str()); chromeos::NetworkLibrary* cros = chromeos::CrosLibrary::Get()->GetNetworkLibrary(); if (type == chromeos::TYPE_ETHERNET) { - const chromeos::EthernetNetwork& ether = cros->ethernet_network(); + chromeos::EthernetNetwork* ether = cros->ethernet_network(); PopulateDictionaryDetails(ether, cros); } else if (type == chromeos::TYPE_WIFI) { - chromeos::WifiNetwork network; + chromeos::WifiNetwork* network; if (command == "forget") { - cros->ForgetWirelessNetwork(service_path); - } else if (cros->FindWifiNetworkByPath(service_path, &network)) { + if (!is_owner) { + LOG(WARNING) << "Non-owner tried to forget a network."; + return; + } + cros->ForgetWifiNetwork(service_path); + } else if ((network = cros->FindWifiNetworkByPath(service_path))) { if (command == "connect") { // Connect to wifi here. Open password page if appropriate. - if (network.encrypted()) { - if (network.encryption() == chromeos::SECURITY_8021X) { + if (network->encrypted() && !network->auto_connect()) { + if (network->encryption() == chromeos::SECURITY_8021X) { PopulateDictionaryDetails(network, cros); } else { - PopupWirelessPassword(network); + DictionaryValue dictionary; + dictionary.SetString("servicePath", network->service_path()); + dom_ui_->CallJavascriptFunction( + L"options.InternetOptions.showPasswordEntry", dictionary); } } else { cros->ConnectToWifiNetwork( @@ -781,12 +842,17 @@ void InternetOptionsHandler::ButtonClickCallback(const ListValue* args) { } } } else if (type == chromeos::TYPE_CELLULAR) { - chromeos::CellularNetwork cellular; - if (cros->FindCellularNetworkByPath(service_path, &cellular)) { + chromeos::CellularNetwork* cellular = + cros->FindCellularNetworkByPath(service_path); + if (cellular) { if (command == "connect") { cros->ConnectToCellularNetwork(cellular); } else if (command == "disconnect") { cros->DisconnectFromWirelessNetwork(cellular); + } else if (command == "activate") { + Browser* browser = BrowserList::GetLastActive(); + if (browser) + browser->OpenMobilePlanTabAndActivate(); } else if (command == "options") { PopulateDictionaryDetails(cellular, cros); } @@ -806,27 +872,35 @@ void InternetOptionsHandler::RefreshCellularPlanCallback( } chromeos::NetworkLibrary* cros = chromeos::CrosLibrary::Get()->GetNetworkLibrary(); - chromeos::CellularNetwork cellular; - if (cros->FindCellularNetworkByPath(service_path, &cellular)) { + chromeos::CellularNetwork* cellular = + cros->FindCellularNetworkByPath(service_path); + if (cellular) cros->RefreshCellularDataPlans(cellular); - } else { - NOTREACHED(); - } } ListValue* InternetOptionsHandler::GetNetwork(const std::string& service_path, const SkBitmap& icon, const std::string& name, bool connecting, - bool connected, int connection_type, bool remembered) { + bool connected, chromeos::ConnectionType connection_type, bool remembered, + chromeos::ActivationState activation_state, bool restricted_ip) { ListValue* network = new ListValue(); - int s = IDS_STATUSBAR_NETWORK_DEVICE_DISCONNECTED; + int connection_state = IDS_STATUSBAR_NETWORK_DEVICE_DISCONNECTED; if (connecting) - s = IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING; + connection_state = IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING; else if (connected) - s = IDS_STATUSBAR_NETWORK_DEVICE_CONNECTED; - string16 status = l10n_util::GetStringUTF16(s); - + connection_state = IDS_STATUSBAR_NETWORK_DEVICE_CONNECTED; + std::string status = l10n_util::GetStringUTF8(connection_state); + if (connection_type == chromeos::TYPE_CELLULAR) { + if (activation_state == chromeos::ACTIVATION_STATE_ACTIVATED && + restricted_ip && connected) { + status = l10n_util::GetStringUTF8(IDS_OPTIONS_SETTINGS_NO_PLAN_LABEL); + } else if (activation_state != chromeos::ACTIVATION_STATE_ACTIVATED) { + status.append(" / "); + status.append( + chromeos::CellularNetwork::ActivationStateToString(activation_state)); + } + } // service path network->Append(Value::CreateStringValue(service_path)); // name @@ -834,7 +908,7 @@ ListValue* InternetOptionsHandler::GetNetwork(const std::string& service_path, // status network->Append(Value::CreateStringValue(status)); // type - network->Append(Value::CreateIntegerValue(connection_type)); + network->Append(Value::CreateIntegerValue(static_cast<int>(connection_type))); // connected network->Append(Value::CreateBooleanValue(connected)); // connecting @@ -844,6 +918,11 @@ ListValue* InternetOptionsHandler::GetNetwork(const std::string& service_path, dom_ui_util::GetImageDataUrl(icon))); // remembered network->Append(Value::CreateBooleanValue(remembered)); + // activation_state + network->Append(Value::CreateIntegerValue( + static_cast<int>(activation_state))); + // restricted + network->Append(Value::CreateBooleanValue(restricted_ip)); return network; } @@ -855,22 +934,26 @@ ListValue* InternetOptionsHandler::GetWiredList() { // If ethernet is not enabled, then don't add anything. if (cros->ethernet_enabled()) { - const chromeos::EthernetNetwork& ethernet_network = + chromeos::EthernetNetwork* ethernet_network = cros->ethernet_network(); SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK); - if (!ethernet_network.connecting() && - !ethernet_network.connected()) { + if (!ethernet_network || (!ethernet_network->connecting() && + !ethernet_network->connected())) { icon = chromeos::NetworkMenu::IconForDisplay(icon, *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED)); } - list->Append(GetNetwork( - ethernet_network.service_path(), - icon, - l10n_util::GetStringUTF8(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET), - ethernet_network.connecting(), - ethernet_network.connected(), - chromeos::TYPE_ETHERNET, - false)); + if (ethernet_network) { + list->Append(GetNetwork( + ethernet_network->service_path(), + icon, + l10n_util::GetStringUTF8(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET), + ethernet_network->connecting(), + ethernet_network->connected(), + chromeos::TYPE_ETHERNET, + false, + chromeos::ACTIVATION_STATE_UNKNOWN, + false)); + } } return list; } @@ -885,48 +968,54 @@ ListValue* InternetOptionsHandler::GetWirelessList() { for (chromeos::WifiNetworkVector::const_iterator it = wifi_networks.begin(); it != wifi_networks.end(); ++it) { SkBitmap icon = chromeos::NetworkMenu::IconForNetworkStrength( - it->strength(), true); - if (it->encrypted()) { + (*it)->strength(), true); + if ((*it)->encrypted()) { icon = chromeos::NetworkMenu::IconForDisplay(icon, *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE)); } list->Append(GetNetwork( - it->service_path(), + (*it)->service_path(), icon, - it->name(), - it->connecting(), - it->connected(), + (*it)->name(), + (*it)->connecting(), + (*it)->connected(), chromeos::TYPE_WIFI, + false, + chromeos::ACTIVATION_STATE_UNKNOWN, false)); } - const chromeos::CellularNetworkVector& cellular_networks = + const chromeos::CellularNetworkVector cellular_networks = cros->cellular_networks(); for (chromeos::CellularNetworkVector::const_iterator it = cellular_networks.begin(); it != cellular_networks.end(); ++it) { SkBitmap icon = chromeos::NetworkMenu::IconForNetworkStrength( - it->strength(), true); - SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_3G); + (*it)->strength(), true); + SkBitmap badge = chromeos::NetworkMenu::BadgeForNetworkTechnology(*it); icon = chromeos::NetworkMenu::IconForDisplay(icon, badge); list->Append(GetNetwork( - it->service_path(), + (*it)->service_path(), icon, - it->name(), - it->connecting(), - it->connected(), + (*it)->name(), + (*it)->connecting(), + (*it)->connected(), chromeos::TYPE_CELLULAR, - false)); + false, + (*it)->activation_state(), + (*it)->restricted_pool())); } // Add "Other..." if wifi is enabled. if (cros->wifi_enabled()) { list->Append(GetNetwork( kOtherNetworksFakePath, - SkBitmap(), + *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0_BLACK), l10n_util::GetStringUTF8(IDS_OPTIONS_SETTINGS_OTHER_NETWORKS), false, false, chromeos::TYPE_WIFI, + false, + chromeos::ACTIVATION_STATE_UNKNOWN, false)); } @@ -943,37 +1032,21 @@ ListValue* InternetOptionsHandler::GetRememberedList() { cros->remembered_wifi_networks(); for (chromeos::WifiNetworkVector::const_iterator it = wifi_networks.begin(); it != wifi_networks.end(); ++it) { - SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0); - if (it->encrypted()) { + SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0_BLACK); + if ((*it)->encrypted()) { icon = chromeos::NetworkMenu::IconForDisplay(icon, *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE)); } list->Append(GetNetwork( - it->service_path(), + (*it)->service_path(), icon, - it->name(), - it->connecting(), - it->connected(), + (*it)->name(), + (*it)->connecting(), + (*it)->connected(), chromeos::TYPE_WIFI, - true)); - } - - const chromeos::CellularNetworkVector& cellular_networks = - cros->remembered_cellular_networks(); - for (chromeos::CellularNetworkVector::const_iterator it = - cellular_networks.begin(); it != cellular_networks.end(); ++it) { - SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0); - SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_3G); - icon = chromeos::NetworkMenu::IconForDisplay(icon, badge); - list->Append(GetNetwork( - it->service_path(), - icon, - it->name(), - it->connecting(), - it->connected(), - chromeos::TYPE_CELLULAR, - true)); + true, + chromeos::ACTIVATION_STATE_UNKNOWN, + false)); } - return list; } diff --git a/chrome/browser/chromeos/dom_ui/internet_options_handler.h b/chrome/browser/chromeos/dom_ui/internet_options_handler.h index f960121..bce82ad 100644 --- a/chrome/browser/chromeos/dom_ui/internet_options_handler.h +++ b/chrome/browser/chromeos/dom_ui/internet_options_handler.h @@ -16,8 +16,11 @@ class WindowDelegate; } // ChromeOS internet options page UI handler. -class InternetOptionsHandler : public OptionsPageUIHandler, - public chromeos::NetworkLibrary::Observer { +class InternetOptionsHandler + : public OptionsPageUIHandler, + public chromeos::NetworkLibrary::NetworkManagerObserver, + public chromeos::NetworkLibrary::NetworkObserver, + public chromeos::NetworkLibrary::CellularDataPlanObserver { public: InternetOptionsHandler(); virtual ~InternetOptionsHandler(); @@ -28,9 +31,13 @@ class InternetOptionsHandler : public OptionsPageUIHandler, // DOMMessageHandler implementation. virtual void RegisterMessages(); - // NetworkLibrary::Observer implementation. - virtual void NetworkChanged(chromeos::NetworkLibrary* obj); - virtual void CellularDataPlanChanged(chromeos::NetworkLibrary* obj); + // NetworkLibrary::NetworkManagerObserver implementation. + virtual void OnNetworkManagerChanged(chromeos::NetworkLibrary* network_lib); + // NetworkLibrary::NetworkObserver implementation. + virtual void OnNetworkChanged(chromeos::NetworkLibrary* network_lib, + const chromeos::Network* network); + // NetworkLibrary::CellularDataPlanObserver implementation. + virtual void OnCellularDataPlanChanged(chromeos::NetworkLibrary* network_lib); private: // Passes data needed to show details overlay for network. @@ -56,11 +63,9 @@ class InternetOptionsHandler : public OptionsPageUIHandler, // Populates the ui with the details of the given device path. This forces // an overlay to be displayed in the UI. - void PopulateDictionaryDetails(const chromeos::Network& net, + void PopulateDictionaryDetails(const chromeos::Network* net, chromeos::NetworkLibrary* cros); - void PopupWirelessPassword(const chromeos::WifiNetwork& network); - // Converts CellularDataPlan structure into dictionary for JS. Formats // plan settings into human readable texts. DictionaryValue* CellularDataPlanToDictionary( @@ -72,7 +77,8 @@ class InternetOptionsHandler : public OptionsPageUIHandler, // Creates the map of a network ListValue* GetNetwork(const std::string& service_path, const SkBitmap& icon, const std::string& name, bool connecting, bool connected, - int connection_type, bool remembered); + chromeos::ConnectionType connection_type, bool remembered, + chromeos::ActivationState activation_state, bool restricted_ip); // Creates the map of wired networks ListValue* GetWiredList(); @@ -80,6 +86,13 @@ class InternetOptionsHandler : public OptionsPageUIHandler, ListValue* GetWirelessList(); // Creates the map of remembered networks ListValue* GetRememberedList(); + // Refresh the display of network information + void RefreshNetworkData(chromeos::NetworkLibrary* cros); + // Monitor the active network, if any + void MonitorActiveNetwork(chromeos::NetworkLibrary* cros); + + // If any network is currently active, this is its service path + std::string active_network_; DISALLOW_COPY_AND_ASSIGN(InternetOptionsHandler); }; diff --git a/chrome/browser/chromeos/dom_ui/keyboard_overlay_ui.cc b/chrome/browser/chromeos/dom_ui/keyboard_overlay_ui.cc new file mode 100644 index 0000000..91b7a37 --- /dev/null +++ b/chrome/browser/chromeos/dom_ui/keyboard_overlay_ui.cc @@ -0,0 +1,282 @@ +// Copyright (c) 2009 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/chromeos/dom_ui/keyboard_overlay_ui.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/callback.h" +#include "base/values.h" +#include "base/weak_ptr.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/dom_ui/chrome_url_data_manager.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/generated_resources.h" + +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/cros/cros_library.h" +#endif + +class KeyboardOverlayUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + KeyboardOverlayUIHTMLSource(); + + // Called when the keyboard overlay has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + ~KeyboardOverlayUIHTMLSource() {} + + DISALLOW_COPY_AND_ASSIGN(KeyboardOverlayUIHTMLSource); +}; + + +// The handler for Javascript messages related to the "keyboardoverlay" view. +class KeyboardOverlayHandler + : public DOMMessageHandler, + public base::SupportsWeakPtr<KeyboardOverlayHandler> { + public: + KeyboardOverlayHandler(); + virtual ~KeyboardOverlayHandler(); + + // DOMMessageHandler implementation. + virtual DOMMessageHandler* Attach(DOMUI* dom_ui); + virtual void RegisterMessages(); + + private: + DISALLOW_COPY_AND_ASSIGN(KeyboardOverlayHandler); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// KeyboardOverlayUIHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +KeyboardOverlayUIHTMLSource::KeyboardOverlayUIHTMLSource() + : DataSource(chrome::kChromeUIKeyboardOverlayHost, + MessageLoop::current()) { +} + +void KeyboardOverlayUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DictionaryValue localized_strings; + localized_strings.SetString("keyboardOverlayTitle", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_TITLE)); + localized_strings.SetString("keyboardOverlayInstructions", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_INSTRUCTIONS)); + localized_strings.SetString("keyboardOverlayActivateLastTab", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ACTIVATE_LAST_TAB)); + localized_strings.SetString("keyboardOverlayActivateNextTab", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ACTIVATE_NEXT_TAB)); + localized_strings.SetString("keyboardOverlayActivatePreviousTab", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ACTIVATE_PREVIOUS_TAB)); + localized_strings.SetString("keyboardOverlayActivateTab1", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_1)); + localized_strings.SetString("keyboardOverlayActivateTab2", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_2)); + localized_strings.SetString("keyboardOverlayActivateTab3", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_3)); + localized_strings.SetString("keyboardOverlayActivateTab4", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_4)); + localized_strings.SetString("keyboardOverlayActivateTab5", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_5)); + localized_strings.SetString("keyboardOverlayActivateTab6", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_6)); + localized_strings.SetString("keyboardOverlayActivateTab7", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_7)); + localized_strings.SetString("keyboardOverlayActivateTab8", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_8)); + localized_strings.SetString("keyboardOverlayAddWwwAndComAndOpenAddress", + l10n_util::GetStringUTF16( + IDS_KEYBOARD_OVERLAY_ADD_WWW_AND_COM_AND_OPEN_ADDRESS)); + localized_strings.SetString("keyboardOverlayBookmarkCurrentPage", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_BOOKMARK_CURRENT_PAGE)); + localized_strings.SetString("keyboardOverlayBookmarkAllTabs", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_BOOKMARK_ALL_TABS)); + localized_strings.SetString("keyboardOverlayCloseTab", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_CLOSE_TAB)); + localized_strings.SetString("keyboardOverlayCloseWindow", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_CLOSE_WINDOW)); + localized_strings.SetString("keyboardOverlayDeleteWord", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_DELETE_WORD)); + localized_strings.SetString("keyboardOverlayDeveloperTools", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_DEVELOPER_TOOLS)); + localized_strings.SetString("keyboardOverlayFindAgain", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_FIND_AGAIN)); + localized_strings.SetString("keyboardOverlayFindPrevious", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_FIND_PREVIOUS)); + localized_strings.SetString("keyboardOverlayFindPrevious", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_FIND_PREVIOUS)); + localized_strings.SetString("keyboardOverlayFindText", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_FIND_TEXT)); + localized_strings.SetString("keyboardOverlayFocusAddressBar", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_FOCUS_ADDRESS_BAR)); + localized_strings.SetString("keyboardOverlayFocusAddressBar", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_FOCUS_ADDRESS_BAR)); + localized_strings.SetString("keyboardOverlayFocusAddressBarInSearchMode", + l10n_util::GetStringUTF16( + IDS_KEYBOARD_OVERLAY_FOCUS_ADDRESS_BAR_IN_SEARCH_MODE)); + localized_strings.SetString("keyboardOverlayDomInspector", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_DOM_INSPECTOR)); + localized_strings.SetString("keyboardOverlayDownloads", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_DOWNLOADS)); + localized_strings.SetString("keyboardOverlayTaskManager", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_TASK_MANAGER)); + localized_strings.SetString("keyboardOverlayBack", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_BACK)); + localized_strings.SetString("keyboardOverlayForward", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_FORWARD)); + localized_strings.SetString("keyboardOverlayForward", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_FORWARD)); + localized_strings.SetString("keyboardOverlayHistory", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_HISTORY)); + localized_strings.SetString("keyboardOverlayNewTab", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_NEW_TAB)); + localized_strings.SetString("keyboardOverlayOpenAddressInNewTab", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_OPEN_ADDRESS_IN_NEW_TAB)); + localized_strings.SetString("keyboardOverlayNewIncognitoWindow", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_NEW_INCOGNITO_WINDOW)); + localized_strings.SetString("keyboardOverlayNewWindow", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_NEW_WINDOW)); + localized_strings.SetString("keyboardOverlayPasteAsPlainText", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_PASTE_AS_PLAIN_TEXT)); + localized_strings.SetString("keyboardOverlayPrint", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_PRINT)); + localized_strings.SetString("keyboardOverlayReloadCurrentPage", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_RELOAD_CURRENT_PAGE)); + localized_strings.SetString("keyboardOverlayReopenLastClosedTab", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_REOPEN_LAST_CLOSED_TAB)); + localized_strings.SetString("keyboardOverlayResetZoom", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_RESET_ZOOM)); + localized_strings.SetString("keyboardOverlaySelectWordAtATime", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_SELECT_WORD_AT_A_TIME)); + localized_strings.SetString("keyboardOverlayToggleBookmarkBar", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_TOGGLE_BOOKMARK_BAR)); + localized_strings.SetString("keyboardOverlayViewSource", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_VIEW_SOURCE)); + localized_strings.SetString("keyboardOverlayZoomIn", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ZOOM_IN)); + localized_strings.SetString("keyboardOverlayZoomOut", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_ZOOM_OUT)); + localized_strings.SetString("keyboardOverlayResetZoom", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_RESET_ZOOM)); + localized_strings.SetString("keyboardOverlayFocusAddressBarInSearchMode", + l10n_util::GetStringUTF16( + IDS_KEYBOARD_OVERLAY_FOCUS_ADDRESS_BAR_IN_SEARCH_MODE)); + localized_strings.SetString("keyboardOverlayFullScreen", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_FULL_SCREEN)); + localized_strings.SetString("keyboardOverlayTakeScreenshot", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_TAKE_SCREENSHOT)); + localized_strings.SetString("keyboardOverlayHome", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_HOME)); + localized_strings.SetString("keyboardOverlayEnd", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_END)); + localized_strings.SetString("keyboardOverlayNextWindow", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_NEXT_WINDOW)); + localized_strings.SetString("keyboardOverlayContentBrowser", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_CONTENT_BROWSER)); + localized_strings.SetString("keyboardOverlayPageUp", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_PAGE_UP)); + localized_strings.SetString("keyboardOverlayPageDown", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_PAGE_DOWN)); + localized_strings.SetString("keyboardOverlayPreviousWindow", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_PREVIOUS_WINDOW)); + localized_strings.SetString("keyboardOverlayUseExternalMonitor", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_USE_EXTERNAL_MONITOR)); + localized_strings.SetString("keyboardOverlayReloadIgnoringCache", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_RELOAD_IGNORING_CACHE)); + localized_strings.SetString("keyboardOverlaySave", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_SAVE)); + localized_strings.SetString("keyboardOverlayScrollUpOnePage", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_SCROLL_UP_ONE_PAGE)); + localized_strings.SetString("keyboardOverlaySettings", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_SETTINGS)); + localized_strings.SetString("keyboardOverlaySignOut", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_SIGN_OUT)); + localized_strings.SetString("keyboardOverlayUndo", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_UNDO)); + localized_strings.SetString("keyboardOverlayWordMove", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_WORD_MOVE)); + localized_strings.SetString("keyboardOverlaySelectAll", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_SELECT_ALL)); + localized_strings.SetString("keyboardOverlaySelectPreviousInputMethod", + l10n_util::GetStringUTF16( + IDS_KEYBOARD_OVERLAY_SELECT_PREVIOUS_INPUT_METHOD)); + localized_strings.SetString("keyboardOverlayCycleThroughInputMethods", + l10n_util::GetStringUTF16( + IDS_KEYBOARD_OVERLAY_CYCLE_THROUGH_INPUT_METHODS)); + localized_strings.SetString("keyboardOverlayCloseWindow", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_CLOSE_WINDOW)); + localized_strings.SetString("keyboardOverlayViewKeyboardOverlay", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_VIEW_KEYBOARD_OVERLAY)); + localized_strings.SetString("keyboardOverlayCut", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_CUT)); + localized_strings.SetString("keyboardOverlayCopy", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_COPY)); + localized_strings.SetString("keyboardOverlayPaste", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_PASTE)); + localized_strings.SetString("keyboardOverlayHelp", + l10n_util::GetStringUTF16(IDS_KEYBOARD_OVERLAY_HELP)); + + static const base::StringPiece keyboard_overlay_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_KEYBOARD_OVERLAY_HTML)); + const std::string full_html = jstemplate_builder::GetI18nTemplateHtml( + keyboard_overlay_html, &localized_strings); + + scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); + html_bytes->data.resize(full_html.size()); + std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin()); + + SendResponse(request_id, html_bytes); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// KeyboardOverlayHandler +// +//////////////////////////////////////////////////////////////////////////////// +KeyboardOverlayHandler::KeyboardOverlayHandler() { +} + +KeyboardOverlayHandler::~KeyboardOverlayHandler() { +} + +DOMMessageHandler* KeyboardOverlayHandler::Attach(DOMUI* dom_ui) { + return DOMMessageHandler::Attach(dom_ui); +} + +void KeyboardOverlayHandler::RegisterMessages() { +} + +//////////////////////////////////////////////////////////////////////////////// +// +// KeyboardOverlayUI +// +//////////////////////////////////////////////////////////////////////////////// + +KeyboardOverlayUI::KeyboardOverlayUI(TabContents* contents) + : HtmlDialogUI(contents) { + KeyboardOverlayHandler* handler = new KeyboardOverlayHandler(); + AddMessageHandler((handler)->Attach(this)); + KeyboardOverlayUIHTMLSource* html_source = new KeyboardOverlayUIHTMLSource(); + + // Set up the chrome://keyboardoverlay/ source. + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod( + Singleton<ChromeURLDataManager>::get(), + &ChromeURLDataManager::AddDataSource, + make_scoped_refptr(html_source))); +} diff --git a/chrome/browser/chromeos/dom_ui/keyboard_overlay_ui.h b/chrome/browser/chromeos/dom_ui/keyboard_overlay_ui.h new file mode 100644 index 0000000..8d112cf --- /dev/null +++ b/chrome/browser/chromeos/dom_ui/keyboard_overlay_ui.h @@ -0,0 +1,24 @@ +// 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_CHROMEOS_DOM_UI_KEYBOARD_OVERLAY_UI_H_ +#define CHROME_BROWSER_CHROMEOS_DOM_UI_KEYBOARD_OVERLAY_UI_H_ +#pragma once + +#include <string> + +#include "chrome/browser/dom_ui/html_dialog_ui.h" + +class Browser; +class Profile; + +class KeyboardOverlayUI : public HtmlDialogUI { + public: + explicit KeyboardOverlayUI(TabContents* contents); + + private: + DISALLOW_COPY_AND_ASSIGN(KeyboardOverlayUI); +}; + +#endif // CHROME_BROWSER_CHROMEOS_DOM_UI_KEYBOARD_OVERLAY_UI_H_ diff --git a/chrome/browser/chromeos/dom_ui/labs_handler.cc b/chrome/browser/chromeos/dom_ui/labs_handler.cc index 3a9a239..da605aa 100644 --- a/chrome/browser/chromeos/dom_ui/labs_handler.cc +++ b/chrome/browser/chromeos/dom_ui/labs_handler.cc @@ -31,11 +31,6 @@ void LabsHandler::GetLocalizedValues( localized_strings->SetString("advanced_filesystem", l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_ADVANCEDFS_DESCRIPTION)); - localized_strings->SetString("talk_title", - l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_SECTION_TITLE_TALK)); - localized_strings->SetString("talk", - l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_TALK_DESCRIPTION)); - localized_strings->SetString("side_tabs_title", l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_SECTION_TITLE_SIDE_TABS)); localized_strings->SetString("side_tabs", diff --git a/chrome/browser/chromeos/dom_ui/language_options_handler.cc b/chrome/browser/chromeos/dom_ui/language_options_handler.cc index 0ef8582..05570bc 100644 --- a/chrome/browser/chromeos/dom_ui/language_options_handler.cc +++ b/chrome/browser/chromeos/dom_ui/language_options_handler.cc @@ -12,7 +12,7 @@ #include "app/l10n_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/cros/cros_library.h" @@ -56,8 +56,8 @@ void LanguageOptionsHandler::GetLocalizedValues( localized_strings->SetString("ok_button", l10n_util::GetStringUTF16(IDS_OK)); localized_strings->SetString("remove_button", l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_REMOVE_BUTTON)); - localized_strings->SetString("restart_button", - l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_RESTART_BUTTON)); + localized_strings->SetString("sign_out_button", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_SIGN_OUT_BUTTON)); localized_strings->SetString("add_language_instructions", l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_LANGUAGES_ADD_LANGUAGE_INSTRUCTIONS)); @@ -84,7 +84,7 @@ void LanguageOptionsHandler::GetLocalizedValues( l10n_util::GetStringFUTF16( IDS_OPTIONS_SETTINGS_LANGUAGES_DISPLAY_IN_THIS_LANGUAGE, l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME))); - localized_strings->SetString("restart_required", + localized_strings->SetString("sign_out_required", l10n_util::GetStringUTF16(IDS_OPTIONS_RESTART_REQUIRED)); localized_strings->SetString("this_language_is_currently_in_use", l10n_util::GetStringFUTF16( @@ -130,8 +130,8 @@ void LanguageOptionsHandler::RegisterMessages() { &LanguageOptionsHandler::SpellCheckLanguageChangeCallback)); dom_ui_->RegisterMessageCallback("uiLanguageChange", NewCallback(this, &LanguageOptionsHandler::UiLanguageChangeCallback)); - dom_ui_->RegisterMessageCallback("uiLanguageRestart", - NewCallback(this, &LanguageOptionsHandler::RestartCallback)); + dom_ui_->RegisterMessageCallback("uiLanguageSignOut", + NewCallback(this, &LanguageOptionsHandler::SignOutCallback)); } ListValue* LanguageOptionsHandler::GetInputMethodList( @@ -303,13 +303,11 @@ void LanguageOptionsHandler::SpellCheckLanguageChangeCallback( UserMetrics::RecordComputedAction(action); } -void LanguageOptionsHandler::RestartCallback(const ListValue* args) { - UserMetrics::RecordAction(UserMetricsAction("LanguageOptions_Restart")); +void LanguageOptionsHandler::SignOutCallback(const ListValue* args) { + UserMetrics::RecordAction(UserMetricsAction("LanguageOptions_SignOut")); Browser* browser = Browser::GetBrowserForController( &dom_ui_->tab_contents()->controller(), NULL); - // TODO(kochi): For ChromiumOS, just exiting means browser restart. - // Implement browser restart for Chromium Win/Linux/Mac. if (browser) browser->ExecuteCommand(IDC_EXIT); } diff --git a/chrome/browser/chromeos/dom_ui/language_options_handler.h b/chrome/browser/chromeos/dom_ui/language_options_handler.h index bd1c78a..ecb8d34 100644 --- a/chrome/browser/chromeos/dom_ui/language_options_handler.h +++ b/chrome/browser/chromeos/dom_ui/language_options_handler.h @@ -84,8 +84,8 @@ class LanguageOptionsHandler : public OptionsPageUIHandler { // |args| will contain the language code as string (ex. "fr"). void SpellCheckLanguageChangeCallback(const ListValue* args); - // Called when the restart button is clicked. - void RestartCallback(const ListValue* args); + // Called when the sign out button is clicked. + void SignOutCallback(const ListValue* args); DISALLOW_COPY_AND_ASSIGN(LanguageOptionsHandler); }; diff --git a/chrome/browser/chromeos/dom_ui/menu_ui.cc b/chrome/browser/chromeos/dom_ui/menu_ui.cc index d7a73b8..8ffd2a8 100644 --- a/chrome/browser/chromeos/dom_ui/menu_ui.cc +++ b/chrome/browser/chromeos/dom_ui/menu_ui.cc @@ -26,7 +26,6 @@ #include "chrome/common/url_constants.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/jstemplate_builder.h" -#include "chrome/common/net/url_fetcher.h" #include "gfx/canvas_skia.h" #include "gfx/favicon_size.h" #include "gfx/font.h" @@ -125,7 +124,7 @@ const std::string& GetImageDataUrlForRadio(bool on) { * same source/css files. */ std::string GetMenuUIHTMLSourceFromString( - const chromeos::MenuUI& menu_ui, + const chromeos::MenuSourceDelegate* delegate, const base::StringPiece& menu_template, const std::string& menu_class, int menu_source_id, @@ -170,7 +169,8 @@ std::string GetMenuUIHTMLSourceFromString( SET_INTEGER_PROPERTY(scroll_arrow_height); SET_INTEGER_PROPERTY(label_to_accelerator_padding); - menu_ui.AddCustomConfigValues(&value_config); + if (delegate) + delegate->AddCustomConfigValues(&value_config); std::string json_config; base::JSONWriter::Write(&value_config, false, &json_config); @@ -196,16 +196,15 @@ std::string GetMenuUIHTMLSourceFromString( ResourceBundle::GetSharedInstance().GetRawDataResource(menu_css_id)); strings.SetString("menu_css", menu_css.as_string()); } - - menu_ui.AddLocalizedStrings(&strings); + if (delegate) + delegate->AddLocalizedStrings(&strings); return jstemplate_builder::GetI18nTemplateHtml(menu_template, &strings); } -class MenuUIHTMLSource : public ChromeURLDataManager::DataSource, - public URLFetcher::Delegate { +class MenuUIHTMLSource : public ChromeURLDataManager::DataSource { public: - MenuUIHTMLSource(const chromeos::MenuUI& menu_ui, + MenuUIHTMLSource(const chromeos::MenuSourceDelegate* delegate, const std::string& source_name, const std::string& menu_class, int menu_source_id, @@ -220,19 +219,11 @@ class MenuUIHTMLSource : public ChromeURLDataManager::DataSource, return "text/html"; } - // URLFetcher::Delegate implements: - virtual void OnURLFetchComplete(const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); - private: virtual ~MenuUIHTMLSource() {} // The menu ui the source is created for. - const chromeos::MenuUI& menu_ui_; + scoped_ptr<const chromeos::MenuSourceDelegate> delegate_; // The name of JS Menu class to use. const std::string menu_class_; @@ -243,9 +234,6 @@ class MenuUIHTMLSource : public ChromeURLDataManager::DataSource, // The resource id of the CSS file of the menu subclass. int menu_css_id_; -#ifndef NDEBUG - int request_id_; -#endif DISALLOW_COPY_AND_ASSIGN(MenuUIHTMLSource); }; @@ -317,45 +305,30 @@ class MenuHandler : public chromeos::MenuHandlerBase, // //////////////////////////////////////////////////////////////////////////////// -MenuUIHTMLSource::MenuUIHTMLSource(const chromeos::MenuUI& menu_ui, +MenuUIHTMLSource::MenuUIHTMLSource(const chromeos::MenuSourceDelegate* delegate, const std::string& source_name, const std::string& menu_class, int menu_source_id, int menu_css_id) : DataSource(source_name, MessageLoop::current()), - menu_ui_(menu_ui), + delegate_(delegate), menu_class_(menu_class), menu_source_id_(menu_source_id), menu_css_id_(menu_css_id) -#ifndef NDEBUG - , request_id_(-1) -#endif { - // Important ! - // Don't call any method on menu_ui. MenuUI object is not yet initialized } void MenuUIHTMLSource::StartDataRequest(const std::string& path, bool is_off_the_record, int request_id) { -#ifndef NDEBUG - std::string url = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kDOMUIMenuUrl); - if (!url.empty()) { - request_id_ = request_id; - URLFetcher* fetcher = new URLFetcher(GURL(url), URLFetcher::GET, this); - fetcher->set_request_context(menu_ui_.GetProfile()->GetRequestContext()); - fetcher->Start(); - return; - } -#endif static const base::StringPiece menu_template( ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_MENU_HTML)); // The resource string should be pure code and should not contain // i18n string. const std::string menu_html = GetMenuUIHTMLSourceFromString( - menu_ui_, menu_template, menu_class_, menu_source_id_, menu_css_id_); + delegate_.get(), menu_template, menu_class_, + menu_source_id_, menu_css_id_); scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); @@ -366,30 +339,6 @@ void MenuUIHTMLSource::StartDataRequest(const std::string& path, SendResponse(request_id, html_bytes); } -void MenuUIHTMLSource::OnURLFetchComplete(const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data) { -#ifndef NDEBUG - // This should not be called in release build. - const std::string menu_html = GetMenuUIHTMLSourceFromString( - menu_ui_, data, menu_class_, menu_source_id_, menu_css_id_); - - scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); - - // TODO(oshima): Eliminate boilerplate code. See http://crbug.com/57583 . - html_bytes->data.resize(menu_html.size()); - std::copy(menu_html.begin(), menu_html.end(), - html_bytes->data.begin()); - SendResponse(request_id_, html_bytes); - - delete source; -#endif -} - - //////////////////////////////////////////////////////////////////////////////// // // MenuHandler @@ -639,9 +588,6 @@ void MenuUI::ModelUpdated(const menus::MenuModel* model) { break; default: // TODO(oshima): We don't support BUTTOM_ITEM for now. - // I haven't decided how to implement zoom/cut&paste - // stuff, but may do somethign similar to what linux_views - // does. NOTREACHED(); continue; } @@ -689,12 +635,12 @@ DictionaryValue* MenuUI::CreateMenuItem(const menus::MenuModel* model, // static ChromeURLDataManager::DataSource* MenuUI::CreateMenuUIHTMLSource( - const MenuUI& menu_ui, + const MenuSourceDelegate* delegate, const std::string& source_name, const std::string& menu_class, int menu_source_id, int menu_css_id) { - return new MenuUIHTMLSource(menu_ui, + return new MenuUIHTMLSource(delegate, source_name, menu_class, menu_source_id, @@ -703,12 +649,12 @@ ChromeURLDataManager::DataSource* MenuUI::CreateMenuUIHTMLSource( // static bool MenuUI::IsEnabled() { - return !CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableDOMUIMenu); + return CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableDOMUIMenu); } ChromeURLDataManager::DataSource* MenuUI::CreateDataSource() { - return CreateMenuUIHTMLSource(*this, + return CreateMenuUIHTMLSource(NULL, chrome::kChromeUIMenu, "Menu" /* class name */, kNoExtraResource, diff --git a/chrome/browser/chromeos/dom_ui/menu_ui.h b/chrome/browser/chromeos/dom_ui/menu_ui.h index 151c324..9f8c5db 100644 --- a/chrome/browser/chromeos/dom_ui/menu_ui.h +++ b/chrome/browser/chromeos/dom_ui/menu_ui.h @@ -21,6 +21,20 @@ namespace chromeos { class DOMUIMenuControl; +// MenuSourceDelegate class allows subclass to injects specific values +// to menu javascript code. +class MenuSourceDelegate { + public: + virtual ~MenuSourceDelegate() {} + // Subclass can add extra parameters or replaces default configuration. + virtual void AddCustomConfigValues(DictionaryValue* config) const {}; + + // Subclass can add their values to |localized_strings| and those values + // are used by JS template builder and could be accessed via JS class + // LocalStrings. + virtual void AddLocalizedStrings(DictionaryValue* localized_strings) const {} +}; + class MenuUI : public DOMUI { public: explicit MenuUI(TabContents* contents); @@ -36,21 +50,11 @@ class MenuUI : public DOMUI { int* max_icon_width, bool* has_accel) const; - // Subclass can add extra parameters or replaces default configuration. - virtual void AddCustomConfigValues(DictionaryValue* config) const {}; - - // Subclass can add their values to |localized_strings| and those values - // are used by JS template builder and could be accessed via JS class - // LocalStrings. - virtual void AddLocalizedStrings(DictionaryValue* localized_strings) const {}; - // A utility function which creates a concrete html file from // template file |menu_resource_id| and |menu_css_id| for given |menu_class|. // The resource_name is the host part of DOMUI's url. - // Caution: This calls MenuUI::GetProfile() when creating the data source, - // thus, it has to be initialized. static ChromeURLDataManager::DataSource* CreateMenuUIHTMLSource( - const MenuUI& menu_ui, + const MenuSourceDelegate* delegate, const std::string& source_name, const std::string& menu_class, int menu_source_res_id, diff --git a/chrome/browser/chromeos/dom_ui/mobile_setup_ui.cc b/chrome/browser/chromeos/dom_ui/mobile_setup_ui.cc index 879d333..d021ab3 100644 --- a/chrome/browser/chromeos/dom_ui/mobile_setup_ui.cc +++ b/chrome/browser/chromeos/dom_ui/mobile_setup_ui.cc @@ -1,9 +1,10 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-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/chromeos/dom_ui/mobile_setup_ui.h" +#include <algorithm> #include <map> #include <string> @@ -12,6 +13,7 @@ #include "base/callback.h" #include "base/file_util.h" #include "base/json/json_reader.h" +#include "base/json/json_writer.h" #include "base/logging.h" #include "base/string_piece.h" #include "base/string_util.h" @@ -26,8 +28,11 @@ #include "chrome/browser/chromeos/cros/network_library.h" #include "chrome/browser/chromeos/cros/system_library.h" #include "chrome/browser/dom_ui/chrome_url_data_manager.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/jstemplate_builder.h" +#include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "googleurl/src/gurl.h" #include "grit/browser_resources.h" @@ -38,21 +43,12 @@ namespace { // Host page JS API function names. +const char kJsApiStartActivation[] = "startActivation"; const char kJsApiCloseTab[] = "closeTab"; const char kJsApiSetTransactionStatus[] = "setTransactionStatus"; const wchar_t kJsDeviceStatusChangedHandler[] = - L"handler.MobileSetup.deviceStateChanged"; - -// Collular device states that are reported to DOM UI layer. -const char kStateUnknown[] = "unknown"; -const char kStateConnecting[] = "connecting"; -const char kStateError[] = "error"; -const char kStateNeedsPayment[] = "payment"; -const char kStateActivating[] = "activating"; -const char kStateDisconnected[] = "disconnected"; -const char kStateConnected[] = "connected"; -const char kFailedPayment[] = "failed_payment"; + L"mobile.MobileSetup.deviceStateChanged"; // Error codes matching codes defined in the cellular config file. const char kErrorDefault[] = "default"; @@ -73,6 +69,20 @@ const char kCellularConfigPath[] = const char kVersionField[] = "version"; const char kErrorsField[] = "errors"; +chromeos::CellularNetwork* GetCellularNetwork() { + chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()-> + GetNetworkLibrary(); + if (lib->cellular_networks().begin() != lib->cellular_networks().end()) { + return *(lib->cellular_networks().begin()); + } + return NULL; +} + +chromeos::CellularNetwork* GetCellularNetwork(const std::string& service_path) { + return chromeos::CrosLibrary::Get()-> + GetNetworkLibrary()->FindCellularNetworkByPath(service_path); +} + } // namespace class CellularConfigDocument { @@ -96,7 +106,7 @@ static std::map<std::string, std::string> error_messages_; class MobileSetupUIHTMLSource : public ChromeURLDataManager::DataSource { public: - MobileSetupUIHTMLSource(); + explicit MobileSetupUIHTMLSource(const std::string& service_path); // Called when the network layer has requested a resource underneath // the path we registered. @@ -108,17 +118,20 @@ class MobileSetupUIHTMLSource : public ChromeURLDataManager::DataSource { } private: - ~MobileSetupUIHTMLSource() {} + virtual ~MobileSetupUIHTMLSource() {} + std::string service_path_; DISALLOW_COPY_AND_ASSIGN(MobileSetupUIHTMLSource); }; // The handler for Javascript messages related to the "register" view. -class MobileSetupHandler : public DOMMessageHandler, - public chromeos::NetworkLibrary::Observer, - public base::SupportsWeakPtr<MobileSetupHandler> { +class MobileSetupHandler + : public DOMMessageHandler, + public chromeos::NetworkLibrary::NetworkManagerObserver, + public chromeos::NetworkLibrary::NetworkObserver, + public base::SupportsWeakPtr<MobileSetupHandler> { public: - MobileSetupHandler(); + explicit MobileSetupHandler(const std::string& service_path); virtual ~MobileSetupHandler(); // Init work after Attach. @@ -128,34 +141,103 @@ class MobileSetupHandler : public DOMMessageHandler, virtual DOMMessageHandler* Attach(DOMUI* dom_ui); virtual void RegisterMessages(); - // NetworkLibrary::Observer implementation. - virtual void NetworkChanged(chromeos::NetworkLibrary* obj); + // NetworkLibrary::NetworkManagerObserver implementation. + virtual void OnNetworkManagerChanged(chromeos::NetworkLibrary* obj); + // NetworkLibrary::NetworkObserver implementation. + virtual void OnNetworkChanged(chromeos::NetworkLibrary* obj, + const chromeos::Network* network); private: + typedef enum PlanActivationState { + PLAN_ACTIVATION_PAGE_LOADING = -1, + PLAN_ACTIVATION_START = 0, + PLAN_ACTIVATION_INITIATING_ACTIVATION = 1, + PLAN_ACTIVATION_RECONNECTING = 2, + PLAN_ACTIVATION_SHOWING_PAYMENT = 3, + PLAN_ACTIVATION_DONE = 4, + PLAN_ACTIVATION_ERROR = 5, + } PlanActivationState; + + class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> { + public: + explicit TaskProxy(const base::WeakPtr<MobileSetupHandler>& handler) + : handler_(handler) { + } + TaskProxy(const base::WeakPtr<MobileSetupHandler>& handler, + const std::string& status) + : handler_(handler), status_(status) { + } + void HandleStartActivation() { + if (handler_) + handler_->StartActivation(); + } + void HandleSetTransactionStatus() { + if (handler_) + handler_->SetTransactionStatus(status_); + } + private: + base::WeakPtr<MobileSetupHandler> handler_; + std::string status_; + DISALLOW_COPY_AND_ASSIGN(TaskProxy); + }; + // Handlers for JS DOMUI messages. void HandleCloseTab(const ListValue* args); void HandleSetTransactionStatus(const ListValue* args); + void HandleStartActivation(const ListValue* args); + void SetTransactionStatus(const std::string& status); + void StartActivation(); // Sends message to host registration page with system/user info data. void SendDeviceInfo(); + // Verify the state of cellular network and modify internal state. + void EvaluateCellularNetwork(chromeos::CellularNetwork* network); + // Check the current cellular network for error conditions. + bool GotActivationError(const chromeos::CellularNetwork* network, + std::string* error); + void ChangeState(chromeos::CellularNetwork* network, + PlanActivationState new_state, + const std::string& error_description); + // Prepares network devices for cellular activation process. + void SetupActivationProcess(chromeos::CellularNetwork* network); + // Resets network devices after cellular activation process. + // |network| should be NULL if the activation process failed. + void CompleteActivation(chromeos::CellularNetwork* network); + // Control routines for handling other types of connections during + // cellular activation. + void ReEnableOtherConnections(); + // Converts the currently active CellularNetwork device into a JS object. - static bool GetDeviceInfo(DictionaryValue* value); + static void GetDeviceInfo(const chromeos::CellularNetwork* network, + DictionaryValue* value); static bool ShouldReportDeviceState(std::string* state, std::string* error); // Performs activation state cellular device evaluation. // Returns false if device activation failed. In this case |error| // will contain error message to be reported to DOM UI. - static bool CheckForActivationError(chromeos::CellularNetwork network, - std::string* error); + static bool EvaluateCellularDeviceState(bool* report_status, + std::string* state, + std::string* error); // Return error message for a given code. static std::string GetErrorMessage(const std::string& code); static void LoadCellularConfig(); + static const char* GetStateDescription(PlanActivationState state); + static scoped_ptr<CellularConfigDocument> cellular_config_; TabContents* tab_contents_; + // Internal handler state. + PlanActivationState state_; + std::string service_path_; + // Flags that control if wifi and ethernet connection needs to be restored + // after the activation of cellular network. + bool reenable_wifi_; + bool reenable_ethernet_; + bool reenable_cert_check_; + bool transaction_complete_signalled_; DISALLOW_COPY_AND_ASSIGN(MobileSetupHandler); }; @@ -184,13 +266,13 @@ bool CellularConfigDocument::LoadFromFile(const FilePath& config_path) { scoped_ptr<Value> root(base::JSONReader::Read(config, true)); DCHECK(root.get() != NULL); if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY) { - VLOG(1) << "Bad cellular config file"; + LOG(WARNING) << "Bad cellular config file"; return false; } DictionaryValue* root_dict = static_cast<DictionaryValue*>(root.get()); if (!root_dict->GetString(kVersionField, &version_)) { - VLOG(1) << "Cellular config file missing version"; + LOG(WARNING) << "Cellular config file missing version"; return false; } DictionaryValue* errors = NULL; @@ -201,7 +283,7 @@ bool CellularConfigDocument::LoadFromFile(const FilePath& config_path) { ++keys) { std::string value; if (!errors->GetString(*keys, &value)) { - VLOG(1) << "Bad cellular config error value"; + LOG(WARNING) << "Bad cellular config error value"; error_map_.clear(); return false; } @@ -217,17 +299,22 @@ bool CellularConfigDocument::LoadFromFile(const FilePath& config_path) { // //////////////////////////////////////////////////////////////////////////////// -MobileSetupUIHTMLSource::MobileSetupUIHTMLSource() - : DataSource(chrome::kChromeUIMobileSetupHost, MessageLoop::current()) { +MobileSetupUIHTMLSource::MobileSetupUIHTMLSource( + const std::string& service_path) + : DataSource(chrome::kChromeUIMobileSetupHost, MessageLoop::current()), + service_path_(service_path) { } void MobileSetupUIHTMLSource::StartDataRequest(const std::string& path, bool is_off_the_record, int request_id) { + const chromeos::CellularNetwork* network = GetCellularNetwork(service_path_); + DictionaryValue strings; strings.SetString("title", l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_TITLE)); strings.SetString("connecting_header", - l10n_util::GetStringUTF16(IDS_MOBILE_CONNECTING_HEADER)); + l10n_util::GetStringFUTF16(IDS_MOBILE_CONNECTING_HEADER, + network ? UTF8ToUTF16(network->name()) : string16())); strings.SetString("error_header", l10n_util::GetStringUTF16(IDS_MOBILE_ERROR_HEADER)); strings.SetString("activating_header", @@ -256,11 +343,22 @@ void MobileSetupUIHTMLSource::StartDataRequest(const std::string& path, // MobileSetupHandler // //////////////////////////////////////////////////////////////////////////////// -MobileSetupHandler::MobileSetupHandler() : tab_contents_(NULL) { +MobileSetupHandler::MobileSetupHandler(const std::string& service_path) + : tab_contents_(NULL), + state_(PLAN_ACTIVATION_PAGE_LOADING), + service_path_(service_path), + reenable_wifi_(false), + reenable_ethernet_(false), + reenable_cert_check_(false), + transaction_complete_signalled_(false) { } MobileSetupHandler::~MobileSetupHandler() { - chromeos::CrosLibrary::Get()->GetNetworkLibrary()->RemoveObserver(this); + chromeos::NetworkLibrary* lib = + chromeos::CrosLibrary::Get()->GetNetworkLibrary(); + lib->RemoveNetworkManagerObserver(this); + lib->RemoveObserverForAllNetworks(this); + ReEnableOtherConnections(); } DOMMessageHandler* MobileSetupHandler::Attach(DOMUI* dom_ui) { @@ -269,149 +367,418 @@ DOMMessageHandler* MobileSetupHandler::Attach(DOMUI* dom_ui) { void MobileSetupHandler::Init(TabContents* contents) { tab_contents_ = contents; - chromeos::CrosLibrary::Get()->GetNetworkLibrary()->AddObserver(this); - - // TODO(zelidrag): We might want to move this to another thread. LoadCellularConfig(); + SetupActivationProcess(GetCellularNetwork(service_path_)); } void MobileSetupHandler::RegisterMessages() { + dom_ui_->RegisterMessageCallback(kJsApiStartActivation, + NewCallback(this, &MobileSetupHandler::HandleStartActivation)); dom_ui_->RegisterMessageCallback(kJsApiCloseTab, NewCallback(this, &MobileSetupHandler::HandleCloseTab)); dom_ui_->RegisterMessageCallback(kJsApiSetTransactionStatus, NewCallback(this, &MobileSetupHandler::HandleSetTransactionStatus)); } -void MobileSetupHandler::NetworkChanged(chromeos::NetworkLibrary* cros) { - if (!dom_ui_) - return; - DictionaryValue device; - GetDeviceInfo(&device); - std::string state, error; - if (!ShouldReportDeviceState(&state, &error)) +void MobileSetupHandler::OnNetworkManagerChanged( + chromeos::NetworkLibrary* cros) { + if (state_ == PLAN_ACTIVATION_PAGE_LOADING) return; + // Note that even though we get here when the service has + // reappeared after disappearing earlier in the activation + // process, there's no need to re-establish the NetworkObserver, + // because the service path remains the same. + EvaluateCellularNetwork(GetCellularNetwork(service_path_)); +} - device.SetString("state", state); - if (error.length()) - device.SetString("error", error); - dom_ui_->CallJavascriptFunction( - kJsDeviceStatusChangedHandler, device); +void MobileSetupHandler::OnNetworkChanged(chromeos::NetworkLibrary* cros, + const chromeos::Network* network) { + if (state_ == PLAN_ACTIVATION_PAGE_LOADING) + return; + DCHECK(network && network->type() == chromeos::TYPE_CELLULAR); + EvaluateCellularNetwork( + static_cast<chromeos::CellularNetwork*>( + const_cast<chromeos::Network*>(network))); } void MobileSetupHandler::HandleCloseTab(const ListValue* args) { - Browser* browser = BrowserList::GetLastActive(); + if (!dom_ui_) + return; + Browser* browser = BrowserList::FindBrowserWithFeature( + dom_ui_->GetProfile(), Browser::FEATURE_TABSTRIP); if (browser) browser->CloseTabContents(tab_contents_); } +void MobileSetupHandler::HandleStartActivation(const ListValue* args) { + scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr()); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod(task.get(), &TaskProxy::HandleStartActivation)); +} + void MobileSetupHandler::HandleSetTransactionStatus(const ListValue* args) { const size_t kSetTransactionStatusParamCount = 1; if (args->GetSize() != kSetTransactionStatusParamCount) return; - // Get change callback function name. std::string status; if (!args->GetString(0, &status)) return; + scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), status); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod(task.get(), &TaskProxy::HandleSetTransactionStatus)); +} - chromeos::NetworkLibrary* network_lib = - chromeos::CrosLibrary::Get()->GetNetworkLibrary(); - const chromeos::CellularNetworkVector& cell_networks = - network_lib->cellular_networks(); - if (!cell_networks.size()) +void MobileSetupHandler::StartActivation() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + chromeos::CellularNetwork* network = GetCellularNetwork(service_path_); + if (!network) { + ChangeState(NULL, PLAN_ACTIVATION_ERROR, std::string()); return; + } + // Start monitoring network property changes. + chromeos::NetworkLibrary* lib = + chromeos::CrosLibrary::Get()->GetNetworkLibrary(); + lib->AddNetworkManagerObserver(this); + lib->RemoveObserverForAllNetworks(this); + lib->AddNetworkObserver(network->service_path(), this); + state_ = PLAN_ACTIVATION_START; + EvaluateCellularNetwork(network); +} - // We assume only one cellular network will come from flimflam for now. - const chromeos::CellularNetwork& network = *(cell_networks.begin()); +void MobileSetupHandler::SetTransactionStatus(const std::string& status) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // The payment is received, try to reconnect and check the status all over + // again. + if (LowerCaseEqualsASCII(status, "ok") && + state_ == PLAN_ACTIVATION_SHOWING_PAYMENT) { + if (transaction_complete_signalled_) { + LOG(WARNING) << "Transaction completion signaled more than once!?"; + return; + } + transaction_complete_signalled_ = true; + state_ = PLAN_ACTIVATION_START; + chromeos::CellularNetwork* network = GetCellularNetwork(); + if (network && + network->activation_state() == chromeos::ACTIVATION_STATE_ACTIVATED) { + chromeos::CrosLibrary::Get()->GetNetworkLibrary()-> + DisconnectFromWirelessNetwork(network); + } else { + EvaluateCellularNetwork(network); + } + } +} + +void MobileSetupHandler::EvaluateCellularNetwork( + chromeos::CellularNetwork* network) { + if (!dom_ui_) + return; - if (LowerCaseEqualsASCII(status, "OK")) { - network.StartActivation(); + PlanActivationState new_state = state_; + if (network) { + LOG(INFO) << "Cellular:\n service=" << network->GetStateString().c_str() + << "\n ui=" << GetStateDescription(state_) + << "\n activation=" << network->GetActivationStateString().c_str() + << "\n restricted=" << (network->restricted_pool() ? "yes" : "no") + << "\n error=" << network->GetErrorString().c_str() + << "\n setvice_path=" << network->service_path().c_str(); } else { - DictionaryValue value; - value.SetString("state", kFailedPaymentError); - dom_ui_->CallJavascriptFunction(kJsDeviceStatusChangedHandler, value); + LOG(WARNING) << "Cellular service lost"; } + switch (state_) { + case PLAN_ACTIVATION_START: + if (network) { + switch (network->activation_state()) { + case chromeos::ACTIVATION_STATE_ACTIVATED: + if (network->failed_or_disconnected()) { + new_state = PLAN_ACTIVATION_RECONNECTING; + } else if (network->connection_state() == chromeos::STATE_READY) { + if (network->restricted_pool()) { + new_state = PLAN_ACTIVATION_SHOWING_PAYMENT; + } else { + new_state = PLAN_ACTIVATION_DONE; + } + } + break; + case chromeos::ACTIVATION_STATE_UNKNOWN: + case chromeos::ACTIVATION_STATE_NOT_ACTIVATED: + case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED: + if (network->failed_or_disconnected()) { + new_state = PLAN_ACTIVATION_INITIATING_ACTIVATION; + } else if (network->connected()) { + LOG(INFO) << "Disconnecting from " << + network->service_path().c_str(); + chromeos::CrosLibrary::Get()->GetNetworkLibrary()-> + DisconnectFromWirelessNetwork(network); + // Disconnect will force networks to be reevaluated, so + // we don't want to continue processing on this path anymore. + return; + } + break; + default: + new_state = PLAN_ACTIVATION_INITIATING_ACTIVATION; + break; + } + } + break; + case PLAN_ACTIVATION_INITIATING_ACTIVATION: + if (network) { + switch (network->activation_state()) { + case chromeos::ACTIVATION_STATE_ACTIVATED: + if (network->failed_or_disconnected()) { + new_state = PLAN_ACTIVATION_RECONNECTING; + } else if (network->connection_state() == chromeos::STATE_READY) { + if (network->restricted_pool()) { + new_state = PLAN_ACTIVATION_SHOWING_PAYMENT; + } else { + new_state = PLAN_ACTIVATION_DONE; + } + } + break; + case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED: + if (network->connected()) + new_state = PLAN_ACTIVATION_SHOWING_PAYMENT; + else + new_state = PLAN_ACTIVATION_RECONNECTING; + break; + case chromeos::ACTIVATION_STATE_NOT_ACTIVATED: + // Wait in this state until activation state changes. + break; + case chromeos::ACTIVATION_STATE_ACTIVATING: + break; + default: + break; + } + } + break; + case PLAN_ACTIVATION_RECONNECTING: + // Wait until the service shows up and gets activated. + if (network) { + switch (network->activation_state()) { + case chromeos::ACTIVATION_STATE_ACTIVATED: + if (network->connection_state() == chromeos::STATE_READY) { + if (network->restricted_pool()) { + new_state = PLAN_ACTIVATION_SHOWING_PAYMENT; + } else { + new_state = PLAN_ACTIVATION_DONE; + } + } + break; + case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED: + if (network->connected()) { + if (network->restricted_pool()) + new_state = PLAN_ACTIVATION_SHOWING_PAYMENT; + } + break; + default: + break; + } + } + break; + case PLAN_ACTIVATION_PAGE_LOADING: + break; + // Just ignore all signals until the site confirms payment. + case PLAN_ACTIVATION_SHOWING_PAYMENT: + // Activation completed/failed, ignore network changes. + case PLAN_ACTIVATION_DONE: + case PLAN_ACTIVATION_ERROR: + break; + } + + std::string error_description; + if (GotActivationError(network, &error_description)) { + new_state = PLAN_ACTIVATION_ERROR; + } + ChangeState(network, new_state, error_description); } -bool MobileSetupHandler::ShouldReportDeviceState(std::string* state, - std::string* error) { - DCHECK(state); - DCHECK(error); - chromeos::NetworkLibrary* network_lib = - chromeos::CrosLibrary::Get()->GetNetworkLibrary(); +// Debugging helper function, will take it out at the end. +const char* MobileSetupHandler::GetStateDescription( + PlanActivationState state) { + switch (state) { + case PLAN_ACTIVATION_PAGE_LOADING: + return "PAGE_LOADING"; + case PLAN_ACTIVATION_START: + return "ACTIVATION_START"; + case PLAN_ACTIVATION_INITIATING_ACTIVATION: + return "INITIATING_ACTIVATION"; + case PLAN_ACTIVATION_RECONNECTING: + return "RECONNECTING"; + case PLAN_ACTIVATION_SHOWING_PAYMENT: + return "SHOWING_PAYMENT"; + case PLAN_ACTIVATION_DONE: + return "DONE"; + case PLAN_ACTIVATION_ERROR: + return "ERROR"; + } + return "UNKNOWN"; +} + + +void MobileSetupHandler::CompleteActivation( + chromeos::CellularNetwork* network) { + // Remove observers, we are done with this page. + chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()-> + GetNetworkLibrary(); + // If we have successfully activated the connection, set autoconnect flag. + if (network) { + network->set_auto_connect(true); + lib->SaveCellularNetwork(network); + } + lib->RemoveNetworkManagerObserver(this); + lib->RemoveObserverForAllNetworks(this); + // Reactivate other types of connections if we have + // shut them down previously. + ReEnableOtherConnections(); +} + + +void MobileSetupHandler::ChangeState(chromeos::CellularNetwork* network, + PlanActivationState new_state, + const std::string& error_description) { + static bool first_time = true; + if (state_ == new_state && !first_time) + return; + LOG(INFO) << "Activation state flip old = " << + GetStateDescription(state_) << ", new = " << + GetStateDescription(new_state); + first_time = false; + state_ = new_state; + DictionaryValue device_dict; + + // Signal to JS layer that the state is changing. + if (network) + GetDeviceInfo(network, &device_dict); + device_dict.SetInteger("state", new_state); + if (error_description.length()) + device_dict.SetString("error", error_description); + dom_ui_->CallJavascriptFunction( + kJsDeviceStatusChangedHandler, device_dict); + + // Decide what to do with network object as a result of the new state. + switch (new_state) { + case PLAN_ACTIVATION_START: + break; + case PLAN_ACTIVATION_INITIATING_ACTIVATION: + DCHECK(network); + LOG(INFO) << "Activating service " << network->service_path().c_str(); + if (!network->StartActivation()) + new_state = PLAN_ACTIVATION_ERROR; + break; + case PLAN_ACTIVATION_RECONNECTING: { + DCHECK(network); + if (network) { + chromeos::CrosLibrary::Get()->GetNetworkLibrary()-> + ConnectToCellularNetwork(network); + } + break; + } + case PLAN_ACTIVATION_PAGE_LOADING: + return; + case PLAN_ACTIVATION_SHOWING_PAYMENT: + // Fix for fix SSL for the walled gardens where cert chain verification + // might not work. + break; + case PLAN_ACTIVATION_DONE: + DCHECK(network); + CompleteActivation(network); + break; + case PLAN_ACTIVATION_ERROR: { + CompleteActivation(NULL); + break; + } + default: + break; + } +} - const chromeos::CellularNetworkVector& cell_networks = - network_lib->cellular_networks(); - // No cellular network present? Treat as network is disconnected. - // This could be transient state that is the result of activation process. - if (!cell_networks.size()) { - *state = kStateDisconnected; - return true; +void MobileSetupHandler::ReEnableOtherConnections() { + chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()-> + GetNetworkLibrary(); + if (reenable_ethernet_) { + reenable_ethernet_ = false; + lib->EnableEthernetNetworkDevice(true); } - const chromeos::CellularNetwork& network = cell_networks.at(0); - - // First, check if device activation / plan payment failed. - // It's slightly more complex than just single state check - // that we are doing for other states below. - if (!CheckForActivationError(network, error)) { - *state = kStateError; - return true; + if (reenable_wifi_) { + reenable_wifi_ = false; + lib->EnableWifiNetworkDevice(true); + } + + PrefService* prefs = dom_ui_->GetProfile()->GetPrefs(); + if (reenable_cert_check_) { + reenable_cert_check_ = false; + prefs->SetBoolean(prefs::kCertRevocationCheckingEnabled, + reenable_cert_check_); } +} + - switch (network.activation_state()) { - case chromeos::ACTIVATION_STATE_UNKNOWN: - case chromeos::ACTIVATION_STATE_NOT_ACTIVATED: - // If this page is shown, I assume that we have already kicked off the - // process of starting the activation even though the device status - // reporting might not have caught up with us yet. - *state = kStateConnecting; - return true; - case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED: - case chromeos::ACTIVATION_STATE_ACTIVATING: - *state = kStateActivating; - return true; - case chromeos::ACTIVATION_STATE_ACTIVATED: - *state = kStateConnected; - return true; +void MobileSetupHandler::SetupActivationProcess( + chromeos::CellularNetwork* network) { + if (!network) + return; + + // Disable SSL cert checks since we will be doing this in + // restricted pool. + PrefService* prefs = dom_ui_->GetProfile()->GetPrefs(); + if (!reenable_cert_check_ && + prefs->GetBoolean( + prefs::kCertRevocationCheckingEnabled)) { + reenable_cert_check_ = true; + prefs->SetBoolean(prefs::kCertRevocationCheckingEnabled, false); } - // We don't report states that we don't understand to DOM UI. - *state = kStateUnknown; - return false; + chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()-> + GetNetworkLibrary(); + // Disable autoconnect to cellular network. + network->set_auto_connect(false); + lib->SaveCellularNetwork(network); + + // Disable ethernet and wifi. + if (!reenable_ethernet_ && lib->ethernet_enabled()) { + reenable_ethernet_ = true; + lib->EnableEthernetNetworkDevice(false); + } + if (!reenable_wifi_ && lib->wifi_enabled()) { + reenable_wifi_ = true; + lib->EnableWifiNetworkDevice(false); + } } -bool MobileSetupHandler::CheckForActivationError( - chromeos::CellularNetwork network, std::string* error) { +bool MobileSetupHandler::GotActivationError( + const chromeos::CellularNetwork* network, std::string* error) { + if (!network) + return false; bool got_error = false; const char* error_code = kErrorDefault; - // This is the magic of error selecting based - if (network.connection_state() == chromeos::STATE_FAILURE && - network.error() == chromeos::ERROR_AAA_FAILED ) { - if (network.activation_state() == + // This is the magic for detection of errors in during activation process. + if (network->connection_state() == chromeos::STATE_FAILURE && + network->error() == chromeos::ERROR_AAA_FAILED ) { + if (network->activation_state() == chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) { error_code = kErrorBadConnectionPartial; - } else if (network.activation_state() == + } else if (network->activation_state() == chromeos::ACTIVATION_STATE_ACTIVATED) { - if (network.roaming_state() == chromeos::ROAMING_STATE_HOME) { + if (network->roaming_state() == chromeos::ROAMING_STATE_HOME) { error_code = kErrorBadConnectionActivated; - } else if (network.roaming_state() == chromeos::ROAMING_STATE_ROAMING) { + } else if (network->roaming_state() == chromeos::ROAMING_STATE_ROAMING) { error_code = kErrorRoamingOnConnection; } } got_error = true; - } else if (network.connection_state() == + } else if (network->connection_state() == chromeos::STATE_ACTIVATION_FAILURE) { - if (network.error() == chromeos::ERROR_NEED_EVDO) { - if (network.activation_state() == + if (network->error() == chromeos::ERROR_NEED_EVDO) { + if (network->activation_state() == chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) error_code = kErrorNoEVDO; - } else if (network.error() == chromeos::ERROR_NEED_HOME_NETWORK) { - if (network.activation_state() == + } else if (network->error() == chromeos::ERROR_NEED_HOME_NETWORK) { + if (network->activation_state() == chromeos::ACTIVATION_STATE_NOT_ACTIVATED) { error_code = kErrorRoamingActivation; - } else if (network.activation_state() == + } else if (network->activation_state() == chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) { error_code = kErrorRoamingPartiallyActivated; } @@ -422,29 +789,16 @@ bool MobileSetupHandler::CheckForActivationError( if (got_error) *error = GetErrorMessage(error_code); - return !got_error; + return got_error; } -bool MobileSetupHandler::GetDeviceInfo(DictionaryValue* value) { - DCHECK(value); - chromeos::NetworkLibrary* network_lib = - chromeos::CrosLibrary::Get()->GetNetworkLibrary(); - - const chromeos::CellularNetworkVector& cell_networks = - network_lib->cellular_networks(); - if (!cell_networks.size()) { - return false; - } - - const chromeos::CellularNetwork& network = *(cell_networks.begin()); - value->SetString("carrier", UTF8ToUTF16(network.name())); - value->SetString("payment_url", UTF8ToUTF16(network.payment_url())); - value->SetString("MEID", UTF8ToUTF16(network.meid())); - value->SetString("IMEI", UTF8ToUTF16(network.imei())); - value->SetString("IMSI", UTF8ToUTF16(network.imsi())); - value->SetString("ESN", UTF8ToUTF16(network.esn())); - value->SetString("MDN", UTF8ToUTF16(network.mdn())); - return true; +void MobileSetupHandler::GetDeviceInfo(const chromeos::CellularNetwork* network, + DictionaryValue* value) { + value->SetString("carrier", network->name()); + value->SetString("payment_url", network->payment_url()); + value->SetString("MEID", network->meid()); + value->SetString("IMEI", network->imei()); + value->SetString("MDN", network->mdn()); } std::string MobileSetupHandler::GetErrorMessage(const std::string& code) { @@ -464,7 +818,7 @@ void MobileSetupHandler::LoadCellularConfig() { scoped_ptr<CellularConfigDocument> config(new CellularConfigDocument()); bool config_loaded = config->LoadFromFile(config_path); if (config_loaded) { - VLOG(1) << "Cellular config file loaded: " << kCellularConfigPath; + LOG(INFO) << "Cellular config file loaded: " << kCellularConfigPath; // lock cellular_config_.reset(config.release()); } else { @@ -481,11 +835,14 @@ void MobileSetupHandler::LoadCellularConfig() { // //////////////////////////////////////////////////////////////////////////////// -MobileSetupUI::MobileSetupUI(TabContents* contents) : DOMUI(contents){ - MobileSetupHandler* handler = new MobileSetupHandler(); +MobileSetupUI::MobileSetupUI(TabContents* contents) : DOMUI(contents) { + const chromeos::CellularNetwork* network = GetCellularNetwork(); + std::string service_path = network ? network->service_path() : std::string(); + MobileSetupHandler* handler = new MobileSetupHandler(service_path); AddMessageHandler((handler)->Attach(this)); handler->Init(contents); - MobileSetupUIHTMLSource* html_source = new MobileSetupUIHTMLSource(); + MobileSetupUIHTMLSource* html_source = + new MobileSetupUIHTMLSource(service_path); // Set up the chrome://mobilesetup/ source. BrowserThread::PostTask( diff --git a/chrome/browser/chromeos/dom_ui/network_menu_ui.cc b/chrome/browser/chromeos/dom_ui/network_menu_ui.cc index bd366c3..30df58f 100644 --- a/chrome/browser/chromeos/dom_ui/network_menu_ui.cc +++ b/chrome/browser/chromeos/dom_ui/network_menu_ui.cc @@ -8,7 +8,7 @@ #include "base/values.h" #include "base/string_number_conversions.h" #include "base/string_util.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/chromeos/status/network_menu.h" #include "chrome/browser/chromeos/views/domui_menu_widget.h" @@ -23,6 +23,22 @@ namespace { +class NetworkMenuSourceDelegate : public chromeos::MenuSourceDelegate { + public: + virtual void AddLocalizedStrings(DictionaryValue* localized_strings) const { + DCHECK(localized_strings); + + localized_strings->SetString("reconnect", l10n_util::GetStringUTF16( + IDS_NETWORK_RECONNECT_TITLE)); + localized_strings->SetString("remeber_this_network", + l10n_util::GetStringUTF16(IDS_NETWORK_REMEMBER_THIS_NETWORK_TITLE)); + localized_strings->SetString("ssid_prompt", + l10n_util::GetStringUTF16(IDS_NETWORK_SSID_HINT)); + localized_strings->SetString("pass_prompt", + l10n_util::GetStringUTF16(IDS_NETWORK_PASSWORD_HINT)); + } +}; + //////////////////////////////////////////////////////////////////////////////// // // MenuHandler @@ -91,7 +107,7 @@ NetworkMenuUI::NetworkMenuUI(TabContents* contents) : chromeos::MenuUI( contents, ALLOW_THIS_IN_INITIALIZER_LIST( - CreateMenuUIHTMLSource(*this, + CreateMenuUIHTMLSource(new NetworkMenuSourceDelegate(), chrome::kChromeUINetworkMenu, "NetworkMenu", IDR_NETWORK_MENU_JS, @@ -109,23 +125,6 @@ NetworkMenuUI::NetworkMenuUI(TabContents* contents) make_scoped_refptr(theme))); } -void NetworkMenuUI::AddCustomConfigValues(DictionaryValue* config) const { -} - -void NetworkMenuUI::AddLocalizedStrings( - DictionaryValue* localized_strings) const { - DCHECK(localized_strings); - - localized_strings->SetString("reconnect", l10n_util::GetStringUTF16( - IDS_NETWORK_RECONNECT_TITLE)); - localized_strings->SetString("remeber_this_network", - l10n_util::GetStringUTF16(IDS_NETWORK_REMEMBER_THIS_NETWORK_TITLE)); - localized_strings->SetString("ssid_prompt", - l10n_util::GetStringUTF16(IDS_NETWORK_SSID_HINT)); - localized_strings->SetString("pass_prompt", - l10n_util::GetStringUTF16(IDS_NETWORK_PASSWORD_HINT)); -} - bool NetworkMenuUI::ModelAction(const menus::MenuModel* model, const ListValue* values) { const NetworkMenu* network_menu = static_cast<const NetworkMenu*>(model); @@ -186,6 +185,7 @@ DictionaryValue* NetworkMenuUI::CreateMenuItem(const menus::MenuModel* model, item->SetString("status", info.status); item->SetString("message", info.message); item->SetString("ip_address", info.ip_address); + item->SetString("passphrase", info.passphrase); item->SetBoolean("need_passphrase", info.need_passphrase); item->SetBoolean("remembered", info.remembered); return item; diff --git a/chrome/browser/chromeos/dom_ui/network_menu_ui.h b/chrome/browser/chromeos/dom_ui/network_menu_ui.h index 67027c8..70a3a4d 100644 --- a/chrome/browser/chromeos/dom_ui/network_menu_ui.h +++ b/chrome/browser/chromeos/dom_ui/network_menu_ui.h @@ -35,8 +35,6 @@ class NetworkMenuUI : public MenuUI { const char* type, int* max_icon_width, bool* has_accel) const; - virtual void AddCustomConfigValues(DictionaryValue* config) const; - virtual void AddLocalizedStrings(DictionaryValue* localized_strings) const; // A convenient factory method to create Menu2 for the network menu. static views::Menu2* CreateMenu2(menus::MenuModel* model); diff --git a/chrome/browser/chromeos/dom_ui/system_info_ui.cc b/chrome/browser/chromeos/dom_ui/system_info_ui.cc index d795122..d9a70da 100644 --- a/chrome/browser/chromeos/dom_ui/system_info_ui.cc +++ b/chrome/browser/chromeos/dom_ui/system_info_ui.cc @@ -94,7 +94,7 @@ void SystemInfoUIHTMLSource::StartDataRequest(const std::string& path, chromeos::CrosLibrary::Get()->GetSyslogsLibrary(); if (syslogs_lib) { syslogs_lib->RequestSyslogs( - false, + false, false, &consumer_, NewCallback(this, &SystemInfoUIHTMLSource::SyslogsComplete)); } diff --git a/chrome/browser/chromeos/dom_ui/system_settings_provider.cc b/chrome/browser/chromeos/dom_ui/system_settings_provider.cc index cd17c0d..a376cf0 100644 --- a/chrome/browser/chromeos/dom_ui/system_settings_provider.cc +++ b/chrome/browser/chromeos/dom_ui/system_settings_provider.cc @@ -67,7 +67,7 @@ SystemSettingsProvider::~SystemSettingsProvider() { STLDeleteElements(&timezones_); } -void SystemSettingsProvider::Set(const std::string& path, Value* in_value) { +void SystemSettingsProvider::DoSet(const std::string& path, Value* in_value) { if (path == kSystemTimezone) { string16 value; if (!in_value || !in_value->IsType(Value::TYPE_STRING) || diff --git a/chrome/browser/chromeos/dom_ui/system_settings_provider.h b/chrome/browser/chromeos/dom_ui/system_settings_provider.h index df97c46..816ea2c 100644 --- a/chrome/browser/chromeos/dom_ui/system_settings_provider.h +++ b/chrome/browser/chromeos/dom_ui/system_settings_provider.h @@ -24,7 +24,6 @@ class SystemSettingsProvider : public CrosSettingsProvider, virtual ~SystemSettingsProvider(); // CrosSettingsProvider overrides. - virtual void Set(const std::string& path, Value* in_value); virtual bool Get(const std::string& path, Value** out_value) const; virtual bool HandlesSetting(const std::string& path); @@ -35,6 +34,8 @@ class SystemSettingsProvider : public CrosSettingsProvider, ListValue* GetTimezoneList(); private: + // CrosSettingsProvider overrides. + virtual void DoSet(const std::string& path, Value* in_value); // Gets timezone name. static string16 GetTimezoneName(const icu::TimeZone& timezone); diff --git a/chrome/browser/chromeos/dom_ui/wrench_menu_ui.cc b/chrome/browser/chromeos/dom_ui/wrench_menu_ui.cc index d5f7a6c..cbc9c82 100644 --- a/chrome/browser/chromeos/dom_ui/wrench_menu_ui.cc +++ b/chrome/browser/chromeos/dom_ui/wrench_menu_ui.cc @@ -10,7 +10,7 @@ #include "base/utf_string_conversions.h" #include "base/values.h" #include "base/weak_ptr.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/chromeos/views/domui_menu_widget.h" @@ -24,6 +24,30 @@ #include "grit/generated_resources.h" #include "views/controls/menu/menu_2.h" +namespace { + +class WrenchMenuSourceDelegate : public chromeos::MenuSourceDelegate { + public: + virtual void AddCustomConfigValues(DictionaryValue* config) const { + // Resources that are necessary to build wrench menu. + config->SetInteger("IDC_CUT", IDC_CUT); + config->SetInteger("IDC_COPY", IDC_COPY); + config->SetInteger("IDC_PASTE", IDC_PASTE); + config->SetInteger("IDC_ZOOM_MINUS", IDC_ZOOM_MINUS); + config->SetInteger("IDC_ZOOM_PLUS", IDC_ZOOM_PLUS); + config->SetInteger("IDC_FULLSCREEN", IDC_FULLSCREEN); + + config->SetString("IDS_EDIT2", WideToUTF8(l10n_util::GetString(IDS_EDIT2))); + config->SetString("IDS_ZOOM_MENU2", + WideToUTF8(l10n_util::GetString(IDS_ZOOM_MENU2))); + config->SetString("IDS_CUT", WideToUTF8(l10n_util::GetString(IDS_CUT))); + config->SetString("IDS_COPY", WideToUTF8(l10n_util::GetString(IDS_COPY))); + config->SetString("IDS_PASTE", WideToUTF8(l10n_util::GetString(IDS_PASTE))); + } +}; + +} // namespace + namespace chromeos { //////////////////////////////////////////////////////////////////////////////// @@ -36,7 +60,7 @@ WrenchMenuUI::WrenchMenuUI(TabContents* contents) : chromeos::MenuUI( contents, ALLOW_THIS_IN_INITIALIZER_LIST( - CreateMenuUIHTMLSource(*this, + CreateMenuUIHTMLSource(new WrenchMenuSourceDelegate(), chrome::kChromeUIWrenchMenu, "WrenchMenu" /* class name */, IDR_WRENCH_MENU_JS, @@ -50,23 +74,6 @@ void WrenchMenuUI::ModelUpdated(const menus::MenuModel* new_model) { UpdateZoomControls(); } -void WrenchMenuUI::AddCustomConfigValues(DictionaryValue* config) const { - // Resources that are necessary to build wrench menu. - config->SetInteger("IDC_CUT", IDC_CUT); - config->SetInteger("IDC_COPY", IDC_COPY); - config->SetInteger("IDC_PASTE", IDC_PASTE); - config->SetInteger("IDC_ZOOM_MINUS", IDC_ZOOM_MINUS); - config->SetInteger("IDC_ZOOM_PLUS", IDC_ZOOM_PLUS); - config->SetInteger("IDC_FULLSCREEN", IDC_FULLSCREEN); - - config->SetString("IDS_EDIT2", WideToUTF8(l10n_util::GetString(IDS_EDIT2))); - config->SetString("IDS_ZOOM_MENU2", - WideToUTF8(l10n_util::GetString(IDS_ZOOM_MENU2))); - config->SetString("IDS_CUT", WideToUTF8(l10n_util::GetString(IDS_CUT))); - config->SetString("IDS_COPY", WideToUTF8(l10n_util::GetString(IDS_COPY))); - config->SetString("IDS_PASTE", WideToUTF8(l10n_util::GetString(IDS_PASTE))); -} - void WrenchMenuUI::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { diff --git a/chrome/browser/chromeos/dom_ui/wrench_menu_ui.h b/chrome/browser/chromeos/dom_ui/wrench_menu_ui.h index cb0cd80..c12528c 100644 --- a/chrome/browser/chromeos/dom_ui/wrench_menu_ui.h +++ b/chrome/browser/chromeos/dom_ui/wrench_menu_ui.h @@ -31,7 +31,6 @@ class WrenchMenuUI : public MenuUI, // MenuUI overrides: virtual void ModelUpdated(const menus::MenuModel* new_model); - virtual void AddCustomConfigValues(DictionaryValue* config) const; // NotificationObserver: virtual void Observe(NotificationType type, diff --git a/chrome/browser/chromeos/frame/browser_view.cc b/chrome/browser/chromeos/frame/browser_view.cc index 0791237..a9d95ad 100644 --- a/chrome/browser/chromeos/frame/browser_view.cc +++ b/chrome/browser/chromeos/frame/browser_view.cc @@ -10,7 +10,7 @@ #include "app/menus/simple_menu_model.h" #include "base/command_line.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/chromeos/frame/panel_browser_view.h" #include "chrome/browser/chromeos/status/input_method_menu_button.h" #include "chrome/browser/chromeos/status/network_menu_button.h" @@ -318,7 +318,7 @@ void BrowserView::ExecuteBrowserCommand(int id) const { browser()->ExecuteCommand(id); } -void BrowserView::OpenButtonOptions(const views::View* button_view) const { +void BrowserView::OpenButtonOptions(const views::View* button_view) { if (button_view == status_area_->network_view()) { browser()->OpenInternetOptionsDialog(); } else if (button_view == status_area_->input_method_view()) { diff --git a/chrome/browser/chromeos/frame/browser_view.h b/chrome/browser/chromeos/frame/browser_view.h index dc88ec2..c04758b 100644 --- a/chrome/browser/chromeos/frame/browser_view.h +++ b/chrome/browser/chromeos/frame/browser_view.h @@ -64,7 +64,7 @@ class BrowserView : public ::BrowserView, virtual bool ShouldOpenButtonOptions( const views::View* button_view) const; virtual void ExecuteBrowserCommand(int id) const; - virtual void OpenButtonOptions(const views::View* button_view) const; + virtual void OpenButtonOptions(const views::View* button_view); virtual bool IsBrowserMode() const; virtual bool IsScreenLockerMode() const; diff --git a/chrome/browser/chromeos/frame/bubble_frame_view.cc b/chrome/browser/chromeos/frame/bubble_frame_view.cc index 5f0207c..becfb78 100644 --- a/chrome/browser/chromeos/frame/bubble_frame_view.cc +++ b/chrome/browser/chromeos/frame/bubble_frame_view.cc @@ -4,6 +4,7 @@ #include "chrome/browser/chromeos/frame/bubble_frame_view.h" +#include "app/resource_bundle.h" #include "gfx/canvas_skia.h" #include "gfx/font.h" #include "gfx/insets.h" @@ -11,6 +12,8 @@ #include "gfx/rect.h" #include "chrome/browser/chromeos/frame/bubble_window.h" #include "chrome/browser/views/bubble_border.h" +#include "grit/theme_resources.h" +#include "views/controls/button/image_button.h" #include "views/controls/label.h" #include "views/window/hit_test.h" #include "views/window/window.h" @@ -27,15 +30,32 @@ static const int kHorizontalPadding = 10; namespace chromeos { -BubbleFrameView::BubbleFrameView(views::Window* frame) +BubbleFrameView::BubbleFrameView(views::Window* frame, + BubbleWindow::Style style) : frame_(frame), - title_(NULL) { + style_(style), + title_(NULL), + close_button_(NULL) { set_border(new BubbleBorder(BubbleBorder::NONE)); - title_ = new views::Label(frame_->GetDelegate()->GetWindowTitle()); - title_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); - title_->SetFont(title_->font().DeriveFont(1, gfx::Font::BOLD)); - AddChildView(title_); + if (frame_->GetDelegate()->ShouldShowWindowTitle()) { + title_ = new views::Label(frame_->GetDelegate()->GetWindowTitle()); + title_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + title_->SetFont(title_->font().DeriveFont(1, gfx::Font::BOLD)); + AddChildView(title_); + } + + if (style_ & BubbleWindow::STYLE_XBAR) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + close_button_ = new views::ImageButton(this); + close_button_->SetImage(views::CustomButton::BS_NORMAL, + rb.GetBitmapNamed(IDR_CLOSE_BAR)); + close_button_->SetImage(views::CustomButton::BS_HOT, + rb.GetBitmapNamed(IDR_CLOSE_BAR_H)); + close_button_->SetImage(views::CustomButton::BS_PUSHED, + rb.GetBitmapNamed(IDR_CLOSE_BAR_P)); + AddChildView(close_button_); + } } BubbleFrameView::~BubbleFrameView() { @@ -48,8 +68,20 @@ gfx::Rect BubbleFrameView::GetBoundsForClientView() const { gfx::Rect BubbleFrameView::GetWindowBoundsForClientBounds( const gfx::Rect& client_bounds) const { gfx::Insets insets = GetInsets(); - gfx::Size title_size = title_->GetPreferredSize(); - int top_height = insets.top() + title_size.height() + kTitleContentPadding; + gfx::Size title_size; + if (title_) { + title_size = title_->GetPreferredSize(); + } + + gfx::Size close_button_size = gfx::Size(); + if (close_button_) { + close_button_size = close_button_->GetPreferredSize(); + } + + int top_height = insets.top(); + if (title_size.height() > 0 || close_button_size.height() > 0) + top_height += std::max(title_size.height(), close_button_size.height()) + + kTitleContentPadding; return gfx::Rect(std::max(0, client_bounds.x() - insets.left()), std::max(0, client_bounds.y() - top_height), client_bounds.width() + insets.width(), @@ -92,12 +124,33 @@ gfx::Size BubbleFrameView::GetPreferredSize() { void BubbleFrameView::Layout() { gfx::Insets insets = GetInsets(); - gfx::Size title_size = title_->GetPreferredSize(); - title_->SetBounds(insets.left(), insets.top(), - std::max(0, width() - insets.width()), - title_size.height()); - - int top_height = insets.top() + title_size.height() + kTitleContentPadding; + gfx::Size title_size; + if (title_) { + title_size = title_->GetPreferredSize(); + } + + gfx::Size close_button_size = gfx::Size(); + if (close_button_) { + close_button_size = close_button_->GetPreferredSize(); + } + + if (title_) { + title_->SetBounds( + insets.left(), insets.top(), + std::max(0, width() - insets.width() - close_button_size.width()), + title_size.height()); + } + + if (close_button_) { + close_button_->SetBounds( + width() - insets.right() - close_button_size.width(), insets.top(), + close_button_size.width(), close_button_size.height()); + } + + int top_height = insets.top(); + if (title_size.height() > 0 || close_button_size.height() > 0) + top_height += std::max(title_size.height(), close_button_size.height()) + + kTitleContentPadding; client_view_bounds_.SetRect(insets.left(), top_height, std::max(0, width() - insets.width()), std::max(0, height() - top_height - insets.bottom())); @@ -122,4 +175,11 @@ void BubbleFrameView::Paint(gfx::Canvas* canvas) { PaintBorder(canvas); } +void BubbleFrameView::ButtonPressed(views::Button* sender, + const views::Event& event) { + if (close_button_ != NULL && sender == close_button_) { + frame_->Close(); + } +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/frame/bubble_frame_view.h b/chrome/browser/chromeos/frame/bubble_frame_view.h index 18505f7..7ebb29e 100644 --- a/chrome/browser/chromeos/frame/bubble_frame_view.h +++ b/chrome/browser/chromeos/frame/bubble_frame_view.h @@ -6,6 +6,8 @@ #define CHROME_BROWSER_CHROMEOS_FRAME_BUBBLE_FRAME_VIEW_H_ #pragma once +#include "chrome/browser/chromeos/frame/bubble_window.h" +#include "views/controls/button/button.h" #include "views/window/non_client_view.h" namespace gfx { @@ -17,6 +19,7 @@ class Size; } namespace views { +class ImageButton; class Label; class Window; } @@ -24,9 +27,10 @@ class Window; namespace chromeos { // BubbleFrameView implements a BubbleBorder based window frame. -class BubbleFrameView : public views::NonClientFrameView { +class BubbleFrameView : public views::NonClientFrameView, + public views::ButtonListener { public: - explicit BubbleFrameView(views::Window* frame); + BubbleFrameView(views::Window* frame, BubbleWindow::Style style); virtual ~BubbleFrameView(); // Overridden from views::NonClientFrameView: @@ -44,16 +48,26 @@ class BubbleFrameView : public views::NonClientFrameView { virtual void Layout(); virtual void Paint(gfx::Canvas* canvas); + // Overridden from views::ButtonListener: + virtual void ButtonPressed(views::Button* sender, + const views::Event& event); + private: // The window that owns this view. views::Window* frame_; + // Allows to tweak appearance of the view. + BubbleWindow::Style style_; + // Title label views::Label* title_; // The bounds of the client view, in this view's coordinates. gfx::Rect client_view_bounds_; + // Close button for STYLE_XBAR case. + views::ImageButton* close_button_; + DISALLOW_COPY_AND_ASSIGN(BubbleFrameView); }; diff --git a/chrome/browser/chromeos/frame/bubble_window.cc b/chrome/browser/chromeos/frame/bubble_window.cc index 79a0c57..25db342 100644 --- a/chrome/browser/chromeos/frame/bubble_window.cc +++ b/chrome/browser/chromeos/frame/bubble_window.cc @@ -32,16 +32,12 @@ void BubbleWindow::Init(GtkWindow* parent, const gfx::Rect& bounds) { views::Window* BubbleWindow::Create( gfx::NativeWindow parent, const gfx::Rect& bounds, + Style style, views::WindowDelegate* window_delegate) { BubbleWindow* window = new BubbleWindow(window_delegate); - window->GetNonClientView()->SetFrameView(new BubbleFrameView(window)); + window->GetNonClientView()->SetFrameView(new BubbleFrameView(window, style)); window->Init(parent, bounds); - chromeos::WmIpc::instance()->SetWindowType( - window->GetNativeView(), - chromeos::WM_IPC_WINDOW_CHROME_INFO_BUBBLE, - NULL); - return window; } diff --git a/chrome/browser/chromeos/frame/bubble_window.h b/chrome/browser/chromeos/frame/bubble_window.h index a6afbd7..fca4805 100644 --- a/chrome/browser/chromeos/frame/bubble_window.h +++ b/chrome/browser/chromeos/frame/bubble_window.h @@ -22,8 +22,14 @@ namespace chromeos { // A window that uses BubbleFrameView as its frame. class BubbleWindow : public views::WindowGtk { public: + enum Style { + STYLE_GENERIC = 0, // Default style. + STYLE_XBAR = 1 << 0 // Show close button at the top right (left for RTL). + }; + static views::Window* Create(gfx::NativeWindow parent, const gfx::Rect& bounds, + Style style, views::WindowDelegate* window_delegate); static const SkColor kBackgroundColor; @@ -33,6 +39,8 @@ class BubbleWindow : public views::WindowGtk { // Overidden from views::WindowGtk: virtual void Init(GtkWindow* parent, const gfx::Rect& bounds); + + Style style_; }; } // namespace chromeos diff --git a/chrome/browser/chromeos/frame/panel_browser_view.cc b/chrome/browser/chromeos/frame/panel_browser_view.cc index e91d51f..a7f075a 100644 --- a/chrome/browser/chromeos/frame/panel_browser_view.cc +++ b/chrome/browser/chromeos/frame/panel_browser_view.cc @@ -55,24 +55,6 @@ void PanelBrowserView::LimitBounds(gfx::Rect* bounds) const { //////////////////////////////////////////////////////////////////////////////// // BrowserView overrides. -void PanelBrowserView::Init() { - ::BrowserView::Init(); - // The visibility of toolbar is controlled in - // the BrowserView::IsToolbarVisible method. - - views::Window* window = frame()->GetWindow(); - gfx::NativeWindow native_window = window->GetNativeWindow(); - // The window manager needs the min size for popups. - gfx::Rect bounds = window->GetBounds(); - LimitBounds(&bounds); - gtk_widget_set_size_request( - GTK_WIDGET(native_window), bounds.width(), bounds.height()); - // If we don't explicitly resize here there is a race condition between - // the X Server and the window manager. Windows will appear with a default - // size of 200x200 if this happens. - gtk_window_resize(native_window, bounds.width(), bounds.height()); -} - void PanelBrowserView::Show() { if (panel_controller_.get() == NULL) { panel_controller_.reset(new PanelController(this, GetNativeHandle())); diff --git a/chrome/browser/chromeos/frame/panel_browser_view.h b/chrome/browser/chromeos/frame/panel_browser_view.h index fdd9c95..a3f3245 100644 --- a/chrome/browser/chromeos/frame/panel_browser_view.h +++ b/chrome/browser/chromeos/frame/panel_browser_view.h @@ -26,7 +26,6 @@ class PanelBrowserView : public ::BrowserView, explicit PanelBrowserView(Browser* browser); // BrowserView overrides. - virtual void Init(); virtual void Show(); virtual void SetBounds(const gfx::Rect& bounds); virtual void Close(); diff --git a/chrome/browser/chromeos/frame/panel_controller.cc b/chrome/browser/chromeos/frame/panel_controller.cc index 2f3f7eb..bfdc915 100644 --- a/chrome/browser/chromeos/frame/panel_controller.cc +++ b/chrome/browser/chromeos/frame/panel_controller.cc @@ -315,7 +315,7 @@ void PanelController::OnCloseButtonPressed() { PanelController::TitleContentView::TitleContentView( PanelController* panel_controller) : panel_controller_(panel_controller) { - LOG(INFO) << "panel: c " << this; + VLOG(1) << "panel: c " << this; InitializeResources(); close_button_ = new views::ImageButton(this); close_button_->SetImage(views::CustomButton::BS_NORMAL, close_button_n); @@ -401,7 +401,7 @@ void PanelController::TitleContentView::ButtonPressed( } PanelController::TitleContentView::~TitleContentView() { - LOG(INFO) << "panel: delete " << this; + VLOG(1) << "panel: delete " << this; } } // namespace chromeos diff --git a/chrome/browser/chromeos/input_method/input_method_util.cc b/chrome/browser/chromeos/input_method/input_method_util.cc index 0df1b45..abf4e32 100644 --- a/chrome/browser/chromeos/input_method/input_method_util.cc +++ b/chrome/browser/chromeos/input_method/input_method_util.cc @@ -372,7 +372,7 @@ std::string NormalizeLanguageCode( copied_language_code[2] = '-'; // Downcase the language code part. for (size_t i = 0; i < 2; ++i) { - copied_language_code[i] = ToLowerASCII(copied_language_code[i]); + copied_language_code[i] = base::ToLowerASCII(copied_language_code[i]); } // Upcase the country code part. for (size_t i = 3; i < copied_language_code.size(); ++i) { diff --git a/chrome/browser/chromeos/login/account_screen_browsertest.cc b/chrome/browser/chromeos/login/account_screen_browsertest.cc index 761bc05..0954013 100644 --- a/chrome/browser/chromeos/login/account_screen_browsertest.cc +++ b/chrome/browser/chromeos/login/account_screen_browsertest.cc @@ -38,10 +38,19 @@ class AccountScreenTest : public WizardInProcessBrowserTest { DISALLOW_COPY_AND_ASSIGN(AccountScreenTest); }; +#if defined(OFFICIAL_BUILD) +// Flaky on official build on autotest. Since this is not +// used in Official build, i'm disabling it. +// See crosbug.com/8517 . +#define MAYBE_TestBasic DISABLED_TestBasic +#else +#define MAYBE_TestBasic TestBasic +#endif + // A basic test. It does not care how things evolve after the URL is // loaded. Thus no message loop is started. Just check that initial // status is expected. -IN_PROC_BROWSER_TEST_F(AccountScreenTest, TestBasic) { +IN_PROC_BROWSER_TEST_F(AccountScreenTest, MAYBE_TestBasic) { ASSERT_TRUE(controller()); EXPECT_EQ(controller()->GetAccountScreen(), controller()->current_screen()); } @@ -68,8 +77,8 @@ static URLRequestJob* InspectorHook(URLRequest* request, return new URLRequestAboutJob(request); } -// Flaky per http://crbug.com/60050. -IN_PROC_BROWSER_TEST_F(AccountScreenTest, FLAKY_TestSchemeInspector) { +// Sometimes times out: http://crbug.com/60050. +IN_PROC_BROWSER_TEST_F(AccountScreenTest, DISABLED_TestSchemeInspector) { ChildProcessSecurityPolicy::GetInstance()->RegisterWebSafeScheme( chrome::kCrosScheme); URLRequestFilter::GetInstance()->AddHostnameHandler(chrome::kCrosScheme, diff --git a/chrome/browser/chromeos/login/background_view.cc b/chrome/browser/chromeos/login/background_view.cc index 79d1a9c..bbaaaf1 100644 --- a/chrome/browser/chromeos/login/background_view.cc +++ b/chrome/browser/chromeos/login/background_view.cc @@ -15,7 +15,9 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/oobe_progress_bar.h" +#include "chrome/browser/chromeos/login/proxy_settings_dialog.h" #include "chrome/browser/chromeos/login/rounded_rect_painter.h" +#include "chrome/browser/chromeos/login/shutdown_button.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/status/clock_menu_button.h" #include "chrome/browser/chromeos/status/feedback_menu_button.h" @@ -94,9 +96,8 @@ BackgroundView::BackgroundView() os_version_label_(NULL), boot_times_label_(NULL), progress_bar_(NULL), - go_incognito_button_(NULL), + shutdown_button_(NULL), did_paint_(false), - delegate_(NULL), #if defined(OFFICIAL_BUILD) is_official_build_(true), #else @@ -120,6 +121,13 @@ void BackgroundView::Init(const GURL& background_url) { } } +void BackgroundView::EnableShutdownButton() { + DCHECK(!shutdown_button_); + shutdown_button_ = new ShutdownButton(); + shutdown_button_->Init(); + AddChildView(shutdown_button_); +} + // static views::Widget* BackgroundView::CreateWindowContainingView( const gfx::Rect& bounds, @@ -168,21 +176,6 @@ void BackgroundView::SetOobeProgress(LoginStep step) { progress_bar_->SetProgress(step); } -// Toggles GoIncognito button visibility. -void BackgroundView::SetGoIncognitoButtonVisible(bool visible, - Delegate *delegate) { - // Set delegate to handle button pressing. - delegate_ = delegate; - bool currently_visible = - go_incognito_button_ && go_incognito_button_->IsVisible(); - if (currently_visible != visible) { - if (!go_incognito_button_) { - InitGoIncognitoButton(); - } - go_incognito_button_->SetVisible(visible); - } -} - void BackgroundView::ShowScreenSaver() { SetStatusAreaVisible(false); background_area_->SetVisible(true); @@ -203,17 +196,6 @@ bool BackgroundView::ScreenSaverEnabled() { return background_area_ != NULL; } -void BackgroundView::OnOwnerChanged() { - delegate_ = NULL; - if (go_incognito_button_) { - // BackgroundView is passed among multiple controllers, so they should - // explicitly enable "Go incognito" button if needed. - RemoveChildView(go_incognito_button_); - delete go_incognito_button_; - go_incognito_button_ = NULL; - } -} - /////////////////////////////////////////////////////////////////////////////// // BackgroundView protected: @@ -233,8 +215,6 @@ void BackgroundView::Layout() { const int kProgressBarBottomPadding = 20; const int kProgressBarWidth = 750; const int kProgressBarHeight = 70; - const int kGoIncognitoButtonBottomPadding = 12; - const int kGoIncognitoButtonRightPadding = 12; gfx::Size status_area_size = status_area_->GetPreferredSize(); status_area_->SetBounds( width() - status_area_size.width() - kCornerPadding, @@ -264,13 +244,8 @@ void BackgroundView::Layout() { kProgressBarWidth, kProgressBarHeight); } - if (go_incognito_button_) { - gfx::Size go_button_size = go_incognito_button_->GetPreferredSize(); - go_incognito_button_->SetBounds( - width() - go_button_size.width()- kGoIncognitoButtonRightPadding, - height() - go_button_size.height() - kGoIncognitoButtonBottomPadding, - go_button_size.width(), - go_button_size.height()); + if (shutdown_button_) { + shutdown_button_->LayoutIn(this); } if (background_area_) background_area_->SetBounds(this->bounds()); @@ -281,12 +256,6 @@ void BackgroundView::ChildPreferredSizeChanged(View* child) { SchedulePaint(); } -void BackgroundView::OnLocaleChanged() { - UpdateLocalizedStrings(); - Layout(); - SchedulePaint(); -} - gfx::NativeWindow BackgroundView::GetNativeWindow() const { return GTK_WINDOW(static_cast<WidgetGtk*>(GetWidget())->GetNativeView()); @@ -294,17 +263,25 @@ gfx::NativeWindow BackgroundView::GetNativeWindow() const { bool BackgroundView::ShouldOpenButtonOptions( const views::View* button_view) const { + if (button_view == status_area_->network_view()) { + return true; + } if (button_view == status_area_->clock_view() || button_view == status_area_->feedback_view() || - button_view == status_area_->input_method_view() || - button_view == status_area_->network_view()) { + button_view == status_area_->input_method_view()) { return false; } return true; } -void BackgroundView::OpenButtonOptions(const views::View* button_view) const { - // TODO(avayvod): Add some dialog for options or remove them completely. +void BackgroundView::OpenButtonOptions(const views::View* button_view) { + if (button_view == status_area_->network_view()) { + if (proxy_settings_dialog_.get() == NULL) { + proxy_settings_dialog_.reset(new ProxySettingsDialog( + this, GetNativeWindow())); + } + proxy_settings_dialog_->Show(); + } } bool BackgroundView::IsBrowserMode() const { @@ -315,14 +292,10 @@ bool BackgroundView::IsScreenLockerMode() const { return false; } -void BackgroundView::ButtonPressed(views::Button* sender, - const views::Event& event) { - if (sender == go_incognito_button_) { - DCHECK(delegate_); - if (delegate_) { - delegate_->OnGoIncognitoButton(); - } - } +// Overridden from LoginHtmlDialog::Delegate: +void BackgroundView::OnLocaleChanged() { + // Proxy settings dialog contains localized strings. + proxy_settings_dialog_.reset(); } /////////////////////////////////////////////////////////////////////////////// @@ -385,51 +358,6 @@ void BackgroundView::InitProgressBar() { AddChildView(progress_bar_); } -void BackgroundView::InitGoIncognitoButton() { - SkColor kButtonColor = 0xFF4F6985; - SkColor kStrokeColor = 0xFF657A91; - int kStrokeWidth = 1; - int kVerticalPadding = 8; - int kHorizontalPadding = 12; - int kCornerRadius = 4; - - go_incognito_button_ = - new TextButtonWithHandCursorOver(this, std::wstring()); - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - go_incognito_button_->SetIcon(*rb.GetBitmapNamed(IDR_INCOGNITO_GUY)); - go_incognito_button_->SetFocusable(true); - // Set label colors. - go_incognito_button_->SetEnabledColor(SK_ColorWHITE); - go_incognito_button_->SetDisabledColor(SK_ColorWHITE); - go_incognito_button_->SetHighlightColor(SK_ColorWHITE); - go_incognito_button_->SetHoverColor(SK_ColorWHITE); - // Disable throbbing and make border always visible. - go_incognito_button_->SetAnimationDuration(0); - go_incognito_button_->SetNormalHasBorder(true); - // Setup round shapes. - go_incognito_button_->set_background( - CreateRoundedBackground( - kCornerRadius, kStrokeWidth, kButtonColor, kStrokeColor)); - - go_incognito_button_->set_border( - views::Border::CreateEmptyBorder(kVerticalPadding, - kHorizontalPadding, - kVerticalPadding, - kHorizontalPadding)); - // Set button text. - UpdateLocalizedStrings(); - // Enable and add to the views hierarchy. - go_incognito_button_->SetEnabled(true); - AddChildView(go_incognito_button_); -} - -void BackgroundView::UpdateLocalizedStrings() { - if (go_incognito_button_) { - go_incognito_button_->SetText( - UTF8ToWide(l10n_util::GetStringUTF8(IDS_GO_INCOGNITO_BUTTON))); - } -} - void BackgroundView::UpdateWindowType() { std::vector<int> params; params.push_back(did_paint_ ? 1 : 0); diff --git a/chrome/browser/chromeos/login/background_view.h b/chrome/browser/chromeos/login/background_view.h index e64caf7..fc4ea91 100644 --- a/chrome/browser/chromeos/login/background_view.h +++ b/chrome/browser/chromeos/login/background_view.h @@ -8,15 +8,15 @@ #include "chrome/browser/chromeos/boot_times_loader.h" #include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/login/login_html_dialog.h" #include "chrome/browser/chromeos/status/status_area_host.h" #include "chrome/browser/chromeos/version_loader.h" -#include "views/controls/button/button.h" #include "views/view.h" namespace views { -class Widget; class Label; class TextButton; +class Widget; } class DOMView; @@ -26,23 +26,15 @@ class Profile; namespace chromeos { class OobeProgressBar; +class ShutdownButton; class StatusAreaView; // View used to render the background during login. BackgroundView contains // StatusAreaView. class BackgroundView : public views::View, public StatusAreaHost, - public views::ButtonListener { + public chromeos::LoginHtmlDialog::Delegate { public: - // Delegate class to handle notificatoin from the view. - class Delegate { - public: - virtual ~Delegate() {} - - // Initializes incognito login. - virtual void OnGoIncognitoButton() = 0; - }; - enum LoginStep { SELECT_NETWORK, #if defined(OFFICIAL_BUILD) @@ -61,6 +53,9 @@ class BackgroundView : public views::View, // it creates a DOMView background area that renders a webpage. void Init(const GURL& background_url); + // Enable shutdown button. + void EnableShutdownButton(); + // Creates a window containing an instance of WizardContentsView as the root // view. The caller is responsible for showing (and closing) the returned // widget. The BackgroundView is set in |view|. If background_url is non @@ -82,9 +77,6 @@ class BackgroundView : public views::View, // Sets current step on OOBE progress bar. void SetOobeProgress(LoginStep step); - // Toggles GoIncognito button visibility. - void SetGoIncognitoButtonVisible(bool visible, Delegate *delegate); - // Shows screen saver. void ShowScreenSaver(); @@ -97,9 +89,6 @@ class BackgroundView : public views::View, // Tells if screen saver is enabled. bool ScreenSaverEnabled(); - // Tells that owner has been changed. - void OnOwnerChanged(); - protected: // Overridden from views::View: virtual void Paint(gfx::Canvas* canvas); @@ -113,12 +102,12 @@ class BackgroundView : public views::View, virtual void ExecuteBrowserCommand(int id) const {} virtual bool ShouldOpenButtonOptions( const views::View* button_view) const; - virtual void OpenButtonOptions(const views::View* button_view) const; + virtual void OpenButtonOptions(const views::View* button_view); virtual bool IsBrowserMode() const; virtual bool IsScreenLockerMode() const; - // Overridden from views::ButtonListener. - virtual void ButtonPressed(views::Button* sender, const views::Event& event); + // Overridden from LoginHtmlDialog::Delegate: + virtual void OnDialogClosed() {} private: // Creates and adds the status_area. @@ -127,11 +116,6 @@ class BackgroundView : public views::View, void InitInfoLabels(); // Creates and add OOBE progress bar. void InitProgressBar(); - // Creates and add GoIncoginito button. - void InitGoIncognitoButton(); - - // Updates string from the resources. - void UpdateLocalizedStrings(); // Invokes SetWindowType for the window. This is invoked during startup and // after we've painted. @@ -148,7 +132,7 @@ class BackgroundView : public views::View, views::Label* os_version_label_; views::Label* boot_times_label_; OobeProgressBar* progress_bar_; - views::TextButton* go_incognito_button_; + ShutdownButton* shutdown_button_; // Handles asynchronously loading the version. VersionLoader version_loader_; @@ -165,16 +149,15 @@ class BackgroundView : public views::View, // TODO(sky): nuke this when the wm knows when chrome has painted. bool did_paint_; - // NOTE: |delegate_| is assigned to NULL when the owner is changed. See - // 'OnOwnerChanged()' for more info. - Delegate *delegate_; - // True if running official BUILD. bool is_official_build_; // DOMView for rendering a webpage as a background. DOMView* background_area_; + // Proxy settings dialog that can be invoked from network menu. + scoped_ptr<LoginHtmlDialog> proxy_settings_dialog_; + DISALLOW_COPY_AND_ASSIGN(BackgroundView); }; diff --git a/chrome/browser/chromeos/login/camera.cc b/chrome/browser/chromeos/login/camera.cc index 6de6075..59e77d3 100644 --- a/chrome/browser/chromeos/login/camera.cc +++ b/chrome/browser/chromeos/login/camera.cc @@ -22,6 +22,7 @@ #include "base/logging.h" #include "base/string_util.h" #include "base/stringprintf.h" +#include "base/thread.h" #include "base/time.h" #include "chrome/browser/browser_thread.h" #include "gfx/size.h" @@ -127,8 +128,9 @@ const long kSelectTimeout = 1 * base::Time::kMicrosecondsPerSecond; /////////////////////////////////////////////////////////////////////////////// // Camera, public members: -Camera::Camera(Delegate* delegate, bool mirrored) +Camera::Camera(Delegate* delegate, base::Thread* thread, bool mirrored) : delegate_(delegate), + thread_(thread), device_name_(kDeviceName), device_descriptor_(-1), is_capturing_(false), @@ -144,7 +146,7 @@ Camera::~Camera() { } void Camera::ReportFailure() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(IsOnCameraThread()); if (device_descriptor_ == -1) { BrowserThread::PostTask( BrowserThread::UI, @@ -168,8 +170,7 @@ void Camera::ReportFailure() { void Camera::Initialize(int desired_width, int desired_height) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - BrowserThread::PostTask( - BrowserThread::IO, + PostCameraTask( FROM_HERE, NewRunnableMethod(this, &Camera::DoInitialize, @@ -178,8 +179,7 @@ void Camera::Initialize(int desired_width, int desired_height) { } void Camera::DoInitialize(int desired_width, int desired_height) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(delegate_); + DCHECK(IsOnCameraThread()); if (device_descriptor_ != -1) { LOG(WARNING) << "Camera is initialized already."; @@ -246,15 +246,11 @@ void Camera::DoInitialize(int desired_width, int desired_height) { void Camera::Uninitialize() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - NewRunnableMethod(this, - &Camera::DoUninitialize)); + PostCameraTask(FROM_HERE, NewRunnableMethod(this, &Camera::DoUninitialize)); } void Camera::DoUninitialize() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(IsOnCameraThread()); if (device_descriptor_ == -1) { LOG(WARNING) << "Calling uninitialize for uninitialized camera."; return; @@ -266,18 +262,14 @@ void Camera::DoUninitialize() { device_descriptor_ = -1; } -void Camera::StartCapturing(int64 rate) { +void Camera::StartCapturing() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - NewRunnableMethod(this, - &Camera::DoStartCapturing, - rate)); + PostCameraTask(FROM_HERE, + NewRunnableMethod(this, &Camera::DoStartCapturing)); } -void Camera::DoStartCapturing(int64 rate) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); +void Camera::DoStartCapturing() { + DCHECK(IsOnCameraThread()); if (is_capturing_) { LOG(WARNING) << "Capturing is already started."; return; @@ -300,31 +292,26 @@ void Camera::DoStartCapturing(int64 rate) { ReportFailure(); return; } + // No need to post DidProcessCameraThreadMethod() as this method is + // being posted instead. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod(this, &Camera::OnStartCapturingSuccess)); is_capturing_ = true; - capturing_rate_ = rate; - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - NewRunnableMethod(this, - &Camera::OnCapture)); + PostCameraTask(FROM_HERE, + NewRunnableMethod(this, &Camera::OnCapture)); } void Camera::StopCapturing() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - NewRunnableMethod(this, - &Camera::DoStopCapturing)); + PostCameraTask(FROM_HERE, + NewRunnableMethod(this, &Camera::DoStopCapturing)); } void Camera::DoStopCapturing() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(IsOnCameraThread()); if (!is_capturing_) { LOG(WARNING) << "Calling StopCapturing when capturing is not started."; return; @@ -336,11 +323,16 @@ void Camera::DoStopCapturing() { log_errno("VIDIOC_STREAMOFF failed."); } +void Camera::GetFrame(SkBitmap* frame) { + AutoLock lock(image_lock_); + frame->swap(frame_image_); +} + /////////////////////////////////////////////////////////////////////////////// // Camera, private members: int Camera::OpenDevice(const char* device_name) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(IsOnCameraThread()); struct stat st; if (stat(device_name, &st) == -1) { log_errno(base::StringPrintf("Cannot identify %s", device_name)); @@ -359,7 +351,7 @@ int Camera::OpenDevice(const char* device_name) const { } bool Camera::InitializeReadingMode(int fd) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(IsOnCameraThread()); v4l2_requestbuffers req; req.count = kRequestBuffersCount; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -404,7 +396,7 @@ bool Camera::InitializeReadingMode(int fd) { } void Camera::UnmapVideoBuffers() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(IsOnCameraThread()); for (size_t i = 0; i < buffers_.size(); ++i) { if (munmap(buffers_[i].start, buffers_[i].length) == -1) log_errno("munmap failed."); @@ -412,7 +404,7 @@ void Camera::UnmapVideoBuffers() { } void Camera::OnCapture() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(IsOnCameraThread()); if (!is_capturing_) return; @@ -441,16 +433,12 @@ void Camera::OnCapture() { // EAGAIN - continue select loop. } while (!ReadFrame()); - BrowserThread::PostDelayedTask( - BrowserThread::IO, - FROM_HERE, - NewRunnableMethod(this, - &Camera::OnCapture), - capturing_rate_); + PostCameraTask(FROM_HERE, + NewRunnableMethod(this, &Camera::OnCapture)); } bool Camera::ReadFrame() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(IsOnCameraThread()); v4l2_buffer buffer = {}; buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buffer.memory = V4L2_MEMORY_MMAP; @@ -475,7 +463,7 @@ bool Camera::ReadFrame() { } void Camera::ProcessImage(void* data) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(IsOnCameraThread()); // If desired resolution is higher than available, we crop the available // image to get the same aspect ratio and scale the result. int desired_width = desired_width_; @@ -538,12 +526,14 @@ void Camera::ProcessImage(void* data) { desired_height_); } image.setIsOpaque(true); + { + AutoLock lock(image_lock_); + frame_image_.swap(image); + } BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - NewRunnableMethod(this, - &Camera::OnCaptureSuccess, - image)); + NewRunnableMethod(this, &Camera::OnCaptureSuccess)); } void Camera::OnInitializeSuccess() { @@ -570,10 +560,10 @@ void Camera::OnStartCapturingFailure() { delegate_->OnStartCapturingFailure(); } -void Camera::OnCaptureSuccess(const SkBitmap& frame) { +void Camera::OnCaptureSuccess() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (delegate_) - delegate_->OnCaptureSuccess(frame); + delegate_->OnCaptureSuccess(); } void Camera::OnCaptureFailure() { @@ -582,4 +572,18 @@ void Camera::OnCaptureFailure() { delegate_->OnCaptureFailure(); } +bool Camera::IsOnCameraThread() const { + AutoLock lock(thread_lock_); + return thread_ && MessageLoop::current() == thread_->message_loop(); +} + +void Camera::PostCameraTask(const tracked_objects::Location& from_here, + Task* task) { + AutoLock lock(thread_lock_); + if (!thread_) + return; + DCHECK(thread_->IsRunning()); + thread_->message_loop()->PostTask(from_here, task); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/login/camera.h b/chrome/browser/chromeos/login/camera.h index 667d9d6..a82f197 100644 --- a/chrome/browser/chromeos/login/camera.h +++ b/chrome/browser/chromeos/login/camera.h @@ -9,21 +9,22 @@ #include <string> #include <vector> +#include "base/lock.h" #include "base/ref_counted.h" -#include "base/timer.h" - -class SkBitmap; +#include "base/thread.h" +#include "third_party/skia/include/core/SkBitmap.h" +class Task; namespace base { -class TimeDelta; +class Thread; } // namespace base namespace chromeos { // Class that wraps interaction with video capturing device. Returns // frames captured with specified intervals of time via delegate interface. -// All communication with camera driver is performed on IO thread. -// Delegate's callback are called on UI thread. +// All communication with camera driver is performed on a separate camera +// thread. Delegate's callback are called on UI thread. class Camera : public base::RefCountedThreadSafe<Camera> { public: class Delegate { @@ -39,31 +40,33 @@ class Camera : public base::RefCountedThreadSafe<Camera> { virtual void OnStartCapturingSuccess() = 0; virtual void OnStartCapturingFailure() = 0; - // Called if video frame was captured successfully. - virtual void OnCaptureSuccess(const SkBitmap& frame) = 0; - // Called if capturing the current frame failed. + // Notifies the delegate that new frame was captured. + // The frame can be obtained via GetFrame() method. + virtual void OnCaptureSuccess() = 0; + + // Notifies the delegate that we failed to capture the next frame. virtual void OnCaptureFailure() = 0; }; // Initializes object members. |delegate| is object that will receive - // notifications about success of async method calls. |mirrored| - // determines if the returned video image is mirrored horizontally. - Camera(Delegate* delegate, bool mirrored); - - // Initializes camera device on IO thread. Corresponding delegate's callback - // is called on UI thread to notify about success or failure. Does nothing if - // camera is successfully initialized already. Sets the desired width and - // height of the frame to receive from camera. + // notifications about success of async method calls. |thread| is a thread + // to post blocking tasks to. |mirrored| determines if the returned video + // image is mirrored horizontally. + Camera(Delegate* delegate, base::Thread* thread, bool mirrored); + + // Initializes camera device on camera thread. Corresponding delegate's + // callback is called on UI thread to notify about success or failure. Does + // nothing if camera is successfully initialized already. Sets the desired + // width and height of the frame to receive from camera. void Initialize(int desired_width, int desired_height); - // Uninitializes the camera on IO thread. Can be called anytime, any number - // of times. + // Uninitializes the camera on camera thread. Can be called anytime, any + // number of times. void Uninitialize(); - // Starts capturing video frames with specified interval, in ms. Calls the - // corresponding method of delegate to report about success or failure. If - // succeeded, subsequent call doesn't do anything. - void StartCapturing(int64 rate); + // Starts capturing video frames on camera thread. Frames can be retrieved + // by calling GetFrame method. + void StartCapturing(); // Stops capturing video frames. Can be called anytime, any number of // times. @@ -73,6 +76,9 @@ class Camera : public base::RefCountedThreadSafe<Camera> { // be destroyed. void set_delegate(Delegate* delegate) { delegate_ = delegate; } + // Returns the last successful frame in the member passed. + void GetFrame(SkBitmap* frame); + private: // Destructor is private so only its base class can delete Camera objects. ~Camera(); @@ -89,9 +95,9 @@ class Camera : public base::RefCountedThreadSafe<Camera> { // Unmaps video buffers stored in |buffers_|. void UnmapVideoBuffers(); - // Task for IO thread that queries camera about the next frame and sends it to - // |delegate_| via its method or reports failure. Schedules the next task - // for itself if capturing still takes place. + // Task for camera thread that queries camera about the next frame and + // saves it to |frame_image| buffer for UI thread to pick up. Schedules the + // next task for itself if capturing still takes place. void OnCapture(); // Reads a frame from the video device. If retry is needed, returns false. @@ -102,10 +108,12 @@ class Camera : public base::RefCountedThreadSafe<Camera> { // size and notifies the delegate that the image is ready. void ProcessImage(void* data); - // Actual routines that run on IO thread and call delegate's callbacks. See - // the corresponding methods without Do prefix for details. + // Actual routines that run on camera thread and call delegate's callbacks. + // See the corresponding methods without Do prefix for details. void DoInitialize(int desired_width, int desired_height); - void DoStartCapturing(int64 rate); + void DoStartCapturing(); + void DoUninitialize(); + void DoStopCapturing(); // Helper method that reports failure to the delegate via method // corresponding to the current state of the object. @@ -116,12 +124,16 @@ class Camera : public base::RefCountedThreadSafe<Camera> { void OnInitializeFailure(); void OnStartCapturingSuccess(); void OnStartCapturingFailure(); - void OnCaptureSuccess(const SkBitmap& frame); + void OnCaptureSuccess(); void OnCaptureFailure(); - // IO thread routines that implement the corresponding public methods. - void DoUninitialize(); - void DoStopCapturing(); + // Returns true if the code is executed on camera thread right now, false + // otherwise. + bool IsOnCameraThread() const; + + // Posts task to camera thread. + void PostCameraTask(const tracked_objects::Location& from_here, + Task* task); // Defines a buffer in memory where one frame from the camera is stored. struct VideoBuffer { @@ -133,7 +145,10 @@ class Camera : public base::RefCountedThreadSafe<Camera> { // Delegate is accessed only on UI thread. Delegate* delegate_; - // All the members below are accessed only on IO thread. + // Thread where all work with the device is going on. + base::Thread* thread_; + + // All the members below are accessed only on camera thread. // Name of the device file, i.e. "/dev/video0". std::string device_name_; @@ -146,9 +161,6 @@ class Camera : public base::RefCountedThreadSafe<Camera> { // Indicates if capturing has been started. bool is_capturing_; - // Rate at which camera device should be queried about new image, in ms. - int64 capturing_rate_; - // Desired size of the frame to get from camera. If it doesn't match // camera's supported resolution, higher resolution is selected (if // available) and frame is cropped. If higher resolution is not available, @@ -165,6 +177,15 @@ class Camera : public base::RefCountedThreadSafe<Camera> { // mimic mirror behavior. bool mirrored_; + // Image where camera frames are stored for UI thread to pick up. + SkBitmap frame_image_; + + // Lock that guards references to |frame_image_|. + mutable Lock image_lock_; + + // Lock that guards references to |camera_thread_|. + mutable Lock thread_lock_; + DISALLOW_COPY_AND_ASSIGN(Camera); }; diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc index 075fd06..4ee8be9 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.cc +++ b/chrome/browser/chromeos/login/existing_user_controller.cc @@ -27,11 +27,13 @@ #include "chrome/browser/chromeos/login/login_utils.h" #include "chrome/browser/chromeos/login/message_bubble.h" #include "chrome/browser/chromeos/login/wizard_controller.h" +#include "chrome/browser/chromeos/view_ids.h" #include "chrome/browser/chromeos/wm_ipc.h" #include "chrome/browser/views/window.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/net/gaia/google_service_auth_error.h" #include "gfx/native_widget_types.h" +#include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "views/screen.h" @@ -97,27 +99,12 @@ void EnableTooltipsIfNeeded(const std::vector<UserController*>& controllers) { } } -// Returns true if given email is in user whitelist. -// Note this function is for display purpose only and should use -// CheckWhitelist op for the real whitelist check. -bool IsEmailInCachedWhitelist(const std::string& email) { - const ListValue* whitelist = UserCrosSettingsProvider::cached_whitelist(); - if (whitelist) { - StringValue email_value(email); - for (ListValue::const_iterator i(whitelist->begin()); - i != whitelist->end(); ++i) { - if ((*i)->Equals(&email_value)) - return true; - } - } - return false; -} - } // namespace ExistingUserController* ExistingUserController::delete_scheduled_instance_ = NULL; +// TODO(xiyuan): Wait for the cached settings update before using them. ExistingUserController::ExistingUserController( const std::vector<UserManager::User>& users, const gfx::Rect& background_bounds) @@ -126,7 +113,8 @@ ExistingUserController::ExistingUserController( background_view_(NULL), selected_view_index_(kNotSelected), num_login_attempts_(0), - bubble_(NULL) { + bubble_(NULL), + user_settings_(new UserCrosSettingsProvider()) { if (delete_scheduled_instance_) delete_scheduled_instance_->Delete(); @@ -148,7 +136,8 @@ ExistingUserController::ExistingUserController( // TODO(xiyuan): Clean user profile whose email is not in whitelist. if (UserCrosSettingsProvider::cached_allow_new_user() || - IsEmailInCachedWhitelist(users[i].email())) { + UserCrosSettingsProvider::IsEmailInCachedWhitelist( + users[i].email())) { controllers_.push_back(new UserController(this, users[i])); } } @@ -171,6 +160,7 @@ void ExistingUserController::Init() { background_bounds_, GURL(url_string), &background_view_); + background_view_->EnableShutdownButton(); if (!WizardController::IsDeviceRegistered()) { background_view_->SetOobeProgressBarVisible(true); @@ -191,6 +181,7 @@ void ExistingUserController::Init() { WmMessageListener::instance()->AddObserver(this); + LoginUtils::Get()->PrewarmAuthentication(); if (CrosLibrary::Get()->EnsureLoaded()) CrosLibrary::Get()->GetLoginLibrary()->EmitLoginPromptReady(); } @@ -201,7 +192,6 @@ void ExistingUserController::OwnBackground( DCHECK(!background_window_); background_window_ = background_widget; background_view_ = background_view; - background_view_->OnOwnerChanged(); } void ExistingUserController::LoginNewUser(const std::string& username, @@ -348,6 +338,12 @@ void ExistingUserController::ActivateWizard(const std::string& screen_name) { void ExistingUserController::RemoveUser(UserController* source) { ClearErrors(); + // TODO(xiyuan): Wait for the cached settings update before using them. + if (UserCrosSettingsProvider::cached_owner() == source->user().email()) { + // Owner is not allowed to be removed from the device. + return; + } + UserManager::Get()->RemoveUser(source->user().email()); controllers_.erase(controllers_.begin() + source->user_index()); @@ -375,10 +371,6 @@ void ExistingUserController::SelectUser(int index) { } } -void ExistingUserController::OnGoIncognitoButton() { - LoginOffTheRecord(); -} - void ExistingUserController::OnLoginFailure(const LoginFailure& failure) { std::string error = failure.GetErrorString(); @@ -404,6 +396,10 @@ void ExistingUserController::OnLoginFailure(const LoginFailure& failure) { LOG(WARNING) << "No captcha image url was found?"; ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error); } + } else if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED && + failure.error().state() == + GoogleServiceAuthError::HOSTED_NOT_ALLOWED) { + ShowError(IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED, error); } else { if (controllers_[selected_view_index_]->is_new_user()) ShowError(IDS_LOGIN_ERROR_AUTHENTICATING_NEW, error); @@ -431,7 +427,14 @@ gfx::NativeWindow ExistingUserController::GetNativeWindow() const { void ExistingUserController::ShowError(int error_id, const std::string& details) { ClearErrors(); - std::wstring error_text = l10n_util::GetString(error_id); + std::wstring error_text; + // GetStringF fails on debug build if there's no replacement in the string. + if (error_id == IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED) { + error_text = l10n_util::GetStringF( + error_id, l10n_util::GetString(IDS_PRODUCT_OS_NAME)); + } else { + error_text = l10n_util::GetString(error_id); + } // TODO(dpolukhin): show detailed error info. |details| string contains // low level error info that is not localized and even is not user friendly. // For now just ignore it because error_text contains all required information @@ -447,8 +450,11 @@ void ExistingUserController::ShowError(int error_id, arrow = BubbleBorder::BOTTOM_LEFT; } std::wstring help_link; - if (num_login_attempts_ > static_cast<size_t>(1)) + if (error_id == IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED) { + help_link = l10n_util::GetString(IDS_LEARN_MORE); + } else if (num_login_attempts_ > static_cast<size_t>(1)) { help_link = l10n_util::GetString(IDS_CANT_ACCESS_ACCOUNT_BUTTON); + } bubble_ = MessageBubble::Show( controllers_[selected_view_index_]->controls_window(), @@ -462,6 +468,7 @@ void ExistingUserController::ShowError(int error_id, void ExistingUserController::OnLoginSuccess( const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests) { // LoginPerformer instance will delete itself once online auth result is OK. @@ -477,7 +484,7 @@ void ExistingUserController::OnLoginSuccess( !UserManager::Get()->IsKnownUser(username)) { // For new user login don't launch browser until we pass image screen. LoginUtils::Get()->EnableBrowserLaunch(false); - LoginUtils::Get()->CompleteLogin(username, credentials); + LoginUtils::Get()->CompleteLogin(username, password, credentials); ActivateWizard(WizardController::IsDeviceRegistered() ? WizardController::kUserImageScreenName : WizardController::kRegistrationScreenName); @@ -486,7 +493,7 @@ void ExistingUserController::OnLoginSuccess( WmIpc::Message message(WM_IPC_MESSAGE_WM_HIDE_LOGIN); WmIpc::instance()->SendMessage(message); - LoginUtils::Get()->CompleteLogin(username, credentials); + LoginUtils::Get()->CompleteLogin(username, password, credentials); // Delay deletion as we're on the stack. MessageLoop::current()->DeleteSoon(FROM_HERE, this); @@ -523,14 +530,18 @@ void ExistingUserController::OnHelpLinkActivated() { if (!help_app_.get()) help_app_.reset(new HelpAppLauncher(GetNativeWindow())); switch (login_performer_->error().state()) { - case(GoogleServiceAuthError::CONNECTION_FAILED): + case GoogleServiceAuthError::CONNECTION_FAILED: help_app_->ShowHelpTopic( HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT_OFFLINE); break; - case(GoogleServiceAuthError::ACCOUNT_DISABLED): + case GoogleServiceAuthError::ACCOUNT_DISABLED: help_app_->ShowHelpTopic( HelpAppLauncher::HELP_ACCOUNT_DISABLED); break; + case GoogleServiceAuthError::HOSTED_NOT_ALLOWED: + help_app_->ShowHelpTopic( + HelpAppLauncher::HELP_HOSTED_ACCOUNT); + break; default: help_app_->ShowHelpTopic(login_performer_->login_timed_out() ? HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT_OFFLINE : diff --git a/chrome/browser/chromeos/login/existing_user_controller.h b/chrome/browser/chromeos/login/existing_user_controller.h index 9366ec6..aa4d7ca 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.h +++ b/chrome/browser/chromeos/login/existing_user_controller.h @@ -26,11 +26,13 @@ namespace chromeos { class HelpAppLauncher; class MessageBubble; +class UserCrosSettingsProvider; -// ExistingUserController is used to handle login when someone has already -// logged into the machine. When Init is invoked a UserController is created for -// each of the Users's in the UserManager (including one for new user and -// one for BWSI login), and the window manager is then told to show the windows. +// ExistingUserController is used to handle login when someone has +// already logged into the machine. When Init is invoked, a +// UserController is created for each of the Users's in the +// UserManager (including one for new user and one for Guest login), +// and the window manager is then told to show the windows. // // To use ExistingUserController create an instance of it and invoke Init. // @@ -38,7 +40,6 @@ class MessageBubble; // the user logs in (or chooses to see other settings). class ExistingUserController : public WmMessageListener::Observer, public UserController::Delegate, - public BackgroundView::Delegate, public LoginPerformer::Delegate, public MessageBubbleDelegate, public CaptchaView::Delegate, @@ -85,13 +86,11 @@ class ExistingUserController : public WmMessageListener::Observer, virtual void AddStartUrl(const GURL& start_url) { start_url_ = start_url; } virtual void SelectUser(int index); - // BackgroundView::Delegate - virtual void OnGoIncognitoButton(); - // LoginPerformer::Delegate implementation: virtual void OnLoginFailure(const LoginFailure& error); virtual void OnLoginSuccess( const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests); virtual void OnOffTheRecordLoginSuccess(); @@ -166,6 +165,9 @@ class ExistingUserController : public WmMessageListener::Observer, // Help application used for help dialogs. scoped_ptr<HelpAppLauncher> help_app_; + // A user settings provider instance to trigger settings cache update. + scoped_ptr<UserCrosSettingsProvider> user_settings_; + DISALLOW_COPY_AND_ASSIGN(ExistingUserController); }; diff --git a/chrome/browser/chromeos/login/existing_user_view.cc b/chrome/browser/chromeos/login/existing_user_view.cc index 919f530..abe1eb2 100644 --- a/chrome/browser/chromeos/login/existing_user_view.cc +++ b/chrome/browser/chromeos/login/existing_user_view.cc @@ -5,20 +5,30 @@ #include "chrome/browser/chromeos/login/existing_user_view.h" #include "app/l10n_util.h" +#include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/user_controller.h" +#include "chrome/browser/chromeos/login/textfield_with_margin.h" #include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" #include "grit/generated_resources.h" +#include "views/background.h" #include "views/focus/focus_manager.h" #include "views/fill_layout.h" namespace chromeos { +// Colors for gradient background. These should be consistent with border +// window background so textfield border is not visible to the user. +// The background is needed for password textfield to imitate its borders +// transparency correctly. +const SkColor kBackgroundColorTop = SkColorSetRGB(209, 213, 216); +const SkColor kBackgroundColorBottom = SkColorSetRGB(205, 210, 213); + // Textfield with custom processing for Tab/Shift+Tab to select entries. -class UserEntryTextfield : public views::Textfield { +class UserEntryTextfield : public TextfieldWithMargin { public: UserEntryTextfield(UserController* controller, views::Textfield::StyleFlags style) - : Textfield(style), + : TextfieldWithMargin(style), controller_(controller) {} // Overridden from views::View: @@ -62,6 +72,10 @@ void ExistingUserView::RecreateFields() { SetLayoutManager(new views::FillLayout); password_field_ = new UserEntryTextfield(user_controller_, views::Textfield::STYLE_PASSWORD); + password_field_->set_background( + views::Background::CreateVerticalGradientBackground( + kBackgroundColorTop, kBackgroundColorBottom)); + CorrectTextfieldFontSize(password_field_); password_field_->SetFocusable(true); password_field_->SetController(user_controller_); AddChildView(password_field_); diff --git a/chrome/browser/chromeos/login/google_authenticator.cc b/chrome/browser/chromeos/login/google_authenticator.cc index 504cfb8..a4daccd 100644 --- a/chrome/browser/chromeos/login/google_authenticator.cc +++ b/chrome/browser/chromeos/login/google_authenticator.cc @@ -24,6 +24,7 @@ #include "chrome/browser/chromeos/login/authentication_notification_details.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" #include "chrome/browser/chromeos/login/ownership_service.h" +#include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/profile.h" #include "chrome/browser/profile_manager.h" #include "chrome/common/chrome_paths.h" @@ -56,6 +57,8 @@ const int kPassHashLen = 32; GoogleAuthenticator::GoogleAuthenticator(LoginStatusConsumer* consumer) : Authenticator(consumer), + user_manager_(UserManager::Get()), + hosted_policy_(GaiaAuthenticator2::HostedAccountsAllowed), unlock_(false), try_again_(true), checked_for_localaccount_(false) { @@ -89,7 +92,7 @@ void GoogleAuthenticator::TryClientLogin() { GaiaConstants::kContactsService, login_token_, login_captcha_, - GaiaAuthenticator2::HostedAccountsAllowed); + hosted_policy_); BrowserThread::PostDelayedTask( BrowserThread::UI, @@ -111,7 +114,8 @@ void GoogleAuthenticator::PrepareClientLoginAttempt( } void GoogleAuthenticator::ClearClientLoginAttempt() { - password_.clear(); + // Not clearing the password, because we may need to pass it to the + // sync service if login is successful. login_token_.clear(); login_captcha_.clear(); } @@ -141,23 +145,19 @@ bool GoogleAuthenticator::AuthenticateToLogin( bool GoogleAuthenticator::AuthenticateToUnlock(const std::string& username, const std::string& password) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); username_.assign(Canonicalize(username)); ascii_hash_.assign(HashPassword(password)); unlock_ = true; - LoadLocalaccount(kLocalaccountFile); - if (!localaccount_.empty() && localaccount_ == username) { - VLOG(1) << "Unlocking localaccount"; - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - NewRunnableMethod(this, - &GoogleAuthenticator::OnLoginSuccess, - GaiaAuthConsumer::ClientLoginResult(), false)); - } else { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline, - LoginFailure(LoginFailure::UNLOCK_FAILED))); - } + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, + &GoogleAuthenticator::LoadLocalaccount, + std::string(kLocalaccountFile))); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline, + LoginFailure(LoginFailure::UNLOCK_FAILED))); return true; } @@ -184,6 +184,19 @@ void GoogleAuthenticator::OnClientLoginSuccess( VLOG(1) << "Online login successful!"; ClearClientLoginAttempt(); + if (hosted_policy_ == GaiaAuthenticator2::HostedAccountsAllowed && + !user_manager_->IsKnownUser(username_)) { + // First time user, and we don't know if the account is HOSTED or not. + // Since we don't allow HOSTED accounts to log in, we need to try + // again, without allowing HOSTED accounts. + // + // NOTE: we used to do this in the opposite order, so that we'd only + // try the HOSTED pathway if GOOGLE-only failed. This breaks CAPTCHA + // handling, though. + hosted_policy_ = GaiaAuthenticator2::HostedAccountsNotAllowed; + TryClientLogin(); + return; + } BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod(this, @@ -204,6 +217,26 @@ void GoogleAuthenticator::OnClientLoginFailure( LOG(ERROR) << "Login attempt canceled again? Already retried..."; } + if (error.state() == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS && + !user_manager_->IsKnownUser(username_) && + hosted_policy_ != GaiaAuthenticator2::HostedAccountsAllowed) { + // This was a first-time login, we already tried allowing HOSTED accounts + // and succeeded. That we've failed with INVALID_GAIA_CREDENTIALS now + // indicates that the account is HOSTED. + LoginFailure failure_details = + LoginFailure::FromNetworkAuthFailure( + GoogleServiceAuthError( + GoogleServiceAuthError::HOSTED_NOT_ALLOWED)); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, + &GoogleAuthenticator::OnLoginFailure, + failure_details)); + LOG(WARNING) << "Rejecting valid HOSTED account."; + hosted_policy_ = GaiaAuthenticator2::HostedAccountsNotAllowed; + return; + } + ClearClientLoginAttempt(); if (error.state() == GoogleServiceAuthError::TWO_FACTOR) { @@ -254,7 +287,10 @@ void GoogleAuthenticator::OnLoginSuccess( ascii_hash_.c_str(), &mount_error))) { BootTimesLoader::Get()->AddLoginTimeMarker("CryptohomeMounted", true); - consumer_->OnLoginSuccess(username_, credentials, request_pending); + consumer_->OnLoginSuccess(username_, + password_, + credentials, + request_pending); } else if (!unlock_ && mount_error == chromeos::kCryptohomeMountErrorKeyFailure) { consumer_->OnPasswordChangeDetected(credentials); @@ -298,6 +334,7 @@ void GoogleAuthenticator::CheckLocalaccount(const LoginFailure& error) { &mount_error)) { LOG(WARNING) << "Logging in with localaccount: " << localaccount_; consumer_->OnLoginSuccess(username_, + std::string(), GaiaAuthConsumer::ClientLoginResult(), false); } else { diff --git a/chrome/browser/chromeos/login/google_authenticator.h b/chrome/browser/chromeos/login/google_authenticator.h index 75c0cc6..a23a96c 100644 --- a/chrome/browser/chromeos/login/google_authenticator.h +++ b/chrome/browser/chromeos/login/google_authenticator.h @@ -16,12 +16,12 @@ #include "chrome/browser/chromeos/cros/cryptohome_library.h" #include "chrome/browser/chromeos/login/authenticator.h" #include "chrome/common/net/gaia/gaia_auth_consumer.h" +#include "chrome/common/net/gaia/gaia_authenticator2.h" // Authenticates a Chromium OS user against the Google Accounts ClientLogin API. class Lock; class Profile; -class GaiaAuthenticator2; class GoogleServiceAuthError; class LoginFailure; @@ -29,6 +29,7 @@ namespace chromeos { class GoogleAuthenticatorTest; class LoginStatusConsumer; +class UserManager; class GoogleAuthenticator : public Authenticator, public GaiaAuthConsumer { public: @@ -43,6 +44,10 @@ class GoogleAuthenticator : public Authenticator, public GaiaAuthConsumer { // Optionally could pass CAPTCHA challenge token - |login_token| and // |login_captcha| string that user has entered. // + // NOTE: We do not allow HOSTED accounts to log in. In the event that + // we are asked to authenticate valid HOSTED account creds, we will + // call OnLoginFailure() with HOSTED_NOT_ALLOWED. + // // Returns true if the attempt gets sent successfully and false if not. bool AuthenticateToLogin(Profile* profile, const std::string& username, @@ -66,9 +71,13 @@ class GoogleAuthenticator : public Authenticator, public GaiaAuthConsumer { system_salt_ = new_salt; } void set_username(const std::string& fake_user) { username_ = fake_user; } + void set_password(const std::string& fake_pass) { password_ = fake_pass; } void set_password_hash(const std::string& fake_hash) { ascii_hash_ = fake_hash; } + void set_user_manager(UserManager* new_manager) { + user_manager_ = new_manager; + } void SetLocalaccount(const std::string& new_name); // These methods must be called on the UI thread, as they make DBus calls @@ -124,11 +133,12 @@ class GoogleAuthenticator : public Authenticator, public GaiaAuthConsumer { // Clear any cached credentials after we've given up trying to authenticate. void ClearClientLoginAttempt(); - // Start a client login attempt. You should set up the - // GaiaAuthenticator2 first. - // Reuses existing credentials from the last attempt. You should + // Start a client login attempt. |hosted_policy_| governs whether we are + // willing to authenticate accounts that are HOSTED or not. + // You must set up |gaia_authenticator_| first. + // Reuses existing credentials from the last attempt. You must // PrepareClientLoginAttempt before calling this. - void TryClientLogin(); + void TryClientLogin(); // A callback for use on the UI thread. Cancel the current login // attempt, and produce a login failure. @@ -143,6 +153,10 @@ class GoogleAuthenticator : public Authenticator, public GaiaAuthConsumer { char* hex_string, const unsigned int len); + void set_hosted_policy(GaiaAuthenticator2::HostedAccountsSetting policy) { + hosted_policy_ = policy; + } + // The format of said POST body when CAPTCHA token & answer are specified. static const char kFormatCaptcha[]; @@ -156,12 +170,18 @@ class GoogleAuthenticator : public Authenticator, public GaiaAuthConsumer { // Handles all net communications with Gaia. scoped_ptr<GaiaAuthenticator2> gaia_authenticator_; + // Allows us to look up users of the device. + UserManager* user_manager_; + // Milliseconds until we timeout our attempt to hit ClientLogin. static const int kClientLoginTimeoutMs; // Milliseconds until we re-check whether we've gotten the localaccount name. static const int kLocalaccountRetryIntervalMs; + // Whether or not we're accepting HOSTED accounts on this auth attempt. + GaiaAuthenticator2::HostedAccountsSetting hosted_policy_; + std::string username_; // These fields are saved so we can retry client login. std::string password_; diff --git a/chrome/browser/chromeos/login/google_authenticator_unittest.cc b/chrome/browser/chromeos/login/google_authenticator_unittest.cc index 3fbe9b0..956742a 100644 --- a/chrome/browser/chromeos/login/google_authenticator_unittest.cc +++ b/chrome/browser/chromeos/login/google_authenticator_unittest.cc @@ -22,6 +22,8 @@ #include "chrome/browser/chromeos/login/mock_auth_response_handler.h" #include "chrome/browser/chromeos/login/mock_login_status_consumer.h" #include "chrome/browser/chromeos/login/mock_url_fetchers.h" +#include "chrome/browser/chromeos/login/mock_user_manager.h" +#include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/net/gaia/gaia_authenticator2_unittest.h" #include "chrome/common/net/url_fetcher.h" @@ -35,6 +37,7 @@ using namespace file_util; using ::testing::AnyNumber; using ::testing::DoAll; +using ::testing::Eq; using ::testing::Invoke; using ::testing::Return; using ::testing::SetArgumentPointee; @@ -46,7 +49,10 @@ class GoogleAuthenticatorTest : public ::testing::Test { public: GoogleAuthenticatorTest() : username_("me@nowhere.org"), - bytes_as_ascii_("ffff") { + password_("fakepass"), + result_("", "", "", ""), + bytes_as_ascii_("ffff"), + user_manager_(new MockUserManager) { memset(fake_hash_, 0, sizeof(fake_hash_)); fake_hash_[0] = 10; fake_hash_[1] = 1; @@ -121,7 +127,16 @@ class GoogleAuthenticatorTest : public ::testing::Test { void PrepForLogin(GoogleAuthenticator* auth) { auth->set_password_hash(hash_ascii_); auth->set_username(username_); + auth->set_password(password_); auth->SetLocalaccount(""); + auth->set_user_manager(user_manager_.get()); + ON_CALL(*user_manager_.get(), IsKnownUser(username_)) + .WillByDefault(Return(true)); + } + + void PrepForFailedLogin(GoogleAuthenticator* auth) { + PrepForLogin(auth); + auth->set_hosted_policy(GaiaAuthenticator2::HostedAccountsAllowed); } void CancelLogin(GoogleAuthenticator* auth) { @@ -135,12 +150,16 @@ class GoogleAuthenticatorTest : public ::testing::Test { unsigned char fake_hash_[32]; std::string hash_ascii_; std::string username_; + std::string password_; GaiaAuthConsumer::ClientLoginResult result_; // Mocks, destroyed by CrosLibrary class. MockCryptohomeLibrary* mock_library_; MockLibraryLoader* loader_; + char raw_bytes_[2]; std::string bytes_as_ascii_; + + scoped_ptr<MockUserManager> user_manager_; }; TEST_F(GoogleAuthenticatorTest, SaltToAscii) { @@ -192,7 +211,7 @@ TEST_F(GoogleAuthenticatorTest, ReadNoLocalaccount) { TEST_F(GoogleAuthenticatorTest, OnLoginSuccess) { MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, _, false)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, password_, _, false)) .Times(1) .RetiresOnSaturation(); @@ -203,6 +222,7 @@ TEST_F(GoogleAuthenticatorTest, OnLoginSuccess) { scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); auth->set_password_hash(hash_ascii_); auth->set_username(username_); + auth->set_password(password_); auth->OnLoginSuccess(result_, false); } @@ -226,7 +246,7 @@ TEST_F(GoogleAuthenticatorTest, PasswordChange) { EXPECT_CALL(consumer, OnPasswordChangeDetected(result_)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(consumer, OnLoginSuccess(username_, result_, false)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, password_, result_, false)) .Times(1) .RetiresOnSaturation(); @@ -281,7 +301,7 @@ TEST_F(GoogleAuthenticatorTest, ForgetOldData) { EXPECT_CALL(consumer, OnPasswordChangeDetected(result_)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(consumer, OnLoginSuccess(username_, result_, false)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, password_, result_, false)) .Times(1) .RetiresOnSaturation(); @@ -340,7 +360,10 @@ TEST_F(GoogleAuthenticatorTest, LoginDenied) { .RetiresOnSaturation(); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); - PrepForLogin(auth.get()); + PrepForFailedLogin(auth.get()); + EXPECT_CALL(*user_manager_.get(), IsKnownUser(username_)) + .WillOnce(Return(true)) + .RetiresOnSaturation(); auth->OnClientLoginFailure(client_error); message_loop.RunAllPending(); } @@ -358,7 +381,7 @@ TEST_F(GoogleAuthenticatorTest, LoginAccountDisabled) { .RetiresOnSaturation(); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); - PrepForLogin(auth.get()); + PrepForFailedLogin(auth.get()); auth->OnClientLoginFailure(client_error); message_loop.RunAllPending(); } @@ -376,7 +399,7 @@ TEST_F(GoogleAuthenticatorTest, LoginAccountDeleted) { .RetiresOnSaturation(); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); - PrepForLogin(auth.get()); + PrepForFailedLogin(auth.get()); auth->OnClientLoginFailure(client_error); message_loop.RunAllPending(); } @@ -394,7 +417,7 @@ TEST_F(GoogleAuthenticatorTest, LoginServiceUnavailable) { .RetiresOnSaturation(); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); - PrepForLogin(auth.get()); + PrepForFailedLogin(auth.get()); auth->OnClientLoginFailure(client_error); message_loop.RunAllPending(); } @@ -417,7 +440,7 @@ TEST_F(GoogleAuthenticatorTest, CaptchaErrorOutputted) { .RetiresOnSaturation(); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); - PrepForLogin(auth.get()); + PrepForFailedLogin(auth.get()); auth->OnClientLoginFailure(auth_error); message_loop.RunAllPending(); } @@ -430,7 +453,7 @@ TEST_F(GoogleAuthenticatorTest, OfflineLogin) { GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET)); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, result_, false)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, password_, result_, false)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, CheckKey(username_, hash_ascii_)) @@ -451,7 +474,7 @@ TEST_F(GoogleAuthenticatorTest, OnlineLogin) { BrowserThread ui_thread(BrowserThread::UI, &message_loop); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, result_, false)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, password_, result_, false)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_, _)) @@ -460,6 +483,9 @@ TEST_F(GoogleAuthenticatorTest, OnlineLogin) { scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); + EXPECT_CALL(*user_manager_.get(), IsKnownUser(username_)) + .WillOnce(Return(true)) + .RetiresOnSaturation(); auth->OnClientLoginSuccess(result_); message_loop.RunAllPending(); } @@ -469,7 +495,7 @@ TEST_F(GoogleAuthenticatorTest, CheckLocalaccount) { URLRequestStatus status(URLRequestStatus::SUCCESS, 0); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, _, false)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, std::string(), _, false)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, MountForBwsi(_)) @@ -483,36 +509,6 @@ TEST_F(GoogleAuthenticatorTest, CheckLocalaccount) { auth->CheckLocalaccount(LoginFailure(LoginFailure::LOGIN_TIMED_OUT)); } -namespace { - -// Compatible with LoginStatusConsumer::OnLoginSuccess() -static void OnSuccessQuit( - const std::string& username, - const GaiaAuthConsumer::ClientLoginResult& credentials, - bool pending_requests) { - MessageLoop::current()->Quit(); -} - -static void OnSuccessQuitAndFail( - const std::string& username, - const GaiaAuthConsumer::ClientLoginResult& credentials, - bool pending_requests) { - ADD_FAILURE() << "Login should NOT have succeeded!"; - MessageLoop::current()->Quit(); -} - -// Compatible with LoginStatusConsumer::OnLoginFailure() -static void OnFailQuit(const LoginFailure& error) { - MessageLoop::current()->Quit(); -} - -static void OnFailQuitAndFail(const LoginFailure& error) { - ADD_FAILURE() << "Login should have succeeded!"; - MessageLoop::current()->Quit(); -} - -} // anonymous namespace - TEST_F(GoogleAuthenticatorTest, LocalaccountLogin) { // This test checks the logic that governs asynchronously reading the // localaccount name off disk and trying to authenticate against it @@ -521,15 +517,15 @@ TEST_F(GoogleAuthenticatorTest, LocalaccountLogin) { BrowserThread ui_thread(BrowserThread::UI, &message_loop); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, _, false)) - .WillOnce(Invoke(OnSuccessQuit)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, std::string(), _, false)) + .WillOnce(Invoke(MockConsumer::OnSuccessQuit)) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, MountForBwsi(_)) .WillOnce(Return(true)) .RetiresOnSaturation(); // Enable the test to terminate (and fail), even if the login fails. ON_CALL(consumer, OnLoginFailure(_)) - .WillByDefault(Invoke(OnFailQuitAndFail)); + .WillByDefault(Invoke(MockConsumer::OnFailQuitAndFail)); // Manually prep for login, so that localaccount isn't set for us. scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); @@ -564,7 +560,10 @@ TEST_F(GoogleAuthenticatorTest, FullLogin) { chromeos::CryptohomeBlob salt_v(fake_hash_, fake_hash_ + sizeof(fake_hash_)); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, result_, false)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, + password_, + Eq(result_), + false)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, Mount(username_, _, _)) @@ -581,13 +580,105 @@ TEST_F(GoogleAuthenticatorTest, FullLogin) { URLFetcher::set_factory(&factory); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + EXPECT_CALL(*user_manager_.get(), IsKnownUser(username_)) + .WillOnce(Return(true)) + .RetiresOnSaturation(); + auth->set_user_manager(user_manager_.get()); auth->AuthenticateToLogin( - &profile, username_, hash_ascii_, std::string(), std::string()); + &profile, username_, password_, std::string(), std::string()); URLFetcher::set_factory(NULL); message_loop.RunAllPending(); } +TEST_F(GoogleAuthenticatorTest, FullHostedLogin) { + MessageLoopForUI message_loop; + BrowserThread ui_thread(BrowserThread::UI, &message_loop); + chromeos::CryptohomeBlob salt_v(fake_hash_, fake_hash_ + sizeof(fake_hash_)); + + LoginFailure failure_details = + LoginFailure::FromNetworkAuthFailure( + GoogleServiceAuthError( + GoogleServiceAuthError::HOSTED_NOT_ALLOWED)); + + MockConsumer consumer; + EXPECT_CALL(consumer, OnLoginFailure(failure_details)) + .WillOnce(Invoke(MockConsumer::OnFailQuit)) + .RetiresOnSaturation(); + // A failure case, but we still want the test to finish gracefully. + ON_CALL(consumer, OnLoginSuccess(username_, password_, _, _)) + .WillByDefault(Invoke(MockConsumer::OnSuccessQuitAndFail)); + + EXPECT_CALL(*mock_library_, GetSystemSalt()) + .WillOnce(Return(salt_v)) + .RetiresOnSaturation(); + + TestingProfile profile; + + MockFactory<HostedFetcher> factory_invalid; + URLFetcher::set_factory(&factory_invalid); + + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + auth->set_user_manager(user_manager_.get()); + EXPECT_CALL(*user_manager_.get(), IsKnownUser(username_)) + .WillOnce(Return(false)) + .WillOnce(Return(false)) + .RetiresOnSaturation(); + auth->AuthenticateToLogin( + &profile, username_, hash_ascii_, std::string(), std::string()); + + MockFactory<SuccessFetcher> factory_success; + URLFetcher::set_factory(&factory_success); + + message_loop.RunAllPending(); + URLFetcher::set_factory(NULL); +} + +TEST_F(GoogleAuthenticatorTest, FullHostedLoginFailure) { + MessageLoop message_loop(MessageLoop::TYPE_UI); + BrowserThread ui_thread(BrowserThread::UI, &message_loop); + chromeos::CryptohomeBlob salt_v(fake_hash_, fake_hash_ + sizeof(fake_hash_)); + + LoginFailure failure_details = + LoginFailure::FromNetworkAuthFailure( + GoogleServiceAuthError( + GoogleServiceAuthError::HOSTED_NOT_ALLOWED)); + + MockConsumer consumer; + EXPECT_CALL(consumer, OnLoginFailure(failure_details)) + .WillOnce(Invoke(MockConsumer::OnFailQuit)) + .RetiresOnSaturation(); + // A failure case, but we still want the test to finish gracefully. + ON_CALL(consumer, OnLoginSuccess(username_, password_, _, _)) + .WillByDefault(Invoke(MockConsumer::OnSuccessQuitAndFail)); + + EXPECT_CALL(*mock_library_, GetSystemSalt()) + .WillOnce(Return(salt_v)) + .RetiresOnSaturation(); + + TestingProfile profile; + + MockFactory<HostedFetcher> factory_invalid; + URLFetcher::set_factory(&factory_invalid); + + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + auth->set_user_manager(user_manager_.get()); + EXPECT_CALL(*user_manager_.get(), IsKnownUser(username_)) + .WillOnce(Return(false)) + .WillOnce(Return(false)) + .RetiresOnSaturation(); + auth->AuthenticateToLogin( + &profile, username_, hash_ascii_, std::string(), std::string()); + + // For when |auth| tries to load the localaccount file. + BrowserThread file_thread(BrowserThread::FILE); + file_thread.Start(); + + // Run the UI thread until we exit it gracefully. + message_loop.Run(); + URLFetcher::set_factory(NULL); +} + TEST_F(GoogleAuthenticatorTest, CancelLogin) { MessageLoop message_loop(MessageLoop::TYPE_UI); BrowserThread ui_thread(BrowserThread::UI, &message_loop); @@ -596,12 +687,12 @@ TEST_F(GoogleAuthenticatorTest, CancelLogin) { MockConsumer consumer; // The expected case. EXPECT_CALL(consumer, OnLoginFailure(_)) - .WillOnce(Invoke(OnFailQuit)) + .WillOnce(Invoke(MockConsumer::OnFailQuit)) .RetiresOnSaturation(); // A failure case, but we still want the test to finish gracefully. - ON_CALL(consumer, OnLoginSuccess(username_, _, _)) - .WillByDefault(Invoke(OnSuccessQuitAndFail)); + ON_CALL(consumer, OnLoginSuccess(username_, password_, _, _)) + .WillByDefault(Invoke(MockConsumer::OnSuccessQuitAndFail)); // Stuff we expect to happen along the way. EXPECT_CALL(*mock_library_, GetSystemSalt()) @@ -621,7 +712,6 @@ TEST_F(GoogleAuthenticatorTest, CancelLogin) { URLFetcher::set_factory(&factory); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); - // For when |auth| tries to load the localaccount file. BrowserThread file_thread(BrowserThread::FILE); file_thread.Start(); @@ -648,12 +738,12 @@ TEST_F(GoogleAuthenticatorTest, CancelLoginAlreadyGotLocalaccount) { MockConsumer consumer; // The expected case. EXPECT_CALL(consumer, OnLoginFailure(_)) - .WillOnce(Invoke(OnFailQuit)) + .WillOnce(Invoke(MockConsumer::OnFailQuit)) .RetiresOnSaturation(); // A failure case, but we still want the test to finish gracefully. - ON_CALL(consumer, OnLoginSuccess(username_, _, _)) - .WillByDefault(Invoke(OnSuccessQuitAndFail)); + ON_CALL(consumer, OnLoginSuccess(username_, password_, _, _)) + .WillByDefault(Invoke(MockConsumer::OnSuccessQuitAndFail)); // Stuff we expect to happen along the way. EXPECT_CALL(*mock_library_, GetSystemSalt()) @@ -673,7 +763,6 @@ TEST_F(GoogleAuthenticatorTest, CancelLoginAlreadyGotLocalaccount) { URLFetcher::set_factory(&factory); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); - // This time, instead of allowing |auth| to go get the localaccount file // itself, we simulate the case where the file is already loaded, which // happens when this isn't the first login since chrome started. diff --git a/chrome/browser/chromeos/login/guest_user_view.cc b/chrome/browser/chromeos/login/guest_user_view.cc index c26db80..9392c7b 100644 --- a/chrome/browser/chromeos/login/guest_user_view.cc +++ b/chrome/browser/chromeos/login/guest_user_view.cc @@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/login/guest_user_view.h" #include "app/l10n_util.h" +#include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/user_controller.h" #include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" #include "grit/generated_resources.h" @@ -63,7 +64,8 @@ void GuestUserView::RecreateFields() { delete submit_button_; submit_button_ = new UserEntryButton( user_controller_, - l10n_util::GetString(IDS_LOGIN_BUTTON)); + l10n_util::GetString(IDS_ENTER_GUEST_SESSION_BUTTON)); + CorrectNativeButtonFontSize(submit_button_); AddChildView(submit_button_); Layout(); SchedulePaint(); @@ -102,11 +104,13 @@ void GuestUserView::OnLocaleChanged() { void GuestUserView::Layout() { gfx::Size submit_button_size = submit_button_->GetPreferredSize(); - int submit_button_x = (width() - submit_button_size.width()) / 2; + int submit_button_width = std::max(login::kButtonMinWidth, + submit_button_size.width()); + int submit_button_x = (width() - submit_button_width) / 2; int submit_button_y = (height() - submit_button_size.height()) / 2; submit_button_->SetBounds(submit_button_x, submit_button_y, - submit_button_size.width(), + submit_button_width, submit_button_size.height()); } diff --git a/chrome/browser/chromeos/login/help_app_launcher.cc b/chrome/browser/chromeos/login/help_app_launcher.cc index 7746d39..0fabd79 100644 --- a/chrome/browser/chromeos/login/help_app_launcher.cc +++ b/chrome/browser/chromeos/login/help_app_launcher.cc @@ -20,7 +20,8 @@ const char* kHelpTopicFiles[] = { "connectivity.html", "usage.html", "accessaccount.html", "password.html", - "disabled.html" }; + "disabled.html", + "hosted.html" }; } // namespace COMPILE_ASSERT(arraysize(kHelpTopicFiles) == HelpAppLauncher::HELP_TOPIC_COUNT, @@ -74,7 +75,7 @@ void HelpAppLauncher::ShowHelpTopicDialog(const GURL& topic_url) { parent_window_, l10n_util::GetString(IDS_LOGIN_OOBE_HELP_DIALOG_TITLE), topic_url, - LoginHtmlDialog::STYLE_GENERIC)); + LoginHtmlDialog::STYLE_BUBBLE)); } else { dialog_->set_url(topic_url); } diff --git a/chrome/browser/chromeos/login/help_app_launcher.h b/chrome/browser/chromeos/login/help_app_launcher.h index 39586fb..bbc7508 100644 --- a/chrome/browser/chromeos/login/help_app_launcher.h +++ b/chrome/browser/chromeos/login/help_app_launcher.h @@ -23,17 +23,19 @@ class HelpAppLauncher : public LoginHtmlDialog::Delegate { HELP_CONNECTIVITY, // Showed at EULA screen as "Learn more" about stats/crash reports. HELP_STATS_USAGE, - // Showed whenever there're troubles signing in (online case). - HELP_CANT_ACCESS_ACCOUNT, // Showed whenever there're troubles signing in (offline case). HELP_CANT_ACCESS_ACCOUNT_OFFLINE, + // Showed whenever there're troubles signing in (online case). + HELP_CANT_ACCESS_ACCOUNT, // Showed in case when account was disabled. HELP_ACCOUNT_DISABLED, + // Showed in case when hosted account is used. + HELP_HOSTED_ACCOUNT, HELP_TOPIC_COUNT }; // Parent window is used to show dialog. - HelpAppLauncher(gfx::NativeWindow parent_window); + explicit HelpAppLauncher(gfx::NativeWindow parent_window); // Shows specified help topic. // TODO: Pass topic ID. diff --git a/chrome/browser/chromeos/login/helper.cc b/chrome/browser/chromeos/login/helper.cc index d2da133..dea756f 100644 --- a/chrome/browser/chromeos/login/helper.cc +++ b/chrome/browser/chromeos/login/helper.cc @@ -10,6 +10,8 @@ #include "googleurl/src/gurl.h" #include "grit/theme_resources.h" #include "third_party/skia/include/effects/SkGradientShader.h" +#include "views/controls/button/native_button.h" +#include "views/controls/textfield/textfield.h" #include "views/controls/throbber.h" #include "views/painter.h" #include "views/screen.h" @@ -21,7 +23,7 @@ namespace { // Time in ms per throbber frame. const int kThrobberFrameMs = 60; -// Time in ms before smothed throbber is shown. +// Time in ms before smoothed throbber is shown. const int kThrobberStartDelayMs = 500; const SkColor kBackgroundCenterColor = SkColorSetRGB(41, 50, 67); @@ -94,6 +96,16 @@ gfx::Rect CalculateScreenBounds(const gfx::Size& size) { return bounds; } +void CorrectNativeButtonFontSize(views::NativeButton* button) { + if (button) + button->set_font(button->font().DeriveFont(kFontSizeCorrectionDelta)); +} + +void CorrectTextfieldFontSize(views::Textfield* textfield) { + if (textfield) + textfield->SetFont(textfield->font().DeriveFont(kFontSizeCorrectionDelta)); +} + GURL GetAccountRecoveryHelpUrl() { return google_util::AppendGoogleLocaleParam(GURL(kAccountRecoveryHelpUrl)); } diff --git a/chrome/browser/chromeos/login/helper.h b/chrome/browser/chromeos/login/helper.h index ed700cd..9713926 100644 --- a/chrome/browser/chromeos/login/helper.h +++ b/chrome/browser/chromeos/login/helper.h @@ -18,7 +18,9 @@ class Size; } // namespace gfx namespace views { +class NativeButton; class Painter; +class Textfield; class Throbber; } // namespace views @@ -38,6 +40,12 @@ views::Painter* CreateBackgroundPainter(); // |size| is not empty. Otherwise the whole monitor is occupied. gfx::Rect CalculateScreenBounds(const gfx::Size& size); +// Corrects font size for NativeButton control. +void CorrectNativeButtonFontSize(views::NativeButton* button); + +// Corrects font size for Textfield control. +void CorrectTextfieldFontSize(views::Textfield* textfield); + // Returns URL used for account recovery. GURL GetAccountRecoveryHelpUrl(); @@ -50,6 +58,9 @@ enum Command { SIGN_OUT, }; +// Minimal width for the button. +const int kButtonMinWidth = 90; + // Gap between edge and image view, and image view and controls. const int kBorderSize = 6; @@ -62,6 +73,9 @@ const SkColor kBackgroundColor = SK_ColorWHITE; // Text color on the login controls. const SkColor kTextColor = SK_ColorWHITE; +// Default link color on login/OOBE controls. +const SkColor kLinkColor = 0xFF0066CC; + // Default size of the OOBE screen. Includes 10px shadow from each side. // See rounded_rect_painter.cc for border definitions. const int kWizardScreenWidth = 800; @@ -70,9 +84,17 @@ const int kWizardScreenHeight = 450; const int kScreenCornerRadius = 10; const int kUserCornerRadius = 5; - } // namespace login +// Font size correction in points for login/oobe textfields/buttons/title. +const int kFontSizeCorrectionDelta = 2; + +// New pod sizes. +const int kNewUserPodFullWidth = 372; +const int kNewUserPodFullHeight = 372; +const int kNewUserPodSmallWidth = 360; +const int kNewUserPodSmallHeight = 322; + } // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_LOGIN_HELPER_H_ diff --git a/chrome/browser/chromeos/login/keyboard_switch_menu.cc b/chrome/browser/chromeos/login/keyboard_switch_menu.cc index c63efd8..de2026b 100644 --- a/chrome/browser/chromeos/login/keyboard_switch_menu.cc +++ b/chrome/browser/chromeos/login/keyboard_switch_menu.cc @@ -18,7 +18,8 @@ namespace chromeos { KeyboardSwitchMenu::KeyboardSwitchMenu() : InputMethodMenu(NULL /* pref_service */, false /* is_browser_mode */, - false /* is_screen_locker */) { + false /* is_screen_locker_mode */, + true /* is_out_of_box_experience_mode */) { } //////////////////////////////////////////////////////////////////////////////// @@ -43,7 +44,7 @@ void KeyboardSwitchMenu::RunMenu(views::View* source, const gfx::Point& pt) { } else { new_pt.set_x(pt.x() - reverse_offset); } - language_menu().RunMenuAt(new_pt, views::Menu2::ALIGN_TOPLEFT); + input_method_menu().RunMenuAt(new_pt, views::Menu2::ALIGN_TOPLEFT); } std::wstring KeyboardSwitchMenu::GetCurrentKeyboardName() const { diff --git a/chrome/browser/chromeos/login/login_html_dialog.cc b/chrome/browser/chromeos/login/login_html_dialog.cc index 47e3e4b..e8ae340 100644 --- a/chrome/browser/chromeos/login/login_html_dialog.cc +++ b/chrome/browser/chromeos/login/login_html_dialog.cc @@ -17,8 +17,8 @@ namespace chromeos { namespace { // Default width/height ratio of screen size. -const float kDefaultWidthRatio = 0.8; -const float kDefaultHeightRatio = 0.8; +const double kDefaultWidthRatio = 0.6; +const double kDefaultHeightRatio = 0.6; // Custom HtmlDialogView with disabled context menu. class HtmlDialogWithoutContextMenuView : public HtmlDialogView { @@ -45,11 +45,11 @@ LoginHtmlDialog::LoginHtmlDialog(Delegate* delegate, const std::wstring& title, const GURL& url, Style style) - : style_(style), - delegate_(delegate), + : delegate_(delegate), parent_window_(parent_window), title_(title), - url_(url) { + url_(url), + style_(style) { gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size())); width_ = static_cast<int>(kDefaultWidthRatio * screen_bounds.width()); height_ = static_cast<int>(kDefaultHeightRatio * screen_bounds.height()); @@ -65,11 +65,16 @@ void LoginHtmlDialog::Show() { this); switch (style_) { case STYLE_BUBBLE: - chromeos::BubbleWindow::Create(parent_window_, gfx::Rect(), html_view); + chromeos::BubbleWindow::Create(parent_window_, + gfx::Rect(), + chromeos::BubbleWindow::STYLE_XBAR, + html_view); break; case STYLE_GENERIC: default: - views::Window::CreateChromeWindow(parent_window_, gfx::Rect(), html_view); + views::Window::CreateChromeWindow(parent_window_, + gfx::Rect(), + html_view); break; } html_view->InitDialog(); diff --git a/chrome/browser/chromeos/login/login_html_dialog.h b/chrome/browser/chromeos/login/login_html_dialog.h index 65c3116..45f4d88 100644 --- a/chrome/browser/chromeos/login/login_html_dialog.h +++ b/chrome/browser/chromeos/login/login_html_dialog.h @@ -29,7 +29,7 @@ class LoginHtmlDialog : public HtmlDialogUIDelegate { enum Style { STYLE_GENERIC, // Use generic CreateChromeWindow as a host. STYLE_BUBBLE // Use chromeos::BubbleWindow as a host. - } style_; + }; LoginHtmlDialog(Delegate* delegate, gfx::NativeWindow parent_window, @@ -57,6 +57,7 @@ class LoginHtmlDialog : public HtmlDialogUIDelegate { virtual std::string GetDialogArgs() const { return std::string(); } virtual void OnDialogClosed(const std::string& json_retval); virtual void OnCloseContents(TabContents* source, bool* out_close_dialog); + virtual bool ShouldShowDialogTitle() const { return true; } private: // Notifications receiver. @@ -65,6 +66,7 @@ class LoginHtmlDialog : public HtmlDialogUIDelegate { gfx::NativeWindow parent_window_; std::wstring title_; GURL url_; + Style style_; // Dialog display size. int width_; diff --git a/chrome/browser/chromeos/login/login_performer.cc b/chrome/browser/chromeos/login/login_performer.cc index 7424c33..8889bb2 100644 --- a/chrome/browser/chromeos/login/login_performer.cc +++ b/chrome/browser/chromeos/login/login_performer.cc @@ -43,10 +43,14 @@ void LoginPerformer::OnLoginFailure(const LoginFailure& failure) { void LoginPerformer::OnLoginSuccess( const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests) { if (delegate_) { - delegate_->OnLoginSuccess(username, credentials, pending_requests); + delegate_->OnLoginSuccess(username, + password, + credentials, + pending_requests); if (!pending_requests) MessageLoop::current()->DeleteSoon(FROM_HERE, this); } else { diff --git a/chrome/browser/chromeos/login/login_performer.h b/chrome/browser/chromeos/login/login_performer.h index 59f5215..e594723 100644 --- a/chrome/browser/chromeos/login/login_performer.h +++ b/chrome/browser/chromeos/login/login_performer.h @@ -42,7 +42,9 @@ class LoginPerformer : public LoginStatusConsumer, // LoginStatusConsumer implementation: virtual void OnLoginFailure(const LoginFailure& error); - virtual void OnLoginSuccess(const std::string& username, + virtual void OnLoginSuccess( + const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests); virtual void OnOffTheRecordLoginSuccess(); diff --git a/chrome/browser/chromeos/login/login_screen.cc b/chrome/browser/chromeos/login/login_screen.cc index 85778ec..0efde84 100644 --- a/chrome/browser/chromeos/login/login_screen.cc +++ b/chrome/browser/chromeos/login/login_screen.cc @@ -30,7 +30,8 @@ namespace chromeos { LoginScreen::LoginScreen(WizardScreenDelegate* delegate) - : ViewScreen<NewUserView>(delegate), + : ViewScreen<NewUserView>(delegate, + kNewUserPodFullWidth, kNewUserPodFullHeight), bubble_(NULL), authenticator_(NULL) { if (CrosLibrary::Get()->EnsureLoaded()) { @@ -95,12 +96,13 @@ void LoginScreen::OnLoginFailure(const LoginFailure& failure) { void LoginScreen::OnLoginSuccess( const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests) { delegate()->GetObserver(this)->OnExit(ScreenObserver::LOGIN_SIGN_IN_SELECTED); AppendStartUrlToCmdline(); - LoginUtils::Get()->CompleteLogin(username, credentials); + LoginUtils::Get()->CompleteLogin(username, password, credentials); } void LoginScreen::OnOffTheRecordLoginSuccess() { diff --git a/chrome/browser/chromeos/login/login_screen.h b/chrome/browser/chromeos/login/login_screen.h index 0a4364b..f339508 100644 --- a/chrome/browser/chromeos/login/login_screen.h +++ b/chrome/browser/chromeos/login/login_screen.h @@ -46,6 +46,7 @@ class LoginScreen : public ViewScreen<NewUserView>, virtual void OnLoginFailure(const LoginFailure& error); virtual void OnLoginSuccess( const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests); virtual void OnOffTheRecordLoginSuccess(); diff --git a/chrome/browser/chromeos/login/login_screen_browsertest.cc b/chrome/browser/chromeos/login/login_screen_browsertest.cc index e08f828..ccb9c7a 100644 --- a/chrome/browser/chromeos/login/login_screen_browsertest.cc +++ b/chrome/browser/chromeos/login/login_screen_browsertest.cc @@ -132,7 +132,7 @@ IN_PROC_BROWSER_TEST_F(LoginScreenTest, IncognitoLogin) { controller()->set_observer(mock_screen_observer.get()); NewUserView* login = controller()->GetLoginScreen()->view(); - login->LinkActivated(login->browse_without_signin_link_, 0); + login->LinkActivated(login->guest_link_, 0); controller()->set_observer(NULL); Quit(); } diff --git a/chrome/browser/chromeos/login/login_status_consumer.h b/chrome/browser/chromeos/login/login_status_consumer.h index b13abb2..da30369 100644 --- a/chrome/browser/chromeos/login/login_status_consumer.h +++ b/chrome/browser/chromeos/login/login_status_consumer.h @@ -97,11 +97,13 @@ class LoginStatusConsumer { virtual ~LoginStatusConsumer() {} // The current login attempt has ended in failure, with error |error|. virtual void OnLoginFailure(const LoginFailure& error) = 0; - // The current login attempt has succeeded for |username|, returning - // |credentials|. If |pending_requests| is false, we're totally done. - // If it's true, we will still have some more results to report later. + // The current login attempt has succeeded for + // |username|/|password|, returning |credentials|. If + // |pending_requests| is false, we're totally done. If it's true, + // we will still have some more results to report later. virtual void OnLoginSuccess( const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests) = 0; // The current guest login attempt has succeeded. diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc index 421c135..209b889 100644 --- a/chrome/browser/chromeos/login/login_utils.cc +++ b/chrome/browser/chromeos/login/login_utils.cc @@ -4,6 +4,8 @@ #include "chrome/browser/chromeos/login/login_utils.h" +#include <vector> + #include "base/command_line.h" #include "base/file_path.h" #include "base/file_util.h" @@ -19,6 +21,7 @@ #include "chrome/browser/browser_thread.h" #include "chrome/browser/chromeos/boot_times_loader.h" #include "chrome/browser/chromeos/cros/login_library.h" +#include "chrome/browser/chromeos/cros/network_library.h" #include "chrome/browser/chromeos/external_cookie_handler.h" #include "chrome/browser/chromeos/input_method/input_method_util.h" #include "chrome/browser/chromeos/login/cookie_fetcher.h" @@ -29,12 +32,15 @@ #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/net/preconnect.h" #include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/profile.h" #include "chrome/browser/profile_manager.h" +#include "chrome/browser/sync/profile_sync_service.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/logging_chrome.h" +#include "chrome/common/net/gaia/gaia_authenticator2.h" #include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/net/url_request_context_getter.h" #include "chrome/common/pref_names.h" @@ -47,7 +53,6 @@ namespace chromeos { namespace { - // Prefix for Auth token received from ClientLogin request. const char kAuthPrefix[] = "Auth="; // Suffix for Auth token received from ClientLogin request. @@ -63,7 +68,9 @@ class LoginUtilsImpl : public LoginUtils { // Invoked after the user has successfully logged in. This launches a browser // and does other bookkeeping after logging in. - virtual void CompleteLogin(const std::string& username, + virtual void CompleteLogin( + const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials); // Invoked after the tmpfs is successfully mounted. @@ -81,6 +88,9 @@ class LoginUtilsImpl : public LoginUtils { // Returns if browser launch enabled now or not. virtual bool IsBrowserLaunchEnabled() const; + // Warms the url used by authentication. + virtual void PrewarmAuthentication(); + private: // Indicates if DoBrowserLaunch will actually launch the browser or not. bool browser_launch_enabled_; @@ -110,7 +120,9 @@ class LoginUtilsWrapper { DISALLOW_COPY_AND_ASSIGN(LoginUtilsWrapper); }; -void LoginUtilsImpl::CompleteLogin(const std::string& username, +void LoginUtilsImpl::CompleteLogin( + const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials) { BootTimesLoader* btl = BootTimesLoader::Get(); @@ -132,10 +144,7 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username, ProfileManager* profile_manager = g_browser_process->profile_manager(); // Switch log file as soon as possible. - logging::RedirectChromeLogging( - user_data_dir.Append(profile_manager->GetCurrentProfileDir()), - *(CommandLine::ForCurrentProcess()), - logging::DELETE_OLD_LOG_FILE); + logging::RedirectChromeLogging(*(CommandLine::ForCurrentProcess())); btl->AddLoginTimeMarker("LoggingRedirected", false); // The default profile will have been changed because the ProfileManager @@ -175,7 +184,7 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username, // Set the CrOS user by getting this constructor run with the // user's email on first retrieval. - profile->GetProfileSyncService(username); + profile->GetProfileSyncService(username)->SetPassphrase(password); btl->AddLoginTimeMarker("SyncStarted", false); // Attempt to take ownership; this will fail if device is already owned. @@ -229,6 +238,16 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username, btl->AddLoginTimeMarker("IMESTarted", false); } } + + // We suck. This is a hack since we do not have the enterprise feature + // done yet to pull down policies from the domain admin. We'll take this + // out when we get that done properly. + // TODO(xiyuan): Remove this once enterprise feature is ready. + if (EndsWith(username, "@google.com", true)) { + PrefService* pref_service = profile->GetPrefs(); + pref_service->SetBoolean(prefs::kEnableScreenLock, true); + } + DoBrowserLaunch(profile); } @@ -285,6 +304,41 @@ bool LoginUtilsImpl::IsBrowserLaunchEnabled() const { return browser_launch_enabled_; } +// We use a special class for this so that it can be safely leaked if we +// never connect. At shutdown the order is not well defined, and it's possible +// for the infrastructure needed to unregister might be unstable and crash. +class WarmingObserver : public NetworkLibrary::NetworkManagerObserver { + public: + WarmingObserver() { + NetworkLibrary *netlib = CrosLibrary::Get()->GetNetworkLibrary(); + netlib->AddNetworkManagerObserver(this); + } + + // If we're now connected, prewarm the auth url. + void OnNetworkManagerChanged(NetworkLibrary* netlib) { + if (netlib->Connected()) { + chrome_browser_net::Preconnect::PreconnectOnUIThread( + GURL(GaiaAuthenticator2::kClientLoginUrl), + chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED); + netlib->RemoveNetworkManagerObserver(this); + delete this; + } + } +}; + +void LoginUtilsImpl::PrewarmAuthentication() { + if (CrosLibrary::Get()->EnsureLoaded()) { + NetworkLibrary *network = CrosLibrary::Get()->GetNetworkLibrary(); + if (network->Connected()) { + chrome_browser_net::Preconnect::PreconnectOnUIThread( + GURL(GaiaAuthenticator2::kClientLoginUrl), + chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED); + } else { + new WarmingObserver(); + } + } +} + LoginUtils* LoginUtils::Get() { return Singleton<LoginUtilsWrapper>::get()->get(); } diff --git a/chrome/browser/chromeos/login/login_utils.h b/chrome/browser/chromeos/login/login_utils.h index 5c5c6fc..26c4c50 100644 --- a/chrome/browser/chromeos/login/login_utils.h +++ b/chrome/browser/chromeos/login/login_utils.h @@ -35,7 +35,9 @@ class LoginUtils { // Invoked after the user has successfully logged in. This launches a browser // and does other bookkeeping after logging in. - virtual void CompleteLogin(const std::string& username, + virtual void CompleteLogin( + const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials) = 0; // Invoked after the tmpfs is successfully mounted. @@ -54,6 +56,9 @@ class LoginUtils { // Returns if browser launch enabled now or not. virtual bool IsBrowserLaunchEnabled() const = 0; + // Prewarms the authentication network connection. + virtual void PrewarmAuthentication() = 0; + }; } // namespace chromeos diff --git a/chrome/browser/chromeos/login/message_bubble.cc b/chrome/browser/chromeos/login/message_bubble.cc index 82cb775..ab1ce1e 100644 --- a/chrome/browser/chromeos/login/message_bubble.cc +++ b/chrome/browser/chromeos/login/message_bubble.cc @@ -6,6 +6,7 @@ #include "app/resource_bundle.h" #include "base/logging.h" +#include "chrome/browser/chromeos/login/helper.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "views/controls/button/image_button.h" @@ -78,6 +79,8 @@ MessageBubble::MessageBubble(views::WidgetGtk::Type type, layout->StartRowWithPadding(0, 1, 0, kBorderSize); help_link_ = new views::Link(help); help_link_->SetController(this); + help_link_->SetNormalColor(login::kLinkColor); + help_link_->SetHighlightedColor(login::kLinkColor); layout->AddView(help_link_); } } diff --git a/chrome/browser/chromeos/login/mock_authenticator.h b/chrome/browser/chromeos/login/mock_authenticator.h index 6c321eb..d58c110 100644 --- a/chrome/browser/chromeos/login/mock_authenticator.h +++ b/chrome/browser/chromeos/login/mock_authenticator.h @@ -66,6 +66,7 @@ class MockAuthenticator : public Authenticator { // If we want to be more like the real thing, we could save username // in AuthenticateToLogin, but there's not much of a point. consumer_->OnLoginSuccess(expected_username_, + expected_password_, credentials, request_pending); } @@ -110,8 +111,10 @@ class MockLoginUtils : public LoginUtils { } virtual void CompleteLogin(const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& res) { EXPECT_EQ(expected_username_, username); + EXPECT_EQ(expected_password_, password); } virtual void CompleteOffTheRecordLogin(const GURL& start_url) { @@ -133,6 +136,9 @@ class MockLoginUtils : public LoginUtils { return auth_token_; } + virtual void PrewarmAuthentication() { + } + private: std::string expected_username_; std::string expected_password_; diff --git a/chrome/browser/chromeos/login/mock_login_status_consumer.h b/chrome/browser/chromeos/login/mock_login_status_consumer.h index 3f0317c..a3dcd19 100644 --- a/chrome/browser/chromeos/login/mock_login_status_consumer.h +++ b/chrome/browser/chromeos/login/mock_login_status_consumer.h @@ -18,8 +18,9 @@ class MockConsumer : public LoginStatusConsumer { MockConsumer() {} ~MockConsumer() {} MOCK_METHOD1(OnLoginFailure, void(const LoginFailure& error)); - MOCK_METHOD3(OnLoginSuccess, void( + MOCK_METHOD4(OnLoginSuccess, void( const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& result, bool pending_requests)); MOCK_METHOD0(OnOffTheRecordLoginSuccess, void(void)); @@ -41,6 +42,7 @@ class MockConsumer : public LoginStatusConsumer { // Compatible with LoginStatusConsumer::OnLoginSuccess() static void OnSuccessQuit( const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests) { MessageLoop::current()->Quit(); @@ -48,6 +50,7 @@ class MockConsumer : public LoginStatusConsumer { static void OnSuccessQuitAndFail( const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests) { ADD_FAILURE() << "Login should NOT have succeeded!"; diff --git a/chrome/browser/chromeos/login/mock_url_fetchers.cc b/chrome/browser/chromeos/login/mock_url_fetchers.cc index 2e8e083..cc2b67a 100644 --- a/chrome/browser/chromeos/login/mock_url_fetchers.cc +++ b/chrome/browser/chromeos/login/mock_url_fetchers.cc @@ -77,9 +77,10 @@ SuccessFetcher::SuccessFetcher(bool success, SuccessFetcher::~SuccessFetcher() {} void SuccessFetcher::Start() { + URLRequestStatus success(URLRequestStatus::SUCCESS, 0); delegate()->OnURLFetchComplete(this, url_, - URLRequestStatus(URLRequestStatus::SUCCESS, 0), + success, RC_REQUEST_OK, ResponseCookies(), std::string()); @@ -106,4 +107,33 @@ void FailFetcher::Start() { std::string()); } +HostedFetcher::HostedFetcher(bool success, + const GURL& url, + const std::string& results, + URLFetcher::RequestType request_type, + URLFetcher::Delegate* d) + : URLFetcher(url, request_type, d), + url_(url) { +} + +HostedFetcher::~HostedFetcher() {} + +void HostedFetcher::Start() { + URLRequestStatus success(URLRequestStatus::SUCCESS, 0); + int response_code = RC_REQUEST_OK; + std::string data; + VLOG(1) << upload_data(); + if (upload_data().find("HOSTED") == std::string::npos) { + VLOG(1) << "HostedFetcher failing request"; + response_code = RC_FORBIDDEN; + data.assign("Error=BadAuthentication"); + } + delegate()->OnURLFetchComplete(this, + url_, + success, + response_code, + ResponseCookies(), + data); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/login/mock_url_fetchers.h b/chrome/browser/chromeos/login/mock_url_fetchers.h index ff874de..187fc66 100644 --- a/chrome/browser/chromeos/login/mock_url_fetchers.h +++ b/chrome/browser/chromeos/login/mock_url_fetchers.h @@ -86,6 +86,23 @@ class FailFetcher : public URLFetcher { DISALLOW_COPY_AND_ASSIGN(FailFetcher); }; +class HostedFetcher : public URLFetcher { + public: + HostedFetcher(bool success, + const GURL& url, + const std::string& results, + URLFetcher::RequestType request_type, + URLFetcher::Delegate* d); + virtual ~HostedFetcher(); + + void Start(); + + private: + GURL url_; + + DISALLOW_COPY_AND_ASSIGN(HostedFetcher); +}; + } // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_URL_FETCHERS_H_ diff --git a/chrome/browser/chromeos/login/mock_user_manager.h b/chrome/browser/chromeos/login/mock_user_manager.h new file mode 100644 index 0000000..87858e6 --- /dev/null +++ b/chrome/browser/chromeos/login/mock_user_manager.h @@ -0,0 +1,34 @@ +// 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_CHROMEOS_LOGIN_MOCK_USER_MANAGER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_USER_MANAGER_H_ +#pragma once + +#include <string> +#include <vector> + +#include "chrome/browser/chromeos/login/user_manager.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace chromeos { + +class MockUserManager : public UserManager { + public: + MockUserManager() {} + virtual ~MockUserManager() {} + + MOCK_CONST_METHOD0(GetUsers, std::vector<User>()); + MOCK_METHOD0(OffTheRecordUserLoggedIn, void()); + MOCK_METHOD1(UserLoggedIn, void(const std::string&)); + MOCK_METHOD1(RemoveUser, void(const std::string&)); + MOCK_METHOD1(IsKnownUser, bool(const std::string&)); + MOCK_METHOD0(logged_in_user, const User&()); + MOCK_METHOD0(current_user_is_owner, bool()); + MOCK_METHOD1(set_current_user_is_owner, void(bool)); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_USER_MANAGER_H_ diff --git a/chrome/browser/chromeos/login/network_screen.cc b/chrome/browser/chromeos/login/network_screen.cc index 2da1197..d760447 100644 --- a/chrome/browser/chromeos/login/network_screen.cc +++ b/chrome/browser/chromeos/login/network_screen.cc @@ -77,9 +77,9 @@ void NetworkScreen::ButtonPressed(views::Button* sender, } //////////////////////////////////////////////////////////////////////////////// -// NetworkLibrary::Observer implementation: +// NetworkLibrary::NetworkManagerObserver implementation: -void NetworkScreen::NetworkChanged(NetworkLibrary* network_lib) { +void NetworkScreen::OnNetworkManagerChanged(NetworkLibrary* network_lib) { UpdateStatus(network_lib); } @@ -110,7 +110,7 @@ void NetworkScreen::OnHelpLinkActivated() { void NetworkScreen::Refresh() { if (CrosLibrary::Get()->EnsureLoaded()) { SubscribeNetworkNotification(); - NetworkChanged(chromeos::CrosLibrary::Get()->GetNetworkLibrary()); + OnNetworkManagerChanged(chromeos::CrosLibrary::Get()->GetNetworkLibrary()); } } @@ -120,14 +120,16 @@ void NetworkScreen::Refresh() { void NetworkScreen::SubscribeNetworkNotification() { if (!is_network_subscribed_) { is_network_subscribed_ = true; - chromeos::CrosLibrary::Get()->GetNetworkLibrary()->AddObserver(this); + chromeos::CrosLibrary::Get()->GetNetworkLibrary() + ->AddNetworkManagerObserver(this); } } void NetworkScreen::UnsubscribeNetworkNotification() { if (is_network_subscribed_) { is_network_subscribed_ = false; - chromeos::CrosLibrary::Get()->GetNetworkLibrary()->RemoveObserver(this); + chromeos::CrosLibrary::Get()->GetNetworkLibrary() + ->RemoveNetworkManagerObserver(this); } } @@ -164,16 +166,16 @@ void NetworkScreen::UpdateStatus(NetworkLibrary* network) { StopWaitingForConnection( l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET)); } else if (network->wifi_connected()) { - StopWaitingForConnection(ASCIIToUTF16(network->wifi_network().name())); + StopWaitingForConnection(ASCIIToUTF16(network->wifi_network()->name())); } else if (network->cellular_connected()) { - StopWaitingForConnection(ASCIIToUTF16(network->cellular_network().name())); + StopWaitingForConnection(ASCIIToUTF16(network->cellular_network()->name())); } else if (network->ethernet_connecting()) { WaitForConnection( l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET)); } else if (network->wifi_connecting()) { - WaitForConnection(ASCIIToUTF16(network->wifi_network().name())); + WaitForConnection(ASCIIToUTF16(network->wifi_network()->name())); } else if (network->cellular_connecting()) { - WaitForConnection(ASCIIToUTF16(network->cellular_network().name())); + WaitForConnection(ASCIIToUTF16(network->cellular_network()->name())); } else { StopWaitingForConnection(network_id_); } diff --git a/chrome/browser/chromeos/login/network_screen.h b/chrome/browser/chromeos/login/network_screen.h index 284ebb0..b3aa531 100644 --- a/chrome/browser/chromeos/login/network_screen.h +++ b/chrome/browser/chromeos/login/network_screen.h @@ -47,8 +47,8 @@ class NetworkScreen : public ViewScreen<NetworkSelectionView>, // views::ButtonListener implementation: virtual void ButtonPressed(views::Button* sender, const views::Event& event); - // NetworkLibrary::Observer implementation: - virtual void NetworkChanged(NetworkLibrary* network_lib); + // NetworkLibrary::NetworkManagerObserver implementation: + virtual void OnNetworkManagerChanged(NetworkLibrary* network_lib); protected: // Subscribes NetworkScreen to the network change notification, diff --git a/chrome/browser/chromeos/login/network_screen_browsertest.cc b/chrome/browser/chromeos/login/network_screen_browsertest.cc index bce1a6f..b45c02d 100644 --- a/chrome/browser/chromeos/login/network_screen_browsertest.cc +++ b/chrome/browser/chromeos/login/network_screen_browsertest.cc @@ -63,33 +63,53 @@ class NetworkScreenTest : public WizardInProcessBrowserTest { // Minimal set of expectations needed on NetworkScreen initialization. // Status bar expectations are defined with RetiresOnSaturation() so // these mocks will be active once status bar is initialized. + EXPECT_CALL(*mock_network_library_, active_network()) + .Times(1) + .WillRepeatedly((Return((const Network*)(NULL)))) + .RetiresOnSaturation(); EXPECT_CALL(*mock_network_library_, ethernet_connected()) - .Times(3) + .Times(2) .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_network_library_, ethernet_connecting()) - .Times(1) + .Times(2) .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_network_library_, wifi_connected()) - .Times(3) + .Times(1) .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_network_library_, wifi_connecting()) - .Times(3) + .Times(2) .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_network_library_, cellular_connected()) - .Times(3) + .Times(1) .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_network_library_, cellular_connecting()) - .Times(3) - .WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_network_library_, Connected()) - .Times(3) + .Times(2) .WillRepeatedly(Return(false)); + EXPECT_CALL(*mock_network_library_, ethernet_available()) + .Times(1) + .WillRepeatedly((Return(true))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_network_library_, wifi_available()) + .Times(1) + .WillRepeatedly((Return(false))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_network_library_, cellular_available()) + .Times(1) + .WillRepeatedly((Return(false))) + .RetiresOnSaturation(); + + // Add a Connecting for prewarming auth url check. EXPECT_CALL(*mock_network_library_, Connecting()) - .Times(2) + .Times(1) .WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_network_library_, AddObserver(_)) - .Times(2); - EXPECT_CALL(*mock_network_library_, RemoveObserver(_)) + // Add a Connected for prewarming auth url check. + EXPECT_CALL(*mock_network_library_, Connected()) + .Times(4) + .WillRepeatedly(Return(false)); + // Add an Observer for prewarming auth url check. + EXPECT_CALL(*mock_network_library_, AddNetworkManagerObserver(_)) + .Times(3); + EXPECT_CALL(*mock_network_library_, RemoveNetworkManagerObserver(_)) .Times(2); cros_mock_->SetStatusAreaMocksExpectations(); @@ -145,7 +165,7 @@ IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Ethernet) { EXPECT_CALL(*mock_network_library_, ethernet_connecting()) .WillOnce((Return(true))); - network_screen->NetworkChanged(mock_network_library_); + network_screen->OnNetworkManagerChanged(mock_network_library_); EXPECT_FALSE(network_view->IsContinueEnabled()); EXPECT_FALSE(network_view->IsConnecting()); @@ -154,7 +174,7 @@ IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Ethernet) { EXPECT_CALL(*mock_network_library_, Connected()) .WillOnce(Return(true)); - network_screen->NetworkChanged(mock_network_library_); + network_screen->OnNetworkManagerChanged(mock_network_library_); EXPECT_TRUE(network_view->IsContinueEnabled()); EmulateContinueButtonExit(network_screen); @@ -180,11 +200,11 @@ IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Wifi) { .WillOnce((Return(false))); EXPECT_CALL(*mock_network_library_, wifi_connecting()) .WillOnce((Return(true))); - WifiNetwork wifi; + scoped_ptr<WifiNetwork> wifi(new WifiNetwork()); EXPECT_CALL(*mock_network_library_, wifi_network()) - .WillOnce(ReturnRef(wifi)); + .WillOnce(Return(wifi.get())); - network_screen->NetworkChanged(mock_network_library_); + network_screen->OnNetworkManagerChanged(mock_network_library_); EXPECT_FALSE(network_view->IsContinueEnabled()); EXPECT_FALSE(network_view->IsConnecting()); @@ -193,7 +213,7 @@ IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Wifi) { EXPECT_CALL(*mock_network_library_, Connected()) .WillOnce(Return(true)); - network_screen->NetworkChanged(mock_network_library_); + network_screen->OnNetworkManagerChanged(mock_network_library_); EXPECT_TRUE(network_view->IsContinueEnabled()); EmulateContinueButtonExit(network_screen); @@ -221,11 +241,11 @@ IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Cellular) { .WillOnce((Return(false))); EXPECT_CALL(*mock_network_library_, cellular_connecting()) .WillOnce((Return(true))); - CellularNetwork cellular; + scoped_ptr<CellularNetwork> cellular(new CellularNetwork()); EXPECT_CALL(*mock_network_library_, cellular_network()) - .WillOnce(ReturnRef(cellular)); + .WillOnce(Return(cellular.get())); - network_screen->NetworkChanged(mock_network_library_); + network_screen->OnNetworkManagerChanged(mock_network_library_); EXPECT_FALSE(network_view->IsContinueEnabled()); EXPECT_FALSE(network_view->IsConnecting()); @@ -234,7 +254,7 @@ IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Cellular) { EXPECT_CALL(*mock_network_library_, Connected()) .WillOnce(Return(true)); - network_screen->NetworkChanged(mock_network_library_); + network_screen->OnNetworkManagerChanged(mock_network_library_); EXPECT_TRUE(network_view->IsContinueEnabled()); EmulateContinueButtonExit(network_screen); @@ -260,13 +280,13 @@ IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Timeout) { .WillOnce((Return(false))); EXPECT_CALL(*mock_network_library_, wifi_connecting()) .WillOnce((Return(true))); - WifiNetwork wifi; + scoped_ptr<WifiNetwork> wifi(new WifiNetwork()); EXPECT_CALL(*mock_network_library_, wifi_network()) - .WillOnce(ReturnRef(wifi)); + .WillOnce(Return(wifi.get())); EXPECT_CALL(*mock_network_library_, Connected()) .WillOnce(Return(false)); - network_screen->NetworkChanged(mock_network_library_); + network_screen->OnNetworkManagerChanged(mock_network_library_); EXPECT_FALSE(network_view->IsContinueEnabled()); EXPECT_FALSE(network_view->IsConnecting()); diff --git a/chrome/browser/chromeos/login/network_screen_delegate.h b/chrome/browser/chromeos/login/network_screen_delegate.h index 3a5978a..e64e380 100644 --- a/chrome/browser/chromeos/login/network_screen_delegate.h +++ b/chrome/browser/chromeos/login/network_screen_delegate.h @@ -22,7 +22,7 @@ class LanguageSwitchMenu; // Interface that NetworkScreen exposes to the NetworkSelectionView. class NetworkScreenDelegate : public views::ButtonListener, - public NetworkLibrary::Observer { + public NetworkLibrary::NetworkManagerObserver { public: // Cleares all error notifications. virtual void ClearErrors() = 0; diff --git a/chrome/browser/chromeos/login/network_selection_view.cc b/chrome/browser/chromeos/login/network_selection_view.cc index 62ca881..ec780f1 100644 --- a/chrome/browser/chromeos/login/network_selection_view.cc +++ b/chrome/browser/chromeos/login/network_selection_view.cc @@ -15,6 +15,7 @@ #include "chrome/browser/chromeos/login/keyboard_switch_menu.h" #include "chrome/browser/chromeos/login/language_switch_menu.h" #include "chrome/browser/chromeos/login/network_screen_delegate.h" +#include "chrome/browser/chromeos/login/proxy_settings_dialog.h" #include "chrome/browser/chromeos/login/rounded_rect_painter.h" #include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" #include "chrome/browser/chromeos/status/network_dropdown_button.h" @@ -60,8 +61,11 @@ const int kPaddingColumnWidth = 55; const int kMediumPaddingColumnWidth = 20; const int kControlPaddingRow = 15; +// Size to add to the welcome title font. +const int kWelcomeTitleFontDelta = 5; + // Fixed size for language/keyboard/network controls height. -const int kSelectionBoxHeight = 29; +const int kSelectionBoxHeight = 30; // Menu button is drawn using our custom icons in resources. See // TextButtonBorder::Paint() for details. So this offset compensate @@ -78,11 +82,18 @@ const int kMenuWidthOffset = 6; const SkColor kWelcomeColor = 0xFFCDD3D6; -// Hints for size of proxy settings dialog. -static const int kProxySettingsDialogReasonableWidth = 700; -static const int kProxySettingsDialogReasonableHeight = 460; -static const int kProxySettingsDialogReasonableWidthRatio = 0.4; -static const int kProxySettingsDialogReasonableHeightRatio = 0.4; +// Initializes menu button default properties. +static void InitMenuButtonProperties(views::MenuButton* menu_button) { + menu_button->SetFocusable(true); + menu_button->SetNormalHasBorder(true); + menu_button->SetEnabledColor(SK_ColorBLACK); + menu_button->SetHighlightColor(SK_ColorBLACK); + menu_button->SetHoverColor(SK_ColorBLACK); + menu_button->set_animate_on_state_change(false); + // Menu is positioned by bottom right corner of the MenuButton. + menu_button->set_menu_offset(kMenuHorizontalOffset, kMenuVerticalOffset); +} + } // namespace namespace chromeos { @@ -250,9 +261,7 @@ void NetworkSelectionView::InitLayout() { GridLayout::FIXED, dropdown_width, dropdown_width); column_set->AddPaddingColumn(1, kPaddingColumnWidth); - const int h_padding = 30/*(screen_size.width() - 2 * kBorderSize - - connecting_network_label_->GetPreferredSize().width() - - throbber_->GetPreferredSize().width()) / 2*/; + const int h_padding = 30; column_set = contents_layout->AddColumnSet(THROBBER_ROW); column_set->AddPaddingColumn(1, h_padding); column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0, @@ -278,8 +287,8 @@ void NetworkSelectionView::Init() { views::Background::CreateBackgroundPainter(true, painter)); ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - gfx::Font welcome_label_font = - rb.GetFont(ResourceBundle::LargeFont).DeriveFont(0, gfx::Font::BOLD); + gfx::Font welcome_label_font = rb.GetFont(ResourceBundle::LargeFont). + DeriveFont(kWelcomeTitleFontDelta, gfx::Font::BOLD); welcome_label_ = new views::Label(); welcome_label_->SetColor(kWelcomeColor); @@ -291,12 +300,7 @@ void NetworkSelectionView::Init() { languages_menubutton_ = new NotifyingMenuButton( NULL, std::wstring(), delegate_->language_switch_menu(), true, delegate_); - languages_menubutton_->SetFocusable(true); - languages_menubutton_->SetNormalHasBorder(true); - languages_menubutton_->set_animate_on_state_change(false); - // Menu is positioned by bottom right corner of the MenuButton. - languages_menubutton_->set_menu_offset(kMenuHorizontalOffset, - kMenuVerticalOffset); + InitMenuButtonProperties(languages_menubutton_); select_keyboard_label_ = new views::Label(); select_keyboard_label_->SetFont(rb.GetFont(ResourceBundle::MediumFont)); @@ -304,11 +308,7 @@ void NetworkSelectionView::Init() { keyboards_menubutton_ = new views::MenuButton( NULL /* listener */, L"", delegate_->keyboard_switch_menu(), true /* show_menu_marker */); - keyboards_menubutton_->SetFocusable(true); - keyboards_menubutton_->SetNormalHasBorder(true); - keyboards_menubutton_->set_animate_on_state_change(false); - keyboards_menubutton_->set_menu_offset(kMenuHorizontalOffset, - kMenuVerticalOffset); + InitMenuButtonProperties(keyboards_menubutton_); select_network_label_ = new views::Label(); select_network_label_->SetFont(rb.GetFont(ResourceBundle::MediumFont)); @@ -316,12 +316,7 @@ void NetworkSelectionView::Init() { network_dropdown_ = new NetworkControlReportOnActivate(false, GetNativeWindow(), delegate_); - - network_dropdown_->set_menu_offset(kMenuHorizontalOffset, - kMenuVerticalOffset); - network_dropdown_->SetNormalHasBorder(true); - network_dropdown_->SetFocusable(true); - network_dropdown_->set_animate_on_state_change(false); + InitMenuButtonProperties(network_dropdown_); connecting_network_label_ = new views::Label(); connecting_network_label_->SetFont(rb.GetFont(ResourceBundle::MediumFont)); @@ -331,6 +326,8 @@ void NetworkSelectionView::Init() { proxy_settings_link_->SetController(this); proxy_settings_link_->SetVisible(true); proxy_settings_link_->SetFocusable(true); + proxy_settings_link_->SetNormalColor(login::kLinkColor); + proxy_settings_link_->SetHighlightedColor(login::kLinkColor); UpdateLocalizedStrings(); } @@ -437,26 +434,8 @@ bool NetworkSelectionView::IsContinueEnabled() const { void NetworkSelectionView::LinkActivated(views::Link* source, int) { if (source == proxy_settings_link_) { if (!proxy_settings_dialog_.get()) { - static const char kProxySettingsURL[] = - "chrome://settings/proxy?menu=off"; - proxy_settings_dialog_.reset(new LoginHtmlDialog( - this, - GetNativeWindow(), - std::wstring(), - GURL(kProxySettingsURL), - LoginHtmlDialog::STYLE_BUBBLE)); - gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size())); - proxy_settings_dialog_->SetDialogSize( - std::min( - screen_bounds.width(), - std::max(kProxySettingsDialogReasonableWidth, static_cast<int>( - kProxySettingsDialogReasonableWidthRatio * - screen_bounds.width()))), - std::min( - screen_bounds.height(), - std::max(kProxySettingsDialogReasonableHeight, static_cast<int>( - kProxySettingsDialogReasonableHeightRatio * - screen_bounds.height())))); + proxy_settings_dialog_.reset( + new ProxySettingsDialog(this, GetNativeWindow())); } proxy_settings_dialog_->Show(); } diff --git a/chrome/browser/chromeos/login/new_user_view.cc b/chrome/browser/chromeos/login/new_user_view.cc index 7c1b841..62b63e5 100644 --- a/chrome/browser/chromeos/login/new_user_view.cc +++ b/chrome/browser/chromeos/login/new_user_view.cc @@ -20,36 +20,55 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros_settings_provider_user.h" #include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/rounded_rect_painter.h" +#include "chrome/browser/chromeos/login/textfield_with_margin.h" #include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" +#include "gfx/font.h" #include "grit/app_resources.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" +#include "views/background.h" #include "views/controls/button/native_button.h" #include "views/controls/label.h" #include "views/controls/throbber.h" #include "views/widget/widget_gtk.h" -using views::Label; -using views::Textfield; using views::View; -using views::WidgetGtk; namespace { -const int kTextfieldWidth = 286; +const int kTextfieldWidth = 230; const int kSplitterHeight = 1; -const int kRowPad = 7; +const int kTitlePad = 20; +const int kRowPad = 13; +const int kBottomPad = 33; +const int kLeftPad = 33; const int kColumnPad = 7; -const int kLanguagesMenuHeight = 30; +const int kLanguagesMenuHeight = 25; +const int kLanguagesMenuPad = 5; const SkColor kLanguagesMenuTextColor = 0xFF999999; const SkColor kErrorColor = 0xFF8F384F; +const SkColor kSplitterUp1Color = 0xFFD0D2D3; +const SkColor kSplitterUp2Color = 0xFFE1E3E4; +const SkColor kSplitterDown1Color = 0xFFE3E6E8; +const SkColor kSplitterDown2Color = 0xFFEAEDEE; const char kDefaultDomain[] = "@gmail.com"; +// Colors for gradient background of textfields. These should be consistent +// with border window background so textfield border is not visible to the +// user. The background is needed for username and password textfield to +// imitate its borders transparency correctly. +const SkColor kUsernameBackgroundColorTop = SkColorSetRGB(229, 232, 233); +const SkColor kUsernameBackgroundColorBottom = SkColorSetRGB(226, 229, 230); +const SkColor kPasswordBackgroundColorTop = SkColorSetRGB(224, 227, 229); +const SkColor kPasswordBackgroundColorBottom = SkColorSetRGB(221, 224, 226); + + // Textfield that adds domain to the entered username if focus is lost and // username doesn't have full domain. -class UsernameField : public views::Textfield { +class UsernameField : public chromeos::TextfieldWithMargin { public: UsernameField() {} @@ -74,15 +93,18 @@ namespace chromeos { NewUserView::NewUserView(Delegate* delegate, bool need_border, - bool need_browse_without_signin) + bool need_guest_link) : username_field_(NULL), password_field_(NULL), title_label_(NULL), title_hint_label_(NULL), - splitter_(NULL), + splitter_up1_(NULL), + splitter_up2_(NULL), + splitter_down1_(NULL), + splitter_down2_(NULL), sign_in_button_(NULL), create_account_link_(NULL), - browse_without_signin_link_(NULL), + guest_link_(NULL), languages_menubutton_(NULL), throbber_(NULL), accel_focus_pass_(views::Accelerator(app::VKEY_P, false, false, true)), @@ -95,10 +117,12 @@ NewUserView::NewUserView(Delegate* delegate, focus_delayed_(false), login_in_process_(false), need_border_(need_border), - need_browse_without_signin_(need_browse_without_signin), + need_guest_link_(false), need_create_account_(false), languages_menubutton_order_(-1), sign_in_button_order_(-1) { + if (need_guest_link && UserCrosSettingsProvider::cached_allow_guest()) + need_guest_link_ = true; } NewUserView::~NewUserView() { @@ -107,15 +131,16 @@ NewUserView::~NewUserView() { void NewUserView::Init() { if (need_border_) { // Use rounded rect background. - set_border(CreateWizardBorder(&BorderDefinition::kScreenBorder)); + set_border(CreateWizardBorder(&BorderDefinition::kUserBorder)); views::Painter* painter = CreateWizardPainter( - &BorderDefinition::kScreenBorder); + &BorderDefinition::kUserBorder); set_background(views::Background::CreateBackgroundPainter(true, painter)); } // Set up fonts. ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - gfx::Font title_font = rb.GetFont(ResourceBundle::MediumBoldFont); + gfx::Font title_font = rb.GetFont(ResourceBundle::MediumBoldFont).DeriveFont( + kFontSizeCorrectionDelta); gfx::Font title_hint_font = rb.GetFont(ResourceBundle::BoldFont); title_label_ = new views::Label(); @@ -131,15 +156,24 @@ void NewUserView::Init() { title_hint_label_->SetMultiLine(true); AddChildView(title_hint_label_); - splitter_ = new views::View(); - splitter_->set_background( - views::Background::CreateSolidBackground(SK_ColorGRAY)); - AddChildView(splitter_); + splitter_up1_ = CreateSplitter(kSplitterUp1Color); + splitter_up2_ = CreateSplitter(kSplitterUp2Color); + splitter_down1_ = CreateSplitter(kSplitterDown1Color); + splitter_down2_ = CreateSplitter(kSplitterDown2Color); username_field_ = new UsernameField(); + CorrectTextfieldFontSize(username_field_); + username_field_->set_background( + views::Background::CreateVerticalGradientBackground( + kUsernameBackgroundColorTop, + kUsernameBackgroundColorBottom)); AddChildView(username_field_); - password_field_ = new views::Textfield(views::Textfield::STYLE_PASSWORD); + password_field_ = new TextfieldWithMargin(views::Textfield::STYLE_PASSWORD); + CorrectTextfieldFontSize(password_field_); + password_field_->set_background( + views::Background::CreateVerticalGradientBackground( + kPasswordBackgroundColorTop, kPasswordBackgroundColorBottom)); AddChildView(password_field_); throbber_ = CreateDefaultSmoothedThrobber(); @@ -153,8 +187,8 @@ void NewUserView::Init() { if (need_create_account_) { InitLink(&create_account_link_); } - if (need_browse_without_signin_) { - InitLink(&browse_without_signin_link_); + if (need_guest_link_) { + InitLink(&guest_link_); } AddChildView(languages_menubutton_); @@ -164,8 +198,7 @@ void NewUserView::Init() { AddAccelerator(accel_login_off_the_record_); AddAccelerator(accel_enable_accessibility_); - UpdateLocalizedStrings(); - RequestFocus(); + OnLocaleChanged(); // Controller to handle events from textfields username_field_->SetController(this); @@ -210,6 +243,7 @@ void NewUserView::RecreatePeculiarControls() { // sized so delete and recreate the button on text update. delete sign_in_button_; sign_in_button_ = new views::NativeButton(this, std::wstring()); + CorrectNativeButtonFontSize(sign_in_button_); UpdateSignInButtonState(); if (!CrosLibrary::Get()->EnsureLoaded()) @@ -222,6 +256,13 @@ void NewUserView::UpdateSignInButtonState() { sign_in_button_->SetEnabled(enabled); } +views::View* NewUserView::CreateSplitter(SkColor color) { + views::View* splitter = new views::View(); + splitter->set_background(views::Background::CreateSolidBackground(color)); + AddChildView(splitter); + return splitter; +} + void NewUserView::AddChildView(View* view) { // languages_menubutton_ and sign_in_button_ are recreated on text change, // so we restore their original position in layout. @@ -253,8 +294,8 @@ void NewUserView::UpdateLocalizedStrings() { create_account_link_->SetText( l10n_util::GetString(IDS_CREATE_ACCOUNT_BUTTON)); } - if (need_browse_without_signin_) { - browse_without_signin_link_->SetText( + if (need_guest_link_) { + guest_link_->SetText( l10n_util::GetString(IDS_BROWSE_WITHOUT_SIGNING_IN_BUTTON)); } delegate_->ClearErrors(); @@ -286,6 +327,10 @@ void NewUserView::ViewHierarchyChanged(bool is_add, focus_grabber_factory_.NewRunnableMethod( &NewUserView::FocusFirstField)); WizardAccessibilityHelper::GetInstance()->MaybeEnableAccessibility(this); + } else if (is_add && (child == username_field_ || child == password_field_)) { + MessageLoop::current()->PostTask(FROM_HERE, + focus_grabber_factory_.NewRunnableMethod( + &NewUserView::Layout)); } } @@ -336,67 +381,81 @@ void NewUserView::Layout() { int x = std::max(0, this->width() - insets.right() - languages_menubutton_->GetPreferredSize().width() - kColumnPad); - int y = insets.top() + kRowPad; + int y = insets.top() + kLanguagesMenuPad; int width = std::min(this->width() - insets.width() - 2 * kColumnPad, languages_menubutton_->GetPreferredSize().width()); int height = kLanguagesMenuHeight; languages_menubutton_->SetBounds(x, y, width, height); - y += height + kRowPad; + y += height + kTitlePad; width = std::min(this->width() - insets.width() - 2 * kColumnPad, kTextfieldWidth); - x = (this->width() - width) / 2; - int max_width = this->width() - x - insets.right(); + x = insets.left() + kLeftPad; + int max_width = this->width() - x - std::max(insets.right(), x); title_label_->SizeToFit(max_width); title_hint_label_->SizeToFit(max_width); // Top align title and title hint. y += setViewBounds(title_label_, x, y, max_width, false); y += setViewBounds(title_hint_label_, x, y, max_width, false); - int title_end = y; - - // Center align all other controls. - int create_account_link_height = need_create_account_ ? - create_account_link_->GetPreferredSize().height() : 0; - int browse_without_signin_link_height = need_browse_without_signin_ ? - browse_without_signin_link_->GetPreferredSize().height() : 0; + int title_end = y + kTitlePad; + + splitter_up1_->SetBounds(0, title_end, this->width(), kSplitterHeight); + splitter_up2_->SetBounds(0, title_end + 1, this->width(), kSplitterHeight); + + // Bottom controls. + int links_height = 0; + if (need_create_account_) + links_height += create_account_link_->GetPreferredSize().height(); + if (need_guest_link_) + links_height += guest_link_->GetPreferredSize().height(); + if (need_create_account_ && need_guest_link_) + links_height += kRowPad; + y = this->height() - insets.bottom() - kBottomPad; + if (links_height) + y -= links_height + kBottomPad; + int bottom_start = y; + + splitter_down1_->SetBounds(0, y, this->width(), kSplitterHeight); + splitter_down2_->SetBounds(0, y + 1, this->width(), kSplitterHeight); + + if (need_guest_link_) { + y -= setViewBounds(guest_link_, + x, y + kBottomPad, max_width, false) + kRowPad; + } + if (need_create_account_) { + y += setViewBounds(create_account_link_, x, y, max_width, false); + } + // Center main controls. height = username_field_->GetPreferredSize().height() + password_field_->GetPreferredSize().height() + sign_in_button_->GetPreferredSize().height() + - create_account_link_height + - browse_without_signin_link_height + - 5 * kRowPad; - y += (this->height() - y - height) / 2; - - int corner_radius = need_border_ ? login::kScreenCornerRadius : 0; - splitter_->SetBounds(insets.left() - corner_radius / 2, - title_end + (y - title_end) / 2, - this->width() - insets.width() + corner_radius, - kSplitterHeight); - - y += (setViewBounds(username_field_, x, y, width, true) + kRowPad); - y += (setViewBounds(password_field_, x, y, width, true) + 3 * kRowPad); + 2 * kRowPad; + y = title_end + (bottom_start - title_end - height) / 2; + + y += setViewBounds(username_field_, x, y, width, true) + kRowPad; + y += setViewBounds(password_field_, x, y, width, true) + kRowPad; + int throbber_y = y; - y += (setViewBounds(sign_in_button_, x, y, width, false) + kRowPad); + int sign_in_button_width = + std::max(login::kButtonMinWidth, + sign_in_button_->GetPreferredSize().width()); + setViewBounds(sign_in_button_, x, y, sign_in_button_width,true); setViewBounds(throbber_, x + width - throbber_->GetPreferredSize().width(), throbber_y + (sign_in_button_->GetPreferredSize().height() - throbber_->GetPreferredSize().height()) / 2, width, false); - if (need_create_account_) { - y += setViewBounds(create_account_link_, x, y, max_width, false); - } - if (need_browse_without_signin_) { - y += setViewBounds(browse_without_signin_link_, x, y, max_width, false); - } SchedulePaint(); } gfx::Size NewUserView::GetPreferredSize() { - return gfx::Size(width(), height()); + return need_guest_link_ ? + gfx::Size(kNewUserPodFullWidth, kNewUserPodFullHeight) : + gfx::Size(kNewUserPodSmallWidth, kNewUserPodSmallHeight); } void NewUserView::SetUsername(const std::string& username) { @@ -436,7 +495,7 @@ void NewUserView::ButtonPressed(views::Button* sender, void NewUserView::LinkActivated(views::Link* source, int event_flags) { if (source == create_account_link_) { delegate_->OnCreateAccount(); - } else if (source == browse_without_signin_link_) { + } else if (source == guest_link_) { delegate_->OnLoginOffTheRecord(); } } @@ -505,14 +564,16 @@ void NewUserView::EnableInputControls(bool enabled) { if (need_create_account_) { create_account_link_->SetEnabled(enabled); } - if (need_browse_without_signin_) { - browse_without_signin_link_->SetEnabled(enabled); + if (need_guest_link_) { + guest_link_->SetEnabled(enabled); } } void NewUserView::InitLink(views::Link** link) { *link = new views::Link(std::wstring()); (*link)->SetController(this); + (*link)->SetNormalColor(login::kLinkColor); + (*link)->SetHighlightedColor(login::kLinkColor); AddChildView(*link); } diff --git a/chrome/browser/chromeos/login/new_user_view.h b/chrome/browser/chromeos/login/new_user_view.h index 758d587..face22b 100644 --- a/chrome/browser/chromeos/login/new_user_view.h +++ b/chrome/browser/chromeos/login/new_user_view.h @@ -59,7 +59,7 @@ class NewUserView : public views::View, // If |need_border| is true, RoundedRect border and background are required. NewUserView(Delegate* delegate, bool need_border, - bool need_browse_without_signin); + bool need_guest_link); virtual ~NewUserView(); @@ -139,6 +139,9 @@ class NewUserView : public views::View, // button is enabled, otherwise it's disabled. void UpdateSignInButtonState(); + // Create view with specified solid background and add it as child. + views::View* CreateSplitter(SkColor color); + // Screen controls. // NOTE: sign_in_button_ and languages_menubutton_ are handled with // special care: they are recreated on any text/locale change @@ -147,10 +150,13 @@ class NewUserView : public views::View, views::Textfield* password_field_; views::Label* title_label_; views::Label* title_hint_label_; - views::View* splitter_; + views::View* splitter_up1_; + views::View* splitter_up2_; + views::View* splitter_down1_; + views::View* splitter_down2_; views::NativeButton* sign_in_button_; views::Link* create_account_link_; - views::Link* browse_without_signin_link_; + views::Link* guest_link_; views::MenuButton* languages_menubutton_; views::Throbber* throbber_; @@ -176,8 +182,8 @@ class NewUserView : public views::View, // If true, this view needs RoundedRect border and background. bool need_border_; - // Whether browse without signin is needed. - bool need_browse_without_signin_; + // Whether Guest Mode link is needed. + bool need_guest_link_; // Whether create account link is needed. Set to false for now but we may // need it back in near future. diff --git a/chrome/browser/chromeos/login/owner_manager.cc b/chrome/browser/chromeos/login/owner_manager.cc index b54aa2e..b2715f3 100644 --- a/chrome/browser/chromeos/login/owner_manager.cc +++ b/chrome/browser/chromeos/login/owner_manager.cc @@ -9,7 +9,9 @@ #include "base/file_path.h" #include "base/file_util.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" +#include "chrome/browser/chromeos/login/signed_settings_temp_storage.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" @@ -95,7 +97,13 @@ void OwnerManager::OnComplete(bool value) { &OwnerManager::SendNotification, result, NotificationService::NoDetails())); - + // We've stored some settings in transient storage + // before owner has been assigned. + // Now owner is assigned and key is generated and we should persist + // those settings into signed storage. + if (g_browser_process && g_browser_process->local_state()) { + SignedSettingsTempStorage::Finalize(g_browser_process->local_state()); + } } bool OwnerManager::EnsurePublicKey() { diff --git a/chrome/browser/chromeos/login/parallel_authenticator.cc b/chrome/browser/chromeos/login/parallel_authenticator.cc index 0ae800f..0eaa340 100644 --- a/chrome/browser/chromeos/login/parallel_authenticator.cc +++ b/chrome/browser/chromeos/login/parallel_authenticator.cc @@ -141,6 +141,7 @@ void ParallelAuthenticator::OnLoginSuccess( already_reported_success_ = true; } consumer_->OnLoginSuccess(current_state_->username, + current_state_->password, credentials, request_pending); } diff --git a/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc b/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc index ffd369c..0cac5f7 100644 --- a/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc +++ b/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc @@ -71,7 +71,8 @@ class ParallelAuthenticatorTest : public ::testing::Test { ui_thread_(BrowserThread::UI, &message_loop_), file_thread_(BrowserThread::FILE), io_thread_(BrowserThread::IO), - username_("me@nowhere.org") { + username_("me@nowhere.org"), + password_("fakepass") { hash_ascii_.assign("0a010000000000a0"); hash_ascii_.append(std::string(16, '0')); } @@ -96,7 +97,11 @@ class ParallelAuthenticatorTest : public ::testing::Test { io_thread_.Start(); auth_ = new ParallelAuthenticator(&consumer_); - state_.reset(new TestAttemptState(username_, "", hash_ascii_, "", "")); + state_.reset(new TestAttemptState(username_, + password_, + hash_ascii_, + "", + "")); } // Tears down the test fixture. @@ -152,7 +157,7 @@ class ParallelAuthenticatorTest : public ::testing::Test { // Allow test to fail and exit gracefully, even if OnLoginSuccess() // wasn't supposed to happen. void FailOnLoginSuccess() { - ON_CALL(consumer_, OnLoginSuccess(_, _, _)) + ON_CALL(consumer_, OnLoginSuccess(_, _, _, _)) .WillByDefault(Invoke(MockConsumer::OnSuccessQuitAndFail)); } @@ -170,9 +175,10 @@ class ParallelAuthenticatorTest : public ::testing::Test { } void ExpectLoginSuccess(const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& result, bool pending) { - EXPECT_CALL(consumer_, OnLoginSuccess(username, result, pending)) + EXPECT_CALL(consumer_, OnLoginSuccess(username, password, result, pending)) .WillOnce(Invoke(MockConsumer::OnSuccessQuit)) .RetiresOnSaturation(); } @@ -206,6 +212,7 @@ class ParallelAuthenticatorTest : public ::testing::Test { BrowserThread io_thread_; std::string username_; + std::string password_; std::string hash_ascii_; GaiaAuthConsumer::ClientLoginResult result_; @@ -260,7 +267,7 @@ TEST_F(ParallelAuthenticatorTest, ReadNoLocalaccount) { } TEST_F(ParallelAuthenticatorTest, OnLoginSuccess) { - EXPECT_CALL(consumer_, OnLoginSuccess(username_, result_, false)) + EXPECT_CALL(consumer_, OnLoginSuccess(username_, password_, result_, false)) .Times(1) .RetiresOnSaturation(); @@ -339,7 +346,7 @@ TEST_F(ParallelAuthenticatorTest, DriveGuestLoginButFail) { } TEST_F(ParallelAuthenticatorTest, DriveDataResync) { - ExpectLoginSuccess(username_, result_, false); + ExpectLoginSuccess(username_, password_, result_, false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a cryptohome @@ -389,7 +396,7 @@ TEST_F(ParallelAuthenticatorTest, DriveRequestOldPassword) { } TEST_F(ParallelAuthenticatorTest, DriveDataRecover) { - ExpectLoginSuccess(username_, result_, false); + ExpectLoginSuccess(username_, password_, result_, false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a key migration. @@ -461,7 +468,7 @@ TEST_F(ParallelAuthenticatorTest, ResolveCreateNew) { } TEST_F(ParallelAuthenticatorTest, DriveCreateForNewUser) { - ExpectLoginSuccess(username_, result_, false); + ExpectLoginSuccess(username_, password_, result_, false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a cryptohome @@ -485,7 +492,7 @@ TEST_F(ParallelAuthenticatorTest, DriveCreateForNewUser) { } TEST_F(ParallelAuthenticatorTest, DriveOfflineLogin) { - ExpectLoginSuccess(username_, result_, false); + ExpectLoginSuccess(username_, password_, result_, false); FailOnLoginFailure(); // Set up state as though a cryptohome mount attempt has occurred and @@ -501,7 +508,7 @@ TEST_F(ParallelAuthenticatorTest, DriveOfflineLogin) { } TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginDelayedOnline) { - ExpectLoginSuccess(username_, result_, true); + ExpectLoginSuccess(username_, password_, result_, true); FailOnLoginFailure(); // Set up state as though a cryptohome mount attempt has occurred and @@ -522,7 +529,7 @@ TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginDelayedOnline) { } TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginGetNewPassword) { - ExpectLoginSuccess(username_, result_, true); + ExpectLoginSuccess(username_, password_, result_, true); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a key migration. @@ -551,7 +558,7 @@ TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginGetNewPassword) { RunResolve(auth_.get(), &message_loop_); // After the request below completes, OnLoginSuccess gets called again. - ExpectLoginSuccess(username_, result_, false); + ExpectLoginSuccess(username_, password_, result_, false); MockFactory<SuccessFetcher> factory; URLFetcher::set_factory(&factory); @@ -567,7 +574,7 @@ TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginGetNewPassword) { TEST_F(ParallelAuthenticatorTest, DriveOnlineLogin) { GaiaAuthConsumer::ClientLoginResult success("sid", "lsid", "", ""); - ExpectLoginSuccess(username_, success, false); + ExpectLoginSuccess(username_, password_, success, false); FailOnLoginFailure(); // Set up state as though a cryptohome mount attempt has occurred and @@ -582,7 +589,7 @@ TEST_F(ParallelAuthenticatorTest, DriveOnlineLogin) { TEST_F(ParallelAuthenticatorTest, DriveNeedNewPassword) { FailOnLoginSuccess(); // Set failing on success as the default... // ...but expect ONE successful login first. - ExpectLoginSuccess(username_, result_, true); + ExpectLoginSuccess(username_, password_, result_, true); GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error); @@ -630,7 +637,7 @@ TEST_F(ParallelAuthenticatorTest, DriveLocalLogin) { } TEST_F(ParallelAuthenticatorTest, DriveUnlock) { - ExpectLoginSuccess(username_, result_, false); + ExpectLoginSuccess(username_, std::string(), result_, false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a cryptohome @@ -648,7 +655,7 @@ TEST_F(ParallelAuthenticatorTest, DriveUnlock) { } TEST_F(ParallelAuthenticatorTest, DriveLocalUnlock) { - ExpectLoginSuccess(username_, result_, false); + ExpectLoginSuccess(username_, std::string(), result_, false); FailOnLoginFailure(); // Set up mock cryptohome library to fail a cryptohome key-check diff --git a/chrome/browser/chromeos/login/proxy_settings_dialog.cc b/chrome/browser/chromeos/login/proxy_settings_dialog.cc new file mode 100644 index 0000000..69dc9c5 --- /dev/null +++ b/chrome/browser/chromeos/login/proxy_settings_dialog.cc @@ -0,0 +1,48 @@ +// 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/chromeos/login/proxy_settings_dialog.h" + +#include "chrome/browser/chromeos/login/helper.h" +#include "gfx/rect.h" +#include "gfx/size.h" + +namespace { + +// Hints for size of proxy settings dialog. +const int kProxySettingsDialogReasonableWidth = 750; +const int kProxySettingsDialogReasonableHeight = 460; +const float kProxySettingsDialogReasonableWidthRatio = 0.4f; +const float kProxySettingsDialogReasonableHeightRatio = 0.4f; + +const char kProxySettingsURL[] = "chrome://settings/proxy?menu=off"; + +int calculate_size(int screen_size, int min_comfortable, float desired_ratio) { + int desired_size = static_cast<int>(desired_ratio * screen_size); + desired_size = std::max(min_comfortable, desired_size); + return std::min(screen_size, desired_size); +} + +} // namespace + +namespace chromeos { + +ProxySettingsDialog::ProxySettingsDialog(LoginHtmlDialog::Delegate* delegate, + gfx::NativeWindow window) + : LoginHtmlDialog( + delegate, + window, + std::wstring(), + GURL(kProxySettingsURL), + LoginHtmlDialog::STYLE_BUBBLE) { + gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size())); + SetDialogSize(calculate_size(screen_bounds.width(), + kProxySettingsDialogReasonableWidth, + kProxySettingsDialogReasonableWidthRatio), + calculate_size(screen_bounds.height(), + kProxySettingsDialogReasonableHeight, + kProxySettingsDialogReasonableHeightRatio)); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/proxy_settings_dialog.h b/chrome/browser/chromeos/login/proxy_settings_dialog.h new file mode 100644 index 0000000..10d495b --- /dev/null +++ b/chrome/browser/chromeos/login/proxy_settings_dialog.h @@ -0,0 +1,25 @@ +// 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_CHROMEOS_LOGIN_PROXY_SETTINGS_DIALOG_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_PROXY_SETTINGS_DIALOG_H_ +#pragma once + +#include "chrome/browser/chromeos/login/login_html_dialog.h" +#include "gfx/native_widget_types.h" + +namespace chromeos { + +class ProxySettingsDialog : public LoginHtmlDialog { + public: + ProxySettingsDialog(LoginHtmlDialog::Delegate* delegate, + gfx::NativeWindow window); + + private: + DISALLOW_COPY_AND_ASSIGN(ProxySettingsDialog); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_PROXY_SETTINGS_DIALOG_H_ diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc index 3a08b52..7b4d6b5 100644 --- a/chrome/browser/chromeos/login/screen_locker.cc +++ b/chrome/browser/chromeos/login/screen_locker.cc @@ -10,6 +10,7 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/command_line.h" +#include "base/metrics/histogram.h" #include "base/message_loop.h" #include "base/singleton.h" #include "base/string_util.h" @@ -29,6 +30,8 @@ #include "chrome/browser/chromeos/login/login_utils.h" #include "chrome/browser/chromeos/login/message_bubble.h" #include "chrome/browser/chromeos/login/screen_lock_view.h" +#include "chrome/browser/chromeos/login/shutdown_button.h" +#include "chrome/browser/chromeos/system_key_event_listener.h" #include "chrome/browser/chromeos/wm_ipc.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/common/chrome_switches.h" @@ -223,6 +226,33 @@ class LockWindow : public views::WidgetGtk { DISALLOW_COPY_AND_ASSIGN(LockWindow); }; +// GrabWidget's root view to layout the ScreenLockView at the center +// and the Shutdown button at the right bottom. +class GrabWidgetRootView : public views::View { + public: + explicit GrabWidgetRootView(chromeos::ScreenLockView* screen_lock_view) + : screen_lock_view_(screen_lock_view), + shutdown_button_(new chromeos::ShutdownButton()) { + shutdown_button_->Init(); + AddChildView(screen_lock_view_); + AddChildView(shutdown_button_); + } + + // views::View implementation. + virtual void Layout() { + gfx::Size size = screen_lock_view_->GetPreferredSize(); + screen_lock_view_->SetBounds(0, 0, size.width(), size.height()); + shutdown_button_->LayoutIn(this); + } + + private: + views::View* screen_lock_view_; + + chromeos::ShutdownButton* shutdown_button_; + + DISALLOW_COPY_AND_ASSIGN(GrabWidgetRootView); +}; + // A child widget that grabs both keyboard and pointer input. class GrabWidget : public views::WidgetGtk { public: @@ -329,8 +359,10 @@ void GrabWidget::TryGrabAllInputs() { // addition to other background components. class ScreenLockerBackgroundView : public chromeos::BackgroundView { public: - explicit ScreenLockerBackgroundView(views::WidgetGtk* lock_widget) - : lock_widget_(lock_widget) { + ScreenLockerBackgroundView(views::WidgetGtk* lock_widget, + views::View* screen_lock_view) + : lock_widget_(lock_widget), + screen_lock_view_(screen_lock_view) { } virtual bool IsScreenLockerMode() const { @@ -340,17 +372,24 @@ class ScreenLockerBackgroundView : public chromeos::BackgroundView { virtual void Layout() { chromeos::BackgroundView::Layout(); gfx::Rect screen = bounds(); - gfx::Size size = lock_widget_->GetRootView()->GetPreferredSize(); - lock_widget_->SetBounds( - gfx::Rect((screen.width() - size.width()) / 2, - (screen.height() - size.height()) / 2, - size.width(), - size.height())); + if (screen_lock_view_) { + gfx::Size size = screen_lock_view_->GetPreferredSize(); + gfx::Point origin((screen.width() - size.width()) / 2, + (screen.height() - size.height()) / 2); + gfx::Size widget_size(screen.size()); + widget_size.Enlarge(-origin.x(), -origin.y()); + lock_widget_->SetBounds(gfx::Rect(origin, widget_size)); + } else { + // No password entry. Move the lock widget to off screen. + lock_widget_->SetBounds(gfx::Rect(-100, -100, 1, 1)); + } } private: views::WidgetGtk* lock_widget_; + views::View* screen_lock_view_; + DISALLOW_COPY_AND_ASSIGN(ScreenLockerBackgroundView); }; @@ -440,7 +479,10 @@ class InputEventObserver : public MessageLoopForUI::Observer { activated_ = true; std::string not_used_string; GaiaAuthConsumer::ClientLoginResult not_used; - screen_locker_->OnLoginSuccess(not_used_string, not_used, false); + screen_locker_->OnLoginSuccess(not_used_string, + not_used_string, + not_used, + false); } } @@ -504,7 +546,8 @@ ScreenLocker::ScreenLocker(const UserManager::User& user) // TODO(oshima): support auto login mode (this is not implemented yet) // http://crosbug.com/1881 unlock_on_input_(user_.email().empty()), - locked_(false) { + locked_(false), + start_time_(base::Time::Now()) { DCHECK(!screen_locker_); screen_locker_ = this; } @@ -535,15 +578,19 @@ void ScreenLocker::Init() { lock_widget_ = new GrabWidget(this); lock_widget_->MakeTransparent(); lock_widget_->InitWithWidget(lock_window_, gfx::Rect()); - if (screen_lock_view_) - lock_widget_->SetContentsView(screen_lock_view_); + if (screen_lock_view_) { + lock_widget_->SetContentsView( + new GrabWidgetRootView(screen_lock_view_)); + } + lock_widget_->Show(); // Configuring the background url. std::string url_string = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kScreenSaverUrl); - background_view_ = new ScreenLockerBackgroundView(lock_widget_); + background_view_ = new ScreenLockerBackgroundView(lock_widget_, + screen_lock_view_); background_view_->Init(GURL(url_string)); if (background_view_->ScreenSaverEnabled()) StartScreenSaver(); @@ -564,10 +611,23 @@ void ScreenLocker::Init() { gdk_window_set_back_pixmap(lock_widget_->GetNativeView()->window, NULL, false); lock_window->set_toplevel_focus_widget(lock_widget_->window_contents()); + + // Create the SystemKeyEventListener so it can listen for system keyboard + // messages regardless of focus while screen locked. + SystemKeyEventListener::instance(); } void ScreenLocker::OnLoginFailure(const LoginFailure& error) { DVLOG(1) << "OnLoginFailure"; + UserMetrics::RecordAction(UserMetricsAction("ScreenLocker_OnLoginFailure")); + if (authentication_start_time_.is_null()) { + LOG(ERROR) << "authentication_start_time_ is not set"; + } else { + base::TimeDelta delta = base::Time::Now() - authentication_start_time_; + VLOG(1) << "Authentication failure time: " << delta.InSecondsF(); + UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationFailureTime", delta); + } + EnableInput(); // Don't enable signout button here as we're showing // MessageBubble. @@ -608,9 +668,18 @@ void ScreenLocker::OnLoginFailure(const LoginFailure& error) { void ScreenLocker::OnLoginSuccess( const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& unused, bool pending_requests) { VLOG(1) << "OnLoginSuccess: Sending Unlock request."; + if (authentication_start_time_.is_null()) { + LOG(ERROR) << "authentication_start_time_ is not set"; + } else { + base::TimeDelta delta = base::Time::Now() - authentication_start_time_; + VLOG(1) << "Authentication success time: " << delta.InSecondsF(); + UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationSuccessTime", delta); + } + if (CrosLibrary::Get()->EnsureLoaded()) CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenUnlockRequested(); } @@ -626,10 +695,11 @@ void ScreenLocker::InfoBubbleClosing(InfoBubble* info_bubble, } void ScreenLocker::Authenticate(const string16& password) { + authentication_start_time_ = base::Time::Now(); screen_lock_view_->SetEnabled(false); screen_lock_view_->SetSignoutEnabled(false); BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, + BrowserThread::UI, FROM_HERE, NewRunnableMethod(authenticator_.get(), &Authenticator::AuthenticateToUnlock, user_.email(), @@ -652,7 +722,7 @@ void ScreenLocker::EnableInput() { void ScreenLocker::Signout() { if (!error_info_) { - // TODO(oshima): record this action in user metrics. + UserMetrics::RecordAction(UserMetricsAction("ScreenLocker_Signout")); if (CrosLibrary::Get()->EnsureLoaded()) { CrosLibrary::Get()->GetLoginLibrary()->StopSession(""); } @@ -672,6 +742,7 @@ void ScreenLocker::OnGrabInputs() { // static void ScreenLocker::Show() { VLOG(1) << "In ScreenLocker::Show"; + UserMetrics::RecordAction(UserMetricsAction("ScreenLocker_Show")); DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); // Exit fullscreen. @@ -767,6 +838,10 @@ void ScreenLocker::SetAuthenticator(Authenticator* authenticator) { void ScreenLocker::ScreenLockReady() { VLOG(1) << "ScreenLockReady: sending completed signal to power manager."; locked_ = true; + base::TimeDelta delta = base::Time::Now() - start_time_; + VLOG(1) << "Screen lock time: " << delta.InSecondsF(); + UMA_HISTOGRAM_TIMES("ScreenLocker.ScreenLockTime", delta); + if (background_view_->ScreenSaverEnabled()) { lock_widget_->GetFocusManager()->RegisterAccelerator( views::Accelerator(app::VKEY_ESCAPE, false, false, false), this); @@ -817,6 +892,8 @@ void ScreenLocker::StopScreenSaver() { void ScreenLocker::StartScreenSaver() { if (!background_view_->IsScreenSaverVisible()) { VLOG(1) << "StartScreenSaver"; + UserMetrics::RecordAction( + UserMetricsAction("ScreenLocker_StartScreenSaver")); background_view_->ShowScreenSaver(); if (screen_lock_view_) { screen_lock_view_->SetEnabled(false); diff --git a/chrome/browser/chromeos/login/screen_locker.h b/chrome/browser/chromeos/login/screen_locker.h index 929a026..a4a40e7 100644 --- a/chrome/browser/chromeos/login/screen_locker.h +++ b/chrome/browser/chromeos/login/screen_locker.h @@ -9,6 +9,7 @@ #include <string> #include "base/task.h" +#include "base/time.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" #include "chrome/browser/chromeos/login/message_bubble.h" #include "chrome/browser/chromeos/login/user_manager.h" @@ -52,6 +53,7 @@ class ScreenLocker : public LoginStatusConsumer, // LoginStatusConsumer implements: virtual void OnLoginFailure(const chromeos::LoginFailure& error); virtual void OnLoginSuccess(const std::string& username, + const std::string& password, const GaiaAuthConsumer::ClientLoginResult& result, bool pending_requests); @@ -181,6 +183,11 @@ class ScreenLocker : public LoginStatusConsumer, // This is used to make sure there is only one screen locker instance. static ScreenLocker* screen_locker_; + // The time when the screen locker object is created. + base::Time start_time_; + // The time when the authenticaton is started. + base::Time authentication_start_time_; + DISALLOW_COPY_AND_ASSIGN(ScreenLocker); }; diff --git a/chrome/browser/chromeos/login/shutdown_button.cc b/chrome/browser/chromeos/login/shutdown_button.cc new file mode 100644 index 0000000..5e79c40 --- /dev/null +++ b/chrome/browser/chromeos/login/shutdown_button.cc @@ -0,0 +1,131 @@ +// Copyright (c) 2011 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/chromeos/login/shutdown_button.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/power_library.h" +#include "chrome/browser/chromeos/login/rounded_rect_painter.h" +#include "chrome/browser/chromeos/view_ids.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "views/background.h" + +namespace { + +// Style parameters for Shutdown button. + +// Bottom/Right padding to locale the shutdown button. +const int kBottomPadding = 12; +const int kRightPadding = 12; + +// Normal/Hover colors. +const SkColor kButtonColor = 0xFF242A35; +const SkColor kHoverColor = 0xFF353E4E; + +// Padding inside button. +const int kVerticalPadding = 13; +const int kIconTextPadding = 10; +const int kHorizontalPadding = 13; + +// Rounded corner radious. +const int kCornerRadius = 4; + +class HoverBackground : public views::Background { + public: + HoverBackground(views::Background* normal, views::Background* hover) + : normal_(normal), hover_(hover) { + } + + // views::Background implementation. + virtual void Paint(gfx::Canvas* canvas, views::View* view) const { + views::TextButton* button = static_cast<views::TextButton*>(view); + if (button->state() == views::CustomButton::BS_HOT) { + hover_->Paint(canvas, view); + } else { + normal_->Paint(canvas, view); + } + } + + private: + views::Background* normal_; + views::Background* hover_; + + DISALLOW_COPY_AND_ASSIGN(HoverBackground); +}; + +} // namespace + +namespace chromeos { + +ShutdownButton::ShutdownButton() + : ALLOW_THIS_IN_INITIALIZER_LIST(TextButton(this, std::wstring())) { +} + +void ShutdownButton::Init() { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + SetIcon(*rb.GetBitmapNamed(IDR_SHUTDOWN_ICON)); + set_icon_text_spacing(kIconTextPadding); + SetFocusable(true); + // Set label colors. + SetEnabledColor(SK_ColorWHITE); + SetDisabledColor(SK_ColorWHITE); + SetHighlightColor(SK_ColorWHITE); + SetHoverColor(SK_ColorWHITE); + // Disable throbbing and make border always visible. + SetAnimationDuration(0); + SetNormalHasBorder(true); + // Setup round shapes. + set_background( + new HoverBackground( + CreateRoundedBackground( + kCornerRadius, 0, kButtonColor, 0), + CreateRoundedBackground( + kCornerRadius, 0, kHoverColor, 0))); + set_border( + views::Border::CreateEmptyBorder(kVerticalPadding, + kHorizontalPadding, + kVerticalPadding, + kHorizontalPadding)); + OnLocaleChanged(); // set text +} + +void ShutdownButton::LayoutIn(views::View* parent) { + // No RTL for now. RTL will be handled in new + // domui based Login/Locker. + gfx::Size button_size = GetPreferredSize(); + SetBounds( + parent->width() - button_size.width()- kRightPadding, + parent->height() - button_size.height() - kBottomPadding, + button_size.width(), + button_size.height()); +} + +void ShutdownButton::OnLocaleChanged() { + SetText(UTF8ToWide(l10n_util::GetStringUTF8(IDS_SHUTDOWN_BUTTON))); + if (GetParent()) { + GetParent()->Layout(); + GetParent()->SchedulePaint(); + } +} + +gfx::NativeCursor ShutdownButton::GetCursorForPoint( + views::Event::EventType event_type, + const gfx::Point& p) { + if (!IsEnabled()) { + return NULL; + } + return gdk_cursor_new(GDK_HAND2); +} + +void ShutdownButton::ButtonPressed(views::Button* sender, + const views::Event& event) { + DCHECK(CrosLibrary::Get()->EnsureLoaded()); + CrosLibrary::Get()->GetPowerLibrary()->RequestShutdown(); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/shutdown_button.h b/chrome/browser/chromeos/login/shutdown_button.h new file mode 100644 index 0000000..dd0a65a --- /dev/null +++ b/chrome/browser/chromeos/login/shutdown_button.h @@ -0,0 +1,40 @@ +// Copyright (c) 2011 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_CHROMEOS_LOGIN_SHUTDOWN_BUTTON_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_SHUTDOWN_BUTTON_H_ +#pragma once + +#include "views/controls/button/text_button.h" + +namespace chromeos { + +class ShutdownButton : public views::TextButton, + public views::ButtonListener { + public: + ShutdownButton(); + + // Initializes shutdown button. + void Init(); + + // Layout the shutdown button at the right bottom corner of + // |parent|. + void LayoutIn(views::View* parent); + + private: + // views::View overrides. + virtual void OnLocaleChanged(); + virtual gfx::NativeCursor GetCursorForPoint( + views::Event::EventType event_type, + const gfx::Point& p); + + // views::ButtonListener implementation. + virtual void ButtonPressed(views::Button* sender, const views::Event& event); + + DISALLOW_COPY_AND_ASSIGN(ShutdownButton); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_SHUTDOWN_BUTTON_H diff --git a/chrome/browser/chromeos/login/signed_settings.cc b/chrome/browser/chromeos/login/signed_settings.cc index 524acab..cef4fa1 100644 --- a/chrome/browser/chromeos/login/signed_settings.cc +++ b/chrome/browser/chromeos/login/signed_settings.cc @@ -9,10 +9,12 @@ #include "base/ref_counted.h" #include "base/stringprintf.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/login_library.h" #include "chrome/browser/chromeos/login/ownership_service.h" +#include "chrome/browser/chromeos/login/signed_settings_temp_storage.h" namespace chromeos { @@ -99,6 +101,7 @@ class RetrievePropertyOp : public SignedSettings { SignedSettings* SignedSettings::CreateCheckWhitelistOp( const std::string& email, SignedSettings::Delegate<bool>* d) { + DCHECK(d != NULL); return new CheckWhitelistOp(email, d); } @@ -107,6 +110,7 @@ SignedSettings* SignedSettings::CreateWhitelistOp( const std::string& email, bool add_to_whitelist, SignedSettings::Delegate<bool>* d) { + DCHECK(d != NULL); return new WhitelistOp(email, add_to_whitelist, d); } @@ -115,6 +119,7 @@ SignedSettings* SignedSettings::CreateStorePropertyOp( const std::string& name, const std::string& value, SignedSettings::Delegate<bool>* d) { + DCHECK(d != NULL); return new StorePropertyOp(name, value, d); } @@ -122,6 +127,7 @@ SignedSettings* SignedSettings::CreateStorePropertyOp( SignedSettings* SignedSettings::CreateRetrievePropertyOp( const std::string& name, SignedSettings::Delegate<std::string>* d) { + DCHECK(d != NULL); return new RetrievePropertyOp(name, d); } @@ -224,6 +230,15 @@ StorePropertyOp::StorePropertyOp(const std::string& name, StorePropertyOp::~StorePropertyOp() {} bool StorePropertyOp::Execute() { + if (!service_->IsAlreadyOwned()) { + if (g_browser_process && + g_browser_process->local_state() && + SignedSettingsTempStorage::Store(name_, value_, + g_browser_process->local_state())) { + d_->OnSettingsOpSucceeded(true); + return true; + } + } // Posts a task to the FILE thread to sign |name_|=|value_|. std::string to_sign = base::StringPrintf("%s=%s", name_.c_str(), @@ -273,6 +288,24 @@ RetrievePropertyOp::~RetrievePropertyOp() {} bool RetrievePropertyOp::Execute() { CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded()); + // TODO(dilmah): Fix the race: + // At the moment when device becomes owned there is lapse of time after + // device has been owned and before temp_storage settings are finally + // persisted into signed settings. + // In this lapse of time Retrieve loses access to those settings. + if (!service_->IsAlreadyOwned()) { + if (g_browser_process && + g_browser_process->local_state() && + SignedSettingsTempStorage::Retrieve( + name_, &value_, g_browser_process->local_state())) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, + &RetrievePropertyOp::OnKeyOpComplete, + OwnerManager::SUCCESS, std::vector<uint8>())); + return true; + } + } std::vector<uint8> sig; if (!CrosLibrary::Get()->GetLoginLibrary()->RetrieveProperty(name_, &value_, diff --git a/chrome/browser/chromeos/login/signed_settings.h b/chrome/browser/chromeos/login/signed_settings.h index 8882d7e..d1bdc33 100644 --- a/chrome/browser/chromeos/login/signed_settings.h +++ b/chrome/browser/chromeos/login/signed_settings.h @@ -39,8 +39,8 @@ class SignedSettings : public base::RefCountedThreadSafe<SignedSettings>, class Delegate { public: // These methods will be called on the UI thread. - virtual void OnSettingsOpSucceeded(T value) = 0; - virtual void OnSettingsOpFailed() = 0; + virtual void OnSettingsOpSucceeded(T value) {} + virtual void OnSettingsOpFailed() {} }; SignedSettings(); diff --git a/chrome/browser/chromeos/login/signed_settings_temp_storage.cc b/chrome/browser/chromeos/login/signed_settings_temp_storage.cc new file mode 100644 index 0000000..d88b075 --- /dev/null +++ b/chrome/browser/chromeos/login/signed_settings_temp_storage.cc @@ -0,0 +1,77 @@ +// 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/chromeos/login/signed_settings_temp_storage.h" + +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/login/ownership_service.h" +#include "chrome/browser/chromeos/login/signed_settings.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/common/pref_names.h" + +namespace chromeos { + +// static +void SignedSettingsTempStorage::RegisterPrefs(PrefService* local_state) { + local_state->RegisterDictionaryPref(prefs::kSignedSettingsTempStorage); +} + +// static +bool SignedSettingsTempStorage::Store(const std::string& name, + const std::string& value, + PrefService* local_state) { + if (local_state) { + DictionaryValue* temp_storage = + local_state->GetMutableDictionary(prefs::kSignedSettingsTempStorage); + if (temp_storage) { + temp_storage->SetWithoutPathExpansion(name, + Value::CreateStringValue(value)); + return true; + } + } + return false; +} + +// static +bool SignedSettingsTempStorage::Retrieve(const std::string& name, + std::string* value, + PrefService* local_state) { + if (local_state) { + const DictionaryValue* temp_storage = + local_state->GetDictionary(prefs::kSignedSettingsTempStorage); + if (temp_storage && temp_storage->HasKey(name)) { + temp_storage->GetStringWithoutPathExpansion(name, value); + return true; + } + } + return false; +} + +// static +void SignedSettingsTempStorage::Finalize(PrefService* local_state) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (local_state) { + const DictionaryValue* temp_storage = + local_state->GetDictionary(prefs::kSignedSettingsTempStorage); + if (temp_storage) { + // We've stored some settings in transient storage + // before owner has been assigned. + // Now owner is assigned and key is generated and we should persist + // those settings into signed storage. + for (DictionaryValue::key_iterator it = temp_storage->begin_keys(); + it != temp_storage->end_keys(); + ++it) { + std::string value; + temp_storage->GetStringWithoutPathExpansion(*it, &value); + SignedSettings::CreateStorePropertyOp( + *it, value, + Singleton< SignedSettings::Delegate<bool> >::get())->Execute(); + } + local_state->ClearPref(prefs::kSignedSettingsTempStorage); + } + } +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/signed_settings_temp_storage.h b/chrome/browser/chromeos/login/signed_settings_temp_storage.h new file mode 100644 index 0000000..f7ae0f5 --- /dev/null +++ b/chrome/browser/chromeos/login/signed_settings_temp_storage.h @@ -0,0 +1,44 @@ +// 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_CHROMEOS_LOGIN_SIGNED_SETTINGS_TEMP_STORAGE_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_SIGNED_SETTINGS_TEMP_STORAGE_H_ +#pragma once + +#include <string> + +#include "base/basictypes.h" + +class PrefService; + +namespace chromeos { + +// There is need (proxy settings at OOBE stage) to store settings +// (that are normally go into SignedSettings storage) +// before owner has been assigned (hence no key is available). +// This class serves as a transient storage in that case. +class SignedSettingsTempStorage { + public: + // Registers required pref section. + static void RegisterPrefs(PrefService* local_state); + + static bool Store(const std::string& name, + const std::string& value, + PrefService* local_state); + static bool Retrieve(const std::string& name, + std::string* value, + PrefService* local_state); + + // Call this after owner has been assigned to persist settings + // into SignedSettings storage. + static void Finalize(PrefService* local_state); + + private: + SignedSettingsTempStorage() {} + DISALLOW_COPY_AND_ASSIGN(SignedSettingsTempStorage); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_SIGNED_SETTINGS_TEMP_STORAGE_H_ diff --git a/chrome/browser/chromeos/login/signed_settings_temp_storage_unittest.cc b/chrome/browser/chromeos/login/signed_settings_temp_storage_unittest.cc new file mode 100644 index 0000000..e92917b --- /dev/null +++ b/chrome/browser/chromeos/login/signed_settings_temp_storage_unittest.cc @@ -0,0 +1,78 @@ +// 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/chromeos/login/signed_settings_temp_storage.h" + +#include <algorithm> +#include <iterator> +#include <map> +#include <vector> + +#include "base/file_util.h" +#include "base/scoped_ptr.h" +#include "base/scoped_temp_dir.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/common/logging_chrome.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +class SignedSettingsTempStorageTest : public ::testing::Test { + protected: + virtual void SetUp() { + ref_map_["some_stuff"] = "a=35;code=64"; + ref_map_["another_stuff"] = ""; + ref_map_["name"] = "value"; + ref_map_["2bc6aa16-e0ea-11df-b13d-18a90520e2e5"] = "512"; + + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + FilePath temp_file; + ASSERT_TRUE( + file_util::CreateTemporaryFileInDir(temp_dir_.path(), &temp_file)); + local_state_.reset(PrefService::CreatePrefService(temp_file, NULL)); + ASSERT_TRUE(NULL != local_state_.get()); + SignedSettingsTempStorage::RegisterPrefs(local_state_.get()); + } + + std::map<std::string, std::string> ref_map_; + ScopedTempDir temp_dir_; + scoped_ptr<PrefService> local_state_; +}; + +TEST_F(SignedSettingsTempStorageTest, Basic) { + EXPECT_GT(ref_map_.size(), 3u); // Number above 3 is many. + typedef std::map<std::string, std::string>::iterator It; + std::vector<It> a_list; + for (It it = ref_map_.begin(); it != ref_map_.end(); ++it) { + a_list.push_back(it); + } + std::random_shuffle(a_list.begin(), a_list.end()); + std::vector<It> b_list(a_list); + std::copy(b_list.begin(), + b_list.begin() + b_list.size() / 2, + std::back_inserter(a_list)); + std::random_shuffle(a_list.begin(), a_list.end()); + for (size_t i = 0; i < a_list.size(); ++i) { + EXPECT_TRUE(SignedSettingsTempStorage::Store(a_list[i]->first, + a_list[i]->second, + local_state_.get())); + } + for (int i = 0; i < 3; ++i) { + std::copy(a_list.begin(), a_list.end(), std::back_inserter(b_list)); + } + std::random_shuffle(b_list.begin(), b_list.end()); + std::string value; + for (size_t i = 0; i < b_list.size(); ++i) { + EXPECT_TRUE(SignedSettingsTempStorage::Retrieve(b_list[i]->first, &value, + local_state_.get())); + EXPECT_EQ(b_list[i]->second, value); + EXPECT_FALSE(SignedSettingsTempStorage::Retrieve("non-existent tv-series", + &value, + local_state_.get())); + } +} + +} // namespace chromeos + diff --git a/chrome/browser/chromeos/login/textfield_with_margin.cc b/chrome/browser/chromeos/login/textfield_with_margin.cc new file mode 100644 index 0000000..7f8edd2 --- /dev/null +++ b/chrome/browser/chromeos/login/textfield_with_margin.cc @@ -0,0 +1,22 @@ +// 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/chromeos/login/textfield_with_margin.h" + +namespace { + +// Holds ratio of the margin to the preferred text height. +const double kTextMarginRate = 0.33; + +} // namespace + +namespace chromeos { + +void TextfieldWithMargin::Layout() { + int margin = GetPreferredSize().height() * kTextMarginRate; + SetHorizontalMargins(margin, margin); + views::Textfield::Layout(); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/textfield_with_margin.h b/chrome/browser/chromeos/login/textfield_with_margin.h new file mode 100644 index 0000000..08817fb --- /dev/null +++ b/chrome/browser/chromeos/login/textfield_with_margin.h @@ -0,0 +1,31 @@ +// 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_CHROMEOS_LOGIN_TEXTFIELD_WITH_MARGIN_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_TEXTFIELD_WITH_MARGIN_H_ +#pragma once + +#include "views/controls/textfield/textfield.h" + +namespace chromeos { + +// Class that represents textfield with margin that is proportional to the text +// height. +class TextfieldWithMargin : public views::Textfield { + public: + TextfieldWithMargin() {}; + + explicit TextfieldWithMargin(views::Textfield::StyleFlags style) + : Textfield(style) {} + + // Reimplements views::Textfield. + virtual void Layout(); + + private: + DISALLOW_COPY_AND_ASSIGN(TextfieldWithMargin); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_TEXTFIELD_WITH_MARGIN_H_ diff --git a/chrome/browser/chromeos/login/update_screen.cc b/chrome/browser/chromeos/login/update_screen.cc index 79288b5..6f58ac1 100644 --- a/chrome/browser/chromeos/login/update_screen.cc +++ b/chrome/browser/chromeos/login/update_screen.cc @@ -8,6 +8,7 @@ #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/login/screen_observer.h" #include "chrome/browser/chromeos/login/update_view.h" +#include "chrome/browser/chromeos/login/wizard_controller.h" namespace { @@ -78,6 +79,8 @@ void UpdateScreen::UpdateStatusChanged(UpdateLibrary* library) { view()->SetProgress(kBeforeFinalizingProgress); break; case UPDATE_STATUS_UPDATED_NEED_REBOOT: + // Make sure that first OOBE stage won't be shown after reboot. + WizardController::MarkOobeCompleted(); view()->SetProgress(kProgressComplete); view()->ShowCurtain(false); CrosLibrary::Get()->GetUpdateLibrary()->RebootAfterUpdate(); diff --git a/chrome/browser/chromeos/login/update_screen_browsertest.cc b/chrome/browser/chromeos/login/update_screen_browsertest.cc index 73e08fc..ebf09dd 100644 --- a/chrome/browser/chromeos/login/update_screen_browsertest.cc +++ b/chrome/browser/chromeos/login/update_screen_browsertest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "chrome/browser/chromeos/cros/mock_login_library.h" +#include "chrome/browser/chromeos/cros/mock_network_library.h" #include "chrome/browser/chromeos/cros/mock_update_library.h" #include "chrome/browser/chromeos/login/mock_screen_observer.h" #include "chrome/browser/chromeos/login/update_screen.h" @@ -20,7 +21,8 @@ class UpdateScreenTest : public WizardInProcessBrowserTest { public: UpdateScreenTest() : WizardInProcessBrowserTest("update"), mock_login_library_(NULL), - mock_update_library_(NULL) {} + mock_update_library_(NULL), + mock_network_library_(NULL) {} protected: virtual void SetUpInProcessBrowserTestFixture() { @@ -47,6 +49,15 @@ class UpdateScreenTest : public WizardInProcessBrowserTest { EXPECT_CALL(*mock_update_library_, CheckForUpdate()) .Times(1) .WillOnce(Return(true)); + + mock_network_library_ = cros_mock_->mock_network_library(); + EXPECT_CALL(*mock_network_library_, Connected()) + .Times(1) // also called by NetworkMenu::InitMenuItems() + .WillRepeatedly((Return(false))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_network_library_, AddNetworkManagerObserver(_)) + .Times(1) + .RetiresOnSaturation(); } virtual void TearDownInProcessBrowserTestFixture() { @@ -56,6 +67,7 @@ class UpdateScreenTest : public WizardInProcessBrowserTest { MockLoginLibrary* mock_login_library_; MockUpdateLibrary* mock_update_library_; + MockNetworkLibrary* mock_network_library_; private: DISALLOW_COPY_AND_ASSIGN(UpdateScreenTest); diff --git a/chrome/browser/chromeos/login/user_controller.cc b/chrome/browser/chromeos/login/user_controller.cc index fe63dc5..7671af4 100644 --- a/chrome/browser/chromeos/login/user_controller.cc +++ b/chrome/browser/chromeos/login/user_controller.cc @@ -47,7 +47,7 @@ const int kUserNameGap = 4; // Approximate height of controls window, this constant is used in new user // case to make border window size close to existing users. -const int kControlsHeight = 26; +const int kControlsHeight = 28; // Widget that notifies window manager about clicking on itself. // Doesn't send anything if user is selected. @@ -84,6 +84,7 @@ const int UserController::kPadding = 20; // Max size needed when an entry is not selected. const int UserController::kUnselectedSize = 100; +const int UserController::kNewUserUnselectedSize = 42; UserController::UserController(Delegate* delegate, bool is_guest) : user_index_(-1), @@ -162,10 +163,12 @@ void UserController::Init(int index, int total_user_count, bool need_browse_without_signin) { int controls_height = 0; + int controls_width = 0; controls_window_ = - CreateControlsWindow(index, &controls_height, need_browse_without_signin); + CreateControlsWindow(index, &controls_width, &controls_height, + need_browse_without_signin); image_window_ = CreateImageWindow(index); - CreateBorderWindow(index, total_user_count, controls_height); + CreateBorderWindow(index, total_user_count, controls_width, controls_height); label_window_ = CreateLabelWindow(index, WM_IPC_WINDOW_LOGIN_LABEL); unselected_label_window_ = CreateLabelWindow(index, WM_IPC_WINDOW_LOGIN_UNSELECTED_LABEL); @@ -292,29 +295,9 @@ void UserController::IsActiveChanged(bool active) { delegate_->OnUserSelected(this); user_view_->SetRemoveButtonVisible( !is_new_user_ && !is_guest_ && !is_owner_); - // Background is NULL for inactive new user pod to make it transparent. - if (is_new_user_ && !border_window_->GetRootView()->background()) { - views::Painter* painter = CreateWizardPainter( - &BorderDefinition::kUserBorder); - border_window_->GetRootView()->set_background( - views::Background::CreateBackgroundPainter(true, painter)); - border_window_->GetRootView()->SchedulePaint(); - } } else { user_view_->SetRemoveButtonVisible(false); delegate_->ClearErrors(); - if (is_new_user_) { - gfx::Rect controls_bounds; - controls_window_->GetBounds(&controls_bounds, true); - gfx::Rect screen_bounds = - views::Screen::GetMonitorWorkAreaNearestWindow(NULL); - // The windows was moved out of screen so the pod was really deactivated, - // otherwise it just some dialog was shown and took focus. - if (!screen_bounds.Intersects(controls_bounds)) { - border_window_->GetRootView()->set_background(NULL); - border_window_->GetRootView()->SchedulePaint(); - } - } } } @@ -343,12 +326,12 @@ void UserController::ConfigureLoginWindow(WidgetGtk* window, WidgetGtk* UserController::CreateControlsWindow( int index, - int* height, + int* width, int* height, bool need_browse_without_signin) { views::View* control_view; if (is_new_user_) { new_user_view_ = - new NewUserView(this, false, need_browse_without_signin); + new NewUserView(this, true, need_browse_without_signin); new_user_view_->Init(); control_view = new_user_view_; } else if (is_guest_) { @@ -362,13 +345,18 @@ WidgetGtk* UserController::CreateControlsWindow( } *height = kControlsHeight; - if (is_new_user_) - *height += kUserImageSize + kUserNameGap; + *width = kUserImageSize; + if (is_new_user_) { + DCHECK(new_user_view_); + gfx::Size size = new_user_view_->GetPreferredSize(); + *width = size.width(); + *height = size.height(); + } WidgetGtk* window = new WidgetGtk(WidgetGtk::TYPE_WINDOW); ConfigureLoginWindow(window, index, - gfx::Rect(kUserImageSize, *height), + gfx::Rect(*width, *height), WM_IPC_WINDOW_LOGIN_CONTROLS, control_view); return window; @@ -399,13 +387,15 @@ WidgetGtk* UserController::CreateImageWindow(int index) { void UserController::CreateBorderWindow(int index, int total_user_count, + int controls_width, int controls_height) { // New user login controls window is much higher than existing user's controls // window so window manager will place the control instead of image window. - int width = kBorderSize * 2 + kUserImageSize; + int width = kBorderSize * 2 + controls_width; int height = kBorderSize * 2 + controls_height; if (!is_new_user_) height += kBorderSize + kUserImageSize; + border_window_ = new WidgetGtk(WidgetGtk::TYPE_WINDOW); border_window_->MakeTransparent(); border_window_->Init(NULL, gfx::Rect(0, 0, width, height)); @@ -428,7 +418,7 @@ void UserController::UpdateUserCount(int index, int total_user_count) { std::vector<int> params; params.push_back(index); params.push_back(total_user_count); - params.push_back(kUnselectedSize); + params.push_back(is_new_user_ ? kNewUserUnselectedSize : kUnselectedSize); params.push_back(kPadding); WmIpc::instance()->SetWindowType( border_window_->GetNativeView(), @@ -475,6 +465,10 @@ WidgetGtk* UserController::CreateLabelWindow(int index, int width = (type == WM_IPC_WINDOW_LOGIN_LABEL) ? kUserImageSize : kUnselectedSize; + if (is_new_user_) { + // Make label as small as possible to don't show tooltip. + width = 0; + } int height = label->GetPreferredSize().height(); WidgetGtk* window = new ClickNotifyingWidget(WidgetGtk::TYPE_WINDOW, this); ConfigureLoginWindow(window, diff --git a/chrome/browser/chromeos/login/user_controller.h b/chrome/browser/chromeos/login/user_controller.h index d864808..2ae08f1 100644 --- a/chrome/browser/chromeos/login/user_controller.h +++ b/chrome/browser/chromeos/login/user_controller.h @@ -142,6 +142,7 @@ class UserController : public views::ButtonListener, // Max size needed when an entry is not selected. static const int kUnselectedSize; + static const int kNewUserUnselectedSize; private: // Invoked when the user wants to login. Forwards the call to the delegate. @@ -154,11 +155,13 @@ class UserController : public views::ButtonListener, chromeos::WmIpcWindowType type, views::View* contents_view); views::WidgetGtk* CreateControlsWindow(int index, - int* height, - bool need_browse_without_signin); + int* width, int* height, + bool need_guest_link); views::WidgetGtk* CreateImageWindow(int index); views::WidgetGtk* CreateLabelWindow(int index, WmIpcWindowType type); - void CreateBorderWindow(int index, int total_user_count, int controls_height); + void CreateBorderWindow(int index, + int total_user_count, + int controls_width, int controls_height); // Sets specified image on the image window. If image's size is less than // 75% of window size, image size is preserved to avoid blur. Otherwise, diff --git a/chrome/browser/chromeos/login/user_image_screen.cc b/chrome/browser/chromeos/login/user_image_screen.cc index b54c47d..9016652 100644 --- a/chrome/browser/chromeos/login/user_image_screen.cc +++ b/chrome/browser/chromeos/login/user_image_screen.cc @@ -4,6 +4,7 @@ #include "chrome/browser/chromeos/login/user_image_screen.h" +#include "app/resource_bundle.h" #include "base/compiler_specific.h" #include "base/time.h" #include "chrome/browser/chromeos/login/login_utils.h" @@ -13,6 +14,7 @@ #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" +#include "grit/theme_resources.h" namespace chromeos { @@ -22,20 +24,29 @@ namespace { const int kFrameWidth = 480; const int kFrameHeight = 480; -// Frame rate in milliseconds for video capturing. -// We want 25 FPS. -const int64 kFrameRate = 40; +// Maximum number of capture failures we ignore before we try to initialize +// the camera again. +const int kMaxCaptureFailureCounter = 5; + +// Maximum number of camera initialization retries. +const int kMaxCameraInitFailureCounter = 3; + +// Name for camera thread. +const char kCameraThreadName[] = "Chrome_CameraThread"; } // namespace UserImageScreen::UserImageScreen(WizardScreenDelegate* delegate) : ViewScreen<UserImageView>(delegate), - ALLOW_THIS_IN_INITIALIZER_LIST(camera_(new Camera(this, true))), - camera_initialized_(false) { + capture_failure_counter_(0), + camera_init_failure_counter_(0), + camera_thread_(kCameraThreadName) { registrar_.Add( this, NotificationType::SCREEN_LOCK_STATE_CHANGED, NotificationService::AllSources()); + camera_thread_.Start(); + camera_ = new Camera(this, &camera_thread_, true); camera_->Initialize(kFrameWidth, kFrameHeight); } @@ -45,12 +56,10 @@ UserImageScreen::~UserImageScreen() { } void UserImageScreen::Refresh() { - if (camera_.get() && camera_initialized_) - camera_->StartCapturing(kFrameRate); } void UserImageScreen::Hide() { - if (camera_.get() && camera_initialized_) + if (camera_.get()) camera_->StopCapturing(); ViewScreen<UserImageView>::Hide(); } @@ -60,36 +69,50 @@ UserImageView* UserImageScreen::AllocateView() { } void UserImageScreen::OnInitializeSuccess() { - camera_initialized_ = true; if (camera_.get()) - camera_->StartCapturing(kFrameRate); + camera_->StartCapturing(); } void UserImageScreen::OnInitializeFailure() { - if (view()) - view()->ShowCameraError(); - camera_initialized_ = false; + ++camera_init_failure_counter_; + if (camera_init_failure_counter_ > kMaxCameraInitFailureCounter) { + if (view()) + view()->ShowCameraError(); + return; + } + // Retry initializing the camera. + if (camera_.get()) { + camera_->Uninitialize(); + camera_->Initialize(kFrameWidth, kFrameHeight); + } } void UserImageScreen::OnStartCapturingSuccess() { } void UserImageScreen::OnStartCapturingFailure() { - if (view()) - view()->ShowCameraError(); + // Try to reinitialize camera. + OnInitializeFailure(); } -void UserImageScreen::OnCaptureSuccess(const SkBitmap& frame) { - if (view()) - view()->UpdateVideoFrame(frame); +void UserImageScreen::OnCaptureSuccess() { + capture_failure_counter_ = 0; + camera_init_failure_counter_ = 0; + if (view() && camera_.get()) { + SkBitmap frame; + camera_->GetFrame(&frame); + if (!frame.isNull()) + view()->UpdateVideoFrame(frame); + } } void UserImageScreen::OnCaptureFailure() { - // If camera failed to provide a picture we don't want to show broken - // camera image since it may lead to flicker if capturing fails and then - // works again. - // TODO(avayvod): Find a better way to handle such cases. - VLOG(1) << "Capturing image failed."; + ++capture_failure_counter_; + if (capture_failure_counter_ < kMaxCaptureFailureCounter) + return; + + capture_failure_counter_ = 0; + OnInitializeFailure(); } void UserImageScreen::OnOK(const SkBitmap& image) { @@ -129,9 +152,9 @@ void UserImageScreen::Observe(NotificationType type, bool is_screen_locked = *Details<bool>(details).ptr(); if (is_screen_locked) - camera_->StopCapturing(); + camera_->Uninitialize(); else - camera_->StartCapturing(kFrameRate); + camera_->Initialize(kFrameWidth, kFrameHeight); } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_image_screen.h b/chrome/browser/chromeos/login/user_image_screen.h index c867023..ce02047 100644 --- a/chrome/browser/chromeos/login/user_image_screen.h +++ b/chrome/browser/chromeos/login/user_image_screen.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_SCREEN_H_ #pragma once +#include "base/thread.h" #include "chrome/browser/chromeos/login/camera.h" #include "chrome/browser/chromeos/login/user_image_view.h" #include "chrome/browser/chromeos/login/view_screen.h" @@ -33,7 +34,7 @@ class UserImageScreen: public ViewScreen<UserImageView>, virtual void OnInitializeFailure(); virtual void OnStartCapturingSuccess(); virtual void OnStartCapturingFailure(); - virtual void OnCaptureSuccess(const SkBitmap& frame); + virtual void OnCaptureSuccess(); virtual void OnCaptureFailure(); // UserImageView::Delegate implementation: @@ -46,11 +47,20 @@ class UserImageScreen: public ViewScreen<UserImageView>, const NotificationDetails& details); private: + // Capturing timer callback that updates image from camera. + void OnCaptureTimer(); + // Object that handles video capturing. scoped_refptr<Camera> camera_; - // Indicates if camera is initialized. - bool camera_initialized_; + // Counts how many times in a row capture failed. + int capture_failure_counter_; + + // Counts how many times camera initialization failed. + int camera_init_failure_counter_; + + // Thread for camera to work on. + base::Thread camera_thread_; NotificationRegistrar registrar_; diff --git a/chrome/browser/chromeos/login/user_image_view.cc b/chrome/browser/chromeos/login/user_image_view.cc index 755d143..6256a62 100644 --- a/chrome/browser/chromeos/login/user_image_view.cc +++ b/chrome/browser/chromeos/login/user_image_view.cc @@ -33,8 +33,6 @@ const int kVerticalMargin = 10; const int kHorizontalPadding = 10; // Padding between vertically neighboring elements. const int kVerticalPadding = 10; -// Size for user image shown on the screen. -const int kUserImageSize = 256; // IDs of column sets for grid layout manager. enum ColumnSets { diff --git a/chrome/browser/chromeos/login/user_manager.cc b/chrome/browser/chromeos/login/user_manager.cc index 36f3163..74aad31 100644 --- a/chrome/browser/chromeos/login/user_manager.cc +++ b/chrome/browser/chromeos/login/user_manager.cc @@ -99,6 +99,15 @@ void SaveImageToFile(const SkBitmap& image, username, image_path.value())); } +// Deletes user's image file. Runs on FILE thread. +void DeleteUserImage(const FilePath& image_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + if (!file_util::Delete(image_path, false)) { + LOG(ERROR) << "Failed to remove user image."; + return; + } +} + // Checks if given path is one of the default ones. If it is, returns true // and its index in kDefaultImageNames through |image_id|. If not, returns // false. @@ -262,6 +271,7 @@ void UserManager::RemoveUser(const std::string& email) { // Clear the prefs view of the users. PrefService* prefs = g_browser_process->local_state(); ListValue* prefs_users = prefs->GetMutableList(kLoggedInUsers); + DCHECK(prefs_users); prefs_users->Clear(); for (std::vector<User>::iterator it = users.begin(); @@ -272,7 +282,24 @@ void UserManager::RemoveUser(const std::string& email) { if (email != user_email) prefs_users->Append(Value::CreateStringValue(user_email)); } + + DictionaryValue* prefs_images = prefs->GetMutableDictionary(kUserImages); + DCHECK(prefs_images); + std::string image_path_string; + prefs_images->GetStringWithoutPathExpansion(email, &image_path_string); + prefs_images->RemoveWithoutPathExpansion(email, NULL); + prefs->SavePersistentPrefs(); + + size_t default_image_id; + if (!IsDefaultImagePath(image_path_string, &default_image_id)) { + FilePath image_path(image_path_string); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + NewRunnableFunction(&DeleteUserImage, + image_path)); + } } bool UserManager::IsKnownUser(const std::string& email) { @@ -297,10 +324,7 @@ void UserManager::SetLoggedInUserImage(const SkBitmap& image) { void UserManager::SaveUserImage(const std::string& username, const SkBitmap& image) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - std::string filename = username + ".png"; - FilePath user_data_dir; - PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); - FilePath image_path = user_data_dir.AppendASCII(filename); + FilePath image_path = GetImagePathForUser(username); DVLOG(1) << "Saving user image to " << image_path.value(); BrowserThread::PostTask( @@ -378,6 +402,13 @@ UserManager::~UserManager() { image_loader_->set_delegate(NULL); } +FilePath UserManager::GetImagePathForUser(const std::string& username) { + std::string filename = username + ".png"; + FilePath user_data_dir; + PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + return user_data_dir.AppendASCII(filename); +} + void UserManager::NotifyOnLogin() { NotificationService::current()->Notify( NotificationType::LOGIN_USER_CHANGED, diff --git a/chrome/browser/chromeos/login/user_manager.h b/chrome/browser/chromeos/login/user_manager.h index b115119..01766d0 100644 --- a/chrome/browser/chromeos/login/user_manager.h +++ b/chrome/browser/chromeos/login/user_manager.h @@ -17,6 +17,7 @@ #include "chrome/common/notification_registrar.h" #include "third_party/skia/include/core/SkBitmap.h" +class FilePath; class PrefService; namespace chromeos { @@ -57,23 +58,23 @@ class UserManager : public UserImageLoader::Delegate, // Returns a list of the users who have logged into this device previously. // It is sorted in order of recency, with most recent at the beginning. - std::vector<User> GetUsers() const; + virtual std::vector<User> GetUsers() const; // Indicates that user just started off the record session. - void OffTheRecordUserLoggedIn(); + virtual void OffTheRecordUserLoggedIn(); // Indicates that a user with the given email has just logged in. // The persistent list will be updated accordingly. - void UserLoggedIn(const std::string& email); + virtual void UserLoggedIn(const std::string& email); // Remove user from persistent list. NOTE: user's data won't be removed. - void RemoveUser(const std::string& email); + virtual void RemoveUser(const std::string& email); // Returns true if given user has logged into the device before. - bool IsKnownUser(const std::string& email); + virtual bool IsKnownUser(const std::string& email); // Returns the logged-in user. - const User& logged_in_user() { + virtual const User& logged_in_user() { return logged_in_user_; } @@ -99,17 +100,21 @@ class UserManager : public UserImageLoader::Delegate, const NotificationDetails& details); // Accessor for current_user_is_owner_ - bool current_user_is_owner() const { + virtual bool current_user_is_owner() const { return current_user_is_owner_; } - void set_current_user_is_owner(bool current_user_is_owner) { + virtual void set_current_user_is_owner(bool current_user_is_owner) { current_user_is_owner_ = current_user_is_owner; } - private: + protected: UserManager(); - ~UserManager(); + virtual ~UserManager(); + + // Returns image filepath for the given user. + FilePath GetImagePathForUser(const std::string& username); + private: // Notifies on new user session. void NotifyOnLogin(); diff --git a/chrome/browser/chromeos/login/web_page_view.cc b/chrome/browser/chromeos/login/web_page_view.cc index 5205fa1..2d5e4de 100644 --- a/chrome/browser/chromeos/login/web_page_view.cc +++ b/chrome/browser/chromeos/login/web_page_view.cc @@ -78,10 +78,15 @@ void WizardWebPageViewTabContents::DidRunInsecureContent( page_delegate_->OnPageLoadFailed(security_origin); } -void WizardWebPageViewTabContents::DocumentLoadedInFrame() { +void WizardWebPageViewTabContents::DocumentLoadedInFrame( + long long /*frame_id*/) { page_delegate_->OnPageLoaded(); } +void WizardWebPageViewTabContents::DidFinishLoad( + long long /*frame_id*/) { +} + void WizardWebPageViewTabContents::OnContentBlocked(ContentSettingsType type) { LOG(ERROR) << "Page load failed: content blocked. Type: " << type; page_delegate_->OnPageLoadFailed(""); diff --git a/chrome/browser/chromeos/login/web_page_view.h b/chrome/browser/chromeos/login/web_page_view.h index b497715..e219722 100644 --- a/chrome/browser/chromeos/login/web_page_view.h +++ b/chrome/browser/chromeos/login/web_page_view.h @@ -52,7 +52,8 @@ class WizardWebPageViewTabContents : public TabContents { virtual void DidDisplayInsecureContent(); virtual void DidRunInsecureContent(const std::string& security_origin); - virtual void DocumentLoadedInFrame(); + virtual void DocumentLoadedInFrame(long long frame_id); + virtual void DidFinishLoad(long long frame_id); virtual void OnContentBlocked(ContentSettingsType type); private: diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc index cb2629a..1962a29 100644 --- a/chrome/browser/chromeos/login/wizard_controller.cc +++ b/chrome/browser/chromeos/login/wizard_controller.cc @@ -319,7 +319,6 @@ void WizardController::OwnBackground( DCHECK(!background_widget_); background_widget_ = background_widget; background_view_ = background_view; - background_view_->OnOwnerChanged(); } chromeos::NetworkScreen* WizardController::GetNetworkScreen() { @@ -981,6 +980,7 @@ void ShowLoginWizard(const std::string& first_screen_name, controller->Init(first_screen_name, screen_bounds); controller->Show(); + chromeos::LoginUtils::Get()->PrewarmAuthentication(); if (chromeos::CrosLibrary::Get()->EnsureLoaded()) chromeos::CrosLibrary::Get()->GetLoginLibrary()->EmitLoginPromptReady(); diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc index e38fdda..87f3a73 100644 --- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc +++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc @@ -214,7 +214,14 @@ IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, ControlFlowErrorNetwork) { EXPECT_EQ(controller()->GetLoginScreen(), controller()->current_screen()); } -IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, Accelerators) { +#if defined(OFFICIAL_BUILD) +// This test is supposed to fail on official test. +#define MAYBE_Accelerators DISABLED_Accelerators +#else +#define MAYBE_Accelerators Accelerators +#endif + +IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, MAYBE_Accelerators) { EXPECT_EQ(controller()->GetNetworkScreen(), controller()->current_screen()); views::FocusManager* focus_manager = diff --git a/chrome/browser/chromeos/network_list.cc b/chrome/browser/chromeos/network_list.cc index 926df02..c22795b 100644 --- a/chrome/browser/chromeos/network_list.cc +++ b/chrome/browser/chromeos/network_list.cc @@ -49,7 +49,8 @@ bool NetworkList::IsNetworkConnecting(NetworkType type, return IsInNetworkList(connecting_networks_, type, id); } -void NetworkList::NetworkChanged(chromeos::NetworkLibrary* network_lib) { +void NetworkList::OnNetworkManagerChanged( + chromeos::NetworkLibrary* network_lib) { networks_.clear(); connected_networks_.clear(); connecting_networks_.clear(); @@ -65,34 +66,37 @@ void NetworkList::NetworkChanged(chromeos::NetworkLibrary* network_lib) { IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET); networks_.push_back(NetworkItem(NETWORK_ETHERNET, label, - WifiNetwork(), - CellularNetwork())); + NULL, + NULL)); AddNetworkIndexToList(index++, ethernet_connected, ethernet_connecting); } // TODO(nkostylev): Show public WiFi networks first. - WifiNetworkVector wifi = network_lib->wifi_networks(); + const WifiNetworkVector& wifi = network_lib->wifi_networks(); for (WifiNetworkVector::const_iterator it = wifi.begin(); it != wifi.end(); ++it, ++index) { networks_.push_back(NetworkItem(NETWORK_WIFI, - ASCIIToUTF16(it->name()), + ASCIIToUTF16((*it)->name()), *it, - CellularNetwork())); - if (network_lib->wifi_network().service_path() == it->service_path()) { + NULL)); + if (network_lib->wifi_network() && + network_lib->wifi_network()->service_path() == (*it)->service_path()) { AddNetworkIndexToList(index, network_lib->wifi_connected(), network_lib->wifi_connecting()); } } - CellularNetworkVector cellular = network_lib->cellular_networks(); + const CellularNetworkVector& cellular = network_lib->cellular_networks(); for (CellularNetworkVector::const_iterator it = cellular.begin(); it != cellular.end(); ++it, ++index) { networks_.push_back(NetworkItem(NETWORK_CELLULAR, - ASCIIToUTF16(it->name()), - WifiNetwork(), + ASCIIToUTF16((*it)->name()), + NULL, *it)); - if (network_lib->cellular_network().service_path() == it->service_path()) { + if (network_lib->cellular_network() && + network_lib->cellular_network()->service_path() == + (*it)->service_path()) { AddNetworkIndexToList(index, network_lib->cellular_connected(), network_lib->cellular_connecting()); @@ -126,10 +130,11 @@ bool NetworkList::IsSameNetwork(const NetworkList::NetworkItem* network, // Assuming that there's only single Ethernet network. return true; case NETWORK_WIFI: - return id == network->wifi_network.name(); + return network->wifi_network && id == network->wifi_network->name(); break; case NETWORK_CELLULAR: - return id == network->cellular_network.name(); + return network->cellular_network && + id == network->cellular_network->name(); break; default: return false; diff --git a/chrome/browser/chromeos/network_list.h b/chrome/browser/chromeos/network_list.h index 5b320fc..47c7426 100644 --- a/chrome/browser/chromeos/network_list.h +++ b/chrome/browser/chromeos/network_list.h @@ -36,8 +36,8 @@ class NetworkList { label(label) {} NetworkItem(NetworkType network_type, string16 label, - WifiNetwork wifi_network, - CellularNetwork cellular_network) + WifiNetwork* wifi_network, + CellularNetwork* cellular_network) : network_type(network_type), label(label), wifi_network(wifi_network), @@ -46,8 +46,8 @@ class NetworkList { NetworkType network_type; string16 label; // string representation of the network (shown in UI). - WifiNetwork wifi_network; - CellularNetwork cellular_network; + WifiNetwork* wifi_network; + CellularNetwork* cellular_network; bool connected; }; @@ -83,7 +83,7 @@ class NetworkList { NetworkList::NetworkItem* GetNetworkAt(int index); // Callback from NetworkLibrary. - void NetworkChanged(chromeos::NetworkLibrary* network_lib); + void OnNetworkManagerChanged(chromeos::NetworkLibrary* network_lib); private: typedef std::vector<NetworkItem> NetworkItemVector; diff --git a/chrome/browser/chromeos/network_message_observer.cc b/chrome/browser/chromeos/network_message_observer.cc index 438f433..8a09207 100644 --- a/chrome/browser/chromeos/network_message_observer.cc +++ b/chrome/browser/chromeos/network_message_observer.cc @@ -6,6 +6,7 @@ #include "app/l10n_util.h" #include "base/callback.h" +#include "base/stl_util-inl.h" #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser.h" @@ -34,14 +35,22 @@ NetworkMessageObserver::NetworkMessageObserver(Profile* profile) notification_no_data_(profile, "network_no_data.chromeos", IDR_NOTIFICATION_BARS_EMPTY, l10n_util::GetStringUTF16(IDS_NETWORK_OUT_OF_DATA_TITLE)) { - NetworkChanged(CrosLibrary::Get()->GetNetworkLibrary()); + NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary(); + OnNetworkManagerChanged(netlib); + // Note that this gets added as a NetworkManagerObserver and a + // CellularDataPlanObserver in browser_init.cc initialized_ = true; } NetworkMessageObserver::~NetworkMessageObserver() { + NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary(); + netlib->RemoveNetworkManagerObserver(this); + netlib->RemoveCellularDataPlanObserver(this); notification_connection_error_.Hide(); notification_low_data_.Hide(); notification_no_data_.Hide(); + STLDeleteValues(&cellular_networks_); + STLDeleteValues(&wifi_networks_); } void NetworkMessageObserver::CreateModalPopup(views::WindowDelegate* view) { @@ -62,7 +71,7 @@ void NetworkMessageObserver::MobileSetup(const ListValue* args) { BrowserList::GetLastActive()->OpenMobilePlanTabAndActivate(); } -void NetworkMessageObserver::NetworkChanged(NetworkLibrary* obj) { +void NetworkMessageObserver::OnNetworkManagerChanged(NetworkLibrary* obj) { const WifiNetworkVector& wifi_networks = obj->wifi_networks(); const CellularNetworkVector& cellular_networks = obj->cellular_networks(); @@ -71,24 +80,24 @@ void NetworkMessageObserver::NetworkChanged(NetworkLibrary* obj) { // Check to see if we have any newly failed wifi network. for (WifiNetworkVector::const_iterator it = wifi_networks.begin(); it < wifi_networks.end(); it++) { - const WifiNetwork& wifi = *it; - if (wifi.failed()) { + const WifiNetwork* wifi = *it; + if (wifi->failed()) { ServicePathWifiMap::iterator iter = - wifi_networks_.find(wifi.service_path()); + wifi_networks_.find(wifi->service_path()); // If the network did not previously exist, then don't do anything. // For example, if the user travels to a location and finds a service // that has previously failed, we don't want to show a notification. if (iter == wifi_networks_.end()) continue; - const WifiNetwork& wifi_old = iter->second; + const WifiNetwork* wifi_old = iter->second; // If this network was in a failed state previously, then it's not new. - if (wifi_old.failed()) + if (wifi_old->failed()) continue; // Display login box again for bad_passphrase and bad_wepkey errors. - if (wifi.error() == ERROR_BAD_PASSPHRASE || - wifi.error() == ERROR_BAD_WEPKEY) { + if (wifi->error() == ERROR_BAD_PASSPHRASE || + wifi->error() == ERROR_BAD_WEPKEY) { // The NetworkConfigView will show the appropriate error message. view = new NetworkConfigView(wifi, true); // There should only be one wifi network that failed to connect. @@ -101,31 +110,35 @@ void NetworkMessageObserver::NetworkChanged(NetworkLibrary* obj) { // We only do this if we were trying to make a new connection. // So if a previously connected network got disconnected for any reason, // we don't display notification. - if (wifi_old.connecting()) { - new_failed_network = wifi.name(); + if (wifi_old->connecting()) { + new_failed_network = wifi->name(); // Like above, there should only be one newly failed network. break; } } // If we find any network connecting, we hide the error notification. - if (wifi.connecting()) { + if (wifi->connecting()) { notification_connection_error_.Hide(); } } // Refresh stored networks. + STLDeleteValues(&wifi_networks_); wifi_networks_.clear(); for (WifiNetworkVector::const_iterator it = wifi_networks.begin(); it < wifi_networks.end(); it++) { - const WifiNetwork& wifi = *it; - wifi_networks_[wifi.service_path()] = wifi; + const WifiNetwork* wifi = *it; + wifi_networks_[wifi->service_path()] = new WifiNetwork(*wifi); } + + STLDeleteValues(&cellular_networks_); cellular_networks_.clear(); for (CellularNetworkVector::const_iterator it = cellular_networks.begin(); it < cellular_networks.end(); it++) { - const CellularNetwork& cellular = *it; - cellular_networks_[cellular.service_path()] = cellular; + const CellularNetwork* cellular = *it; + cellular_networks_[cellular->service_path()] = + new CellularNetwork(*cellular); } // Show connection error notification if necessary. @@ -143,16 +156,18 @@ void NetworkMessageObserver::NetworkChanged(NetworkLibrary* obj) { CreateModalPopup(view); } -void NetworkMessageObserver::CellularDataPlanChanged(NetworkLibrary* obj) { - const CellularNetwork& cellular = obj->cellular_network(); +void NetworkMessageObserver::OnCellularDataPlanChanged(NetworkLibrary* obj) { + const CellularNetwork* cellular = obj->cellular_network(); + if (!cellular) + return; // Active plan is the first one in the list. Use empty one if none found. - const CellularDataPlanList& plans = cellular.GetDataPlans(); + const CellularDataPlanVector& plans = cellular->GetDataPlans(); CellularDataPlan plan = plans.empty() ? CellularDataPlan() : plans[0]; // If connected cellular network changed, or data plan is different, then // it's a new network. Then hide all previous notifications. bool new_plan = false; - if (cellular.service_path() != cellular_service_path_) { - cellular_service_path_ = cellular.service_path(); + if (cellular->service_path() != cellular_service_path_) { + cellular_service_path_ = cellular->service_path(); new_plan = true; } else if (plan.plan_name != cellular_data_plan_.plan_name || plan.plan_type != cellular_data_plan_.plan_type) { @@ -180,7 +195,7 @@ void NetworkMessageObserver::CellularDataPlanChanged(NetworkLibrary* obj) { } if (plan.plan_type != CELLULAR_DATA_PLAN_UNKNOWN) { - if (cellular.data_left() == CellularNetwork::DATA_NONE) { + if (cellular->data_left() == CellularNetwork::DATA_NONE) { notification_low_data_.Hide(); int message = plan.plan_type == CELLULAR_DATA_PLAN_UNLIMITED ? IDS_NETWORK_MINUTES_REMAINING_MESSAGE : @@ -190,13 +205,15 @@ void NetworkMessageObserver::CellularDataPlanChanged(NetworkLibrary* obj) { l10n_util::GetStringUTF16(IDS_NETWORK_PURCHASE_MORE_MESSAGE), NewCallback(this, &NetworkMessageObserver::MobileSetup), false, false); - } else if (cellular.data_left() == CellularNetwork::DATA_VERY_LOW) { + } else if (cellular->data_left() == CellularNetwork::DATA_VERY_LOW) { notification_no_data_.Hide(); int message = plan.plan_type == CELLULAR_DATA_PLAN_UNLIMITED ? IDS_NETWORK_MINUTES_REMAINING_MESSAGE : IDS_NETWORK_DATA_REMAINING_MESSAGE; + int64 remaining_minutes = + base::TimeDelta(plan.plan_end_time - plan.update_time).InMinutes(); int64 remaining = plan.plan_type == CELLULAR_DATA_PLAN_UNLIMITED ? - (plan.plan_end_time - plan.update_time) / 60 : + remaining_minutes : (plan.plan_data_bytes - plan.data_bytes_used) / (1024 * 1024); notification_low_data_.Show(l10n_util::GetStringFUTF16( message, UTF8ToUTF16(base::Int64ToString(remaining))), diff --git a/chrome/browser/chromeos/network_message_observer.h b/chrome/browser/chromeos/network_message_observer.h index 8cf603a..db1ba1b 100644 --- a/chrome/browser/chromeos/network_message_observer.h +++ b/chrome/browser/chromeos/network_message_observer.h @@ -23,20 +23,22 @@ namespace chromeos { // The network message observer displays a system notification for network // messages. -class NetworkMessageObserver : public NetworkLibrary::Observer { +class NetworkMessageObserver : public NetworkLibrary::NetworkManagerObserver, + public NetworkLibrary::CellularDataPlanObserver { public: explicit NetworkMessageObserver(Profile* profile); virtual ~NetworkMessageObserver(); - typedef std::map<std::string, WifiNetwork> ServicePathWifiMap; - typedef std::map<std::string, CellularNetwork> ServicePathCellularMap; + typedef std::map<std::string, WifiNetwork*> ServicePathWifiMap; + typedef std::map<std::string, CellularNetwork*> ServicePathCellularMap; private: virtual void CreateModalPopup(views::WindowDelegate* view); virtual void MobileSetup(const ListValue* args); - // NetworkLibrary::Observer implementation. - virtual void NetworkChanged(NetworkLibrary* obj); - virtual void CellularDataPlanChanged(NetworkLibrary* obj); + // NetworkLibrary::NetworkManagerObserver implementation. + virtual void OnNetworkManagerChanged(NetworkLibrary* obj); + // NetworkLibrary::CellularDataPlanObserver implementation. + virtual void OnCellularDataPlanChanged(NetworkLibrary* obj); bool initialized_; // Wifi networks by service path. @@ -62,4 +64,3 @@ class NetworkMessageObserver : public NetworkLibrary::Observer { } // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_NETWORK_MESSAGE_OBSERVER_H_ - diff --git a/chrome/browser/chromeos/network_state_notifier.cc b/chrome/browser/chromeos/network_state_notifier.cc index 5e23247..80acfaf 100644 --- a/chrome/browser/chromeos/network_state_notifier.cc +++ b/chrome/browser/chromeos/network_state_notifier.cc @@ -34,9 +34,19 @@ NetworkStateNotifier::NetworkStateNotifier() : ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), state_(RetrieveState()), offline_start_time_(Time::Now()) { + // Note that this gets added as a NetworkManagerObserver + // in browser_init.cc } -void NetworkStateNotifier::NetworkChanged(NetworkLibrary* cros) { +NetworkStateNotifier::~NetworkStateNotifier() { + // Let the NetworkManagerObserver leak to avoid a DCHECK + // failure in CommandLine::ForCurrentProcess. +// if (CrosLibrary::Get()->EnsureLoaded()) +// CrosLibrary::Get()->GetNetworkLibrary()-> +// RemoveNetworkManagerObserver(this); +} + +void NetworkStateNotifier::OnNetworkManagerChanged(NetworkLibrary* cros) { DCHECK(CrosLibrary::Get()->EnsureLoaded()); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -47,8 +57,7 @@ void NetworkStateNotifier::NetworkChanged(NetworkLibrary* cros) { void NetworkStateNotifier::UpdateNetworkState( NetworkStateDetails::State new_state) { - DLOG(INFO) << "UpdateNetworkState: new=" - << new_state << ", old=" << state_; + DVLOG(1) << "UpdateNetworkState: new=" << new_state << ", old=" << state_; if (state_ == NetworkStateDetails::CONNECTED && new_state != NetworkStateDetails::CONNECTED) { offline_start_time_ = Time::Now(); diff --git a/chrome/browser/chromeos/network_state_notifier.h b/chrome/browser/chromeos/network_state_notifier.h index 9d49d94..94e57f4 100644 --- a/chrome/browser/chromeos/network_state_notifier.h +++ b/chrome/browser/chromeos/network_state_notifier.h @@ -42,10 +42,10 @@ class NetworkStateDetails { }; // NetworkStateNotifier sends notification when network state has -// chagned. Notificatio is sent in UI thread. +// chagned. Notification is sent in UI thread. // TODO(oshima): port this to other platform. merge with // NetworkChangeNotifier if possible. -class NetworkStateNotifier : public NetworkLibrary::Observer { +class NetworkStateNotifier : public NetworkLibrary::NetworkManagerObserver { public: // Returns the singleton instance of the network state notifier; static NetworkStateNotifier* Get(); @@ -59,8 +59,8 @@ class NetworkStateNotifier : public NetworkLibrary::Observer { return Get()->state_ == NetworkStateDetails::CONNECTED; } - // NetworkLibrary::Observer implementation. - virtual void NetworkChanged(NetworkLibrary* cros); + // NetworkLibrary::NetworkManagerObserver implementation. + virtual void OnNetworkManagerChanged(NetworkLibrary* cros); private: friend struct DefaultSingletonTraits<NetworkStateNotifier>; @@ -69,7 +69,7 @@ class NetworkStateNotifier : public NetworkLibrary::Observer { static NetworkStateDetails::State RetrieveState(); NetworkStateNotifier(); - virtual ~NetworkStateNotifier() {} + virtual ~NetworkStateNotifier(); // Update the current state and sends notification to observers. // This should be invoked in UI thread. diff --git a/chrome/browser/chromeos/network_state_notifier_browsertest.cc b/chrome/browser/chromeos/network_state_notifier_browsertest.cc index 126ff13..47ea0de 100644 --- a/chrome/browser/chromeos/network_state_notifier_browsertest.cc +++ b/chrome/browser/chromeos/network_state_notifier_browsertest.cc @@ -70,7 +70,7 @@ IN_PROC_BROWSER_TEST_F(NetworkStateNotifierTest, TestConnected) { .WillRepeatedly((Return(true))) .RetiresOnSaturation(); NetworkStateNotifier* notifier = NetworkStateNotifier::Get(); - notifier->NetworkChanged(mock_network_library_); + notifier->OnNetworkManagerChanged(mock_network_library_); WaitForNotification(); EXPECT_EQ(chromeos::NetworkStateDetails::CONNECTED, state_); } @@ -88,7 +88,7 @@ IN_PROC_BROWSER_TEST_F(NetworkStateNotifierTest, TestConnecting) { .WillOnce((Return(true))) .RetiresOnSaturation(); NetworkStateNotifier* notifier = NetworkStateNotifier::Get(); - notifier->NetworkChanged(mock_network_library_); + notifier->OnNetworkManagerChanged(mock_network_library_); WaitForNotification(); EXPECT_EQ(chromeos::NetworkStateDetails::CONNECTING, state_); } @@ -106,7 +106,7 @@ IN_PROC_BROWSER_TEST_F(NetworkStateNotifierTest, TestDisconnected) { .WillOnce((Return(false))) .RetiresOnSaturation(); NetworkStateNotifier* notifier = NetworkStateNotifier::Get(); - notifier->NetworkChanged(mock_network_library_); + notifier->OnNetworkManagerChanged(mock_network_library_); WaitForNotification(); EXPECT_EQ(chromeos::NetworkStateDetails::DISCONNECTED, state_); } diff --git a/chrome/browser/chromeos/notifications/balloon_collection_impl.cc b/chrome/browser/chromeos/notifications/balloon_collection_impl.cc index 1cc3740..56a3d1c 100644 --- a/chrome/browser/chromeos/notifications/balloon_collection_impl.cc +++ b/chrome/browser/chromeos/notifications/balloon_collection_impl.cc @@ -160,7 +160,7 @@ void BalloonCollectionImpl::Observe(NotificationType type, // BalloonViewImpl before IO thread gets deleted in the // BrowserProcessImpl's destructor. See http://crbug.com/40810 // for details. - if(app_closing) + if (app_closing) Shutdown(); } @@ -168,7 +168,7 @@ void BalloonCollectionImpl::Shutdown() { // We need to remove the panel first because deleting // views that are not owned by parent will not remove // themselves from the parent. - DLOG(INFO) << "Shutting down notification UI"; + DVLOG(1) << "Shutting down notification UI"; notification_ui_.reset(); STLDeleteElements(&balloons_); } diff --git a/chrome/browser/chromeos/notifications/notification_panel.cc b/chrome/browser/chromeos/notifications/notification_panel.cc index 2b48256..1ff6014 100644 --- a/chrome/browser/chromeos/notifications/notification_panel.cc +++ b/chrome/browser/chromeos/notifications/notification_panel.cc @@ -271,7 +271,7 @@ namespace chromeos { class BalloonContainer : public views::View { public: - BalloonContainer(int margin) + explicit BalloonContainer(int margin) : margin_(margin), sticky_container_(new BalloonSubContainer(margin)), non_sticky_container_(new BalloonSubContainer(margin)) { @@ -700,7 +700,7 @@ void NotificationPanel::ScrollBalloonToVisible(Balloon* balloon) { void NotificationPanel::UpdatePanel(bool update_container_size) { if (update_container_size) UpdateContainerBounds(); - switch(state_) { + switch (state_) { case KEEP_SIZE: { gfx::Rect min_bounds = GetPreferredBounds(); gfx::Rect panel_bounds; @@ -806,8 +806,8 @@ void NotificationPanel::OnStale(BalloonViewImpl* view) { void NotificationPanel::SetState(State new_state, const char* name) { #if !defined(NDEBUG) - DLOG(INFO) << "state transition " << ToStr(state_) << " >> " - << ToStr(new_state) << " in " << name; + DVLOG(1) << "state transition " << ToStr(state_) << " >> " << ToStr(new_state) + << " in " << name; #endif state_ = new_state; } diff --git a/chrome/browser/chromeos/notifications/system_notification.cc b/chrome/browser/chromeos/notifications/system_notification.cc index 83debd5..f83c414 100644 --- a/chrome/browser/chromeos/notifications/system_notification.cc +++ b/chrome/browser/chromeos/notifications/system_notification.cc @@ -5,7 +5,6 @@ #include "chrome/browser/chromeos/notifications/system_notification.h" #include "base/callback.h" -#include "base/move.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/notifications/system_notification_factory.h" #include "chrome/browser/dom_ui/dom_ui_util.h" @@ -14,15 +13,17 @@ namespace chromeos { -SystemNotification::SystemNotification(Profile* profile, std::string id, - int icon_resource_id, string16 title) - : profile_(profile), - collection_(static_cast<BalloonCollectionImpl*>( - g_browser_process->notification_ui_manager()->balloon_collection())), - delegate_(new Delegate(base::move(id))), - title_(move(title)), - visible_(false), - urgent_(false) { +SystemNotification::SystemNotification(Profile* profile, + const std::string& id, + int icon_resource_id, + const string16& title) + : profile_(profile), + collection_(static_cast<BalloonCollectionImpl*>( + g_browser_process->notification_ui_manager()->balloon_collection())), + delegate_(new Delegate(id)), + title_(title), + visible_(false), + urgent_(false) { std::string url = dom_ui_util::GetImageDataUrlFromResource(icon_resource_id); DCHECK(!url.empty()); GURL tmp_gurl(url); diff --git a/chrome/browser/chromeos/notifications/system_notification.h b/chrome/browser/chromeos/notifications/system_notification.h index 82ca8f3..81976b7 100644 --- a/chrome/browser/chromeos/notifications/system_notification.h +++ b/chrome/browser/chromeos/notifications/system_notification.h @@ -9,7 +9,6 @@ #include <string> #include "base/basictypes.h" -#include "base/move.h" #include "base/ref_counted.h" #include "base/string16.h" #include "chrome/browser/chromeos/notifications/balloon_collection_impl.h" @@ -28,8 +27,9 @@ class SystemNotification { // The profile is the current user profile. The id is any string used // to uniquely identify this notification. The title is the title of // the message to be displayed. On creation, the message is hidden. - SystemNotification(Profile* profile, std::string id, int icon_resource_id, - string16 title); + SystemNotification(Profile* profile, const std::string& id, + int icon_resource_id, + const string16& title); virtual ~SystemNotification(); @@ -58,7 +58,7 @@ class SystemNotification { private: class Delegate : public NotificationDelegate { public: - explicit Delegate(std::string id) : id_(base::move(id)) {} + explicit Delegate(const std::string& id) : id_(id) {} void Display() {} void Error() {} void Close(bool by_user) {} diff --git a/chrome/browser/chromeos/offline/offline_load_page.cc b/chrome/browser/chromeos/offline/offline_load_page.cc index c3b88d5..63c9eae 100644 --- a/chrome/browser/chromeos/offline/offline_load_page.cc +++ b/chrome/browser/chromeos/offline/offline_load_page.cc @@ -124,8 +124,8 @@ void OfflineLoadPage::Observe(NotificationType type, if (type.value == NotificationType::NETWORK_STATE_CHANGED) { chromeos::NetworkStateDetails* state_details = Details<chromeos::NetworkStateDetails>(details).ptr(); - DLOG(INFO) << "NetworkStateChanaged notification received: state=" - << state_details->state(); + DVLOG(1) << "NetworkStateChanaged notification received: state=" + << state_details->state(); if (state_details->state() == chromeos::NetworkStateDetails::CONNECTED) { registrar_.Remove(this, NotificationType::NETWORK_STATE_CHANGED, diff --git a/chrome/browser/chromeos/offline/offline_load_service.cc b/chrome/browser/chromeos/offline/offline_load_service.cc index a75fba6..08f2362 100644 --- a/chrome/browser/chromeos/offline/offline_load_service.cc +++ b/chrome/browser/chromeos/offline/offline_load_service.cc @@ -62,8 +62,8 @@ bool OfflineLoadService::ShouldProceed(int process_host_id, process_host_id, render_view_id); DCHECK(tab_contents); bool proceed = tabs_.find(tab_contents) != tabs_.end(); - DLOG(INFO) << "ShouldProceed:" << proceed << ", url=" << url.spec() - << ", tab_contents=" << tab_contents; + DVLOG(1) << "ShouldProceed:" << proceed << ", url=" << url.spec() + << ", tab_contents=" << tab_contents; return proceed; } @@ -75,8 +75,8 @@ void OfflineLoadService::Proceeded(int process_host_id, process_host_id, render_view_id); DCHECK(tab_contents); if (tabs_.find(tab_contents) == tabs_.end()) { - DLOG(INFO) << "Proceeded: url=" << url.spec() - << ", tab_contents=" << tab_contents; + DVLOG(1) << "Proceeded: url=" << url.spec() + << ", tab_contents=" << tab_contents; tabs_.insert(tab_contents); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, diff --git a/chrome/browser/chromeos/options/cellular_config_view.cc b/chrome/browser/chromeos/options/cellular_config_view.cc index 725efe8..be919e9 100644 --- a/chrome/browser/chromeos/options/cellular_config_view.cc +++ b/chrome/browser/chromeos/options/cellular_config_view.cc @@ -40,7 +40,7 @@ struct PlanDetails { // TODO(xiyuan): Get real data from libcros when it's ready. // Get plan details at the time being called. -void GetPlanDetails(const chromeos::CellularNetwork& cellular, +void GetPlanDetails(const chromeos::CellularNetwork* cellular, PlanDetails* details) { // Free 5M 30day plan. details->last_purchase_type = UNKNOWN; @@ -56,9 +56,9 @@ void GetPlanDetails(const chromeos::CellularNetwork& cellular, namespace chromeos { CellularConfigView::CellularConfigView(NetworkConfigView* parent, - const CellularNetwork& cellular) + const CellularNetwork* cellular) : parent_(parent), - cellular_(cellular), + cellular_(new CellularNetwork(*cellular)), purchase_info_(NULL), purchase_more_button_(NULL), remaining_data_info_(NULL), @@ -69,6 +69,9 @@ CellularConfigView::CellularConfigView(NetworkConfigView* parent, Init(); } +CellularConfigView::~CellularConfigView() { +} + void CellularConfigView::ButtonPressed(views::Button* button, const views::Event& event) { if (button == purchase_more_button_) { @@ -85,9 +88,10 @@ void CellularConfigView::LinkActivated(views::Link* source, int event_flags) { bool CellularConfigView::Save() { // Save auto-connect here. bool auto_connect = autoconnect_checkbox_->checked(); - if (auto_connect != cellular_.auto_connect()) { - cellular_.set_auto_connect(auto_connect); - CrosLibrary::Get()->GetNetworkLibrary()->SaveCellularNetwork(cellular_); + if (auto_connect != cellular_->auto_connect()) { + cellular_->set_auto_connect(auto_connect); + CrosLibrary::Get()->GetNetworkLibrary()->SaveCellularNetwork( + cellular_.get()); } return true; } @@ -157,10 +161,10 @@ void CellularConfigView::Init() { } void CellularConfigView::Update() { - autoconnect_checkbox_->SetChecked(cellular_.auto_connect()); + autoconnect_checkbox_->SetChecked(cellular_->auto_connect()); PlanDetails details; - GetPlanDetails(cellular_, &details); + GetPlanDetails(cellular_.get(), &details); switch (details.last_purchase_type) { case NO_PURCHASE: diff --git a/chrome/browser/chromeos/options/cellular_config_view.h b/chrome/browser/chromeos/options/cellular_config_view.h index 9120c6d..36e64ed 100644 --- a/chrome/browser/chromeos/options/cellular_config_view.h +++ b/chrome/browser/chromeos/options/cellular_config_view.h @@ -27,9 +27,9 @@ class CellularConfigView : public views::View, public views::LinkController { public: CellularConfigView(NetworkConfigView* parent, - const CellularNetwork& cellular); + const CellularNetwork* cellular); explicit CellularConfigView(NetworkConfigView* parent); - virtual ~CellularConfigView() {} + virtual ~CellularConfigView(); // views::ButtonListener implementation. virtual void ButtonPressed(views::Button* button, const views::Event& event); @@ -50,7 +50,7 @@ class CellularConfigView : public views::View, NetworkConfigView* parent_; - CellularNetwork cellular_; + scoped_ptr<CellularNetwork> cellular_; views::Label* purchase_info_; views::NativeButton* purchase_more_button_; diff --git a/chrome/browser/chromeos/options/internet_page_view.cc b/chrome/browser/chromeos/options/internet_page_view.cc index c22e1c5..14ad91d 100644 --- a/chrome/browser/chromeos/options/internet_page_view.cc +++ b/chrome/browser/chromeos/options/internet_page_view.cc @@ -301,7 +301,7 @@ class WirelessSection : public NetworkSection { bool connected, int connection_type); WifiNetworkVector wifi_networks_; - CellularNetworkVector celluar_networks_; + CellularNetworkVector cellular_networks_; DISALLOW_COPY_AND_ASSIGN(WirelessSection); }; @@ -319,58 +319,56 @@ void WirelessSection::InitSection() { // Wifi wifi_networks_ = cros->wifi_networks(); for (size_t i = 0; i < wifi_networks_.size(); ++i) { - std::wstring name = ASCIIToWide(wifi_networks_[i].name()); + std::wstring name = ASCIIToWide(wifi_networks_[i]->name()); SkBitmap icon = NetworkMenu::IconForNetworkStrength( - wifi_networks_[i].strength(), true); - if (wifi_networks_[i].encrypted()) { + wifi_networks_[i]->strength(), true); + if (wifi_networks_[i]->encrypted()) { icon = NetworkMenu::IconForDisplay(icon, *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE)); } - bool connecting = wifi_networks_[i].connecting(); - bool connected = wifi_networks_[i].connected(); + bool connecting = wifi_networks_[i]->connecting(); + bool connected = wifi_networks_[i]->connected(); AddWirelessNetwork(i, icon, name, connecting, connected, TYPE_WIFI); } // Cellular - celluar_networks_ = cros->cellular_networks(); - // Cellular networks ssids. - for (size_t i = 0; i < celluar_networks_.size(); ++i) { - std::wstring name = ASCIIToWide(celluar_networks_[i].name()); + cellular_networks_ = cros->cellular_networks(); + for (size_t i = 0; i < cellular_networks_.size(); ++i) { + std::wstring name = ASCIIToWide(cellular_networks_[i]->name()); SkBitmap icon = NetworkMenu::IconForNetworkStrength( - celluar_networks_[i].strength(), true); - // TODO(chocobo): Check cellular network 3g/edge. - SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_3G); -// SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_EDGE); + cellular_networks_[i]->strength(), true); + SkBitmap badge = + NetworkMenu::BadgeForNetworkTechnology(cellular_networks_[i]); icon = NetworkMenu::IconForDisplay(icon, badge); - bool connecting = celluar_networks_[i].connecting(); - bool connected = celluar_networks_[i].connected(); + bool connecting = cellular_networks_[i]->connecting(); + bool connected = cellular_networks_[i]->connected(); AddWirelessNetwork(i, icon, name, connecting, connected, TYPE_CELLULAR); } } void WirelessSection::ButtonClicked(int button, int connection_type, int id) { if (connection_type == TYPE_CELLULAR) { - if (static_cast<int>(celluar_networks_.size()) > id) { + if (static_cast<int>(cellular_networks_.size()) > id) { if (button == CONNECT_BUTTON) { // Connect to cellular network. CrosLibrary::Get()->GetNetworkLibrary()->ConnectToCellularNetwork( - celluar_networks_[id]); + cellular_networks_[id]); } else if (button == DISCONNECT_BUTTON) { CrosLibrary::Get()->GetNetworkLibrary()->DisconnectFromWirelessNetwork( - celluar_networks_[id]); + cellular_networks_[id]); } else { - CreateModalPopup(new NetworkConfigView(celluar_networks_[id])); + CreateModalPopup(new NetworkConfigView(cellular_networks_[id])); } } } else if (connection_type == TYPE_WIFI) { if (static_cast<int>(wifi_networks_.size()) > id) { if (button == CONNECT_BUTTON) { // Connect to wifi here. Open password page if appropriate. - if (wifi_networks_[id].encrypted()) { + if (wifi_networks_[id]->encrypted()) { NetworkConfigView* view = new NetworkConfigView(wifi_networks_[id], true); CreateModalPopup(view); @@ -428,7 +426,6 @@ class RememberedSection : public NetworkSection { private: WifiNetworkVector wifi_networks_; - CellularNetworkVector celluar_networks_; DISALLOW_COPY_AND_ASSIGN(RememberedSection); }; @@ -446,44 +443,23 @@ void RememberedSection::InitSection() { // Wifi wifi_networks_ = cros->remembered_wifi_networks(); for (size_t i = 0; i < wifi_networks_.size(); ++i) { - std::wstring name = ASCIIToWide(wifi_networks_[i].name()); + std::wstring name = ASCIIToWide(wifi_networks_[i]->name()); - SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0); - if (wifi_networks_[i].encrypted()) { + SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0_BLACK); + if (wifi_networks_[i]->encrypted()) { icon = NetworkMenu::IconForDisplay(icon, *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE)); } AddNetwork(i, icon, name, false, std::wstring(), FORGET_BUTTON, TYPE_WIFI); } - - // Cellular - celluar_networks_ = cros->remembered_cellular_networks(); - // Cellular networks ssids. - for (size_t i = 0; i < celluar_networks_.size(); ++i) { - std::wstring name = ASCIIToWide(celluar_networks_[i].name()); - - SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0); - // TODO(chocobo): Check cellular network 3g/edge. - SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_3G); -// SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_EDGE); - icon = NetworkMenu::IconForDisplay(icon, badge); - - AddNetwork(i, icon, name, false, std::wstring(), FORGET_BUTTON, - TYPE_CELLULAR); - } } void RememberedSection::ButtonClicked(int button, int connection_type, int id) { - if (connection_type == TYPE_CELLULAR) { - if (static_cast<int>(celluar_networks_.size()) > id) { - CrosLibrary::Get()->GetNetworkLibrary()->ForgetWirelessNetwork( - celluar_networks_[id].service_path()); - } - } else if (connection_type == TYPE_WIFI) { + if (connection_type == TYPE_WIFI) { if (static_cast<int>(wifi_networks_.size()) > id) { - CrosLibrary::Get()->GetNetworkLibrary()->ForgetWirelessNetwork( - wifi_networks_[id].service_path()); + CrosLibrary::Get()->GetNetworkLibrary()->ForgetWifiNetwork( + wifi_networks_[id]->service_path()); } } else { NOTREACHED(); @@ -590,15 +566,14 @@ InternetPageView::InternetPageView(Profile* profile) contents_view_(new InternetPageContentView(profile)), scroll_view_(new views::ScrollView) { NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); - cros->UpdateSystemInfo(); - cros->AddObserver(this); + cros->AddNetworkManagerObserver(this); } InternetPageView::~InternetPageView() { - CrosLibrary::Get()->GetNetworkLibrary()->RemoveObserver(this); + CrosLibrary::Get()->GetNetworkLibrary()->RemoveNetworkManagerObserver(this); } -void InternetPageView::NetworkChanged(NetworkLibrary* obj) { +void InternetPageView::OnNetworkManagerChanged(NetworkLibrary* obj) { // Refresh wired, wireless, and remembered networks. // Remember the current scroll region, and try to scroll back afterwards. gfx::Rect rect = scroll_view_->GetVisibleRect(); diff --git a/chrome/browser/chromeos/options/internet_page_view.h b/chrome/browser/chromeos/options/internet_page_view.h index 71b7399..c5048df 100644 --- a/chrome/browser/chromeos/options/internet_page_view.h +++ b/chrome/browser/chromeos/options/internet_page_view.h @@ -19,13 +19,13 @@ class InternetPageContentView; // Internet settings page for Chrome OS class InternetPageView : public SettingsPageView, - public NetworkLibrary::Observer { + public NetworkLibrary::NetworkManagerObserver { public: explicit InternetPageView(Profile* profile); virtual ~InternetPageView(); - // NetworkLibrary::Observer implementation. - virtual void NetworkChanged(NetworkLibrary* obj); + // NetworkLibrary::NetworkManagerObserver implementation. + virtual void OnNetworkManagerChanged(NetworkLibrary* obj); // views::View overrides: virtual void Layout(); diff --git a/chrome/browser/chromeos/options/network_config_view.cc b/chrome/browser/chromeos/options/network_config_view.cc index 17e9eb7..0c65eb6 100644 --- a/chrome/browser/chromeos/options/network_config_view.cc +++ b/chrome/browser/chromeos/options/network_config_view.cc @@ -22,20 +22,20 @@ using views::WidgetGtk; namespace chromeos { -NetworkConfigView::NetworkConfigView(EthernetNetwork ethernet) +NetworkConfigView::NetworkConfigView(const EthernetNetwork* ethernet) : browser_mode_(true), flags_(FLAG_ETHERNET | FLAG_SHOW_IPCONFIG), - ethernet_(ethernet), + ethernet_(new EthernetNetwork(*ethernet)), cellularconfig_view_(NULL), wificonfig_view_(NULL), ipconfig_view_(NULL), delegate_(NULL) { } -NetworkConfigView::NetworkConfigView(WifiNetwork wifi, bool login_only) +NetworkConfigView::NetworkConfigView(const WifiNetwork* wifi, bool login_only) : browser_mode_(true), flags_(FLAG_WIFI), - wifi_(wifi), + wifi_(new WifiNetwork(*wifi)), cellularconfig_view_(NULL), wificonfig_view_(NULL), ipconfig_view_(NULL), @@ -46,10 +46,10 @@ NetworkConfigView::NetworkConfigView(WifiNetwork wifi, bool login_only) flags_ |= FLAG_SHOW_IPCONFIG; } -NetworkConfigView::NetworkConfigView(CellularNetwork cellular) +NetworkConfigView::NetworkConfigView(const CellularNetwork* cellular) : browser_mode_(true), flags_(FLAG_CELLULAR | FLAG_SHOW_IPCONFIG), - cellular_(cellular), + cellular_(new CellularNetwork(*cellular)), cellularconfig_view_(NULL), wificonfig_view_(NULL), ipconfig_view_(NULL), @@ -65,6 +65,9 @@ NetworkConfigView::NetworkConfigView() delegate_(NULL) { } +NetworkConfigView::~NetworkConfigView() { +} + gfx::NativeWindow NetworkConfigView::GetNativeWindow() const { return GTK_WINDOW(static_cast<WidgetGtk*>(GetWidget())->GetNativeView()); } @@ -114,9 +117,9 @@ std::wstring NetworkConfigView::GetWindowTitle() const { if (flags_ & FLAG_OTHER_NETWORK) return l10n_util::GetString(IDS_OPTIONS_SETTINGS_OTHER_NETWORKS); if (flags_ & FLAG_WIFI) - return ASCIIToWide(wifi_.name()); + return ASCIIToWide(wifi_->name()); if (flags_ & FLAG_CELLULAR) - return ASCIIToWide(cellular_.name()); + return ASCIIToWide(cellular_->name()); return l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET); } @@ -177,7 +180,7 @@ void NetworkConfigView::Init() { AddChildView(tabs_); if (flags_ & FLAG_CELLULAR) { - cellularconfig_view_ = new CellularConfigView(this, cellular_); + cellularconfig_view_ = new CellularConfigView(this, cellular_.get()); tabs_->AddTab( l10n_util::GetString(IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_USAGE), cellularconfig_view_); @@ -186,7 +189,7 @@ void NetworkConfigView::Init() { if (flags_ & FLAG_OTHER_NETWORK) wificonfig_view_ = new WifiConfigView(this); else - wificonfig_view_ = new WifiConfigView(this, wifi_); + wificonfig_view_ = new WifiConfigView(this, wifi_.get()); tabs_->AddTab( l10n_util::GetString(IDS_OPTIONS_SETTINGS_SECTION_TITLE_NETWORK_CONFIG), wificonfig_view_); @@ -194,11 +197,11 @@ void NetworkConfigView::Init() { if (flags_ & FLAG_SHOW_IPCONFIG) { if (flags_ & FLAG_WIFI) - ipconfig_view_ = new IPConfigView(wifi_.device_path()); + ipconfig_view_ = new IPConfigView(wifi_->device_path()); else if (flags_ & FLAG_CELLULAR) - ipconfig_view_ = new IPConfigView(cellular_.device_path()); + ipconfig_view_ = new IPConfigView(cellular_->device_path()); else - ipconfig_view_ = new IPConfigView(ethernet_.device_path()); + ipconfig_view_ = new IPConfigView(ethernet_->device_path()); tabs_->AddTab( l10n_util::GetString(IDS_OPTIONS_SETTINGS_SECTION_TITLE_IP_CONFIG), ipconfig_view_); diff --git a/chrome/browser/chromeos/options/network_config_view.h b/chrome/browser/chromeos/options/network_config_view.h index 2fda171..b6f13be 100644 --- a/chrome/browser/chromeos/options/network_config_view.h +++ b/chrome/browser/chromeos/options/network_config_view.h @@ -42,14 +42,14 @@ class NetworkConfigView : public views::View, }; // Configure dialog for ethernet. - explicit NetworkConfigView(EthernetNetwork ethernet); + explicit NetworkConfigView(const EthernetNetwork* ethernet); // Configure dialog for wifi. If |login_only|, then only show login tab. - explicit NetworkConfigView(WifiNetwork wifi, bool login_only); + explicit NetworkConfigView(const WifiNetwork* wifi, bool login_only); // Configure dialog for cellular. - explicit NetworkConfigView(CellularNetwork cellular); + explicit NetworkConfigView(const CellularNetwork* cellular); // Login dialog for hidden networks. explicit NetworkConfigView(); - virtual ~NetworkConfigView() {} + virtual ~NetworkConfigView(); // Returns corresponding native window. gfx::NativeWindow GetNativeWindow() const; @@ -116,9 +116,9 @@ class NetworkConfigView : public views::View, // NetworkConfigFlags to specify which UIs to show. int flags_; - EthernetNetwork ethernet_; - WifiNetwork wifi_; - CellularNetwork cellular_; + scoped_ptr<EthernetNetwork> ethernet_; + scoped_ptr<WifiNetwork> wifi_; + scoped_ptr<CellularNetwork> cellular_; CellularConfigView* cellularconfig_view_; WifiConfigView* wificonfig_view_; diff --git a/chrome/browser/chromeos/options/wifi_config_view.cc b/chrome/browser/chromeos/options/wifi_config_view.cc index 72cb512..7cdad5b 100644 --- a/chrome/browser/chromeos/options/wifi_config_view.cc +++ b/chrome/browser/chromeos/options/wifi_config_view.cc @@ -27,15 +27,46 @@ namespace chromeos { // The width of the password field. const int kPasswordWidth = 150; -WifiConfigView::WifiConfigView(NetworkConfigView* parent, WifiNetwork wifi) +enum SecurityComboboxIndex { + INDEX_NONE = 0, + INDEX_WEP = 1, + INDEX_WPA = 2, + INDEX_RSN = 3, + INDEX_COUNT = 4 +}; + +int WifiConfigView::SecurityComboboxModel::GetItemCount() { + return INDEX_COUNT; +} + +string16 WifiConfigView::SecurityComboboxModel::GetItemAt(int index) { + if (index == INDEX_NONE) + return l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_NONE); + else if (index == INDEX_WEP) + return l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_WEP); + else if (index == INDEX_WPA) + return l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_WPA); + else if (index == INDEX_RSN) + return l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY_RSN); + NOTREACHED(); + return string16(); +} + +WifiConfigView::WifiConfigView(NetworkConfigView* parent, + const WifiNetwork* wifi) : parent_(parent), other_network_(false), can_login_(false), - wifi_(wifi), + wifi_(new WifiNetwork(*wifi)), ssid_textfield_(NULL), identity_textfield_(NULL), certificate_browse_button_(NULL), certificate_path_(), + security_combobox_(NULL), passphrase_textfield_(NULL), passphrase_visible_button_(NULL), autoconnect_checkbox_(NULL) { @@ -50,18 +81,24 @@ WifiConfigView::WifiConfigView(NetworkConfigView* parent) identity_textfield_(NULL), certificate_browse_button_(NULL), certificate_path_(), + security_combobox_(NULL), passphrase_textfield_(NULL), passphrase_visible_button_(NULL), autoconnect_checkbox_(NULL) { Init(); } +WifiConfigView::~WifiConfigView() { +} + void WifiConfigView::UpdateCanLogin(void) { bool can_login = true; if (other_network_) { - // Since the user can try to connect to a non-encrypted hidden network, - // only enforce ssid is non-empty. - can_login = !ssid_textfield_->text().empty(); + // Enforce ssid is non empty. + // If security is not none, also enforce passphrase is non empty. + can_login = !ssid_textfield_->text().empty() && + (security_combobox_->selected_item() == INDEX_NONE || + !passphrase_textfield_->text().empty()); } else { // Connecting to an encrypted network if (passphrase_textfield_ != NULL) { @@ -128,6 +165,19 @@ void WifiConfigView::ButtonPressed(views::Button* sender, } } +void WifiConfigView::ItemChanged(views::Combobox* combo_box, + int prev_index, int new_index) { + // If changed to no security, then disable combobox and clear it. + // Otherwise, enable it. Also, update can login. + if (new_index == INDEX_NONE) { + passphrase_textfield_->SetEnabled(false); + passphrase_textfield_->SetText(string16()); + } else { + passphrase_textfield_->SetEnabled(true); + } + UpdateCanLogin(); +} + void WifiConfigView::FileSelected(const FilePath& path, int index, void* params) { certificate_path_ = path.value(); @@ -141,18 +191,29 @@ bool WifiConfigView::Login() { if (identity_textfield_ != NULL) { identity_string = UTF16ToUTF8(identity_textfield_->text()); } + bool connected = false; if (other_network_) { - CrosLibrary::Get()->GetNetworkLibrary()->ConnectToWifiNetwork( - GetSSID(), GetPassphrase(), + ConnectionSecurity sec = SECURITY_UNKNOWN; + int index = security_combobox_->selected_item(); + if (index == INDEX_NONE) + sec = SECURITY_NONE; + else if (index == INDEX_WEP) + sec = SECURITY_WEP; + else if (index == INDEX_WPA) + sec = SECURITY_WPA; + else if (index == INDEX_RSN) + sec = SECURITY_RSN; + connected = CrosLibrary::Get()->GetNetworkLibrary()->ConnectToWifiNetwork( + sec, GetSSID(), GetPassphrase(), identity_string, certificate_path_, autoconnect_checkbox_ ? autoconnect_checkbox_->checked() : true); } else { Save(); - CrosLibrary::Get()->GetNetworkLibrary()->ConnectToWifiNetwork( - wifi_, GetPassphrase(), + connected = CrosLibrary::Get()->GetNetworkLibrary()->ConnectToWifiNetwork( + wifi_.get(), GetPassphrase(), identity_string, certificate_path_); } - return true; + return connected; } bool WifiConfigView::Save() { @@ -162,8 +223,8 @@ bool WifiConfigView::Save() { if (autoconnect_checkbox_) { bool auto_connect = autoconnect_checkbox_->checked(); - if (auto_connect != wifi_.auto_connect()) { - wifi_.set_auto_connect(auto_connect); + if (auto_connect != wifi_->auto_connect()) { + wifi_->set_auto_connect(auto_connect); changed = true; } } @@ -171,14 +232,14 @@ bool WifiConfigView::Save() { if (passphrase_textfield_) { const std::string& passphrase = UTF16ToUTF8(passphrase_textfield_->text()); - if (passphrase != wifi_.passphrase()) { - wifi_.set_passphrase(passphrase); + if (passphrase != wifi_->passphrase()) { + wifi_->set_passphrase(passphrase); changed = true; } } if (changed) - CrosLibrary::Get()->GetNetworkLibrary()->SaveWifiNetwork(wifi_); + CrosLibrary::Get()->GetNetworkLibrary()->SaveWifiNetwork(wifi_.get()); } return true; } @@ -222,6 +283,7 @@ void WifiConfigView::Init() { column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); + // SSID input layout->StartRow(0, column_view_set_id); layout->AddView(new views::Label(l10n_util::GetString( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_ID))); @@ -230,12 +292,13 @@ void WifiConfigView::Init() { ssid_textfield_->SetController(this); layout->AddView(ssid_textfield_); } else { - views::Label* label = new views::Label(ASCIIToWide(wifi_.name())); + views::Label* label = new views::Label(ASCIIToWide(wifi_->name())); label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); layout->AddView(label); } layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + // Certificate input // Loaded certificates (i.e. stored in a pkcs11 device) do not require // a passphrase. bool certificate_loaded = false; @@ -245,23 +308,24 @@ void WifiConfigView::Init() { // in general, but very common. WPA Supplicant doesn't report the // EAP type because it's unknown until the process begins, and we'd // need some kind of callback. - if (wifi_.encrypted() && wifi_.encryption() == SECURITY_8021X) { + if (wifi_.get() && wifi_->encrypted() && + wifi_->encryption() == SECURITY_8021X) { layout->StartRow(0, column_view_set_id); layout->AddView(new views::Label(l10n_util::GetString( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_IDENTITY))); identity_textfield_ = new views::Textfield( views::Textfield::STYLE_DEFAULT); identity_textfield_->SetController(this); - if (!wifi_.identity().empty()) - identity_textfield_->SetText(UTF8ToUTF16(wifi_.identity())); + if (!wifi_->identity().empty()) + identity_textfield_->SetText(UTF8ToUTF16(wifi_->identity())); layout->AddView(identity_textfield_); layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); layout->StartRow(0, column_view_set_id); layout->AddView(new views::Label(l10n_util::GetString( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT))); - if (!wifi_.cert_path().empty()) { - certificate_path_ = wifi_.cert_path(); - certificate_loaded = wifi_.IsCertificateLoaded(); + if (!wifi_->cert_path().empty()) { + certificate_path_ = wifi_->cert_path(); + certificate_loaded = wifi_->IsCertificateLoaded(); } if (certificate_loaded) { std::wstring label = l10n_util::GetString( @@ -282,11 +346,24 @@ void WifiConfigView::Init() { layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); } + // Security select + if (other_network_) { + layout->StartRow(0, column_view_set_id); + layout->AddView(new views::Label(l10n_util::GetString( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SECURITY))); + security_combobox_ = new views::Combobox(new SecurityComboboxModel()); + security_combobox_->set_listener(this); + layout->AddView(security_combobox_); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + } + + // Passphrase input // Add passphrase if other_network or wifi is encrypted. - if (other_network_ || (wifi_.encrypted() && !certificate_loaded)) { + if (other_network_ || (wifi_.get() && wifi_->encrypted() && + !certificate_loaded)) { layout->StartRow(0, column_view_set_id); int label_text_id; - if (wifi_.encryption() == SECURITY_8021X) + if (wifi_.get() && wifi_->encryption() == SECURITY_8021X) label_text_id = IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PRIVATE_KEY_PASSWORD; else @@ -295,8 +372,11 @@ void WifiConfigView::Init() { passphrase_textfield_ = new views::Textfield( views::Textfield::STYLE_PASSWORD); passphrase_textfield_->SetController(this); - if (!wifi_.passphrase().empty()) - passphrase_textfield_->SetText(UTF8ToUTF16(wifi_.passphrase())); + if (wifi_.get() && !wifi_->passphrase().empty()) + passphrase_textfield_->SetText(UTF8ToUTF16(wifi_->passphrase())); + // Disable passphrase input initially for other network. + if (other_network_) + passphrase_textfield_->SetEnabled(false); layout->AddView(passphrase_textfield_); // Password visible button. passphrase_visible_button_ = new views::ImageButton(this); @@ -306,7 +386,7 @@ void WifiConfigView::Init() { passphrase_visible_button_->SetImageAlignment( views::ImageButton::ALIGN_CENTER, views::ImageButton::ALIGN_MIDDLE); // Disable viewing password by unauthenticated user. - if (!wifi_.passphrase().empty() && + if (wifi_.get() && !wifi_->passphrase().empty() && chromeos::UserManager::Get()->logged_in_user().email().empty()) { passphrase_visible_button_->SetVisible(false); } @@ -314,14 +394,15 @@ void WifiConfigView::Init() { layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); } + // Error label // If there's an error, add an error message label. // Right now, only displaying bad_passphrase and bad_wepkey errors. - if (wifi_.error() == ERROR_BAD_PASSPHRASE || - wifi_.error() == ERROR_BAD_WEPKEY) { + if (wifi_.get() && (wifi_->error() == ERROR_BAD_PASSPHRASE || + wifi_->error() == ERROR_BAD_WEPKEY)) { layout->StartRow(0, column_view_set_id); layout->SkipColumns(1); int id = IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_BAD_PASSPHRASE; - if (wifi_.error() == ERROR_BAD_WEPKEY) + if (wifi_->error() == ERROR_BAD_WEPKEY) id = IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_BAD_WEPKEY; views::Label* label_error = new views::Label(l10n_util::GetString(id)); label_error->SetHorizontalAlignment(views::Label::ALIGN_LEFT); @@ -332,11 +413,11 @@ void WifiConfigView::Init() { // Autoconnect checkbox // Only show if this network is already remembered (a favorite). - if (wifi_.favorite()) { + if (wifi_.get() && wifi_->favorite()) { autoconnect_checkbox_ = new views::Checkbox(l10n_util::GetString( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_AUTO_CONNECT)); // For other network, default to autoconnect. - bool autoconnect = other_network_ || wifi_.auto_connect(); + bool autoconnect = other_network_ || wifi_->auto_connect(); autoconnect_checkbox_->SetChecked(autoconnect); layout->StartRow(0, column_view_set_id); layout->AddView(autoconnect_checkbox_, 3, 1); diff --git a/chrome/browser/chromeos/options/wifi_config_view.h b/chrome/browser/chromeos/options/wifi_config_view.h index 3c3137e..9a72a25 100644 --- a/chrome/browser/chromeos/options/wifi_config_view.h +++ b/chrome/browser/chromeos/options/wifi_config_view.h @@ -8,6 +8,7 @@ #include <string> +#include "app/combobox_model.h" #include "base/gtest_prod_util.h" #include "base/string16.h" #include "chrome/browser/chromeos/cros/network_library.h" @@ -16,6 +17,7 @@ #include "views/controls/button/checkbox.h" #include "views/controls/button/image_button.h" #include "views/controls/button/native_button.h" +#include "views/controls/combobox/combobox.h" #include "views/controls/textfield/textfield.h" #include "views/view.h" @@ -29,11 +31,12 @@ class NetworkConfigView; class WifiConfigView : public views::View, public views::Textfield::Controller, public views::ButtonListener, + public views::Combobox::Listener, public SelectFileDialog::Listener { public: - WifiConfigView(NetworkConfigView* parent, WifiNetwork wifi); + WifiConfigView(NetworkConfigView* parent, const WifiNetwork* wifi); explicit WifiConfigView(NetworkConfigView* parent); - virtual ~WifiConfigView() {} + virtual ~WifiConfigView(); // views::Textfield::Controller methods. virtual void ContentsChanged(views::Textfield* sender, @@ -44,6 +47,10 @@ class WifiConfigView : public views::View, // views::ButtonListener virtual void ButtonPressed(views::Button* sender, const views::Event& event); + // views::Combobox::Listener + virtual void ItemChanged(views::Combobox* combo_box, + int prev_index, int new_index); + // SelectFileDialog::Listener implementation. virtual void FileSelected(const FilePath& path, int index, void* params); @@ -69,6 +76,16 @@ class WifiConfigView : public views::View, FRIEND_TEST_ALL_PREFIXES(WifiConfigViewTest, ChangeAutoConnectSaveTest); FRIEND_TEST_ALL_PREFIXES(WifiConfigViewTest, ChangePasswordSaveTest); + class SecurityComboboxModel : public ComboboxModel { + public: + SecurityComboboxModel() {} + virtual ~SecurityComboboxModel() {} + virtual int GetItemCount(); + virtual string16 GetItemAt(int index); + private: + DISALLOW_COPY_AND_ASSIGN(SecurityComboboxModel); + }; + // Initializes UI. void Init(); @@ -86,13 +103,14 @@ class WifiConfigView : public views::View, // contents change. bool can_login_; - WifiNetwork wifi_; + scoped_ptr<WifiNetwork> wifi_; views::Textfield* ssid_textfield_; views::Textfield* identity_textfield_; views::NativeButton* certificate_browse_button_; scoped_refptr<SelectFileDialog> select_file_dialog_; std::string certificate_path_; + views::Combobox* security_combobox_; views::Textfield* passphrase_textfield_; views::ImageButton* passphrase_visible_button_; views::Checkbox* autoconnect_checkbox_; diff --git a/chrome/browser/chromeos/options/wifi_config_view_browsertest.cc b/chrome/browser/chromeos/options/wifi_config_view_browsertest.cc index 4c26499..98f4e1f 100644 --- a/chrome/browser/chromeos/options/wifi_config_view_browsertest.cc +++ b/chrome/browser/chromeos/options/wifi_config_view_browsertest.cc @@ -32,16 +32,17 @@ class WifiConfigViewTest : public CrosInProcessBrowserTest { // Test that if nothing is changed, we don't call SaveWifiNetwork. IN_PROC_BROWSER_TEST_F(WifiConfigViewTest, NoChangeSaveTest) { EXPECT_CALL(*mock_network_library_, SaveWifiNetwork(_)).Times(0); - WifiConfigView* view = new WifiConfigView(NULL, WifiNetwork()); + scoped_ptr<WifiNetwork> network(new WifiNetwork()); + WifiConfigView* view = new WifiConfigView(NULL, network.get()); view->Save(); } // Test that if autoconnect was changed, we call SaveWifiNetwork. IN_PROC_BROWSER_TEST_F(WifiConfigViewTest, ChangeAutoConnectSaveTest) { EXPECT_CALL(*mock_network_library_, SaveWifiNetwork(_)).Times(1); - WifiNetwork remembered_network = WifiNetwork(); - remembered_network.set_favorite(true); - WifiConfigView* view = new WifiConfigView(NULL, remembered_network); + scoped_ptr<WifiNetwork> remembered_network(new WifiNetwork()); + remembered_network->set_favorite(true); + WifiConfigView* view = new WifiConfigView(NULL, remembered_network.get()); ASSERT_TRUE(view->autoconnect_checkbox_ != NULL); view->autoconnect_checkbox_->SetChecked( !view->autoconnect_checkbox_->checked()); @@ -51,9 +52,9 @@ IN_PROC_BROWSER_TEST_F(WifiConfigViewTest, ChangeAutoConnectSaveTest) { // Test that if password was changed, we call SaveWifiNetwork. IN_PROC_BROWSER_TEST_F(WifiConfigViewTest, ChangePasswordSaveTest) { EXPECT_CALL(*mock_network_library_, SaveWifiNetwork(_)).Times(1); - WifiNetwork wifi = WifiNetwork(); - wifi.set_encryption(SECURITY_WEP); - WifiConfigView* view = new WifiConfigView(NULL, wifi); + scoped_ptr<WifiNetwork> wifi(new WifiNetwork()); + wifi->set_encryption(SECURITY_WEP); + WifiConfigView* view = new WifiConfigView(NULL, wifi.get()); view->passphrase_textfield_->SetText(ASCIIToUTF16("test")); view->Save(); } diff --git a/chrome/browser/chromeos/pipe_reader_unittest.cc b/chrome/browser/chromeos/pipe_reader_unittest.cc index 3646b30..f45fe50 100644 --- a/chrome/browser/chromeos/pipe_reader_unittest.cc +++ b/chrome/browser/chromeos/pipe_reader_unittest.cc @@ -63,11 +63,13 @@ TEST_F(PipeReaderTest, SuccessfulMultiLineReadTest) { PipeReader reader(pipe_name); // asking for more should still just return the amount that was written. std::string my_foo = reader.Read(5 * line.length()); + ASSERT_GT(my_foo.length(), 0U); EXPECT_EQ(my_foo[my_foo.length() - 1], '\n'); my_foo.resize(my_foo.length() - 1); EXPECT_EQ(my_foo, foo); std::string my_boo = reader.Read(5 * line.length()); + ASSERT_GT(my_boo.length(), 0U); EXPECT_EQ(my_boo[my_boo.length() - 1], '\n'); my_boo.resize(my_boo.length() - 1); EXPECT_EQ(my_boo, boo); @@ -97,6 +99,7 @@ TEST_F(PipeReaderTest, SuccessfulMultiLineReadNoEndingNewlineTest) { PipeReader reader(pipe_name); // asking for more should still just return the amount that was written. std::string my_foo = reader.Read(5 * line.length()); + ASSERT_GT(my_foo.length(), 0U); EXPECT_EQ(my_foo[my_foo.length() - 1], '\n'); my_foo.resize(my_foo.length() - 1); EXPECT_EQ(my_foo, foo); diff --git a/chrome/browser/chromeos/plugin_selection_policy.cc b/chrome/browser/chromeos/plugin_selection_policy.cc index b27bc42..477965a 100644 --- a/chrome/browser/chromeos/plugin_selection_policy.cc +++ b/chrome/browser/chromeos/plugin_selection_policy.cc @@ -33,7 +33,8 @@ namespace chromeos { static const char kPluginSelectionPolicyFile[] = "/usr/share/chromeos-assets/flash/plugin_policy"; -PluginSelectionPolicy::PluginSelectionPolicy() : initialized_(false) { +PluginSelectionPolicy::PluginSelectionPolicy() + : init_from_file_finished_(false) { } void PluginSelectionPolicy::StartInit() { @@ -52,15 +53,13 @@ bool PluginSelectionPolicy::InitFromFile(const FilePath& policy_file) { // This must always be called from the FILE thread. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - // Note: We're "initialized" even if loading the file fails. - initialized_ = true; - string data; // This should be a really small file, so we're OK with just // slurping it. if (!file_util::ReadFileToString(policy_file, &data)) { LOG(ERROR) << "Unable to read plugin policy file \"" << policy_file.value() << "\"."; + init_from_file_finished_ = true; return false; } @@ -81,6 +80,7 @@ bool PluginSelectionPolicy::InitFromFile(const FilePath& policy_file) { // Has to be preceeded by a "plugin" statement. if (last_plugin.empty()) { LOG(ERROR) << "Plugin policy file error: 'allow' out of context."; + init_from_file_finished_ = true; return false; } line = line.substr(6); @@ -92,6 +92,7 @@ bool PluginSelectionPolicy::InitFromFile(const FilePath& policy_file) { // Has to be preceeded by a "plugin" statement. if (last_plugin.empty()) { LOG(ERROR) << "Plugin policy file error: 'deny' out of context."; + init_from_file_finished_ = true; return false; } line = line.substr(5); @@ -113,6 +114,7 @@ bool PluginSelectionPolicy::InitFromFile(const FilePath& policy_file) { policies.insert(make_pair(last_plugin, policy)); policies_.swap(policies); + init_from_file_finished_ = true; return true; } @@ -136,7 +138,8 @@ bool PluginSelectionPolicy::IsAllowed(const GURL& url, // initialization is complete. Right now it is guaranteed only by // the startup order and the fact that InitFromFile runs on the FILE // thread too. - DCHECK(initialized_) << "Tried to check policy before policy is initialized."; + DCHECK(init_from_file_finished_) + << "Tried to check policy before policy is initialized."; string name = path.BaseName().value(); diff --git a/chrome/browser/chromeos/plugin_selection_policy.h b/chrome/browser/chromeos/plugin_selection_policy.h index 4786e3c..1bf60a0 100644 --- a/chrome/browser/chromeos/plugin_selection_policy.h +++ b/chrome/browser/chromeos/plugin_selection_policy.h @@ -56,9 +56,10 @@ class PluginSelectionPolicy private: // To allow access to InitFromFile FRIEND_TEST_ALL_PREFIXES(PluginSelectionPolicyTest, Basic); + FRIEND_TEST_ALL_PREFIXES(PluginSelectionPolicyTest, FindFirstAllowed); FRIEND_TEST_ALL_PREFIXES(PluginSelectionPolicyTest, InitFromFile); FRIEND_TEST_ALL_PREFIXES(PluginSelectionPolicyTest, IsAllowed); - FRIEND_TEST_ALL_PREFIXES(PluginSelectionPolicyTest, FindFirstAllowed); + FRIEND_TEST_ALL_PREFIXES(PluginSelectionPolicyTest, MissingFile); // Initializes from the default policy file. bool Init(); @@ -70,7 +71,14 @@ class PluginSelectionPolicy typedef std::map<std::string, Policy> PolicyMap; PolicyMap policies_; - bool initialized_; + + // This is used to DCHECK if we try and call IsAllowed or + // FindFirstAllowed before we've finished executing InitFromFile. + // Note: We're "finished" even if loading the file fails -- the + // point of the DCHECK is to make sure we haven't violated our + // ordering/threading assumptions, not to make sure that we're + // properly initialized. + bool init_from_file_finished_; DISALLOW_COPY_AND_ASSIGN(PluginSelectionPolicy); }; diff --git a/chrome/browser/chromeos/plugin_selection_policy_unittest.cc b/chrome/browser/chromeos/plugin_selection_policy_unittest.cc index 455ed37..4bc4538 100644 --- a/chrome/browser/chromeos/plugin_selection_policy_unittest.cc +++ b/chrome/browser/chromeos/plugin_selection_policy_unittest.cc @@ -26,23 +26,23 @@ using std::vector; namespace chromeos { const char kBasicPolicy[] = "# This is a basic policy\n" - "plugin test.so\n" - "allow foo.com\n" - "deny bar.com\n"; + "plugin test.so\n" + "allow foo.com\n" + "deny bar.com\n"; const char kNoPluginPolicy[] = "# This is a policy with missing plugin.\n" - "# Missing plugin test.so\n" - "allow foo.com\n" - "deny bar.com\n"; + "# Missing plugin test.so\n" + "allow foo.com\n" + "deny bar.com\n"; const char kNoRulesPolicy[] = "# This is a policy with no rules\n" - "plugin test.so\n"; + "plugin test.so\n"; const char kEmptyPolicy[] = "# This is an empty policy\n"; const char kCommentTestPolicy[] = "# This is a policy with inline comments.\n" - "plugin test.so# like this\n" - "allow foo.com # and this\n" - "deny bar.com # and this\n"; + "plugin test.so# like this\n" + "allow foo.com # and this\n" + "deny bar.com # and this\n"; const char kMultiPluginTestPolicy[] = "# This is a policy with multiple plugins.\n" @@ -59,9 +59,9 @@ const char kMultiPluginTestPolicy[] = "allow bim.com\n"; const char kWhitespaceTestPolicy[] = "# This is a policy with odd whitespace.\n" - " plugin\ttest.so# like this\n" - "\n\n \n allow\t\tfoo.com # and this\n" - "\tdeny bar.com\t\t\t# and this \n"; + " plugin\ttest.so# like this\n" + "\n\n \n allow\t\tfoo.com # and this\n" + "\tdeny bar.com\t\t\t# and this \n"; class PluginSelectionPolicyTest : public PlatformTest { public: @@ -79,14 +79,19 @@ class PluginSelectionPolicyTest : public PlatformTest { bool CreatePolicy(const std::string& name, const std::string& contents, FilePath* path) { - FilePath policy_file(temp_dir_.path()); - policy_file = policy_file.Append(FilePath(name)); - int bytes_written = file_util::WriteFile(policy_file, - contents.c_str(), - contents.size()); + FilePath policy_file = GetPolicyPath(name); + size_t bytes_written = file_util::WriteFile(policy_file, + contents.c_str(), + contents.size()); if (path) *path = policy_file; - return bytes_written >= 0; + + return bytes_written == contents.size(); + } + + FilePath GetPolicyPath(const std::string& name) { + FilePath policy_file(temp_dir_.path()); + return policy_file.Append(FilePath(name)); } private: @@ -160,37 +165,48 @@ TEST_F(PluginSelectionPolicyTest, IsAllowed) { scoped_refptr<PluginSelectionPolicy> policy1 = new PluginSelectionPolicy; ASSERT_TRUE(policy1->InitFromFile(path)); EXPECT_TRUE(policy1->IsAllowed(GURL("http://www.foo.com/blah.html"), - FilePath("/usr/local/bin/test.so"))); + FilePath("/usr/local/bin/test.so"))); EXPECT_FALSE(policy1->IsAllowed(GURL("http://www.bar.com/blah.html"), - FilePath("/usr/local/bin/test.so"))); + FilePath("/usr/local/bin/test.so"))); EXPECT_FALSE(policy1->IsAllowed(GURL("http://www.baz.com/blah.html"), - FilePath("/usr/local/bin/test.so"))); + FilePath("/usr/local/bin/test.so"))); EXPECT_TRUE(policy1->IsAllowed(GURL("http://www.baz.com/blah.html"), - FilePath("/usr/local/bin/real.so"))); + FilePath("/usr/local/bin/real.so"))); scoped_refptr<PluginSelectionPolicy> policy2 = new PluginSelectionPolicy; ASSERT_TRUE(CreatePolicy("no_rules", kNoRulesPolicy, &path)); ASSERT_TRUE(policy2->InitFromFile(path)); EXPECT_FALSE(policy2->IsAllowed(GURL("http://www.foo.com/blah.html"), - FilePath("/usr/local/bin/test.so"))); + FilePath("/usr/local/bin/test.so"))); EXPECT_FALSE(policy2->IsAllowed(GURL("http://www.bar.com/blah.html"), - FilePath("/usr/local/bin/test.so"))); + FilePath("/usr/local/bin/test.so"))); EXPECT_FALSE(policy2->IsAllowed(GURL("http://www.baz.com/blah.html"), - FilePath("/usr/local/bin/test.so"))); + FilePath("/usr/local/bin/test.so"))); EXPECT_TRUE(policy2->IsAllowed(GURL("http://www.baz.com/blah.html"), - FilePath("/usr/local/bin/real.so"))); + FilePath("/usr/local/bin/real.so"))); scoped_refptr<PluginSelectionPolicy> policy3 = new PluginSelectionPolicy; ASSERT_TRUE(CreatePolicy("empty", kEmptyPolicy, &path)); ASSERT_TRUE(policy3->InitFromFile(path)); EXPECT_TRUE(policy3->IsAllowed(GURL("http://www.foo.com/blah.html"), - FilePath("/usr/local/bin/test.so"))); + FilePath("/usr/local/bin/test.so"))); EXPECT_TRUE(policy3->IsAllowed(GURL("http://www.bar.com/blah.html"), - FilePath("/usr/local/bin/test.so"))); + FilePath("/usr/local/bin/test.so"))); EXPECT_TRUE(policy3->IsAllowed(GURL("http://www.baz.com/blah.html"), - FilePath("/usr/local/bin/test.so"))); + FilePath("/usr/local/bin/test.so"))); EXPECT_TRUE(policy3->IsAllowed(GURL("http://www.baz.com/blah.html"), - FilePath("/usr/local/bin/real.so"))); + FilePath("/usr/local/bin/real.so"))); +} + +TEST_F(PluginSelectionPolicyTest, MissingFile) { + // Don't create any policy file, just get the path to one. + FilePath path = GetPolicyPath("missing_file"); + scoped_refptr<PluginSelectionPolicy> policy = new PluginSelectionPolicy; + ASSERT_FALSE(policy->InitFromFile(path)); + EXPECT_TRUE(policy->IsAllowed(GURL("http://www.foo.com/blah.html"), + FilePath("/usr/local/bin/allow_foo.so"))); + EXPECT_TRUE(policy->IsAllowed(GURL("http://www.bar.com/blah.html"), + FilePath("/usr/local/bin/allow_foo.so"))); } TEST_F(PluginSelectionPolicyTest, FindFirstAllowed) { @@ -199,33 +215,33 @@ TEST_F(PluginSelectionPolicyTest, FindFirstAllowed) { scoped_refptr<PluginSelectionPolicy> policy = new PluginSelectionPolicy; ASSERT_TRUE(policy->InitFromFile(path)); EXPECT_TRUE(policy->IsAllowed(GURL("http://www.foo.com/blah.html"), - FilePath("/usr/local/bin/allow_foo.so"))); - EXPECT_FALSE(policy->IsAllowed(GURL("http://www.bar.com/blah.html"), FilePath("/usr/local/bin/allow_foo.so"))); + EXPECT_FALSE(policy->IsAllowed(GURL("http://www.bar.com/blah.html"), + FilePath("/usr/local/bin/allow_foo.so"))); EXPECT_FALSE(policy->IsAllowed(GURL("http://www.baz.com/blah.html"), - FilePath("/usr/local/bin/allow_foo.so"))); + FilePath("/usr/local/bin/allow_foo.so"))); EXPECT_FALSE(policy->IsAllowed(GURL("http://www.bim.com/blah.html"), - FilePath("/usr/local/bin/allow_foo.so"))); + FilePath("/usr/local/bin/allow_foo.so"))); EXPECT_FALSE(policy->IsAllowed(GURL("http://www.foo.com/blah.html"), - FilePath("/usr/local/bin/allow_baz_bim1.so"))); + FilePath("/usr/local/bin/allow_baz_bim1.so"))); EXPECT_FALSE(policy->IsAllowed(GURL("http://www.bar.com/blah.html"), - FilePath("/usr/local/bin/allow_baz_bim1.so"))); + FilePath("/usr/local/bin/allow_baz_bim1.so"))); EXPECT_TRUE(policy->IsAllowed(GURL("http://www.baz.com/blah.html"), - FilePath("/usr/local/bin/allow_baz_bim1.so"))); + FilePath("/usr/local/bin/allow_baz_bim1.so"))); EXPECT_TRUE(policy->IsAllowed(GURL("http://www.bim.com/blah.html"), - FilePath("/usr/local/bin/allow_baz_bim1.so"))); - EXPECT_FALSE(policy->IsAllowed(GURL("http://www.google.com/blah.html"), FilePath("/usr/local/bin/allow_baz_bim1.so"))); + EXPECT_FALSE(policy->IsAllowed(GURL("http://www.google.com/blah.html"), + FilePath("/usr/local/bin/allow_baz_bim1.so"))); EXPECT_FALSE(policy->IsAllowed(GURL("http://www.foo.com/blah.html"), - FilePath("/usr/local/bin/allow_baz_bim2.so"))); + FilePath("/usr/local/bin/allow_baz_bim2.so"))); EXPECT_FALSE(policy->IsAllowed(GURL("http://www.bar.com/blah.html"), - FilePath("/usr/local/bin/allow_baz_bim2.so"))); + FilePath("/usr/local/bin/allow_baz_bim2.so"))); EXPECT_TRUE(policy->IsAllowed(GURL("http://www.baz.com/blah.html"), - FilePath("/usr/local/bin/allow_baz_bim2.so"))); + FilePath("/usr/local/bin/allow_baz_bim2.so"))); EXPECT_TRUE(policy->IsAllowed(GURL("http://www.bim.com/blah.html"), - FilePath("/usr/local/bin/allow_baz_bim2.so"))); - EXPECT_FALSE(policy->IsAllowed(GURL("http://www.google.com/blah.html"), FilePath("/usr/local/bin/allow_baz_bim2.so"))); + EXPECT_FALSE(policy->IsAllowed(GURL("http://www.google.com/blah.html"), + FilePath("/usr/local/bin/allow_baz_bim2.so"))); std::vector<WebPluginInfo> info_vector; WebPluginInfo info; // First we test that the one without any policy gets @@ -239,13 +255,13 @@ TEST_F(PluginSelectionPolicyTest, FindFirstAllowed) { info.path = FilePath("/usr/local/bin/allow_baz_bim2.so"); info_vector.push_back(info); EXPECT_EQ(0, policy->FindFirstAllowed(GURL("http://www.baz.com/blah.html"), - info_vector)); + info_vector)); EXPECT_EQ(0, policy->FindFirstAllowed(GURL("http://www.foo.com/blah.html"), - info_vector)); + info_vector)); EXPECT_EQ(0, policy->FindFirstAllowed(GURL("http://www.bling.com/blah.html"), - info_vector)); + info_vector)); EXPECT_EQ(0, policy->FindFirstAllowed(GURL("http://www.google.com/blah.html"), - info_vector)); + info_vector)); // Now move the plugin without any policy to the end. info_vector.clear(); @@ -258,13 +274,13 @@ TEST_F(PluginSelectionPolicyTest, FindFirstAllowed) { info.path = FilePath("/usr/local/bin/no_policy.so"); info_vector.push_back(info); EXPECT_EQ(1, policy->FindFirstAllowed(GURL("http://www.baz.com/blah.html"), - info_vector)); + info_vector)); EXPECT_EQ(0, policy->FindFirstAllowed(GURL("http://www.foo.com/blah.html"), - info_vector)); + info_vector)); EXPECT_EQ(3, policy->FindFirstAllowed(GURL("http://www.bling.com/blah.html"), - info_vector)); + info_vector)); EXPECT_EQ(3, policy->FindFirstAllowed(GURL("http://www.google.com/blah.html"), - info_vector)); + info_vector)); } } // namespace chromeos diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc index 2e1e0e2..12c1502 100644 --- a/chrome/browser/chromeos/preferences.cc +++ b/chrome/browser/chromeos/preferences.cc @@ -14,10 +14,8 @@ #include "chrome/browser/chromeos/cros/power_library.h" #include "chrome/browser/chromeos/cros/touchpad_library.h" #include "chrome/browser/chromeos/input_method/input_method_util.h" -#include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/prefs/pref_service.h" -#include "chrome/browser/profile.h" #include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" #include "unicode/timezone.h" @@ -25,11 +23,10 @@ namespace chromeos { static const char kFallbackInputMethodLocale[] = "en-US"; -static const char kTalkAppExtensionId[] = "ggnioahjipcehijkhpdjekioddnjoben"; -Preferences::Preferences(Profile* profile) - : profile_(profile) { -} +Preferences::Preferences() {} + +Preferences::~Preferences() {} // static void Preferences::RegisterUserPrefs(PrefService* prefs) { @@ -42,7 +39,6 @@ void Preferences::RegisterUserPrefs(PrefService* prefs) { if (prefs->FindPreference(prefs::kAccessibilityEnabled) == NULL) { prefs->RegisterBooleanPref(prefs::kAccessibilityEnabled, false); } - prefs->RegisterIntegerPref(prefs::kLabsTalkEnabled, 1); prefs->RegisterIntegerPref(prefs::kTouchpadSensitivity, 3); prefs->RegisterStringPref(prefs::kLanguageCurrentInputMethod, ""); prefs->RegisterStringPref(prefs::kLanguagePreviousInputMethod, ""); @@ -185,8 +181,6 @@ void Preferences::Init(PrefService* prefs) { language_xkb_auto_repeat_interval_pref_.Init( prefs::kLanguageXkbAutoRepeatInterval, prefs, this); - labs_talk_enabled_.Init(prefs::kLabsTalkEnabled, prefs, this); - enable_screen_lock_.Init(prefs::kEnableScreenLock, prefs, this); // Initialize touchpad settings to what's saved in user preferences. @@ -346,11 +340,6 @@ void Preferences::NotifyPrefChanged(const std::string* pref_name) { UpdateAutoRepeatRate(); } - // Listen for explicit changes as ExtensionsService handles startup case. - if (pref_name && *pref_name == prefs::kLabsTalkEnabled) { - UpdateTalkApp(); - } - // Init or update power manager config. if (!pref_name || *pref_name == prefs::kEnableScreenLock) { CrosLibrary::Get()->GetPowerLibrary()->EnableScreenLock( @@ -404,7 +393,7 @@ void Preferences::SetLanguageConfigStringList( void Preferences::SetLanguageConfigStringListAsCSV(const char* section, const char* name, const std::string& value) { - LOG(INFO) << "Setting " << name << " to '" << value << "'"; + VLOG(1) << "Setting " << name << " to '" << value << "'"; std::vector<std::string> split_values; if (!value.empty()) @@ -446,17 +435,4 @@ void Preferences::UpdateAutoRepeatRate() { CrosLibrary::Get()->GetKeyboardLibrary()->SetAutoRepeatRate(rate); } -void Preferences::UpdateTalkApp() { - if (!profile_->GetExtensionsService()->is_ready()) { - NOTREACHED() << "Extensions service should be ready"; - return; - } - - if (labs_talk_enabled_.GetValue() == 0) { - profile_->GetExtensionsService()->DisableExtension(kTalkAppExtensionId); - } else { - profile_->GetExtensionsService()->EnableExtension(kTalkAppExtensionId); - } -} - } // namespace chromeos diff --git a/chrome/browser/chromeos/preferences.h b/chrome/browser/chromeos/preferences.h index 9269915..6568961 100644 --- a/chrome/browser/chromeos/preferences.h +++ b/chrome/browser/chromeos/preferences.h @@ -14,7 +14,6 @@ #include "chrome/common/notification_observer.h" class PrefService; -class Profile; namespace chromeos { @@ -24,8 +23,8 @@ namespace chromeos { // When the preferences change, we change the settings to reflect the new value. class Preferences : public NotificationObserver { public: - explicit Preferences(Profile* profile); - virtual ~Preferences() {} + Preferences(); + virtual ~Preferences(); // This method will register the prefs associated with Chrome OS settings. static void RegisterUserPrefs(PrefService* prefs); @@ -83,11 +82,6 @@ class Preferences : public NotificationObserver { // underlying XKB API requires it. void UpdateAutoRepeatRate(); - // Updates whether the Talk app is enabled. - void UpdateTalkApp(); - - Profile* profile_; - BooleanPrefMember tap_to_click_enabled_; BooleanPrefMember vert_edge_scroll_enabled_; BooleanPrefMember accessibility_enabled_; @@ -126,9 +120,6 @@ class Preferences : public NotificationObserver { IntegerPrefMember language_xkb_auto_repeat_delay_pref_; IntegerPrefMember language_xkb_auto_repeat_interval_pref_; - // Labs preferences. - IntegerPrefMember labs_talk_enabled_; - BooleanPrefMember enable_screen_lock_; DISALLOW_COPY_AND_ASSIGN(Preferences); diff --git a/chrome/browser/chromeos/proxy_config_service_impl.cc b/chrome/browser/chromeos/proxy_config_service_impl.cc index 888b604..73693fd 100644 --- a/chrome/browser/chromeos/proxy_config_service_impl.cc +++ b/chrome/browser/chromeos/proxy_config_service_impl.cc @@ -358,10 +358,10 @@ ProxyConfigServiceImpl::ProxyConfigServiceImpl() retrieve_property_op_ = SignedSettings::CreateRetrievePropertyOp( kSettingProxyEverywhere, this); if (retrieve_property_op_ && retrieve_property_op_->Execute()) { - LOG(INFO) << "Start retrieving proxy setting from device"; + VLOG(1) << "Start retrieving proxy setting from device"; use_default = false; } else { - LOG(INFO) << "Fail to retrieve proxy setting from device"; + VLOG(1) << "Fail to retrieve proxy setting from device"; } } if (use_default) @@ -409,12 +409,8 @@ bool ProxyConfigServiceImpl::UISetProxyConfigToPACScript(const GURL& pac_url) { CheckCurrentlyOnUIThread(); reference_config_.mode = ProxyConfig::MODE_PAC_SCRIPT; reference_config_.automatic_proxy.pac_url = pac_url; - if (pac_url.is_valid()) { - OnUISetProxyConfig(true); - return true; - } - LOG(INFO) << "Cannot set proxy: invalid pac url"; - return false; + OnUISetProxyConfig(true); + return true; } bool ProxyConfigServiceImpl::UISetProxyConfigToSingleProxy( @@ -423,12 +419,8 @@ bool ProxyConfigServiceImpl::UISetProxyConfigToSingleProxy( CheckCurrentlyOnUIThread(); reference_config_.mode = ProxyConfig::MODE_SINGLE_PROXY; reference_config_.single_proxy.server = server; - if (server.is_valid()) { - OnUISetProxyConfig(true); - return true; - } - LOG(INFO) << "Cannot set proxy: invalid single server"; - return false; + OnUISetProxyConfig(true); + return true; } bool ProxyConfigServiceImpl::UISetProxyConfigToProxyPerScheme( @@ -450,12 +442,8 @@ bool ProxyConfigServiceImpl::UISetProxyConfigToProxyPerScheme( } reference_config_.mode = ProxyConfig::MODE_PROXY_PER_SCHEME; proxy->server = server; - if (server.is_valid()) { - OnUISetProxyConfig(true); - return true; - } - LOG(INFO) << "Cannot set proxy: invalid " << scheme << " server"; - return false; + OnUISetProxyConfig(true); + return true; } bool ProxyConfigServiceImpl::UISetProxyConfigBypassRules( @@ -466,8 +454,8 @@ bool ProxyConfigServiceImpl::UISetProxyConfigBypassRules( reference_config_.mode == ProxyConfig::MODE_PROXY_PER_SCHEME); if (reference_config_.mode != ProxyConfig::MODE_SINGLE_PROXY && reference_config_.mode != ProxyConfig::MODE_PROXY_PER_SCHEME) { - LOG(INFO) << "Cannot set bypass rules for proxy mode [" - << reference_config_.mode << "]"; + VLOG(1) << "Cannot set bypass rules for proxy mode [" + << reference_config_.mode << "]"; return false; } reference_config_.bypass_rules = bypass_rules; @@ -501,14 +489,14 @@ bool ProxyConfigServiceImpl::IOGetProxyConfig(net::ProxyConfig* net_config) { } void ProxyConfigServiceImpl::OnSettingsOpSucceeded(bool value) { - LOG(INFO) << "Stored proxy setting to device"; + VLOG(1) << "Stored proxy setting to device"; store_property_op_ = NULL; if (persist_to_device_pending_) PersistConfigToDevice(); } void ProxyConfigServiceImpl::OnSettingsOpSucceeded(std::string value) { - LOG(INFO) << "Retrieved proxy setting from device, value=[" << value << "]"; + VLOG(1) << "Retrieved proxy setting from device, value=[" << value << "]"; if (reference_config_.Deserialize(value)) { OnUISetProxyConfig(false); } else { @@ -534,7 +522,7 @@ void ProxyConfigServiceImpl::OnSettingsOpFailed() { //------------------ ProxyConfigServiceImpl: private methods ------------------- void ProxyConfigServiceImpl::InitConfigToDefault(bool post_to_io_thread) { - LOG(INFO) << "Using default proxy config: auto-detect"; + VLOG(1) << "Using default proxy config: auto-detect"; reference_config_.mode = ProxyConfig::MODE_AUTO_DETECT; reference_config_.automatic_proxy.source = ProxyConfig::SOURCE_OWNER; if (post_to_io_thread && can_post_task_) { @@ -551,14 +539,14 @@ void ProxyConfigServiceImpl::PersistConfigToDevice() { persist_to_device_pending_ = false; std::string value; if (!reference_config_.Serialize(&value)) { - LOG(INFO) << "Error serializing proxy config"; + VLOG(1) << "Error serializing proxy config"; return; } store_property_op_ = SignedSettings::CreateStorePropertyOp( kSettingProxyEverywhere, value, this); bool rc = store_property_op_->Execute(); - LOG(INFO) << "Start storing proxy setting to device, value=" << value - << ", rc=" << rc; + VLOG(1) << "Start storing proxy setting to device, value=" << value << ", rc=" + << rc; } void ProxyConfigServiceImpl::OnUISetProxyConfig(bool persist_to_device) { @@ -567,14 +555,14 @@ void ProxyConfigServiceImpl::OnUISetProxyConfig(bool persist_to_device) { Task* task = NewRunnableMethod(this, &ProxyConfigServiceImpl::IOSetProxyConfig, reference_config_); if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { - LOG(INFO) << "Couldn't post task to IO thread to set new proxy config"; + VLOG(1) << "Couldn't post task to IO thread to set new proxy config"; delete task; } if (persist_to_device && CrosLibrary::Get()->EnsureLoaded()) { if (store_property_op_) { persist_to_device_pending_ = true; - LOG(INFO) << "Pending persisting proxy setting to device"; + VLOG(1) << "Pending persisting proxy setting to device"; } else { PersistConfigToDevice(); } @@ -592,7 +580,7 @@ void ProxyConfigServiceImpl::CheckCurrentlyOnUIThread() { void ProxyConfigServiceImpl::IOSetProxyConfig(const ProxyConfig& new_config) { // This is called on the IO thread (posted from UI thread). CheckCurrentlyOnIOThread(); - LOG(INFO) << "Proxy configuration changed"; + VLOG(1) << "Proxy configuration changed"; has_config_ = true; cached_config_ = new_config; // Notify observers of new proxy config. diff --git a/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc b/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc index b1812a6..46fc3ff 100644 --- a/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc +++ b/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc @@ -388,55 +388,42 @@ TEST_F(ProxyConfigServiceImplTest, ModifyFromUI) { const Input& input = tests[i].input; switch (input.mode) { case MK_MODE(DIRECT) : - EXPECT_EQ(tests[i].is_valid, - config_service()->UISetProxyConfigToDirect()); + config_service()->UISetProxyConfigToDirect(); break; case MK_MODE(AUTO_DETECT) : - EXPECT_EQ(tests[i].is_valid, - config_service()->UISetProxyConfigToAutoDetect()); + config_service()->UISetProxyConfigToAutoDetect(); break; case MK_MODE(PAC_SCRIPT) : - EXPECT_EQ(tests[i].is_valid, - config_service()->UISetProxyConfigToPACScript( - GURL(input.pac_url))); + config_service()->UISetProxyConfigToPACScript(GURL(input.pac_url)); break; case MK_MODE(SINGLE_PROXY) : - EXPECT_EQ(tests[i].is_valid, - config_service()->UISetProxyConfigToSingleProxy( - net::ProxyServer::FromURI(input.single_uri, - MK_SCHM(HTTP)))); + config_service()->UISetProxyConfigToSingleProxy( + net::ProxyServer::FromURI(input.single_uri, MK_SCHM(HTTP))); if (input.bypass_rules) { bypass_rules.ParseFromStringUsingSuffixMatching(input.bypass_rules); - EXPECT_EQ(tests[i].is_valid, - config_service()->UISetProxyConfigBypassRules( - bypass_rules)); + config_service()->UISetProxyConfigBypassRules(bypass_rules); } break; case MK_MODE(PROXY_PER_SCHEME) : if (input.http_uri) { - EXPECT_EQ(tests[i].is_valid, - config_service()->UISetProxyConfigToProxyPerScheme("http", - net::ProxyServer::FromURI(input.http_uri, MK_SCHM(HTTP)))); + config_service()->UISetProxyConfigToProxyPerScheme("http", + net::ProxyServer::FromURI(input.http_uri, MK_SCHM(HTTP))); } if (input.https_uri) { - EXPECT_EQ(tests[i].is_valid, - config_service()->UISetProxyConfigToProxyPerScheme("https", - net::ProxyServer::FromURI(input.https_uri, MK_SCHM(HTTPS)))); + config_service()->UISetProxyConfigToProxyPerScheme("https", + net::ProxyServer::FromURI(input.https_uri, MK_SCHM(HTTPS))); } if (input.ftp_uri) { - EXPECT_EQ(tests[i].is_valid, - config_service()->UISetProxyConfigToProxyPerScheme("ftp", - net::ProxyServer::FromURI(input.ftp_uri, MK_SCHM(HTTP)))); + config_service()->UISetProxyConfigToProxyPerScheme("ftp", + net::ProxyServer::FromURI(input.ftp_uri, MK_SCHM(HTTP))); } if (input.socks_uri) { - EXPECT_EQ(tests[i].is_valid, - config_service()->UISetProxyConfigToProxyPerScheme("socks", - net::ProxyServer::FromURI(input.socks_uri, MK_SCHM(SOCKS4)))); + config_service()->UISetProxyConfigToProxyPerScheme("socks", + net::ProxyServer::FromURI(input.socks_uri, MK_SCHM(SOCKS4))); } if (input.bypass_rules) { bypass_rules.ParseFromStringUsingSuffixMatching(input.bypass_rules); - EXPECT_EQ(tests[i].is_valid, - config_service()->UISetProxyConfigBypassRules(bypass_rules)); + config_service()->UISetProxyConfigBypassRules(bypass_rules); } break; } @@ -552,8 +539,8 @@ TEST_F(ProxyConfigServiceImplTest, SerializeAndDeserialize) { src_serializer.Serialize(*net_src_cfg.ToValue()); JSONStringValueSerializer tgt_serializer(&tgt_output); tgt_serializer.Serialize(*net_tgt_cfg.ToValue()); - LOG(INFO) << "source:\n" << src_output; - LOG(INFO) << "target:\n" << tgt_output; + VLOG(1) << "source:\n" << src_output + << "\ntarget:\n" << tgt_output; } #endif // !defined(NDEBUG) EXPECT_TRUE(net_src_cfg.Equals(net_tgt_cfg)); diff --git a/chrome/browser/chromeos/status/clock_menu_button.cc b/chrome/browser/chromeos/status/clock_menu_button.cc index f63593f..c5e4bd4 100644 --- a/chrome/browser/chromeos/status/clock_menu_button.cc +++ b/chrome/browser/chromeos/status/clock_menu_button.cc @@ -34,7 +34,7 @@ ClockMenuButton::ClockMenuButton(StatusAreaHost* host) set_border(NULL); set_use_menu_button_paint(true); SetFont(ResourceBundle::GetSharedInstance().GetFont( - ResourceBundle::BaseFont).DeriveFont(1, gfx::Font::BOLD)); + ResourceBundle::BaseFont).DeriveFont(1)); SetEnabledColor(0xB3FFFFFF); // White with 70% Alpha SetShowMultipleIconStates(false); set_alignment(TextButton::ALIGN_CENTER); diff --git a/chrome/browser/chromeos/status/clock_menu_button.h b/chrome/browser/chromeos/status/clock_menu_button.h index 405d00a..76f214a 100644 --- a/chrome/browser/chromeos/status/clock_menu_button.h +++ b/chrome/browser/chromeos/status/clock_menu_button.h @@ -59,6 +59,9 @@ class ClockMenuButton : public StatusAreaButton, // changes. void UpdateText(); + protected: + virtual int horizontal_padding() { return 3; } + private: // views::ViewMenuDelegate implementation. virtual void RunMenu(views::View* source, const gfx::Point& pt); diff --git a/chrome/browser/chromeos/status/feedback_menu_button.cc b/chrome/browser/chromeos/status/feedback_menu_button.cc index f7cb958..2a0ea52 100644 --- a/chrome/browser/chromeos/status/feedback_menu_button.cc +++ b/chrome/browser/chromeos/status/feedback_menu_button.cc @@ -8,7 +8,7 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/chromeos/status/status_area_host.h" #include "gfx/canvas.h" #include "grit/generated_resources.h" @@ -24,28 +24,11 @@ FeedbackMenuButton::FeedbackMenuButton(StatusAreaHost* host) host_(host) { DCHECK(host_); SetTooltipText(l10n_util::GetString(IDS_STATUSBAR_FEEDBACK_TOOLTIP)); -} - -FeedbackMenuButton::~FeedbackMenuButton() { -} - -//////////////////////////////////////////////////////////////////////////////// -// FeedbackMenuButton, StatusAreaButton implementation: - -void FeedbackMenuButton::DrawPressed(gfx::Canvas* canvas) { - DrawFeedbackIcon(canvas, *ResourceBundle::GetSharedInstance(). - GetBitmapNamed(IDR_STATUSBAR_FEEDBACK_PRESSED)); -} - -void FeedbackMenuButton::DrawIcon(gfx::Canvas* canvas) { - DrawFeedbackIcon(canvas, *ResourceBundle::GetSharedInstance(). + SetIcon(*ResourceBundle::GetSharedInstance(). GetBitmapNamed(IDR_STATUSBAR_FEEDBACK)); } -void FeedbackMenuButton::DrawFeedbackIcon(gfx::Canvas* canvas, SkBitmap icon) { - // Draw the battery icon 5 pixels down to center it. - static const int kIconVerticalPadding = 5; - canvas->DrawBitmapInt(icon, 0, kIconVerticalPadding); +FeedbackMenuButton::~FeedbackMenuButton() { } //////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/browser/chromeos/status/feedback_menu_button.h b/chrome/browser/chromeos/status/feedback_menu_button.h index 9e1590b..3e7d83c 100644 --- a/chrome/browser/chromeos/status/feedback_menu_button.h +++ b/chrome/browser/chromeos/status/feedback_menu_button.h @@ -26,10 +26,6 @@ class FeedbackMenuButton : public StatusAreaButton, virtual ~FeedbackMenuButton(); private: - // StatusAreaButton implementation. - virtual void DrawPressed(gfx::Canvas* canvas); - virtual void DrawIcon(gfx::Canvas* canvas); - // views::ViewMenuDelegate implementation. virtual void RunMenu(views::View* source, const gfx::Point& pt); @@ -56,9 +52,6 @@ class FeedbackMenuButton : public StatusAreaButton, virtual void ActivatedAt(int index) {} virtual void MenuWillShow() {} - // This method will draw the |icon| in the appropriate place on the |canvas|. - void DrawFeedbackIcon(gfx::Canvas* canvas, SkBitmap icon); - StatusAreaHost* host_; DISALLOW_COPY_AND_ASSIGN(FeedbackMenuButton); diff --git a/chrome/browser/chromeos/status/input_method_menu.cc b/chrome/browser/chromeos/status/input_method_menu.cc index c94e125..ae89e40 100644 --- a/chrome/browser/chromeos/status/input_method_menu.cc +++ b/chrome/browser/chromeos/status/input_method_menu.cc @@ -116,19 +116,21 @@ namespace chromeos { InputMethodMenu::InputMethodMenu(PrefService* pref_service, bool is_browser_mode, - bool is_screen_locker_mode) + bool is_screen_locker_mode, + bool is_out_of_box_experience_mode) : input_method_descriptors_(CrosLibrary::Get()->GetInputMethodLibrary()-> GetActiveInputMethods()), model_(NULL), - // Be aware that the constructor of |language_menu_| calls GetItemCount() - // in this class. Therefore, GetItemCount() have to return 0 when - // |model_| is NULL. - ALLOW_THIS_IN_INITIALIZER_LIST(language_menu_(this)), - minimum_language_menu_width_(0), + // Be aware that the constructor of |input_method_menu_| calls + // GetItemCount() in this class. Therefore, GetItemCount() have to return + // 0 when |model_| is NULL. + ALLOW_THIS_IN_INITIALIZER_LIST(input_method_menu_(this)), + minimum_input_method_menu_width_(0), pref_service_(pref_service), logged_in_(false), is_browser_mode_(is_browser_mode), - is_screen_locker_mode_(is_screen_locker_mode) { + is_screen_locker_mode_(is_screen_locker_mode), + is_out_of_box_experience_mode_(is_out_of_box_experience_mode) { DCHECK(input_method_descriptors_.get() && !input_method_descriptors_->empty()); @@ -214,7 +216,8 @@ int InputMethodMenu::GetGroupIdAt(int index) const { DCHECK_GE(index, 0); if (IndexIsInInputMethodList(index)) { - return kRadioGroupLanguage; + return is_out_of_box_experience_mode_ ? + kRadioGroupNone : kRadioGroupLanguage; } if (GetPropertyIndex(index, &index)) { @@ -276,7 +279,8 @@ menus::MenuModel::ItemType InputMethodMenu::GetTypeAt(int index) const { } if (IndexIsInInputMethodList(index)) { - return menus::MenuModel::TYPE_RADIO; + return is_out_of_box_experience_mode_ ? + menus::MenuModel::TYPE_COMMAND : menus::MenuModel::TYPE_RADIO; } if (GetPropertyIndex(index, &index)) { @@ -367,7 +371,7 @@ void InputMethodMenu::ActivatedAt(int index) { void InputMethodMenu::RunMenu( views::View* unused_source, const gfx::Point& pt) { PrepareForMenuOpen(); - language_menu_.RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT); + input_method_menu_.RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT); } //////////////////////////////////////////////////////////////////////////////// @@ -407,9 +411,9 @@ void InputMethodMenu::PrepareForMenuOpen() { input_method_descriptors_.reset(CrosLibrary::Get()->GetInputMethodLibrary()-> GetActiveInputMethods()); RebuildModel(); - language_menu_.Rebuild(); - if (minimum_language_menu_width_ > 0) { - language_menu_.SetMinimumWidth(minimum_language_menu_width_); + input_method_menu_.Rebuild(); + if (minimum_input_method_menu_width_ > 0) { + input_method_menu_.SetMinimumWidth(minimum_input_method_menu_width_); } } @@ -604,7 +608,7 @@ void InputMethodMenu::Observe(NotificationType type, void InputMethodMenu::SetMinimumWidth(int width) { // On the OOBE network selection screen, fixed width menu would be preferable. - minimum_language_menu_width_ = width; + minimum_input_method_menu_width_ = width; } } // namespace chromeos diff --git a/chrome/browser/chromeos/status/input_method_menu.h b/chrome/browser/chromeos/status/input_method_menu.h index fb87f16..a132c87 100644 --- a/chrome/browser/chromeos/status/input_method_menu.h +++ b/chrome/browser/chromeos/status/input_method_menu.h @@ -31,8 +31,11 @@ class InputMethodMenu : public views::ViewMenuDelegate, public NotificationObserver { public: InputMethodMenu(PrefService* pref_service, + // TODO(yusukes): combine the three booleans into one enum. + // http://crosbug.com/8386. bool is_browser_mode, - bool is_screen_locker); + bool is_screen_locker, + bool is_out_of_box_experience_mode); virtual ~InputMethodMenu(); // menus::MenuModel implementation. @@ -92,8 +95,8 @@ class InputMethodMenu : public views::ViewMenuDelegate, void PrepareForMenuOpen(); // Returns menu2 object for language menu. - views::Menu2& language_menu() { - return language_menu_; + views::Menu2& input_method_menu() { + return input_method_menu_; } private: @@ -141,14 +144,15 @@ class InputMethodMenu : public views::ViewMenuDelegate, scoped_ptr<menus::SimpleMenuModel> model_; // The language menu which pops up when the button in status area is clicked. - views::Menu2 language_menu_; - int minimum_language_menu_width_; + views::Menu2 input_method_menu_; + int minimum_input_method_menu_width_; PrefService* pref_service_; NotificationRegistrar registrar_; bool logged_in_; const bool is_browser_mode_; const bool is_screen_locker_mode_; + const bool is_out_of_box_experience_mode_; DISALLOW_COPY_AND_ASSIGN(InputMethodMenu); }; diff --git a/chrome/browser/chromeos/status/input_method_menu_button.cc b/chrome/browser/chromeos/status/input_method_menu_button.cc index fbc20e4..cd99210 100644 --- a/chrome/browser/chromeos/status/input_method_menu_button.cc +++ b/chrome/browser/chromeos/status/input_method_menu_button.cc @@ -36,12 +36,13 @@ InputMethodMenuButton::InputMethodMenuButton(StatusAreaHost* host) : StatusAreaButton(this), InputMethodMenu(GetPrefService(host), host->IsBrowserMode(), - host->IsScreenLockerMode()), + host->IsScreenLockerMode(), + false /* is_out_of_box_experience_mode */), host_(host) { set_border(NULL); set_use_menu_button_paint(true); SetFont(ResourceBundle::GetSharedInstance().GetFont( - ResourceBundle::BaseFont).DeriveFont(1, gfx::Font::BOLD)); + ResourceBundle::BaseFont).DeriveFont(1)); SetEnabledColor(0xB3FFFFFF); // White with 70% Alpha SetDisabledColor(0x00FFFFFF); // White with 00% Alpha (invisible) SetShowMultipleIconStates(false); @@ -57,6 +58,14 @@ InputMethodMenuButton::InputMethodMenuButton(StatusAreaHost* host) //////////////////////////////////////////////////////////////////////////////// // views::View implementation: +gfx::Size InputMethodMenuButton::GetPreferredSize() { + // If not enabled, then hide this button. + if (!IsEnabled()) { + return gfx::Size(0, 0); + } + return StatusAreaButton::GetPreferredSize(); +} + void InputMethodMenuButton::OnLocaleChanged() { input_method::OnLocaleChanged(); const InputMethodDescriptor& input_method = diff --git a/chrome/browser/chromeos/status/input_method_menu_button.h b/chrome/browser/chromeos/status/input_method_menu_button.h index 41db9a9..d7375d3 100644 --- a/chrome/browser/chromeos/status/input_method_menu_button.h +++ b/chrome/browser/chromeos/status/input_method_menu_button.h @@ -24,6 +24,7 @@ class InputMethodMenuButton : public StatusAreaButton, virtual ~InputMethodMenuButton() {} // views::View implementation. + virtual gfx::Size GetPreferredSize(); virtual void OnLocaleChanged(); private: diff --git a/chrome/browser/chromeos/status/network_dropdown_button.cc b/chrome/browser/chromeos/status/network_dropdown_button.cc index d5fd453..bf8a3be 100644 --- a/chrome/browser/chromeos/status/network_dropdown_button.cc +++ b/chrome/browser/chromeos/status/network_dropdown_button.cc @@ -34,12 +34,13 @@ NetworkDropdownButton::NetworkDropdownButton(bool browser_mode, parent_window_(parent_window) { animation_connecting_.SetThrobDuration(kThrobDuration); animation_connecting_.SetTweenType(Tween::LINEAR); - NetworkChanged(CrosLibrary::Get()->GetNetworkLibrary()); - CrosLibrary::Get()->GetNetworkLibrary()->AddObserver(this); + CrosLibrary::Get()->GetNetworkLibrary()->AddNetworkManagerObserver(this); + // The initial state will be updated on Refresh. + // See network_selection_view.cc. } NetworkDropdownButton::~NetworkDropdownButton() { - CrosLibrary::Get()->GetNetworkLibrary()->RemoveObserver(this); + CrosLibrary::Get()->GetNetworkLibrary()->RemoveNetworkManagerObserver(this); } //////////////////////////////////////////////////////////////////////////////// @@ -62,13 +63,13 @@ void NetworkDropdownButton::AnimationProgressed(const Animation* animation) { } void NetworkDropdownButton::Refresh() { - NetworkChanged(CrosLibrary::Get()->GetNetworkLibrary()); + OnNetworkManagerChanged(CrosLibrary::Get()->GetNetworkLibrary()); } //////////////////////////////////////////////////////////////////////////////// -// NetworkDropdownButton, NetworkLibrary::Observer implementation: +// NetworkDropdownButton, NetworkLibrary::NetworkManagerObserver implementation: -void NetworkDropdownButton::NetworkChanged(NetworkLibrary* cros) { +void NetworkDropdownButton::OnNetworkManagerChanged(NetworkLibrary* cros) { // Show network that we will actually use. It could be another network than // user selected. For example user selected WiFi network but we have Ethernet // connection and Chrome OS device will actually use Ethernet. @@ -78,32 +79,31 @@ void NetworkDropdownButton::NetworkChanged(NetworkLibrary* cros) { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); if (CrosLibrary::Get()->EnsureLoaded()) { - // Always show the higher priority connection first. Ethernet then wifi. - if (cros->ethernet_connected()) { + // Always show the active network, if any + const Network* active_network = cros->active_network(); + const WirelessNetwork* wireless; + if (active_network != NULL) { animation_connecting_.Stop(); - SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_WIRED)); - SetText(l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET)); - } else if (cros->wifi_connected()) { - animation_connecting_.Stop(); - SetIcon(IconForNetworkStrength( - cros->wifi_network().strength(), true)); - SetText(ASCIIToWide(cros->wifi_network().name())); - } else if (cros->cellular_connected()) { - animation_connecting_.Stop(); - SetIcon(IconForNetworkStrength( - cros->cellular_network().strength(), false)); - SetText(ASCIIToWide(cros->cellular_network().name())); + if (active_network->type() == TYPE_ETHERNET) { + SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_WIRED)); + SetText(l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET)); + } else { + DCHECK(active_network->type() == TYPE_WIFI || + active_network->type() == TYPE_CELLULAR); + wireless = static_cast<const WirelessNetwork*>(active_network); + SetIcon(IconForNetworkStrength(wireless->strength(), false)); + SetText(ASCIIToWide(wireless->name())); + } } else if (cros->wifi_connecting() || cros->cellular_connecting()) { if (!animation_connecting_.is_animating()) { animation_connecting_.Reset(); animation_connecting_.StartThrobbing(-1); SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS1_BLACK)); } - if (cros->wifi_connecting()) - SetText(ASCIIToWide(cros->wifi_network().name())); + SetText(ASCIIToWide(cros->wifi_network()->name())); else if (cros->cellular_connecting()) - SetText(ASCIIToWide(cros->cellular_network().name())); + SetText(ASCIIToWide(cros->cellular_network()->name())); } if (!cros->Connected() && !cros->Connecting()) { @@ -118,6 +118,7 @@ void NetworkDropdownButton::NetworkChanged(NetworkLibrary* cros) { } SchedulePaint(); + UpdateMenu(); } } // namespace chromeos diff --git a/chrome/browser/chromeos/status/network_dropdown_button.h b/chrome/browser/chromeos/status/network_dropdown_button.h index 47500f6..ec2e337 100644 --- a/chrome/browser/chromeos/status/network_dropdown_button.h +++ b/chrome/browser/chromeos/status/network_dropdown_button.h @@ -19,7 +19,7 @@ namespace chromeos { // See NetworkMenu for more details. class NetworkDropdownButton : public views::MenuButton, public NetworkMenu, - public NetworkLibrary::Observer { + public NetworkLibrary::NetworkManagerObserver { public: NetworkDropdownButton(bool browser_mode, gfx::NativeWindow parent_window); virtual ~NetworkDropdownButton(); @@ -27,8 +27,8 @@ class NetworkDropdownButton : public views::MenuButton, // AnimationDelegate implementation. virtual void AnimationProgressed(const Animation* animation); - // NetworkLibrary::Observer implementation. - virtual void NetworkChanged(NetworkLibrary* obj); + // NetworkLibrary::NetworkManagerObserver implementation. + virtual void OnNetworkManagerChanged(NetworkLibrary* obj); // Refreshes button state. Used when language has been changed. void Refresh(); @@ -37,8 +37,8 @@ class NetworkDropdownButton : public views::MenuButton, // NetworkMenu implementation: virtual bool IsBrowserMode() const { return browser_mode_; } virtual gfx::NativeWindow GetNativeWindow() const { return parent_window_; } - virtual void OpenButtonOptions() const {} - virtual bool ShouldOpenButtonOptions() const {return false; } + virtual void OpenButtonOptions() {} + virtual bool ShouldOpenButtonOptions() const { return false; } private: bool browser_mode_; diff --git a/chrome/browser/chromeos/status/network_menu.cc b/chrome/browser/chromeos/status/network_menu.cc index 4899bbd..fb64aa3 100644 --- a/chrome/browser/chromeos/status/network_menu.cc +++ b/chrome/browser/chromeos/status/network_menu.cc @@ -4,6 +4,8 @@ #include "chrome/browser/chromeos/status/network_menu.h" +#include <algorithm> + #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/command_line.h" @@ -43,7 +45,7 @@ namespace chromeos { // NetworkMenu // static -const int NetworkMenu::kNumWifiImages = 9; +const int NetworkMenu::kNumWifiImages = 4; // NOTE: Use an array rather than just calculating a resource number to avoid // creating implicit ordering dependencies on the resource values. @@ -53,11 +55,6 @@ const int NetworkMenu::kBarsImages[kNumWifiImages] = { IDR_STATUSBAR_NETWORK_BARS2, IDR_STATUSBAR_NETWORK_BARS3, IDR_STATUSBAR_NETWORK_BARS4, - IDR_STATUSBAR_NETWORK_BARS5, - IDR_STATUSBAR_NETWORK_BARS6, - IDR_STATUSBAR_NETWORK_BARS7, - IDR_STATUSBAR_NETWORK_BARS8, - IDR_STATUSBAR_NETWORK_BARS9, }; // static const int NetworkMenu::kBarsImagesBlack[kNumWifiImages] = { @@ -65,39 +62,26 @@ const int NetworkMenu::kBarsImagesBlack[kNumWifiImages] = { IDR_STATUSBAR_NETWORK_BARS2_BLACK, IDR_STATUSBAR_NETWORK_BARS3_BLACK, IDR_STATUSBAR_NETWORK_BARS4_BLACK, - IDR_STATUSBAR_NETWORK_BARS5_BLACK, - IDR_STATUSBAR_NETWORK_BARS6_BLACK, - IDR_STATUSBAR_NETWORK_BARS7_BLACK, - IDR_STATUSBAR_NETWORK_BARS8_BLACK, - IDR_STATUSBAR_NETWORK_BARS9_BLACK, }; // static const int NetworkMenu::kBarsImagesLowData[kNumWifiImages] = { - IDR_STATUSBAR_NETWORK_BARS1_LOWDATA, - IDR_STATUSBAR_NETWORK_BARS2_LOWDATA, - IDR_STATUSBAR_NETWORK_BARS3_LOWDATA, - IDR_STATUSBAR_NETWORK_BARS4_LOWDATA, - IDR_STATUSBAR_NETWORK_BARS5_LOWDATA, - IDR_STATUSBAR_NETWORK_BARS6_LOWDATA, - IDR_STATUSBAR_NETWORK_BARS7_LOWDATA, - IDR_STATUSBAR_NETWORK_BARS8_LOWDATA, - IDR_STATUSBAR_NETWORK_BARS9_LOWDATA, + IDR_STATUSBAR_NETWORK_BARS1_ORANGE, + IDR_STATUSBAR_NETWORK_BARS2_ORANGE, + IDR_STATUSBAR_NETWORK_BARS3_ORANGE, + IDR_STATUSBAR_NETWORK_BARS4_ORANGE, }; // static const int NetworkMenu::kBarsImagesVLowData[kNumWifiImages] = { - IDR_STATUSBAR_NETWORK_BARS1_VLOWDATA, - IDR_STATUSBAR_NETWORK_BARS2_VLOWDATA, - IDR_STATUSBAR_NETWORK_BARS3_VLOWDATA, - IDR_STATUSBAR_NETWORK_BARS4_VLOWDATA, - IDR_STATUSBAR_NETWORK_BARS5_VLOWDATA, - IDR_STATUSBAR_NETWORK_BARS6_VLOWDATA, - IDR_STATUSBAR_NETWORK_BARS7_VLOWDATA, - IDR_STATUSBAR_NETWORK_BARS8_VLOWDATA, - IDR_STATUSBAR_NETWORK_BARS9_VLOWDATA, + IDR_STATUSBAR_NETWORK_BARS1_RED, + IDR_STATUSBAR_NETWORK_BARS2_RED, + IDR_STATUSBAR_NETWORK_BARS3_RED, + IDR_STATUSBAR_NETWORK_BARS4_RED, }; NetworkMenu::NetworkMenu() : min_width_(-1) { + use_settings_ui_ = CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableTabbedOptions); network_menu_.reset(NetworkMenuUI::CreateMenu2(this)); } @@ -113,30 +97,29 @@ bool NetworkMenu::GetNetworkAt(int index, NetworkInfo* info) const { info->network_type = kNetworkTypeEthernet; if (cros->ethernet_connected()) { info->status = kNetworkStatusConnected; - info->ip_address = cros->ethernet_network().ip_address(); + info->ip_address = cros->ethernet_network() ? + cros->ethernet_network()->ip_address() : std::string(); } info->need_passphrase = false; info->remembered = true; } else if (flags & FLAG_WIFI) { - WifiNetwork wifi; - bool found = cros->FindWifiNetworkByPath( - menu_items_[index].wireless_path, &wifi); - if (found) { + WifiNetwork* wifi = cros->FindWifiNetworkByPath( + menu_items_[index].wireless_path); + if (wifi) { info->network_type = kNetworkTypeWifi; - if (wifi.service_path() == cros->wifi_network().service_path()) { + if (cros->wifi_network() && + wifi->service_path() == cros->wifi_network()->service_path()) { if (cros->wifi_connected()) { info->status = kNetworkStatusConnected; info->message = l10n_util::GetStringUTF8( IDS_STATUSBAR_NETWORK_DEVICE_CONNECTED); } else if (cros->wifi_connecting()) { info->status = kNetworkStatusConnecting; - // TODO(stevenjb): Eliminate status message, or localize properly. info->message = l10n_util::GetStringUTF8( - IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING) - + ": " + wifi.GetStateString(); - } else if (wifi.state() == STATE_FAILURE) { + IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING); + } else if (wifi->state() == STATE_FAILURE) { info->status = kNetworkStatusError; - info->message = wifi.GetErrorString(); + info->message = wifi->GetErrorString(); } else { info->status = kNetworkStatusDisconnected; info->message = l10n_util::GetStringUTF8( @@ -147,29 +130,31 @@ bool NetworkMenu::GetNetworkAt(int index, NetworkInfo* info) const { info->message = l10n_util::GetStringUTF8( IDS_STATUSBAR_NETWORK_DEVICE_DISCONNECTED); } - if (wifi.encrypted()) { - if (wifi.IsCertificateLoaded() || - wifi.encryption() == SECURITY_8021X) { + if (wifi->encrypted()) { + info->need_passphrase = true; + if (wifi->IsCertificateLoaded() || + wifi->encryption() == SECURITY_8021X) { + info->need_passphrase = false; + } + if (wifi->favorite()) { + info->passphrase = wifi->passphrase(); info->need_passphrase = false; - } else { - info->need_passphrase = true; } } else { info->need_passphrase = false; } - info->ip_address = wifi.ip_address(); - info->remembered = wifi.favorite(); + info->ip_address = wifi->ip_address(); + info->remembered = wifi->favorite(); } else { res = false; // Network not found, hide entry. } } else if (flags & FLAG_CELLULAR) { - CellularNetwork cellular; - bool found = cros->FindCellularNetworkByPath( - menu_items_[index].wireless_path, &cellular); - if (found) { + CellularNetwork* cellular = cros->FindCellularNetworkByPath( + menu_items_[index].wireless_path); + if (cellular) { info->network_type = kNetworkTypeCellular; - if (cellular.service_path() == - cros->cellular_network().service_path()) { + if (cros->cellular_network() && cellular->service_path() == + cros->cellular_network()->service_path()) { if (cros->cellular_connected()) { info->status = kNetworkStatusConnected; info->message = l10n_util::GetStringUTF8( @@ -179,10 +164,10 @@ bool NetworkMenu::GetNetworkAt(int index, NetworkInfo* info) const { info->status = kNetworkStatusConnecting; info->message = l10n_util::GetStringUTF8( IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING) - + ": " + cellular.GetStateString(); - } else if (cellular.state() == STATE_FAILURE) { + + ": " + cellular->GetStateString(); + } else if (cellular->state() == STATE_FAILURE) { info->status = kNetworkStatusError; - info->message = cellular.GetErrorString(); + info->message = cellular->GetErrorString(); } else { info->status = kNetworkStatusDisconnected; info->message = l10n_util::GetStringUTF8( @@ -193,7 +178,7 @@ bool NetworkMenu::GetNetworkAt(int index, NetworkInfo* info) const { info->message = l10n_util::GetStringUTF8( IDS_STATUSBAR_NETWORK_DEVICE_DISCONNECTED); } - info->ip_address = cellular.ip_address(); + info->ip_address = cellular->ip_address(); info->need_passphrase = false; info->remembered = true; } else { @@ -219,41 +204,88 @@ bool NetworkMenu::ConnectToNetworkAt(int index, int flags = menu_items_[index].flags; NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); if (flags & FLAG_WIFI) { - WifiNetwork wifi; - bool found = cros->FindWifiNetworkByPath( - menu_items_[index].wireless_path, &wifi); - if (found) { + WifiNetwork* wifi = cros->FindWifiNetworkByPath( + menu_items_[index].wireless_path); + if (wifi) { // Connect or reconnect. if (remember >= 0) - wifi.set_favorite(remember ? true : false); - if (wifi.encrypted()) { - if (wifi.IsCertificateLoaded()) { - cros->ConnectToWifiNetwork(wifi, std::string(), std::string(), - wifi.cert_path()); - } else if (wifi.encryption() == SECURITY_8021X) { - // Show the wifi settings/dialog to load/select a certificate. + wifi->set_favorite(remember ? true : false); + if (cros->wifi_network() && + wifi->service_path() == cros->wifi_network()->service_path()) { + // Show the config settings for the active network. + ShowWifi(wifi, false); + return true; + } + bool connected = false; + if (wifi->encrypted()) { + if (wifi->IsCertificateLoaded()) { + connected = cros->ConnectToWifiNetwork( + wifi, std::string(), std::string(), wifi->cert_path()); + } else if (wifi->encryption() == SECURITY_8021X) { + // Always show the wifi settings/dialog to load/select a certificate. ShowWifi(wifi, true); + return true; } else { - cros->ConnectToWifiNetwork(wifi, passphrase, std::string(), - std::string()); + if (MenuUI::IsEnabled() || !wifi->passphrase_required()) { + connected = cros->ConnectToWifiNetwork( + wifi, passphrase, std::string(), std::string()); + } } } else { - cros->ConnectToWifiNetwork(wifi, std::string(), std::string(), - std::string()); + connected = cros->ConnectToWifiNetwork( + wifi, std::string(), std::string(), std::string()); + } + if (!connected) { + if (!MenuUI::IsEnabled()) { + // Show the wifi dialog on a failed attempt for non DOM UI menus. + ShowWifi(wifi, true); + return true; + } else { + // If the connection attempt failed immediately (e.g. short password) + // keep the menu open so that a retry can be attempted. + return false; + } } + } else { + // If we are attempting to connect to a network that no longer exists, + // display a notification. + // TODO(stevenjb): Show notification. } } else if (flags & FLAG_CELLULAR) { - CellularNetwork cellular; - bool found = cros->FindCellularNetworkByPath( - menu_items_[index].wireless_path, &cellular); - if (found) { - // Connect or reconnect. - cros->ConnectToCellularNetwork(cellular); + CellularNetwork* cellular = cros->FindCellularNetworkByPath( + menu_items_[index].wireless_path); + if (cellular) { + if (cellular->activation_state() != ACTIVATION_STATE_ACTIVATED) { + ActivateCellular(cellular); + return true; + } else if (cros->cellular_network() && + (cellular->service_path() == + cros->cellular_network()->service_path())) { + // Show the config settings for the cellular network. + ShowCellular(cellular, false); + return true; + } else { + bool connected = cros->ConnectToCellularNetwork(cellular); + if (!connected) { + ShowCellular(cellular, true); + } + } + } else { + // If we are attempting to connect to a network that no longer exists, + // display a notification. + // TODO(stevenjb): Show notification. } } else if (flags & FLAG_OTHER_NETWORK) { - bool favorite = remember == 0 ? false : true; // default is true - cros->ConnectToWifiNetwork(ssid, passphrase, std::string(), std::string(), - favorite); + bool connected = false; + if (MenuUI::IsEnabled()) { + bool favorite = remember == 0 ? false : true; // default is true + connected = cros->ConnectToWifiNetwork( + passphrase.empty() ? SECURITY_NONE : SECURITY_UNKNOWN, + ssid, passphrase, std::string(), std::string(), favorite); + } + if (!connected) { + ShowOther(); + } } return true; } @@ -313,41 +345,16 @@ void NetworkMenu::ActivatedAt(int index) { cros->EnableCellularNetworkDevice(!cros->cellular_enabled()); } else if (flags & FLAG_TOGGLE_OFFLINE) { cros->EnableOfflineMode(!cros->offline_mode()); - } else if (flags & FLAG_OTHER_NETWORK) { - ShowOther(); } else if (flags & FLAG_ETHERNET) { if (cros->ethernet_connected()) { ShowEthernet(cros->ethernet_network()); } } else if (flags & FLAG_WIFI) { - WifiNetwork wifi; - bool wifi_exists = cros->FindWifiNetworkByPath( - menu_items_[index].wireless_path, &wifi); - if (!wifi_exists) { - // If we are attempting to connect to a network that no longer exists, - // display a notification. - // TODO(stevenjb): Show notification. - } else if (wifi.service_path() == cros->wifi_network().service_path()) { - // Show the config settings for the active network. - ShowWifi(wifi, false); - } else { - ConnectToNetworkAt(index, std::string(), std::string(), -1); - } + ConnectToNetworkAt(index, std::string(), std::string(), -1); + } else if (flags & FLAG_OTHER_NETWORK) { + ConnectToNetworkAt(index, std::string(), std::string(), -1); } else if (flags & FLAG_CELLULAR) { - CellularNetwork cellular; - bool cellular_exists = cros->FindCellularNetworkByPath( - menu_items_[index].wireless_path, &cellular); - if (!cellular_exists) { - // If we are attempting to connect to a network that no longer exists, - // display a notification. - // TODO(stevenjb): Show notification. - } else if (cellular.service_path() == - cros->cellular_network().service_path()) { - // Show the config settings for the cellular network. - ShowCellular(cellular, false); - } else { - ConnectToNetworkAt(index, std::string(), std::string(), -1); - } + ConnectToNetworkAt(index, std::string(), std::string(), -1); } } @@ -378,13 +385,14 @@ SkBitmap NetworkMenu::IconForNetworkStrength(int strength, bool black) { return *ResourceBundle::GetSharedInstance().GetBitmapNamed(images[index]); } -SkBitmap NetworkMenu::IconForNetworkStrength(CellularNetwork cellular) { +SkBitmap NetworkMenu::IconForNetworkStrength(const CellularNetwork* cellular) { + DCHECK(cellular); // Compose wifi icon by superimposing various icons. - int index = static_cast<int>(cellular.strength() / 100.0 * + int index = static_cast<int>(cellular->strength() / 100.0 * nextafter(static_cast<float>(kNumWifiImages), 0)); index = std::max(std::min(index, kNumWifiImages - 1), 0); const int* images = kBarsImages; - switch (cellular.data_left()) { + switch (cellular->data_left()) { case CellularNetwork::DATA_NONE: case CellularNetwork::DATA_VERY_LOW: images = kBarsImagesVLowData; @@ -400,19 +408,54 @@ SkBitmap NetworkMenu::IconForNetworkStrength(CellularNetwork cellular) { } // static +// TODO(ers) update for GSM when we have the necessary images +SkBitmap NetworkMenu::BadgeForNetworkTechnology( + const CellularNetwork* cellular) { + + int id = -1; + if (cellular->network_technology() == NETWORK_TECHNOLOGY_EVDO) { + switch (cellular->data_left()) { + case CellularNetwork::DATA_NONE: + case CellularNetwork::DATA_VERY_LOW: + id = IDR_STATUSBAR_NETWORK_3G_ERROR; + break; + case CellularNetwork::DATA_LOW: + id = IDR_STATUSBAR_NETWORK_3G_WARN; + break; + case CellularNetwork::DATA_NORMAL: + id = IDR_STATUSBAR_NETWORK_3G; + break; + } + } else if (cellular->network_technology() == NETWORK_TECHNOLOGY_1XRTT) { + switch (cellular->data_left()) { + case CellularNetwork::DATA_NONE: + case CellularNetwork::DATA_VERY_LOW: + id = IDR_STATUSBAR_NETWORK_1X_ERROR; + break; + case CellularNetwork::DATA_LOW: + id = IDR_STATUSBAR_NETWORK_1X_WARN; + break; + case CellularNetwork::DATA_NORMAL: + id = IDR_STATUSBAR_NETWORK_1X; + break; + } + } else { + id = -1; + } + if (id == -1) + return SkBitmap(); + else + return *ResourceBundle::GetSharedInstance().GetBitmapNamed(id); +} + +// static SkBitmap NetworkMenu::IconForDisplay(SkBitmap icon, SkBitmap badge) { - // Icons are 24x24. - static const int kIconWidth = 24; - static const int kIconHeight = 24; - // Draw the network icon 3 pixels down to center it. - static const int kIconX = 0; - static const int kIconY = 3; // Draw badge at (14,14). static const int kBadgeX = 14; static const int kBadgeY = 14; - gfx::CanvasSkia canvas(kIconWidth, kIconHeight, false); - canvas.DrawBitmapInt(icon, kIconX, kIconY); + gfx::CanvasSkia canvas(icon.width(), icon.height(), false); + canvas.DrawBitmapInt(icon, 0, 0); if (!badge.empty()) canvas.DrawBitmapInt(badge, kBadgeX, kBadgeY); return canvas.ExtractBitmap(); @@ -425,9 +468,10 @@ void NetworkMenu::RunMenu(views::View* source, const gfx::Point& pt) { refreshing_menu_ = true; NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); cros->RequestWifiScan(); - cros->UpdateSystemInfo(); - InitMenuItems(); - network_menu_->Rebuild(); + + // Menu contents are built in UpdateMenu, which is called + // when NetworkChagned is called. + // Restore menu width, if it was set up. // NOTE: width isn't checked for correctness here since all width-related // logic implemented inside |network_menu_|. @@ -446,75 +490,153 @@ void NetworkMenu::InitMenuItems() { NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + bool no_networks = true; + string16 label; + // Ethernet - bool ethernet_connected = cros->ethernet_connected(); - bool ethernet_connecting = cros->ethernet_connecting(); - string16 label = l10n_util::GetStringUTF16( - IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET); - SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK); - SkBitmap badge = ethernet_connecting || ethernet_connected ? - SkBitmap() : *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED); - int flag = FLAG_ETHERNET; - if (ethernet_connecting || ethernet_connected) - flag |= FLAG_ASSOCIATED; - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, - IconForDisplay(icon, badge), std::string(), flag)); - - // Wifi - const WifiNetworkVector& wifi_networks = cros->wifi_networks(); - const WifiNetwork& active_wifi = cros->wifi_network(); - // Wifi networks ssids. - for (size_t i = 0; i < wifi_networks.size(); ++i) { - label = ASCIIToUTF16(wifi_networks[i].name()); - SkBitmap icon = IconForNetworkStrength(wifi_networks[i].strength(), true); - SkBitmap badge = wifi_networks[i].encrypted() ? - *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE) : SkBitmap(); - flag = FLAG_WIFI; - if (wifi_networks[i].service_path() == active_wifi.service_path()) + bool ethernet_available = cros->ethernet_available(); + if (ethernet_available) { + no_networks = false; + bool ethernet_connected = cros->ethernet_connected(); + bool ethernet_connecting = cros->ethernet_connecting(); + + if (ethernet_connecting) { + label = l10n_util::GetStringFUTF16( + IDS_STATUSBAR_NETWORK_DEVICE_STATUS, + l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET), + l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING)); + } else { + label = l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET); + } + SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK); + SkBitmap badge = ethernet_connecting || ethernet_connected ? + SkBitmap() : *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED); + int flag = FLAG_ETHERNET; + if (ethernet_connecting || ethernet_connected) flag |= FLAG_ASSOCIATED; - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, - IconForDisplay(icon, badge), wifi_networks[i].service_path(), flag)); + menu_items_.push_back( + MenuItem(menus::MenuModel::TYPE_COMMAND, label, + IconForDisplay(icon, badge), std::string(), flag)); } - // Cellular - const CellularNetworkVector& cell_networks = cros->cellular_networks(); - const CellularNetwork& active_cellular = cros->cellular_network(); - // Cellular networks ssids. - for (size_t i = 0; i < cell_networks.size(); ++i) { - label = ASCIIToUTF16(cell_networks[i].name()); - SkBitmap icon = IconForNetworkStrength(cell_networks[i].strength(), true); - // TODO(chocobo): Check cellular network 3g/edge. - SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_3G); -// SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_EDGE); - flag = FLAG_CELLULAR; - if (cell_networks[i].service_path() == active_cellular.service_path()) - flag |= FLAG_ASSOCIATED; - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, - IconForDisplay(icon, badge), cell_networks[i].service_path(), flag)); + // Wifi Networks + bool wifi_available = cros->wifi_available(); + if (wifi_available) { + const WifiNetworkVector& wifi_networks = cros->wifi_networks(); + const WifiNetwork* active_wifi = cros->wifi_network(); + + if (wifi_networks.size() > 0) { + no_networks = false; + // Separator + menu_items_.push_back(MenuItem()); + } + // List Wifi networks. + for (size_t i = 0; i < wifi_networks.size(); ++i) { + if (wifi_networks[i]->connecting()) { + label = l10n_util::GetStringFUTF16( + IDS_STATUSBAR_NETWORK_DEVICE_STATUS, + ASCIIToUTF16(wifi_networks[i]->name()), + l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING)); + } else { + label = ASCIIToUTF16(wifi_networks[i]->name()); + } + SkBitmap icon = IconForNetworkStrength(wifi_networks[i]->strength(), + true); + SkBitmap badge = wifi_networks[i]->encrypted() ? + *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE) : SkBitmap(); + int flag = FLAG_WIFI; + if (active_wifi + && wifi_networks[i]->service_path() == active_wifi->service_path()) + flag |= FLAG_ASSOCIATED; + menu_items_.push_back( + MenuItem(menus::MenuModel::TYPE_COMMAND, label, + IconForDisplay(icon, badge), + wifi_networks[i]->service_path(), flag)); + } + } + + // Cellular Networks + bool cellular_available = cros->cellular_available(); + if (cellular_available) { + const CellularNetworkVector& cell_networks = cros->cellular_networks(); + const CellularNetwork* active_cellular = cros->cellular_network(); + + bool separator_added = false; + // List Cellular networks. + for (size_t i = 0; i < cell_networks.size(); ++i) { + chromeos::ActivationState activation_state = + cell_networks[i]->activation_state(); + if (activation_state == ACTIVATION_STATE_NOT_ACTIVATED) { + // If we are on the OOBE/login screen, do not show activating 3G option. + if (!IsBrowserMode()) + continue; + label = l10n_util::GetStringFUTF16( + IDS_STATUSBAR_NETWORK_DEVICE_ACTIVATE, + ASCIIToUTF16(cell_networks[i]->name())); + } else if (activation_state == ACTIVATION_STATE_PARTIALLY_ACTIVATED || + activation_state == ACTIVATION_STATE_ACTIVATING) { + label = l10n_util::GetStringFUTF16( + IDS_STATUSBAR_NETWORK_DEVICE_STATUS, + ASCIIToUTF16(cell_networks[i]->name()), + l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ACTIVATING)); + } else if (cell_networks[i]->connecting()) { + label = l10n_util::GetStringFUTF16( + IDS_STATUSBAR_NETWORK_DEVICE_STATUS, + ASCIIToUTF16(cell_networks[i]->name()), + l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING)); + } else { + label = ASCIIToUTF16(cell_networks[i]->name()); + } + + // First add a separator if necessary. + if (!separator_added) { + no_networks = false; + menu_items_.push_back(MenuItem()); + separator_added = true; + } + + SkBitmap icon = IconForNetworkStrength(cell_networks[i]->strength(), + true); + SkBitmap badge = BadgeForNetworkTechnology(cell_networks[i]); + int flag = FLAG_CELLULAR; + if (active_cellular && + cell_networks[i]->service_path() == + active_cellular->service_path() && + (cell_networks[i]->connecting() || cell_networks[i]->connected())) + flag |= FLAG_ASSOCIATED; + menu_items_.push_back( + MenuItem(menus::MenuModel::TYPE_COMMAND, label, + IconForDisplay(icon, badge), + cell_networks[i]->service_path(), flag)); + } } // No networks available message. - if (wifi_networks.empty() && cell_networks.empty()) { + if (no_networks) { label = l10n_util::GetStringFUTF16(IDS_STATUSBAR_NETWORK_MENU_ITEM_INDENT, l10n_util::GetStringUTF16(IDS_STATUSBAR_NO_NETWORKS_MESSAGE)); menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, SkBitmap(), std::string(), FLAG_DISABLED)); } - // Other networks - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, - l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_OTHER_NETWORKS), - IconForDisplay(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0), - SkBitmap()), - std::string(), FLAG_OTHER_NETWORK)); + // Add network. + if (wifi_available) { + // Separator + menu_items_.push_back(MenuItem()); - bool wifi_available = cros->wifi_available(); - bool cellular_available = cros->cellular_available(); + menu_items_.push_back(MenuItem( + menus::MenuModel::TYPE_COMMAND, + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_OTHER_NETWORKS), + IconForDisplay(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0_BLACK), + SkBitmap()), + std::string(), FLAG_OTHER_NETWORK)); + } + + // Enable / disable wireless. if (wifi_available || cellular_available) { - // Separator. + // Separator menu_items_.push_back(MenuItem()); - // Turn Wifi Off. (only if wifi available) if (wifi_available) { int id = cros->wifi_enabled() ? IDS_STATUSBAR_NETWORK_DEVICE_DISABLE : IDS_STATUSBAR_NETWORK_DEVICE_ENABLE; @@ -524,10 +646,9 @@ void NetworkMenu::InitMenuItems() { SkBitmap(), std::string(), FLAG_TOGGLE_WIFI)); } - // Turn Cellular Off. (only if cellular available) if (cellular_available) { int id = cros->cellular_enabled() ? IDS_STATUSBAR_NETWORK_DEVICE_DISABLE : - IDS_STATUSBAR_NETWORK_DEVICE_ENABLE; + IDS_STATUSBAR_NETWORK_DEVICE_ENABLE; label = l10n_util::GetStringFUTF16(id, l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_CELLULAR)); menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, @@ -535,40 +656,59 @@ void NetworkMenu::InitMenuItems() { } } - // TODO(chocobo): Uncomment once we figure out how to do offline mode. // Offline mode. -// menu_items_.push_back(MenuItem(cros->offline_mode() ? -// menus::MenuModel::TYPE_CHECK : menus::MenuModel::TYPE_COMMAND, -// l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_OFFLINE_MODE), -// SkBitmap(), std::string(), FLAG_TOGGLE_OFFLINE)); + // TODO(chocobo): Uncomment once we figure out how to do offline mode. + // menu_items_.push_back(MenuItem(cros->offline_mode() ? + // menus::MenuModel::TYPE_CHECK : menus::MenuModel::TYPE_COMMAND, + // l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_OFFLINE_MODE), + // SkBitmap(), std::string(), FLAG_TOGGLE_OFFLINE)); - if (cros->Connected() || ShouldOpenButtonOptions()) { - // Separator. + bool connected = cros->Connected(); // alwasy call for test expectations. + bool show_ip = !MenuUI::IsEnabled() && connected; + bool show_settings = ShouldOpenButtonOptions(); + + // Separator. + if (show_ip || show_settings) { menu_items_.push_back(MenuItem()); + } - // Network settings. - if (ShouldOpenButtonOptions()) { - label = - l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_OPEN_OPTIONS_DIALOG); - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, - SkBitmap(), std::string(), FLAG_OPTIONS)); + // IP Address + if (show_ip) { + menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, + ASCIIToUTF16(cros->IPAddress()), SkBitmap(), + std::string(), FLAG_DISABLED)); + } + + // Network settings. + if (show_settings) { + if (IsBrowserMode()) { + label = l10n_util::GetStringUTF16( + IDS_STATUSBAR_NETWORK_OPEN_OPTIONS_DIALOG); + } else { + label = l10n_util::GetStringUTF16( + IDS_STATUSBAR_NETWORK_OPEN_PROXY_SETTINGS_DIALOG); } + menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, + SkBitmap(), std::string(), FLAG_OPTIONS)); } } -void NetworkMenu::ShowTabbedNetworkSettings(const Network& network) const { +void NetworkMenu::ShowTabbedNetworkSettings(const Network* network) const { + DCHECK(network); Browser* browser = BrowserList::GetLastActive(); if (!browser) return; std::string page = StringPrintf("%s?servicePath=%s&networkType=%d", chrome::kInternetOptionsSubPage, - EscapeUrlEncodedData(network.service_path()).c_str(), - network.type()); + EscapeUrlEncodedData(network->service_path()).c_str(), + network->type()); browser->ShowOptionsTab(page); } -// TODO(stevenjb): deprecate this once we've committed to the embedded -// menu UI and fully deprecated NetworkConfigView. +// TODO(stevenjb): deprecate this once we've committed to tabbed settings +// and the embedded menu UI (and fully deprecated NetworkConfigView). +// Meanwhile, if MenuUI::IsEnabled() is true, always show the settings UI, +// otherwise show NetworkConfigView only to get passwords when not connected. void NetworkMenu::ShowNetworkConfigView(NetworkConfigView* view, bool focus_login) const { view->set_browser_mode(IsBrowserMode()); @@ -580,28 +720,40 @@ void NetworkMenu::ShowNetworkConfigView(NetworkConfigView* view, view->SetLoginTextfieldFocus(); } -void NetworkMenu::ShowWifi(const WifiNetwork& wifi, bool focus_login) const{ - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableTabbedOptions)) { +void NetworkMenu::ShowWifi(const WifiNetwork* wifi, bool focus_login) const { + DCHECK(wifi); + if (use_settings_ui_ && + (MenuUI::IsEnabled() || wifi->connected() || wifi->connecting())) { ShowTabbedNetworkSettings(wifi); } else { ShowNetworkConfigView(new NetworkConfigView(wifi, true), focus_login); } } -void NetworkMenu::ShowCellular(const CellularNetwork& cellular, +void NetworkMenu::ShowCellular(const CellularNetwork* cellular, bool focus_login) const { - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableTabbedOptions)) { + DCHECK(cellular); + if (use_settings_ui_ && + (MenuUI::IsEnabled() || cellular->connected() || + cellular->connecting())) { ShowTabbedNetworkSettings(cellular); } else { ShowNetworkConfigView(new NetworkConfigView(cellular), focus_login); } } -void NetworkMenu::ShowEthernet(const EthernetNetwork& ethernet) const { - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableTabbedOptions)) { +void NetworkMenu::ActivateCellular(const CellularNetwork* cellular) const { + DCHECK(cellular); + Browser* browser = BrowserList::GetLastActive(); + // TODO(stevenjb) : specify which service to activate. + browser->ShowSingletonTab(GURL(chrome::kChromeUIMobileSetupURL)); +} + +void NetworkMenu::ShowEthernet(const EthernetNetwork* ethernet) const { + DCHECK(ethernet); + if (use_settings_ui_ && + (MenuUI::IsEnabled() || ethernet->connected() || + ethernet->connecting())) { ShowTabbedNetworkSettings(ethernet); } else { ShowNetworkConfigView(new NetworkConfigView(ethernet), false); @@ -609,8 +761,7 @@ void NetworkMenu::ShowEthernet(const EthernetNetwork& ethernet) const { } void NetworkMenu::ShowOther() const { - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableTabbedOptions)) { + if (use_settings_ui_ && MenuUI::IsEnabled()) { Browser* browser = BrowserList::GetLastActive(); if (browser) { std::string page = StringPrintf("%s?networkType=%d", diff --git a/chrome/browser/chromeos/status/network_menu.h b/chrome/browser/chromeos/status/network_menu.h index 3c8d322..28f4d68 100644 --- a/chrome/browser/chromeos/status/network_menu.h +++ b/chrome/browser/chromeos/status/network_menu.h @@ -61,6 +61,8 @@ class NetworkMenu : public views::ViewMenuDelegate, std::string message; // IP address (if network is active, empty otherwise) std::string ip_address; + // Remembered passphrase. + std::string passphrase; // true if the network requires a passphrase. bool need_passphrase; // true if the network is currently remembered. @@ -113,11 +115,12 @@ class NetworkMenu : public views::ViewMenuDelegate, // |black| is used to specify whether to return a black icon for display // on a light background or a white icon for display on a dark background. static SkBitmap IconForNetworkStrength(int strength, bool black); - // Returns the Icon for a network strength for CellularNetwork |cellular|. // This returns different colored bars depending on cellular data left. - static SkBitmap IconForNetworkStrength(CellularNetwork cellular); - + static SkBitmap IconForNetworkStrength(const CellularNetwork* cellular); + // Returns the Badge for a given network technology. + // This returns different colored symbols depending on cellular data left. + static SkBitmap BadgeForNetworkTechnology(const CellularNetwork* cellular); // This method will convert the |icon| bitmap to the correct size for display. // If the |badge| icon is not empty, it will draw that on top of the icon. static SkBitmap IconForDisplay(SkBitmap icon, SkBitmap badge); @@ -125,11 +128,11 @@ class NetworkMenu : public views::ViewMenuDelegate, protected: virtual bool IsBrowserMode() const = 0; virtual gfx::NativeWindow GetNativeWindow() const = 0; - virtual void OpenButtonOptions() const = 0; + virtual void OpenButtonOptions() = 0; virtual bool ShouldOpenButtonOptions() const = 0; // Notify subclasses that connection to |network| was initiated. - virtual void OnConnectNetwork(const Network& network, + virtual void OnConnectNetwork(const Network* network, SkBitmap selected_icon_) {} // Update the menu (e.g. when the network list or status has changed). void UpdateMenu(); @@ -176,16 +179,17 @@ class NetworkMenu : public views::ViewMenuDelegate, void InitMenuItems(); // Shows network details in DOM UI options window. - void ShowTabbedNetworkSettings(const Network& network) const; + void ShowTabbedNetworkSettings(const Network* network) const; // Show a NetworkConfigView modal dialog instance. // TODO(stevenjb): deprecate this once all of the UI is embedded in the menu. void ShowNetworkConfigView(NetworkConfigView* view, bool focus_login) const; // Wrappers for the ShowNetworkConfigView / ShowTabbedNetworkSettings. - void ShowWifi(const WifiNetwork& wifi, bool focus_login) const; - void ShowCellular(const CellularNetwork& cellular, bool focus_login) const; - void ShowEthernet(const EthernetNetwork& ethernet) const; + void ShowWifi(const WifiNetwork* wifi, bool focus_login) const; + void ShowCellular(const CellularNetwork* cellular, bool focus_login) const; + void ActivateCellular(const CellularNetwork* cellular) const; + void ShowEthernet(const EthernetNetwork* ethernet) const; void ShowOther() const; // Set to true if we are currently refreshing the menu. @@ -209,6 +213,9 @@ class NetworkMenu : public views::ViewMenuDelegate, // Holds minimum width or -1 if it wasn't set up. int min_width_; + // If true, call into the settings UI for network configuration dialogs. + bool use_settings_ui_; + DISALLOW_COPY_AND_ASSIGN(NetworkMenu); }; diff --git a/chrome/browser/chromeos/status/network_menu_button.cc b/chrome/browser/chromeos/status/network_menu_button.cc index 3a37df7..6d2486f 100644 --- a/chrome/browser/chromeos/status/network_menu_button.cc +++ b/chrome/browser/chromeos/status/network_menu_button.cc @@ -15,6 +15,7 @@ #include "chrome/browser/chromeos/options/network_config_view.h" #include "chrome/browser/chromeos/status/status_area_host.h" #include "gfx/canvas_skia.h" +#include "gfx/skbitmap_operations.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "views/window/window.h" @@ -33,13 +34,17 @@ NetworkMenuButton::NetworkMenuButton(StatusAreaHost* host) host_(host), ALLOW_THIS_IN_INITIALIZER_LIST(animation_connecting_(this)) { animation_connecting_.SetThrobDuration(kThrobDuration); - animation_connecting_.SetTweenType(Tween::LINEAR); - NetworkChanged(CrosLibrary::Get()->GetNetworkLibrary()); - CrosLibrary::Get()->GetNetworkLibrary()->AddObserver(this); + animation_connecting_.SetTweenType(Tween::EASE_IN_OUT); + OnNetworkManagerChanged(CrosLibrary::Get()->GetNetworkLibrary()); + CrosLibrary::Get()->GetNetworkLibrary()->AddNetworkManagerObserver(this); + CrosLibrary::Get()->GetNetworkLibrary()->AddCellularDataPlanObserver(this); } NetworkMenuButton::~NetworkMenuButton() { - CrosLibrary::Get()->GetNetworkLibrary()->RemoveObserver(this); + NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary(); + netlib->RemoveNetworkManagerObserver(this); + netlib->RemoveObserverForAllNetworks(this); + netlib->RemoveCellularDataPlanObserver(this); } //////////////////////////////////////////////////////////////////////////////// @@ -47,14 +52,16 @@ NetworkMenuButton::~NetworkMenuButton() { void NetworkMenuButton::AnimationProgressed(const Animation* animation) { if (animation == &animation_connecting_) { - // Figure out which image to draw. We want a value between 0-100. - // 0 reperesents no signal and 100 represents full signal strength. - int value = static_cast<int>(animation_connecting_.GetCurrentValue()*100.0); - if (value < 0) - value = 0; - else if (value > 100) - value = 100; - SetIcon(IconForNetworkStrength(value, false)); + // Draw animation of bars icon fading in and out. + // We are fading between 0 bars and a third of the opacity of 4 bars. + // Use the current value of the animation to calculate the alpha value + // of how transparent the icon is. + SetIcon(SkBitmapOperations::CreateBlendedBitmap( + *ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_STATUSBAR_NETWORK_BARS0), + *ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_STATUSBAR_NETWORK_BARS4), + animation_connecting_.GetCurrentValue() / 3)); SchedulePaint(); } else { MenuButton::AnimationProgressed(animation); @@ -64,100 +71,62 @@ void NetworkMenuButton::AnimationProgressed(const Animation* animation) { //////////////////////////////////////////////////////////////////////////////// // NetworkMenuButton, StatusAreaButton implementation: -void NetworkMenuButton::DrawPressed(gfx::Canvas* canvas) { - // If ethernet connected and not current connecting, then show ethernet - // pressed icon. Otherwise, show the bars pressed icon. - if (CrosLibrary::Get()->GetNetworkLibrary()->ethernet_connected() && - !animation_connecting_.is_animating()) - canvas->DrawBitmapInt(IconForDisplay( - *ResourceBundle::GetSharedInstance(). - GetBitmapNamed(IDR_STATUSBAR_NETWORK_WIRED_PRESSED), SkBitmap()), - 0, 0); - else - canvas->DrawBitmapInt(IconForDisplay( - *ResourceBundle::GetSharedInstance(). - GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS_PRESSED), SkBitmap()), - 0, 0); -} - void NetworkMenuButton::DrawIcon(gfx::Canvas* canvas) { - canvas->DrawBitmapInt(IconForDisplay(icon(), badge()), 0, 0); + canvas->DrawBitmapInt(IconForDisplay(icon(), badge()), + horizontal_padding(), 0); } //////////////////////////////////////////////////////////////////////////////// -// NetworkMenuButton, NetworkLibrary::Observer implementation: +// NetworkMenuButton, NetworkLibrary::NetworkManagerObserver implementation: -void NetworkMenuButton::NetworkChanged(NetworkLibrary* cros) { +void NetworkMenuButton::OnNetworkManagerChanged(NetworkLibrary* cros) { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); if (CrosLibrary::Get()->EnsureLoaded()) { + // Add an observer for the active network, if any + const Network* network = cros->active_network(); + if (active_network_.empty() || network == NULL || + active_network_ != network->service_path()) { + if (!active_network_.empty()) { + cros->RemoveNetworkObserver(active_network_, this); + } + if (network != NULL) { + cros->AddNetworkObserver(network->service_path(), this); + } + } + if (network) + active_network_ = network->service_path(); + else + active_network_ = ""; + if (cros->wifi_connecting() || cros->cellular_connecting()) { // Start the connecting animation if not running. if (!animation_connecting_.is_animating()) { animation_connecting_.Reset(); animation_connecting_.StartThrobbing(std::numeric_limits<int>::max()); - SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS1)); + SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0)); } std::string network_name = cros->wifi_connecting() ? - cros->wifi_network().name() : cros->cellular_network().name(); + cros->wifi_network()->name() : cros->cellular_network()->name(); + bool configuring = cros->wifi_connecting() ? + cros->wifi_network()->configuring() : + cros->cellular_network()->configuring(); SetTooltipText( - l10n_util::GetStringF(IDS_STATUSBAR_NETWORK_CONNECTING_TOOLTIP, - UTF8ToWide(network_name))); + l10n_util::GetStringF(configuring ? + IDS_STATUSBAR_NETWORK_CONFIGURING_TOOLTIP : + IDS_STATUSBAR_NETWORK_CONNECTING_TOOLTIP, + UTF8ToWide(network_name))); } else { // Stop connecting animation since we are not connecting. animation_connecting_.Stop(); - - // Always show the higher priority connection first. Ethernet then wifi. - if (cros->ethernet_connected()) { - SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_WIRED)); - SetTooltipText( - l10n_util::GetStringF( - IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP, - l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET))); - } else if (cros->wifi_connected()) { - SetIcon(IconForNetworkStrength( - cros->wifi_network().strength(), false)); - SetTooltipText(l10n_util::GetStringF( - IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP, - UTF8ToWide(cros->wifi_network().name()))); - } else if (cros->cellular_connected()) { - const CellularNetwork& cellular = cros->cellular_network(); - if (cellular.data_left() == CellularNetwork::DATA_NONE) { - // If no data, then we show 0 bars. - SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0)); - } else { - SetIcon(IconForNetworkStrength(cellular)); - } - SetTooltipText(l10n_util::GetStringF( - IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP, - UTF8ToWide(cellular.name()))); - } else { + if (!cros->Connected()) { SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0)); SetTooltipText(l10n_util::GetString( IDS_STATUSBAR_NETWORK_NO_NETWORK_TOOLTIP)); + } else { + SetNetworkIcon(network); } } - - if (!cros->Connected() && !cros->Connecting()) { - SetBadge(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED)); - } else if (!cros->ethernet_connected() && !cros->wifi_connected() && - (cros->cellular_connecting() || cros->cellular_connected())) { - int id = IDR_STATUSBAR_NETWORK_3G; - switch (cros->cellular_network().data_left()) { - case CellularNetwork::DATA_NONE: - case CellularNetwork::DATA_VERY_LOW: - id = IDR_STATUSBAR_NETWORK_3G_VLOWDATA; - break; - case CellularNetwork::DATA_LOW: - id = IDR_STATUSBAR_NETWORK_3G_LOWDATA; - break; - case CellularNetwork::DATA_NORMAL: - id = IDR_STATUSBAR_NETWORK_3G; - break; - } - SetBadge(*rb.GetBitmapNamed(id)); - } else { - SetBadge(SkBitmap()); - } + SetNetworkBadge(cros, network); } else { SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0)); SetBadge(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_WARNING)); @@ -169,13 +138,29 @@ void NetworkMenuButton::NetworkChanged(NetworkLibrary* cros) { UpdateMenu(); } -void NetworkMenuButton::CellularDataPlanChanged(NetworkLibrary* cros) { - // Call NetworkChanged which will update the icon. - NetworkChanged(cros); +//////////////////////////////////////////////////////////////////////////////// +// NetworkMenuButton, NetworkLibrary::NetworkObserver implementation: +void NetworkMenuButton::OnNetworkChanged(NetworkLibrary* cros, + const Network* network) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + if (CrosLibrary::Get()->EnsureLoaded()) { + // Always show the active network connection, if any. + SetNetworkIcon(network); + SetNetworkBadge(cros, network); + } else { + SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0)); + SetBadge(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_WARNING)); + SetTooltipText(l10n_util::GetString( + IDS_STATUSBAR_NETWORK_NO_NETWORK_TOOLTIP)); + } + + SchedulePaint(); + UpdateMenu(); } -void NetworkMenuButton::SetBadge(const SkBitmap& badge) { - badge_ = badge; +void NetworkMenuButton::OnCellularDataPlanChanged(NetworkLibrary* cros) { + // Call OnNetworkManagerChanged which will update the icon. + OnNetworkManagerChanged(cros); } //////////////////////////////////////////////////////////////////////////////// @@ -189,7 +174,7 @@ gfx::NativeWindow NetworkMenuButton::GetNativeWindow() const { return host_->GetNativeWindow(); } -void NetworkMenuButton::OpenButtonOptions() const { +void NetworkMenuButton::OpenButtonOptions() { host_->OpenButtonOptions(this); } @@ -197,4 +182,54 @@ bool NetworkMenuButton::ShouldOpenButtonOptions() const { return host_->ShouldOpenButtonOptions(this); } +//////////////////////////////////////////////////////////////////////////////// +// NetworkMenuButton, private methods + +void NetworkMenuButton::SetNetworkIcon(const Network* network) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + if (network && network->is_active()) { + if (network->type() == TYPE_ETHERNET) { + SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_WIRED)); + SetTooltipText( + l10n_util::GetStringF( + IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP, + l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET))); + } else if (network->type() == TYPE_WIFI) { + const WifiNetwork* wifi = static_cast<const WifiNetwork*>(network); + SetIcon(IconForNetworkStrength(wifi->strength(), false)); + SetTooltipText(l10n_util::GetStringF( + IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP, + UTF8ToWide(wifi->name()))); + } else if (network->type() == TYPE_CELLULAR) { + const CellularNetwork* cellular = + static_cast<const CellularNetwork*>(network); + if (cellular->data_left() == CellularNetwork::DATA_NONE) { + // If no data, then we show 0 bars. + SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0)); + } else { + SetIcon(IconForNetworkStrength(cellular)); + } + SetTooltipText(l10n_util::GetStringF( + IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP, + UTF8ToWide(cellular->name()))); + } + } +} + +void NetworkMenuButton::SetNetworkBadge(NetworkLibrary* cros, + const Network* network) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + // Figure out whether or not to show a badge. + if (network && network->type() == TYPE_CELLULAR && + (network->is_active() || network->connecting())) { + const CellularNetwork* cellular + = static_cast<const CellularNetwork*>(network); + SetBadge(BadgeForNetworkTechnology(cellular)); + } else if (!cros->Connected() && !cros->Connecting()) { + SetBadge(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED)); + } else { + SetBadge(SkBitmap()); + } +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/status/network_menu_button.h b/chrome/browser/chromeos/status/network_menu_button.h index 79c5cdb..208efe2 100644 --- a/chrome/browser/chromeos/status/network_menu_button.h +++ b/chrome/browser/chromeos/status/network_menu_button.h @@ -6,6 +6,8 @@ #define CHROME_BROWSER_CHROMEOS_STATUS_NETWORK_MENU_BUTTON_H_ #pragma once +#include <string> + #include "app/throb_animation.h" #include "base/timer.h" #include "chrome/browser/chromeos/cros/network_library.h" @@ -46,7 +48,9 @@ class StatusAreaHost; // The label will be BOLD if the network is currently connected. class NetworkMenuButton : public StatusAreaButton, public NetworkMenu, - public NetworkLibrary::Observer { + public NetworkLibrary::NetworkManagerObserver, + public NetworkLibrary::NetworkObserver, + public NetworkLibrary::CellularDataPlanObserver { public: explicit NetworkMenuButton(StatusAreaHost* host); virtual ~NetworkMenuButton(); @@ -54,26 +58,31 @@ class NetworkMenuButton : public StatusAreaButton, // AnimationDelegate implementation. virtual void AnimationProgressed(const Animation* animation); - // NetworkLibrary::Observer implementation. - virtual void NetworkChanged(NetworkLibrary* obj); - virtual void CellularDataPlanChanged(NetworkLibrary* obj); + // NetworkLibrary::NetworkManagerObserver implementation. + virtual void OnNetworkManagerChanged(NetworkLibrary* cros); + // NetworkLibrary::NetworkObserver implementation. + virtual void OnNetworkChanged(NetworkLibrary* cros, const Network* network); + // NetworkLibrary::CellularDataPlanObserver implementation. + virtual void OnCellularDataPlanChanged(NetworkLibrary* cros); // Sets the badge icon. - void SetBadge(const SkBitmap& badge); + void SetBadge(const SkBitmap& badge) { badge_ = badge; } SkBitmap badge() const { return badge_; } protected: // StatusAreaButton implementation. - virtual void DrawPressed(gfx::Canvas* canvas); virtual void DrawIcon(gfx::Canvas* canvas); // NetworkMenu implementation: virtual bool IsBrowserMode() const; virtual gfx::NativeWindow GetNativeWindow() const; - virtual void OpenButtonOptions() const; + virtual void OpenButtonOptions(); virtual bool ShouldOpenButtonOptions() const; private: + void SetNetworkIcon(const Network* network); + void SetNetworkBadge(NetworkLibrary* cros, const Network* network); + // The status area host, StatusAreaHost* host_; @@ -86,6 +95,10 @@ class NetworkMenuButton : public StatusAreaButton, // The duration of the icon throbbing in milliseconds. static const int kThrobDuration; + // If any network is currently active, this is the service path of the one + // whose status is displayed in the network menu button. + std::string active_network_; + DISALLOW_COPY_AND_ASSIGN(NetworkMenuButton); }; diff --git a/chrome/browser/chromeos/status/power_menu_button.cc b/chrome/browser/chromeos/status/power_menu_button.cc index de942cb..e6adcdc 100644 --- a/chrome/browser/chromeos/status/power_menu_button.cc +++ b/chrome/browser/chromeos/status/power_menu_button.cc @@ -20,7 +20,7 @@ namespace chromeos { // PowerMenuButton // static -const int PowerMenuButton::kNumPowerImages = 12; +const int PowerMenuButton::kNumPowerImages = 16; PowerMenuButton::PowerMenuButton() : StatusAreaButton(this), @@ -110,21 +110,6 @@ void PowerMenuButton::PowerChanged(PowerLibrary* obj) { //////////////////////////////////////////////////////////////////////////////// // PowerMenuButton, StatusAreaButton implementation: -void PowerMenuButton::DrawPressed(gfx::Canvas* canvas) { - DrawPowerIcon(canvas, *ResourceBundle::GetSharedInstance(). - GetBitmapNamed(IDR_STATUSBAR_BATTERY_PRESSED)); -} - -void PowerMenuButton::DrawIcon(gfx::Canvas* canvas) { - DrawPowerIcon(canvas, icon()); -} - -void PowerMenuButton::DrawPowerIcon(gfx::Canvas* canvas, SkBitmap icon) { - // Draw the battery icon 5 pixels down to center it. - static const int kIconVerticalPadding = 5; - canvas->DrawBitmapInt(icon, 0, kIconVerticalPadding); -} - void PowerMenuButton::UpdateIconAndLabelInfo() { PowerLibrary* cros = CrosLibrary::Get()->GetPowerLibrary(); if (!cros) @@ -168,6 +153,10 @@ void PowerMenuButton::UpdateIconAndLabelInfo() { IDR_STATUSBAR_BATTERY_CHARGING_10, IDR_STATUSBAR_BATTERY_CHARGING_11, IDR_STATUSBAR_BATTERY_CHARGING_12, + IDR_STATUSBAR_BATTERY_CHARGING_13, + IDR_STATUSBAR_BATTERY_CHARGING_14, + IDR_STATUSBAR_BATTERY_CHARGING_15, + IDR_STATUSBAR_BATTERY_CHARGING_16, }; static const int kDischargingImages[kNumPowerImages] = { IDR_STATUSBAR_BATTERY_DISCHARGING_1, @@ -182,6 +171,10 @@ void PowerMenuButton::UpdateIconAndLabelInfo() { IDR_STATUSBAR_BATTERY_DISCHARGING_10, IDR_STATUSBAR_BATTERY_DISCHARGING_11, IDR_STATUSBAR_BATTERY_DISCHARGING_12, + IDR_STATUSBAR_BATTERY_DISCHARGING_13, + IDR_STATUSBAR_BATTERY_DISCHARGING_14, + IDR_STATUSBAR_BATTERY_DISCHARGING_15, + IDR_STATUSBAR_BATTERY_DISCHARGING_16, }; int index = static_cast<int>(battery_percentage_ / 100.0 * diff --git a/chrome/browser/chromeos/status/power_menu_button.h b/chrome/browser/chromeos/status/power_menu_button.h index 7f27333..b3503e8 100644 --- a/chrome/browser/chromeos/status/power_menu_button.h +++ b/chrome/browser/chromeos/status/power_menu_button.h @@ -57,17 +57,12 @@ class PowerMenuButton : public StatusAreaButton, int icon_id() const { return icon_id_; } protected: - // StatusAreaButton implementation. - virtual void DrawPressed(gfx::Canvas* canvas); - virtual void DrawIcon(gfx::Canvas* canvas); + virtual int icon_width() { return 26; } private: // views::ViewMenuDelegate implementation. virtual void RunMenu(views::View* source, const gfx::Point& pt); - // This method will draw the |icon| in the appropriate place on the |canvas|. - void DrawPowerIcon(gfx::Canvas* canvas, SkBitmap icon); - // Update the power icon and menu label info depending on the power status. void UpdateIconAndLabelInfo(); diff --git a/chrome/browser/chromeos/status/power_menu_button_browsertest.cc b/chrome/browser/chromeos/status/power_menu_button_browsertest.cc index 66042fd..9e383bc 100644 --- a/chrome/browser/chromeos/status/power_menu_button_browsertest.cc +++ b/chrome/browser/chromeos/status/power_menu_button_browsertest.cc @@ -93,7 +93,7 @@ IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryChargedTest) { } IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryChargingTest) { - const int NUM_TIMES = 12; // 6 + 8*12 = 102 + const int NUM_TIMES = 16; EXPECT_CALL(*mock_power_library_, battery_is_present()) .Times(NUM_TIMES) .WillRepeatedly((Return(true))) @@ -115,7 +115,7 @@ IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryChargingTest) { .WillRepeatedly((Return(base::TimeDelta::FromMinutes(24)))) .RetiresOnSaturation(); - // Test the 12 battery charging states. + // Test the 16 battery charging states. // NOTE: Use an array rather than just calculating a resource number to avoid // creating implicit ordering dependencies on the resource values. static const int kChargingImages[] = { @@ -131,9 +131,13 @@ IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryChargingTest) { IDR_STATUSBAR_BATTERY_CHARGING_10, IDR_STATUSBAR_BATTERY_CHARGING_11, IDR_STATUSBAR_BATTERY_CHARGING_12, + IDR_STATUSBAR_BATTERY_CHARGING_13, + IDR_STATUSBAR_BATTERY_CHARGING_14, + IDR_STATUSBAR_BATTERY_CHARGING_15, + IDR_STATUSBAR_BATTERY_CHARGING_16, }; size_t id = 0; - for (float percent = 6.0; percent < 100.0; percent += 8.0) { + for (float percent = 6.0; percent < 100.0; percent += 6.0) { EXPECT_CALL(*mock_power_library_, battery_percentage()) .WillOnce((Return(percent))) .RetiresOnSaturation(); @@ -144,7 +148,7 @@ IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryChargingTest) { } IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryDischargingTest) { - const int NUM_TIMES = 12; // 6 + 8*12 = 102 + const int NUM_TIMES = 16; EXPECT_CALL(*mock_power_library_, battery_is_present()) .Times(NUM_TIMES) .WillRepeatedly((Return(true))) @@ -166,7 +170,7 @@ IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryDischargingTest) { .WillRepeatedly((Return(base::TimeDelta::FromMinutes(24)))) .RetiresOnSaturation(); - // Test the 12 battery discharing states. + // Test the 16 battery discharing states. // NOTE: Use an array rather than just calculating a resource number to avoid // creating implicit ordering dependencies on the resource values. static const int kDischargingImages[] = { @@ -182,9 +186,13 @@ IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryDischargingTest) { IDR_STATUSBAR_BATTERY_DISCHARGING_10, IDR_STATUSBAR_BATTERY_DISCHARGING_11, IDR_STATUSBAR_BATTERY_DISCHARGING_12, + IDR_STATUSBAR_BATTERY_DISCHARGING_13, + IDR_STATUSBAR_BATTERY_DISCHARGING_14, + IDR_STATUSBAR_BATTERY_DISCHARGING_15, + IDR_STATUSBAR_BATTERY_DISCHARGING_16, }; size_t id = 0; - for (float percent = 6.0; percent < 100.0; percent += 8.0) { + for (float percent = 6.0; percent < 100.0; percent += 6.0) { EXPECT_CALL(*mock_power_library_, battery_percentage()) .WillOnce((Return(percent))) .RetiresOnSaturation(); diff --git a/chrome/browser/chromeos/status/status_area_button.cc b/chrome/browser/chromeos/status/status_area_button.cc index d2c34e6..eb6c2d5 100644 --- a/chrome/browser/chromeos/status/status_area_button.cc +++ b/chrome/browser/chromeos/status/status_area_button.cc @@ -34,21 +34,15 @@ void StatusAreaButton::Paint(gfx::Canvas* canvas, bool for_drag) { if (use_menu_button_paint_) { views::MenuButton::Paint(canvas, for_drag); } else { - if (state() == BS_PUSHED) - DrawPressed(canvas); - DrawIcon(canvas); PaintFocusBorder(canvas); } } gfx::Size StatusAreaButton::GetPreferredSize() { - // icons are 24x24 - static const int kIconWidth = 24; - static const int kIconHeight = 24; gfx::Insets insets = views::MenuButton::GetInsets(); - gfx::Size prefsize(kIconWidth + insets.width(), - kIconHeight + insets.height()); + gfx::Size prefsize(icon_width() + insets.width(), + icon_height() + insets.height()); // Adjusts size when use menu button paint. if (use_menu_button_paint_) { @@ -65,6 +59,9 @@ gfx::Size StatusAreaButton::GetPreferredSize() { } } + // Add padding. + prefsize.Enlarge(2 * horizontal_padding(), 0); + return prefsize; } @@ -81,7 +78,7 @@ void StatusAreaButton::SetText(const std::wstring& text) { } void StatusAreaButton::DrawIcon(gfx::Canvas* canvas) { - canvas->DrawBitmapInt(icon(), 0, 0); + canvas->DrawBitmapInt(icon(), horizontal_padding(), 0); } } // namespace chromeos diff --git a/chrome/browser/chromeos/status/status_area_button.h b/chrome/browser/chromeos/status/status_area_button.h index 6d57178..cefc474 100644 --- a/chrome/browser/chromeos/status/status_area_button.h +++ b/chrome/browser/chromeos/status/status_area_button.h @@ -30,16 +30,19 @@ class StatusAreaButton : public views::MenuButton { } protected: - // Draws the pressed icon. This is called before DrawIcon if the state is - // pressed. Subclasses should override this method if they need to draw a - // pressed icon. - virtual void DrawPressed(gfx::Canvas* canvas) {} - // Draws the icon for this status area button on the canvas. // Subclasses should override this method if they need to draw their own icon. // Otherwise, just call SetIcon() and the it will be handled for you. virtual void DrawIcon(gfx::Canvas* canvas); + // Subclasses should override these methods to return the correct dimensions. + virtual int icon_height() { return 24; } + virtual int icon_width() { return 23; } + + // Subclasses can override this method to return more or less padding. + // The padding is added to both the left and right side. + virtual int horizontal_padding() { return 1; } + // True if the button wants to use views::MenuButton drawings. bool use_menu_button_paint_; diff --git a/chrome/browser/chromeos/status/status_area_host.h b/chrome/browser/chromeos/status/status_area_host.h index 7e86e67..7b7691e 100644 --- a/chrome/browser/chromeos/status/status_area_host.h +++ b/chrome/browser/chromeos/status/status_area_host.h @@ -33,7 +33,7 @@ class StatusAreaHost { const views::View* button_view) const = 0; // Opens options dialog related to the button specified. - virtual void OpenButtonOptions(const views::View* button_view) const = 0; + virtual void OpenButtonOptions(const views::View* button_view) = 0; // Executes browser command. virtual void ExecuteBrowserCommand(int id) const = 0; diff --git a/chrome/browser/chromeos/status/status_area_view.cc b/chrome/browser/chromeos/status/status_area_view.cc index bd34685..6a89d42 100644 --- a/chrome/browser/chromeos/status/status_area_view.cc +++ b/chrome/browser/chromeos/status/status_area_view.cc @@ -17,7 +17,7 @@ namespace chromeos { // Number of pixels to separate each icon. -const int kSeparation = 6; +const int kSeparation = 1; StatusAreaView::StatusAreaView(StatusAreaHost* host) : host_(host), @@ -29,6 +29,10 @@ StatusAreaView::StatusAreaView(StatusAreaHost* host) } void StatusAreaView::Init() { + // Clock. + clock_view_ = new ClockMenuButton(host_); + AddChildView(clock_view_); + // InputMethod. input_method_view_ = new InputMethodMenuButton(host_); AddChildView(input_method_view_); @@ -44,10 +48,6 @@ void StatusAreaView::Init() { // Power. power_view_ = new PowerMenuButton(); AddChildView(power_view_); - - // Clock. - clock_view_ = new ClockMenuButton(host_); - AddChildView(clock_view_); } gfx::Size StatusAreaView::GetPreferredSize() { @@ -80,7 +80,8 @@ void StatusAreaView::Layout() { // Put next in row horizontally, and center vertically. cur->SetBounds(cur_x, cur_y, cur_size.width(), cur_size.height()); - cur_x += cur_size.width() + kSeparation; + if (cur_size.width() > 0) + cur_x += cur_size.width() + kSeparation; } } } diff --git a/chrome/browser/chromeos/system_key_event_listener.cc b/chrome/browser/chromeos/system_key_event_listener.cc index 21f28f2..999f5bb 100644 --- a/chrome/browser/chromeos/system_key_event_listener.cc +++ b/chrome/browser/chromeos/system_key_event_listener.cc @@ -4,6 +4,9 @@ #include "chrome/browser/chromeos/system_key_event_listener.h" +#include <gdk/gdkx.h> +#include <X11/XF86keysym.h> + #include "chrome/browser/chromeos/audio_handler.h" #include "chrome/browser/chromeos/volume_bubble.h" #include "cros/chromeos_wm_ipc_enums.h" @@ -24,20 +27,31 @@ SystemKeyEventListener* SystemKeyEventListener::instance() { SystemKeyEventListener::SystemKeyEventListener() : audio_handler_(AudioHandler::instance()) { WmMessageListener::instance()->AddObserver(this); + + key_volume_mute_ = XKeysymToKeycode(GDK_DISPLAY(), XF86XK_AudioMute); + key_volume_down_ = XKeysymToKeycode(GDK_DISPLAY(), XF86XK_AudioLowerVolume); + key_volume_up_ = XKeysymToKeycode(GDK_DISPLAY(), XF86XK_AudioRaiseVolume); + key_f8_ = XKeysymToKeycode(GDK_DISPLAY(), XK_F8); + key_f9_ = XKeysymToKeycode(GDK_DISPLAY(), XK_F9); + key_f10_ = XKeysymToKeycode(GDK_DISPLAY(), XK_F10); + + if (key_volume_mute_) + GrabKey(key_volume_mute_, 0); + if (key_volume_down_) + GrabKey(key_volume_down_, 0); + if (key_volume_up_) + GrabKey(key_volume_up_, 0); + GrabKey(key_f8_, 0); + GrabKey(key_f9_, 0); + GrabKey(key_f10_, 0); + gdk_window_add_filter(NULL, GdkEventFilter, this); } SystemKeyEventListener::~SystemKeyEventListener() { WmMessageListener::instance()->RemoveObserver(this); + gdk_window_remove_filter(NULL, GdkEventFilter, this); } -// TODO(davej): Move the ShowVolumeBubble() calls in to AudioHandler so that -// this function returns faster without blocking on GetVolumePercent(), and -// still guarantees that the volume displayed will be that after the adjustment. - -// TODO(davej): The IsMute() check can also be made non-blocking by changing -// to an AdjustVolumeByPercentOrUnmute() function which can do the steps off -// of this thread when ShowVolumeBubble() is moved in to AudioHandler. - void SystemKeyEventListener::ProcessWmMessage(const WmIpc::Message& message, GdkWindow* window) { if (message.type() != WM_IPC_MESSAGE_CHROME_NOTIFY_SYSKEY_PRESSED) @@ -45,27 +59,13 @@ void SystemKeyEventListener::ProcessWmMessage(const WmIpc::Message& message, switch (message.param(0)) { case WM_IPC_SYSTEM_KEY_VOLUME_MUTE: - // Always muting (and not toggling) as per final decision on - // http://crosbug.com/3751 - audio_handler_->SetMute(true); - VolumeBubble::instance()->ShowVolumeBubble(0); + OnVolumeMute(); break; case WM_IPC_SYSTEM_KEY_VOLUME_DOWN: - if (audio_handler_->IsMute()) { - VolumeBubble::instance()->ShowVolumeBubble(0); - } else { - audio_handler_->AdjustVolumeByPercent(-kStepPercentage); - VolumeBubble::instance()->ShowVolumeBubble( - audio_handler_->GetVolumePercent()); - } + OnVolumeDown(); break; case WM_IPC_SYSTEM_KEY_VOLUME_UP: - if (audio_handler_->IsMute()) - audio_handler_->SetMute(false); - else - audio_handler_->AdjustVolumeByPercent(kStepPercentage); - VolumeBubble::instance()->ShowVolumeBubble( - audio_handler_->GetVolumePercent()); + OnVolumeUp(); break; default: DLOG(ERROR) << "SystemKeyEventListener: Unexpected message " @@ -74,4 +74,79 @@ void SystemKeyEventListener::ProcessWmMessage(const WmIpc::Message& message, } } +// static +GdkFilterReturn SystemKeyEventListener::GdkEventFilter(GdkXEvent* gxevent, + GdkEvent* gevent, + gpointer data) { + SystemKeyEventListener* listener = static_cast<SystemKeyEventListener*>(data); + XEvent* xevent = static_cast<XEvent*>(gxevent); + + if (xevent->type == KeyPress) { + int32 keycode = xevent->xkey.keycode; + if (keycode) { + if ((keycode == listener->key_f8_) || + (keycode == listener->key_volume_mute_)) { + listener->OnVolumeMute(); + return GDK_FILTER_REMOVE; + } else if ((keycode == listener->key_f9_) || + keycode == listener->key_volume_down_) { + listener->OnVolumeDown(); + return GDK_FILTER_REMOVE; + } else if ((keycode == listener->key_f10_) || + (keycode == listener->key_volume_up_)) { + listener->OnVolumeUp(); + return GDK_FILTER_REMOVE; + } + } + } + return GDK_FILTER_CONTINUE; +} + +void SystemKeyEventListener::GrabKey(int32 key, uint32 mask) { + uint32 num_lock_mask = Mod2Mask; + uint32 caps_lock_mask = LockMask; + Window root = DefaultRootWindow(GDK_DISPLAY()); + XGrabKey(GDK_DISPLAY(), key, mask, root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(GDK_DISPLAY(), key, mask | caps_lock_mask, root, True, + GrabModeAsync, GrabModeAsync); + XGrabKey(GDK_DISPLAY(), key, mask | num_lock_mask, root, True, + GrabModeAsync, GrabModeAsync); + XGrabKey(GDK_DISPLAY(), key, mask | caps_lock_mask | num_lock_mask, root, + True, GrabModeAsync, GrabModeAsync); +} + +// TODO(davej): Move the ShowVolumeBubble() calls in to AudioHandler so that +// this function returns faster without blocking on GetVolumePercent(), and +// still guarantees that the volume displayed will be that after the adjustment. + +// TODO(davej): The IsMute() check can also be made non-blocking by changing +// to an AdjustVolumeByPercentOrUnmute() function which can do the steps off +// of this thread when ShowVolumeBubble() is moved in to AudioHandler. + +void SystemKeyEventListener::OnVolumeMute() { + // Always muting (and not toggling) as per final decision on + // http://crosbug.com/3751 + audio_handler_->SetMute(true); + VolumeBubble::instance()->ShowVolumeBubble(0); +} + +void SystemKeyEventListener::OnVolumeDown() { + if (audio_handler_->IsMute()) { + VolumeBubble::instance()->ShowVolumeBubble(0); + } else { + audio_handler_->AdjustVolumeByPercent(-kStepPercentage); + VolumeBubble::instance()->ShowVolumeBubble( + audio_handler_->GetVolumePercent()); + } +} + +void SystemKeyEventListener::OnVolumeUp() { + if (audio_handler_->IsMute()) + audio_handler_->SetMute(false); + else + audio_handler_->AdjustVolumeByPercent(kStepPercentage); + VolumeBubble::instance()->ShowVolumeBubble( + audio_handler_->GetVolumePercent()); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/system_key_event_listener.h b/chrome/browser/chromeos/system_key_event_listener.h index b731b94..61a20ca 100644 --- a/chrome/browser/chromeos/system_key_event_listener.h +++ b/chrome/browser/chromeos/system_key_event_listener.h @@ -6,6 +6,8 @@ #define CHROME_BROWSER_CHROMEOS_SYSTEM_KEY_EVENT_LISTENER_H_ #pragma once +#include <gdk/gdk.h> + #include "base/singleton.h" #include "chrome/browser/chromeos/wm_message_listener.h" @@ -13,9 +15,12 @@ namespace chromeos { class AudioHandler; -// SystemKeyEventListener listens for volume related key presses from the -// window manager, then tells the AudioHandler to adjust volume accordingly. -// Start by just calling instance() to get it going. +// SystemKeyEventListener listens for volume related key presses from GDK, then +// tells the AudioHandler to adjust volume accordingly. Start by just calling +// instance() to get it going. + +// TODO(davej): Remove WmMessageListener::Observer once volume key handling has +// been removed from the window manager since those keys take precedence. class SystemKeyEventListener : public WmMessageListener::Observer { public: @@ -33,6 +38,28 @@ class SystemKeyEventListener : public WmMessageListener::Observer { SystemKeyEventListener(); virtual ~SystemKeyEventListener(); + // This event filter intercepts events before they reach GDK, allowing us to + // check for system level keyboard events regardless of which window has + // focus. + static GdkFilterReturn GdkEventFilter(GdkXEvent* gxevent, + GdkEvent* gevent, + gpointer data); + + // Tell X we are interested in the specified key/mask combination. + // Capslock and Numlock are always ignored. + void GrabKey(int32 key, uint32 mask); + + void OnVolumeMute(); + void OnVolumeDown(); + void OnVolumeUp(); + + int32 key_volume_mute_; + int32 key_volume_down_; + int32 key_volume_up_; + int32 key_f8_; + int32 key_f9_; + int32 key_f10_; + // AudioHandler is a Singleton class we are just caching a pointer to here, // and we do not own the pointer. AudioHandler* const audio_handler_; diff --git a/chrome/browser/chromeos/tab_closeable_state_watcher_browsertest.cc b/chrome/browser/chromeos/tab_closeable_state_watcher_browsertest.cc index b9399de..97362b7 100644 --- a/chrome/browser/chromeos/tab_closeable_state_watcher_browsertest.cc +++ b/chrome/browser/chromeos/tab_closeable_state_watcher_browsertest.cc @@ -37,9 +37,7 @@ class TabCloseableStateWatcherTest : public InProcessBrowserTest { protected: // Wrapper for Browser::AddTabWithURL void AddTabWithURL(Browser* browser, const GURL& url) { - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - params.index = 0; - browser->AddTabWithURL(¶ms); + AddTabAtIndexToBrowser(browser, 0, url, PageTransition::TYPED); // Wait for page to finish loading. ui_test_utils::WaitForNavigation( &browser->GetSelectedTabContents()->controller()); diff --git a/chrome/browser/chromeos/usb_mount_observer.cc b/chrome/browser/chromeos/usb_mount_observer.cc index fbfb501..3d0695f 100644 --- a/chrome/browser/chromeos/usb_mount_observer.cc +++ b/chrome/browser/chromeos/usb_mount_observer.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -105,13 +105,13 @@ void USBMountObserver::MountChanged(chromeos::MountLibrary* obj, } } } - LOG(INFO) << "Got added mount:" << path; + VLOG(1) << "Got added mount: " << path; } else if (evt == chromeos::DISK_REMOVED || evt == chromeos::DEVICE_REMOVED) { RemoveBrowserFromVector(path); } else if (evt == chromeos::DISK_CHANGED) { BrowserIterator iter = FindBrowserForPath(path); - LOG(INFO) << "Got changed mount:" << path; + VLOG(1) << "Got changed mount: " << path; if (iter == browsers_.end()) { // We don't currently have this one, so it must have been // mounted @@ -142,10 +142,10 @@ void USBMountObserver::MountChanged(chromeos::MountLibrary* obj, } } } else if (evt == chromeos::DEVICE_ADDED) { - LOG(INFO) << "Got device added" << path; + VLOG(1) << "Got device added: " << path; OpenFileBrowse(kFilebrowseScanning, path, true); } else if (evt == chromeos::DEVICE_SCANNED) { - LOG(INFO) << "Got device scanned:" << path; + VLOG(1) << "Got device scanned: " << path; } } diff --git a/chrome/browser/chromeos/views/domui_menu_widget.cc b/chrome/browser/chromeos/views/domui_menu_widget.cc index 1657ddc..6231d6a 100644 --- a/chrome/browser/chromeos/views/domui_menu_widget.cc +++ b/chrome/browser/chromeos/views/domui_menu_widget.cc @@ -103,137 +103,6 @@ class InsetsLayout : public views::LayoutManager { DISALLOW_COPY_AND_ASSIGN(InsetsLayout); }; -const int kDOMViewWarmUpDelayMs = 1000 * 5; - -// A delayed task to initialize a cache. This is -// create when a profile is switched. -// (incognito, oobe/login has different profile). -class WarmUpTask : public Task { - public: - WarmUpTask() {} - virtual ~WarmUpTask() {} - - virtual void Run(); - - private: - DISALLOW_COPY_AND_ASSIGN(WarmUpTask); -}; - -// DOMViewCache holds single cache instance of DOMView. -class DOMViewCache : NotificationObserver { - public: - DOMViewCache() - : current_profile_(NULL), - cache_(NULL), - warmup_enabled_(true) { - registrar_.Add(this, NotificationType::APP_TERMINATING, - NotificationService::AllSources()); - } - virtual ~DOMViewCache() {} - - // Returns a DOMView for given profile. If there is - // matching cache, - DOMView* Get(Profile* profile) { - if (cache_ && - cache_->tab_contents()->profile() == profile) { - DOMView* c = cache_; - cache_ = NULL; - CheckClassInvariant(); - return c; - } - DOMView* dom_view = new DOMView(); - dom_view->Init(profile, NULL); - CheckClassInvariant(); - return dom_view; - } - - // Release a dom_view. A dom view is reused if its profile matches - // the current profile, or gets deleted otherwise. - void Release(DOMView* dom_view) { - if (cache_ == NULL && - current_profile_ == dom_view->tab_contents()->profile()) { - cache_ = dom_view; - } else { - delete dom_view; - } - CheckClassInvariant(); - } - - // (Re)Initiailzes the cache with profile. - // If the current profile does not match the new profile, - // it deletes the existing cache (if any) and creates new one. - void Init(Profile* profile) { - if (current_profile_ != profile) { - delete cache_; - cache_ = NULL; - current_profile_ = profile; - BrowserThread::PostDelayedTask(BrowserThread::UI, - FROM_HERE, - new WarmUpTask(), - kDOMViewWarmUpDelayMs); - } - CheckClassInvariant(); - } - - // Create a cache if one does not exist yet. - void WarmUp() { - // skip if domui is created in delay, or - // chromeos is shutting down. - if (cache_ || !current_profile_ || !warmup_enabled_) { - CheckClassInvariant(); - return; - } - cache_ = new DOMView(); - cache_->Init(current_profile_, NULL); - /** - * Tentative workaround for the failing tests that expects - * page loads. - cache_->LoadURL( - GURL(StringPrintf("chrome://%s", chrome::kChromeUIMenu))); - */ - CheckClassInvariant(); - } - - // Deletes cached DOMView instance if any. - void Shutdown() { - delete cache_; - cache_ = NULL; - // Reset current_profile_ as well so that a domview that - // is currently in use will be deleted in Release as well. - current_profile_ = NULL; - } - - // Enable/disable warmup. This has to be called - // before WarmUp method is invoked. - void set_warmup_enabled(bool enabled) { - warmup_enabled_ = enabled; - } - - private: - // NotificationObserver impelmentation: - void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - DCHECK_EQ(NotificationType::APP_TERMINATING, type.value); - Shutdown(); - } - - // Tests the class invariant condition. - void CheckClassInvariant() { - DCHECK(!cache_ || - cache_->tab_contents()->profile() == current_profile_); - } - - Profile* current_profile_; - DOMView* cache_; - NotificationRegistrar registrar_; - bool warmup_enabled_; -}; - -void WarmUpTask::Run() { - Singleton<DOMViewCache>::get()->WarmUp(); -} - // A gtk widget key used to test if a given WidgetGtk instance is // DOMUIMenuWidgetKey. const char* kDOMUIMenuWidgetKey = "__DOMUI_MENU_WIDGET__"; @@ -260,14 +129,12 @@ DOMUIMenuWidget::DOMUIMenuWidget(chromeos::NativeMenuDOMUI* domui_menu, : views::WidgetGtk(views::WidgetGtk::TYPE_POPUP), domui_menu_(domui_menu), dom_view_(NULL), - did_pointer_grab_(false), + did_input_grab_(false), is_root_(root) { DCHECK(domui_menu_); // TODO(oshima): Disabling transparent until we migrate bookmark // menus to DOMUI. See crosbug.com/7718. // MakeTransparent(); - - Singleton<DOMViewCache>::get()->Init(domui_menu->GetProfile()); } DOMUIMenuWidget::~DOMUIMenuWidget() { @@ -291,7 +158,7 @@ void DOMUIMenuWidget::Hide() { void DOMUIMenuWidget::Close() { if (dom_view_ != NULL) { dom_view_->GetParent()->RemoveChildView(dom_view_); - Singleton<DOMViewCache>::get()->Release(dom_view_); + delete dom_view_; dom_view_ = NULL; } @@ -302,9 +169,10 @@ void DOMUIMenuWidget::Close() { void DOMUIMenuWidget::ReleaseGrab() { WidgetGtk::ReleaseGrab(); - if (did_pointer_grab_) { - did_pointer_grab_ = false; + if (did_input_grab_) { + did_input_grab_ = false; gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_keyboard_ungrab(GDK_CURRENT_TIME); ClearGrabWidget(); } @@ -312,7 +180,7 @@ void DOMUIMenuWidget::ReleaseGrab() { gboolean DOMUIMenuWidget::OnGrabBrokeEvent(GtkWidget* widget, GdkEvent* event) { - did_pointer_grab_ = false; + did_input_grab_ = false; Hide(); return WidgetGtk::OnGrabBrokeEvent(widget, event); } @@ -392,7 +260,10 @@ void DOMUIMenuWidget::ShowAt(chromeos::MenuLocator* locator) { DCHECK(domui_menu_); menu_locator_.reset(locator); if (!dom_view_) { - dom_view_ = Singleton<DOMViewCache>::get()->Get(domui_menu_->GetProfile()); + // TODO(oshima): Replace DOMView with direct use of RVH for beta. + // DOMView should be refactored to use RVH directly, but + // it'll require a lot of change and will take time. + dom_view_ = new DOMView(); dom_view_->Init(domui_menu_->GetProfile(), NULL); // TODO(oshima): remove extra view to draw rounded corner. views::View* container = new views::View(); @@ -440,16 +311,22 @@ void DOMUIMenuWidget::CaptureGrab() { // Release the current grab. ClearGrabWidget(); - // NOTE: we do this to ensure we get mouse events from other apps, a grab - // done with gtk_grab_add doesn't get events from other apps. - GdkGrabStatus grab_status = + // NOTE: we do this to ensure we get mouse/keyboard events from + // other apps, a grab done with gtk_grab_add doesn't get events from + // other apps. + GdkGrabStatus pointer_grab_status = gdk_pointer_grab(window_contents()->window, FALSE, static_cast<GdkEventMask>( GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK), NULL, NULL, GDK_CURRENT_TIME); - did_pointer_grab_ = (grab_status == GDK_GRAB_SUCCESS); - DCHECK(did_pointer_grab_); + GdkGrabStatus keyboard_grab_status = + gdk_keyboard_grab(window_contents()->window, FALSE, + GDK_CURRENT_TIME); + + did_input_grab_ = pointer_grab_status == GDK_GRAB_SUCCESS && + keyboard_grab_status == GDK_GRAB_SUCCESS; + DCHECK(did_input_grab_); EnableInput(false /* no selection */); } @@ -460,8 +337,4 @@ void DOMUIMenuWidget::ClearGrabWidget() { gtk_grab_remove(grab_widget); } -void DOMUIMenuWidget::DisableWarmUp() { - Singleton<DOMViewCache>::get()->set_warmup_enabled(false); -} - } // namespace chromeos diff --git a/chrome/browser/chromeos/views/domui_menu_widget.h b/chrome/browser/chromeos/views/domui_menu_widget.h index 13fa579..095d4c4 100644 --- a/chrome/browser/chromeos/views/domui_menu_widget.h +++ b/chrome/browser/chromeos/views/domui_menu_widget.h @@ -45,8 +45,8 @@ class DOMUIMenuWidget : public views::WidgetGtk { } // Returns true if the menu widget has input grab. - bool did_pointer_grab() const { - return did_pointer_grab_; + bool did_input_grab() const { + return did_input_grab_; } // Enables/Disables menu scroll. @@ -77,11 +77,6 @@ class DOMUIMenuWidget : public views::WidgetGtk { static DOMUIMenuWidget* FindDOMUIMenuWidget(gfx::NativeView native); private: - friend class ::ExtensionApiTest; - // Disable warming up domview. This is to avoid confusing Extension - // API tests which listens to load notification with AllSources(). - static void DisableWarmUp(); - // Capture the X pointer grab. This also enables input on the widget by // calling EnableInput(false). void CaptureGrab(); @@ -98,8 +93,8 @@ class DOMUIMenuWidget : public views::WidgetGtk { // MenuLocator that controls the position of this menu widget. scoped_ptr<chromeos::MenuLocator> menu_locator_; - // True if the widget has pointer grab. - bool did_pointer_grab_; + // True if the widget has input grab. + bool did_input_grab_; // True if the widget is for root menu (very first menu in // submenu chain). diff --git a/chrome/browser/chromeos/views/native_menu_domui.cc b/chrome/browser/chromeos/views/native_menu_domui.cc index d421f5f..5ce238c 100644 --- a/chrome/browser/chromeos/views/native_menu_domui.cc +++ b/chrome/browser/chromeos/views/native_menu_domui.cc @@ -20,9 +20,17 @@ #include "gfx/rect.h" #include "views/controls/menu/menu_2.h" #include "views/controls/menu/native_menu_gtk.h" +#include "views/controls/menu/nested_dispatcher_gtk.h" + +#if defined(TOUCH_UI) +#include "views/focus/accelerator_handler.h" +#endif namespace { +using chromeos::NativeMenuDOMUI; +using chromeos::DOMUIMenuWidget; + // Returns true if the menu item type specified can be executed as a command. bool MenuTypeCanExecute(menus::MenuModel::ItemType type) { return type == menus::MenuModel::TYPE_COMMAND || @@ -31,9 +39,11 @@ bool MenuTypeCanExecute(menus::MenuModel::ItemType type) { } gboolean Destroy(GtkWidget* widget, gpointer data) { - chromeos::NativeMenuDOMUI* domui_menu = - static_cast<chromeos::NativeMenuDOMUI*>(data); - domui_menu->Hide(); + DOMUIMenuWidget* menu_widget = static_cast<DOMUIMenuWidget*>(data); + NativeMenuDOMUI* domui_menu = menu_widget->domui_menu(); + // domui_menu can be NULL if widget is destroyed by signal. + if (domui_menu) + domui_menu->Hide(); return true; } @@ -51,7 +61,7 @@ gfx::NativeWindow FindActiveToplevelWindow() { } // Currently opened menu. See RunMenuAt for reason why we need this. -chromeos::NativeMenuDOMUI* current_ = NULL; +NativeMenuDOMUI* current_ = NULL; } // namespace @@ -83,7 +93,8 @@ NativeMenuDOMUI::NativeMenuDOMUI(menus::MenuModel* menu_model, bool root) activated_index_(-1), menu_action_(MENU_ACTION_NONE), menu_url_(StringPrintf("chrome://%s", chrome::kChromeUIMenu)), - on_menu_opened_called_(false) { + on_menu_opened_called_(false), + nested_dispatcher_(NULL) { menu_widget_ = new DOMUIMenuWidget(this, root); // Set the initial location off the screen not to show small // window with dropshadow. @@ -91,7 +102,12 @@ NativeMenuDOMUI::NativeMenuDOMUI(menus::MenuModel* menu_model, bool root) } NativeMenuDOMUI::~NativeMenuDOMUI() { - DCHECK(!menu_shown_) << "Deleting while the menu is shown"; + if (nested_dispatcher_) { + // Menu is destroyed while its in message loop. + // Let nested dispatcher know the creator is deleted. + nested_dispatcher_->CreatorDestroyed(); + Hide(); + } if (menu_widget_) { menu_widget_->Close(); menu_widget_ = NULL; @@ -141,15 +157,20 @@ void NativeMenuDOMUI::RunMenuAt(const gfx::Point& point, int alignment) { if (parent) { handle = g_signal_connect(G_OBJECT(parent), "destroy", G_CALLBACK(&Destroy), - this); + menu_widget_); } - // We need to turn on nestable tasks as a renderer uses tasks internally. // Without this, renderer cannnot finish loading page. - bool nestable = MessageLoopForUI::current()->NestableTasksAllowed(); - MessageLoopForUI::current()->SetNestableTasksAllowed(true); - MessageLoopForUI::current()->Run(this); - MessageLoopForUI::current()->SetNestableTasksAllowed(nestable); + nested_dispatcher_ = + new views::NestedDispatcherGtk(this, true /* allow nested */); + bool deleted = nested_dispatcher_->RunAndSelfDestruct(); + current_ = NULL; // this is static and safe to access. + if (deleted) { + // The menu was destryed while menu is shown, so return immediately. + // Don't touch the instance which is already deleted. + return; + } + nested_dispatcher_ = NULL; if (menu_shown_) { // If this happens it means we haven't yet gotten the hide signal and // someone else quit the message loop on us. @@ -162,7 +183,6 @@ void NativeMenuDOMUI::RunMenuAt(const gfx::Point& point, int alignment) { menu_widget_->Hide(); // Close All submenus. submenu_.reset(); - current_ = NULL; ProcessActivate(); } @@ -228,6 +248,12 @@ bool NativeMenuDOMUI::Dispatch(GdkEvent* event) { return true; } +#if defined(TOUCH_UI) +bool NativeMenuDOMUI::Dispatch(XEvent* xevent) { + return views::DispatchXEvent(xevent); +} +#endif + //////////////////////////////////////////////////////////////////////////////// // NativeMenuDOMUI, MenuControl implementation: @@ -359,7 +385,8 @@ void NativeMenuDOMUI::ShowAt(MenuLocator* locator) { NativeMenuDOMUI* NativeMenuDOMUI::FindMenuAt(const gfx::Point& point) { if (submenu_.get()) { NativeMenuDOMUI* found = submenu_->FindMenuAt(point); - if (found) return found; + if (found) + return found; } gfx::Rect bounds; menu_widget_->GetBounds(&bounds, false); diff --git a/chrome/browser/chromeos/views/native_menu_domui.h b/chrome/browser/chromeos/views/native_menu_domui.h index 2c5e216..03a6b92 100644 --- a/chrome/browser/chromeos/views/native_menu_domui.h +++ b/chrome/browser/chromeos/views/native_menu_domui.h @@ -10,6 +10,7 @@ #include "base/message_loop.h" #include "base/observer_list.h" +#include "base/scoped_ptr.h" #include "chrome/browser/chromeos/dom_ui/domui_menu_control.h" #include "googleurl/src/gurl.h" #include "views/controls/menu/menu_wrapper.h" @@ -22,6 +23,14 @@ namespace menus { class MenuModel; } // namespace menus +namespace views { +class NestedDispatcherGtk; +} // namespace views; + +#if defined(TOUCH_UI) +typedef union _XEvent XEvent; +#endif + namespace chromeos { class MenuLocator; @@ -30,7 +39,7 @@ class DOMUIMenuWidget; // A DOMUI implementation of MenuWrapper. class NativeMenuDOMUI : public views::MenuWrapper, public DOMUIMenuControl, - public MessageLoopForUI::Dispatcher { + public MessageLoop::Dispatcher { public: NativeMenuDOMUI(menus::MenuModel* menu_model, bool root); virtual ~NativeMenuDOMUI(); @@ -54,6 +63,9 @@ class NativeMenuDOMUI : public views::MenuWrapper, // Overriden from MessageLoopForUI::Dispatcher: virtual bool Dispatch(GdkEvent* event); +#if defined(TOUCH_UI) + virtual bool Dispatch(XEvent* xevent); +#endif // Overriden from DOMUIMenuControl; virtual menus::MenuModel* GetMenuModel() { return model_; } @@ -140,6 +152,11 @@ class NativeMenuDOMUI : public views::MenuWrapper, // A guard flag to avoid calling MenuListener::OnMenuOpened twice. bool on_menu_opened_called_; + // Nested dispatcher object that can outlive this object. + // This is to deal with the menu being deleted while the nested + // message loop is handled. see http://crosbug.com/7929 . + views::NestedDispatcherGtk* nested_dispatcher_; + DISALLOW_COPY_AND_ASSIGN(NativeMenuDOMUI); }; diff --git a/chrome/browser/chromeos/wm_overview_controller.cc b/chrome/browser/chromeos/wm_overview_controller.cc index f4bf91d..785651a 100644 --- a/chrome/browser/chromeos/wm_overview_controller.cc +++ b/chrome/browser/chromeos/wm_overview_controller.cc @@ -317,6 +317,12 @@ void BrowserListener::ShowSnapshots() { } void BrowserListener::SelectTab(int index, uint32 timestamp) { + // Ignore requests to switch to non-existent tabs (the window manager gets + // notified asynchronously about the number of tabs in each window, so there's + // no guarantee that the messages that it sends us will make sense). + if (index < 0 || index >= browser_->tab_count()) + return; + uint32 old_value = select_tab_timestamp_; select_tab_timestamp_ = timestamp; browser_->SelectTabContentsAt(index, true); diff --git a/chrome/browser/cocoa/accelerators_cocoa.h b/chrome/browser/cocoa/accelerators_cocoa.h index b8eaac8..e43c550 100644 --- a/chrome/browser/cocoa/accelerators_cocoa.h +++ b/chrome/browser/cocoa/accelerators_cocoa.h @@ -11,7 +11,7 @@ #include "app/menus/accelerator_cocoa.h" // This class maintains a map of command_ids to AcceleratorCocoa objects (see -// chrome/app/chrome_dll_resource.h). Currently, this only lists the commands +// chrome/app/chrome_command_ids.h). Currently, this only lists the commands // that are used in the Wrench menu. // // It is recommended that this class be used as a singleton so that the key map diff --git a/chrome/browser/cocoa/accelerators_cocoa.mm b/chrome/browser/cocoa/accelerators_cocoa.mm index 86b4bd5..4b1eb5d 100644 --- a/chrome/browser/cocoa/accelerators_cocoa.mm +++ b/chrome/browser/cocoa/accelerators_cocoa.mm @@ -6,7 +6,7 @@ #import <Cocoa/Cocoa.h> -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" namespace { diff --git a/chrome/browser/cocoa/accelerators_cocoa_unittest.mm b/chrome/browser/cocoa/accelerators_cocoa_unittest.mm index b829351..3f0e307 100644 --- a/chrome/browser/cocoa/accelerators_cocoa_unittest.mm +++ b/chrome/browser/cocoa/accelerators_cocoa_unittest.mm @@ -6,7 +6,7 @@ #include "app/menus/accelerator_cocoa.h" #include "base/singleton.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #import "chrome/browser/cocoa/accelerators_cocoa.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest_mac.h" diff --git a/chrome/browser/cocoa/applescript/window_applescript.mm b/chrome/browser/cocoa/applescript/window_applescript.mm index c51e9d8..07401d3 100644 --- a/chrome/browser/cocoa/applescript/window_applescript.mm +++ b/chrome/browser/cocoa/applescript/window_applescript.mm @@ -11,6 +11,7 @@ #import "chrome/browser/app_controller_mac.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/browser_window.h" #import "chrome/browser/chrome_browser_application_mac.h" #include "chrome/browser/cocoa/applescript/constants_applescript.h" @@ -185,13 +186,15 @@ // Set how long it takes a tab to be created. base::TimeTicks newTabStartTime = base::TimeTicks::Now(); - Browser::AddTabWithURLParams params(GURL(chrome::kChromeUINewTabURL), - PageTransition::TYPED); - params.index = index; - TabContents* contents = browser_->AddTabWithURL(¶ms); - contents->set_new_tab_start_time(newTabStartTime); - - [aTab setTabContent:contents]; + browser::NavigateParams params(browser_, + GURL(chrome::kChromeUINewTabURL), + PageTransition::TYPED); + params.disposition = NEW_FOREGROUND_TAB; + params.tabstrip_index = index; + browser::Navigate(¶ms); + params.target_contents->set_new_tab_start_time(newTabStartTime); + + [aTab setTabContent:params.target_contents]; } - (void)removeFromTabsAtIndex:(int)index { diff --git a/chrome/browser/cocoa/base_bubble_controller.h b/chrome/browser/cocoa/base_bubble_controller.h index fc63f0f..7cc2c5b 100644 --- a/chrome/browser/cocoa/base_bubble_controller.h +++ b/chrome/browser/cocoa/base_bubble_controller.h @@ -5,6 +5,11 @@ #import <Cocoa/Cocoa.h> #import "base/cocoa_protocols_mac.h" +#include "base/scoped_ptr.h" + +namespace BaseBubbleControllerInternal { +class Bridge; +} @class InfoBubbleView; @@ -24,6 +29,8 @@ NSWindow* parentWindow_; // weak NSPoint anchor_; IBOutlet InfoBubbleView* bubble_; // to set arrow position + // Bridge that listens for notifications. + scoped_ptr<BaseBubbleControllerInternal::Bridge> base_bridge_; } @property (nonatomic, readonly) NSWindow* parentWindow; diff --git a/chrome/browser/cocoa/base_bubble_controller.mm b/chrome/browser/cocoa/base_bubble_controller.mm index a67bc64..e9804c5 100644 --- a/chrome/browser/cocoa/base_bubble_controller.mm +++ b/chrome/browser/cocoa/base_bubble_controller.mm @@ -10,12 +10,41 @@ #include "base/scoped_nsobject.h" #include "base/string_util.h" #import "chrome/browser/cocoa/info_bubble_view.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" #include "grit/generated_resources.h" @interface BaseBubbleController (Private) - (void)updateOriginFromAnchor; @end +namespace BaseBubbleControllerInternal { + +// This bridge listens for notifications so that the bubble closes when a user +// switches tabs (including by opening a new one). +class Bridge : public NotificationObserver { + public: + explicit Bridge(BaseBubbleController* controller) : controller_(controller) { + registrar_.Add(this, NotificationType::TAB_CONTENTS_HIDDEN, + NotificationService::AllSources()); + } + + // NotificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + [controller_ close]; + } + + private: + BaseBubbleController* controller_; // Weak, owns this. + NotificationRegistrar registrar_; +}; + +} // namespace BaseBubbleControllerInternal + @implementation BaseBubbleController @synthesize parentWindow = parentWindow_; @@ -62,6 +91,7 @@ if ((self = [super initWithWindow:theWindow])) { parentWindow_ = parentWindow; anchor_ = anchoredAt; + DCHECK(![[self window] delegate]); [theWindow setDelegate:self]; @@ -88,6 +118,8 @@ DCHECK(bubble_); DCHECK_EQ(self, [[self window] delegate]); + base_bridge_.reset(new BaseBubbleControllerInternal::Bridge(self)); + [bubble_ setBubbleType:info_bubble::kWhiteInfoBubble]; [bubble_ setArrowLocation:info_bubble::kTopRight]; } @@ -158,7 +190,7 @@ info_bubble::kBubbleArrowWidth / 2.0, 0); offsets = [[parentWindow_ contentView] convertSize:offsets toView:nil]; if ([bubble_ arrowLocation] == info_bubble::kTopRight) { - origin.x -= NSWidth([window frame]) + offsets.width; + origin.x -= NSWidth([window frame]) - offsets.width; } else { origin.x -= offsets.width; } diff --git a/chrome/browser/cocoa/base_view.h b/chrome/browser/cocoa/base_view.h index c3d49fd..bece608 100644 --- a/chrome/browser/cocoa/base_view.h +++ b/chrome/browser/cocoa/base_view.h @@ -35,4 +35,11 @@ @end +// A notification that a view may issue when it receives first responder status. +// The name is |kViewDidBecomeFirstResponder|, the object is the view, and the +// NSSelectionDirection is wrapped in an NSNumber under the key +// |kSelectionDirection|. +extern NSString* kViewDidBecomeFirstResponder; +extern NSString* kSelectionDirection; + #endif // CHROME_BROWSER_COCOA_BASE_VIEW_H_ diff --git a/chrome/browser/cocoa/base_view.mm b/chrome/browser/cocoa/base_view.mm index 4c9f999..433733e 100644 --- a/chrome/browser/cocoa/base_view.mm +++ b/chrome/browser/cocoa/base_view.mm @@ -4,6 +4,10 @@ #include "chrome/browser/cocoa/base_view.h" +NSString* kViewDidBecomeFirstResponder = + @"Chromium.kViewDidBecomeFirstResponder"; +NSString* kSelectionDirection = @"Chromium.kSelectionDirection"; + @implementation BaseView - (id)initWithFrame:(NSRect)frame { diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h index 70f2ea6..6d0a0f6 100644 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h +++ b/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h @@ -13,6 +13,7 @@ #include "base/scoped_nsobject.h" #include "base/scoped_ptr.h" #include "chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.h" +#import "chrome/browser/cocoa/bookmarks/bookmark_bar_constants.h" #import "chrome/browser/cocoa/bookmarks/bookmark_bar_state.h" #import "chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.h" #import "chrome/browser/cocoa/bookmarks/bookmark_button.h" @@ -43,14 +44,33 @@ namespace bookmarks { // Used as a maximum width for buttons on the bar. const CGFloat kDefaultBookmarkWidth = 150.0; -// TODO(jrg): http://crbug.com/36276 to get final sizes. +// Horizontal frame inset for buttons in the bookmark bar. +const CGFloat kBookmarkHorizontalPadding = 1.0; + +// Vertical frame inset for buttons in the bookmark bar. +const CGFloat kBookmarkVerticalPadding = 2.0; + // Used as a min/max width for buttons on menus (not on the bar). const CGFloat kBookmarkMenuButtonMinimumWidth = 100.0; const CGFloat kBookmarkMenuButtonMaximumWidth = 485.0; -const CGFloat kBookmarkVerticalPadding = 2.0; -const CGFloat kBookmarkHorizontalPadding = 1.0; +// Horizontal separation between a menu button and both edges of its menu. const CGFloat kBookmarkSubMenuHorizontalPadding = 5.0; + +// TODO(mrossetti): Add constant (kBookmarkVerticalSeparation) for the gap +// between buttons in a folder menu. Right now we're using +// kBookmarkVerticalPadding, which is dual purpose and wrong. +// http://crbug.com/59057 + +// Convenience constant giving the vertical distance from the top extent of one +// folder button to the next button. +const CGFloat kBookmarkButtonVerticalSpan = + kBookmarkButtonHeight + kBookmarkVerticalPadding; + +// The minimum separation between a folder menu and the edge of the screen. +// If the menu gets closer to the edge of the screen (either right or left) +// then it is pops up in the opposite direction. +// (See -[BookmarkBarFolderController childFolderWindowLeftForWidth:]). const CGFloat kBookmarkHorizontalScreenPadding = 8.0; // Our NSScrollView is supposed to be just barely big enough to fit its diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.mm index 63cc171..03e867e 100644 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.mm +++ b/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.mm @@ -15,7 +15,6 @@ #include "chrome/browser/browser_list.h" #import "chrome/browser/cocoa/background_gradient_view.h" #import "chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_constants.h" #import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.h" #import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window.h" #import "chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.h" diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.mm index ca1514c..6eaca1a 100644 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.mm +++ b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.mm @@ -27,13 +27,11 @@ const NSTimeInterval kBookmarkBarFolderScrollInterval = 0.1; // Amount to scroll by per timer fire. We scroll rather slowly; to // accomodate we do several at a time. const CGFloat kBookmarkBarFolderScrollAmount = - 3 * (bookmarks::kBookmarkButtonHeight + - bookmarks::kBookmarkVerticalPadding); + 3 * bookmarks::kBookmarkButtonVerticalSpan; // Amount to scroll for each scroll wheel delta. const CGFloat kBookmarkBarFolderScrollWheelAmount = - 1 * (bookmarks::kBookmarkButtonHeight + - bookmarks::kBookmarkVerticalPadding); + 1 * bookmarks::kBookmarkButtonVerticalSpan; // When constraining a scrolling bookmark bar folder window to the // screen, shrink the "constrain" by this much vertically. Currently @@ -387,8 +385,7 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; } - (int)windowHeightForButtonCount:(int)buttonCount { - return (buttonCount * (bookmarks::kBookmarkButtonHeight + - bookmarks::kBookmarkVerticalPadding)) + + return (buttonCount * bookmarks::kBookmarkButtonVerticalSpan) + bookmarks::kBookmarkVerticalPadding; } @@ -475,8 +472,7 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; // http://crbug.com/35966 NSRect buttonsOuterFrame = NSMakeRect( bookmarks::kBookmarkSubMenuHorizontalPadding, - (height - - (bookmarks::kBookmarkButtonHeight + bookmarks::kBookmarkVerticalPadding)), + (height - bookmarks::kBookmarkButtonVerticalSpan), bookmarks::kDefaultBookmarkWidth, bookmarks::kBookmarkButtonHeight); @@ -498,8 +494,7 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; frame:buttonsOuterFrame]; [buttons_ addObject:button]; [mainView_ addSubview:button]; - buttonsOuterFrame.origin.y -= (bookmarks::kBookmarkButtonHeight + - bookmarks::kBookmarkVerticalPadding); + buttonsOuterFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; } } @@ -886,8 +881,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { // bookmark_bar_controller.mm, but vertical instead of horizontal. // Generalize to be axis independent then share code. // http://crbug.com/35966 -// Get UI review on "middle half" ness. -// http://crbug.com/36276 - (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point { for (BookmarkButton* button in buttons_.get()) { // No early break -- makes no assumption about button ordering. @@ -1252,8 +1245,11 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { - (void)addButtonForNode:(const BookmarkNode*)node atIndex:(NSInteger)buttonIndex { - // Propose the frame for the new button. - NSRect newButtonFrame = NSMakeRect(0, 0, 500, 500); // Placeholder values. + // Propose the frame for the new button. By default, this will be set to the + // topmost button's frame (and there will always be one) offset upward in + // anticipation of insertion. + NSRect newButtonFrame = [[buttons_ objectAtIndex:0] frame]; + newButtonFrame.origin.y += bookmarks::kBookmarkButtonVerticalSpan; // When adding a button to an empty folder we must remove the 'empty' // placeholder button. This can be detected by checking for a parent // child count of 1. @@ -1277,8 +1273,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { // which is where the new button will be located. newButtonFrame = [button frame]; NSRect buttonFrame = [button frame]; - buttonFrame.origin.y += bookmarks::kBookmarkBarHeight + - bookmarks::kBookmarkVerticalPadding; + buttonFrame.origin.y += bookmarks::kBookmarkButtonVerticalSpan; [button setFrame:buttonFrame]; } [[button cell] mouseExited:nil]; // De-highlight. @@ -1352,8 +1347,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { for (NSInteger i = fromIndex; i < toIndex; ++i) { BookmarkButton* button = [buttons_ objectAtIndex:i]; NSRect frame = [button frame]; - frame.origin.y += bookmarks::kBookmarkBarHeight + - bookmarks::kBookmarkVerticalPadding; + frame.origin.y += bookmarks::kBookmarkButtonVerticalSpan; [button setFrameOrigin:frame.origin]; } } else { @@ -1362,8 +1356,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { for (NSInteger i = fromIndex - 1; i >= toIndex; --i) { BookmarkButton* button = [buttons_ objectAtIndex:i]; NSRect buttonFrame = [button frame]; - buttonFrame.origin.y -= bookmarks::kBookmarkBarHeight + - bookmarks::kBookmarkVerticalPadding; + buttonFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; [button setFrameOrigin:buttonFrame.origin]; } } @@ -1408,8 +1401,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { for (NSInteger i = 0; i < buttonIndex; ++i) { BookmarkButton* button = [buttons_ objectAtIndex:i]; NSRect buttonFrame = [button frame]; - buttonFrame.origin.y -= bookmarks::kBookmarkButtonHeight + - bookmarks::kBookmarkVerticalPadding; + buttonFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; [button setFrame:buttonFrame]; } // Search for and adjust submenus, if necessary. diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm index 293aff1..ee9ccb8 100644 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm +++ b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm @@ -24,12 +24,32 @@ // Add a redirect to make testing easier. @interface BookmarkBarFolderController(MakeTestingEasier) - (IBAction)openBookmarkFolderFromButton:(id)sender; +- (void)validateMenuSpacing; @end @implementation BookmarkBarFolderController(MakeTestingEasier) - (IBAction)openBookmarkFolderFromButton:(id)sender { [[self folderTarget] openBookmarkFolderFromButton:sender]; } + +// Utility function to verify that the buttons in this folder are all +// evenly spaced in a progressive manner. +- (void)validateMenuSpacing { + BOOL firstButton = YES; + CGFloat lastVerticalOffset = 0.0; + for (BookmarkButton* button in [self buttons]) { + if (firstButton) { + firstButton = NO; + lastVerticalOffset = [button frame].origin.y; + } else { + CGFloat nextVerticalOffset = [button frame].origin.y; + EXPECT_CGFLOAT_EQ(lastVerticalOffset - + bookmarks::kBookmarkButtonVerticalSpan, + nextVerticalOffset); + lastVerticalOffset = nextVerticalOffset; + } + } +} @end // Don't use a high window level when running unit tests -- it'll @@ -709,6 +729,9 @@ TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToFolder) { expectedToWindowFrame.size.height += diff; EXPECT_NSRECT_EQ(expectedToWindowFrame, newToWindowFrame); + // Check button spacing. + [folderController validateMenuSpacing]; + // Move the button back to the bar at the beginning. draggedButton = [folderController buttonWithTitleEqualTo:@"1b"]; ASSERT_TRUE(draggedButton); @@ -836,6 +859,10 @@ TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToSubfolder) { "4f2f1b 4f2f2b 4f2f3b 5b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] "); EXPECT_EQ(expected_string, model_test_utils::ModelStringFromNode(root)); + // Check button spacing. + [folderController validateMenuSpacing]; + [subfolderController validateMenuSpacing]; + // Check the window layouts. The folder window should not have changed, // but the subfolder window should have shifted vertically and grown. NSRect newToWindowFrame = [toWindow frame]; @@ -893,6 +920,9 @@ TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveWithinFolder) { // The folder window should not have changed. NSRect newToWindowFrame = [toWindow frame]; EXPECT_NSRECT_EQ(oldToWindowFrame, newToWindowFrame); + + // Check button spacing. + [folderController validateMenuSpacing]; } TEST_F(BookmarkBarFolderControllerMenuTest, DragParentOntoChild) { @@ -927,6 +957,9 @@ TEST_F(BookmarkBarFolderControllerMenuTest, DragParentOntoChild) { copy:NO]; // The model should not have changed. EXPECT_EQ(model_string, model_test_utils::ModelStringFromNode(root)); + + // Check button spacing. + [folderController validateMenuSpacing]; } TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveChildToParent) { @@ -973,6 +1006,8 @@ TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveChildToParent) { "4f2f1b 4f2f2b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); EXPECT_EQ(expected_string, model_test_utils::ModelStringFromNode(root)); + // Check button spacing. + [folderController validateMenuSpacing]; // The window should not have gone away. EXPECT_TRUE([bar_ folderController]); // The subfolder should have gone away. @@ -1077,6 +1112,9 @@ TEST_F(BookmarkBarFolderControllerMenuTest, MoveRemoveAddButtons) { EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:1] title]); EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:2] title]); EXPECT_EQ(oldDisplayedButtons, [buttons count]); + + // Check button spacing. + [folder validateMenuSpacing]; } TEST_F(BookmarkBarFolderControllerMenuTest, ControllerForNode) { @@ -1199,6 +1237,9 @@ TEST_F(BookmarkBarFolderControllerMenuTest, MenuSizingAndScrollArrows) { // Check the size. It should have reduced. EXPECT_GT(menuWidth, NSWidth([folderMenu frame])); EXPECT_GT(buttonWidth, NSWidth([button frame])); + + // Check button spacing. + [folderController validateMenuSpacing]; } // See http://crbug.com/46101 @@ -1324,6 +1365,9 @@ TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkData) { "2f3b ] 3b 4b "); actual = model_test_utils::ModelStringFromNode(root); EXPECT_EQ(expectedA, actual); + + // Check button spacing. + [folderController validateMenuSpacing]; } TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkDataToTrash) { @@ -1363,6 +1407,9 @@ TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkDataToTrash) { "2f3b ] 3b 4b "); actual = model_test_utils::ModelStringFromNode(root); EXPECT_EQ(expected, actual); + + // Check button spacing. + [folderController validateMenuSpacing]; } TEST_F(BookmarkBarFolderControllerMenuTest, AddURLs) { @@ -1405,6 +1452,9 @@ TEST_F(BookmarkBarFolderControllerMenuTest, AddURLs) { "2f2f3b ] 2f3b ] 3b 4b "); actual = model_test_utils::ModelStringFromNode(root); EXPECT_EQ(expected, actual); + + // Check button spacing. + [folderController validateMenuSpacing]; } TEST_F(BookmarkBarFolderControllerMenuTest, DropPositionIndicator) { diff --git a/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm index 4be2067..c2f9026 100644 --- a/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm +++ b/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm @@ -8,7 +8,7 @@ #include "base/string16.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser.h" #include "chrome/browser/cocoa/bookmarks/bookmark_menu_bridge.h" diff --git a/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm b/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm index 534ae66..439bb2c 100644 --- a/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm +++ b/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm @@ -7,7 +7,7 @@ #include "app/text_elider.h" #include "base/sys_string_conversions.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" // IDC_BOOKMARK_MENU +#include "chrome/app/chrome_command_ids.h" // IDC_BOOKMARK_MENU #import "chrome/browser/app_controller_mac.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser.h" diff --git a/chrome/browser/cocoa/browser_window_cocoa.h b/chrome/browser/cocoa/browser_window_cocoa.h index 9747178..9a46f7d 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.h +++ b/chrome/browser/cocoa/browser_window_cocoa.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_COCOA_BROWSER_WINDOW_COCOA_H_ #pragma once +#include "base/scoped_nsobject.h" #include "base/task.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/bookmarks/bookmark_model.h" @@ -134,6 +135,7 @@ class BrowserWindowCocoa : public BrowserWindow, Browser* browser_; // weak, owned by controller BrowserWindowController* controller_; // weak, owns us ScopedRunnableMethodFactory<Browser> confirm_close_factory_; + scoped_nsobject<NSString> pending_window_title_; }; #endif // CHROME_BROWSER_COCOA_BROWSER_WINDOW_COCOA_H_ diff --git a/chrome/browser/cocoa/browser_window_cocoa.mm b/chrome/browser/cocoa/browser_window_cocoa.mm index 2203240..5e21af0 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.mm +++ b/chrome/browser/cocoa/browser_window_cocoa.mm @@ -9,7 +9,7 @@ #include "base/logging.h" #include "base/message_loop.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/bookmarks/bookmark_utils.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" @@ -151,7 +151,24 @@ void BrowserWindowCocoa::UpdateTitleBar() { NSString* newTitle = base::SysUTF16ToNSString(browser_->GetWindowTitleForCurrentTab()); - [window() setTitle:newTitle]; + // Work around Cocoa bug: if a window changes title during the tracking of the + // Window menu it doesn't display well and the constant re-sorting of the list + // makes it difficult for the user to pick the desired window. Delay window + // title updates until the default run-loop mode. + + if (pending_window_title_.get()) + [[NSRunLoop currentRunLoop] + cancelPerformSelector:@selector(setTitle:) + target:window() + argument:pending_window_title_.get()]; + + pending_window_title_.reset([newTitle copy]); + [[NSRunLoop currentRunLoop] + performSelector:@selector(setTitle:) + target:window() + argument:newTitle + order:0 + modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]]; } void BrowserWindowCocoa::ShelfVisibilityChanged() { diff --git a/chrome/browser/cocoa/browser_window_controller.h b/chrome/browser/cocoa/browser_window_controller.h index efa49ab..39bd9c4 100644 --- a/chrome/browser/cocoa/browser_window_controller.h +++ b/chrome/browser/cocoa/browser_window_controller.h @@ -217,7 +217,7 @@ class TabContents; // Executes the command in the context of the current browser. // |command| is an integer value containing one of the constants defined in the -// "chrome/app/chrome_dll_resource.h" file. +// "chrome/app/chrome_command_ids.h" file. - (void)executeCommand:(int)command; // Delegate method for the status bubble to query its base frame. diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm index dc9f400..0e39267 100644 --- a/chrome/browser/cocoa/browser_window_controller.mm +++ b/chrome/browser/cocoa/browser_window_controller.mm @@ -13,7 +13,7 @@ #include "base/nsimage_cache_mac.h" #import "base/scoped_nsobject.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" // IDC_* +#include "chrome/app/chrome_command_ids.h" // IDC_* #include "chrome/browser/bookmarks/bookmark_editor.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" @@ -1887,6 +1887,9 @@ willAnimateFromState:(bookmarks::VisualState)oldState if (statusBubble_) statusBubble_->SwitchParentWindow(destWindow); + // Move the title over. + [destWindow setTitle:[window title]]; + // The window needs to be onscreen before we can set its first responder. [destWindow makeKeyAndOrderFront:self]; [focusTracker restoreFocusInWindow:destWindow]; diff --git a/chrome/browser/cocoa/browser_window_controller_unittest.mm b/chrome/browser/cocoa/browser_window_controller_unittest.mm index a6fda62..4e3a5ac 100644 --- a/chrome/browser/cocoa/browser_window_controller_unittest.mm +++ b/chrome/browser/cocoa/browser_window_controller_unittest.mm @@ -5,7 +5,7 @@ #include "app/l10n_util_mac.h" #include "base/scoped_nsobject.h" #include "base/scoped_ptr.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/cocoa/browser_test_helper.h" diff --git a/chrome/browser/cocoa/chrome_browser_window_unittest.mm b/chrome/browser/cocoa/chrome_browser_window_unittest.mm index 2b82f79..fc39ccd 100644 --- a/chrome/browser/cocoa/chrome_browser_window_unittest.mm +++ b/chrome/browser/cocoa/chrome_browser_window_unittest.mm @@ -4,6 +4,7 @@ #import <Cocoa/Cocoa.h> +#include "base/debug/debugger.h" #import "chrome/browser/cocoa/chrome_browser_window.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" #include "testing/gtest/include/gtest/gtest.h" @@ -22,7 +23,7 @@ class ChromeBrowserWindowTest : public CocoaTest { styleMask:mask backing:NSBackingStoreBuffered defer:NO]; - if (DebugUtil::BeingDebugged()) { + if (base::debug::BeingDebugged()) { [window_ orderFront:nil]; } else { [window_ orderBack:nil]; diff --git a/chrome/browser/cocoa/chrome_event_processing_window_unittest.mm b/chrome/browser/cocoa/chrome_event_processing_window_unittest.mm index 1b54dcb..ebcd3f3 100644 --- a/chrome/browser/cocoa/chrome_event_processing_window_unittest.mm +++ b/chrome/browser/cocoa/chrome_event_processing_window_unittest.mm @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - +#include "base/debug/debugger.h" #include "base/scoped_nsobject.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #import "chrome/browser/cocoa/chrome_event_processing_window.h" #import "chrome/browser/cocoa/browser_window_controller.h" #import "chrome/browser/cocoa/browser_frame_view.h" @@ -38,7 +38,7 @@ class ChromeEventProcessingWindowTest : public CocoaTest { styleMask:mask backing:NSBackingStoreBuffered defer:NO]; - if (DebugUtil::BeingDebugged()) { + if (base::debug::BeingDebugged()) { [window_ orderFront:nil]; } else { [window_ orderBack:nil]; diff --git a/chrome/browser/cocoa/cocoa_test_helper.mm b/chrome/browser/cocoa/cocoa_test_helper.mm index 136412b..885a88a 100644 --- a/chrome/browser/cocoa/cocoa_test_helper.mm +++ b/chrome/browser/cocoa/cocoa_test_helper.mm @@ -4,6 +4,7 @@ #import "chrome/browser/cocoa/cocoa_test_helper.h" +#include "base/debug/debugger.h" #include "base/logging.h" #include "base/test/test_timeouts.h" #import "chrome/browser/chrome_browser_application_mac.h" @@ -49,9 +50,19 @@ CocoaTest::CocoaTest() : called_tear_down_(false), test_window_(nil) { BootstrapCocoa(); + // Set the duration of AppKit-evaluated animations (such as frame changes) // to zero for testing purposes. That way they take effect immediately. [[NSAnimationContext currentContext] setDuration:0.0]; + + // The above does not affect window-resize time, such as for an + // attached sheet dropping in. Set that duration for the current + // process (this is not persisted). Empirically, the value of 0.0 + // is ignored. + NSDictionary* dict = + [NSDictionary dictionaryWithObject:@"0.01" forKey:@"NSWindowResizeTime"]; + [[NSUserDefaults standardUserDefaults] registerDefaults:dict]; + // Collect the list of windows that were open when the test started so // that we don't wait for them to close in TearDown. Has to be done // after BootstrapCocoa is called. @@ -184,7 +195,7 @@ std::set<NSWindow*> CocoaTest::WindowsLeft() { CocoaTestHelperWindow* CocoaTest::test_window() { if (!test_window_) { test_window_ = [[CocoaTestHelperWindow alloc] init]; - if (DebugUtil::BeingDebugged()) { + if (base::debug::BeingDebugged()) { [test_window_ orderFront:nil]; } else { [test_window_ orderBack:nil]; diff --git a/chrome/browser/cocoa/confirm_quit_panel_controller.h b/chrome/browser/cocoa/confirm_quit_panel_controller.h new file mode 100644 index 0000000..c518222 --- /dev/null +++ b/chrome/browser/cocoa/confirm_quit_panel_controller.h @@ -0,0 +1,24 @@ +// 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. + +#import <Cocoa/Cocoa.h> + +#include "base/cocoa_protocols_mac.h" + +// The ConfirmQuitPanelController manages the black HUD window that tells users +// to "Hold Cmd+Q to Quit". +@interface ConfirmQuitPanelController : NSWindowController<NSWindowDelegate> { +} + +// Designated initializer. Loads window from NIB but does not show it. +- (id)init; + +// Shows the window. +- (void)showWindow:(id)sender; + +// If the user did not confirm quit, send this message to give the user +// instructions on how to quit. +- (void)dismissPanel; + +@end diff --git a/chrome/browser/cocoa/confirm_quit_panel_controller.mm b/chrome/browser/cocoa/confirm_quit_panel_controller.mm new file mode 100644 index 0000000..5b624dd --- /dev/null +++ b/chrome/browser/cocoa/confirm_quit_panel_controller.mm @@ -0,0 +1,69 @@ +// 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. + +#import <Cocoa/Cocoa.h> +#import <QuartzCore/QuartzCore.h> + +#include "base/logging.h" +#include "base/mac_util.h" +#include "base/scoped_nsobject.h" +#import "chrome/browser/cocoa/confirm_quit_panel_controller.h" + +@interface ConfirmQuitPanelController (Private) +- (void)animateFadeOut; +@end + +@implementation ConfirmQuitPanelController + +- (id)init { + NSString* nibPath = + [mac_util::MainAppBundle() pathForResource:@"ConfirmQuitPanel" + ofType:@"nib"]; + if ((self = [super initWithWindowNibPath:nibPath owner:self])) { + } + return self; +} + +- (void)awakeFromNib { + DCHECK([self window]); + DCHECK_EQ(self, [[self window] delegate]); +} + +- (void)windowWillClose:(NSNotification*)notif { + // Release all animations because CAAnimation retains its delegate (self), + // which will cause a retain cycle. Break it! + [[self window] setAnimations:[NSDictionary dictionary]]; + [self autorelease]; +} + +- (void)showWindow:(id)sender { + [[self window] center]; + [[self window] setAlphaValue:1.0]; + [super showWindow:sender]; +} + +- (void)dismissPanel { + [self performSelector:@selector(animateFadeOut) + withObject:nil + afterDelay:1.0]; +} + +- (void)animateFadeOut { + NSWindow* window = [self window]; + scoped_nsobject<CAAnimation> animation( + [[window animationForKey:@"alphaValue"] copy]); + [animation setDelegate:self]; + [animation setDuration:0.2]; + NSMutableDictionary* dictionary = + [NSMutableDictionary dictionaryWithDictionary:[window animations]]; + [dictionary setObject:animation forKey:@"alphaValue"]; + [window setAnimations:dictionary]; + [[window animator] setAlphaValue:0.0]; +} + +- (void)animationDidStop:(CAAnimation*)theAnimation finished:(BOOL)flag { + [self close]; +} + +@end diff --git a/chrome/browser/cocoa/confirm_quit_panel_controller_unittest.mm b/chrome/browser/cocoa/confirm_quit_panel_controller_unittest.mm new file mode 100644 index 0000000..fddf164 --- /dev/null +++ b/chrome/browser/cocoa/confirm_quit_panel_controller_unittest.mm @@ -0,0 +1,26 @@ +// 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/cocoa/confirm_quit_panel_controller.h" + +#import "chrome/browser/cocoa/cocoa_test_helper.h" + +namespace { + +class ConfirmQuitPanelControllerTest : public CocoaTest { + public: + ConfirmQuitPanelControllerTest() : controller_(nil) { + } + + ConfirmQuitPanelController* controller_; // Weak, owns self. +}; + + +TEST_F(ConfirmQuitPanelControllerTest, ShowAndDismiss) { + controller_ = [[ConfirmQuitPanelController alloc] init]; + [controller_ showWindow:nil]; + [controller_ dismissPanel]; // Releases self. +} + +} // namespace diff --git a/chrome/browser/cocoa/constrained_html_delegate_mac.mm b/chrome/browser/cocoa/constrained_html_delegate_mac.mm index 22d4558..efb228a 100644 --- a/chrome/browser/cocoa/constrained_html_delegate_mac.mm +++ b/chrome/browser/cocoa/constrained_html_delegate_mac.mm @@ -29,7 +29,7 @@ class ConstrainedHtmlDelegateMac : // your delegate in this method." if (is_sheet_open()) [NSApp endSheet:sheet()]; - + html_delegate_->OnDialogClosed(""); delete this; } diff --git a/chrome/browser/cocoa/content_setting_bubble_cocoa.mm b/chrome/browser/cocoa/content_setting_bubble_cocoa.mm index f5e7e93..dda55f5 100644 --- a/chrome/browser/cocoa/content_setting_bubble_cocoa.mm +++ b/chrome/browser/cocoa/content_setting_bubble_cocoa.mm @@ -437,6 +437,8 @@ NSTextField* LabelWithFrame(NSString* text, const NSRect& frame) { } - (void)awakeFromNib { + [super awakeFromNib]; + [[self bubble] setBubbleType:info_bubble::kWhiteInfoBubble]; [[self bubble] setArrowLocation:info_bubble::kTopRight]; diff --git a/chrome/browser/cocoa/content_setting_bubble_cocoa_unittest.mm b/chrome/browser/cocoa/content_setting_bubble_cocoa_unittest.mm index c42520d..3485782 100644 --- a/chrome/browser/cocoa/content_setting_bubble_cocoa_unittest.mm +++ b/chrome/browser/cocoa/content_setting_bubble_cocoa_unittest.mm @@ -6,6 +6,7 @@ #import <Cocoa/Cocoa.h> +#include "base/debug/debugger.h" #include "base/scoped_nsobject.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" #include "chrome/browser/content_setting_bubble_model.h" @@ -42,7 +43,7 @@ TEST_F(ContentSettingBubbleControllerTest, Init) { backing:NSBackingStoreBuffered defer:NO]); [parent setReleasedWhenClosed:NO]; - if (DebugUtil::BeingDebugged()) + if (base::debug::BeingDebugged()) [parent.get() orderFront:nil]; else [parent.get() orderBack:nil]; diff --git a/chrome/browser/cocoa/cookie_details.h b/chrome/browser/cocoa/cookie_details.h index 5298ad8..614c87c 100644 --- a/chrome/browser/cocoa/cookie_details.h +++ b/chrome/browser/cocoa/cookie_details.h @@ -83,9 +83,8 @@ enum CocoaCookieDetailsType { // kCocoaCookieDetailsTypeTreeAppCache nodes. scoped_nsobject<NSString> created_; - // Only set for types kCocoaCookieDetailsTypeCookie, - // kCocoaCookieDetailsTypePromptDatabase, and - // kCocoaCookieDetailsTypeTreeIndexedDB nodes. + // Only set for types kCocoaCookieDetailsTypeCookie, and + // kCocoaCookieDetailsTypePromptDatabase nodes. scoped_nsobject<NSString> name_; // Only set for type kCocoaCookieDetailsTypeTreeLocalStorage, diff --git a/chrome/browser/cocoa/cookie_details.mm b/chrome/browser/cocoa/cookie_details.mm index 4c483d1..b60e005 100644 --- a/chrome/browser/cocoa/cookie_details.mm +++ b/chrome/browser/cocoa/cookie_details.mm @@ -248,7 +248,6 @@ lastModified_.reset([base::SysWideToNSString( base::TimeFormatFriendlyDateAndTime( indexedDBInfo->last_modified)) retain]); - name_.reset([base::SysUTF8ToNSString(indexedDBInfo->database_name) retain]); } return self; } diff --git a/chrome/browser/cocoa/cookie_details_unittest.mm b/chrome/browser/cocoa/cookie_details_unittest.mm index 897d92a..305c0f9 100644 --- a/chrome/browser/cocoa/cookie_details_unittest.mm +++ b/chrome/browser/cocoa/cookie_details_unittest.mm @@ -145,7 +145,6 @@ TEST_F(CookiesDetailsTest, CreateForTreeIndexedDB) { unsigned short port = 80; std::string database_identifier("id"); std::string origin("moose.org"); - std::string name("name"); FilePath file_path(FILE_PATH_LITERAL("/")); int64 size = 1234; base::Time last_modified = base::Time::Now(); @@ -154,7 +153,6 @@ TEST_F(CookiesDetailsTest, CreateForTreeIndexedDB) { port, database_identifier, origin, - name, file_path, size, last_modified); diff --git a/chrome/browser/cocoa/dev_tools_controller.mm b/chrome/browser/cocoa/dev_tools_controller.mm index 3dee715..c9cf4e1 100644 --- a/chrome/browser/cocoa/dev_tools_controller.mm +++ b/chrome/browser/cocoa/dev_tools_controller.mm @@ -92,11 +92,11 @@ const int kMinWebHeight = 50; } // Make sure |splitOffset| isn't too large or too small. + splitOffset = std::max(static_cast<CGFloat>(kMinWebHeight), splitOffset); splitOffset = std::min(splitOffset, NSHeight([splitView_ frame]) - kMinWebHeight); DCHECK_GE(splitOffset, 0) << "kMinWebHeight needs to be smaller than " << "smallest available tab contents space."; - splitOffset = std::max(static_cast<CGFloat>(0), splitOffset); [self resizeDevToolsToNewHeight:splitOffset]; } else { diff --git a/chrome/browser/cocoa/download/download_item_cell.mm b/chrome/browser/cocoa/download/download_item_cell.mm index c286669..2f5d856 100644 --- a/chrome/browser/cocoa/download/download_item_cell.mm +++ b/chrome/browser/cocoa/download/download_item_cell.mm @@ -235,7 +235,7 @@ NSGradient* BackgroundTheme::GetNSGradient(int id) const { - (void)setStateFromDownload:(BaseDownloadItemModel*)downloadModel { // Set the name of the download. - downloadPath_ = downloadModel->download()->GetFileName(); + downloadPath_ = downloadModel->download()->GetFileNameToReportUser(); std::wstring statusText = downloadModel->GetStatusText(); if (statusText.empty()) { diff --git a/chrome/browser/cocoa/download/download_item_controller.mm b/chrome/browser/cocoa/download/download_item_controller.mm index d061e93..c210a3b 100644 --- a/chrome/browser/cocoa/download/download_item_controller.mm +++ b/chrome/browser/cocoa/download/download_item_controller.mm @@ -181,8 +181,8 @@ class DownloadShelfContextMenuMac : public DownloadShelfContextMenu { // This basic fixup copies Windows DownloadItemView::DownloadItemView(). // Extract the file extension (if any). - FilePath filepath(downloadModel->download()->original_name()); - FilePath::StringType extension = filepath.Extension(); + FilePath filename(downloadModel->download()->target_name()); + FilePath::StringType extension = filename.Extension(); // Remove leading '.' from the extension if (extension.length() > 0) @@ -197,15 +197,14 @@ class DownloadShelfContextMenuMac : public DownloadShelfContextMenu { } // Rebuild the filename.extension. - std::wstring rootname = - UTF8ToWide(filepath.BaseName().RemoveExtension().value()); + std::wstring rootname = UTF8ToWide(filename.RemoveExtension().value()); ElideString(rootname, kFileNameMaxLength - extension.length(), &rootname); - std::string filename = WideToUTF8(rootname); + std::string new_filename = WideToUTF8(rootname); if (extension.length()) - filename += std::string(".") + extension; + new_filename += std::string(".") + extension; dangerousWarning = l10n_util::GetNSStringFWithFixup( - IDS_PROMPT_DANGEROUS_DOWNLOAD, UTF8ToUTF16(filename)); + IDS_PROMPT_DANGEROUS_DOWNLOAD, UTF8ToUTF16(new_filename)); confirmButtonTitle = l10n_util::GetNSStringWithFixup(IDS_SAVE_DOWNLOAD); } [dangerousDownloadLabel_ setStringValue:dangerousWarning]; @@ -268,7 +267,7 @@ class DownloadShelfContextMenuMac : public DownloadShelfContextMenu { - (void)updateToolTip { string16 elidedFilename = gfx::ElideFilename( - [self download]->GetFileName(), + [self download]->GetFileNameToReportUser(), gfx::Font(), kToolTipMaxWidth); [progressView_ setToolTip:base::SysUTF16ToNSString(elidedFilename)]; } diff --git a/chrome/browser/cocoa/download/download_item_mac.h b/chrome/browser/cocoa/download/download_item_mac.h index d5f013e..1a27549 100644 --- a/chrome/browser/cocoa/download/download_item_mac.h +++ b/chrome/browser/cocoa/download/download_item_mac.h @@ -55,7 +55,7 @@ class DownloadItemMac : DownloadItem::Observer { CancelableRequestConsumerT<int, 0> icon_consumer_; // Stores the last known name where the file will be saved. - FilePath lastFilePath_; + FilePath lastFileName_; DISALLOW_COPY_AND_ASSIGN(DownloadItemMac); }; diff --git a/chrome/browser/cocoa/download/download_item_mac.mm b/chrome/browser/cocoa/download/download_item_mac.mm index b647655..0f8c5b7 100644 --- a/chrome/browser/cocoa/download/download_item_mac.mm +++ b/chrome/browser/cocoa/download/download_item_mac.mm @@ -34,12 +34,12 @@ void DownloadItemMac::OnDownloadUpdated(DownloadItem* download) { [item_controller_ clearDangerousMode]; } - if (download->full_path() != lastFilePath_) { - // Turns out the file path is "unconfirmed %d.download" for dangerous + if (download->GetUserVerifiedFileName() != lastFileName_) { + // Turns out the file path is "unconfirmed %d.crdownload" for dangerous // downloads. When the download is confirmed, the file is renamed on // another thread, so reload the icon if the download filename changes. LoadIcon(); - lastFilePath_ = download->full_path(); + lastFileName_ = download->GetUserVerifiedFileName(); [item_controller_ updateToolTip]; } @@ -72,7 +72,7 @@ void DownloadItemMac::LoadIcon() { } // We may already have this particular image cached. - FilePath file = download_model_->download()->full_path(); + FilePath file = download_model_->download()->GetUserVerifiedFileName(); SkBitmap* icon_bitmap = icon_manager->LookupIcon(file, IconLoader::SMALL); if (icon_bitmap) { NSImage* icon = gfx::SkBitmapToNSImage(*icon_bitmap); diff --git a/chrome/browser/cocoa/encoding_menu_controller_delegate_mac.mm b/chrome/browser/cocoa/encoding_menu_controller_delegate_mac.mm index bb7d67b..afba4b0 100644 --- a/chrome/browser/cocoa/encoding_menu_controller_delegate_mac.mm +++ b/chrome/browser/cocoa/encoding_menu_controller_delegate_mac.mm @@ -8,7 +8,7 @@ #include "base/string16.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/encoding_menu_controller.h" #include "chrome/browser/profile.h" diff --git a/chrome/browser/cocoa/extension_install_prompt.mm b/chrome/browser/cocoa/extension_install_prompt.mm index cb33136..3306806 100644 --- a/chrome/browser/cocoa/extension_install_prompt.mm +++ b/chrome/browser/cocoa/extension_install_prompt.mm @@ -20,7 +20,10 @@ class Profile; void ExtensionInstallUI::ShowExtensionInstallUIPromptImpl( - Profile* profile, Delegate* delegate, Extension* extension, SkBitmap* icon, + Profile* profile, + Delegate* delegate, + const Extension* extension, + SkBitmap* icon, ExtensionInstallUI::PromptType type) { NSAlert* alert = [[[NSAlert alloc] init] autorelease]; diff --git a/chrome/browser/cocoa/extension_installed_bubble_bridge.h b/chrome/browser/cocoa/extension_installed_bubble_bridge.h index 999fd0a..d878d70 100644 --- a/chrome/browser/cocoa/extension_installed_bubble_bridge.h +++ b/chrome/browser/cocoa/extension_installed_bubble_bridge.h @@ -20,7 +20,7 @@ namespace ExtensionInstalledBubbleCocoa { // This function is called by the ExtensionInstallUI when an extension has been // installed. void ShowExtensionInstalledBubble(gfx::NativeWindow window, - Extension* extension, + const Extension* extension, Browser* browser, SkBitmap icon); } diff --git a/chrome/browser/cocoa/extension_installed_bubble_bridge.mm b/chrome/browser/cocoa/extension_installed_bubble_bridge.mm index e43b956..9c15e97 100644 --- a/chrome/browser/cocoa/extension_installed_bubble_bridge.mm +++ b/chrome/browser/cocoa/extension_installed_bubble_bridge.mm @@ -12,7 +12,7 @@ void ExtensionInstalledBubbleCocoa::ShowExtensionInstalledBubble( gfx::NativeWindow window, - Extension* extension, + const Extension* extension, Browser* browser, SkBitmap icon) { // The controller is deallocated when the window is closed, so no need to diff --git a/chrome/browser/cocoa/extension_installed_bubble_controller.h b/chrome/browser/cocoa/extension_installed_bubble_controller.h index de2998a..e5f50a3 100644 --- a/chrome/browser/cocoa/extension_installed_bubble_controller.h +++ b/chrome/browser/cocoa/extension_installed_bubble_controller.h @@ -47,7 +47,7 @@ typedef enum { NSWindowController<NSWindowDelegate> { @private NSWindow* parentWindow_; // weak - Extension* extension_; // weak + const Extension* extension_; // weak Browser* browser_; // weak scoped_nsobject<NSImage> icon_; @@ -72,13 +72,13 @@ typedef enum { IBOutlet NSTextField* extensionInstalledInfoMsg_; } -@property (nonatomic, readonly) Extension* extension; +@property (nonatomic, readonly) const Extension* extension; @property (nonatomic) BOOL pageActionRemoved; // Initialize the window, and then create observers to wait for the extension // to complete loading, or the browser window to close. - (id)initWithParentWindow:(NSWindow*)parentWindow - extension:(Extension*)extension + extension:(const Extension*)extension browser:(Browser*)browser icon:(SkBitmap)icon; @@ -89,6 +89,10 @@ typedef enum { // the extensionObserver when the extension has completed loading. - (void)showWindow:(id)sender; +// Clears our weak pointer to the Extension. This callback is triggered by +// the extensionObserver when the extension is unloaded. +- (void)extensionUnloaded:(id)sender; + @end @interface ExtensionInstalledBubbleController(ExposedForTesting) diff --git a/chrome/browser/cocoa/extension_installed_bubble_controller.mm b/chrome/browser/cocoa/extension_installed_bubble_controller.mm index 00cd535..b1263fd 100644 --- a/chrome/browser/cocoa/extension_installed_bubble_controller.mm +++ b/chrome/browser/cocoa/extension_installed_bubble_controller.mm @@ -31,10 +31,12 @@ class ExtensionLoadedNotificationObserver : public NotificationObserver { public: ExtensionLoadedNotificationObserver( - ExtensionInstalledBubbleController* controller) + ExtensionInstalledBubbleController* controller, Profile* profile) : controller_(controller) { registrar_.Add(this, NotificationType::EXTENSION_LOADED, - NotificationService::AllSources()); + Source<Profile>(profile)); + registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, + Source<Profile>(profile)); } private: @@ -44,12 +46,19 @@ class ExtensionLoadedNotificationObserver : public NotificationObserver { const NotificationSource& source, const NotificationDetails& details) { if (type == NotificationType::EXTENSION_LOADED) { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (extension == [controller_ extension]) { [controller_ performSelectorOnMainThread:@selector(showWindow:) withObject:controller_ waitUntilDone:NO]; } + } else if (type == NotificationType::EXTENSION_UNLOADED) { + const Extension* extension = Details<const Extension>(details).ptr(); + if (extension == [controller_ extension]) { + [controller_ performSelectorOnMainThread:@selector(extensionUnloaded:) + withObject:controller_ + waitUntilDone:NO]; + } } else { NOTREACHED() << "Received unexpected notification."; } @@ -65,7 +74,7 @@ class ExtensionLoadedNotificationObserver : public NotificationObserver { @synthesize pageActionRemoved = pageActionRemoved_; // Exposed for unit test. - (id)initWithParentWindow:(NSWindow*)parentWindow - extension:(Extension*)extension + extension:(const Extension*)extension browser:(Browser*)browser icon:(SkBitmap)icon { NSString* nibPath = @@ -91,7 +100,8 @@ class ExtensionLoadedNotificationObserver : public NotificationObserver { } // Start showing window only after extension has fully loaded. - extensionObserver_.reset(new ExtensionLoadedNotificationObserver(self)); + extensionObserver_.reset(new ExtensionLoadedNotificationObserver( + self, browser->profile())); } return self; } @@ -140,8 +150,7 @@ class ExtensionLoadedNotificationObserver : public NotificationObserver { // Extracted to a function here so that it can be overwritten for unit // testing. - (void)removePageActionPreviewIfNecessary { - DCHECK(extension_); - if (!extension_->page_action() || pageActionRemoved_) + if (!extension_ || !extension_->page_action() || pageActionRemoved_) return; pageActionRemoved_ = YES; @@ -329,4 +338,8 @@ class ExtensionLoadedNotificationObserver : public NotificationObserver { return [extensionInstalledInfoMsg_ frame]; } +- (void)extensionUnloaded:(id)sender { + extension_ = NULL; +} + @end diff --git a/chrome/browser/cocoa/extension_installed_bubble_controller_unittest.mm b/chrome/browser/cocoa/extension_installed_bubble_controller_unittest.mm index adcc426..ad68c85 100644 --- a/chrome/browser/cocoa/extension_installed_bubble_controller_unittest.mm +++ b/chrome/browser/cocoa/extension_installed_bubble_controller_unittest.mm @@ -75,7 +75,8 @@ class ExtensionInstalledBubbleControllerTest : public CocoaTest { // Create a skeletal framework of either page action or browser action // type. This extension only needs to have a type and a name to initialize // the ExtensionInstalledBubble for unit testing. - Extension* CreateExtension(extension_installed_bubble::ExtensionType type) { + scoped_refptr<Extension> CreateExtension( + extension_installed_bubble::ExtensionType type) { FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); path = path.AppendASCII("extensions").AppendASCII("dummy"); @@ -98,10 +99,9 @@ class ExtensionInstalledBubbleControllerTest : public CocoaTest { extension_input_value.Set(keys::kBrowserAction, browser_action); } - Extension* extension = new Extension(path); std::string error; - extension->InitFromValue(extension_input_value, false, &error); - return extension; + return Extension::Create( + path, Extension::INVALID, extension_input_value, false, &error); } // Allows us to create the window and browser for testing. @@ -114,7 +114,7 @@ class ExtensionInstalledBubbleControllerTest : public CocoaTest { Browser* browser_; // weak, owned by BrowserTestHelper. // Skeleton extension to be tested; reinitialized for each test. - scoped_ptr<Extension> extension_; + scoped_refptr<Extension> extension_; // The icon_ to be loaded into the bubble window. SkBitmap icon_; @@ -122,8 +122,7 @@ class ExtensionInstalledBubbleControllerTest : public CocoaTest { // Confirm that window sizes are set correctly for a page action extension. TEST_F(ExtensionInstalledBubbleControllerTest, PageActionTest) { - extension_.reset( - CreateExtension(extension_installed_bubble::kPageAction)); + extension_ = CreateExtension(extension_installed_bubble::kPageAction); ExtensionInstalledBubbleControllerForTest* controller = [[ExtensionInstalledBubbleControllerForTest alloc] initWithParentWindow:window_ @@ -166,8 +165,7 @@ TEST_F(ExtensionInstalledBubbleControllerTest, PageActionTest) { } TEST_F(ExtensionInstalledBubbleControllerTest, BrowserActionTest) { - extension_.reset( - CreateExtension(extension_installed_bubble::kBrowserAction)); + extension_ = CreateExtension(extension_installed_bubble::kBrowserAction); ExtensionInstalledBubbleControllerForTest* controller = [[ExtensionInstalledBubbleControllerForTest alloc] initWithParentWindow:window_ diff --git a/chrome/browser/cocoa/extensions/browser_action_button.h b/chrome/browser/cocoa/extensions/browser_action_button.h index 606b033..1205c80 100644 --- a/chrome/browser/cocoa/extensions/browser_action_button.h +++ b/chrome/browser/cocoa/extensions/browser_action_button.h @@ -42,7 +42,7 @@ extern NSString* const kBrowserActionButtonDragEndNotification; scoped_nsobject<NSViewAnimation> moveAnimation_; // The extension for this button. Weak. - Extension* extension_; + const Extension* extension_; // The ID of the active tab. int tabId_; @@ -57,7 +57,7 @@ extern NSString* const kBrowserActionButtonDragEndNotification; } - (id)initWithFrame:(NSRect)frame - extension:(Extension*)extension + extension:(const Extension*)extension profile:(Profile*)profile tabId:(int)tabId; @@ -76,7 +76,7 @@ extern NSString* const kBrowserActionButtonDragEndNotification; - (NSImage*)compositedImage; @property(readonly, nonatomic) BOOL isBeingDragged; -@property(readonly, nonatomic) Extension* extension; +@property(readonly, nonatomic) const Extension* extension; @property(readwrite, nonatomic) int tabId; @end diff --git a/chrome/browser/cocoa/extensions/browser_action_button.mm b/chrome/browser/cocoa/extensions/browser_action_button.mm index 70f15ee..0cdaee5 100644 --- a/chrome/browser/cocoa/extensions/browser_action_button.mm +++ b/chrome/browser/cocoa/extensions/browser_action_button.mm @@ -45,7 +45,8 @@ const CGFloat kShadowOffset = 2.0; class ExtensionImageTrackerBridge : public NotificationObserver, public ImageLoadingTracker::Observer { public: - ExtensionImageTrackerBridge(BrowserActionButton* owner, Extension* extension) + ExtensionImageTrackerBridge(BrowserActionButton* owner, + const Extension* extension) : owner_(owner), tracker_(this) { // The Browser Action API does not allow the default icon path to be @@ -113,7 +114,7 @@ class ExtensionImageTrackerBridge : public NotificationObserver, } - (id)initWithFrame:(NSRect)frame - extension:(Extension*)extension + extension:(const Extension*)extension profile:(Profile*)profile tabId:(int)tabId { if ((self = [super initWithFrame:frame])) { diff --git a/chrome/browser/cocoa/extensions/browser_actions_controller.h b/chrome/browser/cocoa/extensions/browser_actions_controller.h index 21b0b95..8f039ce 100644 --- a/chrome/browser/cocoa/extensions/browser_actions_controller.h +++ b/chrome/browser/cocoa/extensions/browser_actions_controller.h @@ -88,7 +88,7 @@ extern NSString* const kBrowserActionVisibilityChangedNotification; - (void)resizeContainerAndAnimate:(BOOL)animate; // Returns the NSView for the action button associated with an extension. -- (NSView*)browserActionViewForExtension:(Extension*)extension; +- (NSView*)browserActionViewForExtension:(const Extension*)extension; // Returns the saved width determined by the number of shown Browser Actions // preference property. If no preference is found, then the width for the @@ -98,7 +98,7 @@ extern NSString* const kBrowserActionVisibilityChangedNotification; // Returns where the popup arrow should point to for a given Browser Action. If // it is passed an extension that is not a Browser Action, then it will return // NSZeroPoint. -- (NSPoint)popupPointForBrowserAction:(Extension*)extension; +- (NSPoint)popupPointForBrowserAction:(const Extension*)extension; // Returns whether the chevron button is currently hidden or in the process of // being hidden (fading out). Will return NO if it is not hidden or is in the diff --git a/chrome/browser/cocoa/extensions/browser_actions_controller.mm b/chrome/browser/cocoa/extensions/browser_actions_controller.mm index e5a58ad..456ceba 100644 --- a/chrome/browser/cocoa/extensions/browser_actions_controller.mm +++ b/chrome/browser/cocoa/extensions/browser_actions_controller.mm @@ -70,13 +70,13 @@ const CGFloat kBrowserActionBubbleYOffset = 3.0; // Creates and then adds the given extension's action button to the container // at the given index within the container. It does not affect the toolbar model // object since it is called when the toolbar model changes. -- (void)createActionButtonForExtension:(Extension*)extension +- (void)createActionButtonForExtension:(const Extension*)extension withIndex:(NSUInteger)index; // Removes an action button for the given extension from the container. This // method also does not affect the underlying toolbar model since it is called // when the toolbar model changes. -- (void)removeActionButtonForExtension:(Extension*)extension; +- (void)removeActionButtonForExtension:(const Extension*)extension; // Useful in the case of a Browser Action being added/removed from the middle of // the container, this method repositions each button according to the current @@ -90,7 +90,7 @@ const CGFloat kBrowserActionBubbleYOffset = 3.0; // Returns the existing button with the given extension backing it; nil if it // cannot be found or the extension's ID is invalid. -- (BrowserActionButton*)buttonForExtension:(Extension*)extension; +- (BrowserActionButton*)buttonForExtension:(const Extension*)extension; // Returns the preferred width of the container given the number of visible // buttons |buttonCount|. @@ -141,7 +141,7 @@ const CGFloat kBrowserActionBubbleYOffset = 3.0; // Returns whether the given extension should be displayed. Only displays // incognito-enabled extensions in incognito mode. Otherwise returns YES. -- (BOOL)shouldDisplayBrowserAction:(Extension*)extension; +- (BOOL)shouldDisplayBrowserAction:(const Extension*)extension; // The reason |frame| is specified in these chevron functions is because the // container may be animating and the end frame of the animation should be @@ -203,12 +203,12 @@ class ExtensionsServiceObserverBridge : public NotificationObserver, } // ExtensionToolbarModel::Observer implementation. - void BrowserActionAdded(Extension* extension, int index) { + void BrowserActionAdded(const Extension* extension, int index) { [owner_ createActionButtonForExtension:extension withIndex:index]; [owner_ resizeContainerAndAnimate:NO]; } - void BrowserActionRemoved(Extension* extension) { + void BrowserActionRemoved(const Extension* extension) { [owner_ removeActionButtonForExtension:extension]; [owner_ resizeContainerAndAnimate:NO]; } @@ -342,7 +342,7 @@ class ExtensionsServiceObserverBridge : public NotificationObserver, } } -- (NSView*)browserActionViewForExtension:(Extension*)extension { +- (NSView*)browserActionViewForExtension:(const Extension*)extension { for (BrowserActionButton* button in [buttons_ allValues]) { if ([button extension] == extension) return button; @@ -374,7 +374,7 @@ class ExtensionsServiceObserverBridge : public NotificationObserver, return [self containerWidthWithButtonCount:savedButtonCount]; } -- (NSPoint)popupPointForBrowserAction:(Extension*)extension { +- (NSPoint)popupPointForBrowserAction:(const Extension*)extension { if (!extension->browser_action()) return NSZeroPoint; @@ -446,7 +446,7 @@ class ExtensionsServiceObserverBridge : public NotificationObserver, [containerView_ resizeToWidth:width animate:NO]; } -- (void)createActionButtonForExtension:(Extension*)extension +- (void)createActionButtonForExtension:(const Extension*)extension withIndex:(NSUInteger)index { if (!extension->browser_action()) return; @@ -491,7 +491,7 @@ class ExtensionsServiceObserverBridge : public NotificationObserver, [containerView_ setNeedsDisplay:YES]; } -- (void)removeActionButtonForExtension:(Extension*)extension { +- (void)removeActionButtonForExtension:(const Extension*)extension { if (!extension->browser_action()) return; @@ -555,7 +555,7 @@ class ExtensionsServiceObserverBridge : public NotificationObserver, } } -- (BrowserActionButton*)buttonForExtension:(Extension*)extension { +- (BrowserActionButton*)buttonForExtension:(const Extension*)extension { NSString* extensionId = base::SysUTF8ToNSString(extension->id()); DCHECK(extensionId); if (!extensionId) @@ -744,7 +744,7 @@ class ExtensionsServiceObserverBridge : public NotificationObserver, } } -- (BOOL)shouldDisplayBrowserAction:(Extension*)extension { +- (BOOL)shouldDisplayBrowserAction:(const Extension*)extension { // Only display incognito-enabled extensions while in incognito mode. return (!profile_->IsOffTheRecord() || profile_->GetExtensionsService()->IsIncognitoEnabled(extension)); @@ -854,7 +854,7 @@ class ExtensionsServiceObserverBridge : public NotificationObserver, if (profile_->IsOffTheRecord()) index = toolbarModel_->IncognitoIndexToOriginal(index); if (index < toolbarModel_->size()) { - Extension* extension = toolbarModel_->GetExtensionByIndex(index); + const Extension* extension = toolbarModel_->GetExtensionByIndex(index); return [buttons_ objectForKey:base::SysUTF8ToNSString(extension->id())]; } return nil; diff --git a/chrome/browser/cocoa/extensions/extension_action_context_menu.h b/chrome/browser/cocoa/extensions/extension_action_context_menu.h index 3c9e216..80e8398 100644 --- a/chrome/browser/cocoa/extensions/extension_action_context_menu.h +++ b/chrome/browser/cocoa/extensions/extension_action_context_menu.h @@ -28,7 +28,7 @@ class DevmodeObserver; @interface ExtensionActionContextMenu : NSMenu { @private // The extension that this menu belongs to. Weak. - Extension* extension_; + const Extension* extension_; // The extension action this menu belongs to. Weak. ExtensionAction* action_; @@ -48,7 +48,7 @@ class DevmodeObserver; } // Initializes and returns a context menu for the given extension and profile. -- (id)initWithExtension:(Extension*)extension +- (id)initWithExtension:(const Extension*)extension profile:(Profile*)profile extensionAction:(ExtensionAction*)action; diff --git a/chrome/browser/cocoa/extensions/extension_action_context_menu.mm b/chrome/browser/cocoa/extensions/extension_action_context_menu.mm index 81aa61e..36ff520 100644 --- a/chrome/browser/cocoa/extensions/extension_action_context_menu.mm +++ b/chrome/browser/cocoa/extensions/extension_action_context_menu.mm @@ -36,7 +36,7 @@ // Also acts as the extension's UI delegate in order to display the dialog. class AsyncUninstaller : public ExtensionInstallUI::Delegate { public: - AsyncUninstaller(Extension* extension, Profile* profile) + AsyncUninstaller(const Extension* extension, Profile* profile) : extension_(extension), profile_(profile) { install_ui_.reset(new ExtensionInstallUI(profile)); @@ -55,7 +55,7 @@ class AsyncUninstaller : public ExtensionInstallUI::Delegate { private: // The extension that we're loading the icon for. Weak. - Extension* extension_; + const Extension* extension_; // The current profile. Weak. Profile* profile_; @@ -125,7 +125,7 @@ int CurrentTabId() { } // namespace -- (id)initWithExtension:(Extension*)extension +- (id)initWithExtension:(const Extension*)extension profile:(Profile*)profile extensionAction:(ExtensionAction*)action{ if ((self = [super initWithTitle:@""])) { diff --git a/chrome/browser/cocoa/extensions/extension_infobar_controller.mm b/chrome/browser/cocoa/extensions/extension_infobar_controller.mm index 1bb1a5f..a2a38f2 100644 --- a/chrome/browser/cocoa/extensions/extension_infobar_controller.mm +++ b/chrome/browser/cocoa/extensions/extension_infobar_controller.mm @@ -65,7 +65,7 @@ class InfobarBridge : public ExtensionInfoBarDelegate::DelegateObserver, // Load the Extension's icon image. void LoadIcon() { - Extension* extension = delegate_->extension_host()->extension(); + const Extension* extension = delegate_->extension_host()->extension(); ExtensionResource icon_resource = extension->GetIconResource( Extension::EXTENSION_ICON_BITTY, ExtensionIconSet::MATCH_EXACTLY); if (!icon_resource.relative_path().empty()) { diff --git a/chrome/browser/cocoa/extensions/extension_install_prompt_controller.h b/chrome/browser/cocoa/extensions/extension_install_prompt_controller.h index 0fb4d4e..8a6a4ce 100644 --- a/chrome/browser/cocoa/extensions/extension_install_prompt_controller.h +++ b/chrome/browser/cocoa/extensions/extension_install_prompt_controller.h @@ -49,7 +49,7 @@ class Profile; - (id)initWithParentWindow:(NSWindow*)window profile:(Profile*)profile - extension:(Extension*)extension + extension:(const Extension*)extension delegate:(ExtensionInstallUI::Delegate*)delegate icon:(SkBitmap*)bitmap warnings:(const std::vector<string16>&)warnings; diff --git a/chrome/browser/cocoa/extensions/extension_install_prompt_controller.mm b/chrome/browser/cocoa/extensions/extension_install_prompt_controller.mm index 9ba758b..10f4f81 100644 --- a/chrome/browser/cocoa/extensions/extension_install_prompt_controller.mm +++ b/chrome/browser/cocoa/extensions/extension_install_prompt_controller.mm @@ -58,7 +58,7 @@ void OffsetControlVertically(NSControl* control, CGFloat amount) { - (id)initWithParentWindow:(NSWindow*)window profile:(Profile*)profile - extension:(Extension*)extension + extension:(const Extension*)extension delegate:(ExtensionInstallUI::Delegate*)delegate icon:(SkBitmap*)icon warnings:(const std::vector<string16>&)warnings { @@ -185,7 +185,10 @@ void OffsetControlVertically(NSControl* control, CGFloat amount) { void ExtensionInstallUI::ShowExtensionInstallUIPrompt2Impl( - Profile* profile, Delegate* delegate, Extension* extension, SkBitmap* icon, + Profile* profile, + Delegate* delegate, + const Extension* extension, + SkBitmap* icon, const std::vector<string16>& warnings) { Browser* browser = BrowserList::GetLastActiveWithProfile(profile); if (!browser) { diff --git a/chrome/browser/cocoa/extensions/extension_install_prompt_controller_unittest.mm b/chrome/browser/cocoa/extensions/extension_install_prompt_controller_unittest.mm index f596851..12eadff 100644 --- a/chrome/browser/cocoa/extensions/extension_install_prompt_controller_unittest.mm +++ b/chrome/browser/cocoa/extensions/extension_install_prompt_controller_unittest.mm @@ -60,19 +60,18 @@ public: return; } - scoped_ptr<Extension> extension(new Extension(path.DirName())); - if (!extension->InitFromValue(*value, false, &error)) { + extension_ = Extension::Create( + path.DirName(), Extension::INVALID, *value, false, &error); + if (!extension_.get()) { LOG(ERROR) << error; return; } - - extension_.reset(extension.release()); } BrowserTestHelper helper_; FilePath test_data_dir_; SkBitmap icon_; - scoped_ptr<Extension> extension_; + scoped_refptr<Extension> extension_; }; diff --git a/chrome/browser/cocoa/first_run_bubble_controller_unittest.mm b/chrome/browser/cocoa/first_run_bubble_controller_unittest.mm index a6009a6..094b1f6 100644 --- a/chrome/browser/cocoa/first_run_bubble_controller_unittest.mm +++ b/chrome/browser/cocoa/first_run_bubble_controller_unittest.mm @@ -6,6 +6,7 @@ #import <Cocoa/Cocoa.h> +#include "base/debug/debugger.h" #include "base/scoped_nsobject.h" #include "chrome/browser/cocoa/browser_test_helper.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" @@ -26,7 +27,7 @@ TEST_F(FirstRunBubbleControllerTest, Init) { backing:NSBackingStoreBuffered defer:NO]); [parent setReleasedWhenClosed:NO]; - if (DebugUtil::BeingDebugged()) + if (base::debug::BeingDebugged()) [parent.get() orderFront:nil]; else [parent.get() orderBack:nil]; diff --git a/chrome/browser/cocoa/first_run_dialog.mm b/chrome/browser/cocoa/first_run_dialog.mm index 7f72bb4..5b97bde 100644 --- a/chrome/browser/cocoa/first_run_dialog.mm +++ b/chrome/browser/cocoa/first_run_dialog.mm @@ -77,7 +77,7 @@ void FirstRunShowBridge::ShowDialog() { // -[NSApplication runModalForWindow:] we will hang <http://crbug.com/54248>. // Therefore the main MessageLoop is run so things work. - scoped_refptr<FirstRunShowBridge> bridge = new FirstRunShowBridge(self); + scoped_refptr<FirstRunShowBridge> bridge(new FirstRunShowBridge(self)); MessageLoop::current()->PostTask( FROM_HERE, NewRunnableMethod(bridge.get(), diff --git a/chrome/browser/cocoa/framed_browser_window_unittest.mm b/chrome/browser/cocoa/framed_browser_window_unittest.mm index ae9c329..5638561 100644 --- a/chrome/browser/cocoa/framed_browser_window_unittest.mm +++ b/chrome/browser/cocoa/framed_browser_window_unittest.mm @@ -4,8 +4,9 @@ #import <Cocoa/Cocoa.h> +#include "base/debug/debugger.h" #include "base/scoped_nsobject.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #import "chrome/browser/cocoa/browser_window_controller.h" #import "chrome/browser/cocoa/browser_frame_view.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" @@ -27,7 +28,7 @@ class FramedBrowserWindowTest : public CocoaTest { styleMask:mask backing:NSBackingStoreBuffered defer:NO]; - if (DebugUtil::BeingDebugged()) { + if (base::debug::BeingDebugged()) { [window_ orderFront:nil]; } else { [window_ orderBack:nil]; diff --git a/chrome/browser/cocoa/fullscreen_window.mm b/chrome/browser/cocoa/fullscreen_window.mm index d0fb304..47582b2 100644 --- a/chrome/browser/cocoa/fullscreen_window.mm +++ b/chrome/browser/cocoa/fullscreen_window.mm @@ -26,12 +26,27 @@ defer:YES screen:screen])) { [self setReleasedWhenClosed:NO]; + // Borderless windows don't usually show up in the Windows menu so whine at + // Cocoa until it complies. See -dealloc and -setTitle: as well. + [NSApp addWindowsItem:self title:@"" filename:NO]; } return self; } +- (void)dealloc { + // Paranoia; doesn't seem to be necessary but it doesn't hurt. + [NSApp removeWindowsItem:self]; + + [super dealloc]; +} + +- (void)setTitle:(NSString *)title { + [NSApp changeWindowsItem:self title:title filename:NO]; + [super setTitle:title]; +} + // According to -// http://www.cocoabuilder.com/archive/message/cocoa/2006/6/19/165953, +// http://www.cocoabuilder.com/archive/message/cocoa/2006/6/19/165953 , // NSBorderlessWindowMask windows cannot become key or main. // In our case, however, we don't want that behavior, so we override // canBecomeKeyWindow and canBecomeMainWindow. diff --git a/chrome/browser/cocoa/gradient_button_cell.mm b/chrome/browser/cocoa/gradient_button_cell.mm index f6ac577..8b99fa5 100644 --- a/chrome/browser/cocoa/gradient_button_cell.mm +++ b/chrome/browser/cocoa/gradient_button_cell.mm @@ -448,7 +448,7 @@ static const NSTimeInterval kAnimationContinuousCycleDuration = 0.4; active ? BrowserThemeProvider::COLOR_TOOLBAR_BUTTON_STROKE : BrowserThemeProvider::COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE, true) : [NSColor colorWithCalibratedWhite:0.0 - alpha:0.6 * outerStrokeAlphaMult_]; + alpha:0.3 * outerStrokeAlphaMult_]; } [strokeColor setStroke]; diff --git a/chrome/browser/cocoa/history_menu_bridge.mm b/chrome/browser/cocoa/history_menu_bridge.mm index 844ca38..0b975ec 100644 --- a/chrome/browser/cocoa/history_menu_bridge.mm +++ b/chrome/browser/cocoa/history_menu_bridge.mm @@ -11,7 +11,7 @@ #include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" // IDC_HISTORY_MENU +#include "chrome/app/chrome_command_ids.h" // IDC_HISTORY_MENU #import "chrome/browser/app_controller_mac.h" #import "chrome/browser/cocoa/history_menu_cocoa_controller.h" #include "chrome/browser/history/page_usage_data.h" diff --git a/chrome/browser/cocoa/history_menu_bridge_unittest.mm b/chrome/browser/cocoa/history_menu_bridge_unittest.mm index a3fd2b6..399f510 100644 --- a/chrome/browser/cocoa/history_menu_bridge_unittest.mm +++ b/chrome/browser/cocoa/history_menu_bridge_unittest.mm @@ -9,7 +9,7 @@ #include "base/string_util.h" #include "base/sys_string_conversions.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/cancelable_request.h" #include "chrome/browser/cocoa/browser_test_helper.h" diff --git a/chrome/browser/cocoa/history_menu_cocoa_controller.mm b/chrome/browser/cocoa/history_menu_cocoa_controller.mm index 1b6241e..cf6cb18 100644 --- a/chrome/browser/cocoa/history_menu_cocoa_controller.mm +++ b/chrome/browser/cocoa/history_menu_cocoa_controller.mm @@ -5,7 +5,7 @@ #import "chrome/browser/cocoa/history_menu_cocoa_controller.h" #include "base/scoped_vector.h" -#include "chrome/app/chrome_dll_resource.h" // IDC_HISTORY_MENU +#include "chrome/app/chrome_command_ids.h" // IDC_HISTORY_MENU #import "chrome/browser/app_controller_mac.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_process.h" diff --git a/chrome/browser/cocoa/history_menu_cocoa_controller_unittest.mm b/chrome/browser/cocoa/history_menu_cocoa_controller_unittest.mm index a57e12b..605e2ba 100644 --- a/chrome/browser/cocoa/history_menu_cocoa_controller_unittest.mm +++ b/chrome/browser/cocoa/history_menu_cocoa_controller_unittest.mm @@ -6,7 +6,7 @@ #include "base/scoped_ptr.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/cocoa/browser_test_helper.h" #include "chrome/browser/cocoa/cocoa_test_helper.h" diff --git a/chrome/browser/cocoa/html_dialog_window_controller.mm b/chrome/browser/cocoa/html_dialog_window_controller.mm index b959798..aed0d83 100644 --- a/chrome/browser/cocoa/html_dialog_window_controller.mm +++ b/chrome/browser/cocoa/html_dialog_window_controller.mm @@ -44,6 +44,7 @@ public: virtual std::string GetDialogArgs() const; virtual void OnDialogClosed(const std::string& json_retval); virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { } + virtual bool ShouldShowDialogTitle() const { return true; } // HtmlDialogTabContentsDelegate declarations. virtual void MoveContents(TabContents* source, const gfx::Rect& pos); diff --git a/chrome/browser/cocoa/html_dialog_window_controller_unittest.mm b/chrome/browser/cocoa/html_dialog_window_controller_unittest.mm index b6eae68..c8cdfb7 100644 --- a/chrome/browser/cocoa/html_dialog_window_controller_unittest.mm +++ b/chrome/browser/cocoa/html_dialog_window_controller_unittest.mm @@ -35,6 +35,7 @@ public: MOCK_METHOD1(OnDialogClosed, void(const std::string& json_retval)); MOCK_METHOD2(OnCloseContents, void(TabContents* source, bool* out_close_dialog)); + MOCK_CONST_METHOD0(ShouldShowDialogTitle, bool()); }; class HtmlDialogWindowControllerTest : public BrowserWithTestWindowTest { diff --git a/chrome/browser/cocoa/import_progress_dialog.mm b/chrome/browser/cocoa/import_progress_dialog.mm index 109365b..f40ae5f 100644 --- a/chrome/browser/cocoa/import_progress_dialog.mm +++ b/chrome/browser/cocoa/import_progress_dialog.mm @@ -154,7 +154,7 @@ NSString* keyForImportItem(importer::ImportItem item) { @end void StartImportingWithUI(gfx::NativeWindow parent_window, - int16 items, + uint16 items, ImporterHost* coordinator, const importer::ProfileInfo& source_profile, Profile* target_profile, diff --git a/chrome/browser/cocoa/import_settings_dialog.mm b/chrome/browser/cocoa/import_settings_dialog.mm index a11244a..d18dec8 100644 --- a/chrome/browser/cocoa/import_settings_dialog.mm +++ b/chrome/browser/cocoa/import_settings_dialog.mm @@ -174,9 +174,7 @@ bool importSettingsDialogVisible = false; const importer::ProfileInfo& sourceProfile = importerList_.get()->GetSourceProfileInfoAt([self sourceBrowserIndex]); uint16 items = sourceProfile.services_supported; - // ProfileInfo.services_supported is a uint16 while the call to - // StartImportingWithUI requires an int16. - int16 servicesToImport = static_cast<int16>(items & [self servicesToImport]); + uint16 servicesToImport = items & [self servicesToImport]; if (servicesToImport) { if (profile_) { ImporterHost* importerHost = new ExternalProcessImporterHost; diff --git a/chrome/browser/cocoa/keystone_glue.mm b/chrome/browser/cocoa/keystone_glue.mm index a0162a3..4ba9b21 100644 --- a/chrome/browser/cocoa/keystone_glue.mm +++ b/chrome/browser/cocoa/keystone_glue.mm @@ -13,8 +13,12 @@ #import "app/l10n_util_mac.h" #include "base/logging.h" #include "base/mac_util.h" +#include "base/mac/scoped_nsautorelease_pool.h" #include "base/sys_string_conversions.h" #import "base/worker_pool_mac.h" +#include "base/ref_counted.h" +#include "base/task.h" +#include "base/worker_pool.h" #include "chrome/browser/cocoa/authorization_util.h" #include "chrome/common/chrome_constants.h" #include "grit/chromium_strings.h" @@ -89,6 +93,51 @@ NSString* SystemBrandFilePath() { return [kBrandSystemFile stringByStandardizingPath]; } +// Adaptor for scheduling an Objective-C method call on a |WorkerPool| +// thread. +// TODO(shess): Move this into workerpool_mac.h? +class PerformBridge : public base::RefCountedThreadSafe<PerformBridge> { + public: + + // Call |sel| on |target| with |arg| in a WorkerPool thread. + // |target| and |arg| are retained, |arg| may be |nil|. + static void PostPerform(id target, SEL sel, id arg) { + DCHECK(target); + DCHECK(sel); + + scoped_refptr<PerformBridge> op = new PerformBridge(target, sel, arg); + WorkerPool::PostTask( + FROM_HERE, NewRunnableMethod(op.get(), &PerformBridge::Run), true); + } + + // Convenience for the no-argument case. + static void PostPerform(id target, SEL sel) { + PostPerform(target, sel, nil); + } + + private: + // Allow RefCountedThreadSafe<> to delete. + friend class base::RefCountedThreadSafe<PerformBridge>; + + PerformBridge(id target, SEL sel, id arg) + : target_([target retain]), + sel_(sel), + arg_([arg retain]) { + } + + ~PerformBridge() {} + + // Happens on a WorkerPool thread. + void Run() { + base::mac::ScopedNSAutoreleasePool pool; + [target_ performSelector:sel_ withObject:arg_]; + } + + scoped_nsobject<id> target_; + SEL sel_; + scoped_nsobject<id> arg_; +}; + } // namespace @interface KSRegistration : NSObject @@ -147,7 +196,7 @@ NSString* SystemBrandFilePath() { // -determineUpdateStatusAsync is called on the main thread to initiate the // operation. It performs initial set-up work that must be done on the main // thread and arranges for -determineUpdateStatus to be called on a work queue -// thread managed by NSOperationQueue. +// thread managed by WorkerPool. // -determineUpdateStatus then reads the Info.plist, gets the version from the // CFBundleShortVersionString key, and performs // -determineUpdateStatusForVersion: on the main thread. @@ -562,17 +611,10 @@ NSString* const kBrandKey = @"KSBrandID"; - (void)determineUpdateStatusAsync { DCHECK([NSThread isMainThread]); - SEL selector = @selector(determineUpdateStatus); - NSInvocationOperation* operation = - [[[NSInvocationOperation alloc] initWithTarget:self - selector:selector - object:nil] autorelease]; - - NSOperationQueue* operationQueue = [WorkerPoolObjC sharedOperationQueue]; - [operationQueue addOperation:operation]; + PerformBridge::PostPerform(self, @selector(determineUpdateStatus)); } -// Runs on a thread managed by NSOperationQueue. +// Runs on a thread managed by WorkerPool. - (void)determineUpdateStatus { DCHECK(![NSThread isMainThread]); @@ -849,7 +891,7 @@ NSString* const kBrandKey = @"KSBrandID"; - (void)changePermissionsForPromotionAsync { // NSBundle is not documented as being thread-safe. Do NSBundle operations - // on the main thread before jumping over to a NSOperationQueue-managed + // on the main thread before jumping over to a WorkerPool-managed // thread to run the tool. DCHECK([NSThread isMainThread]); @@ -858,13 +900,7 @@ NSString* const kBrandKey = @"KSBrandID"; [mac_util::MainAppBundle() pathForResource:@"keystone_promote_postflight" ofType:@"sh"]; - NSInvocationOperation* operation = - [[[NSInvocationOperation alloc] initWithTarget:self - selector:selector - object:toolPath] autorelease]; - - NSOperationQueue* operationQueue = [WorkerPoolObjC sharedOperationQueue]; - [operationQueue addOperation:operation]; + PerformBridge::PostPerform(self, selector, toolPath); } - (void)changePermissionsForPromotionWithTool:(NSString*)toolPath { diff --git a/chrome/browser/cocoa/location_bar/autocomplete_text_field_editor.mm b/chrome/browser/cocoa/location_bar/autocomplete_text_field_editor.mm index 4904e50..aa5d391 100644 --- a/chrome/browser/cocoa/location_bar/autocomplete_text_field_editor.mm +++ b/chrome/browser/cocoa/location_bar/autocomplete_text_field_editor.mm @@ -8,7 +8,7 @@ #include "base/string_util.h" #include "grit/generated_resources.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" // IDC_* +#include "chrome/app/chrome_command_ids.h" // IDC_* #include "chrome/browser/browser_list.h" #import "chrome/browser/cocoa/browser_window_controller.h" #import "chrome/browser/cocoa/location_bar/autocomplete_text_field.h" diff --git a/chrome/browser/cocoa/location_bar/autocomplete_text_field_editor_unittest.mm b/chrome/browser/cocoa/location_bar/autocomplete_text_field_editor_unittest.mm index fc21da0..21ad21a 100644 --- a/chrome/browser/cocoa/location_bar/autocomplete_text_field_editor_unittest.mm +++ b/chrome/browser/cocoa/location_bar/autocomplete_text_field_editor_unittest.mm @@ -7,7 +7,7 @@ #include "base/scoped_nsobject.h" #include "base/scoped_ptr.h" #include "base/string_util.h" -#include "chrome/app/chrome_dll_resource.h" // IDC_* +#include "chrome/app/chrome_command_ids.h" // IDC_* #import "chrome/browser/cocoa/cocoa_test_helper.h" #import "chrome/browser/cocoa/location_bar/autocomplete_text_field_unittest_helper.h" #import "chrome/browser/cocoa/test_event_utils.h" diff --git a/chrome/browser/cocoa/location_bar/ev_bubble_decoration.h b/chrome/browser/cocoa/location_bar/ev_bubble_decoration.h index ae06586..bbb29fc 100644 --- a/chrome/browser/cocoa/location_bar/ev_bubble_decoration.h +++ b/chrome/browser/cocoa/location_bar/ev_bubble_decoration.h @@ -29,6 +29,10 @@ class EVBubbleDecoration : public BubbleDecoration { // fits, else it will set an elided version. void SetFullLabel(NSString* full_label); + // Get the point where the page info bubble should point within the + // decoration's frame, in the cell's coordinates. + NSPoint GetBubblePointInFrame(NSRect frame); + // Implement |LocationBarDecoration|. virtual CGFloat GetWidthForSpace(CGFloat width); virtual bool IsDraggable(); diff --git a/chrome/browser/cocoa/location_bar/ev_bubble_decoration.mm b/chrome/browser/cocoa/location_bar/ev_bubble_decoration.mm index 1f54500..40220e4 100644 --- a/chrome/browser/cocoa/location_bar/ev_bubble_decoration.mm +++ b/chrome/browser/cocoa/location_bar/ev_bubble_decoration.mm @@ -28,6 +28,10 @@ const CGFloat kMinElidedBubbleWidth = 150.0; // |kMinElidedBubbleWidth|. const float kMaxBubbleFraction = 0.5; +// The info-bubble point should look like it points to the bottom of the lock +// icon. Determined with Pixie.app. +const CGFloat kPageInfoBubblePointYOffset = 6.0; + // TODO(shess): This is ugly, find a better way. Using it right now // so that I can crib from gtk and still be able to see that I'm using // the same values easily. @@ -61,6 +65,12 @@ void EVBubbleDecoration::SetFullLabel(NSString* label) { SetLabel(full_label_); } +NSPoint EVBubbleDecoration::GetBubblePointInFrame(NSRect frame) { + NSRect image_rect = GetImageRectInFrame(frame); + return NSMakePoint(NSMidX(image_rect), + NSMaxY(image_rect) - kPageInfoBubblePointYOffset); +} + CGFloat EVBubbleDecoration::GetWidthForSpace(CGFloat width) { // Limit with to not take up too much of the available width, but // also don't let it shrink too much. diff --git a/chrome/browser/cocoa/location_bar/location_bar_view_mac.h b/chrome/browser/cocoa/location_bar/location_bar_view_mac.h index 583925d..687cb1a 100644 --- a/chrome/browser/cocoa/location_bar/location_bar_view_mac.h +++ b/chrome/browser/cocoa/location_bar/location_bar_view_mac.h @@ -94,6 +94,9 @@ class LocationBarViewMac : public AutocompleteEditController, // Get the point in the security icon at which the page info bubble aims. NSPoint GetPageInfoBubblePoint() const; + // Get the point in the omnibox at which the first run bubble aims. + NSPoint GetFirstRunBubblePoint() const; + // Updates the location bar. Resets the bar's permanent text and // security style, and if |should_restore_state| is true, restores // saved state from the tab (for tab switching). @@ -130,6 +133,7 @@ class LocationBarViewMac : public AutocompleteEditController, virtual void OnAutocompleteLosingFocus(gfx::NativeView unused); virtual void OnAutocompleteWillAccept(); virtual bool OnCommitSuggestedText(const std::wstring& typed_text); + virtual void OnSetSuggestedSearchText(const string16& suggested_text); virtual void OnPopupBoundsChanged(const gfx::Rect& bounds); virtual void OnAutocompleteAccept(const GURL& url, WindowOpenDisposition disposition, diff --git a/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm b/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm index e20a50f..f469b80 100644 --- a/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm +++ b/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm @@ -11,7 +11,7 @@ #include "base/string_util.h" #include "base/sys_string_conversions.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/alternate_nav_url_fetcher.h" #import "chrome/browser/app_controller_mac.h" #import "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" @@ -56,6 +56,13 @@ #include "skia/ext/skia_utils_mac.h" #include "third_party/skia/include/core/SkBitmap.h" +namespace { + +// Vertical space between the bottom edge of the location_bar and the first run +// bubble arrow point. +const static int kFirstRunBubbleYOffset = 1; + +} // TODO(shess): This code is mostly copied from the gtk // implementation. Make sure it's all appropriate and flesh it out. @@ -117,9 +124,15 @@ void LocationBarViewMac::ShowFirstRunBubbleInternal( if (!field_ || ![field_ window]) return; - // The bubble needs to be just below the Omnibox and slightly to the right - // of the left omnibox icon, so shift x and y co-ordinates. - const NSPoint kOffset = NSMakePoint(1, 4); + // The first run bubble's left edge should line up with the left edge of the + // omnibox. This is different from other bubbles, which line up at a point + // set by their top arrow. Because the BaseBubbleController adjusts the + // window origin left to account for the arrow spacing, the first run bubble + // moves the window origin right by this spacing, so that the + // BaseBubbleController will move it back to the correct position. + const NSPoint kOffset = NSMakePoint( + info_bubble::kBubbleArrowXOffset + info_bubble::kBubbleArrowWidth/2.0, + kFirstRunBubbleYOffset); [FirstRunBubbleController showForView:field_ offset:kOffset profile:profile_]; } @@ -238,6 +251,11 @@ bool LocationBarViewMac::OnCommitSuggestedText(const std::wstring& typed_text) { return false; } +void LocationBarViewMac::OnSetSuggestedSearchText( + const string16& suggested_text) { + SetSuggestedText(suggested_text); +} + void LocationBarViewMac::OnPopupBoundsChanged(const gfx::Rect& bounds) { InstantController* instant = browser_->instant(); if (instant) @@ -474,10 +492,19 @@ NSPoint LocationBarViewMac::GetBookmarkBubblePoint() const { NSPoint LocationBarViewMac::GetPageInfoBubblePoint() const { AutocompleteTextFieldCell* cell = [field_ cell]; - const NSRect frame = [cell frameForDecoration:location_icon_decoration_.get() - inFrame:[field_ bounds]]; - const NSPoint point = location_icon_decoration_->GetBubblePointInFrame(frame); - return [field_ convertPoint:point toView:nil]; + if (ev_bubble_decoration_->IsVisible()) { + const NSRect frame = [cell frameForDecoration:ev_bubble_decoration_.get() + inFrame:[field_ bounds]]; + const NSPoint point = ev_bubble_decoration_->GetBubblePointInFrame(frame); + return [field_ convertPoint:point toView:nil]; + } else { + const NSRect frame = + [cell frameForDecoration:location_icon_decoration_.get() + inFrame:[field_ bounds]]; + const NSPoint point = + location_icon_decoration_->GetBubblePointInFrame(frame); + return [field_ convertPoint:point toView:nil]; + } } NSImage* LocationBarViewMac::GetKeywordImage(const std::wstring& keyword) { diff --git a/chrome/browser/cocoa/location_bar/location_icon_decoration.mm b/chrome/browser/cocoa/location_bar/location_icon_decoration.mm index f967af2..9649789 100644 --- a/chrome/browser/cocoa/location_bar/location_icon_decoration.mm +++ b/chrome/browser/cocoa/location_bar/location_icon_decoration.mm @@ -11,10 +11,8 @@ #include "chrome/browser/tab_contents/tab_contents.h" #import "third_party/mozilla/NSPasteboard+Utils.h" -// The info-bubble point should look like it points to the point -// between the star's lower tips. The popup should be where the -// Omnibox popup ends up (2px below field). Determined via Pixie.app -// magnification. +// The info-bubble point should look like it points to the bottom of the lock +// icon. Determined with Pixie.app. const CGFloat kBubblePointYOffset = 2.0; LocationIconDecoration::LocationIconDecoration(LocationBarViewMac* owner) diff --git a/chrome/browser/cocoa/location_bar/page_action_decoration.mm b/chrome/browser/cocoa/location_bar/page_action_decoration.mm index 572a65a..79636b2 100644 --- a/chrome/browser/cocoa/location_bar/page_action_decoration.mm +++ b/chrome/browser/cocoa/location_bar/page_action_decoration.mm @@ -39,8 +39,8 @@ PageActionDecoration::PageActionDecoration( current_tab_id_(-1), preview_enabled_(false) { DCHECK(profile); - Extension* extension = profile->GetExtensionsService()->GetExtensionById( - page_action->extension_id(), false); + const Extension* extension = profile->GetExtensionsService()-> + GetExtensionById(page_action->extension_id(), false); DCHECK(extension); // Load all the icons declared in the manifest. This is the contents of the @@ -219,7 +219,7 @@ NSMenu* PageActionDecoration::GetMenu() { ExtensionsService* service = profile_->GetExtensionsService(); if (!service) return nil; - Extension* extension = service->GetExtensionById( + const Extension* extension = service->GetExtensionById( page_action_->extension_id(), false); DCHECK(extension); if (!extension) diff --git a/chrome/browser/cocoa/location_bar/star_decoration.mm b/chrome/browser/cocoa/location_bar/star_decoration.mm index adedd16..0d91929 100644 --- a/chrome/browser/cocoa/location_bar/star_decoration.mm +++ b/chrome/browser/cocoa/location_bar/star_decoration.mm @@ -5,7 +5,7 @@ #import "chrome/browser/cocoa/location_bar/star_decoration.h" #include "app/l10n_util_mac.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #import "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" #include "chrome/browser/command_updater.h" #include "grit/generated_resources.h" diff --git a/chrome/browser/cocoa/notifications/balloon_controller.h b/chrome/browser/cocoa/notifications/balloon_controller.h index 8199265..03f8e4f 100644 --- a/chrome/browser/cocoa/notifications/balloon_controller.h +++ b/chrome/browser/cocoa/notifications/balloon_controller.h @@ -8,13 +8,15 @@ #import <Cocoa/Cocoa.h> -#include "base/scoped_nsobject.h" #include "base/cocoa_protocols_mac.h" -#import "chrome/browser/cocoa/hover_image_button.h" -#import "chrome/browser/cocoa/notifications/balloon_view.h" -#import "chrome/browser/cocoa/notifications/balloon_view_host_mac.h" -#include "chrome/browser/notifications/balloon.h" +#include "base/scoped_nsobject.h" +#include "base/scoped_ptr.h" +class Balloon; +@class BalloonContentViewCocoa; +@class BalloonShelfViewCocoa; +class BalloonViewHost; +@class HoverImageButton; @class MenuController; class NotificationOptionsMenuModel; @@ -50,6 +52,9 @@ class NotificationOptionsMenuModel; // The host for the renderer of the HTML contents. scoped_ptr<BalloonViewHost> htmlContents_; + + // The psn of the front application process. + ProcessSerialNumber frontProcessNum_; } // Initialize with a balloon object containing the notification data. @@ -81,6 +86,9 @@ class NotificationOptionsMenuModel; // The BalloonHost - (BalloonViewHost*)getHost; + +// Handle the event if it is for the balloon. +- (BOOL)handleEvent:(NSEvent*)event; @end @interface BalloonController (UnitTesting) diff --git a/chrome/browser/cocoa/notifications/balloon_controller.mm b/chrome/browser/cocoa/notifications/balloon_controller.mm index 55ee041..3f8a8b0 100644 --- a/chrome/browser/cocoa/notifications/balloon_controller.mm +++ b/chrome/browser/cocoa/notifications/balloon_controller.mm @@ -11,7 +11,9 @@ #include "base/nsimage_cache_mac.h" #import "base/scoped_nsobject.h" #include "base/utf_string_conversions.h" +#import "chrome/browser/cocoa/hover_image_button.h" #import "chrome/browser/cocoa/menu_controller.h" +#import "chrome/browser/cocoa/notifications/balloon_view.h" #include "chrome/browser/cocoa/notifications/balloon_view_host_mac.h" #include "chrome/browser/notifications/balloon.h" #include "chrome/browser/notifications/desktop_notification_service.h" @@ -110,12 +112,45 @@ const int kRightMargin = 2; assumeInside:NO]; } +- (BOOL)handleEvent:(NSEvent*)event { + BOOL eventHandled = NO; + if ([event type] == NSLeftMouseDown) { + NSPoint mouse = [shelf_ convertPoint:[event locationInWindow] + fromView:nil]; + if (NSPointInRect(mouse, [closeButton_ frame])) { + [closeButton_ mouseDown:event]; + + // Bring back the front process that is deactivated when we click the + // close button. + if (frontProcessNum_.highLongOfPSN || frontProcessNum_.lowLongOfPSN) { + SetFrontProcessWithOptions(&frontProcessNum_, + kSetFrontProcessFrontWindowOnly); + frontProcessNum_.highLongOfPSN = 0; + frontProcessNum_.lowLongOfPSN = 0; + } + + eventHandled = YES; + } else if (NSPointInRect(mouse, [optionsButton_ frame])) { + [optionsButton_ mouseDown:event]; + eventHandled = YES; + } + } + return eventHandled; +} + - (void) mouseEntered:(NSEvent*)event { [[closeButton_ cell] setHighlighted:YES]; + + // Remember the current front process so that we can bring it back later. + if (!frontProcessNum_.highLongOfPSN && !frontProcessNum_.lowLongOfPSN) + GetFrontProcess(&frontProcessNum_); } - (void) mouseExited:(NSEvent*)event { [[closeButton_ cell] setHighlighted:NO]; + + frontProcessNum_.highLongOfPSN = 0; + frontProcessNum_.lowLongOfPSN = 0; } - (IBAction)optionsButtonPressed:(id)sender { @@ -143,7 +178,8 @@ const int kRightMargin = 2; } - (void)closeBalloon:(bool)byUser { - DCHECK(balloon_); + if (!balloon_) + return; [self close]; if (htmlContents_.get()) htmlContents_->Shutdown(); diff --git a/chrome/browser/cocoa/notifications/balloon_view.h b/chrome/browser/cocoa/notifications/balloon_view.h index 5164360..819df68 100644 --- a/chrome/browser/cocoa/notifications/balloon_view.h +++ b/chrome/browser/cocoa/notifications/balloon_view.h @@ -8,8 +8,6 @@ #import <Cocoa/Cocoa.h> -#include "base/scoped_nsobject.h" - @interface BalloonWindow : NSWindow { } @end diff --git a/chrome/browser/cocoa/notifications/balloon_view.mm b/chrome/browser/cocoa/notifications/balloon_view.mm index 3a5d13e..0c2e83e 100644 --- a/chrome/browser/cocoa/notifications/balloon_view.mm +++ b/chrome/browser/cocoa/notifications/balloon_view.mm @@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "base/scoped_nsobject.h" +#import "chrome/browser/cocoa/notifications/balloon_controller.h" #import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h" namespace { @@ -36,6 +37,16 @@ const int kRoundedCornerSize = 6; - (BOOL)canBecomeMainWindow { return NO; } + +- (void)sendEvent:(NSEvent*)event { + // We do not want to bring chrome window to foreground when we click on close + // or option button. To do this, we have to intercept the event. + BalloonController* delegate = + static_cast<BalloonController*>([self delegate]); + if (![delegate handleEvent:event]) { + [super sendEvent:event]; + } +} @end @implementation BalloonShelfViewCocoa diff --git a/chrome/browser/cocoa/notifications/balloon_view_bridge.h b/chrome/browser/cocoa/notifications/balloon_view_bridge.h index 01adcd8..985fcde 100644 --- a/chrome/browser/cocoa/notifications/balloon_view_bridge.h +++ b/chrome/browser/cocoa/notifications/balloon_view_bridge.h @@ -6,10 +6,13 @@ #define CHROME_BROWSER_COCOA_NOTIFICATIONS_BALLOON_VIEW_BRIDGE_H_ #pragma once -#include "base/scoped_nsobject.h" -#import "chrome/browser/cocoa/notifications/balloon_controller.h" #include "chrome/browser/notifications/balloon.h" -#include "gfx/size.h" + +@class BalloonController; +class BalloonHost; +namespace gfx { +class Size; +} // Bridges from the cross-platform BalloonView interface to the Cocoa // controller which will draw the view on screen. diff --git a/chrome/browser/cocoa/notifications/balloon_view_bridge.mm b/chrome/browser/cocoa/notifications/balloon_view_bridge.mm index 7f6aa7c..c0b8b1f 100644 --- a/chrome/browser/cocoa/notifications/balloon_view_bridge.mm +++ b/chrome/browser/cocoa/notifications/balloon_view_bridge.mm @@ -4,6 +4,10 @@ #include "chrome/browser/cocoa/notifications/balloon_view_bridge.h" +#include "chrome/browser/cocoa/notifications/balloon_controller.h" +#import "chrome/browser/cocoa/notifications/balloon_view_host_mac.h" +#include "gfx/size.h" + #import <Cocoa/Cocoa.h> BalloonViewBridge::BalloonViewBridge() : diff --git a/chrome/browser/cocoa/notifications/balloon_view_host_mac.h b/chrome/browser/cocoa/notifications/balloon_view_host_mac.h index 286d5b1..3ef85b2 100644 --- a/chrome/browser/cocoa/notifications/balloon_view_host_mac.h +++ b/chrome/browser/cocoa/notifications/balloon_view_host_mac.h @@ -7,7 +7,9 @@ #pragma once #include "chrome/browser/notifications/balloon_host.h" -#import "chrome/browser/renderer_host/render_widget_host_view_mac.h" + +class RenderWidgetHostView; +class RenderWidgetHostViewMac; // BalloonViewHost class is a delegate to the renderer host for the HTML // notification. When initialized it creates a new RenderViewHost and loads @@ -17,23 +19,17 @@ class BalloonViewHost : public BalloonHost { public: explicit BalloonViewHost(Balloon* balloon); - ~BalloonViewHost() { - Shutdown(); - } + ~BalloonViewHost(); // Changes the size of the balloon. void UpdateActualSize(const gfx::Size& new_size); // Accessors. - gfx::NativeView native_view() const { - return render_widget_host_view_->native_view(); - } + gfx::NativeView native_view() const; protected: virtual void InitRenderWidgetHostView(); - virtual RenderWidgetHostView* render_widget_host_view() const { - return render_widget_host_view_; - } + virtual RenderWidgetHostView* render_widget_host_view() const; private: // The Mac-specific widget host view. This is owned by its native view, diff --git a/chrome/browser/cocoa/notifications/balloon_view_host_mac.mm b/chrome/browser/cocoa/notifications/balloon_view_host_mac.mm index 3b3aa35..67b75b3 100644 --- a/chrome/browser/cocoa/notifications/balloon_view_host_mac.mm +++ b/chrome/browser/cocoa/notifications/balloon_view_host_mac.mm @@ -4,15 +4,17 @@ #include "chrome/browser/cocoa/notifications/balloon_view_host_mac.h" -#include "chrome/browser/notifications/balloon.h" #include "chrome/browser/renderer_host/render_view_host.h" -#include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/browser/renderer_host/render_widget_host_view_mac.h" BalloonViewHost::BalloonViewHost(Balloon* balloon) : BalloonHost(balloon) { } +BalloonViewHost::~BalloonViewHost() { + Shutdown(); +} + void BalloonViewHost::UpdateActualSize(const gfx::Size& new_size) { NSView* view = render_widget_host_view_->native_view(); NSRect frame = [view frame]; @@ -23,8 +25,15 @@ void BalloonViewHost::UpdateActualSize(const gfx::Size& new_size) { [view setNeedsDisplay:YES]; } +gfx::NativeView BalloonViewHost::native_view() const { + return render_widget_host_view_->native_view(); +} + void BalloonViewHost::InitRenderWidgetHostView() { DCHECK(render_view_host_); render_widget_host_view_ = new RenderWidgetHostViewMac(render_view_host_); } +RenderWidgetHostView* BalloonViewHost::render_widget_host_view() const { + return render_widget_host_view_; +} diff --git a/chrome/browser/cocoa/page_info_bubble_controller.mm b/chrome/browser/cocoa/page_info_bubble_controller.mm index a2cc34f..c2c2d36 100644 --- a/chrome/browser/cocoa/page_info_bubble_controller.mm +++ b/chrome/browser/cocoa/page_info_bubble_controller.mm @@ -6,7 +6,9 @@ #include "app/l10n_util.h" #include "app/l10n_util_mac.h" +#include "base/message_loop.h" #include "base/sys_string_conversions.h" +#include "base/task.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/cert_store.h" #include "chrome/browser/certificate_viewer.h" @@ -26,9 +28,9 @@ - (PageInfoModel*)model; - (NSButton*)certificateButtonWithFrame:(NSRect)frame; - (void)configureTextFieldAsLabel:(NSTextField*)textField; -- (CGFloat)addTitleViewForInfo:(const PageInfoModel::SectionInfo&)info - toSubviews:(NSMutableArray*)subviews - atPoint:(NSPoint)point; +- (CGFloat)addHeadlineViewForInfo:(const PageInfoModel::SectionInfo&)info + toSubviews:(NSMutableArray*)subviews + atPoint:(NSPoint)point; - (CGFloat)addDescriptionViewForInfo:(const PageInfoModel::SectionInfo&)info toSubviews:(NSMutableArray*)subviews atPoint:(NSPoint)point; @@ -45,6 +47,19 @@ parentWindow:(NSWindow*)parent; @end +// This simple NSView subclass is used as the single subview of the page info +// bubble's window's contentView. Drawing is flipped so that layout of the +// sections is easier. Apple recommends flipping the coordinate origin when +// doing a lot of text layout because it's more natural. +@interface PageInfoContentView : NSView { +} +@end +@implementation PageInfoContentView +- (BOOL)isFlipped { + return YES; +} +@end + namespace { // The width of the window, in view coordinates. The height will be determined @@ -57,8 +72,8 @@ const NSInteger kVerticalSpacing = 10; // Padding along on the X-axis between the window frame and content. const NSInteger kFramePadding = 20; -// Spacing between the title and description text views. -const NSInteger kTitleSpacing = 2; +// Spacing between the optional headline and description text views. +const NSInteger kHeadlineSpacing = 2; // Spacing between the image and the text. const NSInteger kImageSpacing = 10; @@ -75,20 +90,33 @@ const CGFloat kTextXPosition = kTextXPositionNoImage + kImageSize + const CGFloat kTextWidth = kWindowWidth - (kImageSize + kImageSpacing + kFramePadding * 2); - // Bridge that listens for change notifications from the model. class PageInfoModelBubbleBridge : public PageInfoModel::PageInfoModelObserver { public: - PageInfoModelBubbleBridge() : controller_(nil) {} + PageInfoModelBubbleBridge() + : controller_(nil), + ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { + } // PageInfoModelObserver implementation. virtual void ModelChanged() { + // Check to see if a layout has already been scheduled. + if (!task_factory_.empty()) + return; + // Delay performing layout by a second so that all the animations from // InfoBubbleWindow and origin updates from BaseBubbleController finish, so // that we don't all race trying to change the frame's origin. - [controller_ performSelector:@selector(performLayout) - withObject:nil - afterDelay:1.0]; + // + // Using ScopedRunnableMethodFactory is superior here to |-performSelector:| + // because it will not retain its target; if the child outlives its parent, + // zombies get left behind (http://crbug.com/59619). This will also cancel + // the scheduled Tasks if the controller (and thus this bridge) get + // destroyed before the message can be delivered. + MessageLoop::current()->PostDelayedTask(FROM_HERE, + task_factory_.NewRunnableMethod( + &PageInfoModelBubbleBridge::PerformLayout), + 1000 /* milliseconds */); } // Sets the controller. @@ -97,7 +125,14 @@ class PageInfoModelBubbleBridge : public PageInfoModel::PageInfoModelObserver { } private: + void PerformLayout() { + [controller_ performLayout]; + } + PageInfoBubbleController* controller_; // weak + + // Factory that vends RunnableMethod tasks for scheduling layout. + ScopedRunnableMethodFactory<PageInfoModelBubbleBridge> task_factory_; }; } // namespace @@ -173,28 +208,33 @@ void ShowPageInfoBubble(gfx::NativeWindow parent, // not using HTTPS. - (void)performLayout { // |offset| is the Y position that should be drawn at next. - CGFloat offset = kVerticalSpacing; + CGFloat offset = kFramePadding; // Keep the new subviews in an array that gets replaced at the end. NSMutableArray* subviews = [NSMutableArray array]; - // First item, drawn at the bottom of the window, is the help center link. - offset += [self addHelpButtonToSubviews:subviews atOffset:offset]; - offset += kVerticalSpacing; - offset += [self addSeparatorToSubviews:subviews atOffset:offset]; - - // Build the window from bottom-up because Cocoa's coordinate origin is the - // lower left. - for (int i = model_->GetSectionCount() - 1; i >= 0; --i) { + // The subviews will be attached to the PageInfoContentView, which has a + // flipped origin. This allows the code to build top-to-bottom. + const int sectionCount = model_->GetSectionCount(); + for (int i = 0; i < sectionCount; ++i) { PageInfoModel::SectionInfo info = model_->GetSectionInfo(i); // Only certain sections have images. This affects the X position. BOOL hasImage = model_->GetIconImage(info.icon_id) != nil; CGFloat xPosition = (hasImage ? kTextXPosition : kTextXPositionNoImage); - if (info.type == PageInfoModel::SECTION_INFO_IDENTITY) { - offset += [self addCertificateButtonToSubviews:subviews - atOffset:offset]; + // Insert the image subview for sections that are appropriate. + CGFloat imageBaseline = offset + kImageSize; + if (hasImage) { + [self addImageViewForInfo:info toSubviews:subviews atOffset:offset]; + } + + // Add the title. + if (!info.headline.empty()) { + offset += [self addHeadlineViewForInfo:info + toSubviews:subviews + atPoint:NSMakePoint(xPosition, offset)]; + offset += kHeadlineSpacing; } // Create the description of the state. @@ -202,31 +242,37 @@ void ShowPageInfoBubble(gfx::NativeWindow parent, toSubviews:subviews atPoint:NSMakePoint(xPosition, offset)]; - // Add the title. - offset += kTitleSpacing; - offset += [self addTitleViewForInfo:info - toSubviews:subviews - atPoint:NSMakePoint(xPosition, offset)]; - - // Insert the image subview for sections that are appropriate. - if (hasImage) { - [self addImageViewForInfo:info toSubviews:subviews atOffset:offset]; + if (info.type == PageInfoModel::SECTION_INFO_IDENTITY && certID_) { + offset += kVerticalSpacing; + offset += [self addCertificateButtonToSubviews:subviews atOffset:offset]; } + // If at this point the description and optional headline and button are + // not as tall as the image, adjust the offset by the difference. + CGFloat imageBaselineDelta = imageBaseline - offset; + if (imageBaselineDelta > 0) + offset += imageBaselineDelta; + // Add the separators. - if (i != 0) { - offset += kVerticalSpacing; - offset += [self addSeparatorToSubviews:subviews atOffset:offset]; - } + offset += kVerticalSpacing; + offset += [self addSeparatorToSubviews:subviews atOffset:offset]; } - // Replace the window's content. - [[[self window] contentView] setSubviews:subviews]; + // The last item at the bottom of the window is the help center link. + offset += [self addHelpButtonToSubviews:subviews atOffset:offset]; + offset += kVerticalSpacing; - offset += kFramePadding; + // Create the dummy view that uses flipped coordinates. + NSRect contentFrame = NSMakeRect(0, 0, kWindowWidth, offset); + scoped_nsobject<PageInfoContentView> contentView( + [[PageInfoContentView alloc] initWithFrame:contentFrame]); + [contentView setSubviews:subviews]; - NSRect windowFrame = NSMakeRect(0, 0, kWindowWidth, 0); - windowFrame.size.height += offset; + // Replace the window's content. + [[[self window] contentView] setSubviews: + [NSArray arrayWithObject:contentView]]; + + NSRect windowFrame = NSMakeRect(0, 0, kWindowWidth, offset); windowFrame.size = [[[self window] contentView] convertSize:windowFrame.size toView:nil]; // Adjust the origin by the difference in height. @@ -273,21 +319,21 @@ void ShowPageInfoBubble(gfx::NativeWindow parent, // Adds the title text field at the given x,y position, and returns the y // position for the next element. -- (CGFloat)addTitleViewForInfo:(const PageInfoModel::SectionInfo&)info - toSubviews:(NSMutableArray*)subviews - atPoint:(NSPoint)point { +- (CGFloat)addHeadlineViewForInfo:(const PageInfoModel::SectionInfo&)info + toSubviews:(NSMutableArray*)subviews + atPoint:(NSPoint)point { NSRect frame = NSMakeRect(point.x, point.y, kTextWidth, kImageSpacing); - scoped_nsobject<NSTextField> titleField( + scoped_nsobject<NSTextField> textField( [[NSTextField alloc] initWithFrame:frame]); - [self configureTextFieldAsLabel:titleField.get()]; - [titleField setStringValue:base::SysUTF16ToNSString(info.title)]; + [self configureTextFieldAsLabel:textField.get()]; + [textField setStringValue:base::SysUTF16ToNSString(info.headline)]; NSFont* font = [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]]; - [titleField setFont:font]; + [textField setFont:font]; frame.size.height += [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField: - titleField]; - [titleField setFrame:frame]; - [subviews addObject:titleField.get()]; + textField]; + [textField setFrame:frame]; + [subviews addObject:textField.get()]; return NSHeight(frame); } @@ -315,6 +361,9 @@ void ShowPageInfoBubble(gfx::NativeWindow parent, // Returns the y position for the next element. - (CGFloat)addCertificateButtonToSubviews:(NSMutableArray*)subviews atOffset:(CGFloat)offset { + // The certificate button should only be added if there is SSL information. + DCHECK(certID_); + // Create the certificate button. The frame will be fixed up by GTM, so // use arbitrary values. NSRect frame = NSMakeRect(kTextXPosition, offset, 100, 14); @@ -323,19 +372,17 @@ void ShowPageInfoBubble(gfx::NativeWindow parent, [GTMUILocalizerAndLayoutTweaker sizeToFitView:certButton]; // By default, assume that we don't have certificate information to show. - [certButton setEnabled:NO]; - if (certID_) { - scoped_refptr<net::X509Certificate> cert; - CertStore::GetSharedInstance()->RetrieveCert(certID_, &cert); - - // Don't bother showing certificates if there isn't one. Gears runs - // with no OS root certificate. - if (cert.get() && cert->os_cert_handle()) { - [certButton setEnabled:YES]; - } + scoped_refptr<net::X509Certificate> cert; + CertStore::GetSharedInstance()->RetrieveCert(certID_, &cert); + + // Don't bother showing certificates if there isn't one. Gears runs + // with no OS root certificate. + if (!cert.get() || !cert->os_cert_handle()) { + // This should only ever happen in unit tests. + [certButton setEnabled:NO]; } - return NSHeight(frame) + kVerticalSpacing; + return NSHeight([certButton frame]); } // Adds the state image at a pre-determined x position and the given y. This @@ -344,7 +391,7 @@ void ShowPageInfoBubble(gfx::NativeWindow parent, - (void)addImageViewForInfo:(const PageInfoModel::SectionInfo&)info toSubviews:(NSMutableArray*)subviews atOffset:(CGFloat)offset { - NSRect frame = NSMakeRect(kFramePadding, offset - kImageSize, kImageSize, + NSRect frame = NSMakeRect(kFramePadding, offset, kImageSize, kImageSize); scoped_nsobject<NSImageView> imageView( [[NSImageView alloc] initWithFrame:frame]); diff --git a/chrome/browser/cocoa/page_info_bubble_controller_unittest.mm b/chrome/browser/cocoa/page_info_bubble_controller_unittest.mm index eb7c177..3354577 100644 --- a/chrome/browser/cocoa/page_info_bubble_controller_unittest.mm +++ b/chrome/browser/cocoa/page_info_bubble_controller_unittest.mm @@ -22,11 +22,11 @@ class FakeModel : public PageInfoModel { FakeModel() : PageInfoModel() {} void AddSection(SectionStateIcon icon_id, - const string16& title, + const string16& headline, const string16& description, SectionInfoType type) { sections_.push_back(SectionInfo( - icon_id, title, string16(), description, type)); + icon_id, headline, description, type)); } }; @@ -67,7 +67,13 @@ class PageInfoBubbleControllerTest : public CocoaTest { int link_count = 1; ++spacer_count; - for (NSView* view in [[window_ contentView] subviews]) { + // The window's only immediate child is an invisible view that has a flipped + // coordinate origin. It is into this that all views get placed. + NSArray* windowSubviews = [[window_ contentView] subviews]; + EXPECT_EQ(1U, [windowSubviews count]); + NSArray* subviews = [[windowSubviews lastObject] subviews]; + + for (NSView* view in subviews) { if ([view isKindOfClass:[NSTextField class]]) { --text_count; } else if ([view isKindOfClass:[NSImageView class]]) { @@ -113,28 +119,28 @@ class PageInfoBubbleControllerTest : public CocoaTest { TEST_F(PageInfoBubbleControllerTest, NoHistoryNoSecurity) { model_->AddSection(PageInfoModel::ICON_STATE_ERROR, - l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_IDENTITY_TITLE), + string16(), l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY), PageInfoModel::SECTION_INFO_IDENTITY); model_->AddSection(PageInfoModel::ICON_STATE_ERROR, - l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_CONNECTION_TITLE), + string16(), l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT, ASCIIToUTF16("google.com")), PageInfoModel::SECTION_INFO_CONNECTION); CreateBubble(); - CheckWindow(/*text=*/4, /*image=*/2, /*spacer=*/1, /*button=*/1); + CheckWindow(/*text=*/2, /*image=*/2, /*spacer=*/1, /*button=*/0); } TEST_F(PageInfoBubbleControllerTest, HistoryNoSecurity) { model_->AddSection(PageInfoModel::ICON_STATE_ERROR, - l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_IDENTITY_TITLE), + string16(), l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY), PageInfoModel::SECTION_INFO_IDENTITY); model_->AddSection(PageInfoModel::ICON_STATE_ERROR, - l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_CONNECTION_TITLE), + string16(), l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT, ASCIIToUTF16("google.com")), @@ -145,21 +151,20 @@ TEST_F(PageInfoBubbleControllerTest, HistoryNoSecurity) { CreateBubble(); model_->AddSection(PageInfoModel::ICON_STATE_ERROR, - l10n_util::GetStringUTF16( - IDS_PAGE_INFO_SECURITY_TAB_PERSONAL_HISTORY_TITLE), + l10n_util::GetStringUTF16(IDS_PAGE_INFO_SITE_INFO_TITLE), l10n_util::GetStringUTF16( IDS_PAGE_INFO_SECURITY_TAB_FIRST_VISITED_TODAY), PageInfoModel::SECTION_INFO_FIRST_VISIT); [controller_ performLayout]; - CheckWindow(/*text=*/6, /*image=*/3, /*spacer=*/2, /*button=*/1); + CheckWindow(/*text=*/4, /*image=*/3, /*spacer=*/2, /*button=*/0); } TEST_F(PageInfoBubbleControllerTest, NoHistoryMixedSecurity) { model_->AddSection(PageInfoModel::ICON_STATE_OK, - l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_IDENTITY_TITLE), + string16(), l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, ASCIIToUTF16("Goat Security Systems")), @@ -177,17 +182,20 @@ TEST_F(PageInfoBubbleControllerTest, NoHistoryMixedSecurity) { IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_INSECURE_CONTENT_WARNING)); model_->AddSection(PageInfoModel::ICON_STATE_OK, - l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_CONNECTION_TITLE), + string16(), description, PageInfoModel::SECTION_INFO_CONNECTION); + CreateBubble(); + [controller_ setCertID:1]; + [controller_ performLayout]; - NSArray* subviews = [[window_ contentView] subviews]; - CheckWindow(/*text=*/4, /*image=*/2, /*spacer=*/1, /*button=*/1); + CheckWindow(/*text=*/2, /*image=*/2, /*spacer=*/1, /*button=*/1); // Look for the over-sized box. NSString* targetDesc = base::SysUTF16ToNSString(description); + NSArray* subviews = [[window_ contentView] subviews]; for (NSView* subview in subviews) { if ([subview isKindOfClass:[NSTextField class]]) { NSTextField* desc = static_cast<NSTextField*>(subview); diff --git a/chrome/browser/cocoa/page_info_window_mac.mm b/chrome/browser/cocoa/page_info_window_mac.mm deleted file mode 100644 index 15b3e7f..0000000 --- a/chrome/browser/cocoa/page_info_window_mac.mm +++ /dev/null @@ -1,21 +0,0 @@ -// 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/page_info_window.h" - -#include "base/logging.h" - -// TODO(finnur): Delete this file. http://crbug.com/59030 - -namespace browser { - -void ShowPageInfo(gfx::NativeWindow parent, - Profile* profile, - const GURL& url, - const NavigationEntry::SSLStatus& ssl, - bool show_history) { - NOTREACHED(); -} - -} // namespace browser diff --git a/chrome/browser/cocoa/reload_button.h b/chrome/browser/cocoa/reload_button.h index 493da1c..edb6e9c 100644 --- a/chrome/browser/cocoa/reload_button.h +++ b/chrome/browser/cocoa/reload_button.h @@ -20,9 +20,8 @@ BOOL isMouseInside_; scoped_nsobject<NSTrackingArea> trackingArea_; - // Set when reload mode is requested, but not forced, and the mouse - // is hovering. - BOOL pendingReloadMode_; + // Timer used when setting reload mode while the mouse is hovered. + scoped_nsobject<NSTimer> pendingReloadTimer_; } // Returns YES if the mouse is currently inside the bounds. @@ -32,13 +31,14 @@ // |isLoading|. If |force|, always sets the indicated mode. If // |!force|, and the mouse is over the button, defer the transition // from stop button to reload button until the mouse has left the -// button. This prevents an inadvertent click _just_ as the state -// changes. +// button, or until |pendingReloadTimer_| fires. This prevents an +// inadvertent click _just_ as the state changes. - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force; @end @interface ReloadButton (PrivateTestingMethods) ++ (void)setPendingReloadTimeout:(NSTimeInterval)seconds; - (NSTrackingArea*)trackingArea; @end diff --git a/chrome/browser/cocoa/reload_button.mm b/chrome/browser/cocoa/reload_button.mm index c1379ed..9975db7 100644 --- a/chrome/browser/cocoa/reload_button.mm +++ b/chrome/browser/cocoa/reload_button.mm @@ -7,7 +7,7 @@ #include "app/l10n_util.h" #include "app/l10n_util_mac.h" #include "base/nsimage_cache_mac.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #import "chrome/browser/cocoa/gradient_button_cell.h" #import "chrome/browser/cocoa/view_id_util.h" #include "grit/generated_resources.h" @@ -17,6 +17,9 @@ namespace { NSString* const kReloadImageName = @"reload_Template.pdf"; NSString* const kStopImageName = @"stop_Template.pdf"; +// Constant matches Windows. +NSTimeInterval kPendingReloadTimeout = 1.35; + } // namespace @implementation ReloadButton @@ -58,17 +61,17 @@ NSString* const kStopImageName = @"stop_Template.pdf"; } - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force { - pendingReloadMode_ = NO; - // Can always transition to stop mode. Only transition to reload // mode if forced or if the mouse isn't hovering. Otherwise, note // that reload mode is desired and disable the button. if (isLoading) { + pendingReloadTimer_.reset(); [self setImage:nsimage_cache::ImageNamed(kStopImageName)]; [self setTag:IDC_STOP]; [self setToolTip:l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_STOP)]; [self setEnabled:YES]; } else if (force || ![self isMouseInside]) { + pendingReloadTimer_.reset(); [self setImage:nsimage_cache::ImageNamed(kReloadImageName)]; [self setTag:IDC_RELOAD]; [self setToolTip:l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_RELOAD)]; @@ -81,26 +84,34 @@ NSString* const kStopImageName = @"stop_Template.pdf"; if ([cell respondsToSelector:@selector(setMouseInside:animate:)]) [cell setMouseInside:[self isMouseInside] animate:NO]; [self setEnabled:YES]; - } else if ([self tag] == IDC_STOP) { - pendingReloadMode_ = YES; + } else if ([self tag] == IDC_STOP && !pendingReloadTimer_) { [self setEnabled:NO]; + pendingReloadTimer_.reset( + [[NSTimer scheduledTimerWithTimeInterval:kPendingReloadTimeout + target:self + selector:@selector(forceReloadState) + userInfo:nil + repeats:NO] retain]); } } +- (void)forceReloadState { + [self setIsLoading:NO force:YES]; +} + - (BOOL)sendAction:(SEL)theAction to:(id)theTarget { if ([self tag] == IDC_STOP) { - // The stop command won't be valid after the attempt to change - // back to reload. But it "worked", so short-circuit it. - const BOOL ret = - pendingReloadMode_ ? YES : [super sendAction:theAction to:theTarget]; + // When the timer is started, the button is disabled, so this + // should not be possible. + DCHECK(!pendingReloadTimer_.get()); // When the stop is processed, immediately change to reload mode, // even though the IPC still has to bounce off the renderer and // back before the regular |-setIsLoaded:force:| will be called. // [This is how views and gtk do it.] + const BOOL ret = [super sendAction:theAction to:theTarget]; if (ret) - [self setIsLoading:NO force:YES]; - + [self forceReloadState]; return ret; } @@ -115,12 +126,12 @@ NSString* const kStopImageName = @"stop_Template.pdf"; isMouseInside_ = NO; // Reload mode was requested during the hover. - if (pendingReloadMode_) - [self setIsLoading:NO force:YES]; + if (pendingReloadTimer_) + [self forceReloadState]; } - (BOOL)isMouseInside { - return trackingArea_ && isMouseInside_; + return isMouseInside_; } - (ViewID)viewID { @@ -131,6 +142,10 @@ NSString* const kStopImageName = @"stop_Template.pdf"; @implementation ReloadButton (Testing) ++ (void)setPendingReloadTimeout:(NSTimeInterval)seconds { + kPendingReloadTimeout = seconds; +} + - (NSTrackingArea*)trackingArea { return trackingArea_; } diff --git a/chrome/browser/cocoa/reload_button_unittest.mm b/chrome/browser/cocoa/reload_button_unittest.mm index c2c9e84..19fde82 100644 --- a/chrome/browser/cocoa/reload_button_unittest.mm +++ b/chrome/browser/cocoa/reload_button_unittest.mm @@ -7,7 +7,7 @@ #import "chrome/browser/cocoa/reload_button.h" #include "base/scoped_nsobject.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" #import "chrome/browser/cocoa/test_event_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -109,7 +109,7 @@ TEST_F(ReloadButtonTest, SetIsLoadingForce) { // Test that without force, stop mode is set immediately, but reload // is affected by the hover status. -TEST_F(ReloadButtonTest, SetIsLoadingNoForce) { +TEST_F(ReloadButtonTest, SetIsLoadingNoForceUnHover) { EXPECT_FALSE([button_ isMouseInside]); EXPECT_EQ([button_ tag], IDC_RELOAD); @@ -142,6 +142,54 @@ TEST_F(ReloadButtonTest, SetIsLoadingNoForce) { EXPECT_EQ([button_ tag], IDC_RELOAD); } +// Test that without force, stop mode is set immediately, and reload +// will be set after a timeout. +// TODO(shess): Reenable, http://crbug.com/61485 +TEST_F(ReloadButtonTest, DISABLED_SetIsLoadingNoForceTimeout) { + // When the event loop first spins, some delayed tracking-area setup + // is done, which causes -mouseExited: to be called. Spin it at + // least once, and dequeue any pending events. + // TODO(shess): It would be more reasonable to have an MockNSTimer + // factory for the class to use, which this code could fire + // directly. + while ([NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:nil + inMode:NSDefaultRunLoopMode + dequeue:YES]) { + } + + const NSTimeInterval kShortTimeout = 0.1; + [ReloadButton setPendingReloadTimeout:kShortTimeout]; + + EXPECT_FALSE([button_ isMouseInside]); + EXPECT_EQ(IDC_RELOAD, [button_ tag]); + + // Move the mouse into the button and press it. + [button_ mouseEntered:nil]; + EXPECT_TRUE([button_ isMouseInside]); + [button_ setIsLoading:YES force:NO]; + EXPECT_EQ(IDC_STOP, [button_ tag]); + + // Does not change to reload immediately when the mouse is hovered. + EXPECT_TRUE([button_ isMouseInside]); + [button_ setIsLoading:NO force:NO]; + EXPECT_TRUE([button_ isMouseInside]); + EXPECT_EQ(IDC_STOP, [button_ tag]); + EXPECT_TRUE([button_ isMouseInside]); + + // Spin event loop until the timeout passes. + NSDate* pastTimeout = [NSDate dateWithTimeIntervalSinceNow:2 * kShortTimeout]; + [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:pastTimeout + inMode:NSDefaultRunLoopMode + dequeue:NO]; + + // Mouse is still hovered, button is in reload mode. If the mouse + // is no longer hovered, see comment at top of function. + EXPECT_TRUE([button_ isMouseInside]); + EXPECT_EQ(IDC_RELOAD, [button_ tag]); +} + // Test that pressing stop after reload mode has been requested // doesn't forward the stop message. TEST_F(ReloadButtonTest, StopAfterReloadSet) { diff --git a/chrome/browser/cocoa/sad_tab_controller_unittest.mm b/chrome/browser/cocoa/sad_tab_controller_unittest.mm index c125f87..7797397 100644 --- a/chrome/browser/cocoa/sad_tab_controller_unittest.mm +++ b/chrome/browser/cocoa/sad_tab_controller_unittest.mm @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/debug/debugger.h" #include "base/scoped_nsobject.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" #import "chrome/browser/cocoa/sad_tab_controller.h" @@ -35,7 +36,7 @@ class SadTabControllerTest : public RenderViewHostTestHarness { // from CocoaTest, so do a bootstrap and create test window. CocoaTest::BootstrapCocoa(); test_window_ = [[CocoaTestHelperWindow alloc] init]; - if (DebugUtil::BeingDebugged()) { + if (base::debug::BeingDebugged()) { [test_window_ orderFront:nil]; } else { [test_window_ orderBack:nil]; diff --git a/chrome/browser/cocoa/search_engine_dialog_controller.h b/chrome/browser/cocoa/search_engine_dialog_controller.h index a410614..0cc06f2 100644 --- a/chrome/browser/cocoa/search_engine_dialog_controller.h +++ b/chrome/browser/cocoa/search_engine_dialog_controller.h @@ -6,6 +6,7 @@ #include <vector> +#import "base/ref_counted.h" #import "base/scoped_nsobject.h" #include "base/scoped_ptr.h" @@ -27,7 +28,7 @@ class TemplateURLModel; TemplateURLModel* searchEnginesModel_; // Bridge to the C++ world. - scoped_ptr<SearchEngineDialogControllerBridge> bridge_; + scoped_refptr<SearchEngineDialogControllerBridge> bridge_; // Offered search engine choices. std::vector<const TemplateURL*> choices_; diff --git a/chrome/browser/cocoa/search_engine_dialog_controller.mm b/chrome/browser/cocoa/search_engine_dialog_controller.mm index 044bb58..0888050 100644 --- a/chrome/browser/cocoa/search_engine_dialog_controller.mm +++ b/chrome/browser/cocoa/search_engine_dialog_controller.mm @@ -40,7 +40,9 @@ const int kLogoLabelHeight = 25; - (IBAction)searchEngineSelected:(id)sender; @end -class SearchEngineDialogControllerBridge : public TemplateURLModelObserver { +class SearchEngineDialogControllerBridge : + public base::RefCounted<SearchEngineDialogControllerBridge>, + public TemplateURLModelObserver { public: SearchEngineDialogControllerBridge(SearchEngineDialogController* controller); @@ -71,7 +73,7 @@ void SearchEngineDialogControllerBridge::OnTemplateURLModelChanged() { ofType:@"nib"]; self = [super initWithWindowNibPath:nibpath owner:self]; if (self != nil) { - bridge_.reset(new SearchEngineDialogControllerBridge(self)); + bridge_ = new SearchEngineDialogControllerBridge(self); } return self; } @@ -85,11 +87,15 @@ void SearchEngineDialogControllerBridge::OnTemplateURLModelChanged() { searchEnginesModel_->AddObserver(bridge_.get()); if (searchEnginesModel_->loaded()) { - [self onTemplateURLModelChanged]; + MessageLoop::current()->PostTask( + FROM_HERE, + NewRunnableMethod( + bridge_.get(), + &SearchEngineDialogControllerBridge::OnTemplateURLModelChanged)); } else { searchEnginesModel_->Load(); - MessageLoop::current()->Run(); } + MessageLoop::current()->Run(); } - (void)onTemplateURLModelChanged { diff --git a/chrome/browser/cocoa/shell_dialogs_mac.mm b/chrome/browser/cocoa/shell_dialogs_mac.mm index bb1ad05..46f5ea2 100644 --- a/chrome/browser/cocoa/shell_dialogs_mac.mm +++ b/chrome/browser/cocoa/shell_dialogs_mac.mm @@ -13,6 +13,7 @@ #include "app/l10n_util_mac.h" #import "base/cocoa_protocols_mac.h" +#include "base/file_util.h" #include "base/logging.h" #include "base/mac_util.h" #include "base/mac/scoped_cftyperef.h" @@ -165,8 +166,13 @@ void SelectFileDialogImpl::SelectFile( NSString* default_dir = nil; NSString* default_filename = nil; if (!default_path.empty()) { - default_dir = base::SysUTF8ToNSString(default_path.DirName().value()); - default_filename = base::SysUTF8ToNSString(default_path.BaseName().value()); + if (file_util::DirectoryExists(default_path)) { + default_dir = base::SysUTF8ToNSString(default_path.value()); + } else { + default_dir = base::SysUTF8ToNSString(default_path.DirName().value()); + default_filename = + base::SysUTF8ToNSString(default_path.BaseName().value()); + } } NSMutableArray* allowed_file_types = nil; diff --git a/chrome/browser/cocoa/speech_input_window_controller.mm b/chrome/browser/cocoa/speech_input_window_controller.mm index 191549f..76d1dd1 100644 --- a/chrome/browser/cocoa/speech_input_window_controller.mm +++ b/chrome/browser/cocoa/speech_input_window_controller.mm @@ -42,6 +42,8 @@ const int kBubbleHorizontalMargin = 5; // Space on either sides of controls. } - (void)awakeFromNib { + [super awakeFromNib]; + NSWindow* window = [self window]; [[self bubble] setArrowLocation:info_bubble::kTopLeft]; diff --git a/chrome/browser/cocoa/status_bubble_mac.mm b/chrome/browser/cocoa/status_bubble_mac.mm index 447d263..58da593 100644 --- a/chrome/browser/cocoa/status_bubble_mac.mm +++ b/chrome/browser/cocoa/status_bubble_mac.mm @@ -152,8 +152,8 @@ void StatusBubbleMac::SetURL(const GURL& url, const string16& languages) { [font pointSize]); string16 original_url_text = net::FormatUrl(url, UTF16ToUTF8(languages)); - string16 status = WideToUTF16(gfx::ElideUrl(url, font_chr, text_width, - UTF16ToWideHack(languages))); + string16 status = gfx::ElideUrl(url, font_chr, text_width, + UTF16ToWideHack(languages)); SetText(status, true); @@ -608,8 +608,8 @@ void StatusBubbleMac::ExpandBubble() { NSFont* font = [[window_ contentView] font]; gfx::Font font_chr(base::SysNSStringToWide([font fontName]), [font pointSize]); - string16 expanded_url = WideToUTF16(gfx::ElideUrl(url_, font_chr, - max_bubble_width, UTF16ToWide(languages_))); + string16 expanded_url = gfx::ElideUrl(url_, font_chr, + max_bubble_width, UTF16ToWideHack(languages_)); // Scale width from gfx::Font in view coordinates to window coordinates. int required_width_for_string = diff --git a/chrome/browser/cocoa/tab_strip_controller.mm b/chrome/browser/cocoa/tab_strip_controller.mm index fab844b..8d06213 100644 --- a/chrome/browser/cocoa/tab_strip_controller.mm +++ b/chrome/browser/cocoa/tab_strip_controller.mm @@ -14,8 +14,9 @@ #include "base/mac_util.h" #include "base/nsimage_cache_mac.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/find_bar.h" #include "chrome/browser/find_bar_controller.h" #include "chrome/browser/metrics/user_metrics.h" @@ -1350,6 +1351,11 @@ private: TabController* tabController = [tabArray_ objectAtIndex:index]; DCHECK([tabController isKindOfClass:[TabController class]]); + + // Don't do anything if the change was already picked up by the move event. + if (tabStripModel_->IsMiniTab(modelIndex) == [tabController mini]) + return; + [tabController setMini:tabStripModel_->IsMiniTab(modelIndex)]; [tabController setPinned:tabStripModel_->IsTabPinned(modelIndex)]; [tabController setApp:tabStripModel_->IsAppTab(modelIndex)]; @@ -1697,11 +1703,12 @@ private: case NEW_FOREGROUND_TAB: { UserMetrics::RecordAction(UserMetricsAction("Tab_DropURLBetweenTabs"), browser_->profile()); - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - params.index = index; - params.add_types = + browser::NavigateParams params(browser_, url, PageTransition::TYPED); + params.disposition = disposition; + params.tabstrip_index = index; + params.tabstrip_add_types = TabStripModel::ADD_SELECTED | TabStripModel::ADD_FORCE_INDEX; - browser_->AddTabWithURL(¶ms); + browser::Navigate(¶ms); break; } case CURRENT_TAB: diff --git a/chrome/browser/cocoa/tab_view.h b/chrome/browser/cocoa/tab_view.h index 74689c7..73c6334 100644 --- a/chrome/browser/cocoa/tab_view.h +++ b/chrome/browser/cocoa/tab_view.h @@ -47,6 +47,10 @@ enum AlertState { // TODO(rohitrao): Add this button to a CoreAnimation layer so we can fade it // in and out on mouseovers. IBOutlet HoverCloseButton* closeButton_; + + // See awakeFromNib for purpose. + scoped_nsobject<HoverCloseButton> closeButtonRetainer_; + BOOL closing_; // Tracking area for close button mouseover images. diff --git a/chrome/browser/cocoa/tab_view.mm b/chrome/browser/cocoa/tab_view.mm index 7b7ed59..1550d7e 100644 --- a/chrome/browser/cocoa/tab_view.mm +++ b/chrome/browser/cocoa/tab_view.mm @@ -8,6 +8,7 @@ #import "base/mac_util.h" #include "base/mac/scoped_cftyperef.h" #include "base/nsimage_cache_mac.h" +#include "chrome/browser/accessibility/browser_accessibility_state.h" #import "chrome/browser/cocoa/tab_controller.h" #import "chrome/browser/cocoa/tab_window_controller.h" #import "chrome/browser/cocoa/themed_window.h" @@ -73,6 +74,16 @@ const CGFloat kRapidCloseDist = 2.5; - (void)awakeFromNib { [self setShowsDivider:NO]; + + // It is desirable for us to remove the close button from the cocoa hierarchy, + // so that VoiceOver does not encounter it. + // TODO(dtseng): crbug.com/59978. + // Retain in case we remove it from its superview. + closeButtonRetainer_.reset([closeButton_ retain]); + if (Singleton<BrowserAccessibilityState>::get()->IsAccessibleBrowser()) { + // The superview gives up ownership of the closeButton here. + [closeButton_ removeFromSuperview]; + } } - (void)dealloc { diff --git a/chrome/browser/cocoa/tabpose_window.mm b/chrome/browser/cocoa/tabpose_window.mm index 33fcb6a..dde7caf 100644 --- a/chrome/browser/cocoa/tabpose_window.mm +++ b/chrome/browser/cocoa/tabpose_window.mm @@ -11,7 +11,7 @@ #include "base/mac/scoped_cftyperef.h" #include "base/scoped_callback_factory.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_process.h" #import "chrome/browser/cocoa/bookmarks/bookmark_bar_constants.h" #import "chrome/browser/cocoa/browser_window_controller.h" diff --git a/chrome/browser/cocoa/task_manager_mac.mm b/chrome/browser/cocoa/task_manager_mac.mm index b4cee66..65241d2 100644 --- a/chrome/browser/cocoa/task_manager_mac.mm +++ b/chrome/browser/cocoa/task_manager_mac.mm @@ -195,6 +195,12 @@ class SortHelper { [tableView_ sizeToFit]; } +- (void)dealloc { + [tableView_ setDelegate:nil]; + [tableView_ setDataSource:nil]; + [super dealloc]; +} + // Adds a column which has the given string id as title. |isVisible| specifies // if the column is initially visible. - (NSTableColumn*)addColumnWithId:(int)columnId visible:(BOOL)isVisible { diff --git a/chrome/browser/cocoa/toolbar_controller.mm b/chrome/browser/cocoa/toolbar_controller.mm index 4405b3f..25e031b 100644 --- a/chrome/browser/cocoa/toolbar_controller.mm +++ b/chrome/browser/cocoa/toolbar_controller.mm @@ -14,7 +14,7 @@ #include "base/nsimage_cache_mac.h" #include "base/singleton.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/autocomplete/autocomplete_edit_view.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_window.h" diff --git a/chrome/browser/cocoa/toolbar_controller_unittest.mm b/chrome/browser/cocoa/toolbar_controller_unittest.mm index 03914c6..43fbafb 100644 --- a/chrome/browser/cocoa/toolbar_controller_unittest.mm +++ b/chrome/browser/cocoa/toolbar_controller_unittest.mm @@ -5,7 +5,7 @@ #import <Cocoa/Cocoa.h> #import "base/scoped_nsobject.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/cocoa/browser_test_helper.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" #import "chrome/browser/cocoa/gradient_button_cell.h" diff --git a/chrome/browser/cocoa/translate/translate_infobar_base.mm b/chrome/browser/cocoa/translate/translate_infobar_base.mm index bdc34f0..48ce514 100644 --- a/chrome/browser/cocoa/translate/translate_infobar_base.mm +++ b/chrome/browser/cocoa/translate/translate_infobar_base.mm @@ -10,7 +10,7 @@ #include "base/mac_util.h" #include "base/metrics/histogram.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #import "chrome/browser/cocoa/hover_close_button.h" #include "chrome/browser/cocoa/infobar.h" #import "chrome/browser/cocoa/infobar_controller.h" diff --git a/chrome/browser/cocoa/translate/translate_infobar_unittest.mm b/chrome/browser/cocoa/translate/translate_infobar_unittest.mm index a389eba..fbe85b0 100644 --- a/chrome/browser/cocoa/translate/translate_infobar_unittest.mm +++ b/chrome/browser/cocoa/translate/translate_infobar_unittest.mm @@ -7,7 +7,7 @@ #import "base/scoped_nsobject.h" #import "base/string_util.h" #include "base/utf_string_conversions.h" -#import "chrome/app/chrome_dll_resource.h" // For translate menu command ids. +#import "chrome/app/chrome_command_ids.h" // For translate menu command ids. #import "chrome/browser/cocoa/browser_test_helper.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" #import "chrome/browser/cocoa/infobar.h" diff --git a/chrome/browser/cocoa/web_drag_source.mm b/chrome/browser/cocoa/web_drag_source.mm index a2402da..95ab0d1 100644 --- a/chrome/browser/cocoa/web_drag_source.mm +++ b/chrome/browser/cocoa/web_drag_source.mm @@ -307,13 +307,13 @@ void PromiseWriterTask::Run() { if (downloadURL_.is_valid()) { TabContents* tabContents = [contentsView_ tabContents]; - scoped_refptr<DragDownloadFile> dragFileDownloader = new DragDownloadFile( + scoped_refptr<DragDownloadFile> dragFileDownloader(new DragDownloadFile( filePath, linked_ptr<net::FileStream>(fileStream), downloadURL_, tabContents->GetURL(), tabContents->encoding(), - tabContents); + tabContents)); // The finalizer will take care of closing and deletion. dragFileDownloader->Start( diff --git a/chrome/browser/cocoa/wrench_menu_button_cell_unittest.mm b/chrome/browser/cocoa/wrench_menu_button_cell_unittest.mm index e736fdc..b24ad50 100644 --- a/chrome/browser/cocoa/wrench_menu_button_cell_unittest.mm +++ b/chrome/browser/cocoa/wrench_menu_button_cell_unittest.mm @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "base/scoped_nsobject.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" #import "chrome/browser/cocoa/wrench_menu_button_cell.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chrome/browser/cocoa/wrench_menu_controller.mm b/chrome/browser/cocoa/wrench_menu_controller.mm index 5b0a92a..7cecb71 100644 --- a/chrome/browser/cocoa/wrench_menu_controller.mm +++ b/chrome/browser/cocoa/wrench_menu_controller.mm @@ -7,7 +7,7 @@ #include "app/l10n_util.h" #include "app/menus/menu_model.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_window.h" #import "chrome/browser/cocoa/menu_tracked_root_view.h" diff --git a/chrome/browser/cocoa/wrench_menu_controller_unittest.mm b/chrome/browser/cocoa/wrench_menu_controller_unittest.mm index 446bd80..1018666 100644 --- a/chrome/browser/cocoa/wrench_menu_controller_unittest.mm +++ b/chrome/browser/cocoa/wrench_menu_controller_unittest.mm @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "base/scoped_nsobject.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/cocoa/browser_test_helper.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" #import "chrome/browser/cocoa/toolbar_controller.h" diff --git a/chrome/browser/collected_cookies_uitest.cc b/chrome/browser/collected_cookies_uitest.cc index 169f072..6c241ce 100644 --- a/chrome/browser/collected_cookies_uitest.cc +++ b/chrome/browser/collected_cookies_uitest.cc @@ -4,7 +4,7 @@ #include <string> -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/url_constants.h" @@ -21,7 +21,8 @@ const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data"); typedef UITest CollectedCookiesTest; -TEST_F(CollectedCookiesTest, DoubleDisplay) { +// Flaky, http://crbug.com/62311. +TEST_F(CollectedCookiesTest, FLAKY_DoubleDisplay) { net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); ASSERT_TRUE(test_server.Start()); @@ -43,7 +44,8 @@ TEST_F(CollectedCookiesTest, DoubleDisplay) { ASSERT_TRUE(tab->ShowCollectedCookiesDialog()); } -TEST_F(CollectedCookiesTest, NavigateAway) { +// Flaky, http://crbug.com/62311. +TEST_F(CollectedCookiesTest, FLAKY_NavigateAway) { net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); ASSERT_TRUE(test_server.Start()); diff --git a/chrome/browser/content_setting_bubble_model.cc b/chrome/browser/content_setting_bubble_model.cc index 65f1baa..2cc5d91 100644 --- a/chrome/browser/content_setting_bubble_model.cc +++ b/chrome/browser/content_setting_bubble_model.cc @@ -474,6 +474,21 @@ ContentSettingBubbleModel::ContentSettingBubbleModel( ContentSettingBubbleModel::~ContentSettingBubbleModel() { } +ContentSettingBubbleModel::RadioGroup::RadioGroup() : default_item(0) {} + +ContentSettingBubbleModel::RadioGroup::~RadioGroup() {} + +ContentSettingBubbleModel::DomainList::DomainList() {} + +ContentSettingBubbleModel::DomainList::~DomainList() {} + +ContentSettingBubbleModel::BubbleContent::BubbleContent() + : load_plugins_link_enabled(false) { +} + +ContentSettingBubbleModel::BubbleContent::~BubbleContent() {} + + void ContentSettingBubbleModel::AddBlockedResource( const std::string& resource_identifier) { bubble_content_.resource_identifiers.insert(resource_identifier); diff --git a/chrome/browser/content_setting_bubble_model.h b/chrome/browser/content_setting_bubble_model.h index 97664d8..49af1b2 100644 --- a/chrome/browser/content_setting_bubble_model.h +++ b/chrome/browser/content_setting_bubble_model.h @@ -42,6 +42,9 @@ class ContentSettingBubbleModel : public NotificationObserver { typedef std::vector<std::string> RadioItems; struct RadioGroup { + RadioGroup(); + ~RadioGroup(); + GURL url; std::string title; RadioItems radio_items; @@ -49,11 +52,17 @@ class ContentSettingBubbleModel : public NotificationObserver { }; struct DomainList { + DomainList(); + ~DomainList(); + std::string title; std::set<std::string> hosts; }; struct BubbleContent { + BubbleContent(); + ~BubbleContent(); + std::string title; PopupItems popup_items; RadioGroup radio_group; @@ -64,6 +73,9 @@ class ContentSettingBubbleModel : public NotificationObserver { std::string info_link; std::string load_plugins_link_title; bool load_plugins_link_enabled; + + private: + DISALLOW_COPY_AND_ASSIGN(BubbleContent); }; const BubbleContent& bubble_content() const { return bubble_content_; } diff --git a/chrome/browser/cookies_tree_model.cc b/chrome/browser/cookies_tree_model.cc index 1dd02be..24e3c9a 100644 --- a/chrome/browser/cookies_tree_model.cc +++ b/chrome/browser/cookies_tree_model.cc @@ -186,9 +186,10 @@ CookieTreeSessionStorageNode::CookieTreeSessionStorageNode( CookieTreeIndexedDBNode::CookieTreeIndexedDBNode( BrowsingDataIndexedDBHelper::IndexedDBInfo* indexed_db_info) - : CookieTreeNode(indexed_db_info->database_name.empty() ? - l10n_util::GetStringUTF16(IDS_COOKIES_WEB_DATABASE_UNNAMED_NAME) : - UTF8ToUTF16(indexed_db_info->database_name)), + : CookieTreeNode(UTF8ToUTF16( + indexed_db_info->origin.empty() ? + indexed_db_info->database_identifier : + indexed_db_info->origin)), indexed_db_info_(indexed_db_info) { } diff --git a/chrome/browser/crash_recovery_browsertest.cc b/chrome/browser/crash_recovery_browsertest.cc index 6cc6a0d..71d6a5d 100644 --- a/chrome/browser/crash_recovery_browsertest.cc +++ b/chrome/browser/crash_recovery_browsertest.cc @@ -31,14 +31,8 @@ class CrashRecoveryBrowserTest : public InProcessBrowserTest { // be fixed before it can be enabled to not cause the bots issues. #if defined(OS_MACOSX) #define MAYBE_Reload DISABLED_Reload -#define MAYBE_LoadInNewTab DISABLED_LoadInNewTab -#elif defined(OS_WIN) -// http://crbug.com/57158 - Times out sometimes on windows. -#define MAYBE_LoadInNewTab DISABLED_LoadInNewTab -#define MAYBE_Reload Reload #else #define MAYBE_Reload Reload -#define MAYBE_LoadInNewTab LoadInNewTab #endif // Test that reload works after a crash. @@ -65,7 +59,8 @@ IN_PROC_BROWSER_TEST_F(CrashRecoveryBrowserTest, MAYBE_Reload) { // There was an earlier bug (1270510) in process-per-site in which the max page // ID of the RenderProcessHost was stale, so the NavigationEntry in the new tab // was not committed. This prevents regression of that bug. -IN_PROC_BROWSER_TEST_F(CrashRecoveryBrowserTest, MAYBE_LoadInNewTab) { +// http://crbug.com/57158 - Times out sometimes on all platforms. +IN_PROC_BROWSER_TEST_F(CrashRecoveryBrowserTest, DISABLED_LoadInNewTab) { const FilePath::CharType* kTitle2File = FILE_PATH_LITERAL("title2.html"); ui_test_utils::NavigateToURL(browser(), diff --git a/chrome/browser/debugger/devtools_http_protocol_handler.cc b/chrome/browser/debugger/devtools_http_protocol_handler.cc index 5f01335..307aa84 100644 --- a/chrome/browser/debugger/devtools_http_protocol_handler.cc +++ b/chrome/browser/debugger/devtools_http_protocol_handler.cc @@ -95,7 +95,7 @@ void DevToolsHttpProtocolHandler::OnHttpRequest( FROM_HERE, NewRunnableMethod(this, &DevToolsHttpProtocolHandler::OnHttpRequestUI, - socket, + make_scoped_refptr(socket), info)); return; } @@ -123,7 +123,7 @@ void DevToolsHttpProtocolHandler::OnWebSocketRequest( NewRunnableMethod( this, &DevToolsHttpProtocolHandler::OnWebSocketRequestUI, - socket, + make_scoped_refptr(socket), request)); } @@ -135,7 +135,7 @@ void DevToolsHttpProtocolHandler::OnWebSocketMessage(HttpListenSocket* socket, NewRunnableMethod( this, &DevToolsHttpProtocolHandler::OnWebSocketMessageUI, - socket, + make_scoped_refptr(socket), data)); } @@ -154,6 +154,8 @@ void DevToolsHttpProtocolHandler::OnClose(HttpListenSocket* socket) { socket_to_requests_io_.erase(socket); } + // This can't use make_scoped_refptr because |socket| is already deleted + // when this runs -- http://crbug.com/59930 BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, diff --git a/chrome/browser/debugger/devtools_manager.cc b/chrome/browser/debugger/devtools_manager.cc index e141baf..86d7e52 100644 --- a/chrome/browser/debugger/devtools_manager.cc +++ b/chrome/browser/debugger/devtools_manager.cc @@ -74,7 +74,7 @@ void DevToolsManager::RegisterDevToolsClientHostFor( void DevToolsManager::ForwardToDevToolsAgent( RenderViewHost* client_rvh, const IPC::Message& message) { - DevToolsClientHost* client_host = FindOnwerDevToolsClientHost(client_rvh); + DevToolsClientHost* client_host = FindOwnerDevToolsClientHost(client_rvh); if (client_host) ForwardToDevToolsAgent(client_host, message); } @@ -105,7 +105,7 @@ void DevToolsManager::ForwardToDevToolsClient(RenderViewHost* inspected_rvh, } void DevToolsManager::ActivateWindow(RenderViewHost* client_rvh) { - DevToolsClientHost* client_host = FindOnwerDevToolsClientHost(client_rvh); + DevToolsClientHost* client_host = FindOwnerDevToolsClientHost(client_rvh); if (!client_host) return; @@ -115,7 +115,7 @@ void DevToolsManager::ActivateWindow(RenderViewHost* client_rvh) { } void DevToolsManager::CloseWindow(RenderViewHost* client_rvh) { - DevToolsClientHost* client_host = FindOnwerDevToolsClientHost(client_rvh); + DevToolsClientHost* client_host = FindOwnerDevToolsClientHost(client_rvh); if (client_host) { RenderViewHost* inspected_rvh = GetInspectedRenderViewHost(client_host); DCHECK(inspected_rvh); @@ -303,7 +303,7 @@ void DevToolsManager::ForceReopenWindow() { } } -DevToolsClientHost* DevToolsManager::FindOnwerDevToolsClientHost( +DevToolsClientHost* DevToolsManager::FindOwnerDevToolsClientHost( RenderViewHost* client_rvh) { for (InspectedRvhToClientHostMap::iterator it = inspected_rvh_to_client_host_.begin(); @@ -319,7 +319,7 @@ DevToolsClientHost* DevToolsManager::FindOnwerDevToolsClientHost( } void DevToolsManager::ReopenWindow(RenderViewHost* client_rvh, bool docked) { - DevToolsClientHost* client_host = FindOnwerDevToolsClientHost(client_rvh); + DevToolsClientHost* client_host = FindOwnerDevToolsClientHost(client_rvh); if (!client_host) return; RenderViewHost* inspected_rvh = GetInspectedRenderViewHost(client_host); @@ -415,3 +415,16 @@ void DevToolsManager::UnbindClientHost(RenderViewHost* inspected_rvh, // We've disconnected from the last renderer -> revoke cookie permissions. ChildProcessSecurityPolicy::GetInstance()->RevokeReadRawCookies(process_id); } + +void DevToolsManager::CloseAllClientHosts() { + std::vector<RenderViewHost*> rhvs; + for (InspectedRvhToClientHostMap::iterator it = + inspected_rvh_to_client_host_.begin(); + it != inspected_rvh_to_client_host_.end(); ++it) { + rhvs.push_back(it->first); + } + for (std::vector<RenderViewHost*>::iterator it = rhvs.begin(); + it != rhvs.end(); ++it) { + UnregisterDevToolsClientHostFor(*it); + } +} diff --git a/chrome/browser/debugger/devtools_manager.h b/chrome/browser/debugger/devtools_manager.h index 2012d36..736fb48 100644 --- a/chrome/browser/debugger/devtools_manager.h +++ b/chrome/browser/debugger/devtools_manager.h @@ -85,6 +85,9 @@ class DevToolsManager : public DevToolsClientHost::CloseListener, void AttachClientHost(int client_host_cookie, RenderViewHost* to_rvh); + // Closes all open developer tools windows. + void CloseAllClientHosts(); + private: friend class base::RefCounted<DevToolsManager>; @@ -105,7 +108,7 @@ class DevToolsManager : public DevToolsClientHost::CloseListener, void ForceReopenWindow(); - DevToolsClientHost* FindOnwerDevToolsClientHost(RenderViewHost* client_rvh); + DevToolsClientHost* FindOwnerDevToolsClientHost(RenderViewHost* client_rvh); void ToggleDevToolsWindow(RenderViewHost* inspected_rvh, bool force_open, diff --git a/chrome/browser/debugger/devtools_manager_unittest.cc b/chrome/browser/debugger/devtools_manager_unittest.cc index ddcc54f..a66b986 100644 --- a/chrome/browser/debugger/devtools_manager_unittest.cc +++ b/chrome/browser/debugger/devtools_manager_unittest.cc @@ -71,7 +71,7 @@ class DevToolsManagerTest : public RenderViewHostTestHarness { }; TEST_F(DevToolsManagerTest, OpenAndManuallyCloseDevToolsClientHost) { - scoped_refptr<DevToolsManager> manager = new DevToolsManager(); + scoped_refptr<DevToolsManager> manager(new DevToolsManager()); DevToolsClientHost* host = manager->GetDevToolsClientHostFor(rvh()); EXPECT_TRUE(NULL == host); @@ -95,7 +95,7 @@ TEST_F(DevToolsManagerTest, OpenAndManuallyCloseDevToolsClientHost) { } TEST_F(DevToolsManagerTest, ForwardMessageToClient) { - scoped_refptr<DevToolsManager> manager = new DevToolsManager(); + scoped_refptr<DevToolsManager> manager(new DevToolsManager()); TestDevToolsClientHost client_host; manager->RegisterDevToolsClientHostFor(rvh(), &client_host); diff --git a/chrome/browser/debugger/devtools_netlog_observer.cc b/chrome/browser/debugger/devtools_netlog_observer.cc index 776cc9f..83a7fbb 100644 --- a/chrome/browser/debugger/devtools_netlog_observer.cc +++ b/chrome/browser/debugger/devtools_netlog_observer.cc @@ -53,7 +53,7 @@ void DevToolsNetLogObserver::OnAddEntry(net::NetLog::EventType type, "larger than expected, resetting"; request_to_info_.clear(); } - scoped_refptr<ResourceInfo> new_record = new ResourceInfo(); + scoped_refptr<ResourceInfo> new_record(new ResourceInfo()); request_to_info_.insert(std::make_pair(source.id, new_record)); return; } diff --git a/chrome/browser/debugger/devtools_remote_listen_socket.cc b/chrome/browser/debugger/devtools_remote_listen_socket.cc index bce2ba4..8ea1821 100644 --- a/chrome/browser/debugger/devtools_remote_listen_socket.cc +++ b/chrome/browser/debugger/devtools_remote_listen_socket.cc @@ -209,9 +209,9 @@ void DevToolsRemoteListenSocket::HandleMessage() { void DevToolsRemoteListenSocket::Accept() { SOCKET conn = ListenSocket::Accept(socket_); if (conn != INVALID_SOCKET) { - scoped_refptr<DevToolsRemoteListenSocket> sock = + scoped_refptr<DevToolsRemoteListenSocket> sock( new DevToolsRemoteListenSocket(conn, - message_listener_); + message_listener_)); // it's up to the delegate to AddRef if it wants to keep it around #if defined(OS_POSIX) sock->WatchSocket(WAITING_READ); diff --git a/chrome/browser/debugger/devtools_remote_listen_socket_unittest.cc b/chrome/browser/debugger/devtools_remote_listen_socket_unittest.cc index a3eb3ef..4c1884f 100644 --- a/chrome/browser/debugger/devtools_remote_listen_socket_unittest.cc +++ b/chrome/browser/debugger/devtools_remote_listen_socket_unittest.cc @@ -344,8 +344,7 @@ class DevToolsRemoteListenSocketTest: public PlatformTest { }; // This test is flaky; see comment in ::TestServerSend. -// http://code.google.com/p/chromium/issues/detail?id=48562 -TEST_F(DevToolsRemoteListenSocketTest, FLAKY_ServerSend) { +TEST_F(DevToolsRemoteListenSocketTest, ServerSend) { tester_->TestServerSend(); } diff --git a/chrome/browser/debugger/devtools_sanity_unittest.cc b/chrome/browser/debugger/devtools_sanity_unittest.cc index 3f6393d..a246b3c 100644 --- a/chrome/browser/debugger/devtools_sanity_unittest.cc +++ b/chrome/browser/debugger/devtools_sanity_unittest.cc @@ -273,24 +273,8 @@ IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, MAYBE_TestEnableResourcesTab) { #define MAYBE_TestResourceContentLength TestResourceContentLength #endif // defined(OS_LINUX) -// Tests resources have correct sizes. -IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, MAYBE_TestResourceContentLength) { - RunTest("testResourceContentLength", kResourceContentLengthTestPage); -} - -// Tests resource headers. -IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestResourceHeaders) { - RunTest("testResourceHeaders", kResourceTestPage); -} - -// Tests cached resource mime type. -// @see http://crbug.com/27364 -IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestCachedResourceMimeType) { - RunTest("testCachedResourceMimeType", kResourceTestPage); -} - // Tests profiler panel. -IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestProfilerTab) { +IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, FLAKY_TestProfilerTab) { RunTest("testProfilerTab", kJsPage); } diff --git a/chrome/browser/device_orientation/device_orientation_browsertest.cc b/chrome/browser/device_orientation/device_orientation_browsertest.cc index d33f5d3..6fe92ea 100644 --- a/chrome/browser/device_orientation/device_orientation_browsertest.cc +++ b/chrome/browser/device_orientation/device_orientation_browsertest.cc @@ -49,7 +49,7 @@ class DeviceOrientationBrowserTest : public InProcessBrowserTest { IN_PROC_BROWSER_TEST_F(DeviceOrientationBrowserTest, BasicTest) { const Orientation kTestOrientation(true, 1, true, 2, true, 3); - scoped_refptr<MockProvider> provider = new MockProvider(kTestOrientation); + scoped_refptr<MockProvider> provider(new MockProvider(kTestOrientation)); Provider::SetInstanceForTests(provider.get()); // The test page will register an event handler for orientation events, diff --git a/chrome/browser/device_orientation/orientation.h b/chrome/browser/device_orientation/orientation.h index 2193170..de876c9 100644 --- a/chrome/browser/device_orientation/orientation.h +++ b/chrome/browser/device_orientation/orientation.h @@ -36,7 +36,7 @@ class Orientation { static Orientation Empty() { return Orientation(); } - bool IsEmpty() { + bool IsEmpty() const { return !can_provide_alpha_ && !can_provide_beta_ && !can_provide_gamma_; } diff --git a/chrome/browser/device_orientation/provider_impl.cc b/chrome/browser/device_orientation/provider_impl.cc index 81ecfa3..e5e2397 100644 --- a/chrome/browser/device_orientation/provider_impl.cc +++ b/chrome/browser/device_orientation/provider_impl.cc @@ -99,7 +99,7 @@ void ProviderImpl::ScheduleInitializePollingThread() { polling_loop->PostTask(FROM_HERE, task); } -void ProviderImpl::DoNotify(Orientation orientation) { +void ProviderImpl::DoNotify(const Orientation& orientation) { DCHECK(MessageLoop::current() == creator_loop_); last_notification_ = orientation; @@ -115,7 +115,7 @@ void ProviderImpl::DoNotify(Orientation orientation) { } } -void ProviderImpl::ScheduleDoNotify(Orientation orientation) { +void ProviderImpl::ScheduleDoNotify(const Orientation& orientation) { DCHECK(MessageLoop::current() == polling_thread_->message_loop()); Task* task = NewRunnableMethod(this, &ProviderImpl::DoNotify, orientation); diff --git a/chrome/browser/device_orientation/provider_impl.h b/chrome/browser/device_orientation/provider_impl.h index d984062..7396035 100644 --- a/chrome/browser/device_orientation/provider_impl.h +++ b/chrome/browser/device_orientation/provider_impl.h @@ -52,8 +52,8 @@ class ProviderImpl : public Provider { // Method for notifying observers of an orientation update. // Runs on the creator_thread_. - void DoNotify(Orientation orientation); - void ScheduleDoNotify(Orientation orientation); + void DoNotify(const Orientation& orientation); + void ScheduleDoNotify(const Orientation& orientation); static bool SignificantlyDifferent(const Orientation& orientation1, const Orientation& orientation2); diff --git a/chrome/browser/dom_ui/app_launcher_handler.cc b/chrome/browser/dom_ui/app_launcher_handler.cc index f3309c9..9bc27d2 100644 --- a/chrome/browser/dom_ui/app_launcher_handler.cc +++ b/chrome/browser/dom_ui/app_launcher_handler.cc @@ -44,7 +44,7 @@ bool ExtractInt(const ListValue* list, size_t index, int* out_int) { return false; } -std::string GetIconURL(Extension* extension, Extension::Icons icon, +std::string GetIconURL(const Extension* extension, Extension::Icons icon, const std::string& default_val) { GURL url = extension->GetIconURL(icon, ExtensionIconSet::MATCH_EXACTLY); if (!url.is_empty()) @@ -103,7 +103,7 @@ void AppLauncherHandler::Observe(NotificationType type, } // static -void AppLauncherHandler::CreateAppInfo(Extension* extension, +void AppLauncherHandler::CreateAppInfo(const Extension* extension, ExtensionPrefs* extension_prefs, DictionaryValue* value) { value->Clear(); @@ -202,7 +202,7 @@ void AppLauncherHandler::HandleLaunchApp(const ListValue* args) { dom_ui_->tab_contents()->GetContainerBounds(&tab_contents_bounds); rect.Offset(tab_contents_bounds.origin()); - Extension* extension = + const Extension* extension = extensions_service_->GetExtensionById(extension_id, false); DCHECK(extension); Profile* profile = extensions_service_->profile(); @@ -231,7 +231,7 @@ void AppLauncherHandler::HandleSetLaunchType(const ListValue* args) { return; } - Extension* extension = + const Extension* extension = extensions_service_->GetExtensionById(extension_id, false); DCHECK(extension); @@ -240,7 +240,7 @@ void AppLauncherHandler::HandleSetLaunchType(const ListValue* args) { static_cast<ExtensionPrefs::LaunchType>(launch_type)); } -void AppLauncherHandler::AnimateAppIcon(Extension* extension, +void AppLauncherHandler::AnimateAppIcon(const Extension* extension, const gfx::Rect& rect) { // We make this check for the case of minimized windows, unit tests, etc. if (platform_util::IsVisible(dom_ui_->tab_contents()->GetNativeView()) && @@ -255,7 +255,7 @@ void AppLauncherHandler::AnimateAppIcon(Extension* extension, void AppLauncherHandler::HandleUninstallApp(const ListValue* args) { std::string extension_id = WideToUTF8(ExtractStringValue(args)); - Extension* extension = extensions_service_->GetExtensionById( + const Extension* extension = extensions_service_->GetExtensionById( extension_id, false); if (!extension) return; @@ -295,7 +295,7 @@ void AppLauncherHandler::InstallUIProceed() { // The extension can be uninstalled in another window while the UI was // showing. Do nothing in that case. - Extension* extension = + const Extension* extension = extensions_service_->GetExtensionById(extension_id_prompting_, true); if (!extension) return; diff --git a/chrome/browser/dom_ui/app_launcher_handler.h b/chrome/browser/dom_ui/app_launcher_handler.h index c3ca622..5a3478e 100644 --- a/chrome/browser/dom_ui/app_launcher_handler.h +++ b/chrome/browser/dom_ui/app_launcher_handler.h @@ -42,7 +42,7 @@ class AppLauncherHandler const NotificationDetails& details); // Populate a dictionary with the information from an extension. - static void CreateAppInfo(Extension* extension, + static void CreateAppInfo(const Extension* extension, ExtensionPrefs* extension_prefs, DictionaryValue* value); @@ -75,7 +75,7 @@ class AppLauncherHandler ExtensionInstallUI* GetExtensionInstallUI(); // Starts the animation of the app icon. - void AnimateAppIcon(Extension* extension, const gfx::Rect& rect); + void AnimateAppIcon(const Extension* extension, const gfx::Rect& rect); // The apps are represented in the extensions model. scoped_refptr<ExtensionsService> extensions_service_; diff --git a/chrome/browser/dom_ui/bookmarks_ui_uitest.cc b/chrome/browser/dom_ui/bookmarks_ui_uitest.cc index 3e3ac33..304c9ac 100644 --- a/chrome/browser/dom_ui/bookmarks_ui_uitest.cc +++ b/chrome/browser/dom_ui/bookmarks_ui_uitest.cc @@ -4,7 +4,7 @@ #include "chrome/test/ui/ui_test.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/common/url_constants.h" #include "chrome/test/automation/browser_proxy.h" #include "chrome/test/automation/tab_proxy.h" diff --git a/chrome/browser/dom_ui/bug_report_ui.cc b/chrome/browser/dom_ui/bug_report_ui.cc index 00a2cec..bfe841c 100644 --- a/chrome/browser/dom_ui/bug_report_ui.cc +++ b/chrome/browser/dom_ui/bug_report_ui.cc @@ -22,6 +22,7 @@ #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_thread.h" +#include "chrome/browser/bug_report_data.h" #include "chrome/browser/bug_report_util.h" #include "chrome/browser/dom_ui/dom_ui_screenshot_source.h" #include "chrome/browser/tab_contents/tab_contents.h" @@ -196,39 +197,17 @@ class BugReportHandler : public DOMMessageHandler, void SetupScreenshotsSource(); void ClobberScreenshotsSource(); - void CloseTab(); - void SendReport(); -#if defined(OS_CHROMEOS) - void SyslogsComplete(chromeos::LogDictionaryType* logs, - std::string* zip_content); -#endif + void CancelFeedbackCollection(); + void CloseFeedbackTab(); TabContents* tab_; DOMUIScreenshotSource* screenshot_source_; - // Target tab url. - std::string target_tab_url_; - // Target tab page title. + BugReportData* bug_report_; string16 target_tab_title_; - - // These are filled in by HandleSendReport and used in SendReport. - int problem_type_; - std::string page_url_; - std::string description_; - std::vector<unsigned char> image_; - + std::string target_tab_url_; #if defined(OS_CHROMEOS) - // Chromeos specific values for SendReport. - std::string user_email_; - chromeos::LogDictionaryType* sys_info_; - // Flags to control behavior of SyslogsComplete callback. - bool send_sys_info_; - // NOTE: Extra boolean sent_report_ is required because callback may - // occur before or after we call SendReport(). - bool sent_report_; - // Content of the compressed system logs. - std::string* zip_content_; // Variables to track SyslogsLibrary::RequestSyslogs callback. chromeos::SyslogsLibrary::Handle syslogs_handle_; CancelableRequestConsumer syslogs_consumer_; @@ -367,34 +346,41 @@ void BugReportUIHTMLSource::StartDataRequest(const std::string& path, SendResponse(request_id, html_bytes); } + //////////////////////////////////////////////////////////////////////////////// // -// BugReportHandler +// BugReportData // //////////////////////////////////////////////////////////////////////////////// -BugReportHandler::BugReportHandler(TabContents* tab) - : tab_(tab) - , screenshot_source_(NULL) - , problem_type_(0) +void BugReportData::SendReport() { #if defined(OS_CHROMEOS) - , sys_info_(NULL) - , send_sys_info_(false) - , sent_report_(false) - , zip_content_(NULL) - , syslogs_handle_(0) + // In case we already got the syslogs and sent the report, leave + if (sent_report_) return; + // Set send_report_ so that no one else processes SendReport + sent_report_ = true; #endif -{ -} -BugReportHandler::~BugReportHandler() { + int image_data_size = image_.size(); + char* image_data = image_data_size ? + reinterpret_cast<char*>(&(image_.front())) : NULL; + BugReportUtil::SendReport(profile_ + , UTF16ToUTF8(target_tab_title_) + , problem_type_ + , page_url_ + , description_ + , image_data + , image_data_size + , browser::screen_size.width() + , browser::screen_size.height() +#if defined(OS_CHROMEOS) + , user_email_ + , zip_content_ ? zip_content_->c_str() : NULL + , zip_content_ ? zip_content_->length() : 0 + , send_sys_info_ ? sys_info_ : NULL +#endif + ); + #if defined(OS_CHROMEOS) - // If we requested the syslogs but haven't received them, cancel the request. - if (syslogs_handle_ != 0) { - chromeos::SyslogsLibrary* syslogs_lib = - chromeos::CrosLibrary::Get()->GetSyslogsLibrary(); - if (syslogs_lib) - syslogs_lib->CancelRequest(syslogs_handle_); - } if (sys_info_) { delete sys_info_; sys_info_ = NULL; @@ -404,6 +390,31 @@ BugReportHandler::~BugReportHandler() { zip_content_ = NULL; } #endif + + // Once the report has been sent, this object has no purpose in life, delete + // ourselves. + delete this; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// BugReportHandler +// +//////////////////////////////////////////////////////////////////////////////// +BugReportHandler::BugReportHandler(TabContents* tab) + : tab_(tab) + , screenshot_source_(NULL) +#if defined(OS_CHROMEOS) + , syslogs_handle_(0) +#endif +{ +} + +BugReportHandler::~BugReportHandler() { + // Just in case we didn't send off bug_report_ to SendReport + if (bug_report_) + delete bug_report_; } void BugReportHandler::ClobberScreenshotsSource() { @@ -497,6 +508,8 @@ void BugReportHandler::RegisterMessages() { } void BugReportHandler::HandleGetDialogDefaults(const ListValue*) { + bug_report_ = new BugReportData(); + // send back values which the dialog js needs initially ListValue dialog_defaults; @@ -514,8 +527,8 @@ void BugReportHandler::HandleGetDialogDefaults(const ListValue*) { chromeos::CrosLibrary::Get()->GetSyslogsLibrary(); if (syslogs_lib) { syslogs_handle_ = syslogs_lib->RequestSyslogs( - true, &syslogs_consumer_, - NewCallback(this, &BugReportHandler::SyslogsComplete)); + true, true, &syslogs_consumer_, + NewCallback(bug_report_, &BugReportData::SyslogsComplete)); } // 2: user e-mail dialog_defaults.Append(new StringValue(GetUserEmail())); @@ -549,9 +562,10 @@ void BugReportHandler::HandleSendReport(const ListValue* list_value) { } // #0 - Problem type. + int problem_type; std::string problem_type_str; (*i)->GetAsString(&problem_type_str); - if (!base::StringToInt(problem_type_str, &problem_type_)) { + if (!base::StringToInt(problem_type_str, &problem_type)) { LOG(ERROR) << "Incorrect data passed to sendReport."; return; } @@ -561,14 +575,16 @@ void BugReportHandler::HandleSendReport(const ListValue* list_value) { } // #1 - Page url. - (*i)->GetAsString(&page_url_); + std::string page_url; + (*i)->GetAsString(&page_url); if (++i == list_value->end()) { LOG(ERROR) << "Incorrect data passed to sendReport."; return; } // #2 - Description. - (*i)->GetAsString(&description_); + std::string description; + (*i)->GetAsString(&description); if (++i == list_value->end()) { LOG(ERROR) << "Incorrect data passed to sendReport."; return; @@ -580,8 +596,9 @@ void BugReportHandler::HandleSendReport(const ListValue* list_value) { screenshot_path.erase(0, strlen(kScreenshotBaseUrl)); // Get the image to send in the report. + std::vector<unsigned char> image; if (screenshot_path.size() > 0) { - image_ = screenshot_source_->GetScreenshot(screenshot_path); + image = screenshot_source_->GetScreenshot(screenshot_path); } #if defined(OS_CHROMEOS) @@ -591,7 +608,8 @@ void BugReportHandler::HandleSendReport(const ListValue* list_value) { } // #4 - User e-mail - (*i)->GetAsString(&user_email_); + std::string user_email; + (*i)->GetAsString(&user_email); if (++i == list_value->end()) { LOG(ERROR) << "Incorrect data passed to sendReport."; return; @@ -600,92 +618,70 @@ void BugReportHandler::HandleSendReport(const ListValue* list_value) { // #5 - System info checkbox. std::string sys_info_checkbox; (*i)->GetAsString(&sys_info_checkbox); - send_sys_info_ = (sys_info_checkbox == "true"); + bool send_sys_info = (sys_info_checkbox == "true"); + + // If we aren't sending the sys_info, cancel the gathering of the syslogs. + if (!send_sys_info) + CancelFeedbackCollection(); + + // Update the data in bug_report_ so it can be sent + bug_report_->UpdateData(dom_ui_->GetProfile() + , target_tab_url_ + , target_tab_title_ + , problem_type + , page_url + , description + , image +#if defined(OS_CHROMEOS) + , user_email + , send_sys_info + , false // sent_report +#endif + ); // If we don't require sys_info, or we have it, or we never requested it // (because libcros failed to load), then send the report now. // Otherwise, the report will get sent when we receive sys_info. - if (!send_sys_info_ || sys_info_ != NULL || syslogs_handle_ == 0) { - SendReport(); - // If we scheduled a callback, don't call SendReport() again. - send_sys_info_ = false; + if (!send_sys_info || bug_report_->sys_info() != NULL || + syslogs_handle_ == 0) { + bug_report_->SendReport(); } #else - SendReport(); + bug_report_->SendReport(); #endif + // Lose the pointer to the BugReportData object; the object will delete itself + // from SendReport, whether we called it, or will be called by the log + // completion routine. + bug_report_ = NULL; + + // Whether we sent the report, or if it will be sent by the Syslogs complete + // function, close our feedback tab anyway, we have no more use for it. + CloseFeedbackTab(); } -void BugReportHandler::SendReport() { - int image_data_size = image_.size(); - char* image_data = image_data_size ? - reinterpret_cast<char*>(&(image_.front())) : NULL; - if (!dom_ui_) - return; - BugReportUtil::SendReport(dom_ui_->GetProfile() - , UTF16ToUTF8(target_tab_title_) - , problem_type_ - , page_url_ - , description_ - , image_data - , image_data_size - , browser::screen_size.width() - , browser::screen_size.height() -#if defined(OS_CHROMEOS) - , user_email_ - , zip_content_ ? zip_content_->c_str() : NULL - , zip_content_ ? zip_content_->length() : 0 - , send_sys_info_ ? sys_info_ : NULL -#endif - ); - -#if defined(OS_CHROMEOS) - if (sys_info_) { - delete sys_info_; - sys_info_ = NULL; - } - if (zip_content_) { - delete zip_content_; - zip_content_ = NULL; - } - sent_report_ = true; -#endif - - CloseTab(); +void BugReportHandler::HandleCancel(const ListValue*) { + CancelFeedbackCollection(); + CloseFeedbackTab(); } +void BugReportHandler::HandleOpenSystemTab(const ListValue* args) { #if defined(OS_CHROMEOS) -// Called from the same thread as HandleGetDialogDefaults, i.e. the UI thread. -void BugReportHandler::SyslogsComplete(chromeos::LogDictionaryType* logs, - std::string* zip_content) { - if (sent_report_) { - // We already sent the report, just delete the data. - if (logs) - delete logs; - if (zip_content) - delete zip_content; - } else { - zip_content_ = zip_content; - sys_info_ = logs; // Will get deleted when SendReport() is called. - if (send_sys_info_) { - // We already prepared the report, send it now. - SendReport(); - } - } - syslogs_handle_ = 0; -} + BrowserList::GetLastActive()->OpenSystemTabAndActivate(); #endif - -void BugReportHandler::HandleCancel(const ListValue*) { - CloseTab(); } -void BugReportHandler::HandleOpenSystemTab(const ListValue* args) { +void BugReportHandler::CancelFeedbackCollection() { #if defined(OS_CHROMEOS) - BrowserList::GetLastActive()->OpenSystemTabAndActivate(); + if (syslogs_handle_ != 0) { + chromeos::SyslogsLibrary* syslogs_lib = + chromeos::CrosLibrary::Get()->GetSyslogsLibrary(); + if (syslogs_lib) + syslogs_lib->CancelRequest(syslogs_handle_); + } #endif } -void BugReportHandler::CloseTab() { +void BugReportHandler::CloseFeedbackTab() { Browser* browser = BrowserList::GetLastActive(); if (browser) { browser->CloseTabContents(tab_); diff --git a/chrome/browser/dom_ui/chrome_url_data_manager.cc b/chrome/browser/dom_ui/chrome_url_data_manager.cc index e415699..b793ed8 100644 --- a/chrome/browser/dom_ui/chrome_url_data_manager.cc +++ b/chrome/browser/dom_ui/chrome_url_data_manager.cc @@ -102,6 +102,8 @@ void RegisterURLRequestChromeJob() { } SharedResourcesDataSource::Register(); + URLRequest::RegisterProtocolFactory(chrome::kChromeDevToolsScheme, + &ChromeURLDataManager::Factory); URLRequest::RegisterProtocolFactory(chrome::kChromeUIScheme, &ChromeURLDataManager::Factory); } @@ -118,7 +120,8 @@ void UnregisterURLRequestChromeJob() { void ChromeURLDataManager::URLToRequest(const GURL& url, std::string* source_name, std::string* path) { - DCHECK(url.SchemeIs(chrome::kChromeUIScheme)); + DCHECK(url.SchemeIs(chrome::kChromeDevToolsScheme) || + url.SchemeIs(chrome::kChromeUIScheme)); if (!url.is_valid()) { NOTREACHED(); @@ -266,7 +269,7 @@ void ChromeURLDataManager::DataAvailable( if (i != pending_requests_.end()) { // We acquire a reference to the job so that it doesn't disappear under the // feet of any method invoked here (we could trigger a callback). - scoped_refptr<URLRequestChromeJob> job = i->second; + scoped_refptr<URLRequestChromeJob> job(i->second); pending_requests_.erase(i); job->DataAvailable(bytes); } diff --git a/chrome/browser/dom_ui/conflicts_ui.cc b/chrome/browser/dom_ui/conflicts_ui.cc new file mode 100644 index 0000000..a93257a --- /dev/null +++ b/chrome/browser/dom_ui/conflicts_ui.cc @@ -0,0 +1,213 @@ +// 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/dom_ui/conflicts_ui.h" + +#if defined(OS_WIN) + +#include <string> + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/string_number_conversions.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/dom_ui/chrome_url_data_manager.h" +#include "chrome/browser/enumerate_modules_model_win.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" + +namespace { + +//////////////////////////////////////////////////////////////////////////////// +// +// ConflictsUIHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +class ConflictsUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + ConflictsUIHTMLSource() + : DataSource(chrome::kChromeUIConflictsHost, MessageLoop::current()) {} + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ConflictsUIHTMLSource); +}; + +void ConflictsUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + // Strings used in the JsTemplate file. + DictionaryValue localized_strings; + localized_strings.SetString("modulesLongTitle", + l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_PAGE_TITLE_LONG)); + localized_strings.SetString("modulesBlurb", + l10n_util::GetStringUTF16(IDS_CONFLICTS_EXPLANATION_TEXT)); + localized_strings.SetString("moduleSuspectedBad", + l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_WARNING_SUSPECTED)); + localized_strings.SetString("moduleConfirmedBad", + l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_WARNING_CONFIRMED)); + localized_strings.SetString("helpCenterLink", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HELP_CENTER_LINK)); + localized_strings.SetString("investigatingText", + l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_INVESTIGATING)); + localized_strings.SetString("modulesNoneLoaded", + l10n_util::GetStringUTF16(IDS_CONFLICTS_NO_MODULES_LOADED)); + localized_strings.SetString("headerSoftware", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_SOFTWARE)); + localized_strings.SetString("headerSignedBy", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_SIGNED_BY)); + localized_strings.SetString("headerLocation", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_LOCATION)); + localized_strings.SetString("headerWarning", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_WARNING)); + localized_strings.SetString("headerHelpTip", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_HELP_TIP)); + + ChromeURLDataManager::DataSource::SetFontAndTextDirection(&localized_strings); + + static const base::StringPiece flags_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_ABOUT_CONFLICTS_HTML)); + std::string full_html(flags_html.data(), flags_html.size()); + jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html); + jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html); + jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html); + jstemplate_builder::AppendJsTemplateSourceHtml(&full_html); + + scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); + html_bytes->data.resize(full_html.size()); + std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin()); + + SendResponse(request_id, html_bytes); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ConflictsDOMHandler +// +//////////////////////////////////////////////////////////////////////////////// + +// The handler for Javascript messages for the about:flags page. +class ConflictsDOMHandler : public DOMMessageHandler, + public NotificationObserver { + public: + ConflictsDOMHandler() {} + virtual ~ConflictsDOMHandler() {} + + // DOMMessageHandler implementation. + virtual void RegisterMessages(); + + // Callback for the "requestModuleList" message. + void HandleRequestModuleList(const ListValue* args); + + private: + void SendModuleList(); + + void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(ConflictsDOMHandler); +}; + +void ConflictsDOMHandler::RegisterMessages() { + dom_ui_->RegisterMessageCallback("requestModuleList", + NewCallback(this, &ConflictsDOMHandler::HandleRequestModuleList)); +} + +void ConflictsDOMHandler::HandleRequestModuleList(const ListValue* args) { + // This request is handled asynchronously. See Observe for when we reply back. + registrar_.Add(this, NotificationType::MODULE_LIST_ENUMERATED, + NotificationService::AllSources()); + EnumerateModulesModel::GetSingleton()->ScanNow(); +} + +void ConflictsDOMHandler::SendModuleList() { + EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetSingleton(); + ListValue* list = loaded_modules->GetModuleList(); + DictionaryValue results; + results.Set("moduleList", list); + + // Add the section title and the total count for bad modules found. + int confirmed_bad = loaded_modules->confirmed_bad_modules_detected(); + int suspected_bad = loaded_modules->suspected_bad_modules_detected(); + string16 table_title; + if (!confirmed_bad && !suspected_bad) { + table_title += l10n_util::GetStringFUTF16( + IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_ONE, + base::IntToString16(list->GetSize())); + } else { + table_title += l10n_util::GetStringFUTF16( + IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_TWO, + base::IntToString16(list->GetSize()), + base::IntToString16(confirmed_bad), + base::IntToString16(suspected_bad)); + } + results.SetString("modulesTableTitle", table_title); + + dom_ui_->CallJavascriptFunction(L"returnModuleList", results); +} + +void ConflictsDOMHandler::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::MODULE_LIST_ENUMERATED: + SendModuleList(); + registrar_.RemoveAll(); + break; + default: + NOTREACHED(); + break; + } +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// +// ConflictsUI +// +/////////////////////////////////////////////////////////////////////////////// + +ConflictsUI::ConflictsUI(TabContents* contents) : DOMUI(contents) { + AddMessageHandler((new ConflictsDOMHandler())->Attach(this)); + + ConflictsUIHTMLSource* html_source = new ConflictsUIHTMLSource(); + + // Set up the about:conflicts source. + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod(Singleton<ChromeURLDataManager>::get(), + &ChromeURLDataManager::AddDataSource, + make_scoped_refptr(html_source))); +} + +// static +RefCountedMemory* ConflictsUI::GetFaviconResourceBytes() { + return ResourceBundle::GetSharedInstance(). + LoadDataResourceBytes(IDR_CONFLICTS); +} + +#endif diff --git a/chrome/browser/dom_ui/conflicts_ui.h b/chrome/browser/dom_ui/conflicts_ui.h new file mode 100644 index 0000000..d683a9c --- /dev/null +++ b/chrome/browser/dom_ui/conflicts_ui.h @@ -0,0 +1,28 @@ +// 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_DOM_UI_CONFLICTS_UI_H_ +#define CHROME_BROWSER_DOM_UI_CONFLICTS_UI_H_ +#pragma once + +#include "chrome/browser/dom_ui/dom_ui.h" + +#if defined(OS_WIN) + +class RefCountedMemory; + +// The DOM UI handler for about:conflicts. +class ConflictsUI : public DOMUI { + public: + explicit ConflictsUI(TabContents* contents); + + static RefCountedMemory* GetFaviconResourceBytes(); + + private: + DISALLOW_COPY_AND_ASSIGN(ConflictsUI); +}; + +#endif + +#endif // CHROME_BROWSER_DOM_UI_CONFLICTS_UI_H_ diff --git a/chrome/browser/dom_ui/constrained_html_dialog.cc b/chrome/browser/dom_ui/constrained_html_dialog.cc deleted file mode 100644 index baa3cc6..0000000 --- a/chrome/browser/dom_ui/constrained_html_dialog.cc +++ /dev/null @@ -1,43 +0,0 @@ -// 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/dom_ui/constrained_html_dialog.h" - -#include "chrome/browser/dom_ui/dom_ui_util.h" -#include "chrome/browser/dom_ui/html_dialog_ui.h" -#include "chrome/browser/renderer_host/render_view_host.h" - -ConstrainedHtmlDialog::ConstrainedHtmlDialog(Profile* profile, - HtmlDialogUIDelegate* delegate) - : DOMUI(NULL), - profile_(profile), - render_view_host_(NULL), - html_dialog_ui_delegate_(delegate) { -} - -ConstrainedHtmlDialog::~ConstrainedHtmlDialog() { -} - -void ConstrainedHtmlDialog::InitializeDOMUI(RenderViewHost* render_view_host) { - render_view_host_ = render_view_host; - - std::vector<DOMMessageHandler*> handlers; - html_dialog_ui_delegate_->GetDOMMessageHandlers(&handlers); - render_view_host->SetDOMUIProperty("dialogArguments", - html_dialog_ui_delegate_->GetDialogArgs()); - for (std::vector<DOMMessageHandler*>::iterator it = handlers.begin(); - it != handlers.end(); ++it) { - (*it)->Attach(this); - AddMessageHandler(*it); - } - - // Add a "DialogClosed" callback which matches HTMLDialogUI behavior. - RegisterMessageCallback("DialogClose", - NewCallback(this, &ConstrainedHtmlDialog::OnDialogClosed)); -} - -void ConstrainedHtmlDialog::OnDialogClosed(const ListValue* args) { - html_dialog_ui_delegate()->OnDialogClosed( - dom_ui_util::GetJsonResponseFromFirstArgumentInList(args)); -} diff --git a/chrome/browser/dom_ui/constrained_html_dialog.h b/chrome/browser/dom_ui/constrained_html_dialog.h deleted file mode 100644 index 9eda12a..0000000 --- a/chrome/browser/dom_ui/constrained_html_dialog.h +++ /dev/null @@ -1,60 +0,0 @@ -// 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_DOM_UI_CONSTRAINED_HTML_DIALOG_H_ -#define CHROME_BROWSER_DOM_UI_CONSTRAINED_HTML_DIALOG_H_ -#pragma once - -#include <vector> - -#include "chrome/browser/dom_ui/dom_ui.h" -#include "chrome/browser/tab_contents/constrained_window.h" - -class HtmlDialogUIDelegate; -class Profile; -class RenderViewHost; - -// ConstrainedHtmlDialog is a facility to show HTML DOM_UI content -// in a tab-modal constrained dialog. It is implemented as an adapter -// between an HtmlDialogUI object and a ConstrainedWindow object. -// -// Since ConstrainedWindow requires platform-specific delegate -// implementations, this class is just a factory stub. -class ConstrainedHtmlDialog : public DOMUI { - public: - static ConstrainedHtmlDialog* CreateConstrainedHTMLDialog( - Profile* profile, - HtmlDialogUIDelegate* delegate); - - virtual ConstrainedWindowDelegate* GetConstrainedWindowDelegate() = 0; - - // DOMUI override since this class does not use TabContents. - Profile* GetProfile() const { return profile_; } - - protected: - ConstrainedHtmlDialog(Profile* profile, - HtmlDialogUIDelegate* delegate); - virtual ~ConstrainedHtmlDialog(); - - HtmlDialogUIDelegate* html_dialog_ui_delegate() { - return html_dialog_ui_delegate_; - } - - // DOMUI override since this class does not use TabContents. - RenderViewHost* GetRenderViewHost() const { return render_view_host_; } - - void InitializeDOMUI(RenderViewHost* render_view_host); - - private: - // JS Message Handler - void OnDialogClosed(const ListValue* args); - - Profile* profile_; - RenderViewHost* render_view_host_; - HtmlDialogUIDelegate* html_dialog_ui_delegate_; - - DISALLOW_COPY_AND_ASSIGN(ConstrainedHtmlDialog); -}; - -#endif // CHROME_BROWSER_DOM_UI_CONSTRAINED_HTML_DIALOG_H_ diff --git a/chrome/browser/dom_ui/constrained_html_ui_browsertest.cc b/chrome/browser/dom_ui/constrained_html_ui_browsertest.cc index cc21bfa..a1cd300 100644 --- a/chrome/browser/dom_ui/constrained_html_ui_browsertest.cc +++ b/chrome/browser/dom_ui/constrained_html_ui_browsertest.cc @@ -50,6 +50,7 @@ class TestHtmlDialogUIDelegate : public HtmlDialogUIDelegate { if (out_close_dialog) *out_close_dialog = true; } + virtual bool ShouldShowDialogTitle() const { return true; } }; } // namespace diff --git a/chrome/browser/dom_ui/dom_ui_factory.cc b/chrome/browser/dom_ui/dom_ui_factory.cc index 8ed538f..fbdb42b 100644 --- a/chrome/browser/dom_ui/dom_ui_factory.cc +++ b/chrome/browser/dom_ui/dom_ui_factory.cc @@ -26,6 +26,7 @@ #include "chrome/browser/dom_ui/remoting_ui.h" #include "chrome/browser/dom_ui/options/options_ui.h" #include "chrome/browser/dom_ui/slideshow_ui.h" +#include "chrome/browser/dom_ui/textfields_ui.h" #include "chrome/browser/extensions/extension_dom_ui.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/extensions/extensions_ui.h" @@ -39,6 +40,7 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/dom_ui/imageburner_ui.h" +#include "chrome/browser/chromeos/dom_ui/keyboard_overlay_ui.h" #include "chrome/browser/chromeos/dom_ui/menu_ui.h" #include "chrome/browser/chromeos/dom_ui/mobile_setup_ui.h" #include "chrome/browser/chromeos/dom_ui/register_page_ui.h" @@ -49,6 +51,10 @@ #include "chrome/browser/dom_ui/mediaplayer_ui.h" #endif +#if defined(OS_WIN) +#include "chrome/browser/dom_ui/conflicts_ui.h" +#endif + const DOMUITypeID DOMUIFactory::kNoDOMUI = NULL; // A function for creating a new DOMUI. The caller owns the return value, which @@ -85,6 +91,9 @@ static DOMUIFactoryFunction GetDOMUIFactoryFunction(Profile* profile, if (url.SchemeIs(chrome::kGearsScheme)) return &NewDOMUI<HtmlDialogUI>; + if (url.host() == chrome::kChromeUIDialogHost) + return &NewDOMUI<ConstrainedHtmlUI>; + ExtensionsService* service = profile->GetExtensionsService(); if (service && service->ExtensionBindingsAllowed(url)) return &NewDOMUI<ExtensionDOMUI>; @@ -97,7 +106,8 @@ static DOMUIFactoryFunction GetDOMUIFactoryFunction(Profile* profile, // This will get called a lot to check all URLs, so do a quick check of other // schemes (gears was handled above) to filter out most URLs. - if (!url.SchemeIs(chrome::kChromeInternalScheme) && + if (!url.SchemeIs(chrome::kChromeDevToolsScheme) && + !url.SchemeIs(chrome::kChromeInternalScheme) && !url.SchemeIs(chrome::kChromeUIScheme)) return NULL; @@ -125,8 +135,14 @@ static DOMUIFactoryFunction GetDOMUIFactoryFunction(Profile* profile, return &NewDOMUI<BugReportUI>; if (url.host() == chrome::kChromeUIDevToolsHost) return &NewDOMUI<DevToolsUI>; +#if defined(OS_WIN) + if (url.host() == chrome::kChromeUIConflictsHost) + return &NewDOMUI<ConflictsUI>; +#endif if (url.host() == chrome::kChromeUIDownloadsHost) return &NewDOMUI<DownloadsUI>; + if (url.host() == chrome::kChromeUITextfieldsHost) + return &NewDOMUI<TextfieldsUI>; if (url.host() == chrome::kChromeUIExtensionsHost) return &NewDOMUI<ExtensionsUI>; if (url.host() == chrome::kChromeUIHistoryHost) @@ -157,12 +173,12 @@ static DOMUIFactoryFunction GetDOMUIFactoryFunction(Profile* profile, return &NewDOMUI<FileBrowseUI>; if (url.host() == chrome::kChromeUIImageBurnerHost) return &NewDOMUI<ImageBurnUI>; + if (url.host() == chrome::kChromeUIKeyboardOverlayHost) + return &NewDOMUI<KeyboardOverlayUI>; if (url.host() == chrome::kChromeUIMediaplayerHost) return &NewDOMUI<MediaplayerUI>; if (url.host() == chrome::kChromeUIMobileSetupHost) return &NewDOMUI<MobileSetupUI>; - if (url.host() == chrome::kChromeUIPrintHost) - return &NewDOMUI<PrintPreviewUI>; if (url.host() == chrome::kChromeUIRegisterPageHost) return &NewDOMUI<RegisterPageUI>; if (url.host() == chrome::kChromeUISettingsHost) @@ -206,7 +222,8 @@ DOMUITypeID DOMUIFactory::GetDOMUIType(Profile* profile, const GURL& url) { // static bool DOMUIFactory::HasDOMUIScheme(const GURL& url) { - return url.SchemeIs(chrome::kChromeInternalScheme) || + return url.SchemeIs(chrome::kChromeDevToolsScheme) || + url.SchemeIs(chrome::kChromeInternalScheme) || url.SchemeIs(chrome::kChromeUIScheme) || url.SchemeIs(chrome::kExtensionScheme); } @@ -236,8 +253,8 @@ void DOMUIFactory::GetFaviconForURL(Profile* profile, page_url.host() != extension_misc::kBookmarkManagerId) { ExtensionDOMUI::GetFaviconForURL(profile, request, page_url); } else { - scoped_refptr<RefCountedMemory> icon_data = - DOMUIFactory::GetFaviconResourceBytes(profile, page_url); + scoped_refptr<RefCountedMemory> icon_data( + DOMUIFactory::GetFaviconResourceBytes(profile, page_url)); bool know_icon = icon_data.get() != NULL && icon_data->size() > 0; request->ForwardResultAsync( FaviconService::FaviconDataCallback::TupleType(request->handle(), @@ -262,6 +279,11 @@ RefCountedMemory* DOMUIFactory::GetFaviconResourceBytes(Profile* profile, if (!HasDOMUIScheme(page_url)) return NULL; +#if defined(OS_WIN) + if (page_url.host() == chrome::kChromeUIConflictsHost) + return ConflictsUI::GetFaviconResourceBytes(); +#endif + if (page_url.host() == chrome::kChromeUIDownloadsHost) return DownloadsUI::GetFaviconResourceBytes(); diff --git a/chrome/browser/dom_ui/dom_ui_favicon_source.cc b/chrome/browser/dom_ui/dom_ui_favicon_source.cc index ce32ef9..de5adba 100644 --- a/chrome/browser/dom_ui/dom_ui_favicon_source.cc +++ b/chrome/browser/dom_ui/dom_ui_favicon_source.cc @@ -13,7 +13,7 @@ DOMUIFavIconSource::DOMUIFavIconSource(Profile* profile) : DataSource(chrome::kChromeUIFavIconHost, MessageLoop::current()), - profile_(profile) { + profile_(profile->GetOriginalProfile()) { } DOMUIFavIconSource::~DOMUIFavIconSource() { diff --git a/chrome/browser/dom_ui/dom_ui_theme_source.h b/chrome/browser/dom_ui/dom_ui_theme_source.h index 61f3abf..2d55aaa 100644 --- a/chrome/browser/dom_ui/dom_ui_theme_source.h +++ b/chrome/browser/dom_ui/dom_ui_theme_source.h @@ -13,8 +13,6 @@ class Profile; class RefCountedBytes; -// ThumbnailSource is the gateway between network-level chrome: -// requests for thumbnails and the history backend that serves these. class DOMUIThemeSource : public ChromeURLDataManager::DataSource { public: explicit DOMUIThemeSource(Profile* profile); diff --git a/chrome/browser/dom_ui/dom_ui_thumbnail_source.cc b/chrome/browser/dom_ui/dom_ui_thumbnail_source.cc index f17586f..4ed9d4a 100644 --- a/chrome/browser/dom_ui/dom_ui_thumbnail_source.cc +++ b/chrome/browser/dom_ui/dom_ui_thumbnail_source.cc @@ -10,14 +10,16 @@ #include "chrome/browser/history/top_sites.h" #include "chrome/common/notification_service.h" #include "chrome/common/url_constants.h" -#include "gfx/codec/jpeg_codec.h" #include "googleurl/src/gurl.h" #include "grit/theme_resources.h" -#include "third_party/skia/include/core/SkBitmap.h" DOMUIThumbnailSource::DOMUIThumbnailSource(Profile* profile) : DataSource(chrome::kChromeUIThumbnailPath, MessageLoop::current()), profile_(profile) { + if (history::TopSites::IsEnabled()) { + // Set TopSites now as Profile isn't thread safe. + top_sites_ = profile_->GetTopSites(); + } } DOMUIThumbnailSource::~DOMUIThumbnailSource() { @@ -26,12 +28,11 @@ DOMUIThumbnailSource::~DOMUIThumbnailSource() { void DOMUIThumbnailSource::StartDataRequest(const std::string& path, bool is_off_the_record, int request_id) { - if (history::TopSites::IsEnabled()) { - history::TopSites* top_sites = profile_->GetTopSites(); - RefCountedBytes* data = NULL; - if (top_sites->GetPageThumbnail(GURL(path), &data)) { + if (top_sites_.get()) { + scoped_refptr<RefCountedBytes> data; + if (top_sites_->GetPageThumbnail(GURL(path), &data)) { // We have the thumbnail. - SendResponse(request_id, data); + SendResponse(request_id, data.get()); } else { SendDefaultThumbnail(request_id); } @@ -58,6 +59,12 @@ std::string DOMUIThumbnailSource::GetMimeType(const std::string&) const { return "image/png"; } +MessageLoop* DOMUIThumbnailSource::MessageLoopForRequestPath( + const std::string& path) const { + // TopSites can be accessed from the IO thread. + return top_sites_.get() ? NULL : DataSource::MessageLoopForRequestPath(path); +} + void DOMUIThumbnailSource::SendDefaultThumbnail(int request_id) { // Use placeholder thumbnail. if (!default_thumbnail_.get()) { @@ -71,9 +78,7 @@ void DOMUIThumbnailSource::SendDefaultThumbnail(int request_id) { void DOMUIThumbnailSource::OnThumbnailDataAvailable( HistoryService::Handle request_handle, scoped_refptr<RefCountedBytes> data) { - HistoryService* hs = - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - int request_id = cancelable_consumer_.GetClientData(hs, request_handle); + int request_id = cancelable_consumer_.GetClientDataForCurrentRequest(); // Forward the data along to the networking system. if (data.get() && !data->data.empty()) { SendResponse(request_id, data); diff --git a/chrome/browser/dom_ui/dom_ui_thumbnail_source.h b/chrome/browser/dom_ui/dom_ui_thumbnail_source.h index 3c0578b..8dd3a2d 100644 --- a/chrome/browser/dom_ui/dom_ui_thumbnail_source.h +++ b/chrome/browser/dom_ui/dom_ui_thumbnail_source.h @@ -15,10 +15,13 @@ #include "chrome/common/notification_registrar.h" class Profile; -class ThumbnailStore; -// ThumbnailSource is the gateway between network-level chrome: -// requests for thumbnails and the history backend that serves these. +namespace history { +class TopSites; +} + +// ThumbnailSource is the gateway between network-level chrome: requests for +// thumbnails and the history/top-sites backend that serves these. class DOMUIThumbnailSource : public ChromeURLDataManager::DataSource { public: explicit DOMUIThumbnailSource(Profile* profile); @@ -29,7 +32,9 @@ class DOMUIThumbnailSource : public ChromeURLDataManager::DataSource { bool is_off_the_record, int request_id); - virtual std::string GetMimeType(const std::string&) const; + virtual std::string GetMimeType(const std::string& path) const; + + virtual MessageLoop* MessageLoopForRequestPath(const std::string& path) const; // Called when thumbnail data is available from the history backend. void OnThumbnailDataAvailable(HistoryService::Handle request_handle, @@ -42,14 +47,15 @@ class DOMUIThumbnailSource : public ChromeURLDataManager::DataSource { void SendDefaultThumbnail(int request_id); Profile* profile_; + CancelableRequestConsumerT<int, 0> cancelable_consumer_; // Raw PNG representation of the thumbnail to show when the thumbnail // database doesn't have a thumbnail for a webpage. scoped_refptr<RefCountedMemory> default_thumbnail_; - // To register to be notified when the ThumbnailStore is ready. - NotificationRegistrar registrar_; + // TopSites. If non-null we're using TopSites. + scoped_refptr<history::TopSites> top_sites_; DISALLOW_COPY_AND_ASSIGN(DOMUIThumbnailSource); }; diff --git a/chrome/browser/dom_ui/downloads_dom_handler.cc b/chrome/browser/dom_ui/downloads_dom_handler.cc index e193db3..e94ac0a 100644 --- a/chrome/browser/dom_ui/downloads_dom_handler.cc +++ b/chrome/browser/dom_ui/downloads_dom_handler.cc @@ -159,7 +159,8 @@ void DownloadsDOMHandler::HandleDrag(const ListValue* args) { DownloadItem* file = GetDownloadByValue(args); if (file) { IconManager* im = g_browser_process->icon_manager(); - SkBitmap* icon = im->LookupIcon(file->full_path(), IconLoader::NORMAL); + SkBitmap* icon = im->LookupIcon(file->GetUserVerifiedFileName(), + IconLoader::NORMAL); gfx::NativeView view = dom_ui_->tab_contents()->GetNativeView(); download_util::DragDownload(file, icon, view); } diff --git a/chrome/browser/dom_ui/filebrowse_ui.cc b/chrome/browser/dom_ui/filebrowse_ui.cc index 9a63394..9ea95b3 100644 --- a/chrome/browser/dom_ui/filebrowse_ui.cc +++ b/chrome/browser/dom_ui/filebrowse_ui.cc @@ -7,6 +7,7 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/callback.h" +#include "base/file_util.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/path_service.h" @@ -21,6 +22,7 @@ #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/dom_ui/dom_ui_favicon_source.h" @@ -181,6 +183,16 @@ class FilebrowseHandler : public net::DirectoryLister::DirectoryListerDelegate, void FireUploadComplete(); void SendPicasawebRequest(); + + // Callback for the "validateSavePath" message. + void HandleValidateSavePath(const ListValue* args); + + // Validate a save path on file thread. + void ValidateSavePathOnFileThread(const FilePath& save_path); + + // Fire save path validation result to JS onValidatedSavePath. + void FireOnValidatedSavePathOnUIThread(bool valid, const FilePath& save_path); + private: void OpenNewWindow(const ListValue* args, bool popup); @@ -265,6 +277,17 @@ class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> { handler_->FireCopyComplete(src_, dest_); } } + + void ValidateSavePathOnFileThread() { + if (handler_) + handler_->ValidateSavePathOnFileThread(src_); + } + void FireOnValidatedSavePathOnUIThread(bool valid, + const FilePath& save_path) { + if (handler_) + handler_->FireOnValidatedSavePathOnUIThread(valid, save_path); + } + private: base::WeakPtr<FilebrowseHandler> handler_; FilePath src_; @@ -459,6 +482,8 @@ void FilebrowseHandler::RegisterMessages() { NewCallback(this, &FilebrowseHandler::HandleRefreshDirectory)); dom_ui_->RegisterMessageCallback("isAdvancedEnabled", NewCallback(this, &FilebrowseHandler::HandleIsAdvancedEnabled)); + dom_ui_->RegisterMessageCallback("validateSavePath", + NewCallback(this, &FilebrowseHandler::HandleValidateSavePath)); } @@ -696,14 +721,16 @@ void FilebrowseHandler::OpenNewWindow(const ListValue* args, bool popup) { Browser* browser = popup ? Browser::CreateForType(Browser::TYPE_APP_PANEL, profile_) : BrowserList::GetLastActive(); - Browser::AddTabWithURLParams params(GURL(url), PageTransition::LINK); - browser->AddTabWithURL(¶ms); + browser::NavigateParams params(browser, GURL(url), PageTransition::LINK); + params.disposition = NEW_FOREGROUND_TAB; + browser::Navigate(¶ms); + // TODO(beng): The following two calls should be automatic by Navigate(). if (popup) { // TODO(dhg): Remove these from being hardcoded. Allow javascript // to specify. - params.target->window()->SetBounds(gfx::Rect(0, 0, 400, 300)); + params.browser->window()->SetBounds(gfx::Rect(0, 0, 400, 300)); } - params.target->window()->Show(); + params.browser->window()->Show(); } void FilebrowseHandler::SendPicasawebRequest() { @@ -988,6 +1015,60 @@ void FilebrowseHandler::HandleCopyFile(const ListValue* value) { #endif } +void FilebrowseHandler::HandleValidateSavePath(const ListValue* args) { + std::string string_path; + if (!args || !args->GetString(0, &string_path)) { + FireOnValidatedSavePathOnUIThread(false, FilePath()); // Invalid save path. + return; + } + + FilePath save_path(string_path); + +#if defined(OS_CHROMEOS) + scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), save_path); + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(task.get(), &TaskProxy::ValidateSavePathOnFileThread)); +#else + // No save path checking for non-ChromeOS platforms. + FireOnValidatedSavePathOnUIThread(true, save_path); +#endif +} + +void FilebrowseHandler::ValidateSavePathOnFileThread( + const FilePath& save_path) { +#if defined(OS_CHROMEOS) + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + FilePath default_download_path; + if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, + &default_download_path)) { + NOTREACHED(); + } + + // Get containing folder of save_path. + FilePath save_dir = save_path.DirName(); + + // Valid save path must be inside default download dir. + bool valid = default_download_path == save_dir || + file_util::ContainsPath(default_download_path, save_dir); + + scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), save_path); + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(task.get(), + &TaskProxy::FireOnValidatedSavePathOnUIThread, + valid, save_path)); +#endif +} + +void FilebrowseHandler::FireOnValidatedSavePathOnUIThread(bool valid, + const FilePath& save_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + FundamentalValue valid_value(valid); + StringValue path_value(save_path.value()); + dom_ui_->CallJavascriptFunction(L"onValidatedSavePath", + valid_value, path_value); +} void FilebrowseHandler::OnDownloadUpdated(DownloadItem* download) { DownloadList::iterator it = find(active_download_items_.begin(), @@ -997,9 +1078,9 @@ void FilebrowseHandler::OnDownloadUpdated(DownloadItem* download) { return; const int id = static_cast<int>(it - active_download_items_.begin()); - ListValue results_value; - results_value.Append(download_util::CreateDownloadItemValue(download, id)); - dom_ui_->CallJavascriptFunction(L"downloadUpdated", results_value); + scoped_ptr<DictionaryValue> download_item( + download_util::CreateDownloadItemValue(download, id)); + dom_ui_->CallJavascriptFunction(L"downloadUpdated", *download_item.get()); } void FilebrowseHandler::ClearDownloadItems() { @@ -1061,14 +1142,16 @@ Browser* FileBrowseUI::OpenPopup(Profile* profile, url.append(hashArgument); } - Browser::AddTabWithURLParams params(GURL(url), PageTransition::LINK); - browser->AddTabWithURL(¶ms); - params.target->window()->SetBounds(gfx::Rect(kPopupLeft, - kPopupTop, - width, - height)); + browser::NavigateParams params(browser, GURL(url), PageTransition::LINK); + params.disposition = NEW_FOREGROUND_TAB; + browser::Navigate(¶ms); + // TODO(beng): The following two calls should be automatic by Navigate(). + params.browser->window()->SetBounds(gfx::Rect(kPopupLeft, + kPopupTop, + width, + height)); - params.target->window()->Show(); + params.browser->window()->Show(); } else { browser->window()->Show(); } diff --git a/chrome/browser/dom_ui/fileicon_source.cc b/chrome/browser/dom_ui/fileicon_source.cc index 599c755..7572cdc 100644 --- a/chrome/browser/dom_ui/fileicon_source.cc +++ b/chrome/browser/dom_ui/fileicon_source.cc @@ -43,7 +43,7 @@ void FileIconSource::StartDataRequest(const std::string& path, SkBitmap* icon = im->LookupIcon(escaped_filepath, IconLoader::NORMAL); if (icon) { - scoped_refptr<RefCountedBytes> icon_data = new RefCountedBytes; + scoped_refptr<RefCountedBytes> icon_data(new RefCountedBytes); gfx::PNGCodec::EncodeBGRASkBitmap(*icon, false, &icon_data->data); SendResponse(request_id, icon_data); @@ -65,7 +65,7 @@ void FileIconSource::OnFileIconDataAvailable(IconManager::Handle handle, int request_id = cancelable_consumer_.GetClientData(im, handle); if (icon) { - scoped_refptr<RefCountedBytes> icon_data = new RefCountedBytes; + scoped_refptr<RefCountedBytes> icon_data(new RefCountedBytes); gfx::PNGCodec::EncodeBGRASkBitmap(*icon, false, &icon_data->data); SendResponse(request_id, icon_data); diff --git a/chrome/browser/dom_ui/foreign_session_handler.cc b/chrome/browser/dom_ui/foreign_session_handler.cc index e35d8e5..d0ceabc 100644 --- a/chrome/browser/dom_ui/foreign_session_handler.cc +++ b/chrome/browser/dom_ui/foreign_session_handler.cc @@ -142,7 +142,7 @@ void ForeignSessionHandler::GetForeignSessions( // TODO(zea): sessionTag is per client, it might be better per window. window_data->SetString("sessionTag", - foreign_session->foreign_tession_tag); + foreign_session->foreign_session_tag); window_data->SetInteger("sessionId", entry->id); // Give ownership to |list_value|. diff --git a/chrome/browser/dom_ui/html_dialog_tab_contents_delegate.cc b/chrome/browser/dom_ui/html_dialog_tab_contents_delegate.cc index 1c9c5bb..cc8ae07 100644 --- a/chrome/browser/dom_ui/html_dialog_tab_contents_delegate.cc +++ b/chrome/browser/dom_ui/html_dialog_tab_contents_delegate.cc @@ -5,7 +5,7 @@ #include "chrome/browser/dom_ui/html_dialog_tab_contents_delegate.h" #include "chrome/browser/browser.h" -#include "chrome/browser/browser_window.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tabs/tab_strip_model.h" @@ -27,24 +27,19 @@ void HtmlDialogTabContentsDelegate::Detach() { profile_ = NULL; } -Browser* HtmlDialogTabContentsDelegate::CreateBrowser() { - DCHECK(profile_); - return Browser::Create(profile_); -} - void HtmlDialogTabContentsDelegate::OpenURLFromTab( TabContents* source, const GURL& url, const GURL& referrer, WindowOpenDisposition disposition, PageTransition::Type transition) { if (profile_) { - // Force all links to open in a new window, ignoring the incoming - // disposition. This is a tabless, modal dialog so we can't just - // open it in the current frame. Code adapted from - // Browser::OpenURLFromTab() with disposition == NEW_WINDOW. - Browser* browser = CreateBrowser(); - Browser::AddTabWithURLParams params(url, transition); + // Specify a NULL browser for navigation. This will cause Navigate() + // to find a browser matching params.profile or create a new one. + Browser* browser = NULL; + browser::NavigateParams params(browser, url, transition); + params.profile = profile_; params.referrer = referrer; - browser->AddTabWithURL(¶ms); - browser->window()->Show(); + params.disposition = disposition; + params.show_window = true; + browser::Navigate(¶ms); } } @@ -59,13 +54,16 @@ void HtmlDialogTabContentsDelegate::AddNewContents( WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture) { if (profile_) { - // Force this to open in a new window, too. Code adapted from - // Browser::AddNewContents() with disposition == NEW_WINDOW. - Browser* browser = CreateBrowser(); - static_cast<TabContentsDelegate*>(browser)-> - AddNewContents(source, new_contents, NEW_FOREGROUND_TAB, - initial_pos, user_gesture); - browser->window()->Show(); + // Specify a NULL browser for navigation. This will cause Navigate() + // to find a browser matching params.profile or create a new one. + Browser* browser = NULL; + browser::NavigateParams params(browser, new_contents); + params.profile = profile_; + params.source_contents = source; + params.disposition = disposition; + params.window_bounds = initial_pos; + params.show_window = true; + browser::Navigate(¶ms); } } @@ -110,4 +108,3 @@ bool HtmlDialogTabContentsDelegate::ShouldAddNavigationToHistory( NavigationType::Type navigation_type) { return false; } - diff --git a/chrome/browser/dom_ui/html_dialog_tab_contents_delegate.h b/chrome/browser/dom_ui/html_dialog_tab_contents_delegate.h index b0ab266..c6ec732 100644 --- a/chrome/browser/dom_ui/html_dialog_tab_contents_delegate.h +++ b/chrome/browser/dom_ui/html_dialog_tab_contents_delegate.h @@ -58,13 +58,8 @@ class HtmlDialogTabContentsDelegate : public TabContentsDelegate { const history::HistoryAddPageArgs& add_page_args, NavigationType::Type navigation_type); - protected: - // Overridden only for testing. - virtual Browser* CreateBrowser(); - private: Profile* profile_; // Weak pointer. Always an original profile. }; #endif // CHROME_BROWSER_DOM_UI_HTML_DIALOG_TAB_CONTENTS_DELEGATE_H_ - diff --git a/chrome/browser/dom_ui/html_dialog_tab_contents_delegate_unittest.cc b/chrome/browser/dom_ui/html_dialog_tab_contents_delegate_unittest.cc index ac3ccaa..d49bfdd 100644 --- a/chrome/browser/dom_ui/html_dialog_tab_contents_delegate_unittest.cc +++ b/chrome/browser/dom_ui/html_dialog_tab_contents_delegate_unittest.cc @@ -23,63 +23,18 @@ namespace { -class MockTestBrowserWindow : public TestBrowserWindow { - public: - // TestBrowserWindow() doesn't actually use its browser argument so we just - // pass NULL. - MockTestBrowserWindow() : TestBrowserWindow(NULL) {} - - virtual ~MockTestBrowserWindow() {} - - MOCK_METHOD0(Show, void()); - - private: - DISALLOW_COPY_AND_ASSIGN(MockTestBrowserWindow); -}; - class TestTabContentsDelegate : public HtmlDialogTabContentsDelegate { public: explicit TestTabContentsDelegate(Profile* profile) - : HtmlDialogTabContentsDelegate(profile), - window_for_next_created_browser_(NULL) {} + : HtmlDialogTabContentsDelegate(profile) {} virtual ~TestTabContentsDelegate() { - CHECK(!window_for_next_created_browser_); - for (std::vector<Browser*>::iterator i = created_browsers_.begin(); - i != created_browsers_.end(); ++i) { - (*i)->CloseAllTabs(); - // We need to explicitly cast this since BrowserWindow does *not* - // have a virtual destructor. - delete static_cast<MockTestBrowserWindow*>((*i)->window()); - delete *i; - } } virtual void MoveContents(TabContents* source, const gfx::Rect& pos) {} virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {} - // Takes ownership of window. - void SetWindowForNextCreatedBrowser(BrowserWindow* window) { - CHECK(window); - window_for_next_created_browser_ = window; - } - - protected: - // CreateBrowser() is called by OpenURLFromTab() and AddNewContents(). - virtual Browser* CreateBrowser() { - DCHECK(profile()); - DCHECK(window_for_next_created_browser_); - Browser* browser = new Browser(Browser::TYPE_NORMAL, profile()); - browser->set_window(window_for_next_created_browser_); - window_for_next_created_browser_ = NULL; - created_browsers_.push_back(browser); - return browser; - } - private: - BrowserWindow* window_for_next_created_browser_; - std::vector<Browser*> created_browsers_; - DISALLOW_COPY_AND_ASSIGN(TestTabContentsDelegate); }; @@ -124,28 +79,22 @@ TEST_F(HtmlDialogTabContentsDelegateTest, DoNothingMethodsTest) { } TEST_F(HtmlDialogTabContentsDelegateTest, OpenURLFromTabTest) { - MockTestBrowserWindow* window = new MockTestBrowserWindow(); - EXPECT_CALL(*window, Show()).Times(1); - test_tab_contents_delegate_->SetWindowForNextCreatedBrowser(window); - test_tab_contents_delegate_->OpenURLFromTab( NULL, GURL(chrome::kAboutBlankURL), GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); - EXPECT_EQ(0, browser()->tab_count()); - EXPECT_EQ(2U, BrowserList::size()); + // This should create a new foreground tab in the existing browser. + EXPECT_EQ(1, browser()->tab_count()); + EXPECT_EQ(1U, BrowserList::size()); } -TEST_F(HtmlDialogTabContentsDelegateTest, AddNewContentsTest) { - MockTestBrowserWindow* window = new MockTestBrowserWindow(); - EXPECT_CALL(*window, Show()).Times(1); - test_tab_contents_delegate_->SetWindowForNextCreatedBrowser(window); - +TEST_F(HtmlDialogTabContentsDelegateTest, AddNewContentsForegroundTabTest) { TabContents* contents = new TabContents(profile(), NULL, MSG_ROUTING_NONE, NULL, NULL); test_tab_contents_delegate_->AddNewContents( NULL, contents, NEW_FOREGROUND_TAB, gfx::Rect(), false); - EXPECT_EQ(0, browser()->tab_count()); - EXPECT_EQ(2U, BrowserList::size()); + // This should create a new foreground tab in the existing browser. + EXPECT_EQ(1, browser()->tab_count()); + EXPECT_EQ(1U, BrowserList::size()); } TEST_F(HtmlDialogTabContentsDelegateTest, DetachTest) { @@ -163,4 +112,3 @@ TEST_F(HtmlDialogTabContentsDelegateTest, DetachTest) { } } // namespace - diff --git a/chrome/browser/dom_ui/html_dialog_ui.h b/chrome/browser/dom_ui/html_dialog_ui.h index 38673c4..d1cc849 100644 --- a/chrome/browser/dom_ui/html_dialog_ui.h +++ b/chrome/browser/dom_ui/html_dialog_ui.h @@ -51,6 +51,10 @@ class HtmlDialogUIDelegate { // is set to true, then the dialog is closed. The default is false. virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) = 0; + // A callback to allow the delegate to dictate that the window should not + // have a title bar. This is useful when presenting branded interfaces. + virtual bool ShouldShowDialogTitle() const = 0; + protected: virtual ~HtmlDialogUIDelegate() {} }; diff --git a/chrome/browser/dom_ui/most_visited_handler.cc b/chrome/browser/dom_ui/most_visited_handler.cc index bb10fe7..5b49973 100644 --- a/chrome/browser/dom_ui/most_visited_handler.cc +++ b/chrome/browser/dom_ui/most_visited_handler.cc @@ -135,7 +135,8 @@ void MostVisitedHandler::SendPagesValue() { bool has_blacklisted_urls = !url_blacklist_->empty(); if (history::TopSites::IsEnabled()) { history::TopSites* ts = dom_ui_->GetProfile()->GetTopSites(); - has_blacklisted_urls = ts->HasBlacklistedItems(); + if (ts) + has_blacklisted_urls = ts->HasBlacklistedItems(); } FundamentalValue first_run(IsFirstRun()); FundamentalValue has_blacklisted_urls_value(has_blacklisted_urls); @@ -151,9 +152,11 @@ void MostVisitedHandler::StartQueryForMostVisited() { if (history::TopSites::IsEnabled()) { // Use TopSites. history::TopSites* ts = dom_ui_->GetProfile()->GetTopSites(); - ts->GetMostVisitedURLs( - &topsites_consumer_, - NewCallback(this, &MostVisitedHandler::OnMostVisitedURLsAvailable)); + if (ts) { + ts->GetMostVisitedURLs( + &topsites_consumer_, + NewCallback(this, &MostVisitedHandler::OnMostVisitedURLsAvailable)); + } return; } @@ -195,7 +198,8 @@ void MostVisitedHandler::HandleRemoveURLsFromBlacklist(const ListValue* args) { dom_ui_->GetProfile()); if (history::TopSites::IsEnabled()) { history::TopSites* ts = dom_ui_->GetProfile()->GetTopSites(); - ts->RemoveBlacklistedURL(GURL(url)); + if (ts) + ts->RemoveBlacklistedURL(GURL(url)); return; } @@ -210,7 +214,8 @@ void MostVisitedHandler::HandleClearBlacklist(const ListValue* args) { if (history::TopSites::IsEnabled()) { history::TopSites* ts = dom_ui_->GetProfile()->GetTopSites(); - ts->ClearBlacklistedURLs(); + if (ts) + ts->ClearBlacklistedURLs(); return; } @@ -254,7 +259,8 @@ void MostVisitedHandler::HandleAddPinnedURL(const ListValue* args) { void MostVisitedHandler::AddPinnedURL(const MostVisitedPage& page, int index) { if (history::TopSites::IsEnabled()) { history::TopSites* ts = dom_ui_->GetProfile()->GetTopSites(); - ts->AddPinnedURL(page.url, index); + if (ts) + ts->AddPinnedURL(page.url, index); return; } @@ -283,7 +289,8 @@ void MostVisitedHandler::HandleRemovePinnedURL(const ListValue* args) { void MostVisitedHandler::RemovePinnedURL(const GURL& url) { if (history::TopSites::IsEnabled()) { history::TopSites* ts = dom_ui_->GetProfile()->GetTopSites(); - ts->RemovePinnedURL(url); + if (ts) + ts->RemovePinnedURL(url); return; } @@ -442,14 +449,14 @@ void MostVisitedHandler::SetPagesValueFromTopSites( } history::TopSites* ts = dom_ui_->GetProfile()->GetTopSites(); - if (ts->IsURLPinned(url.url)) + if (ts && ts->IsURLPinned(url.url)) page_value->SetBoolean("pinned", true); pages_value_->Append(page_value); } } void MostVisitedHandler::OnMostVisitedURLsAvailable( - history::MostVisitedURLList data) { + const history::MostVisitedURLList& data) { SetPagesValueFromTopSites(data); if (got_first_most_visited_request_) { SendPagesValue(); @@ -518,7 +525,8 @@ void MostVisitedHandler::Observe(NotificationType type, void MostVisitedHandler::BlacklistURL(const GURL& url) { if (history::TopSites::IsEnabled()) { history::TopSites* ts = dom_ui_->GetProfile()->GetTopSites(); - ts->AddBlacklistedURL(url); + if (ts) + ts->AddBlacklistedURL(url); return; } diff --git a/chrome/browser/dom_ui/most_visited_handler.h b/chrome/browser/dom_ui/most_visited_handler.h index 92aaa2e..e589c66 100644 --- a/chrome/browser/dom_ui/most_visited_handler.h +++ b/chrome/browser/dom_ui/most_visited_handler.h @@ -80,7 +80,7 @@ class MostVisitedHandler : public DOMMessageHandler, void SetPagesValueFromTopSites(const history::MostVisitedURLList& data); // Callback for TopSites. - void OnMostVisitedURLsAvailable(history::MostVisitedURLList data); + void OnMostVisitedURLsAvailable(const history::MostVisitedURLList& data); // Puts the passed URL in the blacklist (so it does not show as a thumbnail). void BlacklistURL(const GURL& url); diff --git a/chrome/browser/dom_ui/new_tab_page_sync_handler.cc b/chrome/browser/dom_ui/new_tab_page_sync_handler.cc index ec74f3c..d79ee91 100644 --- a/chrome/browser/dom_ui/new_tab_page_sync_handler.cc +++ b/chrome/browser/dom_ui/new_tab_page_sync_handler.cc @@ -163,7 +163,8 @@ void NewTabPageSyncHandler::HandleSyncLinkClicked(const ListValue* args) { sync_service_->GetAuthError().state() == GoogleServiceAuthError::ACCOUNT_DISABLED || sync_service_->GetAuthError().state() == - GoogleServiceAuthError::SERVICE_UNAVAILABLE) { + GoogleServiceAuthError::SERVICE_UNAVAILABLE || + sync_service_->observed_passphrase_required()) { sync_service_->ShowLoginDialog(NULL); return; } diff --git a/chrome/browser/dom_ui/new_tab_ui.cc b/chrome/browser/dom_ui/new_tab_ui.cc index b7471b7..a662102 100644 --- a/chrome/browser/dom_ui/new_tab_ui.cc +++ b/chrome/browser/dom_ui/new_tab_ui.cc @@ -627,8 +627,8 @@ void NewTabUI::NewTabHTMLSource::StartDataRequest(const std::string& path, return; } - scoped_refptr<RefCountedBytes> html_bytes = - profile_->GetNTPResourceCache()->GetNewTabHTML(is_off_the_record); + scoped_refptr<RefCountedBytes> html_bytes( + profile_->GetNTPResourceCache()->GetNewTabHTML(is_off_the_record)); SendResponse(request_id, html_bytes); } diff --git a/chrome/browser/dom_ui/new_tab_ui_uitest.cc b/chrome/browser/dom_ui/new_tab_ui_uitest.cc index 7222405..5be42f5 100644 --- a/chrome/browser/dom_ui/new_tab_ui_uitest.cc +++ b/chrome/browser/dom_ui/new_tab_ui_uitest.cc @@ -4,7 +4,7 @@ #include "chrome/test/ui/ui_test.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/dom_ui/new_tab_ui.h" #include "chrome/browser/prefs/pref_value_store.h" diff --git a/chrome/browser/dom_ui/options/about_page_handler.cc b/chrome/browser/dom_ui/options/about_page_handler.cc index cece7a3..8bb2353 100644 --- a/chrome/browser/dom_ui/options/about_page_handler.cc +++ b/chrome/browser/dom_ui/options/about_page_handler.cc @@ -15,6 +15,7 @@ #include "base/time.h" #include "base/utf_string_conversions.h" #include "base/values.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/platform_util.h" #include "chrome/common/notification_service.h" #include "chrome/common/url_constants.h" @@ -33,6 +34,7 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/power_library.h" #include "chrome/browser/chromeos/cros/update_library.h" #endif @@ -66,6 +68,7 @@ const LocalizeEntry localize_table[] = { { "loading", IDS_ABOUT_PAGE_LOADING }, { "check_now", IDS_ABOUT_PAGE_CHECK_NOW }, { "update_status", IDS_UPGRADE_CHECK_STARTED }, + { "restart_now", IDS_RESTART_AND_UPDATE }, #else { "product", IDS_PRODUCT_NAME }, { "check_now", IDS_ABOUT_CHROME_UPDATE_CHECK }, @@ -235,10 +238,14 @@ void AboutPageHandler::GetLocalizedValues(DictionaryValue* localized_strings) { void AboutPageHandler::RegisterMessages() { dom_ui_->RegisterMessageCallback("PageReady", NewCallback(this, &AboutPageHandler::PageReady)); + dom_ui_->RegisterMessageCallback("SetReleaseTrack", + NewCallback(this, &AboutPageHandler::SetReleaseTrack)); #if defined(OS_CHROMEOS) dom_ui_->RegisterMessageCallback("CheckNow", NewCallback(this, &AboutPageHandler::CheckNow)); + dom_ui_->RegisterMessageCallback("RestartNow", + NewCallback(this, &AboutPageHandler::RestartNow)); #endif } @@ -249,9 +256,16 @@ void AboutPageHandler::PageReady(const ListValue* args) { NewCallback(this, &AboutPageHandler::OnOSVersion), true); - update_observer_.reset(new UpdateObserver(this)); chromeos::UpdateLibrary* update_library = chromeos::CrosLibrary::Get()->GetUpdateLibrary(); + + // Update the channel information. + std::string channel = update_library->GetReleaseTrack(); + scoped_ptr<Value> channel_string(Value::CreateStringValue(channel)); + dom_ui_->CallJavascriptFunction(L"AboutPage.updateSelectedOptionCallback", + *channel_string); + + update_observer_.reset(new UpdateObserver(this)); update_library->AddObserver(update_observer_.get()); // Update the DOMUI page with the current status. See comments below. @@ -265,6 +279,13 @@ void AboutPageHandler::PageReady(const ListValue* args) { #endif } +void AboutPageHandler::SetReleaseTrack(const ListValue* args) { +#if defined(OS_CHROMEOS) + const std::string channel = WideToUTF8(ExtractStringValue(args)); + chromeos::CrosLibrary::Get()->GetUpdateLibrary()->SetReleaseTrack(channel); +#endif +} + #if defined(OS_CHROMEOS) void AboutPageHandler::CheckNow(const ListValue* args) { @@ -272,6 +293,10 @@ void AboutPageHandler::CheckNow(const ListValue* args) { chromeos::InitiateUpdateCheck(); } +void AboutPageHandler::RestartNow(const ListValue* args) { + chromeos::CrosLibrary::Get()->GetPowerLibrary()->RequestRestart(); +} + void AboutPageHandler::UpdateStatus( const chromeos::UpdateLibrary::Status& status) { string16 message; @@ -347,6 +372,10 @@ void AboutPageHandler::UpdateStatus( dom_ui_->CallJavascriptFunction(L"AboutPage.setUpdateImage", *image_string); } + // We'll change the "Check For Update" button to "Restart" button. + if (status.status == chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT) { + dom_ui_->CallJavascriptFunction(L"AboutPage.changeToRestartButton"); + } } void AboutPageHandler::OnOSVersion(chromeos::VersionLoader::Handle handle, diff --git a/chrome/browser/dom_ui/options/about_page_handler.h b/chrome/browser/dom_ui/options/about_page_handler.h index 2e4a760..60fefbc 100644 --- a/chrome/browser/dom_ui/options/about_page_handler.h +++ b/chrome/browser/dom_ui/options/about_page_handler.h @@ -25,11 +25,20 @@ class AboutPageHandler : public OptionsPageUIHandler { virtual void RegisterMessages(); private: - + // The function is called from JavaScript when the about page is ready. void PageReady(const ListValue* args); + // The function is called from JavaScript to set the release track like + // "beta-channel" and "dev-channel". + void SetReleaseTrack(const ListValue* args); + #if defined(OS_CHROMEOS) + // Initiates update check. void CheckNow(const ListValue* args); + + // Restarts the system. + void RestartNow(const ListValue* args); + // Callback from chromeos::VersionLoader giving the version. void OnOSVersion(chromeos::VersionLoader::Handle handle, std::string version); diff --git a/chrome/browser/dom_ui/options/advanced_options_handler.cc b/chrome/browser/dom_ui/options/advanced_options_handler.cc index c5b950f..bb7bb88 100644 --- a/chrome/browser/dom_ui/options/advanced_options_handler.cc +++ b/chrome/browser/dom_ui/options/advanced_options_handler.cc @@ -42,6 +42,11 @@ #endif AdvancedOptionsHandler::AdvancedOptionsHandler() { +#if !defined(OS_CHROMEOS) + cloud_print_proxy_ui_enabled_ = + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableCloudPrintProxy); +#endif } AdvancedOptionsHandler::~AdvancedOptionsHandler() { @@ -53,8 +58,6 @@ void AdvancedOptionsHandler::GetLocalizedValues( localized_strings->SetString("privacyLearnMoreURL", l10n_util::GetStringUTF16(IDS_LEARN_MORE_PRIVACY_URL)); - localized_strings->SetString("privacyLearnMoreLabel", - l10n_util::GetStringUTF16(IDS_OPTIONS_LEARN_MORE_LABEL)); localized_strings->SetString("downloadLocationGroupName", l10n_util::GetStringUTF16(IDS_OPTIONS_DOWNLOADLOCATION_GROUP_NAME)); localized_strings->SetString("downloadLocationBrowseButton", @@ -112,6 +115,8 @@ void AdvancedOptionsHandler::GetLocalizedValues( l10n_util::GetStringUTF16(IDS_OPTIONS_TABS_TO_LINKS_PREF)); localized_strings->SetString("fontSettingsInfo", l10n_util::GetStringUTF16(IDS_OPTIONS_FONTSETTINGS_INFO)); + localized_strings->SetString("defaultZoomLevelLabel", + l10n_util::GetStringUTF16(IDS_OPTIONS_DEFAULT_ZOOM_LEVEL_LABEL)); localized_strings->SetString("fontSettingsConfigureFontsOnlyButton", l10n_util::GetStringUTF16( IDS_OPTIONS_FONTSETTINGS_CONFIGUREFONTSONLY_BUTTON)); @@ -138,17 +143,13 @@ void AdvancedOptionsHandler::GetLocalizedValues( localized_strings->SetString("chromeAppsEnableBackgroundMode", l10n_util::GetStringUTF16( IDS_OPTIONS_CHROME_APPS_ENABLE_BACKGROUND_MODE)); - localized_strings->SetString("chromeAppsLearnMoreBackgroundModeLabel", - l10n_util::GetStringUTF16(IDS_OPTIONS_LEARN_MORE_LABEL)); localized_strings->SetString("chromeAppsLearnMoreBackgroundModeURL", l10n_util::GetStringUTF16(IDS_LEARN_MORE_BACKGROUND_MODE_URL)); #if !defined(OS_CHROMEOS) // Add the cloud print proxy management ui section if it's been runtime // enabled. - bool cloud_print_enabled = CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableCloudPrintProxy); localized_strings->SetString("enable-cloud-print-proxy", - cloud_print_enabled ? "true" : "false"); + cloud_print_proxy_ui_enabled_ ? "true" : "false"); localized_strings->SetString("advancedSectionTitleCloudPrint", l10n_util::GetStringUTF16( IDS_OPTIONS_ADVANCED_SECTION_TITLE_CLOUD_PRINT)); @@ -183,6 +184,8 @@ void AdvancedOptionsHandler::GetLocalizedValues( void AdvancedOptionsHandler::Initialize() { DCHECK(dom_ui_); SetupMetricsReportingCheckbox(false); + SetupMetricsReportingSettingVisibility(); + SetupDefaultZoomLevel(); SetupDownloadLocationPath(); SetupAutoOpenFileTypesDisabledAttribute(); SetupProxySettingsSection(); @@ -190,8 +193,10 @@ void AdvancedOptionsHandler::Initialize() { SetupSSLConfigSettings(); #endif #if !defined(OS_CHROMEOS) - SetupCloudPrintProxySection(); - RefreshCloudPrintStatusFromService(); + if (cloud_print_proxy_ui_enabled_) { + SetupCloudPrintProxySection(); + RefreshCloudPrintStatusFromService(); + } #endif banner_handler_.reset( new OptionsManagedBannerHandler(dom_ui_, @@ -215,6 +220,7 @@ DOMMessageHandler* AdvancedOptionsHandler::Attach(DOMUI* dom_ui) { default_download_location_.Init(prefs::kDownloadDefaultDirectory, prefs, this); auto_open_files_.Init(prefs::kDownloadExtensionsToOpen, prefs, this); + default_zoom_level_.Init(prefs::kDefaultZoomLevel, prefs, this); proxy_prefs_.reset( PrefSetObserver::CreateProxyPrefSetObserver(prefs, this)); @@ -234,29 +240,33 @@ void AdvancedOptionsHandler::RegisterMessages() { dom_ui_->RegisterMessageCallback("resetToDefaults", NewCallback(this, &AdvancedOptionsHandler::HandleResetToDefaults)); + dom_ui_->RegisterMessageCallback("defaultZoomLevelAction", + NewCallback(this, &AdvancedOptionsHandler::HandleDefaultZoomLevel)); #if !defined(OS_CHROMEOS) dom_ui_->RegisterMessageCallback("metricsReportingCheckboxAction", NewCallback(this, &AdvancedOptionsHandler::HandleMetricsReportingCheckbox)); #endif -#if !defined(USE_NSS) +#if !defined(USE_NSS) && !defined(USE_OPENSSL) dom_ui_->RegisterMessageCallback("showManageSSLCertificates", NewCallback(this, &AdvancedOptionsHandler::ShowManageSSLCertificates)); #endif #if !defined(OS_CHROMEOS) - dom_ui_->RegisterMessageCallback("showCloudPrintSetupDialog", - NewCallback(this, - &AdvancedOptionsHandler::ShowCloudPrintSetupDialog)); - dom_ui_->RegisterMessageCallback("disableCloudPrintProxy", - NewCallback(this, - &AdvancedOptionsHandler::HandleDisableCloudPrintProxy)); - dom_ui_->RegisterMessageCallback("showCloudPrintManagePage", - NewCallback(this, - &AdvancedOptionsHandler::ShowCloudPrintManagePage)); - dom_ui_->RegisterMessageCallback("showNetworkProxySettings", - NewCallback(this, - &AdvancedOptionsHandler::ShowNetworkProxySettings)); + if (cloud_print_proxy_ui_enabled_) { + dom_ui_->RegisterMessageCallback("showCloudPrintSetupDialog", + NewCallback(this, + &AdvancedOptionsHandler::ShowCloudPrintSetupDialog)); + dom_ui_->RegisterMessageCallback("disableCloudPrintProxy", + NewCallback(this, + &AdvancedOptionsHandler::HandleDisableCloudPrintProxy)); + dom_ui_->RegisterMessageCallback("showCloudPrintManagePage", + NewCallback(this, + &AdvancedOptionsHandler::ShowCloudPrintManagePage)); + dom_ui_->RegisterMessageCallback("showNetworkProxySettings", + NewCallback(this, + &AdvancedOptionsHandler::ShowNetworkProxySettings)); + } #endif #if defined(OS_WIN) @@ -292,7 +302,8 @@ void AdvancedOptionsHandler::Observe(NotificationType type, SetupProxySettingsSection(); } else if (*pref_name == prefs::kCloudPrintEmail) { #if !defined(OS_CHROMEOS) - SetupCloudPrintProxySection(); + if (cloud_print_proxy_ui_enabled_) + SetupCloudPrintProxySection(); #endif } } @@ -319,7 +330,8 @@ void AdvancedOptionsHandler::FileSelected(const FilePath& path, int index, void AdvancedOptionsHandler::OnDialogClosed() { #if !defined(OS_CHROMEOS) - SetupCloudPrintProxySection(); + if (cloud_print_proxy_ui_enabled_) + SetupCloudPrintProxySection(); #endif } @@ -351,6 +363,14 @@ void AdvancedOptionsHandler::HandleMetricsReportingCheckbox( #endif } +void AdvancedOptionsHandler::HandleDefaultZoomLevel(const ListValue* args) { + UserMetricsRecordAction(UserMetricsAction("Options_ChangeDefaultZoomLevel")); + int zoom_level; + if (ExtractIntegerValue(args, &zoom_level)) { + default_zoom_level_.SetValue(static_cast<double>(zoom_level)); + } +} + #if defined(OS_WIN) void AdvancedOptionsHandler::HandleCheckRevocationCheckbox( const ListValue* args) { @@ -404,7 +424,7 @@ void AdvancedOptionsHandler::ShowNetworkProxySettings(const ListValue* args) { } #endif -#if !defined(USE_NSS) +#if !defined(USE_NSS) && !defined(USE_OPENSSL) void AdvancedOptionsHandler::ShowManageSSLCertificates(const ListValue* args) { UserMetricsRecordAction(UserMetricsAction("Options_ManageSSLCertificates")); AdvancedOptionsUtilities::ShowManageSSLCertificates(dom_ui_->tab_contents()); @@ -435,11 +455,19 @@ void AdvancedOptionsHandler::ShowCloudPrintManagePage(const ListValue* args) { void AdvancedOptionsHandler::RefreshCloudPrintStatusFromService() { DCHECK(dom_ui_); - dom_ui_->GetProfile()->GetCloudPrintProxyService()-> - RefreshStatusFromService(); + if (cloud_print_proxy_ui_enabled_) + dom_ui_->GetProfile()->GetCloudPrintProxyService()-> + RefreshStatusFromService(); } void AdvancedOptionsHandler::SetupCloudPrintProxySection() { + if (NULL == dom_ui_->GetProfile()->GetCloudPrintProxyService()) { + cloud_print_proxy_ui_enabled_ = false; + dom_ui_->CallJavascriptFunction( + L"options.AdvancedOptions.HideCloudPrintProxySection"); + return; + } + std::string email; if (dom_ui_->GetProfile()->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail)) email = dom_ui_->GetProfile()->GetPrefs()->GetString( @@ -473,6 +501,25 @@ void AdvancedOptionsHandler::SetupMetricsReportingCheckbox(bool user_changed) { #endif } +void AdvancedOptionsHandler::SetupMetricsReportingSettingVisibility() { +#if defined(GOOGLE_CHROME_BUILD) && defined(OS_CHROMEOS) + // Don't show the reporting setting if we are in the guest mode. + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kGuestSession)) { + FundamentalValue visible(false); + dom_ui_->CallJavascriptFunction( + L"options.AdvancedOptions.SetMetricsReportingSettingVisibility", + visible); + } +#endif +} + +void AdvancedOptionsHandler::SetupDefaultZoomLevel() { + // We're only interested in integer values, so convert to int. + FundamentalValue value(static_cast<int>(default_zoom_level_.GetValue())); + dom_ui_->CallJavascriptFunction( + L"options.AdvancedOptions.SetDefaultZoomLevel", value); +} + void AdvancedOptionsHandler::SetupDownloadLocationPath() { StringValue value(default_download_location_.GetValue().value()); dom_ui_->CallJavascriptFunction( diff --git a/chrome/browser/dom_ui/options/advanced_options_handler.h b/chrome/browser/dom_ui/options/advanced_options_handler.h index 64f1094..58e4df9 100644 --- a/chrome/browser/dom_ui/options/advanced_options_handler.h +++ b/chrome/browser/dom_ui/options/advanced_options_handler.h @@ -58,6 +58,12 @@ class AdvancedOptionsHandler // Callback for the "metricsReportingCheckboxAction" message. This is called // if the user toggles the metrics reporting checkbox. void HandleMetricsReportingCheckbox(const ListValue* args); + + // Callback for the "defaultZoomLevelAction" message. This is called if the + // user changes the default zoom level. |args| is an array that contains + // one item, the zoom level as a numeric value. + void HandleDefaultZoomLevel(const ListValue* args); + #if defined(OS_WIN) // Callback for the "Check SSL Revocation" checkbox. This is needed so we // can support manual handling on Windows. @@ -115,6 +121,11 @@ class AdvancedOptionsHandler // Setup the checked state for the metrics reporting checkbox. void SetupMetricsReportingCheckbox(bool user_changed); + // Setup the visibility for the metrics reporting setting. + void SetupMetricsReportingSettingVisibility(); + + void SetupDefaultZoomLevel(); + // Setup the download path based on user preferences. void SetupDownloadLocationPath(); @@ -134,10 +145,12 @@ class AdvancedOptionsHandler #if !defined(OS_CHROMEOS) BooleanPrefMember enable_metrics_recording_; StringPrefMember cloud_print_proxy_email_; + bool cloud_print_proxy_ui_enabled_; #endif FilePathPrefMember default_download_location_; StringPrefMember auto_open_files_; + RealPrefMember default_zoom_level_; scoped_ptr<PrefSetObserver> proxy_prefs_; scoped_ptr<OptionsManagedBannerHandler> banner_handler_; diff --git a/chrome/browser/dom_ui/options/advanced_options_utils_gtk.cc b/chrome/browser/dom_ui/options/advanced_options_utils_gtk.cc index 094701e..66dbe27 100644 --- a/chrome/browser/dom_ui/options/advanced_options_utils_gtk.cc +++ b/chrome/browser/dom_ui/options/advanced_options_utils_gtk.cc @@ -7,7 +7,6 @@ #include "chrome/browser/dom_ui/options/advanced_options_utils.h" #include "app/gtk_signal.h" -#include "app/gtk_util.h" #include "base/file_util.h" #include "base/environment.h" #include "base/process_util.h" diff --git a/chrome/browser/dom_ui/options/autofill_options_handler.cc b/chrome/browser/dom_ui/options/autofill_options_handler.cc index 808353b..b3cd3ce 100644 --- a/chrome/browser/dom_ui/options/autofill_options_handler.cc +++ b/chrome/browser/dom_ui/options/autofill_options_handler.cc @@ -13,6 +13,7 @@ #include "base/values.h" #include "chrome/browser/autofill/autofill_profile.h" #include "chrome/browser/autofill/credit_card.h" +#include "chrome/browser/guid.h" #include "chrome/browser/profile.h" #include "grit/generated_resources.h" @@ -88,8 +89,8 @@ void AutoFillOptionsHandler::RegisterMessages() { NewCallback(this, &AutoFillOptionsHandler::UpdateCreditCard)); dom_ui_->RegisterMessageCallback( - "editCreditCard", - NewCallback(this, &AutoFillOptionsHandler::EditCreditCard)); + "editCreditCard", + NewCallback(this, &AutoFillOptionsHandler::EditCreditCard)); dom_ui_->RegisterMessageCallback( "removeCreditCard", @@ -142,10 +143,6 @@ void AutoFillOptionsHandler::SetCreditCardOverlayStrings( l10n_util::GetStringUTF16(IDS_AUTOFILL_EDIT_CREDITCARD_CAPTION)); localized_strings->SetString("nameOnCardLabel", l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_NAME_ON_CARD)); - localized_strings->SetString("billingAddressLabel", - l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_BILLING_ADDRESS)); - localized_strings->SetString("chooseExistingAddress", - l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_CHOOSE_EXISTING_ADDRESS)); localized_strings->SetString("creditCardNumberLabel", l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_CREDIT_CARD_NUMBER)); localized_strings->SetString("creditCardExpirationDateLabel", @@ -158,11 +155,11 @@ void AutoFillOptionsHandler::LoadAutoFillData() { ListValue addresses; for (std::vector<AutoFillProfile*>::const_iterator i = - personal_data_->profiles().begin(); - i != personal_data_->profiles().end(); ++i) { + personal_data_->web_profiles().begin(); + i != personal_data_->web_profiles().end(); ++i) { DictionaryValue* address = new DictionaryValue(); - address->SetString("label", (*i)->PreviewSummary()); - address->SetInteger("uniqueID", (*i)->unique_id()); + address->SetString("label", (*i)->Label()); + address->SetString("guid", (*i)->guid()); addresses.Append(address); } @@ -175,7 +172,7 @@ void AutoFillOptionsHandler::LoadAutoFillData() { i != personal_data_->credit_cards().end(); ++i) { DictionaryValue* credit_card = new DictionaryValue(); credit_card->SetString("label", (*i)->PreviewSummary()); - credit_card->SetInteger("uniqueID", (*i)->unique_id()); + credit_card->SetString("guid", (*i)->guid()); credit_cards.Append(credit_card); } @@ -187,14 +184,13 @@ void AutoFillOptionsHandler::UpdateAddress(const ListValue* args) { if (!personal_data_->IsDataLoaded()) return; - int unique_id = 0; - if (!ExtractIntegerValue(args, &unique_id)) { + std::string guid; + if (!args->GetString(0, &guid)) { NOTREACHED(); return; } - AutoFillProfile profile; - profile.set_unique_id(unique_id); + AutoFillProfile profile(guid); string16 value; if (args->GetString(1, &value)) @@ -220,23 +216,25 @@ void AutoFillOptionsHandler::UpdateAddress(const ListValue* args) { if (args->GetString(11, &value)) profile.SetInfo(AutoFillType(EMAIL_ADDRESS), value); - if (unique_id == 0) + if (!guid::IsValidGUID(profile.guid())) { + profile.set_guid(guid::GenerateGUID()); personal_data_->AddProfile(profile); - else + } else { personal_data_->UpdateProfile(profile); + } } void AutoFillOptionsHandler::EditAddress(const ListValue* args) { if (!personal_data_->IsDataLoaded()) return; - int unique_id = 0; - if (!ExtractIntegerValue(args, &unique_id)) { + std::string guid; + if (!args->GetString(0, &guid)) { NOTREACHED(); return; } - AutoFillProfile* profile = personal_data_->GetProfileById(unique_id); + AutoFillProfile* profile = personal_data_->GetProfileByGUID(guid); if (!profile) { NOTREACHED(); return; @@ -246,7 +244,7 @@ void AutoFillOptionsHandler::EditAddress(const ListValue* args) { // directly to CallJavascriptFunction(). ListValue addressList; DictionaryValue* address = new DictionaryValue(); - address->SetInteger("uniqueID", profile->unique_id()); + address->SetString("guid", profile->guid()); address->SetString("fullName", profile->GetFieldText(AutoFillType(NAME_FULL))); address->SetString("companyName", @@ -281,69 +279,57 @@ void AutoFillOptionsHandler::RemoveAddress(const ListValue* args) { if (!personal_data_->IsDataLoaded()) return; - int unique_id = 0; - if (!ExtractIntegerValue(args, &unique_id)) { + std::string guid; + if (!args->GetString(0, &guid)) { NOTREACHED(); return; } - personal_data_->RemoveProfile(unique_id); + personal_data_->RemoveProfile(guid); } void AutoFillOptionsHandler::UpdateCreditCard(const ListValue* args) { if (!personal_data_->IsDataLoaded()) return; - int unique_id = 0; - if (!ExtractIntegerValue(args, &unique_id)) { + std::string guid; + if (!args->GetString(0, &guid)) { NOTREACHED(); return; } - CreditCard credit_card; - credit_card.set_unique_id(unique_id); + CreditCard credit_card(guid); string16 value; if (args->GetString(1, &value)) credit_card.SetInfo(AutoFillType(CREDIT_CARD_NAME), value); - if (args->GetString(2, &value)) { - int id = 0; - base::StringToInt(value, &id); - credit_card.set_billing_address_id(id); - } - if (args->GetString(3, &value)) + if (args->GetString(2, &value)) credit_card.SetInfo(AutoFillType(CREDIT_CARD_NUMBER), value); - if (args->GetString(4, &value)) + if (args->GetString(3, &value)) credit_card.SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), value); - if (args->GetString(5, &value)) + if (args->GetString(4, &value)) credit_card.SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), value); - if (unique_id == 0) + if (!guid::IsValidGUID(credit_card.guid())) { + credit_card.set_guid(guid::GenerateGUID()); personal_data_->AddCreditCard(credit_card); - else + } else { personal_data_->UpdateCreditCard(credit_card); + } + } void AutoFillOptionsHandler::EditCreditCard(const ListValue* args) { if (!personal_data_->IsDataLoaded()) return; - int unique_id = 0; - if (!ExtractIntegerValue(args, &unique_id)) { + std::string guid; + if (!args->GetString(0, &guid)) { NOTREACHED(); return; } - // TODO(jhawkins): Refactor and move this into PersonalDataManager. - CreditCard* credit_card = NULL; - for (std::vector<CreditCard*>::const_iterator iter = - personal_data_->credit_cards().begin(); - iter != personal_data_->credit_cards().end(); ++iter) { - if ((*iter)->unique_id() == unique_id) { - credit_card = *iter; - break; - } - } + CreditCard* credit_card = personal_data_->GetCreditCardByGUID(guid); if (!credit_card) { NOTREACHED(); @@ -354,12 +340,10 @@ void AutoFillOptionsHandler::EditCreditCard(const ListValue* args) { // directly to CallJavascriptFunction(). ListValue credit_card_list; DictionaryValue* credit_card_data = new DictionaryValue(); - credit_card_data->SetInteger("uniqueID", credit_card->unique_id()); + credit_card_data->SetString("guid", credit_card->guid()); credit_card_data->SetString( "nameOnCard", credit_card->GetFieldText(AutoFillType(CREDIT_CARD_NAME))); - credit_card_data->SetInteger( - "billingAddress", credit_card->billing_address_id()); credit_card_data->SetString( "creditCardNumber", credit_card->GetFieldText(AutoFillType(CREDIT_CARD_NUMBER))); @@ -379,11 +363,11 @@ void AutoFillOptionsHandler::RemoveCreditCard(const ListValue* args) { if (!personal_data_->IsDataLoaded()) return; - int unique_id = 0; - if (!ExtractIntegerValue(args, &unique_id)) { + std::string guid; + if (!args->GetString(0, &guid)) { NOTREACHED(); return; } - personal_data_->RemoveCreditCard(unique_id); + personal_data_->RemoveCreditCard(guid); } diff --git a/chrome/browser/dom_ui/options/browser_options_handler.cc b/chrome/browser/dom_ui/options/browser_options_handler.cc index a46adfe..63acf0f 100644 --- a/chrome/browser/dom_ui/options/browser_options_handler.cc +++ b/chrome/browser/dom_ui/options/browser_options_handler.cc @@ -15,6 +15,7 @@ #include "chrome/browser/custom_home_pages_table_model.h" #include "chrome/browser/dom_ui/dom_ui_favicon_source.h" #include "chrome/browser/dom_ui/options/options_managed_banner_handler.h" +#include "chrome/browser/instant/instant_confirm_dialog.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/options_window.h" @@ -71,6 +72,16 @@ void BrowserOptionsHandler::GetLocalizedValues( l10n_util::GetStringUTF16(IDS_OPTIONS_DEFAULTSEARCH_GROUP_NAME)); localized_strings->SetString("defaultSearchManageEnginesLink", l10n_util::GetStringUTF16(IDS_OPTIONS_DEFAULTSEARCH_MANAGE_ENGINES_LINK)); + localized_strings->SetString("instantName", + l10n_util::GetStringUTF16(IDS_INSTANT_PREF)); + localized_strings->SetString("instantWarningText", + l10n_util::GetStringUTF16(IDS_INSTANT_PREF_WARNING)); + localized_strings->SetString("instantLearnMoreLink", + ASCIIToUTF16(browser::kInstantLearnMoreURL)); + localized_strings->SetString("instantConfirmTitle", + l10n_util::GetStringUTF16(IDS_INSTANT_OPT_IN_TITLE)); + localized_strings->SetString("instantConfirmMessage", + l10n_util::GetStringUTF16(IDS_INSTANT_OPT_IN_MESSAGE)); localized_strings->SetString("defaultBrowserGroupName", l10n_util::GetStringUTF16(IDS_OPTIONS_DEFAULTBROWSER_GROUP_NAME)); localized_strings->SetString("defaultBrowserUnknown", diff --git a/chrome/browser/dom_ui/options/certificate_manager_handler.cc b/chrome/browser/dom_ui/options/certificate_manager_handler.cc index bf30674..b5d684a 100644 --- a/chrome/browser/dom_ui/options/certificate_manager_handler.cc +++ b/chrome/browser/dom_ui/options/certificate_manager_handler.cc @@ -25,6 +25,7 @@ namespace { static const char kKeyId[] = "id"; static const char kSubNodesId[] = "subnodes"; static const char kNameId[] = "name"; +static const char kReadOnlyId[] = "readonly"; static const char kIconId[] = "icon"; static const char kSecurityDeviceId[] = "device"; static const char kErrorId[] = "error"; @@ -249,9 +250,6 @@ void CertificateManagerHandler::GetLocalizedValues( // Tabs. localized_strings->SetString("personalCertsTabTitle", l10n_util::GetStringUTF16(IDS_CERT_MANAGER_PERSONAL_CERTS_TAB_LABEL)); - localized_strings->SetString("emailCertsTabTitle", - l10n_util::GetStringUTF16( - IDS_CERT_MANAGER_OTHER_PEOPLES_CERTS_TAB_LABEL)); localized_strings->SetString("serverCertsTabTitle", l10n_util::GetStringUTF16(IDS_CERT_MANAGER_SERVER_CERTS_TAB_LABEL)); localized_strings->SetString("caCertsTabTitle", @@ -262,9 +260,6 @@ void CertificateManagerHandler::GetLocalizedValues( // Tab descriptions. localized_strings->SetString("personalCertsTabDescription", l10n_util::GetStringUTF16(IDS_CERT_MANAGER_USER_TREE_DESCRIPTION)); - localized_strings->SetString("emailCertsTabDescription", - l10n_util::GetStringUTF16( - IDS_CERT_MANAGER_OTHER_PEOPLE_TREE_DESCRIPTION)); localized_strings->SetString("serverCertsTabDescription", l10n_util::GetStringUTF16(IDS_CERT_MANAGER_SERVER_TREE_DESCRIPTION)); localized_strings->SetString("caCertsTabDescription", @@ -281,8 +276,6 @@ void CertificateManagerHandler::GetLocalizedValues( l10n_util::GetStringUTF16(IDS_CERT_MANAGER_SERIAL_NUMBER_COLUMN_LABEL)); localized_strings->SetString("certExpiresColumn", l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EXPIRES_COLUMN_LABEL)); - localized_strings->SetString("certEmailColumn", - l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EMAIL_ADDRESS_COLUMN_LABEL)); // Buttons. localized_strings->SetString("view_certificate", @@ -303,11 +296,6 @@ void CertificateManagerHandler::GetLocalizedValues( l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_USER_FORMAT)); localized_strings->SetString("personalCertsTabDeleteImpact", l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_USER_DESCRIPTION)); - // For now, use the "unknown" strings for email certs too. Maybe we should - // just get rid of the email tab. - localized_strings->SetString("emailCertsTabDeleteConfirm", - l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_UNKNOWN_FORMAT)); - localized_strings->SetString("emailCertsTabDeleteImpact", ""); localized_strings->SetString("serverCertsTabDeleteConfirm", l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_SERVER_FORMAT)); localized_strings->SetString("serverCertsTabDeleteImpact", @@ -402,7 +390,6 @@ void CertificateManagerHandler::RegisterMessages() { void CertificateManagerHandler::CertificatesRefreshed() { PopulateTree("personalCertsTab", net::USER_CERT); - PopulateTree("emailCertsTab", net::EMAIL_CERT); PopulateTree("serverCertsTab", net::SERVER_CERT); PopulateTree("caCertsTab", net::CA_CERT); PopulateTree("otherCertsTab", net::UNKNOWN_CERT); @@ -848,6 +835,9 @@ void CertificateManagerHandler::PopulateTree(const std::string& tab_name, cert_dict->SetString(kKeyId, CertToId(*cert)); cert_dict->SetString(kNameId, certificate_manager_model_->GetColumnText( *cert, CertificateManagerModel::COL_SUBJECT_NAME)); + cert_dict->SetBoolean( + kReadOnlyId, + certificate_manager_model_->cert_db().IsReadOnly(cert)); // TODO(mattm): Other columns. cert_dict->SetString(kIconId, "none"); subnodes->Append(cert_dict); diff --git a/chrome/browser/dom_ui/options/content_settings_handler.cc b/chrome/browser/dom_ui/options/content_settings_handler.cc index 8ba0982..bfb463d 100644 --- a/chrome/browser/dom_ui/options/content_settings_handler.cc +++ b/chrome/browser/dom_ui/options/content_settings_handler.cc @@ -19,6 +19,7 @@ #include "chrome/common/notification_service.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" @@ -309,17 +310,16 @@ void ContentSettingsHandler::Initialize() { this, NotificationType::CONTENT_SETTINGS_CHANGED, Source<const HostContentSettingsMap>(settings_map)); notification_registrar_.Add( - this, NotificationType::GEOLOCATION_DEFAULT_CHANGED, - NotificationService::AllSources()); - notification_registrar_.Add( - this, NotificationType::GEOLOCATION_SETTINGS_CHANGED, - NotificationService::AllSources()); - notification_registrar_.Add( this, NotificationType::DESKTOP_NOTIFICATION_DEFAULT_CHANGED, NotificationService::AllSources()); notification_registrar_.Add( this, NotificationType::DESKTOP_NOTIFICATION_SETTINGS_CHANGED, NotificationService::AllSources()); + + PrefService* prefs = dom_ui_->GetProfile()->GetPrefs(); + pref_change_registrar_.Init(prefs); + pref_change_registrar_.Add(prefs::kGeolocationDefaultContentSetting, this); + pref_change_registrar_.Add(prefs::kGeolocationContentSettings, this); } void ContentSettingsHandler::Observe(NotificationType type, @@ -350,14 +350,12 @@ void ContentSettingsHandler::Observe(NotificationType type, break; } - case NotificationType::GEOLOCATION_DEFAULT_CHANGED: { - UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_GEOLOCATION); - break; - } - - case NotificationType::GEOLOCATION_SETTINGS_CHANGED: { - UpdateGeolocationExceptionsView(); - break; + case NotificationType::PREF_CHANGED: { + const std::string& pref_name = *Details<std::string>(details).ptr(); + if (pref_name == prefs::kGeolocationDefaultContentSetting) + UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_GEOLOCATION); + else if (pref_name == prefs::kGeolocationContentSettings) + UpdateGeolocationExceptionsView(); } case NotificationType::DESKTOP_NOTIFICATION_DEFAULT_CHANGED: { diff --git a/chrome/browser/dom_ui/options/content_settings_handler.h b/chrome/browser/dom_ui/options/content_settings_handler.h index 7874dae..e18de26 100644 --- a/chrome/browser/dom_ui/options/content_settings_handler.h +++ b/chrome/browser/dom_ui/options/content_settings_handler.h @@ -7,6 +7,7 @@ #pragma once #include "chrome/browser/dom_ui/options/options_ui.h" +#include "chrome/browser/prefs/pref_change_registrar.h" #include "chrome/common/content_settings_types.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" @@ -98,6 +99,7 @@ class ContentSettingsHandler : public OptionsPageUIHandler { // Member variables --------------------------------------------------------- NotificationRegistrar notification_registrar_; + PrefChangeRegistrar pref_change_registrar_; DISALLOW_COPY_AND_ASSIGN(ContentSettingsHandler); }; diff --git a/chrome/browser/dom_ui/options/cookies_view_handler.cc b/chrome/browser/dom_ui/options/cookies_view_handler.cc index 160a8fa..f1eee91 100644 --- a/chrome/browser/dom_ui/options/cookies_view_handler.cc +++ b/chrome/browser/dom_ui/options/cookies_view_handler.cc @@ -157,9 +157,6 @@ void GetCookieTreeNodeDictionary(const CookieTreeNode& node, const BrowsingDataIndexedDBHelper::IndexedDBInfo& indexed_db_info = *node.GetDetailedInfo().indexed_db_info; - dict->SetString(kKeyName, indexed_db_info.database_name.empty() ? - l10n_util::GetStringUTF8(IDS_COOKIES_WEB_DATABASE_UNNAMED_NAME) : - indexed_db_info.database_name); dict->SetString(kKeyOrigin, indexed_db_info.origin); dict->SetString(kKeySize, FormatBytes(indexed_db_info.size, @@ -234,8 +231,6 @@ void CookiesViewHandler::GetLocalizedValues( l10n_util::GetStringUTF16(IDS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL)); localized_strings->SetString("label_local_storage_origin", l10n_util::GetStringUTF16(IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL)); - localized_strings->SetString("label_indexed_db_name", - l10n_util::GetStringUTF16(IDS_COOKIES_COOKIE_NAME_LABEL)); localized_strings->SetString("label_indexed_db_size", l10n_util::GetStringUTF16(IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL)); localized_strings->SetString("label_indexed_db_last_modified", diff --git a/chrome/browser/dom_ui/options/import_data_handler.cc b/chrome/browser/dom_ui/options/import_data_handler.cc index d57e616..fe60703 100644 --- a/chrome/browser/dom_ui/options/import_data_handler.cc +++ b/chrome/browser/dom_ui/options/import_data_handler.cc @@ -6,13 +6,14 @@ #include "app/l10n_util.h" #include "base/basictypes.h" +#include "base/callback.h" #include "base/scoped_ptr.h" #include "base/string16.h" #include "base/string_number_conversions.h" #include "base/string_util.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "base/values.h" -#include "base/callback.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" #include "grit/chromium_strings.h" @@ -52,6 +53,13 @@ void ImportDataHandler::GetLocalizedValues( void ImportDataHandler::Initialize() { importer_list_.reset(new ImporterList); + + // The ImporterHost object creates an ImporterList, which calls PathExists + // one or more times. Because we are currently in the UI thread, this will + // trigger a DCHECK due to IO being done on the UI thread. For now we will + // supress the DCHECK. See the following bug for more detail: + // http://crbug.com/60825 + base::ThreadRestrictions::ScopedAllowIO allow_io; importer_list_->DetectSourceProfiles(); int profiles_count = importer_list_->GetAvailableProfileCount(); @@ -123,10 +131,17 @@ void ImportDataHandler::ImportData(const ListValue* args) { dom_ui_->CallJavascriptFunction( L"ImportDataOverlay.setImportingState", state); -// TODO(csilv): Out-of-process import has only been qualified on MacOS X, -// so we will only use it on that platform since it is required. Remove this -// conditional logic once oop import is qualified for Linux/Windows. -// http://crbug.com/22142 + // The ImporterHost object creates an ImporterList, which calls PathExists + // one or more times. Because we are currently in the UI thread, this will + // trigger a DCHECK due to IO being done on the UI thread. For now we will + // supress the DCHECK. See the following bug for more detail: + // http://crbug.com/60825 + base::ThreadRestrictions::ScopedAllowIO allow_io; + + // TODO(csilv): Out-of-process import has only been qualified on MacOS X, + // so we will only use it on that platform since it is required. Remove this + // conditional logic once oop import is qualified for Linux/Windows. + // http://crbug.com/22142 #if defined(OS_MACOSX) importer_host_ = new ExternalProcessImporterHost; #else diff --git a/chrome/browser/dom_ui/options/options_ui_uitest.cc b/chrome/browser/dom_ui/options/options_ui_uitest.cc index 42d00a3..6abad48 100644 --- a/chrome/browser/dom_ui/options/options_ui_uitest.cc +++ b/chrome/browser/dom_ui/options/options_ui_uitest.cc @@ -6,7 +6,7 @@ #include "base/command_line.h" #include "base/string16.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/url_constants.h" #include "chrome/test/automation/browser_proxy.h" @@ -66,7 +66,16 @@ TEST_F(OptionsUITest, CommandOpensOptionsTab) { } // TODO(csilv): Investigate why this fails and fix. http://crbug.com/48521 -TEST_F(OptionsUITest, FLAKY_CommandAgainGoesBackToOptionsTab) { +// Also, crashing on linux/views. +#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) +#define MAYBE_CommandAgainGoesBackToOptionsTab \ + DISABLED_CommandAgainGoesBackToOptionsTab +#else +#define MAYBE_CommandAgainGoesBackToOptionsTab \ + FLAKY_CommandAgainGoesBackToOptionsTab +#endif + +TEST_F(OptionsUITest, MAYBE_CommandAgainGoesBackToOptionsTab) { scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); ASSERT_TRUE(browser.get()); @@ -95,8 +104,14 @@ TEST_F(OptionsUITest, FLAKY_CommandAgainGoesBackToOptionsTab) { } // TODO(csilv): Investigate why this fails (sometimes) on 10.5 and fix. -// http://crbug.com/48521 -TEST_F(OptionsUITest, FLAKY_TwoCommandsOneTab) { +// http://crbug.com/48521. Also, crashing on linux/views. +#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) +#define MAYBE_TwoCommandsOneTab DISABLED_TwoCommandsOneTab +#else +#define MAYBE_TwoCommandsOneTab FLAKY_TwoCommandsOneTab +#endif + +TEST_F(OptionsUITest, MAYBE_TwoCommandsOneTab) { scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); ASSERT_TRUE(browser.get()); diff --git a/chrome/browser/dom_ui/options/passwords_exceptions_handler.cc b/chrome/browser/dom_ui/options/passwords_exceptions_handler.cc index dcb5419..39f77ca 100644 --- a/chrome/browser/dom_ui/options/passwords_exceptions_handler.cc +++ b/chrome/browser/dom_ui/options/passwords_exceptions_handler.cc @@ -167,7 +167,10 @@ void PasswordsExceptionsHandler::SetPasswordExceptionList() { void PasswordsExceptionsHandler::PasswordListPopulater::Populate() { DCHECK(!pending_login_query_); PasswordStore* store = page_->GetPasswordStore(); - pending_login_query_ = store->GetAutofillableLogins(this); + if (store != NULL) + pending_login_query_ = store->GetAutofillableLogins(this); + else + LOG(ERROR) << "No password store! Cannot display passwords."; } void PasswordsExceptionsHandler::PasswordListPopulater:: @@ -182,7 +185,10 @@ void PasswordsExceptionsHandler::PasswordListPopulater:: void PasswordsExceptionsHandler::PasswordExceptionListPopulater::Populate() { DCHECK(!pending_login_query_); PasswordStore* store = page_->GetPasswordStore(); - pending_login_query_ = store->GetBlacklistLogins(this); + if (store != NULL) + pending_login_query_ = store->GetBlacklistLogins(this); + else + LOG(ERROR) << "No password store! Cannot display exceptions."; } void PasswordsExceptionsHandler::PasswordExceptionListPopulater:: diff --git a/chrome/browser/dom_ui/plugins_ui.cc b/chrome/browser/dom_ui/plugins_ui.cc index c049b27..73fdbc0 100644 --- a/chrome/browser/dom_ui/plugins_ui.cc +++ b/chrome/browser/dom_ui/plugins_ui.cc @@ -244,7 +244,7 @@ void PluginsDOMHandler::HandleEnablePluginMessage(const ListValue* args) { // TODO(viettrungluu): We might also want to ensure that the plugins // list is always written to prefs even when the user hasn't disabled a // plugin. <http://crbug.com/39101> - plugin_updater->UpdatePreferences(dom_ui_->GetProfile()); + plugin_updater->UpdatePreferences(dom_ui_->GetProfile(), 0); } void PluginsDOMHandler::HandleShowTermsOfServiceMessage(const ListValue* args) { diff --git a/chrome/browser/dom_ui/print_preview_handler.cc b/chrome/browser/dom_ui/print_preview_handler.cc new file mode 100644 index 0000000..9248a8a --- /dev/null +++ b/chrome/browser/dom_ui/print_preview_handler.cc @@ -0,0 +1,33 @@ +// 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/dom_ui/print_preview_handler.h" + +#include "base/values.h" +#include "printing/backend/print_backend.h" + +PrintPreviewHandler::PrintPreviewHandler() + : print_backend_(printing::PrintBackend::CreateInstance(NULL)) { +} + +PrintPreviewHandler::~PrintPreviewHandler() { +} + +void PrintPreviewHandler::RegisterMessages() { + dom_ui_->RegisterMessageCallback("getPrinters", + NewCallback(this, &PrintPreviewHandler::HandleGetPrinters)); +} + +void PrintPreviewHandler::HandleGetPrinters(const ListValue*) { + ListValue printers; + + printing::PrinterList printer_list; + print_backend_->EnumeratePrinters(&printer_list); + for (printing::PrinterList::iterator index = printer_list.begin(); + index != printer_list.end(); ++index) { + printers.Append(new StringValue(index->printer_name)); + } + + dom_ui_->CallJavascriptFunction(L"setPrinters", printers); +} diff --git a/chrome/browser/dom_ui/print_preview_handler.h b/chrome/browser/dom_ui/print_preview_handler.h new file mode 100644 index 0000000..edcb8e6 --- /dev/null +++ b/chrome/browser/dom_ui/print_preview_handler.h @@ -0,0 +1,37 @@ +// 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_DOM_UI_PRINT_PREVIEW_HANDLER_H_ +#define CHROME_BROWSER_DOM_UI_PRINT_PREVIEW_HANDLER_H_ +#pragma once + +#include "base/scoped_ptr.h" +#include "base/weak_ptr.h" +#include "chrome/browser/dom_ui/dom_ui.h" + +namespace printing { +class PrintBackend; +} // namespace printing + +// The handler for Javascript messages related to the "print preview" dialog. +class PrintPreviewHandler : public DOMMessageHandler, + public base::SupportsWeakPtr<PrintPreviewHandler> { + public: + PrintPreviewHandler(); + virtual ~PrintPreviewHandler(); + + // DOMMessageHandler implementation. + virtual void RegisterMessages(); + + private: + // Get the list of printers and send it to the DOM UI. |args| is unused. + void HandleGetPrinters(const ListValue* args); + + // Pointer to current print system. + scoped_refptr<printing::PrintBackend> print_backend_; + + DISALLOW_COPY_AND_ASSIGN(PrintPreviewHandler); +}; + +#endif // CHROME_BROWSER_DOM_UI_PRINT_PREVIEW_HANDLER_H_ diff --git a/chrome/browser/dom_ui/print_preview_ui.cc b/chrome/browser/dom_ui/print_preview_ui.cc index 7c34dfd..a153518 100644 --- a/chrome/browser/dom_ui/print_preview_ui.cc +++ b/chrome/browser/dom_ui/print_preview_ui.cc @@ -13,7 +13,9 @@ #include "base/string_piece.h" #include "base/values.h" #include "chrome/browser/browser_thread.h" +#include "chrome/browser/dom_ui/chrome_url_data_manager.h" #include "chrome/browser/dom_ui/dom_ui_theme_source.h" +#include "chrome/browser/dom_ui/print_preview_handler.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/url_constants.h" @@ -27,6 +29,8 @@ namespace { void SetLocalizedStrings(DictionaryValue* localized_strings) { localized_strings->SetString(std::string("title"), l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_TITLE)); + localized_strings->SetString(std::string("no-printer"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_NO_PRINTER)); } } // namespace @@ -37,6 +41,22 @@ void SetLocalizedStrings(DictionaryValue* localized_strings) { // //////////////////////////////////////////////////////////////////////////////// +class PrintPreviewUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + PrintPreviewUIHTMLSource(); + virtual ~PrintPreviewUIHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const; + + private: + DISALLOW_COPY_AND_ASSIGN(PrintPreviewUIHTMLSource); +}; + PrintPreviewUIHTMLSource::PrintPreviewUIHTMLSource() : DataSource(chrome::kChromeUIPrintHost, MessageLoop::current()) { } @@ -74,6 +94,10 @@ std::string PrintPreviewUIHTMLSource::GetMimeType(const std::string&) const { //////////////////////////////////////////////////////////////////////////////// PrintPreviewUI::PrintPreviewUI(TabContents* contents) : DOMUI(contents) { + // PrintPreviewUI owns |handler|. + PrintPreviewHandler* handler = new PrintPreviewHandler(); + AddMessageHandler(handler->Attach(this)); + // Set up the chrome://print/ source. BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, diff --git a/chrome/browser/dom_ui/print_preview_ui.h b/chrome/browser/dom_ui/print_preview_ui.h index 5884463..5745856 100644 --- a/chrome/browser/dom_ui/print_preview_ui.h +++ b/chrome/browser/dom_ui/print_preview_ui.h @@ -8,27 +8,8 @@ #include <string> -#include "chrome/browser/dom_ui/chrome_url_data_manager.h" #include "chrome/browser/dom_ui/dom_ui.h" -struct UserMetricsAction; - -class PrintPreviewUIHTMLSource : public ChromeURLDataManager::DataSource { - public: - explicit PrintPreviewUIHTMLSource(); - virtual ~PrintPreviewUIHTMLSource(); - - // Called when the network layer has requested a resource underneath - // the path we registered. - virtual void StartDataRequest(const std::string& path, - bool is_off_the_record, - int request_id); - virtual std::string GetMimeType(const std::string&) const; - - private: - DISALLOW_COPY_AND_ASSIGN(PrintPreviewUIHTMLSource); -}; - class PrintPreviewUI : public DOMUI { public: explicit PrintPreviewUI(TabContents* contents); diff --git a/chrome/browser/dom_ui/shared_resources_data_source.cc b/chrome/browser/dom_ui/shared_resources_data_source.cc index 43257a2..2c51470 100644 --- a/chrome/browser/dom_ui/shared_resources_data_source.cc +++ b/chrome/browser/dom_ui/shared_resources_data_source.cc @@ -6,6 +6,7 @@ #include "app/resource_bundle.h" #include "base/singleton.h" +#include "base/thread_restrictions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/dom_ui/chrome_url_data_manager.h" @@ -79,6 +80,11 @@ void SharedResourcesDataSource::StartDataRequest(const std::string& path, std::string SharedResourcesDataSource::GetMimeType( const std::string& path) const { + // Requests should not block on the disk! On Windows this goes to the + // registry. + // http://code.google.com/p/chromium/issues/detail?id=59849 + base::ThreadRestrictions::ScopedAllowIO allow_io; + std::string mime_type; net::GetMimeTypeFromFile(FilePath().AppendASCII(path), &mime_type); return mime_type; diff --git a/chrome/browser/dom_ui/shown_sections_handler.cc b/chrome/browser/dom_ui/shown_sections_handler.cc index 381ea32..189c1c3 100644 --- a/chrome/browser/dom_ui/shown_sections_handler.cc +++ b/chrome/browser/dom_ui/shown_sections_handler.cc @@ -14,6 +14,7 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/notification_details.h" +#include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" @@ -49,11 +50,14 @@ int ShownSectionsHandler::GetShownSections(PrefService* prefs) { ShownSectionsHandler::ShownSectionsHandler(PrefService* pref_service) : pref_service_(pref_service) { - registrar_.Init(pref_service); - registrar_.Add(prefs::kNTPShownSections, this); + pref_registrar_.Init(pref_service); + pref_registrar_.Add(prefs::kNTPShownSections, this); } void ShownSectionsHandler::RegisterMessages() { + notification_registrar_.Add(this, NotificationType::EXTENSION_INSTALLED, + Source<Profile>(dom_ui_->GetProfile())); + dom_ui_->RegisterMessageCallback("getShownSections", NewCallback(this, &ShownSectionsHandler::HandleGetShownSections)); dom_ui_->RegisterMessageCallback("setShownSections", @@ -63,13 +67,30 @@ void ShownSectionsHandler::RegisterMessages() { void ShownSectionsHandler::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - DCHECK(NotificationType::PREF_CHANGED == type); - std::string* pref_name = Details<std::string>(details).ptr(); - DCHECK(*pref_name == prefs::kNTPShownSections); - - int sections = pref_service_->GetInteger(prefs::kNTPShownSections); - FundamentalValue sections_value(sections); - dom_ui_->CallJavascriptFunction(L"setShownSections", sections_value); + if (type == NotificationType::PREF_CHANGED) { + std::string* pref_name = Details<std::string>(details).ptr(); + DCHECK(*pref_name == prefs::kNTPShownSections); + int sections = pref_service_->GetInteger(prefs::kNTPShownSections); + FundamentalValue sections_value(sections); + dom_ui_->CallJavascriptFunction(L"setShownSections", sections_value); + } else if (type == NotificationType::EXTENSION_INSTALLED) { + if (Details<const Extension>(details).ptr()->is_app()) { + int mode = pref_service_->GetInteger(prefs::kNTPShownSections); + + // De-minimize the apps section. + mode &= ~MINIMIZED_APPS; + + // Hide any open sections. + mode &= ~ALL_SECTIONS_MASK; + + // Show the apps section. + mode |= APPS; + + pref_service_->SetInteger(prefs::kNTPShownSections, mode); + } + } else { + NOTREACHED(); + } } void ShownSectionsHandler::HandleGetShownSections(const ListValue* args) { diff --git a/chrome/browser/dom_ui/shown_sections_handler.h b/chrome/browser/dom_ui/shown_sections_handler.h index be07735..e82c3fb 100644 --- a/chrome/browser/dom_ui/shown_sections_handler.h +++ b/chrome/browser/dom_ui/shown_sections_handler.h @@ -8,6 +8,7 @@ #include "chrome/browser/dom_ui/dom_ui.h" #include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" #include "chrome/browser/prefs/pref_change_registrar.h" class DOMUI; @@ -23,6 +24,9 @@ enum Section { THUMB = 1 << 0, APPS = 1 << 6, + // We use the low 16 bits for sections, the high 16 bits for minimized state. + ALL_SECTIONS_MASK = 0x0000FFFF, + // If one of these is set, then the corresponding section is shown minimized // at the bottom of the NTP and no data is directly visible on the NTP. MINIMIZED_THUMB = 1 << (0 + 16), @@ -61,7 +65,8 @@ class ShownSectionsHandler : public DOMMessageHandler, private: PrefService* pref_service_; - PrefChangeRegistrar registrar_; + PrefChangeRegistrar pref_registrar_; + NotificationRegistrar notification_registrar_; DISALLOW_COPY_AND_ASSIGN(ShownSectionsHandler); }; diff --git a/chrome/browser/dom_ui/textfields_ui.cc b/chrome/browser/dom_ui/textfields_ui.cc new file mode 100644 index 0000000..db4fb0c --- /dev/null +++ b/chrome/browser/dom_ui/textfields_ui.cc @@ -0,0 +1,76 @@ +// 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/dom_ui/textfields_ui.h" + +#include <algorithm> +#include <string> + +#include "app/resource_bundle.h" +#include "base/singleton.h" +#include "base/string_piece.h" +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" + +/** + * TextfieldsUIHTMLSource implementation. + */ +TextfieldsUIHTMLSource::TextfieldsUIHTMLSource() + : DataSource(chrome::kChromeUITextfieldsHost, MessageLoop::current()) { +} + +void TextfieldsUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + const std::string full_html = ResourceBundle::GetSharedInstance() + .GetRawDataResource(IDR_TEXTFIELDS_HTML).as_string(); + + scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); + html_bytes->data.resize(full_html.size()); + std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin()); + + SendResponse(request_id, html_bytes); +} + +std::string TextfieldsUIHTMLSource::GetMimeType( + const std::string& /* path */) const { + return "text/html"; +} + +TextfieldsUIHTMLSource::~TextfieldsUIHTMLSource() {} + +/** + * TextfieldsDOMHandler implementation. + */ +TextfieldsDOMHandler::TextfieldsDOMHandler() : DOMMessageHandler() {} + +void TextfieldsDOMHandler::RegisterMessages() { + dom_ui_->RegisterMessageCallback("textfieldValue", + NewCallback(this, &TextfieldsDOMHandler::HandleTextfieldValue)); +} + +void TextfieldsDOMHandler::HandleTextfieldValue(const ListValue* args) { + static_cast<TextfieldsUI*>(dom_ui_)->set_text(ExtractStringValue(args)); +} + +/** + * TextfieldsUI implementation. + */ +TextfieldsUI::TextfieldsUI(TabContents* contents) : DOMUI(contents) { + TextfieldsDOMHandler* handler = new TextfieldsDOMHandler(); + AddMessageHandler(handler); + handler->Attach(this); + + TextfieldsUIHTMLSource* html_source = new TextfieldsUIHTMLSource(); + + // Set up the chrome://textfields/ source. + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod(Singleton<ChromeURLDataManager>::get(), + &ChromeURLDataManager::AddDataSource, + make_scoped_refptr(html_source))); +} diff --git a/chrome/browser/dom_ui/textfields_ui.h b/chrome/browser/dom_ui/textfields_ui.h new file mode 100644 index 0000000..f70d45e --- /dev/null +++ b/chrome/browser/dom_ui/textfields_ui.h @@ -0,0 +1,71 @@ +// 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_DOM_UI_TEXTFIELDS_UI_H_ +#define CHROME_BROWSER_DOM_UI_TEXTFIELDS_UI_H_ +#pragma once + +#include <string> + +#include "chrome/browser/dom_ui/chrome_url_data_manager.h" +#include "chrome/browser/dom_ui/dom_ui.h" + +class RefCountedMemory; + +/** + * ChromeURLDataManager::DataSource implementation that asynchronously answers + * requests for chrome://textfields URL. On receiving a request, this object + * reads the html from the local resource textfields.html and sends back the + * response. + */ +class TextfieldsUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + TextfieldsUIHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string& path) const; + + private: + virtual ~TextfieldsUIHTMLSource(); + + DISALLOW_COPY_AND_ASSIGN(TextfieldsUIHTMLSource); +}; + +/** + * Handler for JavaScript calls from the DOM. + */ +class TextfieldsDOMHandler : public DOMMessageHandler { + public: + TextfieldsDOMHandler(); + + // Handles the "textfieldValue" call from the JavaScript. This call + // synchonizes the value inside the JavaScript textfield with the copy in the + // DOM object. + virtual void HandleTextfieldValue(const ListValue* args); + + protected: + virtual void RegisterMessages(); + + private: + DISALLOW_COPY_AND_ASSIGN(TextfieldsDOMHandler); +}; + +class TextfieldsUI : public DOMUI { + public: + explicit TextfieldsUI(TabContents* contents); + + const std::wstring& text() const { return text_; } + void set_text(const std::wstring& text) { text_ = text; } + + private: + std::wstring text_; + + DISALLOW_COPY_AND_ASSIGN(TextfieldsUI); +}; + +#endif // CHROME_BROWSER_DOM_UI_TEXTFIELDS_UI_H_ diff --git a/chrome/browser/download/download_file_manager.cc b/chrome/browser/download/download_file_manager.cc index 8415c87..86346a8 100644 --- a/chrome/browser/download/download_file_manager.cc +++ b/chrome/browser/download/download_file_manager.cc @@ -176,7 +176,7 @@ void DownloadFileManager::StartDownload(DownloadCreateInfo* info) { BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, NewRunnableMethod(this, &DownloadFileManager::CreateDownloadFile, - info, manager)); + info, make_scoped_refptr(manager))); } // We don't forward an update to the UI thread here, since we want to throttle @@ -286,7 +286,7 @@ void DownloadFileManager::OnIntermediateDownloadName( return; DownloadFile* download = it->second; - if (!download->Rename(full_path, false)) { + if (!download->Rename(full_path, false /* is_final_rename */)) { // Error. Between the time the UI thread generated 'full_path' to the time // this code runs, something happened that prevents us from renaming. CancelDownloadOnRename(id); @@ -303,17 +303,16 @@ void DownloadFileManager::OnIntermediateDownloadName( // 1. tmp -> foo (need_delete_crdownload=T) // 2. foo.crdownload -> foo (need_delete_crdownload=F) // 3. tmp-> unconfirmed.xxx.crdownload (need_delete_crdownload=F) -void DownloadFileManager::OnFinalDownloadName(int id, - const FilePath& full_path, - bool need_delete_crdownload, - DownloadManager* manager) { +void DownloadFileManager::OnFinalDownloadName( + int id, const FilePath& full_path, bool need_delete_crdownload, + DownloadManager* download_manager) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); DownloadFile* download = GetDownloadFile(id); if (!download) return; - if (download->Rename(full_path, true)) { + if (download->Rename(full_path, true /* is_final_rename */)) { #if defined(OS_MACOSX) // Done here because we only want to do this once; see // http://crbug.com/13120 for details. @@ -322,7 +321,7 @@ void DownloadFileManager::OnFinalDownloadName(int id, BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod( - manager, &DownloadManager::DownloadRenamedToFinalName, id, + download_manager, &DownloadManager::DownloadRenamedToFinalName, id, full_path)); } else { // Error. Between the time the UI thread generated 'full_path' to the time diff --git a/chrome/browser/download/download_item.cc b/chrome/browser/download/download_item.cc index df5edcc..7b4b134 100644 --- a/chrome/browser/download/download_item.cc +++ b/chrome/browser/download/download_item.cc @@ -42,6 +42,7 @@ DownloadItem::DownloadItem(DownloadManager* download_manager, const DownloadCreateInfo& info) : id_(-1), full_path_(info.path), + path_uniquifier_(0), url_(info.url), referrer_url_(info.referrer_url), mime_type_(info.mime_type), @@ -57,7 +58,7 @@ DownloadItem::DownloadItem(DownloadManager* download_manager, open_when_complete_(false), safety_state_(SAFE), auto_opened_(false), - original_name_(info.original_name), + target_name_(info.original_name), render_process_id_(-1), request_id_(-1), save_as_(false), @@ -65,7 +66,6 @@ DownloadItem::DownloadItem(DownloadManager* download_manager, is_extension_install_(info.is_extension_install), name_finalized_(false), is_temporary_(false), - need_final_rename_(false), opened_(false) { if (state_ == IN_PROGRESS) state_ = CANCELLED; @@ -94,7 +94,7 @@ DownloadItem::DownloadItem(DownloadManager* download_manager, open_when_complete_(false), safety_state_(info.is_dangerous ? DANGEROUS : SAFE), auto_opened_(false), - original_name_(info.original_name), + target_name_(info.original_name), render_process_id_(info.child_id), request_id_(info.request_id), save_as_(info.prompt_user_for_save_location), @@ -102,7 +102,6 @@ DownloadItem::DownloadItem(DownloadManager* download_manager, is_extension_install_(info.is_extension_install), name_finalized_(false), is_temporary_(!info.save_info.file_path.empty()), - need_final_rename_(false), opened_(false) { Init(true /* start progress timer */); } @@ -130,7 +129,6 @@ DownloadItem::DownloadItem(DownloadManager* download_manager, open_when_complete_(false), safety_state_(SAFE), auto_opened_(false), - original_name_(FilePath()), render_process_id_(-1), request_id_(-1), save_as_(false), @@ -138,7 +136,6 @@ DownloadItem::DownloadItem(DownloadManager* download_manager, is_extension_install_(false), name_finalized_(false), is_temporary_(false), - need_final_rename_(false), opened_(false) { Init(true /* start progress timer */); } @@ -165,24 +162,21 @@ void DownloadItem::NotifyObserversDownloadFileCompleted() { } bool DownloadItem::CanOpenDownload() { - FilePath file_to_use = full_path(); - if (!original_name().value().empty()) - file_to_use = original_name(); - - return !Extension::IsExtension(file_to_use) && - !download_util::IsExecutableFile(file_to_use); + return !Extension::IsExtension(target_name_) && + !download_util::IsExecutableFile(target_name_); } bool DownloadItem::ShouldOpenFileBasedOnExtension() { - return download_manager_->ShouldOpenFileBasedOnExtension(full_path()); + return download_manager_->ShouldOpenFileBasedOnExtension( + GetUserVerifiedFileName()); } void DownloadItem::OpenFilesBasedOnExtension(bool open) { DownloadPrefs* prefs = download_manager_->download_prefs(); if (open) - prefs->EnableAutoOpenBasedOnExtension(full_path()); + prefs->EnableAutoOpenBasedOnExtension(GetUserVerifiedFileName()); else - prefs->DisableAutoOpenBasedOnExtension(full_path()); + prefs->DisableAutoOpenBasedOnExtension(GetUserVerifiedFileName()); } void DownloadItem::OpenDownload() { @@ -281,7 +275,8 @@ void DownloadItem::Finished() { *this); auto_opened_ = true; } else if (open_when_complete() || - download_manager_->ShouldOpenFileBasedOnExtension(full_path()) || + download_manager_->ShouldOpenFileBasedOnExtension( + GetUserVerifiedFileName()) || is_temporary()) { // If the download is temporary, like in drag-and-drop, do not open it but // we still need to set it auto-opened so that it can be removed from the @@ -344,7 +339,6 @@ int DownloadItem::PercentComplete() const { void DownloadItem::Rename(const FilePath& full_path) { DCHECK(!full_path.empty()); full_path_ = full_path; - file_name_ = full_path_.BaseName(); } void DownloadItem::TogglePause() { @@ -386,26 +380,35 @@ bool DownloadItem::MatchesQuery(const string16& query) const { if (url_formatted.find(query) != string16::npos) return true; - string16 path(l10n_util::ToLower(WideToUTF16(full_path_.ToWStringHack()))); + string16 path(l10n_util::ToLower(WideToUTF16(full_path().ToWStringHack()))); if (path.find(query) != std::wstring::npos) return true; return false; } -FilePath DownloadItem::GetFileName() const { - if (safety_state_ == DownloadItem::SAFE) - return file_name_; +FilePath DownloadItem::GetTargetFilePath() const { + return full_path_.DirName().Append(target_name_); +} + +FilePath DownloadItem::GetFileNameToReportUser() const { if (path_uniquifier_ > 0) { - FilePath name(original_name_); + FilePath name(target_name_); download_util::AppendNumberToPath(&name, path_uniquifier_); return name; } - return original_name_; + return target_name_; +} + +FilePath DownloadItem::GetUserVerifiedFileName() const { + if (safety_state_ == DownloadItem::SAFE) + return target_name_; + return full_path_.BaseName(); } void DownloadItem::Init(bool start_timer) { - file_name_ = full_path_.BaseName(); + if (target_name_.value().empty()) + target_name_ = full_path_.BaseName(); if (start_timer) StartProgressTimer(); } diff --git a/chrome/browser/download/download_item.h b/chrome/browser/download/download_item.h index 3431bc7..4bc2b39 100644 --- a/chrome/browser/download/download_item.h +++ b/chrome/browser/download/download_item.h @@ -187,23 +187,31 @@ class DownloadItem { safety_state_ = safety_state; } bool auto_opened() { return auto_opened_; } - FilePath original_name() const { return original_name_; } + FilePath target_name() const { return target_name_; } bool save_as() const { return save_as_; } bool is_otr() const { return is_otr_; } bool is_extension_install() const { return is_extension_install_; } bool name_finalized() const { return name_finalized_; } bool is_temporary() const { return is_temporary_; } - bool need_final_rename() const { return need_final_rename_; } - void set_need_final_rename(bool need_final_rename) { - need_final_rename_ = need_final_rename; - } void set_opened(bool opened) { opened_ = opened; } bool opened() const { return opened_; } + // Returns the final target file path for the download. + FilePath GetTargetFilePath() const; + // Returns the file-name that should be reported to the user, which is - // file_name_ for safe downloads and original_name_ for dangerous ones with - // the uniquifier number. - FilePath GetFileName() const; + // target_name_ possibly with the uniquifier number. + FilePath GetFileNameToReportUser() const; + + // Returns the user-verified target name for the download. + // This returns the same path as target_name() for safe downloads + // but does not for dangerous downloads until the name is verified. + FilePath GetUserVerifiedFileName() const; + + // Returns true if the current file name is not the final target name yet. + bool NeedsRename() const { + return target_name_ != full_path_.BaseName(); + } private: void Init(bool start_timer); @@ -218,16 +226,13 @@ class DownloadItem { // Request ID assigned by the ResourceDispatcherHost. int32 id_; - // Full path to the downloaded file + // Full path to the downloaded or downloading file. FilePath full_path_; // A number that should be appended to the path to make it unique, or 0 if the // path should be used as is. int path_uniquifier_; - // Short display version of the file - FilePath file_name_; - // The URL from whence we came. GURL url_; @@ -283,9 +288,10 @@ class DownloadItem { // before the observer is added. bool auto_opened_; - // Dangerous download are given temporary names until the user approves them. - // This stores their original name. - FilePath original_name_; + // Dangerous downloads or ongoing downloads are given temporary names until + // the user approves them or the downloads finish. + // This stores their final target name. + FilePath target_name_; // For canceling or pausing requests. int render_process_id_; @@ -306,9 +312,6 @@ class DownloadItem { // True if the item was downloaded temporarily. bool is_temporary_; - // True if the file needs final rename. - bool need_final_rename_; - // Did the user open the item either directly or indirectly (such as by // setting always open files of this type)? The shelf also sets this field // when the user closes the shelf before the item has been opened but should diff --git a/chrome/browser/download/download_manager.cc b/chrome/browser/download/download_manager.cc index 106c8fc..59476b4 100644 --- a/chrome/browser/download/download_manager.cc +++ b/chrome/browser/download/download_manager.cc @@ -12,6 +12,7 @@ #include "base/path_service.h" #include "base/rand_util.h" #include "base/stl_util-inl.h" +#include "base/stringprintf.h" #include "base/sys_string_conversions.h" #include "base/task.h" #include "base/utf_string_conversions.h" @@ -136,6 +137,8 @@ void DownloadManager::Shutdown() { download_history_.reset(); + request_context_getter_ = NULL; + shutdown_needed_ = false; } @@ -339,8 +342,10 @@ void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info, FilePath::StringType file_name; FilePath path; while (path.empty()) { - SStringPrintf(&file_name, FILE_PATH_LITERAL("unconfirmed %d.crdownload"), - base::RandInt(0, 100000)); + base::SStringPrintf( + &file_name, + FILE_PATH_LITERAL("unconfirmed %d.crdownload"), + base::RandInt(0, 100000)); path = dir.Append(file_name); if (file_util::PathExists(path)) path = FilePath(); @@ -448,7 +453,7 @@ void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info, NewRunnableMethod( file_manager_, &DownloadFileManager::OnIntermediateDownloadName, download->id(), download_path, this)); - download->set_need_final_rename(true); + download->Rename(download_path); } if (download_finished) { @@ -458,8 +463,6 @@ void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info, pending_finished_downloads_[info->download_id]); } - download->Rename(target_path); - download_history_->AddEntry(*info, download, NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete)); @@ -524,16 +527,16 @@ void DownloadManager::OnAllDataSaved(int32 download_id, int64 size) { NewRunnableMethod( this, &DownloadManager::ProceedWithFinishedDangerousDownload, download->db_handle(), - download->full_path(), download->original_name())); + download->full_path(), download->target_name())); return; } - if (download->need_final_rename()) { + if (download->NeedsRename()) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod( file_manager_, &DownloadFileManager::OnFinalDownloadName, - download->id(), download->full_path(), false, this)); + download->id(), download->GetTargetFilePath(), false, this)); return; } @@ -543,16 +546,18 @@ void DownloadManager::OnAllDataSaved(int32 download_id, int64 size) { void DownloadManager::DownloadRenamedToFinalName(int download_id, const FilePath& full_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DownloadItem* item = GetDownloadItem(download_id); if (!item) return; + + bool needed_rename = item->NeedsRename(); + item->Rename(full_path); + item->OnNameFinalized(); - // This was called from DownloadFinished; continue to call - // ContinueDownloadFinished. - if (item->need_final_rename()) { - item->set_need_final_rename(false); + if (needed_rename) { + // This was called from OnAllDataSaved; continue to call + // ContinueDownloadFinished. ContinueDownloadFinished(item); } } @@ -611,7 +616,7 @@ void DownloadManager::DangerousDownloadRenamed(int64 download_handle, // If we failed to rename the file, we'll just keep the name as is. if (success) { // We need to update the path uniquifier so that the UI shows the right - // name when calling GetFileName(). + // name when calling GetFileNameToReportUser(). download->set_path_uniquifier(new_path_uniquifier); RenameDownload(download, new_path); } @@ -892,7 +897,7 @@ void DownloadManager::DangerousDownloadValidated(DownloadItem* download) { NewRunnableMethod( this, &DownloadManager::ProceedWithFinishedDangerousDownload, download->db_handle(), download->full_path(), - download->original_name())); + download->target_name())); } // Operations posted to us from the history service ---------------------------- @@ -912,8 +917,9 @@ void DownloadManager::OnQueryDownloadEntriesComplete( // Once the new DownloadItem's creation info has been committed to the history // service, we associate the DownloadItem with the db handle, update our // 'downloads_' map and inform observers. -void DownloadManager::OnCreateDownloadEntryComplete(DownloadCreateInfo info, - int64 db_handle) { +void DownloadManager::OnCreateDownloadEntryComplete( + const DownloadCreateInfo& info, + int64 db_handle) { DownloadMap::iterator it = in_progress_.find(info.download_id); DCHECK(it != in_progress_.end()); diff --git a/chrome/browser/download/download_manager.h b/chrome/browser/download/download_manager.h index 56acb00..6608af2 100644 --- a/chrome/browser/download/download_manager.h +++ b/chrome/browser/download/download_manager.h @@ -167,7 +167,8 @@ class DownloadManager // Methods called on completion of a query sent to the history system. void OnQueryDownloadEntriesComplete( std::vector<DownloadCreateInfo>* entries); - void OnCreateDownloadEntryComplete(DownloadCreateInfo info, int64 db_handle); + void OnCreateDownloadEntryComplete( + const DownloadCreateInfo& info, int64 db_handle); // Display a new download in the appropriate browser UI. void ShowDownloadInBrowser(const DownloadCreateInfo& info, diff --git a/chrome/browser/download/download_prefs.cc b/chrome/browser/download/download_prefs.cc index b8645db..8673e0a 100644 --- a/chrome/browser/download/download_prefs.cc +++ b/chrome/browser/download/download_prefs.cc @@ -81,8 +81,8 @@ bool DownloadPrefs::IsAutoOpenEnabledForExtension( return auto_open_.find(extension) != auto_open_.end(); } -bool DownloadPrefs::EnableAutoOpenBasedOnExtension(const FilePath& file_path) { - FilePath::StringType extension = file_path.Extension(); +bool DownloadPrefs::EnableAutoOpenBasedOnExtension(const FilePath& file_name) { + FilePath::StringType extension = file_name.Extension(); if (extension.empty()) return false; DCHECK(extension[0] == FilePath::kExtensionSeparator); @@ -95,8 +95,8 @@ bool DownloadPrefs::EnableAutoOpenBasedOnExtension(const FilePath& file_path) { return true; } -void DownloadPrefs::DisableAutoOpenBasedOnExtension(const FilePath& file_path) { - FilePath::StringType extension = file_path.Extension(); +void DownloadPrefs::DisableAutoOpenBasedOnExtension(const FilePath& file_name) { + FilePath::StringType extension = file_name.Extension(); if (extension.empty()) return; DCHECK(extension[0] == FilePath::kExtensionSeparator); diff --git a/chrome/browser/download/download_prefs.h b/chrome/browser/download/download_prefs.h index bc8a9f3..1345a44 100644 --- a/chrome/browser/download/download_prefs.h +++ b/chrome/browser/download/download_prefs.h @@ -33,10 +33,10 @@ class DownloadPrefs { // Enables auto-open based on file extension. Returns true on success. // TODO(phajdan.jr): Add WARN_UNUSED_RESULT here. - bool EnableAutoOpenBasedOnExtension(const FilePath& file_path); + bool EnableAutoOpenBasedOnExtension(const FilePath& file_name); // Disables auto-open based on file extension. - void DisableAutoOpenBasedOnExtension(const FilePath& file_path); + void DisableAutoOpenBasedOnExtension(const FilePath& file_name); void ResetToDefaults(); void ResetAutoOpen(); diff --git a/chrome/browser/download/download_uitest.cc b/chrome/browser/download/download_uitest.cc index 16db455..f5bae1f 100644 --- a/chrome/browser/download/download_uitest.cc +++ b/chrome/browser/download/download_uitest.cc @@ -16,7 +16,7 @@ #include "base/platform_thread.h" #include "base/string_util.h" #include "base/test/test_file_util.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/net/url_request_mock_http_job.h" #include "chrome/browser/net/url_request_slow_download_job.h" #include "chrome/common/chrome_constants.h" diff --git a/chrome/browser/download/download_util.cc b/chrome/browser/download/download_util.cc index affa24a..1235285 100644 --- a/chrome/browser/download/download_util.cc +++ b/chrome/browser/download/download_util.cc @@ -20,7 +20,9 @@ #include "base/singleton.h" #include "base/string16.h" #include "base/string_number_conversions.h" +#include "base/stringprintf.h" #include "base/sys_string_conversions.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "base/win/windows_version.h" @@ -150,8 +152,13 @@ void GenerateExtension(const FilePath& file_name, extension.assign(default_extension); #endif - if (extension.empty()) + if (extension.empty()) { + // The GetPreferredExtensionForMimeType call will end up going to disk. Do + // this on another thread to avoid slowing the IO thread. + // http://crbug.com/61827 + base::ThreadRestrictions::ScopedAllowIO allow_io; net::GetPreferredExtensionForMimeType(mime_type, &extension); + } generated_extension->swap(extension); } @@ -442,8 +449,8 @@ void DragDownload(const DownloadItem* download, OSExchangeData data; if (icon) { - drag_utils::CreateDragImageForFile(download->GetFileName().value(), icon, - &data); + drag_utils::CreateDragImageForFile( + download->GetFileNameToReportUser().value(), icon, &data); } const FilePath full_path = download->full_path(); @@ -456,7 +463,7 @@ void DragDownload(const DownloadItem* download, // Add URL so that we can load supported files when dragged to TabContents. if (net::IsSupportedMimeType(mime_type)) { data.SetURL(GURL(WideToUTF8(full_path.ToWStringHack())), - download->GetFileName().ToWStringHack()); + download->GetFileNameToReportUser().ToWStringHack()); } #if defined(OS_WIN) @@ -496,10 +503,10 @@ DictionaryValue* CreateDownloadItemValue(DownloadItem* download, int id) { WideToUTF16Hack(base::TimeFormatShortDate(download->start_time()))); file_value->SetInteger("id", id); file_value->SetString("file_path", - WideToUTF16Hack(download->full_path().ToWStringHack())); + WideToUTF16Hack(download->GetTargetFilePath().ToWStringHack())); // Keep file names as LTR. string16 file_name = WideToUTF16Hack( - download->GetFileName().ToWStringHack()); + download->GetFileNameToReportUser().ToWStringHack()); file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name); file_value->SetString("file_name", file_name); file_value->SetString("url", download->url().spec()); @@ -709,8 +716,10 @@ int GetUniquePathNumberWithCrDownload(const FilePath& path) { FilePath GetCrDownloadPath(const FilePath& suggested_path) { FilePath::StringType file_name; - SStringPrintf(&file_name, PRFilePathLiteral FILE_PATH_LITERAL(".crdownload"), - suggested_path.value().c_str()); + base::SStringPrintf( + &file_name, + PRFilePathLiteral FILE_PATH_LITERAL(".crdownload"), + suggested_path.value().c_str()); return FilePath(file_name); } diff --git a/chrome/browser/download/save_file_manager.cc b/chrome/browser/download/save_file_manager.cc index 3cc3daf..2c432a7 100644 --- a/chrome/browser/download/save_file_manager.cc +++ b/chrome/browser/download/save_file_manager.cc @@ -138,7 +138,7 @@ void SaveFileManager::SaveURL(const GURL& url, referrer, render_process_host_id, render_view_id, - request_context_getter)); + make_scoped_refptr(request_context_getter))); } else { // We manually start the save job. SaveFileCreateInfo* info = new SaveFileCreateInfo(file_full_path, @@ -250,7 +250,6 @@ void SaveFileManager::UpdateSaveProgress(int save_id, this, &SaveFileManager::OnUpdateSaveProgress, save_file->save_id(), save_file->bytes_so_far(), write_success)); } - data->Release(); } // The IO thread will call this when saving is completed or it got error when @@ -260,7 +259,7 @@ void SaveFileManager::UpdateSaveProgress(int save_id, // thread, which will use the save URL to find corresponding request record and // delete it. void SaveFileManager::SaveFinished(int save_id, - GURL save_url, + const GURL& save_url, int render_process_id, bool is_success) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); @@ -339,7 +338,7 @@ void SaveFileManager::OnSaveFinished(int save_id, package->SaveFinished(save_id, bytes_so_far, is_success); } -void SaveFileManager::OnErrorFinished(GURL save_url, int tab_id) { +void SaveFileManager::OnErrorFinished(const GURL& save_url, int tab_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); SavePackage* save_package = UnregisterStartingRequest(save_url, tab_id); if (save_package) diff --git a/chrome/browser/download/save_file_manager.h b/chrome/browser/download/save_file_manager.h index 4b13acb..207d91e 100644 --- a/chrome/browser/download/save_file_manager.h +++ b/chrome/browser/download/save_file_manager.h @@ -102,8 +102,10 @@ class SaveFileManager // Notifications sent from the IO thread and run on the file thread: void StartSave(SaveFileCreateInfo* info); void UpdateSaveProgress(int save_id, net::IOBuffer* data, int size); - void SaveFinished(int save_id, GURL save_url, - int render_process_id, bool is_success); + void SaveFinished(int save_id, + const GURL& save_url, + int render_process_id, + bool is_success); // Notifications sent from the UI thread and run on the file thread. // Cancel a SaveFile instance which has specified save id. @@ -190,7 +192,7 @@ class SaveFileManager void OnSaveFinished(int save_id, int64 bytes_so_far, bool is_success); // For those requests that do not have valid save id, use // map:(url, SavePackage) to find the request and remove it. - void OnErrorFinished(GURL save_url, int tab_id); + void OnErrorFinished(const GURL& save_url, int tab_id); // Notifies SavePackage that the whole page saving job is finished. void OnFinishSavePageJob(int render_process_id, int render_view_id, diff --git a/chrome/browser/download/save_package.cc b/chrome/browser/download/save_package.cc index a148f18..69cbed1 100644 --- a/chrome/browser/download/save_package.cc +++ b/chrome/browser/download/save_package.cc @@ -53,24 +53,6 @@ using base::Time; using WebKit::WebPageSerializerClient; -// This structure is for storing parameters which we will use to create a -// SavePackage object later. -struct SavePackageParam { - // MIME type of current tab contents. - const std::string current_tab_mime_type; - // Pointer to preference service. - SavePackage::SavePackageType save_type; - // File path for main html file. - FilePath saved_main_file_path; - // Directory path for saving sub resources and sub html frames. - FilePath dir; - - explicit SavePackageParam(const std::string& mime_type) - : current_tab_mime_type(mime_type), - save_type(SavePackage::SAVE_TYPE_UNKNOWN) { - } -}; - namespace { // A counter for uniquely identifying each save package. @@ -183,46 +165,6 @@ bool GetSafePureFileName(const FilePath& dir_path, return false; } -// This task creates a directory (if needed) and then posts a task on the given -// thread. -class CreateDownloadDirectoryTask : public Task { - public: - CreateDownloadDirectoryTask(SavePackage* save_package, - const FilePath& default_save_file_dir, - const FilePath& default_download_dir) - : save_package_(save_package), - default_save_file_dir_(default_save_file_dir), - default_download_dir_(default_download_dir){ - } - - virtual void Run() { - // If the default html/websites save folder doesn't exist... - if (!file_util::DirectoryExists(default_save_file_dir_)) { - // If the default download dir doesn't exist, create it. - if (!file_util::DirectoryExists(default_download_dir_)) - file_util::CreateDirectory(default_download_dir_); - - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - NewRunnableMethod(save_package_, - &SavePackage::ContinueGetSaveInfo, - default_download_dir_)); - } else { - // If it does exist, use the default save dir param. - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - NewRunnableMethod(save_package_, - &SavePackage::ContinueGetSaveInfo, - default_save_file_dir_)); - } - } - - private: - SavePackage* save_package_; - FilePath default_save_file_dir_; - FilePath default_download_dir_; - - DISALLOW_COPY_AND_ASSIGN(CreateDownloadDirectoryTask); -}; - } // namespace SavePackage::SavePackage(TabContents* tab_contents, @@ -1025,8 +967,7 @@ void SavePackage::OnReceivedSerializedHtmlData(const GURL& frame_url, if (!data.empty()) { // Prepare buffer for saving HTML data. - net::IOBuffer* new_data = new net::IOBuffer(data.size()); - new_data->AddRef(); // We'll pass the buffer to SaveFileManager. + scoped_refptr<net::IOBuffer> new_data(new net::IOBuffer(data.size())); memcpy(new_data->data(), data.data(), data.size()); // Call write file functionality in file thread. @@ -1114,7 +1055,7 @@ void SavePackage::SetShouldPromptUser(bool should_prompt) { FilePath SavePackage::GetSuggestedNameForSaveAs( bool can_save_as_complete, - const FilePath::StringType& contents_mime_type) { + const std::string& contents_mime_type) { FilePath name_with_proper_ext = FilePath::FromWStringHack(UTF16ToWideHack(title_)); @@ -1174,7 +1115,7 @@ FilePath SavePackage::EnsureHtmlExtension(const FilePath& name) { } FilePath SavePackage::EnsureMimeExtension(const FilePath& name, - const FilePath::StringType& contents_mime_type) { + const std::string& contents_mime_type) { // Start extension at 1 to skip over period if non-empty. FilePath::StringType ext = name.Extension().length() ? name.Extension().substr(1) : name.Extension(); @@ -1191,8 +1132,8 @@ FilePath SavePackage::EnsureMimeExtension(const FilePath& name, return name; } -const FilePath::CharType *SavePackage::ExtensionForMimeType( - const FilePath::StringType& contents_mime_type) { +const FilePath::CharType* SavePackage::ExtensionForMimeType( + const std::string& contents_mime_type) { static const struct { const FilePath::CharType *mime_type; const FilePath::CharType *suggested_extension; @@ -1203,8 +1144,13 @@ const FilePath::CharType *SavePackage::ExtensionForMimeType( { FILE_PATH_LITERAL("text/plain"), FILE_PATH_LITERAL("txt") }, { FILE_PATH_LITERAL("text/css"), FILE_PATH_LITERAL("css") }, }; +#if defined(OS_POSIX) + FilePath::StringType mime_type(contents_mime_type); +#elif defined(OS_WIN) + FilePath::StringType mime_type(UTF8ToWide(contents_mime_type)); +#endif // OS_WIN for (uint32 i = 0; i < ARRAYSIZE_UNSAFE(extensions); ++i) { - if (contents_mime_type == extensions[i].mime_type) + if (mime_type == extensions[i].mime_type) return extensions[i].suggested_extension; } return FILE_PATH_LITERAL(""); @@ -1235,41 +1181,52 @@ FilePath SavePackage::GetSaveDirPreference(PrefService* prefs) { } void SavePackage::GetSaveInfo() { + // Can't use tab_contents_ in the file thread, so get the data that we need + // before calling to it. PrefService* prefs = tab_contents_->profile()->GetPrefs(); FilePath website_save_dir = GetSaveDirPreference(prefs); FilePath download_save_dir = prefs->GetFilePath( prefs::kDownloadDefaultDirectory); + std::string mime_type = tab_contents_->contents_mime_type(); BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, - new CreateDownloadDirectoryTask(this, - website_save_dir, - download_save_dir)); - // CreateDownloadDirectoryTask calls ContinueGetSaveInfo() below. + NewRunnableMethod(this, &SavePackage::CreateDirectoryOnFileThread, + website_save_dir, download_save_dir, mime_type)); } -void SavePackage::ContinueGetSaveInfo(FilePath save_dir) { +void SavePackage::CreateDirectoryOnFileThread( + const FilePath& website_save_dir, + const FilePath& download_save_dir, + const std::string& mime_type) { + FilePath save_dir; + // If the default html/websites save folder doesn't exist... + if (!file_util::DirectoryExists(website_save_dir)) { + // If the default download dir doesn't exist, create it. + if (!file_util::DirectoryExists(download_save_dir)) + file_util::CreateDirectory(download_save_dir); + save_dir = download_save_dir; + } else { + // If it does exist, use the default save dir param. + save_dir = website_save_dir; + } + + bool can_save_as_complete = CanSaveAsComplete(mime_type); + FilePath suggested_path = save_dir.Append( + GetSuggestedNameForSaveAs(can_save_as_complete, mime_type)); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, &SavePackage::ContinueGetSaveInfo, + suggested_path, can_save_as_complete)); +} + +void SavePackage::ContinueGetSaveInfo(const FilePath& suggested_path, + bool can_save_as_complete) { // Use "Web Page, Complete" option as default choice of saving page. int file_type_index = 2; SelectFileDialog::FileTypeInfo file_type_info; FilePath::StringType default_extension; - SavePackageParam* save_params = - new SavePackageParam(tab_contents_->contents_mime_type()); - - bool can_save_as_complete = - CanSaveAsComplete(save_params->current_tab_mime_type); - -#if defined(OS_POSIX) - FilePath::StringType mime_type(save_params->current_tab_mime_type); -#elif defined(OS_WIN) - FilePath::StringType mime_type( - UTF8ToWide(save_params->current_tab_mime_type)); -#endif // OS_WIN - - FilePath suggested_path = save_dir.Append( - GetSuggestedNameForSaveAs(can_save_as_complete, mime_type)); - // If the contents can not be saved as complete-HTML, do not show the // file filters. if (can_save_as_complete) { @@ -1316,34 +1273,32 @@ void SavePackage::ContinueGetSaveInfo(FilePath save_dir) { default_extension, platform_util::GetTopLevel( tab_contents_->GetNativeView()), - save_params); + NULL); } else { // Just use 'suggested_path' instead of opening the dialog prompt. - ContinueSave(save_params, suggested_path, file_type_index); - delete save_params; + ContinueSave(suggested_path, file_type_index); } } // Called after the save file dialog box returns. -void SavePackage::ContinueSave(SavePackageParam* param, - const FilePath& final_name, +void SavePackage::ContinueSave(const FilePath& final_name, int index) { // Ensure the filename is safe. - param->saved_main_file_path = final_name; - download_util::GenerateSafeFileName(param->current_tab_mime_type, - ¶m->saved_main_file_path); + saved_main_file_path_ = final_name; + download_util::GenerateSafeFileName(tab_contents_->contents_mime_type(), + &saved_main_file_path_); // The option index is not zero-based. DCHECK(index > 0 && index < 3); - param->dir = param->saved_main_file_path.DirName(); + saved_main_directory_path_ = saved_main_file_path_.DirName(); PrefService* prefs = tab_contents_->profile()->GetPrefs(); StringPrefMember save_file_path; save_file_path.Init(prefs::kSaveFileDefaultDirectory, prefs, NULL); #if defined(OS_POSIX) - std::string path_string = param->dir.value(); + std::string path_string = saved_main_directory_path_.value(); #elif defined(OS_WIN) - std::string path_string = WideToUTF8(param->dir.value()); + std::string path_string = WideToUTF8(saved_main_directory_path_.value()); #endif // If user change the default saving directory, we will remember it just // like IE and FireFox. @@ -1352,20 +1307,16 @@ void SavePackage::ContinueSave(SavePackageParam* param, save_file_path.SetValue(path_string); } - param->save_type = (index == 1) ? SavePackage::SAVE_AS_ONLY_HTML : - SavePackage::SAVE_AS_COMPLETE_HTML; + save_type_ = (index == 1) ? SavePackage::SAVE_AS_ONLY_HTML : + SavePackage::SAVE_AS_COMPLETE_HTML; - if (param->save_type == SavePackage::SAVE_AS_COMPLETE_HTML) { + if (save_type_ == SavePackage::SAVE_AS_COMPLETE_HTML) { // Make new directory for saving complete file. - param->dir = param->dir.Append( - param->saved_main_file_path.RemoveExtension().BaseName().value() + + saved_main_directory_path_ = saved_main_directory_path_.Append( + saved_main_file_path_.RemoveExtension().BaseName().value() + FILE_PATH_LITERAL("_files")); } - save_type_ = param->save_type; - saved_main_file_path_ = param->saved_main_file_path; - saved_main_directory_path_ = param->dir; - Init(); } @@ -1394,12 +1345,8 @@ bool SavePackage::IsSavableContents(const std::string& contents_mime_type) { // SelectFileDialog::Listener interface. void SavePackage::FileSelected(const FilePath& path, int index, void* params) { - SavePackageParam* save_params = reinterpret_cast<SavePackageParam*>(params); - ContinueSave(save_params, path, index); - delete save_params; + ContinueSave(path, index); } void SavePackage::FileSelectionCanceled(void* params) { - SavePackageParam* save_params = reinterpret_cast<SavePackageParam*>(params); - delete save_params; } diff --git a/chrome/browser/download/save_package.h b/chrome/browser/download/save_package.h index 68b3bf6..e7080a8 100644 --- a/chrome/browser/download/save_package.h +++ b/chrome/browser/download/save_package.h @@ -127,10 +127,6 @@ class SavePackage : public base::RefCountedThreadSafe<SavePackage>, int id() const { return unique_id_; } void GetSaveInfo(); - void ContinueGetSaveInfo(FilePath save_dir); - void ContinueSave(SavePackageParam* param, - const FilePath& final_name, - int index); // RenderViewHostDelegate::Save ---------------------------------------------- @@ -202,6 +198,13 @@ class SavePackage : public base::RefCountedThreadSafe<SavePackage>, // Retrieves the URL to be saved from tab_contents_ variable. GURL GetUrlToBeSaved(); + void CreateDirectoryOnFileThread(const FilePath& website_save_dir, + const FilePath& download_save_dir, + const std::string& mime_type); + void ContinueGetSaveInfo(const FilePath& suggested_path, + bool can_save_as_complete); + void ContinueSave(const FilePath& final_name, int index); + typedef base::hash_map<std::string, SaveItem*> SaveUrlItemMap; // in_progress_items_ is map of all saving job in in-progress state. @@ -228,7 +231,7 @@ class SavePackage : public base::RefCountedThreadSafe<SavePackage>, // suggested name is determined by the web document's title. FilePath GetSuggestedNameForSaveAs( bool can_save_as_complete, - const FilePath::StringType& contents_mime_type); + const std::string& contents_mime_type); // Ensures that the file name has a proper extension for HTML by adding ".htm" // if necessary. @@ -237,12 +240,12 @@ class SavePackage : public base::RefCountedThreadSafe<SavePackage>, // Ensures that the file name has a proper extension for supported formats // if necessary. static FilePath EnsureMimeExtension(const FilePath& name, - const FilePath::StringType& contents_mime_type); + const std::string& contents_mime_type); // Returns extension for supported MIME types (for example, for "text/plain" // it returns "txt"). static const FilePath::CharType* ExtensionForMimeType( - const FilePath::StringType& contents_mime_type); + const std::string& contents_mime_type); typedef std::queue<SaveItem*> SaveItemQueue; // A queue for items we are about to start saving. diff --git a/chrome/browser/download/save_package_unittest.cc b/chrome/browser/download/save_package_unittest.cc index b315097..948280f 100644 --- a/chrome/browser/download/save_package_unittest.cc +++ b/chrome/browser/download/save_package_unittest.cc @@ -82,7 +82,7 @@ class SavePackageTest : public RenderViewHostTestHarness { } FilePath EnsureMimeExtension(const FilePath& name, - const FilePath::StringType& content_mime_type) { + const std::string& content_mime_type) { return SavePackage::EnsureMimeExtension(name, content_mime_type); } @@ -242,35 +242,35 @@ TEST_F(SavePackageTest, TestEnsureMimeExtension) { static const struct { const FilePath::CharType* page_title; const FilePath::CharType* expected_name; - const FilePath::CharType* contents_mime_type; + const char* contents_mime_type; } kExtensionTests[] = { - { FPL("filename.html"), FPL("filename.html"), FPL("text/html") }, - { FPL("filename.htm"), FPL("filename.htm"), FPL("text/html") }, - { FPL("filename.xhtml"), FPL("filename.xhtml"), FPL("text/html") }, + { FPL("filename.html"), FPL("filename.html"), "text/html" }, + { FPL("filename.htm"), FPL("filename.htm"), "text/html" }, + { FPL("filename.xhtml"), FPL("filename.xhtml"), "text/html" }, #if defined(OS_WIN) - { FPL("filename"), FPL("filename.htm"), FPL("text/html") }, + { FPL("filename"), FPL("filename.htm"), "text/html" }, #else // defined(OS_WIN) - { FPL("filename"), FPL("filename.html"), FPL("text/html") }, + { FPL("filename"), FPL("filename.html"), "text/html" }, #endif // defined(OS_WIN) - { FPL("filename.html"), FPL("filename.html"), FPL("text/xml") }, - { FPL("filename.xml"), FPL("filename.xml"), FPL("text/xml") }, - { FPL("filename"), FPL("filename.xml"), FPL("text/xml") }, + { FPL("filename.html"), FPL("filename.html"), "text/xml" }, + { FPL("filename.xml"), FPL("filename.xml"), "text/xml" }, + { FPL("filename"), FPL("filename.xml"), "text/xml" }, { FPL("filename.xhtml"), FPL("filename.xhtml"), - FPL("application/xhtml+xml") }, + "application/xhtml+xml" }, { FPL("filename.html"), FPL("filename.html"), - FPL("application/xhtml+xml") }, - { FPL("filename"), FPL("filename.xhtml"), FPL("application/xhtml+xml") }, - { FPL("filename.txt"), FPL("filename.txt"), FPL("text/plain") }, - { FPL("filename"), FPL("filename.txt"), FPL("text/plain") }, - { FPL("filename.css"), FPL("filename.css"), FPL("text/css") }, - { FPL("filename"), FPL("filename.css"), FPL("text/css") }, - { FPL("filename.abc"), FPL("filename.abc"), FPL("unknown/unknown") }, - { FPL("filename"), FPL("filename"), FPL("unknown/unknown") }, + "application/xhtml+xml" }, + { FPL("filename"), FPL("filename.xhtml"), "application/xhtml+xml" }, + { FPL("filename.txt"), FPL("filename.txt"), "text/plain" }, + { FPL("filename"), FPL("filename.txt"), "text/plain" }, + { FPL("filename.css"), FPL("filename.css"), "text/css" }, + { FPL("filename"), FPL("filename.css"), "text/css" }, + { FPL("filename.abc"), FPL("filename.abc"), "unknown/unknown" }, + { FPL("filename"), FPL("filename"), "unknown/unknown" }, }; for (uint32 i = 0; i < ARRAYSIZE_UNSAFE(kExtensionTests); ++i) { FilePath original = FilePath(kExtensionTests[i].page_title); FilePath expected = FilePath(kExtensionTests[i].expected_name); - FilePath::StringType mime_type(kExtensionTests[i].contents_mime_type); + std::string mime_type(kExtensionTests[i].contents_mime_type); FilePath actual = EnsureMimeExtension(original, mime_type); EXPECT_EQ(expected.value(), actual.value()) << "Failed for page title: " << kExtensionTests[i].page_title << " MIME:" << mime_type; @@ -338,7 +338,7 @@ TEST_F(SavePackageTest, TestSuggestedSaveNames) { FilePath save_name = save_package->GetSuggestedNameForSaveAs( kSuggestedSaveNames[i].ensure_html_extension, - FilePath::StringType()); + std::string()); EXPECT_EQ(kSuggestedSaveNames[i].expected_name, save_name.value()) << "Test case " << i; } diff --git a/chrome/browser/download/save_page_browsertest.cc b/chrome/browser/download/save_page_browsertest.cc index bed7228..fe777e4 100644 --- a/chrome/browser/download/save_page_browsertest.cc +++ b/chrome/browser/download/save_page_browsertest.cc @@ -6,7 +6,7 @@ #include "base/file_util.h" #include "base/path_service.h" #include "base/scoped_temp_dir.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/net/url_request_mock_http_job.h" diff --git a/chrome/browser/download/save_page_uitest.cc b/chrome/browser/download/save_page_uitest.cc index 07fd78c..e6c6e08 100644 --- a/chrome/browser/download/save_page_uitest.cc +++ b/chrome/browser/download/save_page_uitest.cc @@ -7,7 +7,7 @@ #include "base/platform_thread.h" #include "base/string_util.h" #include "base/test/test_file_util.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/net/url_request_mock_http_job.h" #include "chrome/browser/download/save_package.h" #include "chrome/common/chrome_paths.h" diff --git a/chrome/browser/encoding_menu_controller.cc b/chrome/browser/encoding_menu_controller.cc index a286fdf..d53536d 100644 --- a/chrome/browser/encoding_menu_controller.cc +++ b/chrome/browser/encoding_menu_controller.cc @@ -7,7 +7,7 @@ #include "app/l10n_util.h" #include "base/i18n/rtl.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/character_encoding.h" #include "chrome/browser/prefs/pref_service.h" diff --git a/chrome/browser/encoding_menu_controller_unittest.cc b/chrome/browser/encoding_menu_controller_unittest.cc index 91cd2a4..ab8b6b5 100644 --- a/chrome/browser/encoding_menu_controller_unittest.cc +++ b/chrome/browser/encoding_menu_controller_unittest.cc @@ -7,7 +7,7 @@ #include <string> #include "base/basictypes.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/common/pref_names.h" diff --git a/chrome/browser/enumerate_modules_model_unittest_win.cc b/chrome/browser/enumerate_modules_model_unittest_win.cc new file mode 100644 index 0000000..374ec42 --- /dev/null +++ b/chrome/browser/enumerate_modules_model_unittest_win.cc @@ -0,0 +1,182 @@ +// 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 "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/enumerate_modules_model_win.h" +#include "testing/gtest/include/gtest/gtest.h" + +typedef public testing::Test EnumerateModulesTest; + +// Set up some constants to use as default when creating the structs. +static const ModuleEnumerator::ModuleType kType = + ModuleEnumerator::LOADED_MODULE; + +static const ModuleEnumerator::ModuleStatus kStatus = + ModuleEnumerator::NOT_MATCHED; + +static const ModuleEnumerator::RecommendedAction kAction = + ModuleEnumerator::NONE; + +// This is a list of test cases to normalize. +static const struct NormalizationEntryList { + ModuleEnumerator::Module test_case; + ModuleEnumerator::Module expected; +} kNormalizationTestCases[] = { + { + // Only path normalization needed. + {kType, kStatus, L"c:\\foo\\bar.dll", L"", L"Prod", L"Desc", L"1.0", + L"Sig", kAction}, + {kType, kStatus, L"c:\\foo\\", L"bar.dll", L"Prod", L"Desc", L"1.0", + L"Sig", kAction}, + }, { + // Lower case normalization. + {kType, kStatus, L"C:\\Foo\\Bar.dll", L"", L"", L"", L"1.0", + L"", kAction}, + {kType, kStatus, L"c:\\foo\\", L"bar.dll", L"", L"", L"1.0", + L"", kAction}, + }, { + // Version can include strings after the version number. Strip that away. + {kType, kStatus, L"c:\\foo.dll", L"", L"", L"", L"1.0 asdf", + L"", kAction}, + {kType, kStatus, L"c:\\", L"foo.dll", L"", L"", L"1.0", + L"", kAction}, + }, { + // Corner case: No path (not sure this will ever happen). + {kType, kStatus, L"bar.dll", L"", L"", L"", L"", L"", kAction}, + {kType, kStatus, L"", L"bar.dll", L"", L"", L"", L"", kAction}, + }, { + // Error case: Missing filename (not sure this will ever happen). + {kType, kStatus, L"", L"", L"", L"", L"1.0", L"", kAction}, + {kType, kStatus, L"", L"", L"", L"", L"1.0", L"", kAction}, + }, +}; + +TEST_F(EnumerateModulesTest, NormalizeEntry) { + for (size_t i = 0; i < arraysize(kNormalizationTestCases); ++i) { + ModuleEnumerator::Module test = kNormalizationTestCases[i].test_case; + EXPECT_FALSE(test.normalized); + ModuleEnumerator::NormalizeModule(&test); + ModuleEnumerator::Module expected = kNormalizationTestCases[i].expected; + + SCOPED_TRACE("Test case no: " + base::IntToString(i)); + EXPECT_EQ(expected.type, test.type); + EXPECT_EQ(expected.status, test.status); + EXPECT_STREQ(expected.location.c_str(), test.location.c_str()); + EXPECT_STREQ(expected.name.c_str(), test.name.c_str()); + EXPECT_STREQ(expected.product_name.c_str(), test.product_name.c_str()); + EXPECT_STREQ(expected.description.c_str(), test.description.c_str()); + EXPECT_STREQ(expected.version.c_str(), test.version.c_str()); + EXPECT_STREQ(expected.digital_signer.c_str(), test.digital_signer.c_str()); + EXPECT_EQ(expected.recommended_action, test.recommended_action); + EXPECT_TRUE(test.normalized); + } +} + +const ModuleEnumerator::Module kStandardModule = + { kType, kStatus, L"c:\\foo\\bar.dll", L"", L"Prod", L"Desc", L"1.0", L"Sig", + ModuleEnumerator::NONE }; + +// Name, location, description and signature are compared by hashing. +static const char kMatchName[] = "88e8c9e0"; // "bar.dll". +static const char kNoMatchName[] = "barfoo.dll"; +static const char kMatchLocation[] = "e6ca7b1c"; // "c:\\foo\\". +static const char kNoMatchLocation[] = "c:\\foobar\\"; +static const char kMatchDesc[] = "5c4419a6"; // "Desc". +static const char kNoMatchDesc[] = "NoDesc"; +static const char kVersionHigh[] = "2.0"; +static const char kVersionLow[] = "0.5"; +static const char kMatchSignature[] = "7bfd87e1"; // "Sig". +static const char kNoMatchSignature[] = "giS"; +static const char kEmpty[] = ""; + +const struct MatchingEntryList { + ModuleEnumerator::ModuleStatus expected_result; + ModuleEnumerator::Module test_case; + ModuleEnumerator::BlacklistEntry blacklist; +} kMatchineEntryList[] = { + // Each BlacklistEntry is: + // Filename, location, desc_or_signer, version from, version to, help_tip. + + { // Matches: Name (location doesn't match) => Not enough for a match. + ModuleEnumerator::NOT_MATCHED, + kStandardModule, + { kMatchName, kNoMatchLocation, kEmpty, kEmpty, kEmpty, + ModuleEnumerator::SEE_LINK } + }, { // Matches: Name (location not given) => Suspected match. + ModuleEnumerator::SUSPECTED_BAD, + kStandardModule, + { kMatchName, kEmpty, kEmpty, kEmpty, kEmpty, + ModuleEnumerator::SEE_LINK } + }, { // Matches: Name, not version (location not given) => Not a match. + ModuleEnumerator::NOT_MATCHED, + kStandardModule, + { kMatchName, kEmpty, kEmpty, kVersionHigh, kVersionHigh, + ModuleEnumerator::SEE_LINK } + }, { // Matches: Name, location => Suspected match. + ModuleEnumerator::SUSPECTED_BAD, + kStandardModule, + { kMatchName, kMatchLocation, kEmpty, kEmpty, kEmpty, + ModuleEnumerator::SEE_LINK } + }, { // Matches: Name, location (not version) => Not a match. + ModuleEnumerator::NOT_MATCHED, + kStandardModule, + { kMatchName, kMatchLocation, kEmpty, kVersionHigh, kVersionLow, + ModuleEnumerator::SEE_LINK } + }, { // Matches: Name, location, signature => Confirmed match. + ModuleEnumerator::CONFIRMED_BAD, + kStandardModule, + { kMatchName, kMatchLocation, kMatchSignature, kEmpty, kEmpty, + ModuleEnumerator::SEE_LINK } + }, { // Matches: Name, location, signature (not version) => No match. + ModuleEnumerator::NOT_MATCHED, + kStandardModule, + { kMatchName, kMatchLocation, kMatchSignature, + kVersionLow, kVersionLow, ModuleEnumerator::SEE_LINK } + }, { // Matches: Name, location, description => Confirmed match. + ModuleEnumerator::CONFIRMED_BAD, + kStandardModule, + { kMatchName, kMatchLocation, kMatchDesc, kEmpty, kEmpty, + ModuleEnumerator::SEE_LINK } + }, { // Matches: Name, location, description (not version) => No match. + ModuleEnumerator::NOT_MATCHED, + kStandardModule, + { kMatchName, kMatchLocation, kMatchDesc, + kVersionHigh, kVersionHigh, ModuleEnumerator::SEE_LINK } + }, { // Matches: Name, location, signature, version => Confirmed match. + ModuleEnumerator::CONFIRMED_BAD, + kStandardModule, + { kMatchName, kMatchLocation, kMatchSignature, + kVersionLow, kVersionHigh, ModuleEnumerator::SEE_LINK } + }, { // Matches: Name, location, signature, version (lower) => Confirmed. + ModuleEnumerator::CONFIRMED_BAD, + kStandardModule, + { kMatchName, kMatchLocation, kMatchSignature, + kVersionLow, kEmpty, ModuleEnumerator::SEE_LINK } + }, { // Matches: Name, location, signature, version (upper) => Confirmed. + ModuleEnumerator::CONFIRMED_BAD, + kStandardModule, + { kMatchName, kMatchLocation, kMatchSignature, + kEmpty, kVersionHigh, ModuleEnumerator::SEE_LINK } + }, { // All empty fields doesn't produce a match. + ModuleEnumerator::NOT_MATCHED, + {kType, kStatus, L"", L"", L"", L"", L""}, + { "a.dll", "", "", "", "", ModuleEnumerator::SEE_LINK } + }, +}; + +TEST_F(EnumerateModulesTest, MatchFunction) { + for (size_t i = 0; i < arraysize(kMatchineEntryList); ++i) { + ModuleEnumerator::Module test = kMatchineEntryList[i].test_case; + ModuleEnumerator::NormalizeModule(&test); + ModuleEnumerator::BlacklistEntry blacklist = + kMatchineEntryList[i].blacklist; + + SCOPED_TRACE("Test case no " + base::IntToString(i) + + ": '" + UTF16ToASCII(test.name) + "'"); + EXPECT_EQ(kMatchineEntryList[i].expected_result, + ModuleEnumerator::Match(test, blacklist)); + } +} diff --git a/chrome/browser/enumerate_modules_model_win.cc b/chrome/browser/enumerate_modules_model_win.cc new file mode 100644 index 0000000..0fbd575 --- /dev/null +++ b/chrome/browser/enumerate_modules_model_win.cc @@ -0,0 +1,626 @@ +// 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/enumerate_modules_model_win.h" + +#include <Tlhelp32.h> +#include <wintrust.h> + +#include "app/l10n_util.h" +#include "app/win_util.h" +#include "base/command_line.h" +#include "base/environment.h" +#include "base/file_path.h" +#include "base/file_version_info_win.h" +#include "base/scoped_handle.h" +#include "base/sha2.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "base/version.h" +#include "chrome/browser/net/service_providers_win.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" +#include "grit/generated_resources.h" + +// The period of time (in milliseconds) to wait until checking to see if any +// incompatible modules exist. +static const int kModuleCheckDelayMs = 60 * 1000; + +// A sort method that sorts by ModuleType ordinal (loaded module at the top), +// then by full name (including path). +static bool ModuleSort(const ModuleEnumerator::Module& a, + const ModuleEnumerator::Module& b) { + if (a.type != b.type) + return a.type < b.type; + if (a.location == b.location) + return a.name < b.name; + + return a.location < b.location; +} + +namespace { + +// Used to protect the LoadedModuleVector which is accessed +// from both the UI thread and the FILE thread. +Lock* lock = NULL; + +} + +// The browser process module blacklist. This lists all modules that are known +// to cause compatibility issues within the browser process. When adding to this +// list, make sure that all paths are lower-case, in long pathname form, end +// with a slash and use environments variables (or just look at one of the +// comments below and keep it consistent with that). When adding an entry with +// an environment variable not currently used in the list below, make sure to +// update the list in PreparePathMappings. Filename, Description/Signer, and +// Location must be entered as hashes (see GenerateHash). Filename is mandatory. +// Entries without any Description, Signer info, or Location will never be +// marked as confirmed bad (only as suspicious). +const ModuleEnumerator::BlacklistEntry ModuleEnumerator::kModuleBlacklist[] = { + // Test DLLs, to demonstrate the feature. Will be removed soon. + { // apphelp.dll, "%systemroot%\\system32\\" + "f5fda581", "23d01d5b", "", "", "", NONE + }, { // rsaenh.dll, "%systemroot%\\system32\\", "Microsoft Windows" + "6af212cb", "23d01d5b", "7b47bf79", "", "", + static_cast<RecommendedAction>(UPDATE | DISABLE | SEE_LINK) + }, + + // NOTE: Please keep this list sorted by dll name, then location. + + // foldersizecolumn.dll. + {"5ec91bd7", "", "", "", "", NONE}, + + // idmmbc.dll, "%programfiles%\\internet download manager\\", "Tonec Inc.". + // See: http://crbug.com/26892/. + {"b8dce5c3", "94541bf5", "d33ad640", "", "", NONE}, + + // imon.dll. See: http://crbug.com/21715. + {"8f42f22e", "", "", "", "", NONE}, + + // is3lsp.dll. See: http://crbug.com/26892. + {"7ffbdce9", "", "", "", "", NONE}, + + // nvlsp.dll. See: http://crbug.com/22083. + {"37f907e2", "", "", "", "", NONE}, + + // nvshell.dll. See: http://crbug.com/3269. + {"9290318f", "", "", "", "", NONE}, + + // securenet.dll. See: http://crbug.com/5165. + {"9b266e1c", "", "", "", "", NONE}, + + // sgprxy.dll. + {"005965ea", "", "", "", "", NONE}, + + // vaproxyd.dll. See: http://crbug.com/42445. + {"0a1c7f81", "", "", "", "", NONE}, + + // vlsp.dll. See: http://crbug.com/22826. + {"2e4eb93d", "", "", "", "", NONE}, +}; + +// Generates an 8 digit hash from the input given. +static void GenerateHash(const std::string& input, std::string* output) { + if (input.empty()) { + *output = ""; + return; + } + + uint8 hash[4]; + base::SHA256HashString(input, hash, sizeof(hash)); + *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash))); +} + +// ----------------------------------------------------------------------------- + +// static +void ModuleEnumerator::NormalizeModule(Module* module) { + string16 path = module->location; + if (!win_util::ConvertToLongPath(path, &module->location)) + module->location = path; + + module->location = l10n_util::ToLower(module->location); + + // Location contains the filename, so the last slash is where the path + // ends. + size_t last_slash = module->location.find_last_of(L"\\"); + if (last_slash != string16::npos) { + module->name = module->location.substr(last_slash + 1); + module->location = module->location.substr(0, last_slash + 1); + } else { + module->name = module->location; + module->location.clear(); + } + + // Some version strings have things like (win7_rtm.090713-1255) appended + // to them. Remove that. + size_t first_space = module->version.find_first_of(L" "); + if (first_space != string16::npos) + module->version = module->version.substr(0, first_space); + + module->normalized = true; +} + +// static +ModuleEnumerator::ModuleStatus ModuleEnumerator::Match( + const ModuleEnumerator::Module& module, + const ModuleEnumerator::BlacklistEntry& blacklisted) { + // All modules must be normalized before matching against blacklist. + DCHECK(module.normalized); + // Filename is mandatory and version should not contain spaces. + DCHECK(strlen(blacklisted.filename) > 0); + DCHECK(!strstr(blacklisted.version_from, " ")); + DCHECK(!strstr(blacklisted.version_to, " ")); + + std::string filename_hash, location_hash; + GenerateHash(WideToUTF8(module.name), &filename_hash); + GenerateHash(WideToUTF8(module.location), &location_hash); + + // Filenames are mandatory. Location is mandatory if given. + if (filename_hash == blacklisted.filename && + (std::string(blacklisted.location).empty() || + location_hash == blacklisted.location)) { + // We have a name match against the blacklist (and possibly location match + // also), so check version. + scoped_ptr<Version> module_version( + Version::GetVersionFromString(module.version)); + scoped_ptr<Version> version_min( + Version::GetVersionFromString(blacklisted.version_from)); + scoped_ptr<Version> version_max( + Version::GetVersionFromString(blacklisted.version_to)); + bool version_ok = !version_min.get() && !version_max.get(); + if (!version_ok) { + bool too_low = version_min.get() && + (!module_version.get() || + module_version->CompareTo(*version_min.get()) < 0); + bool too_high = version_max.get() && + (!module_version.get() || + module_version->CompareTo(*version_max.get()) > 0); + version_ok = !too_low && !too_high; + } + + if (version_ok) { + // At this point, the names match and there is no version specified + // or the versions also match. + + std::string desc_or_signer(blacklisted.desc_or_signer); + std::string signer_hash, description_hash; + GenerateHash(WideToUTF8(module.digital_signer), &signer_hash); + GenerateHash(WideToUTF8(module.description), &description_hash); + + // If signatures match, we have a winner. + if (!desc_or_signer.empty() && signer_hash == desc_or_signer) + return CONFIRMED_BAD; + + // If description matches and location, then we also have a match. + if (!desc_or_signer.empty() && description_hash == desc_or_signer && + !location_hash.empty() && location_hash == blacklisted.location) { + return CONFIRMED_BAD; + } + + // We are not sure, but it is likely bad. + return SUSPECTED_BAD; + } + } + + return NOT_MATCHED; +} + +ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer) +: observer_(observer) { + CHECK(BrowserThread::GetCurrentThreadIdentifier(&callback_thread_id_)); + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); +} + +ModuleEnumerator::~ModuleEnumerator() { +} + +void ModuleEnumerator::ScanNow(ModulesVector* list) { + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); + enumerated_modules_ = list; + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, &ModuleEnumerator::ScanOnFileThread)); +} + +void ModuleEnumerator::ScanOnFileThread() { + enumerated_modules_->clear(); + + // Make sure the path mapping vector is setup so we can collapse paths. + PreparePathMappings(); + + // Get all modules in the current process. + ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, + ::GetCurrentProcessId())); + if (!snap.Get()) + return; + + // Walk the module list. + MODULEENTRY32 module = { sizeof(module) }; + if (!::Module32First(snap.Get(), &module)) + return; + + do { + // It would be weird to present chrome.exe as a loaded module. + if (_wcsicmp(chrome::kBrowserProcessExecutableName, module.szModule) == 0) + continue; + + Module entry; + entry.type = LOADED_MODULE; + entry.status = NOT_MATCHED; + entry.normalized = false; + entry.location = module.szExePath; + entry.digital_signer = + GetSubjectNameFromDigitalSignature(FilePath(entry.location)); + entry.recommended_action = NONE; + scoped_ptr<FileVersionInfo> version_info( + FileVersionInfo::CreateFileVersionInfo(FilePath(entry.location))); + if (version_info.get()) { + FileVersionInfoWin* version_info_win = + static_cast<FileVersionInfoWin*>(version_info.get()); + + VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info(); + if (fixed_file_info) { + entry.description = version_info_win->file_description(); + entry.version = version_info_win->file_version(); + entry.product_name = version_info_win->product_name(); + } + } + + NormalizeModule(&entry); + CollapsePath(&entry); + enumerated_modules_->push_back(entry); + } while (::Module32Next(snap.Get(), &module)); + + // Add to this list the Winsock LSP DLLs. + WinsockLayeredServiceProviderList layered_providers; + GetWinsockLayeredServiceProviders(&layered_providers); + for (size_t i = 0; i < layered_providers.size(); ++i) { + Module entry; + entry.type = WINSOCK_MODULE_REGISTRATION; + entry.status = NOT_MATCHED; + entry.normalized = false; + entry.location = layered_providers[i].path; + entry.description = layered_providers[i].name; + entry.recommended_action = NONE; + + wchar_t expanded[MAX_PATH]; + DWORD size = ExpandEnvironmentStrings( + entry.location.c_str(), expanded, MAX_PATH); + if (size != 0 && size <= MAX_PATH) { + entry.digital_signer = + GetSubjectNameFromDigitalSignature(FilePath(expanded)); + } + entry.version = base::IntToString16(layered_providers[i].version); + + // Paths have already been collapsed. + NormalizeModule(&entry); + enumerated_modules_->push_back(entry); + } + + MatchAgainstBlacklist(); + + std::sort(enumerated_modules_->begin(), + enumerated_modules_->end(), ModuleSort); + + // Send a reply back on the UI thread. + BrowserThread::PostTask( + callback_thread_id_, FROM_HERE, + NewRunnableMethod(this, &ModuleEnumerator::ReportBack)); +} + +void ModuleEnumerator::PreparePathMappings() { + path_mapping_.clear(); + + scoped_ptr<base::Environment> environment(base::Environment::Create()); + std::vector<string16> env_vars; + env_vars.push_back(L"LOCALAPPDATA"); + env_vars.push_back(L"ProgramFiles"); + env_vars.push_back(L"USERPROFILE"); + env_vars.push_back(L"SystemRoot"); + env_vars.push_back(L"TEMP"); + for (std::vector<string16>::const_iterator variable = env_vars.begin(); + variable != env_vars.end(); ++variable) { + std::string path; + if (environment->GetVar(WideToASCII(*variable).c_str(), &path)) { + path_mapping_.push_back( + std::make_pair(l10n_util::ToLower(UTF8ToWide(path)) + L"\\", + L"%" + l10n_util::ToLower(*variable) + L"%")); + } + } +} + +void ModuleEnumerator::CollapsePath(Module* entry) { + // Take the path and see if we can use any of the substitution values + // from the vector constructed above to replace c:\windows with, for + // example, %systemroot%. + for (PathMapping::const_iterator mapping = path_mapping_.begin(); + mapping != path_mapping_.end(); ++mapping) { + string16 prefix = mapping->first; + if (StartsWith(entry->location, prefix, false)) { + entry->location = mapping->second + + entry->location.substr(prefix.length() - 1); + return; + } + } +} + +void ModuleEnumerator::MatchAgainstBlacklist() { + for (size_t m = 0; m < enumerated_modules_->size(); ++m) { + // Match this module against the blacklist. + Module* module = &(*enumerated_modules_)[m]; + module->status = GOOD; // We change this below potentially. + for (size_t i = 0; i < arraysize(kModuleBlacklist); ++i) { + #if !defined(NDEBUG) + // This saves time when constructing the blacklist. + std::string hashes(kModuleBlacklist[i].filename); + std::string hash1, hash2, hash3; + GenerateHash(kModuleBlacklist[i].filename, &hash1); + hashes += " - " + hash1; + GenerateHash(kModuleBlacklist[i].location, &hash2); + hashes += " - " + hash2; + GenerateHash(kModuleBlacklist[i].desc_or_signer, &hash3); + hashes += " - " + hash3; + #endif + + ModuleStatus status = Match(*module, kModuleBlacklist[i]); + if (status != NOT_MATCHED) { + // We have a match against the blacklist. Mark it as such. + module->status = status; + module->recommended_action = kModuleBlacklist[i].help_tip; + break; + } + } + } +} + +void ModuleEnumerator::ReportBack() { + DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_)); + observer_->DoneScanning(); +} + +string16 ModuleEnumerator::GetSubjectNameFromDigitalSignature( + const FilePath& filename) { + HCERTSTORE store = NULL; + HCRYPTMSG message = NULL; + + // Find the crypto message for this filename. + bool result = !!CryptQueryObject(CERT_QUERY_OBJECT_FILE, + filename.value().c_str(), + CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, + CERT_QUERY_FORMAT_FLAG_BINARY, + 0, + NULL, + NULL, + NULL, + &store, + &message, + NULL); + if (!result) + return string16(); + + // Determine the size of the signer info data. + DWORD signer_info_size = 0; + result = !!CryptMsgGetParam(message, + CMSG_SIGNER_INFO_PARAM, + 0, + NULL, + &signer_info_size); + if (!result) + return string16(); + + // Allocate enough space to hold the signer info. + scoped_array<BYTE> signer_info_buffer(new BYTE[signer_info_size]); + CMSG_SIGNER_INFO* signer_info = + reinterpret_cast<CMSG_SIGNER_INFO*>(signer_info_buffer.get()); + + // Obtain the signer info. + result = !!CryptMsgGetParam(message, + CMSG_SIGNER_INFO_PARAM, + 0, + signer_info, + &signer_info_size); + if (!result) + return string16(); + + // Search for the signer certificate. + CERT_INFO CertInfo = {0}; + PCCERT_CONTEXT cert_context = NULL; + CertInfo.Issuer = signer_info->Issuer; + CertInfo.SerialNumber = signer_info->SerialNumber; + + cert_context = CertFindCertificateInStore( + store, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CERT_FIND_SUBJECT_CERT, + &CertInfo, + NULL); + if (!cert_context) + return string16(); + + // Determine the size of the Subject name. + DWORD subject_name_size = 0; + if (!(subject_name_size = CertGetNameString(cert_context, + CERT_NAME_SIMPLE_DISPLAY_TYPE, + 0, + NULL, + NULL, + 0))) { + return string16(); + } + + string16 subject_name; + subject_name.resize(subject_name_size); + + // Get subject name. + if (!(CertGetNameString(cert_context, + CERT_NAME_SIMPLE_DISPLAY_TYPE, + 0, + NULL, + const_cast<LPWSTR>(subject_name.c_str()), + subject_name_size))) { + return string16(); + } + + return subject_name; +} + +// ---------------------------------------------------------------------------- + +void EnumerateModulesModel::ScanNow() { + if (scanning_) + return; // A scan is already in progress. + + lock->Acquire(); // Balanced in DoneScanning(); + + scanning_ = true; + + // Instruct the ModuleEnumerator class to load this on the File thread. + // ScanNow does not block. + if (!module_enumerator_) + module_enumerator_ = new ModuleEnumerator(this); + module_enumerator_->ScanNow(&enumerated_modules_); +} + +ListValue* EnumerateModulesModel::GetModuleList() { + if (scanning_) + return NULL; + + lock->Acquire(); + + if (enumerated_modules_.empty()) { + lock->Release(); + return NULL; + } + + ListValue* list = new ListValue(); + + for (ModuleEnumerator::ModulesVector::const_iterator module = + enumerated_modules_.begin(); + module != enumerated_modules_.end(); ++module) { + DictionaryValue* data = new DictionaryValue(); + data->SetInteger("type", module->type); + data->SetString("type_description", + (module->type == ModuleEnumerator::WINSOCK_MODULE_REGISTRATION) ? + ASCIIToWide("Winsock") : ASCIIToWide("")); + data->SetInteger("status", module->status); + data->SetString("location", module->location); + data->SetString("name", module->name); + data->SetString("product_name", module->product_name); + data->SetString("description", module->description); + data->SetString("version", module->version.empty() ? ASCIIToWide("") : + l10n_util::GetStringF(IDS_CONFLICTS_CHECK_VERSION_STRING, + module->version)); + data->SetString("digital_signer", module->digital_signer); + + // Figure out the possible resolution help string. + string16 actions; + string16 separator = ASCIIToWide(" ") + l10n_util::GetStringUTF16( + IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_SEPERATOR) + + ASCIIToWide(" "); + + if (module->recommended_action & ModuleEnumerator::NONE) { + actions = l10n_util::GetStringUTF16( + IDS_CONFLICTS_CHECK_INVESTIGATING); + } + if (module->recommended_action & ModuleEnumerator::UNINSTALL) { + if (!actions.empty()) + actions += separator; + actions = l10n_util::GetStringUTF16( + IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UNINSTALL); + } + if (module->recommended_action & ModuleEnumerator::UPDATE) { + if (!actions.empty()) + actions += separator; + actions += l10n_util::GetStringUTF16( + IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UPDATE); + } + if (module->recommended_action & ModuleEnumerator::DISABLE) { + if (!actions.empty()) + actions += separator; + actions += l10n_util::GetStringUTF16( + IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_DISABLE); + } + string16 possible_resolution = actions.empty() ? ASCIIToWide("") : + l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_POSSIBLE_ACTIONS) + + ASCIIToWide(" ") + + actions; + data->SetString("possibleResolution", possible_resolution); + data->SetString("help_url", ConstructHelpCenterUrl(*module).spec().c_str()); + + list->Append(data); + } + + lock->Release(); + return list; +} + +EnumerateModulesModel::EnumerateModulesModel() +: scanning_(false), +confirmed_bad_modules_detected_(0), +suspected_bad_modules_detected_(0) { + const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); + if (cmd_line.HasSwitch(switches::kConflictingModulesCheck)) { + check_modules_timer_.Start( + base::TimeDelta::FromMilliseconds(kModuleCheckDelayMs), + this, &EnumerateModulesModel::ScanNow); + } + + lock = new Lock(); +} + +EnumerateModulesModel::~EnumerateModulesModel() { + delete lock; +} + +void EnumerateModulesModel::DoneScanning() { + confirmed_bad_modules_detected_ = 0; + suspected_bad_modules_detected_ = 0; + for (ModuleEnumerator::ModulesVector::const_iterator module = + enumerated_modules_.begin(); + module != enumerated_modules_.end(); ++module) { + if (module->status == ModuleEnumerator::CONFIRMED_BAD) + ++confirmed_bad_modules_detected_; + if (module->status == ModuleEnumerator::SUSPECTED_BAD) + ++suspected_bad_modules_detected_; + } + + scanning_ = false; + lock->Release(); + + NotificationService::current()->Notify( + NotificationType::MODULE_LIST_ENUMERATED, + Source<EnumerateModulesModel>(this), + NotificationService::NoDetails()); + + if (suspected_bad_modules_detected_ || confirmed_bad_modules_detected_) { + bool found_confirmed_bad_modules = confirmed_bad_modules_detected_ > 0; + NotificationService::current()->Notify( + NotificationType::MODULE_INCOMPATIBILITY_DETECTED, + Source<EnumerateModulesModel>(this), + Details<bool>(&found_confirmed_bad_modules)); + } +} + +GURL EnumerateModulesModel::ConstructHelpCenterUrl( + const ModuleEnumerator::Module& module) { + if (!(module.recommended_action & ModuleEnumerator::SEE_LINK)) + return GURL(); + + // Construct the needed hashes. + std::string filename, location, description, signer; + GenerateHash(WideToUTF8(module.name), &filename); + GenerateHash(WideToUTF8(module.location), &location); + GenerateHash(WideToUTF8(module.description), &description); + GenerateHash(WideToUTF8(module.digital_signer), &signer); + + string16 url = l10n_util::GetStringF(IDS_HELP_CENTER_VIEW_CONFLICTS, + ASCIIToWide(filename), ASCIIToWide(location), + ASCIIToWide(description), ASCIIToWide(signer)); + return GURL(WideToUTF8(url)); +} diff --git a/chrome/browser/enumerate_modules_model_win.h b/chrome/browser/enumerate_modules_model_win.h new file mode 100644 index 0000000..54684d0 --- /dev/null +++ b/chrome/browser/enumerate_modules_model_win.h @@ -0,0 +1,259 @@ +// 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_ENUMERATE_MODULES_MODEL_WIN_H_ +#define CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_ +#pragma once + +#include <utility> +#include <vector> + +#include "base/ref_counted.h" +#include "base/singleton.h" +#include "base/string16.h" +#include "base/timer.h" +#include "chrome/browser/browser_thread.h" +#include "googleurl/src/gurl.h" + +class EnumerateModulesModel; +class FilePath; +class ListValue; + +// A helper class that implements the enumerate module functionality on the File +// thread. +class ModuleEnumerator : public base::RefCountedThreadSafe<ModuleEnumerator> { + public: + // What type of module we are dealing with. Loaded modules are modules we + // detect as loaded in the process at the time of scanning. The others are + // modules of interest and may or may not be loaded in the process at the + // time of scan. + enum ModuleType { + LOADED_MODULE, + WINSOCK_MODULE_REGISTRATION, + }; + + // The blacklist status of the module. Suspected Bad modules have been + // partially matched (ie. name matches and location, but not description) + // whereas Confirmed Bad modules have been identified further (ie. + // AuthentiCode signer matches). + enum ModuleStatus { + // This is returned by the matching function when comparing against the + // blacklist and the module does not match the current entry in the + // blacklist. + NOT_MATCHED, + // The module is not on the blacklist. Assume it is good. + GOOD, + // Module is a suspected bad module. + SUSPECTED_BAD, + // Module is a bad bad dog. + CONFIRMED_BAD, + }; + + // A bitmask with the possible resolutions for bad modules. + enum RecommendedAction { + NONE = 0, + INVESTIGATING = 1 << 0, + UNINSTALL = 1 << 1, + DISABLE = 1 << 2, + UPDATE = 1 << 3, + SEE_LINK = 1 << 4, + }; + + // The structure we populate when enumerating modules. + struct Module { + // The type of module found + ModuleType type; + // The module status (benign/bad/etc). + ModuleStatus status; + // The module path, not including filename. + string16 location; + // The name of the module (filename). + string16 name; + // The name of the product the module belongs to. + string16 product_name; + // The module file description. + string16 description; + // The module version. + string16 version; + // The signer of the digital certificate for the module. + string16 digital_signer; + // The help tips bitmask. + RecommendedAction recommended_action; + // Whether this module has been normalized (necessary before checking it + // against blacklist). + bool normalized; + }; + + // A vector typedef of all modules enumerated. + typedef std::vector<Module> ModulesVector; + + // A structure we populate with the blacklist entries. + struct BlacklistEntry { + const char* filename; + const char* location; + const char* desc_or_signer; + const char* version_from; + const char* version_to; + RecommendedAction help_tip; + }; + + // A static function that normalizes the module information in the |module| + // struct. Module information needs to be normalized before comparing against + // the blacklist. This is because the same module can be described in many + // different ways, ie. file paths can be presented in long/short name form, + // and are not case sensitive on Windows. Also, the version string returned + // can include appended text, which we don't want to use during comparison + // against the blacklist. + static void NormalizeModule(Module* module); + + // A static function that checks whether |module| has been |blacklisted|. + static ModuleStatus Match(const Module& module, + const BlacklistEntry& blacklisted); + + explicit ModuleEnumerator(EnumerateModulesModel* observer); + virtual ~ModuleEnumerator(); + + // Start scanning the loaded module list (if a scan is not already in + // progress). This function does not block while reading the module list, but + // will notify when done through the MODULE_LIST_ENUMERATED notification. + // The process will also send MODULE_INCOMPATIBILITY_DETECTED if an + // incompatible module was detected. + void ScanNow(ModulesVector* list); + + private: + // The (currently) hard coded blacklist of known bad modules. + static const BlacklistEntry kModuleBlacklist[]; + + // This function does the actual file scanning work on the FILE thread. It + // enumerates all loaded modules in the process and other modules of + // interest, such as the registered Winsock LSP modules and stores them in + // |enumerated_modules_|. It then normalizes the module info and matches + // them against a blacklist of known bad modules. Finally, it calls + // ReportBack to let the observer know we are done. + void ScanOnFileThread(); + + // Builds up a vector of path values mapping to environment variable, + // with pairs like [c:\windows\, %systemroot%]. This is later used to + // collapse paths like c:\windows\system32 into %systemroot%\system32, which + // we can use for comparison against our blacklist (which uses only env vars). + // NOTE: The vector will not contain an exhaustive list of environment + // variables, only the ones currently found on the blacklist or ones that are + // likely to appear there. + void PreparePathMappings(); + + // For a given |module|, collapse the path from c:\windows to %systemroot%, + // based on the |path_mapping_| vector. + void CollapsePath(Module* module); + + // Takes each module in the |enumerated_modules_| vector and matches it + // against a fixed blacklist of bad and suspected bad modules. + void MatchAgainstBlacklist(); + + // This function executes on the UI thread when the scanning and matching + // process is done. It notifies the observer. + void ReportBack(); + + // Given a filename, returns the Subject (who signed it) retrieved from + // the digital signature (Authenticode). + string16 GetSubjectNameFromDigitalSignature(const FilePath& filename); + + // The typedef for the vector that maps a regular file path to %env_var%. + typedef std::vector< std::pair<string16, string16> > PathMapping; + + // The vector of paths to %env_var%, used to account for differences in + // where people keep there files, c:\windows vs. d:\windows, etc. + PathMapping path_mapping_; + + // The vector containing all the enumerated modules (loaded and modules of + // interest). + ModulesVector* enumerated_modules_; + + // The observer, who needs to be notified when we are done. + EnumerateModulesModel* observer_; + + // The thread that we need to call back on to report that we are done. + BrowserThread::ID callback_thread_id_; + + DISALLOW_COPY_AND_ASSIGN(ModuleEnumerator); +}; + +// This is a singleton class that enumerates all modules loaded into Chrome, +// both currently loaded modules (called DLLs on Windows) and modules 'of +// interest', such as WinSock LSP modules. This class also marks each module +// as benign or suspected bad or outright bad, using a supplied blacklist that +// is currently hard-coded. +// +// To use this class, grab the singleton pointer and call ScanNow(). +// Then wait to get notified through MODULE_LIST_ENUMERATED when the list is +// ready. +// +// This class can be used on the UI thread as it asynchronously offloads the +// file work over to the FILE thread and reports back to the caller with a +// notification. +class EnumerateModulesModel { + public: + static EnumerateModulesModel* GetSingleton() { + return Singleton<EnumerateModulesModel>::get(); + } + + // Returns the number of suspected bad modules found in the last scan. + // Returns 0 if no scan has taken place yet. + int suspected_bad_modules_detected() { + return suspected_bad_modules_detected_; + } + + // Returns the number of confirmed bad modules found in the last scan. + // Returns 0 if no scan has taken place yet. + int confirmed_bad_modules_detected() { + return confirmed_bad_modules_detected_; + } + + // Asynchronously start the scan for the loaded module list. + // When the list is ready. + void ScanNow(); + + // Gets the whole module list as a ListValue. + ListValue* GetModuleList(); + + private: + friend struct DefaultSingletonTraits<EnumerateModulesModel>; + friend class ModuleEnumerator; + + EnumerateModulesModel(); + virtual ~EnumerateModulesModel(); + + // Called on the UI thread when the helper class is done scanning. + void DoneScanning(); + + // Constructs a Help Center article URL for help with a particular module. + // The module must have the SEE_LINK attribute for |recommended_action| set, + // otherwise this returns a blank string. + GURL ConstructHelpCenterUrl(const ModuleEnumerator::Module& module); + + // The vector containing all the modules enumerated. Will be normalized and + // any bad modules will be marked. + ModuleEnumerator::ModulesVector enumerated_modules_; + + // The object responsible for enumerating the modules on the File thread. + scoped_refptr<ModuleEnumerator> module_enumerator_; + + // When this singleton object is constructed we go and fire off this timer to + // start scanning for modules after a certain amount of time has passed. + base::OneShotTimer<EnumerateModulesModel> check_modules_timer_; + + // True if we are currently scanning for modules. + bool scanning_; + + // The number of confirmed bad modules (not including suspected bad ones) + // found during last scan. + int confirmed_bad_modules_detected_; + + // The number of suspected bad modules (not including confirmed bad ones) + // found during last scan. + int suspected_bad_modules_detected_; + + DISALLOW_COPY_AND_ASSIGN(EnumerateModulesModel); +}; + +#endif // CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_ diff --git a/chrome/browser/errorpage_uitest.cc b/chrome/browser/errorpage_uitest.cc index 6446b56..427f067 100644 --- a/chrome/browser/errorpage_uitest.cc +++ b/chrome/browser/errorpage_uitest.cc @@ -163,7 +163,14 @@ TEST_F(ErrorPageTest, IFrameDNSError_GoBackAndForward) { EXPECT_TRUE(WaitForTitleMatching(L"Blah")); } -TEST_F(ErrorPageTest, IFrame404) { +#if defined(OS_WIN) +// Might be related to http://crbug.com/60937 +#define MAYBE_IFrame404 FLAKY_IFrame404 +#else +#define MAYBE_IFrame404 IFrame404 +#endif + +TEST_F(ErrorPageTest, MAYBE_IFrame404) { // iframes that have 404 pages should not trigger an alternate error page. // In this test, the iframe sets the title of the parent page to "SUCCESS" // when the iframe loads. If the iframe fails to load (because an alternate diff --git a/chrome/browser/extensions/alert_apitest.cc b/chrome/browser/extensions/alert_apitest.cc index 23e8fe8..8e70fdb 100644 --- a/chrome/browser/extensions/alert_apitest.cc +++ b/chrome/browser/extensions/alert_apitest.cc @@ -14,7 +14,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, AlertBasic) { ASSERT_TRUE(RunExtensionTest("alert")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ExtensionHost* host = browser()->profile()->GetExtensionProcessManager()-> GetBackgroundHostForExtension(extension); ASSERT_TRUE(host); diff --git a/chrome/browser/extensions/app_background_page_apitest.cc b/chrome/browser/extensions/app_background_page_apitest.cc index d1e78d1..80d2218 100644 --- a/chrome/browser/extensions/app_background_page_apitest.cc +++ b/chrome/browser/extensions/app_background_page_apitest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/string_util.h" #include "chrome/browser/browser.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/common/chrome_switches.h" @@ -14,14 +15,53 @@ class AppBackgroundPageApiTest : public ExtensionApiTest { ExtensionApiTest::SetUpCommandLine(command_line); command_line->AppendSwitch(switches::kDisablePopupBlocking); } + + bool CreateApp(const std::string& app_manifest, + FilePath* app_dir) { + if (!app_dir_.CreateUniqueTempDir()) { + LOG(ERROR) << "Unable to create a temporary directory."; + return false; + } + FilePath manifest_path = app_dir_.path().AppendASCII("manifest.json"); + int bytes_written = file_util::WriteFile(manifest_path, + app_manifest.data(), + app_manifest.size()); + if (bytes_written != static_cast<int>(app_manifest.size())) { + LOG(ERROR) << "Unable to write complete manifest to file. Return code=" + << bytes_written; + return false; + } + *app_dir = app_dir_.path(); + return true; + } + + private: + ScopedTempDir app_dir_; }; IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, Basic) { host_resolver()->AddRule("a.com", "127.0.0.1"); ASSERT_TRUE(StartTestServer()); - LoadExtension(test_data_dir_.AppendASCII( - "app_background_page/app_has_permission")); + std::string app_manifest = StringPrintf( + "{" + " \"name\": \"App\"," + " \"version\": \"0.1\"," + " \"app\": {" + " \"urls\": [" + " \"http://a.com/\"" + " ]," + " \"launch\": {" + " \"web_url\": \"http://a.com:%d/\"" + " }" + " }," + " \"permissions\": [\"background\"]" + "}", + test_server()->host_port_pair().port()); + + FilePath app_dir; + ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); + ASSERT_TRUE(LoadExtension(app_dir)); ASSERT_TRUE(RunExtensionTest("app_background_page/basic")) << message_; } @@ -29,8 +69,24 @@ IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, LacksPermission) { host_resolver()->AddRule("a.com", "127.0.0.1"); ASSERT_TRUE(StartTestServer()); - LoadExtension(test_data_dir_.AppendASCII( - "app_background_page/app_lacks_permission")); + std::string app_manifest = StringPrintf( + "{" + " \"name\": \"App\"," + " \"version\": \"0.1\"," + " \"app\": {" + " \"urls\": [" + " \"http://a.com/\"" + " ]," + " \"launch\": {" + " \"web_url\": \"http://a.com:%d/\"" + " }" + " }" + "}", + test_server()->host_port_pair().port()); + + FilePath app_dir; + ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); + ASSERT_TRUE(LoadExtension(app_dir)); ASSERT_TRUE(RunExtensionTest("app_background_page/lacks_permission")) << message_; } diff --git a/chrome/browser/extensions/autoupdate_interceptor.cc b/chrome/browser/extensions/autoupdate_interceptor.cc index 2acd0a0..ef0091f 100644 --- a/chrome/browser/extensions/autoupdate_interceptor.cc +++ b/chrome/browser/extensions/autoupdate_interceptor.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/autoupdate_interceptor.h" #include "base/file_util.h" +#include "base/thread_restrictions.h" #include "chrome/browser/browser_thread.h" #include "net/url_request/url_request_test_job.h" #include "testing/gtest/include/gtest/gtest.h" @@ -40,6 +41,10 @@ URLRequestJob* AutoUpdateInterceptor::MaybeIntercept(URLRequest* request) { return NULL; } + // It's ok to do a blocking disk access on this thread; this class + // is just used for tests. + base::ThreadRestrictions::ScopedAllowIO allow_io; + // Search for this request's url, ignoring any query parameters. GURL url = request->url(); if (url.has_query()) { @@ -61,6 +66,9 @@ URLRequestJob* AutoUpdateInterceptor::MaybeIntercept(URLRequest* request) { void AutoUpdateInterceptor::SetResponse(const std::string url, const FilePath& path) { EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // It's ok to do a blocking disk access on this thread; this class + // is just used for tests. + base::ThreadRestrictions::ScopedAllowIO allow_io; GURL gurl(url); EXPECT_EQ("http", gurl.scheme()); EXPECT_EQ("localhost", gurl.host()); diff --git a/chrome/browser/extensions/browser_action_apitest.cc b/chrome/browser/extensions/browser_action_apitest.cc index 8b09783..5e76310 100644 --- a/chrome/browser/extensions/browser_action_apitest.cc +++ b/chrome/browser/extensions/browser_action_apitest.cc @@ -47,7 +47,7 @@ class BrowserActionApiTest : public ExtensionApiTest { IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, Basic) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(RunExtensionTest("browser_action/basics")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // Test that there is a browser action in the toolbar. @@ -87,7 +87,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, Basic) { IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DynamicBrowserAction) { ASSERT_TRUE(RunExtensionTest("browser_action/no_icon")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // Test that there is a browser action in the toolbar and that it has no icon. @@ -117,7 +117,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DynamicBrowserAction) { IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, TabSpecificBrowserActionState) { ASSERT_TRUE(RunExtensionTest("browser_action/tab_specific_state")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // Test that there is a browser action in the toolbar and that it has an icon. @@ -147,7 +147,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionPopup) { ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII( "browser_action/popup"))); BrowserActionTestUtil actions_bar = GetBrowserActionsBar(); - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // The extension's popup's size grows by |growFactor| each click. @@ -180,7 +180,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionPopup) { // a popup. IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionAddPopup) { ASSERT_TRUE(RunExtensionTest("browser_action/add_popup")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents()); @@ -235,7 +235,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionAddPopup) { IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionRemovePopup) { // Load the extension, which has a browser action with a default popup. ASSERT_TRUE(RunExtensionTest("browser_action/remove_popup")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents()); @@ -269,7 +269,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoBasic) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(RunExtensionTest("browser_action/basics")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // Test that there is a browser action in the toolbar. diff --git a/chrome/browser/extensions/content_script_all_frames_apitest.cc b/chrome/browser/extensions/content_script_all_frames_apitest.cc index fac6362..b465c42 100644 --- a/chrome/browser/extensions/content_script_all_frames_apitest.cc +++ b/chrome/browser/extensions/content_script_all_frames_apitest.cc @@ -5,11 +5,11 @@ #include "chrome/browser/extensions/extension_apitest.h" IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptAllFrames) { - ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(StartTestServer()); ASSERT_TRUE(RunExtensionTest("content_scripts/all_frames")) << message_; } IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptExtensionIframe) { - ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(StartTestServer()); ASSERT_TRUE(RunExtensionTest("content_scripts/extension_iframe")) << message_; } diff --git a/chrome/browser/extensions/content_script_extension_process_apitest.cc b/chrome/browser/extensions/content_script_extension_process_apitest.cc index 46729d2..64574a0 100644 --- a/chrome/browser/extensions/content_script_extension_process_apitest.cc +++ b/chrome/browser/extensions/content_script_extension_process_apitest.cc @@ -10,6 +10,7 @@ #include "chrome/test/ui_test_utils.h" IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptExtensionProcess) { - ASSERT_TRUE(test_server()->Start()); - ASSERT_TRUE(RunExtensionTest("content_scripts/extension_process")) << message_; + ASSERT_TRUE(StartTestServer()); + ASSERT_TRUE( + RunExtensionTest("content_scripts/extension_process")) << message_; } diff --git a/chrome/browser/extensions/convert_user_script.cc b/chrome/browser/extensions/convert_user_script.cc index d50bec1..89caec4 100644 --- a/chrome/browser/extensions/convert_user_script.cc +++ b/chrome/browser/extensions/convert_user_script.cc @@ -24,9 +24,9 @@ namespace keys = extension_manifest_keys; -Extension* ConvertUserScriptToExtension(const FilePath& user_script_path, - const GURL& original_url, - std::string* error) { +scoped_refptr<Extension> ConvertUserScriptToExtension( + const FilePath& user_script_path, const GURL& original_url, + std::string* error) { std::string content; if (!file_util::ReadFileToString(user_script_path, &content)) { *error = "Could not read source file: " + @@ -138,12 +138,13 @@ Extension* ConvertUserScriptToExtension(const FilePath& user_script_path, return NULL; } - scoped_ptr<Extension> extension(new Extension(temp_dir.path())); - if (!extension->InitFromValue(*root, false, error)) { + scoped_refptr<Extension> extension = Extension::Create( + temp_dir.path(), Extension::INTERNAL, *root, false, error); + if (!extension) { NOTREACHED() << "Could not init extension " << *error; return NULL; } temp_dir.Take(); // The caller takes ownership of the directory. - return extension.release(); + return extension; } diff --git a/chrome/browser/extensions/convert_user_script.h b/chrome/browser/extensions/convert_user_script.h index d392c4d..d779de0 100644 --- a/chrome/browser/extensions/convert_user_script.h +++ b/chrome/browser/extensions/convert_user_script.h @@ -8,6 +8,8 @@ #include <string> +#include "base/ref_counted.h" + class Extension; class FilePath; class GURL; @@ -17,8 +19,7 @@ class GURL; // should take ownership on success, or NULL and |error| on failure. // // NOTE: This function does file IO and should not be called on the UI thread. -Extension* ConvertUserScriptToExtension(const FilePath& user_script, - const GURL& original_url, - std::string* error); +scoped_refptr<Extension> ConvertUserScriptToExtension( + const FilePath& user_script, const GURL& original_url, std::string* error); #endif // CHROME_BROWSER_EXTENSIONS_CONVERT_USER_SCRIPT_H_ diff --git a/chrome/browser/extensions/convert_user_script_unittest.cc b/chrome/browser/extensions/convert_user_script_unittest.cc index 299fee7..a0e1b8b 100644 --- a/chrome/browser/extensions/convert_user_script_unittest.cc +++ b/chrome/browser/extensions/convert_user_script_unittest.cc @@ -21,7 +21,7 @@ TEST(ExtensionFromUserScript, Basic) { .AppendASCII("user_script_basic.user.js"); std::string error; - scoped_ptr<Extension> extension(ConvertUserScriptToExtension( + scoped_refptr<Extension> extension(ConvertUserScriptToExtension( test_file, GURL("http://www.google.com/foo"), &error)); ASSERT_TRUE(extension.get()); @@ -61,7 +61,7 @@ TEST(ExtensionFromUserScript, NoMetdata) { .AppendASCII("user_script_no_metadata.user.js"); std::string error; - scoped_ptr<Extension> extension(ConvertUserScriptToExtension( + scoped_refptr<Extension> extension(ConvertUserScriptToExtension( test_file, GURL("http://www.google.com/foo/bar.user.js?monkey"), &error)); ASSERT_TRUE(extension.get()); diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index 6f34b3c..b2c60e3 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -4,6 +4,8 @@ #include "chrome/browser/extensions/crx_installer.h" +#include <list> + #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/file_util.h" @@ -12,6 +14,7 @@ #include "base/singleton.h" #include "base/stringprintf.h" #include "base/task.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "base/version.h" #include "chrome/browser/browser_process.h" @@ -45,7 +48,7 @@ struct WhitelistedInstallData { std::list<std::string> ids; }; -} +} // namespace // static void CrxInstaller::SetWhitelistedInstallId(const std::string& id) { @@ -55,6 +58,7 @@ void CrxInstaller::SetWhitelistedInstallId(const std::string& id) { // static bool CrxInstaller::ClearWhitelistedInstallId(const std::string& id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); std::list<std::string>& ids = Singleton<WhitelistedInstallData>::get()->ids; std::list<std::string>::iterator iter = ids.begin(); for (; iter != ids.end(); ++iter) { @@ -112,7 +116,12 @@ void CrxInstaller::InstallCrx(const FilePath& source_file) { source_file_ = source_file; FilePath user_data_temp_dir; - CHECK(PathService::Get(chrome::DIR_USER_DATA_TEMP, &user_data_temp_dir)); + { + // We shouldn't be doing disk IO on the UI thread. + // http://code.google.com/p/chromium/issues/detail?id=60634 + base::ThreadRestrictions::ScopedAllowIO allow_io; + CHECK(PathService::Get(chrome::DIR_USER_DATA_TEMP, &user_data_temp_dir)); + } scoped_refptr<SandboxedExtensionUnpacker> unpacker( new SandboxedExtensionUnpacker( @@ -141,8 +150,8 @@ void CrxInstaller::InstallUserScript(const FilePath& source_file, void CrxInstaller::ConvertUserScriptOnFileThread() { std::string error; - Extension* extension = ConvertUserScriptToExtension(source_file_, - original_url_, &error); + scoped_refptr<Extension> extension = + ConvertUserScriptToExtension(source_file_, original_url_, &error); if (!extension) { ReportFailureFromFileThread(error); return; @@ -151,7 +160,8 @@ void CrxInstaller::ConvertUserScriptOnFileThread() { OnUnpackSuccess(extension->path(), extension->path(), extension); } -bool CrxInstaller::AllowInstall(Extension* extension, std::string* error) { +bool CrxInstaller::AllowInstall(const Extension* extension, + std::string* error) { DCHECK(error); // We always allow themes and external installs. @@ -233,11 +243,11 @@ void CrxInstaller::OnUnpackFailure(const std::string& error_message) { void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, const FilePath& extension_dir, - Extension* extension) { + const Extension* extension) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); // Note: We take ownership of |extension| and |temp_dir|. - extension_.reset(extension); + extension_ = extension; temp_dir_ = temp_dir; // We don't have to delete the unpack dir explicity since it is a child of @@ -263,8 +273,8 @@ void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, void CrxInstaller::ConfirmInstall() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (frontend_->extension_prefs()->IsExtensionBlacklisted(extension_->id())) { - LOG(INFO) << "This extension: " << extension_->id() - << " is blacklisted. Install failed."; + VLOG(1) << "This extension: " << extension_->id() + << " is blacklisted. Install failed."; ReportFailureFromUIThread("This extension is blacklisted."); return; } @@ -276,7 +286,7 @@ void CrxInstaller::ConfirmInstall() { } GURL overlapping_url; - Extension* overlapping_extension = + const Extension* overlapping_extension = frontend_->GetExtensionByOverlappingWebExtent(extension_->web_extent()); if (overlapping_extension) { ReportFailureFromUIThread(l10n_util::GetStringFUTF8( @@ -288,9 +298,11 @@ void CrxInstaller::ConfirmInstall() { current_version_ = frontend_->extension_prefs()->GetVersionString(extension_->id()); + bool whitelisted = ClearWhitelistedInstallId(extension_->id()) && + extension_->plugins().empty(); + if (client_ && - (!allow_silent_install_ || - !ClearWhitelistedInstallId(extension_->id()))) { + (!allow_silent_install_ || !whitelisted)) { AddRef(); // Balanced in Proceed() and Abort(). client_->ConfirmInstall(this, extension_.get()); } else { @@ -351,8 +363,8 @@ void CrxInstaller::CompleteInstall() { // TODO(aa): All paths to resources inside extensions should be created // lazily and based on the Extension's root path at that moment. std::string error; - extension_.reset(extension_file_util::LoadExtension( - version_dir, install_source_, true, &error)); + extension_ = extension_file_util::LoadExtension( + version_dir, install_source_, true, &error); DCHECK(error.empty()); ReportSuccessFromFileThread(); @@ -400,8 +412,8 @@ void CrxInstaller::ReportSuccessFromUIThread() { // Tell the frontend about the installation and hand off ownership of // extension_ to it. - frontend_->OnExtensionInstalled(extension_.release(), - allow_privilege_increase_); + frontend_->OnExtensionInstalled(extension_, allow_privilege_increase_); + extension_ = NULL; // We're done. We don't post any more tasks to ourselves so we are deleted // soon. diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h index b1212a2..349b225 100644 --- a/chrome/browser/extensions/crx_installer.h +++ b/chrome/browser/extensions/crx_installer.h @@ -49,11 +49,12 @@ class CrxInstaller // ExtensionFunction to a resulting download in the download manager, it's // currently necessary. This is the |id| of an extension to be installed // *by the web store only* which should not get the permissions install - // prompt. + // prompt. This should only be called on the UI thread. // crbug.com/54916 static void SetWhitelistedInstallId(const std::string& id); - // Returns whether |id| was found and removed (was whitelisted). + // Returns whether |id| was found and removed (was whitelisted). This should + // only be called on the UI thread. static bool ClearWhitelistedInstallId(const std::string& id); // Constructor. Extensions will be unpacked to |install_directory|. @@ -120,13 +121,13 @@ class CrxInstaller // Called after OnUnpackSuccess as a last check to see whether the install // should complete. - bool AllowInstall(Extension* extension, std::string* error); + bool AllowInstall(const Extension* extension, std::string* error); // SandboxedExtensionUnpackerClient virtual void OnUnpackFailure(const std::string& error_message); virtual void OnUnpackSuccess(const FilePath& temp_dir, const FilePath& extension_dir, - Extension* extension); + const Extension* extension); // Runs on the UI thread. Confirms with the user (via ExtensionInstallUI) that // it is OK to install this extension. @@ -187,7 +188,7 @@ class CrxInstaller // The extension we're installing. We own this and either pass it off to // ExtensionsService on success, or delete it on failure. - scoped_ptr<Extension> extension_; + scoped_refptr<const Extension> extension_; // If non-empty, contains the current version of the extension we're // installing (for upgrades). diff --git a/chrome/browser/extensions/crx_installer_browsertest.cc b/chrome/browser/extensions/crx_installer_browsertest.cc new file mode 100644 index 0000000..535efc9 --- /dev/null +++ b/chrome/browser/extensions/crx_installer_browsertest.cc @@ -0,0 +1,77 @@ +// 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/browser.h" +#include "chrome/browser/extensions/crx_installer.h" +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extension_install_ui.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/profile.h" +#include "chrome/test/ui_test_utils.h" + +namespace { + +class MockInstallUI : public ExtensionInstallUI { + public: + explicit MockInstallUI(Profile* profile) : + ExtensionInstallUI(profile), confirmation_requested_(false) {} + + // Did the Delegate request confirmation? + bool confirmation_requested() { return confirmation_requested_; } + + // Overriding some of the ExtensionInstallUI API. + void ConfirmInstall(Delegate* delegate, const Extension* extension) { + confirmation_requested_ = true; + delegate->InstallUIProceed(); + } + void OnInstallSuccess(const Extension* extension) { + MessageLoopForUI::current()->Quit(); + } + void OnInstallFailure(const std::string& error) { + ADD_FAILURE() << "insall failed"; + MessageLoopForUI::current()->Quit(); + } + + private: + bool confirmation_requested_; +}; + +} // namespace + +class ExtensionCrxInstallerTest : public ExtensionBrowserTest { + public: + // Installs a crx from |crx_relpath| (a path relative to the extension test + // data dir) with expected id |id|. Returns whether a confirmation prompt + // happened or not. + bool DidWhitelistInstallPrompt(const std::string& crx_relpath, + const std::string& id) { + ExtensionsService* service = browser()->profile()->GetExtensionsService(); + MockInstallUI* mock_install_ui = new MockInstallUI(browser()->profile()); + + scoped_refptr<CrxInstaller> installer( + new CrxInstaller(service->install_directory(), + service, + mock_install_ui /* ownership transferred */)); + + installer->set_allow_silent_install(true); + CrxInstaller::SetWhitelistedInstallId(id); + + FilePath crx_path = test_data_dir_.AppendASCII(crx_relpath); + installer->InstallCrx(crx_path); + ui_test_utils::RunMessageLoop(); + + return mock_install_ui->confirmation_requested(); + } +}; + +IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, Whitelisting) { + // A regular extension should give no prompt. + EXPECT_FALSE(DidWhitelistInstallPrompt("good.crx", + "ldnnhddmnhbkjipkidpdiheffobcpfmf")); +#if !defined(OS_CHROMEOS) + // An extension with NPAPI should give a prompt. + EXPECT_TRUE(DidWhitelistInstallPrompt("uitest/plugins.crx", + "hdgllgikmikobbofgnabhfimcfoopgnd")); +#endif // !defined(OS_CHROMEOS +} diff --git a/chrome/browser/extensions/default_apps.cc b/chrome/browser/extensions/default_apps.cc index ad39a34..1dff210 100644 --- a/chrome/browser/extensions/default_apps.cc +++ b/chrome/browser/extensions/default_apps.cc @@ -19,10 +19,12 @@ void DefaultApps::RegisterUserPrefs(PrefService* prefs) { DefaultApps::DefaultApps(PrefService* prefs) : prefs_(prefs) { +#if !defined(OS_CHROMEOS) // gmail, calendar, docs ids_.insert("pjkljhegncpnkpknbcohdijeoejaedia"); ids_.insert("ejjicmeblgpmajnghnpcppodonldlgfn"); ids_.insert("apdfllckaahabafndbhieahigkjlhalf"); +#endif // OS_CHROMEOS } DefaultApps::~DefaultApps() {} diff --git a/chrome/browser/extensions/default_apps_unittest.cc b/chrome/browser/extensions/default_apps_unittest.cc index 8037236..24a9d70 100644 --- a/chrome/browser/extensions/default_apps_unittest.cc +++ b/chrome/browser/extensions/default_apps_unittest.cc @@ -7,6 +7,8 @@ #include "chrome/test/testing_pref_service.h" #include "testing/gtest/include/gtest/gtest.h" +// TODO(dpolukhin): On Chrome OS all apps are installed via external extensions +#if !defined(OS_CHROMEOS) TEST(DefaultApps, Basics) { TestingPrefService pref_service; DefaultApps::RegisterUserPrefs(&pref_service); @@ -59,6 +61,7 @@ TEST(DefaultApps, Basics) { EXPECT_FALSE(default_apps.ShouldShowPromo(default_app_ids)); EXPECT_EQ(DefaultApps::kAppsPromoCounterMax, default_apps.GetPromoCounter()); } +#endif // OS_CHROMEOS TEST(DefaultApps, HidePromo) { TestingPrefService pref_service; @@ -97,6 +100,8 @@ TEST(DefaultApps, InstallingAnAppHidesPromo) { EXPECT_EQ(DefaultApps::kAppsPromoCounterMax, default_apps.GetPromoCounter()); } +// TODO(dpolukhin): On Chrome OS all apps are installed via external extensions +#if !defined(OS_CHROMEOS) TEST(DefaultApps, ManualAppInstalledWhileInstallingDefaultApps) { // It is possible to have apps manually installed while the default apps are // being installed. The network or server might be down, causing the default @@ -131,3 +136,4 @@ TEST(DefaultApps, ManualAppInstalledWhileInstallingDefaultApps) { EXPECT_FALSE(default_apps.ShouldShowPromo(installed_ids)); EXPECT_EQ(DefaultApps::kAppsPromoCounterMax, default_apps.GetPromoCounter()); } +#endif // OS_CHROMEOS diff --git a/chrome/browser/extensions/execute_code_in_tab_function.cc b/chrome/browser/extensions/execute_code_in_tab_function.cc index b7bf999..8021e6c 100644 --- a/chrome/browser/extensions/execute_code_in_tab_function.cc +++ b/chrome/browser/extensions/execute_code_in_tab_function.cc @@ -78,7 +78,7 @@ bool ExecuteCodeInTabFunction::RunImpl() { // NOTE: This can give the wrong answer due to race conditions, but it is OK, // we check again in the renderer. - Extension* extension = GetExtension(); + const Extension* extension = GetExtension(); const std::vector<URLPattern> host_permissions = extension->host_permissions(); if (!Extension::CanExecuteScriptOnPage( @@ -158,7 +158,7 @@ bool ExecuteCodeInTabFunction::Execute(const std::string& code_string) { return false; } - Extension* extension = GetExtension(); + const Extension* extension = GetExtension(); if (!extension) { SendResponse(false); return false; diff --git a/chrome/browser/extensions/execute_script_apitest.cc b/chrome/browser/extensions/execute_script_apitest.cc index a450fe0..9edd0b6 100644 --- a/chrome/browser/extensions/execute_script_apitest.cc +++ b/chrome/browser/extensions/execute_script_apitest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 20109 The Chromium Authors. All rights reserved. +// 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. diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc index d136b50..07668b3 100644 --- a/chrome/browser/extensions/extension_apitest.cc +++ b/chrome/browser/extensions/extension_apitest.cc @@ -13,10 +13,6 @@ #include "chrome/common/notification_registrar.h" #include "chrome/test/ui_test_utils.h" -#if defined(OS_CHROMEOS) -#include "chrome/browser/chromeos/views/domui_menu_widget.h" -#endif - namespace { const char kTestServerPort[] = "testServer.port"; @@ -68,7 +64,7 @@ void ExtensionApiTest::ResultCatcher::Observe( switch (type.value) { case NotificationType::EXTENSION_TEST_PASSED: - LOG(INFO) << "Got EXTENSION_TEST_PASSED notification."; + VLOG(1) << "Got EXTENSION_TEST_PASSED notification."; results_.push_back(true); messages_.push_back(""); if (waiting_) @@ -76,7 +72,7 @@ void ExtensionApiTest::ResultCatcher::Observe( break; case NotificationType::EXTENSION_TEST_FAILED: - LOG(INFO) << "Got EXTENSION_TEST_FAILED notification."; + VLOG(1) << "Got EXTENSION_TEST_FAILED notification."; results_.push_back(false); messages_.push_back(*(Details<std::string>(details).ptr())); if (waiting_) @@ -122,13 +118,6 @@ bool ExtensionApiTest::RunPageTest(const std::string& page_url) { bool ExtensionApiTest::RunExtensionTestImpl(const char* extension_name, const std::string& page_url, bool enable_incognito) { -#if defined(OS_CHROMEOS) - // ChromeOS uses DOMUI for menu and creates a stand-by renderer to - // improve 1st time response. This can confuse this test as the test - // uses AllSources to listen to notifications, thus disabling the warmup - // process for ExtensionAPITests. - chromeos::DOMUIMenuWidget::DisableWarmUp(); -#endif ResultCatcher catcher; DCHECK(!std::string(extension_name).empty() || !page_url.empty()) << "extension_name and page_url cannot both be empty"; @@ -155,7 +144,7 @@ bool ExtensionApiTest::RunExtensionTestImpl(const char* extension_name, "Relative page_url given with no extension_name"; ExtensionsService* service = browser()->profile()->GetExtensionsService(); - Extension* extension = + const Extension* extension = service->GetExtensionById(last_loaded_extension_id_, false); if (!extension) return false; @@ -176,7 +165,7 @@ bool ExtensionApiTest::RunExtensionTestImpl(const char* extension_name, } // Test that exactly one extension loaded. -Extension* ExtensionApiTest::GetSingleLoadedExtension() { +const Extension* ExtensionApiTest::GetSingleLoadedExtension() { ExtensionsService* service = browser()->profile()->GetExtensionsService(); int found_extension_index = -1; @@ -196,7 +185,7 @@ Extension* ExtensionApiTest::GetSingleLoadedExtension() { found_extension_index = static_cast<int>(i); } - Extension* extension = service->extensions()->at(found_extension_index); + const Extension* extension = service->extensions()->at(found_extension_index); if (!extension) { message_ = "extension pointer is NULL."; return NULL; diff --git a/chrome/browser/extensions/extension_apitest.h b/chrome/browser/extensions/extension_apitest.h index b0a0e01..b2079a6 100644 --- a/chrome/browser/extensions/extension_apitest.h +++ b/chrome/browser/extensions/extension_apitest.h @@ -94,7 +94,7 @@ class ExtensionApiTest : public ExtensionBrowserTest { // Test that exactly one extension loaded. If so, return a pointer to // the extension. If not, return NULL and set message_. - Extension* GetSingleLoadedExtension(); + const Extension* GetSingleLoadedExtension(); // All extensions tested by ExtensionApiTest are in the "api_test" dir. virtual void SetUpCommandLine(CommandLine* command_line); diff --git a/chrome/browser/extensions/extension_browser_actions_api.cc b/chrome/browser/extensions/extension_browser_actions_api.cc index 1ce4dfe..1986d28 100644 --- a/chrome/browser/extensions/extension_browser_actions_api.cc +++ b/chrome/browser/extensions/extension_browser_actions_api.cc @@ -26,7 +26,7 @@ bool BrowserActionFunction::RunImpl() { if (details_->HasKey("tabId")) EXTENSION_FUNCTION_VALIDATE(details_->GetInteger("tabId", &tab_id_)); - Extension* extension = GetExtension(); + const Extension* extension = GetExtension(); browser_action_ = extension->browser_action(); if (!browser_action_) { error_ = kNoBrowserActionError; diff --git a/chrome/browser/extensions/extension_browser_event_router.cc b/chrome/browser/extensions/extension_browser_event_router.cc index ce58dc7..dcf9b9d 100644 --- a/chrome/browser/extensions/extension_browser_event_router.cc +++ b/chrome/browser/extensions/extension_browser_event_router.cc @@ -270,7 +270,7 @@ void ExtensionBrowserEventRouter::TabInsertedAt(TabContents* contents, bool foreground) { // If tab is new, send created event. int tab_id = ExtensionTabUtil::GetTabId(contents); - if (tab_entries_.find(tab_id) == tab_entries_.end()) { + if (!GetTabEntry(contents)) { tab_entries_[tab_id] = TabEntry(); TabCreatedAt(contents, index, foreground); @@ -295,14 +295,13 @@ void ExtensionBrowserEventRouter::TabInsertedAt(TabContents* contents, void ExtensionBrowserEventRouter::TabDetachedAt(TabContents* contents, int index) { - int tab_id = ExtensionTabUtil::GetTabId(contents); - if (tab_entries_.find(tab_id) == tab_entries_.end()) { + if (!GetTabEntry(contents)) { // The tab was removed. Don't send detach event. return; } ListValue args; - args.Append(Value::CreateIntegerValue(tab_id)); + args.Append(Value::CreateIntegerValue(ExtensionTabUtil::GetTabId(contents))); DictionaryValue* object_args = new DictionaryValue(); object_args->Set(tab_keys::kOldWindowIdKey, Value::CreateIntegerValue( @@ -384,36 +383,51 @@ void ExtensionBrowserEventRouter::TabMoved(TabContents* contents, void ExtensionBrowserEventRouter::TabUpdated(TabContents* contents, bool did_navigate) { - int tab_id = ExtensionTabUtil::GetTabId(contents); - std::map<int, TabEntry>::iterator i = tab_entries_.find(tab_id); - DCHECK(tab_entries_.end() != i); - TabEntry& entry = i->second; - + TabEntry* entry = GetTabEntry(contents); DictionaryValue* changed_properties = NULL; + + DCHECK(entry); + if (did_navigate) - changed_properties = entry.DidNavigate(contents); + changed_properties = entry->DidNavigate(contents); else - changed_properties = entry.UpdateLoadState(contents); + changed_properties = entry->UpdateLoadState(contents); + + if (changed_properties) + DispatchTabUpdatedEvent(contents, changed_properties); +} - if (changed_properties) { - // The state of the tab (as seen from the extension point of view) has - // changed. Send a notification to the extension. - ListValue args; +void ExtensionBrowserEventRouter::DispatchTabUpdatedEvent( + TabContents* contents, DictionaryValue* changed_properties) { + DCHECK(changed_properties); + DCHECK(contents); - // First arg: The id of the tab that changed. - args.Append(Value::CreateIntegerValue(tab_id)); + // The state of the tab (as seen from the extension point of view) has + // changed. Send a notification to the extension. + ListValue args; - // Second arg: An object containing the changes to the tab state. - args.Append(changed_properties); + // First arg: The id of the tab that changed. + args.Append(Value::CreateIntegerValue(ExtensionTabUtil::GetTabId(contents))); - // Third arg: An object containing the state of the tab. - args.Append(ExtensionTabUtil::CreateTabValue(contents)); + // Second arg: An object containing the changes to the tab state. + args.Append(changed_properties); - std::string json_args; - base::JSONWriter::Write(&args, false, &json_args); + // Third arg: An object containing the state of the tab. + args.Append(ExtensionTabUtil::CreateTabValue(contents)); - DispatchEvent(contents->profile(), events::kOnTabUpdated, json_args); - } + std::string json_args; + base::JSONWriter::Write(&args, false, &json_args); + + DispatchEvent(contents->profile(), events::kOnTabUpdated, json_args); +} + +ExtensionBrowserEventRouter::TabEntry* ExtensionBrowserEventRouter::GetTabEntry( + const TabContents* contents) { + int tab_id = ExtensionTabUtil::GetTabId(contents); + std::map<int, TabEntry>::iterator i = tab_entries_.find(tab_id); + if (tab_entries_.end() == i) + return NULL; + return &i->second; } void ExtensionBrowserEventRouter::Observe(NotificationType type, @@ -455,6 +469,19 @@ void ExtensionBrowserEventRouter::TabReplacedAt(TabContents* old_contents, RegisterForTabNotifications(new_contents); } +void ExtensionBrowserEventRouter::TabPinnedStateChanged(TabContents* contents, + int index) { + TabStripModel* tab_strip = NULL; + int tab_index; + + if (ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index)) { + DictionaryValue* changed_properties = new DictionaryValue(); + changed_properties->SetBoolean(tab_keys::kPinnedKey, + tab_strip->IsTabPinned(tab_index)); + DispatchTabUpdatedEvent(contents, changed_properties); + } +} + void ExtensionBrowserEventRouter::TabStripEmpty() {} void ExtensionBrowserEventRouter::DispatchOldPageActionEvent( diff --git a/chrome/browser/extensions/extension_browser_event_router.h b/chrome/browser/extensions/extension_browser_event_router.h index bfd2205..a84749d 100644 --- a/chrome/browser/extensions/extension_browser_event_router.h +++ b/chrome/browser/extensions/extension_browser_event_router.h @@ -74,6 +74,7 @@ class ExtensionBrowserEventRouter : public TabStripModelObserver, virtual void TabReplacedAt(TabContents* old_contents, TabContents* new_contents, int index); + virtual void TabPinnedStateChanged(TabContents* contents, int index); virtual void TabStripEmpty(); // Page Action execute event. @@ -100,6 +101,11 @@ class ExtensionBrowserEventRouter : public TabStripModelObserver, // and Observe/NAV_ENTRY_COMMITTED. void TabUpdated(TabContents* contents, bool did_navigate); + // Packages |changed_properties| as a tab updated event for the tab |contents| + // and dispatches the event to the extension. + void DispatchTabUpdatedEvent(TabContents* contents, + DictionaryValue* changed_properties); + // Called to dispatch a deprecated style page action click event that was // registered like: // chrome.pageActions["name"].addListener(function(actionId, info){}) @@ -164,6 +170,10 @@ class ExtensionBrowserEventRouter : public TabStripModelObserver, GURL url_; }; + // Gets the TabEntry for the given |contents|. Returns TabEntry* if + // found, NULL if not. + TabEntry* GetTabEntry(const TabContents* contents); + std::map<int, TabEntry> tab_entries_; // The currently focused window. We keep this so as to avoid sending multiple diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index aa5b46a..af8a6de 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc @@ -58,7 +58,6 @@ void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) { bool ExtensionBrowserTest::LoadExtensionImpl(const FilePath& path, bool incognito_enabled) { ExtensionsService* service = browser()->profile()->GetExtensionsService(); - size_t num_before = service->extensions()->size(); { NotificationRegistrar registrar; registrar.Add(this, NotificationType::EXTENSION_LOADED, @@ -66,15 +65,26 @@ bool ExtensionBrowserTest::LoadExtensionImpl(const FilePath& path, service->LoadExtension(path); ui_test_utils::RunMessageLoop(); } - size_t num_after = service->extensions()->size(); - if (num_after != (num_before + 1)) + + // Find the extension by iterating backwards since it is likely last. + FilePath extension_path = path; + file_util::AbsolutePath(&extension_path); + const Extension* extension = NULL; + for (ExtensionList::const_reverse_iterator iter = + service->extensions()->rbegin(); + iter != service->extensions()->rend(); ++iter) { + if ((*iter)->path() == extension_path) { + extension = *iter; + break; + } + } + if (!extension) return false; if (incognito_enabled) { // Enable the incognito bit in the extension prefs. The call to // OnExtensionInstalled ensures the other extension prefs are set up with // the defaults. - Extension* extension = service->extensions()->at(num_after - 1); service->extension_prefs()->OnExtensionInstalled( extension, Extension::ENABLED, false); service->SetIsIncognitoEnabled(extension, true); @@ -97,14 +107,15 @@ class MockAbortExtensionInstallUI : public ExtensionInstallUI { MockAbortExtensionInstallUI() : ExtensionInstallUI(NULL) {} // Simulate a user abort on an extension installation. - virtual void ConfirmInstall(Delegate* delegate, Extension* extension) { + virtual void ConfirmInstall(Delegate* delegate, const Extension* extension) { delegate->InstallUIAbort(); MessageLoopForUI::current()->Quit(); } - virtual void ConfirmUninstall(Delegate* delegate, Extension* extension) {} + virtual void ConfirmUninstall(Delegate* delegate, + const Extension* extension) {} - virtual void OnInstallSuccess(Extension* extension) {} + virtual void OnInstallSuccess(const Extension* extension) {} virtual void OnInstallFailure(const std::string& error) {} }; @@ -142,21 +153,19 @@ bool ExtensionBrowserTest::InstallOrUpdateExtension(const std::string& id, size_t num_after = service->extensions()->size(); if (num_after != (num_before + expected_change)) { - LOG(INFO) << "Num extensions before: " - << base::IntToString(num_before) << " " - << "num after: " << base::IntToString(num_after) << " " - << "Installed extensions follow:"; + VLOG(1) << "Num extensions before: " << base::IntToString(num_before) + << " num after: " << base::IntToString(num_after) + << " Installed extensions follow:"; for (size_t i = 0; i < service->extensions()->size(); ++i) - LOG(INFO) << " " << service->extensions()->at(i)->id(); + VLOG(1) << " " << (*service->extensions())[i]->id(); - LOG(INFO) << "Errors follow:"; + VLOG(1) << "Errors follow:"; const std::vector<std::string>* errors = ExtensionErrorReporter::GetInstance()->GetErrors(); for (std::vector<std::string>::const_iterator iter = errors->begin(); - iter != errors->end(); ++iter) { - LOG(INFO) << *iter; - } + iter != errors->end(); ++iter) + VLOG(1) << *iter; return false; } @@ -280,48 +289,47 @@ void ExtensionBrowserTest::Observe(NotificationType type, const NotificationDetails& details) { switch (type.value) { case NotificationType::EXTENSION_LOADED: - last_loaded_extension_id_ = Details<Extension>(details).ptr()->id(); - LOG(INFO) << "Got EXTENSION_LOADED notification."; + last_loaded_extension_id_ = Details<const Extension>(details).ptr()->id(); + VLOG(1) << "Got EXTENSION_LOADED notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_UPDATE_DISABLED: - LOG(INFO) << "Got EXTENSION_UPDATE_DISABLED notification."; + VLOG(1) << "Got EXTENSION_UPDATE_DISABLED notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: - LOG(INFO) << "Got EXTENSION_HOST_DID_STOP_LOADING notification."; + VLOG(1) << "Got EXTENSION_HOST_DID_STOP_LOADING notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_INSTALLED: - LOG(INFO) << "Got EXTENSION_INSTALLED notification."; + VLOG(1) << "Got EXTENSION_INSTALLED notification."; ++extension_installs_observed_; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_INSTALL_ERROR: - LOG(INFO) << "Got EXTENSION_INSTALL_ERROR notification."; + VLOG(1) << "Got EXTENSION_INSTALL_ERROR notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_PROCESS_CREATED: - LOG(INFO) << "Got EXTENSION_PROCESS_CREATED notification."; + VLOG(1) << "Got EXTENSION_PROCESS_CREATED notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_PROCESS_TERMINATED: - LOG(INFO) << "Got EXTENSION_PROCESS_TERMINATED notification."; + VLOG(1) << "Got EXTENSION_PROCESS_TERMINATED notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED: { LocationBarTesting* location_bar = browser()->window()->GetLocationBar()->GetLocationBarForTesting(); - LOG(INFO) << "Got EXTENSION_PAGE_ACTION_COUNT_CHANGED " - << "notification. Number of page actions: " - << location_bar->PageActionCount() << ""; + VLOG(1) << "Got EXTENSION_PAGE_ACTION_COUNT_CHANGED notification. Number " + "of page actions: " << location_bar->PageActionCount(); if (location_bar->PageActionCount() == target_page_action_count_) { target_page_action_count_ = -1; @@ -333,9 +341,9 @@ void ExtensionBrowserTest::Observe(NotificationType type, case NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED: { LocationBarTesting* location_bar = browser()->window()->GetLocationBar()->GetLocationBarForTesting(); - LOG(INFO) << "Got EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED " - << "notification. Number of visible page actions: " - << location_bar->PageActionVisibleCount(); + VLOG(1) << "Got EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED notification. " + "Number of visible page actions: " + << location_bar->PageActionVisibleCount(); if (location_bar->PageActionVisibleCount() == target_visible_page_action_count_) { target_visible_page_action_count_ = -1; diff --git a/chrome/browser/extensions/extension_browsertests_misc.cc b/chrome/browser/extensions/extension_browsertests_misc.cc index 8338a52..3b1780d 100644 --- a/chrome/browser/extensions/extension_browsertests_misc.cc +++ b/chrome/browser/extensions/extension_browsertests_misc.cc @@ -260,13 +260,13 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, PageActionRefreshCrash) { ASSERT_TRUE(LoadExtension(base_path.AppendASCII("ExtA"))); ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); ASSERT_EQ(size_before + 1, service->extensions()->size()); - Extension* extensionA = service->extensions()->at(size_before); + const Extension* extensionA = service->extensions()->at(size_before); // Load extension B. ASSERT_TRUE(LoadExtension(base_path.AppendASCII("ExtB"))); ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(2)); ASSERT_EQ(size_before + 2, service->extensions()->size()); - Extension* extensionB = service->extensions()->at(size_before + 1); + const Extension* extensionB = service->extensions()->at(size_before + 1); ReloadExtension(extensionA->id()); // ExtensionA has changed, so refetch it. @@ -306,7 +306,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TitleLocalizationBrowserAction) { ASSERT_TRUE(LoadExtension(extension_path)); ASSERT_EQ(size_before + 1, service->extensions()->size()); - Extension* extension = service->extensions()->at(size_before); + const Extension* extension = service->extensions()->at(size_before); EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur: l10n browser action").c_str(), extension->description().c_str()); @@ -335,7 +335,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TitleLocalizationPageAction) { ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); ASSERT_EQ(size_before + 1, service->extensions()->size()); - Extension* extension = service->extensions()->at(size_before); + const Extension* extension = service->extensions()->at(size_before); EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur: l10n page action").c_str(), extension->description().c_str()); @@ -422,7 +422,7 @@ void NavigateToFeedAndValidate(net::TestServer* server, } ExtensionsService* service = browser->profile()->GetExtensionsService(); - Extension* extension = service->extensions()->back(); + const Extension* extension = service->extensions()->back(); std::string id = extension->id(); // Navigate to the subscribe page directly. @@ -701,7 +701,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WindowOpenNoPrivileges) { #define MAYBE_PluginLoadUnload PluginLoadUnload #elif defined(OS_LINUX) // http://crbug.com/47598 -#define MAYBE_PluginLoadUnload FLAKY_PluginLoadUnload +#define MAYBE_PluginLoadUnload DISABLED_PluginLoadUnload #else // TODO(mpcomplete): http://crbug.com/29900 need cross platform plugin support. #define MAYBE_PluginLoadUnload DISABLED_PluginLoadUnload @@ -778,7 +778,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, DISABLED_OptionsPage) { ExtensionsService* service = browser()->profile()->GetExtensionsService(); const ExtensionList* extensions = service->extensions(); ASSERT_EQ(1u, extensions->size()); - Extension* extension = extensions->at(0); + const Extension* extension = extensions->at(0); // Go to the chrome://extensions page and click the Options button. ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIExtensionsURL)); diff --git a/chrome/browser/extensions/extension_context_menu_api.cc b/chrome/browser/extensions/extension_context_menu_api.cc index 92e9ad4..a004b97 100644 --- a/chrome/browser/extensions/extension_context_menu_api.cc +++ b/chrome/browser/extensions/extension_context_menu_api.cc @@ -138,7 +138,7 @@ bool ExtensionContextMenuFunction::ParseURLPatterns( return false; URLPattern pattern(ExtensionMenuManager::kAllowedSchemes); - if (!pattern.Parse(tmp)) { + if (URLPattern::PARSE_SUCCESS != pattern.Parse(tmp)) { error_ = ExtensionErrorUtils::FormatErrorMessage(kInvalidURLPatternError, tmp); return false; @@ -174,22 +174,21 @@ bool ExtensionContextMenuFunction::SetURLPatterns( return true; } - bool ExtensionContextMenuFunction::GetParent( const DictionaryValue& properties, const ExtensionMenuManager& manager, ExtensionMenuItem** result) { if (!properties.HasKey(kParentIdKey)) return true; - ExtensionMenuItem::Id parent_id(extension_id(), 0); + ExtensionMenuItem::Id parent_id(profile(), extension_id(), 0); if (properties.HasKey(kParentIdKey) && - !properties.GetInteger(kParentIdKey, &parent_id.second)) + !properties.GetInteger(kParentIdKey, &parent_id.uid)) return false; ExtensionMenuItem* parent = manager.GetItemById(parent_id); if (!parent) { error_ = "Cannot find menu item with id " + - base::IntToString(parent_id.second); + base::IntToString(parent_id.uid); return false; } if (parent->type() != ExtensionMenuItem::NORMAL) { @@ -205,9 +204,9 @@ bool CreateContextMenuFunction::RunImpl() { EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &properties)); EXTENSION_FUNCTION_VALIDATE(properties != NULL); - ExtensionMenuItem::Id id(extension_id(), 0); + ExtensionMenuItem::Id id(profile(), extension_id(), 0); EXTENSION_FUNCTION_VALIDATE(properties->GetInteger(kGeneratedIdKey, - &id.second)); + &id.uid)); std::string title; if (properties->HasKey(kTitleKey) && !properties->GetString(kTitleKey, &title)) @@ -241,13 +240,13 @@ bool CreateContextMenuFunction::RunImpl() { bool success = true; if (properties->HasKey(kParentIdKey)) { - ExtensionMenuItem::Id parent_id(extension_id(), 0); + ExtensionMenuItem::Id parent_id(profile(), extension_id(), 0); EXTENSION_FUNCTION_VALIDATE(properties->GetInteger(kParentIdKey, - &parent_id.second)); + &parent_id.uid)); ExtensionMenuItem* parent = menu_manager->GetItemById(parent_id); if (!parent) { error_ = ExtensionErrorUtils::FormatErrorMessage( - kCannotFindItemError, base::IntToString(parent_id.second)); + kCannotFindItemError, base::IntToString(parent_id.uid)); return false; } if (parent->type() != ExtensionMenuItem::NORMAL) { @@ -266,15 +265,15 @@ bool CreateContextMenuFunction::RunImpl() { } bool UpdateContextMenuFunction::RunImpl() { - ExtensionMenuItem::Id item_id(extension_id(), 0); - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &item_id.second)); + ExtensionMenuItem::Id item_id(profile(), extension_id(), 0); + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &item_id.uid)); ExtensionsService* service = profile()->GetExtensionsService(); ExtensionMenuManager* manager = service->menu_manager(); ExtensionMenuItem* item = manager->GetItemById(item_id); if (!item || item->extension_id() != extension_id()) { error_ = ExtensionErrorUtils::FormatErrorMessage( - kCannotFindItemError, base::IntToString(item_id.second)); + kCannotFindItemError, base::IntToString(item_id.uid)); return false; } @@ -333,8 +332,8 @@ bool UpdateContextMenuFunction::RunImpl() { } bool RemoveContextMenuFunction::RunImpl() { - ExtensionMenuItem::Id id(extension_id(), 0); - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &id.second)); + ExtensionMenuItem::Id id(profile(), extension_id(), 0); + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &id.uid)); ExtensionsService* service = profile()->GetExtensionsService(); ExtensionMenuManager* manager = service->menu_manager(); @@ -342,7 +341,7 @@ bool RemoveContextMenuFunction::RunImpl() { // Ensure one extension can't remove another's menu items. if (!item || item->extension_id() != extension_id()) { error_ = ExtensionErrorUtils::FormatErrorMessage( - kCannotFindItemError, base::IntToString(id.second)); + kCannotFindItemError, base::IntToString(id.uid)); return false; } diff --git a/chrome/browser/extensions/extension_context_menu_browsertest.cc b/chrome/browser/extensions/extension_context_menu_browsertest.cc index 62221e1..da83124 100644 --- a/chrome/browser/extensions/extension_context_menu_browsertest.cc +++ b/chrome/browser/extensions/extension_context_menu_browsertest.cc @@ -3,8 +3,9 @@ // found in the LICENSE file. #include "app/menus/menu_model.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_test_message_listener.h" #include "chrome/browser/extensions/extensions_service.h" @@ -122,9 +123,16 @@ class ExtensionContextMenuBrowserTest : public ExtensionBrowserTest { return LoadExtension(extension_dir); } - TestRenderViewContextMenu* CreateMenu(const GURL& page_url, + bool LoadContextMenuExtensionIncognito(std::string subdirectory) { + FilePath extension_dir = + test_data_dir_.AppendASCII("context_menus").AppendASCII(subdirectory); + return LoadExtensionIncognito(extension_dir); + } + + TestRenderViewContextMenu* CreateMenu(Browser* browser, + const GURL& page_url, const GURL& link_url) { - TabContents* tab_contents = browser()->GetSelectedTabContents(); + TabContents* tab_contents = browser->GetSelectedTabContents(); WebContextMenuData data; ContextMenuParams params(data); params.page_url = page_url; @@ -142,7 +150,7 @@ class ExtensionContextMenuBrowserTest : public ExtensionBrowserTest { // Returns a pointer to the currently loaded extension with |name|, or null // if not found. - Extension* GetExtensionNamed(std::string name) { + const Extension* GetExtensionNamed(std::string name) { const ExtensionList* extensions = browser()->profile()->GetExtensionsService()->extensions(); ExtensionList::const_iterator i; @@ -173,7 +181,8 @@ class ExtensionContextMenuBrowserTest : public ExtensionBrowserTest { bool MenuHasItemWithLabel(const GURL& page_url, const GURL& link_url, const std::string& label) { - scoped_ptr<TestRenderViewContextMenu> menu(CreateMenu(page_url, link_url)); + scoped_ptr<TestRenderViewContextMenu> menu( + CreateMenu(browser(), page_url, link_url)); return menu->HasExtensionItemWithLabel(label); } }; @@ -190,7 +199,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Simple) { GURL page_url("http://www.google.com"); // Create and build our test context menu. - scoped_ptr<TestRenderViewContextMenu> menu(CreateMenu(page_url, GURL())); + scoped_ptr<TestRenderViewContextMenu> menu( + CreateMenu(browser(), page_url, GURL())); // Look for the extension item in the menu, and execute it. int command_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; @@ -249,7 +259,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, LongTitle) { // Create a context menu, then find the item's label. It should be properly // truncated. GURL url("http://foo.com/"); - scoped_ptr<TestRenderViewContextMenu> menu(CreateMenu(url, GURL())); + scoped_ptr<TestRenderViewContextMenu> menu( + CreateMenu(browser(), url, GURL())); string16 label; ASSERT_TRUE(menu->GetItemLabel(item->id(), &label)); @@ -301,7 +312,7 @@ static void VerifyMenuForSeparatorsTest(const MenuModel& menu) { IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Separators) { // Load the extension. ASSERT_TRUE(LoadContextMenuExtension("separators")); - Extension* extension = GetExtensionNamed("Separators Test"); + const Extension* extension = GetExtensionNamed("Separators Test"); ASSERT_TRUE(extension != NULL); // Navigate to test1.html inside the extension, which should create a bunch @@ -313,7 +324,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Separators) { listener1.WaitUntilSatisfied(); GURL url("http://www.google.com/"); - scoped_ptr<TestRenderViewContextMenu> menu(CreateMenu(url, GURL())); + scoped_ptr<TestRenderViewContextMenu> menu( + CreateMenu(browser(), url, GURL())); // The top-level item should be an "automagic parent" with the extension's // name. @@ -336,7 +348,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Separators) { ui_test_utils::NavigateToURL(browser(), GURL(extension->GetResourceURL("test2.html"))); listener2.WaitUntilSatisfied(); - menu.reset(CreateMenu(url, GURL())); + menu.reset(CreateMenu(browser(), url, GURL())); ASSERT_TRUE(menu->GetMenuModelAndItemIndex( IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST, &model, &index)); EXPECT_EQ(UTF8ToUTF16("parent"), model->GetLabelAt(index)); @@ -368,3 +380,50 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, TargetURLs) { non_google_url, std::string("item1"))); } + +// Tests adding a simple context menu item. +IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, IncognitoSplit) { + ExtensionTestMessageListener created("created item regular", false); + ExtensionTestMessageListener created_incognito("created item incognito", + false); + + ExtensionTestMessageListener onclick("onclick fired regular", false); + ExtensionTestMessageListener onclick_incognito("onclick fired incognito", + false); + + // Open an incognito window. + ui_test_utils::OpenURLOffTheRecord(browser()->profile(), GURL("about:blank")); + + ASSERT_TRUE(LoadContextMenuExtensionIncognito("incognito")); + + // Wait for the extension's processes to tell us they've created an item. + ASSERT_TRUE(created.WaitUntilSatisfied()); + ASSERT_TRUE(created_incognito.WaitUntilSatisfied()); + + GURL page_url("http://www.google.com"); + + // Create and build our test context menu. + Browser* browser_incognito = BrowserList::FindBrowserWithType( + browser()->profile()->GetOffTheRecordProfile(), + Browser::TYPE_NORMAL, false); + ASSERT_TRUE(browser_incognito); + scoped_ptr<TestRenderViewContextMenu> menu( + CreateMenu(browser(), page_url, GURL())); + scoped_ptr<TestRenderViewContextMenu> menu_incognito( + CreateMenu(browser_incognito, page_url, GURL())); + + // Look for the extension item in the menu, and execute it. + int command_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; + ASSERT_TRUE(menu->IsCommandIdEnabled(command_id)); + menu->ExecuteCommand(command_id); + + // Wait for the extension's script to tell us its onclick fired. Ensure + // that the incognito version doesn't fire until we explicitly click the + // incognito menu item. + ASSERT_TRUE(onclick.WaitUntilSatisfied()); + EXPECT_FALSE(onclick_incognito.was_satisfied()); + + ASSERT_TRUE(menu_incognito->IsCommandIdEnabled(command_id)); + menu_incognito->ExecuteCommand(command_id); + ASSERT_TRUE(onclick_incognito.WaitUntilSatisfied()); +} diff --git a/chrome/browser/extensions/extension_context_menu_model.cc b/chrome/browser/extensions/extension_context_menu_model.cc index 16ef321..404241e 100644 --- a/chrome/browser/extensions/extension_context_menu_model.cc +++ b/chrome/browser/extensions/extension_context_menu_model.cc @@ -28,11 +28,11 @@ enum MenuEntries { }; ExtensionContextMenuModel::ExtensionContextMenuModel( - Extension* extension, + const Extension* extension, Browser* browser, PopupDelegate* delegate) : ALLOW_THIS_IN_INITIALIZER_LIST(SimpleMenuModel(this)), - extension_(extension), + extension_id_(extension->id()), browser_(browser), profile_(browser->profile()), delegate_(delegate) { @@ -53,7 +53,13 @@ ExtensionContextMenuModel::~ExtensionContextMenuModel() { } void ExtensionContextMenuModel::InitCommonCommands() { - AddItem(NAME, UTF8ToUTF16(extension_->name())); + const Extension* extension = GetExtension(); + + // The extension pointer should only be null if the extension was uninstalled, + // and since the menu just opened, it should still be installed. + DCHECK(extension); + + AddItem(NAME, UTF8ToUTF16(extension->name())); AddSeparator(); AddItemWithStringId(CONFIGURE, IDS_EXTENSIONS_OPTIONS); AddItemWithStringId(DISABLE, IDS_EXTENSIONS_DISABLE); @@ -67,14 +73,16 @@ bool ExtensionContextMenuModel::IsCommandIdChecked(int command_id) const { } bool ExtensionContextMenuModel::IsCommandIdEnabled(int command_id) const { + const Extension* extension = this->GetExtension(); + if (!extension) + return false; + if (command_id == CONFIGURE) { - return extension_->options_url().spec().length() > 0; + return extension->options_url().spec().length() > 0; } else if (command_id == NAME) { - // The NAME links to the gallery page, which only makes sense if Google is - // hosting the extension. For other 3rd party extensions we don't have a - // homepage url, so we just disable this menu item on those cases, at least - // for now. - return extension_->GalleryUrl().is_valid(); + // The NAME links to the Homepage URL. If the extension doesn't have a + // homepage, we just disable this menu item. + return extension->GetHomepageURL().is_valid(); } else if (command_id == INSPECT_POPUP) { TabContents* contents = browser_->GetSelectedTabContents(); if (!contents) @@ -91,26 +99,30 @@ bool ExtensionContextMenuModel::GetAcceleratorForCommandId( } void ExtensionContextMenuModel::ExecuteCommand(int command_id) { + const Extension* extension = GetExtension(); + if (!extension) + return; + switch (command_id) { case NAME: { - browser_->OpenURL(extension_->GalleryUrl(), GURL(), + browser_->OpenURL(extension->GetHomepageURL(), GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); break; } case CONFIGURE: - DCHECK(!extension_->options_url().is_empty()); - profile_->GetExtensionProcessManager()->OpenOptionsPage(extension_, + DCHECK(!extension->options_url().is_empty()); + profile_->GetExtensionProcessManager()->OpenOptionsPage(extension, browser_); break; case DISABLE: { ExtensionsService* extension_service = profile_->GetExtensionsService(); - extension_service->DisableExtension(extension_->id()); + extension_service->DisableExtension(extension_id_); break; } case UNINSTALL: { AddRef(); // Balanced in InstallUIProceed and InstallUIAbort. install_ui_.reset(new ExtensionInstallUI(profile_)); - install_ui_->ConfirmUninstall(this, extension_); + install_ui_->ConfirmUninstall(this, extension); break; } case MANAGE: { @@ -129,8 +141,8 @@ void ExtensionContextMenuModel::ExecuteCommand(int command_id) { } void ExtensionContextMenuModel::InstallUIProceed() { - std::string id = extension_->id(); - profile_->GetExtensionsService()->UninstallExtension(id, false); + if (GetExtension()) + profile_->GetExtensionsService()->UninstallExtension(extension_id_, false); Release(); } @@ -138,3 +150,8 @@ void ExtensionContextMenuModel::InstallUIProceed() { void ExtensionContextMenuModel::InstallUIAbort() { Release(); } + +const Extension* ExtensionContextMenuModel::GetExtension() const { + ExtensionsService* extension_service = profile_->GetExtensionsService(); + return extension_service->GetExtensionById(extension_id_, false); +} diff --git a/chrome/browser/extensions/extension_context_menu_model.h b/chrome/browser/extensions/extension_context_menu_model.h index 35af19d..89cf3f9 100644 --- a/chrome/browser/extensions/extension_context_menu_model.h +++ b/chrome/browser/extensions/extension_context_menu_model.h @@ -37,7 +37,7 @@ class ExtensionContextMenuModel // prefs::kExtensionsUIDeveloperMode is enabled then a menu item // will be shown for "Inspect Popup" which, when selected, will cause // ShowPopupForDevToolsWindow() to be called on |delegate|. - ExtensionContextMenuModel(Extension* extension, + ExtensionContextMenuModel(const Extension* extension, Browser* browser, PopupDelegate* delegate); virtual ~ExtensionContextMenuModel(); @@ -56,8 +56,12 @@ class ExtensionContextMenuModel private: void InitCommonCommands(); - // The extension we are displaying the menu for. - Extension* extension_; + // Gets the extension we are displaying the menu for. Returns NULL if the + // extension has been uninstalled and no longer exists. + const Extension* GetExtension() const; + + // A copy of the extension's id. + std::string extension_id_; // The extension action we are displaying the menu for (or NULL). ExtensionAction* extension_action_; diff --git a/chrome/browser/extensions/extension_cookies_api.cc b/chrome/browser/extensions/extension_cookies_api.cc index b97c74d..e66c4b3 100644 --- a/chrome/browser/extensions/extension_cookies_api.cc +++ b/chrome/browser/extensions/extension_cookies_api.cc @@ -409,7 +409,7 @@ bool RemoveCookieFunction::RunImpl() { // should happen after this. bool rv = BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - new RemoveCookieTask(url, name, store_context)); + new RemoveCookieTask(url, name, make_scoped_refptr(store_context))); DCHECK(rv); return true; diff --git a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc index 32b67af..3ad8813 100644 --- a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc +++ b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc @@ -51,7 +51,8 @@ class ExtensionCrashRecoveryTest : public ExtensionBrowserTest { void CrashExtension(size_t index) { ASSERT_LT(index, GetExtensionsService()->extensions()->size()); - Extension* extension = GetExtensionsService()->extensions()->at(index); + const Extension* extension = + GetExtensionsService()->extensions()->at(index); ASSERT_TRUE(extension); std::string extension_id(extension->id()); ExtensionHost* extension_host = @@ -69,7 +70,8 @@ class ExtensionCrashRecoveryTest : public ExtensionBrowserTest { void CheckExtensionConsistency(size_t index) { ASSERT_LT(index, GetExtensionsService()->extensions()->size()); - Extension* extension = GetExtensionsService()->extensions()->at(index); + const Extension* extension = + GetExtensionsService()->extensions()->at(index); ASSERT_TRUE(extension); ExtensionHost* extension_host = GetExtensionProcessManager()->GetBackgroundHostForExtension(extension); @@ -85,7 +87,7 @@ class ExtensionCrashRecoveryTest : public ExtensionBrowserTest { const size_t size_before = GetExtensionsService()->extensions()->size(); ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("common").AppendASCII("background_page"))); - Extension* extension = GetExtensionsService()->extensions()->back(); + const Extension* extension = GetExtensionsService()->extensions()->back(); ASSERT_TRUE(extension); first_extension_id_ = extension->id(); CheckExtensionConsistency(size_before); @@ -95,7 +97,8 @@ class ExtensionCrashRecoveryTest : public ExtensionBrowserTest { int offset = GetExtensionsService()->extensions()->size(); ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("install").AppendASCII("install"))); - Extension* extension = GetExtensionsService()->extensions()->at(offset); + const Extension* extension = + GetExtensionsService()->extensions()->at(offset); ASSERT_TRUE(extension); second_extension_id_ = extension->id(); CheckExtensionConsistency(offset); diff --git a/chrome/browser/extensions/extension_creator.cc b/chrome/browser/extensions/extension_creator.cc index 86c78fe..8bd68a2 100644 --- a/chrome/browser/extensions/extension_creator.cc +++ b/chrome/browser/extensions/extension_creator.cc @@ -56,7 +56,7 @@ bool ExtensionCreator::InitializeInput( // Load the extension once. We don't really need it, but this does a lot of // useful validation of the structure. - scoped_ptr<Extension> extension( + scoped_refptr<Extension> extension( extension_file_util::LoadExtension(extension_dir, Extension::INTERNAL, false, // key not required diff --git a/chrome/browser/extensions/extension_disabled_infobar_delegate.cc b/chrome/browser/extensions/extension_disabled_infobar_delegate.cc index 6e51c47..af38e4f 100644 --- a/chrome/browser/extensions/extension_disabled_infobar_delegate.cc +++ b/chrome/browser/extensions/extension_disabled_infobar_delegate.cc @@ -26,7 +26,7 @@ class ExtensionDisabledDialogDelegate public: ExtensionDisabledDialogDelegate(Profile* profile, ExtensionsService* service, - Extension* extension) + const Extension* extension) : service_(service), extension_(extension) { AddRef(); // Balanced in Proceed or Abort. @@ -55,7 +55,7 @@ class ExtensionDisabledDialogDelegate scoped_ptr<ExtensionInstallUI> install_ui_; ExtensionsService* service_; - Extension* extension_; + const Extension* extension_; }; class ExtensionDisabledInfobarDelegate @@ -64,7 +64,7 @@ class ExtensionDisabledInfobarDelegate public: ExtensionDisabledInfobarDelegate(TabContents* tab_contents, ExtensionsService* service, - Extension* extension) + const Extension* extension) : ConfirmInfoBarDelegate(tab_contents), tab_contents_(tab_contents), service_(service), @@ -110,7 +110,7 @@ class ExtensionDisabledInfobarDelegate switch (type.value) { case NotificationType::EXTENSION_LOADED: case NotificationType::EXTENSION_UNLOADED_DISABLED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (extension == extension_) tab_contents_->RemoveInfoBar(this); break; @@ -124,11 +124,11 @@ class ExtensionDisabledInfobarDelegate NotificationRegistrar registrar_; TabContents* tab_contents_; ExtensionsService* service_; - Extension* extension_; + const Extension* extension_; }; void ShowExtensionDisabledUI(ExtensionsService* service, Profile* profile, - Extension* extension) { + const Extension* extension) { Browser* browser = BrowserList::GetLastActiveWithProfile(profile); if (!browser) return; @@ -142,7 +142,7 @@ void ShowExtensionDisabledUI(ExtensionsService* service, Profile* profile, } void ShowExtensionDisabledDialog(ExtensionsService* service, Profile* profile, - Extension* extension) { + const Extension* extension) { // This object manages its own lifetime. new ExtensionDisabledDialogDelegate(profile, service, extension); } diff --git a/chrome/browser/extensions/extension_disabled_infobar_delegate.h b/chrome/browser/extensions/extension_disabled_infobar_delegate.h index 16d3b6f..430e652 100644 --- a/chrome/browser/extensions/extension_disabled_infobar_delegate.h +++ b/chrome/browser/extensions/extension_disabled_infobar_delegate.h @@ -13,10 +13,10 @@ class Profile; // Shows UI to inform the user that an extension was disabled after upgrading // to higher permissions. void ShowExtensionDisabledUI(ExtensionsService* service, Profile* profile, - Extension* extension); + const Extension* extension); // Shows the extension install dialog. void ShowExtensionDisabledDialog(ExtensionsService* service, Profile* profile, - Extension* extension); + const Extension* extension); #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_DISABLED_INFOBAR_DELEGATE_H_ diff --git a/chrome/browser/extensions/extension_dom_ui.cc b/chrome/browser/extensions/extension_dom_ui.cc index 08f3b45..602ad6a 100644 --- a/chrome/browser/extensions/extension_dom_ui.cc +++ b/chrome/browser/extensions/extension_dom_ui.cc @@ -112,7 +112,7 @@ class ExtensionDOMUIImageLoadingTracker : public ImageLoadingTracker::Observer { ImageLoadingTracker tracker_; scoped_refptr<FaviconService::GetFaviconRequest> request_; - Extension* extension_; + const Extension* extension_; DISALLOW_COPY_AND_ASSIGN(ExtensionDOMUIImageLoadingTracker); }; @@ -122,10 +122,11 @@ class ExtensionDOMUIImageLoadingTracker : public ImageLoadingTracker::Observer { const char ExtensionDOMUI::kExtensionURLOverrides[] = "extensions.chrome_url_overrides"; -ExtensionDOMUI::ExtensionDOMUI(TabContents* tab_contents, GURL url) - : DOMUI(tab_contents) { +ExtensionDOMUI::ExtensionDOMUI(TabContents* tab_contents, const GURL& url) + : DOMUI(tab_contents), + url_(url) { ExtensionsService* service = tab_contents->profile()->GetExtensionsService(); - Extension* extension = service->GetExtensionByURL(url); + const Extension* extension = service->GetExtensionByURL(url); if (!extension) extension = service->GetExtensionByWebExtent(url); DCHECK(extension); @@ -150,12 +151,10 @@ ExtensionDOMUI::~ExtensionDOMUI() {} void ExtensionDOMUI::ResetExtensionFunctionDispatcher( RenderViewHost* render_view_host) { - // Use the NavigationController to get the URL rather than the TabContents - // since this is the real underlying URL (see HandleChromeURLOverride). - NavigationController& controller = tab_contents()->controller(); - const GURL& url = controller.GetActiveEntry()->url(); + // TODO(jcivelli): http://crbug.com/60608 we should get the URL out of the + // active entry of the navigation controller. extension_function_dispatcher_.reset( - ExtensionFunctionDispatcher::Create(render_view_host, this, url)); + ExtensionFunctionDispatcher::Create(render_view_host, this, url_)); DCHECK(extension_function_dispatcher_.get()); } @@ -195,10 +194,6 @@ TabContents* ExtensionDOMUI::associated_tab_contents() const { return tab_contents(); } -Profile* ExtensionDOMUI::GetProfile() { - return DOMUI::GetProfile(); -} - ExtensionBookmarkManagerEventRouter* ExtensionDOMUI::extension_bookmark_manager_event_router() { return extension_bookmark_manager_event_router_.get(); @@ -272,7 +267,7 @@ bool ExtensionDOMUI::HandleChromeURLOverride(GURL* url, Profile* profile) { } // Verify that the extension that's being referred to actually exists. - Extension* extension = service->GetExtensionByURL(extension_url); + const Extension* extension = service->GetExtensionByURL(extension_url); if (!extension) { // This can currently happen if you use --load-extension one run, and // then don't use it the next. It could also happen if an extension diff --git a/chrome/browser/extensions/extension_dom_ui.h b/chrome/browser/extensions/extension_dom_ui.h index 22a106c..ea59b41 100644 --- a/chrome/browser/extensions/extension_dom_ui.h +++ b/chrome/browser/extensions/extension_dom_ui.h @@ -33,7 +33,7 @@ class ExtensionDOMUI public: static const char kExtensionURLOverrides[]; - explicit ExtensionDOMUI(TabContents* tab_contents, GURL url); + explicit ExtensionDOMUI(TabContents* tab_contents, const GURL& url); virtual ~ExtensionDOMUI(); @@ -51,7 +51,6 @@ class ExtensionDOMUI virtual gfx::NativeView GetNativeViewOfHost(); virtual gfx::NativeWindow GetCustomFrameNativeWindow(); virtual TabContents* associated_tab_contents() const; - virtual Profile* GetProfile(); virtual ExtensionBookmarkManagerEventRouter* extension_bookmark_manager_event_router(); @@ -99,6 +98,9 @@ class ExtensionDOMUI // the other extension APIs? scoped_ptr<ExtensionBookmarkManagerEventRouter> extension_bookmark_manager_event_router_; + + // The URL this DOMUI was created for. + GURL url_; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_DOM_UI_H_ diff --git a/chrome/browser/extensions/extension_error_reporter.h b/chrome/browser/extensions/extension_error_reporter.h index 2866211..8753c39 100644 --- a/chrome/browser/extensions/extension_error_reporter.h +++ b/chrome/browser/extensions/extension_error_reporter.h @@ -28,7 +28,7 @@ class ExtensionErrorReporter { // Get the singleton instance. static ExtensionErrorReporter* GetInstance(); - // Report an error. Errors always go to LOG(INFO). Optionally, they can also + // Report an error. Errors always go to VLOG(1). Optionally, they can also // cause a noisy alert box. This method can be called from any thread. void ReportError(const std::string& message, bool be_noisy); diff --git a/chrome/browser/extensions/extension_event_router.cc b/chrome/browser/extensions/extension_event_router.cc index 9d22da7..4fc48e3 100644 --- a/chrome/browser/extensions/extension_event_router.cc +++ b/chrome/browser/extensions/extension_event_router.cc @@ -33,17 +33,6 @@ static void DispatchEvent(RenderProcessHost* renderer, extension_id, kDispatchEvent, args, event_url)); } -static bool CanCrossIncognito(Profile* profile, - const std::string& extension_id) { - // We allow the extension to see events and data from another profile iff it - // uses "spanning" behavior and it has incognito access. "split" mode - // extensions only see events for a matching profile. - Extension* extension = - profile->GetExtensionsService()->GetExtensionById(extension_id, false); - return (profile->GetExtensionsService()->IsIncognitoEnabled(extension) && - !extension->incognito_split_mode()); -} - } // namespace struct ExtensionEventRouter::EventListener { @@ -63,6 +52,24 @@ struct ExtensionEventRouter::EventListener { } }; +// static +bool ExtensionEventRouter::CanCrossIncognito(Profile* profile, + const std::string& extension_id) { + const Extension* extension = + profile->GetExtensionsService()->GetExtensionById(extension_id, false); + return CanCrossIncognito(profile, extension); +} + +// static +bool ExtensionEventRouter::CanCrossIncognito(Profile* profile, + const Extension* extension) { + // We allow the extension to see events and data from another profile iff it + // uses "spanning" behavior and it has incognito access. "split" mode + // extensions only see events for a matching profile. + return (profile->GetExtensionsService()->IsIncognitoEnabled(extension) && + !extension->incognito_split_mode()); +} + ExtensionEventRouter::ExtensionEventRouter(Profile* profile) : profile_(profile), extension_devtools_manager_(profile->GetExtensionDevToolsManager()) { @@ -161,6 +168,7 @@ void ExtensionEventRouter::DispatchEventImpl( return; std::set<EventListener>& listeners = it->second; + ExtensionsService* service = profile_->GetExtensionsService(); // Send the event only to renderers that are listening for it. for (std::set<EventListener>::iterator listener = listeners.begin(); @@ -178,7 +186,9 @@ void ExtensionEventRouter::DispatchEventImpl( // incognito tab event sent to a normal process, or vice versa). bool cross_incognito = restrict_to_profile && listener->process->profile() != restrict_to_profile; - if (cross_incognito && !CanCrossIncognito(profile_, listener->extension_id)) + const Extension* extension = service->GetExtensionById( + listener->extension_id, false); + if (cross_incognito && !service->CanCrossIncognito(extension)) continue; DispatchEvent(listener->process, listener->extension_id, diff --git a/chrome/browser/extensions/extension_event_router.h b/chrome/browser/extensions/extension_event_router.h index 5e1ca97..c9f1f21 100644 --- a/chrome/browser/extensions/extension_event_router.h +++ b/chrome/browser/extensions/extension_event_router.h @@ -15,12 +15,19 @@ #include "chrome/common/notification_registrar.h" class GURL; +class Extension; class ExtensionDevToolsManager; class Profile; class RenderProcessHost; class ExtensionEventRouter : public NotificationObserver { public: + // Returns true if the given extension can see events and data from another + // sub-profile (incognito to original profile, or vice versa). + static bool CanCrossIncognito(Profile* profile, + const std::string& extension_id); + static bool CanCrossIncognito(Profile* profile, const Extension* extension); + explicit ExtensionEventRouter(Profile* profile); ~ExtensionEventRouter(); diff --git a/chrome/browser/extensions/extension_function.cc b/chrome/browser/extensions/extension_function.cc index f285e9d..88a6c9e 100644 --- a/chrome/browser/extensions/extension_function.cc +++ b/chrome/browser/extensions/extension_function.cc @@ -21,7 +21,7 @@ ExtensionFunction::ExtensionFunction() ExtensionFunction::~ExtensionFunction() { } -Extension* ExtensionFunction::GetExtension() { +const Extension* ExtensionFunction::GetExtension() { ExtensionsService* service = profile_->GetExtensionsService(); DCHECK(service); return service->GetExtensionById(extension_id_, false); diff --git a/chrome/browser/extensions/extension_function.h b/chrome/browser/extensions/extension_function.h index b48d0a9..57a8665 100644 --- a/chrome/browser/extensions/extension_function.h +++ b/chrome/browser/extensions/extension_function.h @@ -107,7 +107,7 @@ class ExtensionFunction : public base::RefCountedThreadSafe<ExtensionFunction> { // Gets the extension that called this function. This can return NULL for // async functions, for example if the extension is unloaded while the // function is running. - Extension* GetExtension(); + const Extension* GetExtension(); // Gets the "current" browser, if any. // diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index e3ca590..beb96e8 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -274,6 +274,7 @@ void FactoryRegistry::ResetFunctions() { // Management. RegisterFunction<GetAllExtensionsFunction>(); + RegisterFunction<GetExtensionByIdFunction>(); RegisterFunction<LaunchAppFunction>(); RegisterFunction<SetEnabledFunction>(); RegisterFunction<UninstallFunction>(); @@ -341,7 +342,7 @@ ExtensionFunctionDispatcher* ExtensionFunctionDispatcher::Create( if (!service->ExtensionBindingsAllowed(url)) return NULL; - Extension* extension = service->GetExtensionByURL(url); + const Extension* extension = service->GetExtensionByURL(url); if (!extension) extension = service->GetExtensionByWebExtent(url); @@ -355,7 +356,7 @@ ExtensionFunctionDispatcher* ExtensionFunctionDispatcher::Create( ExtensionFunctionDispatcher::ExtensionFunctionDispatcher( RenderViewHost* render_view_host, Delegate* delegate, - Extension* extension, + const Extension* extension, const GURL& url) : profile_(render_view_host->process()->profile()), render_view_host_(render_view_host), @@ -448,10 +449,9 @@ void ExtensionFunctionDispatcher::HandleRequest( function->set_user_gesture(params.user_gesture); ExtensionsService* service = profile()->GetExtensionsService(); DCHECK(service); - Extension* extension = service->GetExtensionById(extension_id(), false); + const Extension* extension = service->GetExtensionById(extension_id(), false); DCHECK(extension); - function->set_include_incognito(service->IsIncognitoEnabled(extension) && - !extension->incognito_split_mode()); + function->set_include_incognito(service->CanCrossIncognito(extension)); if (!service->ExtensionBindingsAllowed(function->source_url()) || !extension->HasApiPermission(function->name())) { diff --git a/chrome/browser/extensions/extension_function_dispatcher.h b/chrome/browser/extensions/extension_function_dispatcher.h index 99328f7..9969b1f 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.h +++ b/chrome/browser/extensions/extension_function_dispatcher.h @@ -125,7 +125,7 @@ class ExtensionFunctionDispatcher { private: ExtensionFunctionDispatcher(RenderViewHost* render_view_host, Delegate* delegate, - Extension* extension, + const Extension* extension, const GURL& url); // We need to keep a pointer to the profile because we use it in the dtor diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc index b35d58c..09d30b7 100644 --- a/chrome/browser/extensions/extension_host.cc +++ b/chrome/browser/extensions/extension_host.cc @@ -34,6 +34,7 @@ #include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/browser/renderer_host/site_instance.h" #include "chrome/browser/renderer_preferences_util.h" +#include "chrome/browser/tab_contents/popup_menu_helper_mac.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/browser/themes/browser_theme_provider.h" @@ -121,8 +122,10 @@ class ExtensionHost::ProcessCreationQueue { //////////////// // ExtensionHost -ExtensionHost::ExtensionHost(Extension* extension, SiteInstance* site_instance, - const GURL& url, ViewType::Type host_type) +ExtensionHost::ExtensionHost(const Extension* extension, + SiteInstance* site_instance, + const GURL& url, + ViewType::Type host_type) : extension_(extension), profile_(site_instance->browsing_instance()->profile()), did_stop_loading_(false), @@ -235,7 +238,8 @@ void ExtensionHost::NavigateToURL(const GURL& url) { url_ = url; - if (!is_background_page() && !extension_->GetBackgroundPageReady()) { + if (!is_background_page() && + !profile_->GetExtensionsService()->IsBackgroundPageReady(extension_)) { // Make sure the background page loads before any others. registrar_.Add(this, NotificationType::EXTENSION_BACKGROUND_PAGE_READY, Source<Extension>(extension_)); @@ -250,7 +254,8 @@ void ExtensionHost::Observe(NotificationType type, const NotificationDetails& details) { switch (type.value) { case NotificationType::EXTENSION_BACKGROUND_PAGE_READY: - DCHECK(extension_->GetBackgroundPageReady()); + DCHECK(profile_->GetExtensionsService()-> + IsBackgroundPageReady(extension_)); NavigateToURL(url_); break; case NotificationType::RENDERER_PROCESS_CREATED: @@ -264,7 +269,7 @@ void ExtensionHost::Observe(NotificationType type, // sent. NULL it out so that dirty pointer issues don't arise in cases // when multiple ExtensionHost objects pointing to the same Extension are // present. - if (extension_ == Details<Extension>(details).ptr()) + if (extension_ == Details<const Extension>(details).ptr()) extension_ = NULL; break; default: @@ -394,7 +399,7 @@ void ExtensionHost::DocumentAvailableInMainFrame(RenderViewHost* rvh) { document_element_available_ = true; if (is_background_page()) { - extension_->SetBackgroundPageReady(); + profile_->GetExtensionsService()->SetBackgroundPageReady(extension_); } else { switch (extension_host_type_) { case ViewType::EXTENSION_INFOBAR: @@ -564,33 +569,43 @@ void ExtensionHost::ShowCreatedWindow(int route_id, contents, initial_pos); browser->window()->Show(); - } else { - Browser* browser = BrowserList::FindBrowserWithType( + return; + } + + // If the tab contents isn't a popup, it's a normal tab. We need to find a + // home for it. This is typically a Browser, but it can also be some other + // TabContentsDelegate in the case of ChromeFrame. + + // First, if the creating extension view was associated with a tab contents, + // use that tab content's delegate. We must be careful here that the + // associated tab contents has the same profile as the new tab contents. In + // the case of extensions in 'spanning' incognito mode, they can mismatch. + // We don't want to end up putting a normal tab into an incognito window, or + // vice versa. + TabContents* associated_contents = associated_tab_contents(); + if (associated_contents && + associated_contents->profile() == contents->profile()) { + associated_contents->AddNewContents(contents, disposition, initial_pos, + user_gesture); + return; + } + + // If there's no associated tab contents, or it doesn't have a matching + // profile, try finding an open window. Again, we must make sure to find a + // window with the correct profile. + Browser* browser = BrowserList::FindBrowserWithType( contents->profile(), Browser::TYPE_NORMAL, false); // Match incognito exactly. - if (!browser) { - // If no browser is associated with the created TabContents, then the - // created TabContents may be an intermediate struct used during topmost - // url navigation from within an experimental extension popup view. - // - // If the ExtensionHost has an associated TabContents, then register the - // new contents with this contents. This will allow top-level link - // navigation within the new contents to function just as navigation - // within the current host. - TabContents* associated_contents = associated_tab_contents(); - if (associated_contents) { - associated_contents->AddNewContents(contents, disposition, initial_pos, - user_gesture); - } else { - browser = Browser::Create(contents->profile()); - browser->window()->Show(); - } - } - if (browser) - browser->AddTabContents(contents, disposition, initial_pos, user_gesture); + // If there's no Browser open with the right profile, create a new one. + if (!browser) { + browser = Browser::Create(contents->profile()); + browser->window()->Show(); } + + if (browser) + browser->AddTabContents(contents, disposition, initial_pos, user_gesture); } void ExtensionHost::ShowCreatedWidget(int route_id, @@ -621,6 +636,22 @@ void ExtensionHost::ShowContextMenu(const ContextMenuParams& params) { // TODO(erikkay) Show a default context menu. } +void ExtensionHost::ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned) { +#if defined(OS_MACOSX) + PopupMenuHelper popup_menu_helper(render_view_host()); + popup_menu_helper.ShowPopupMenu(bounds, item_height, item_font_size, + selected_item, items, right_aligned); +#else + // Only on Mac are select popup menus external. + NOTREACHED(); +#endif +} + void ExtensionHost::StartDragging(const WebDropData& drop_data, WebDragOperationsMask operation_mask, const SkBitmap& image, diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h index 49b8191..bb96c5d 100644 --- a/chrome/browser/extensions/extension_host.h +++ b/chrome/browser/extensions/extension_host.h @@ -6,8 +6,9 @@ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_HOST_H_ #pragma once -#include <string> #include <list> +#include <string> +#include <vector> #include "base/perftimer.h" #include "base/scoped_ptr.h" @@ -50,7 +51,7 @@ class ExtensionHost : public RenderViewHostDelegate, typedef std::list<ExtensionHost*> HostPointerList; static HostPointerList* recently_deleted(); - ExtensionHost(Extension* extension, SiteInstance* site_instance, + ExtensionHost(const Extension* extension, SiteInstance* site_instance, const GURL& url, ViewType::Type host_type); ~ExtensionHost(); @@ -72,7 +73,7 @@ class ExtensionHost : public RenderViewHostDelegate, // instantiate Browser objects. void CreateView(Browser* browser); - Extension* extension() { return extension_; } + const Extension* extension() { return extension_; } RenderViewHost* render_view_host() const { return render_view_host_; } RenderProcessHost* render_process_host() const; SiteInstance* site_instance() const; @@ -155,6 +156,12 @@ class ExtensionHost : public RenderViewHostDelegate, const gfx::Rect& initial_pos); virtual void ShowCreatedFullscreenWidget(int route_id); virtual void ShowContextMenu(const ContextMenuParams& params); + virtual void ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned); virtual void StartDragging(const WebDropData& drop_data, WebKit::WebDragOperationsMask allowed_operations, const SkBitmap& image, @@ -231,7 +238,7 @@ class ExtensionHost : public RenderViewHostDelegate, bool is_background_page() const { return !view(); } // The extension that we're hosting in this view. - Extension* extension_; + const Extension* extension_; // The profile that this host is tied to. Profile* profile_; diff --git a/chrome/browser/extensions/extension_host_mac.h b/chrome/browser/extensions/extension_host_mac.h index 65aceaa..da46f16 100644 --- a/chrome/browser/extensions/extension_host_mac.h +++ b/chrome/browser/extensions/extension_host_mac.h @@ -12,7 +12,7 @@ class RenderWidgetHostView; class ExtensionHostMac : public ExtensionHost { public: - ExtensionHostMac(Extension* extension, SiteInstance* site_instance, + ExtensionHostMac(const Extension* extension, SiteInstance* site_instance, const GURL& url, ViewType::Type host_type) : ExtensionHost(extension, site_instance, url, host_type) {} virtual ~ExtensionHostMac(); diff --git a/chrome/browser/extensions/extension_host_mac.mm b/chrome/browser/extensions/extension_host_mac.mm index 3239ebd..d6de067 100644 --- a/chrome/browser/extensions/extension_host_mac.mm +++ b/chrome/browser/extensions/extension_host_mac.mm @@ -31,9 +31,6 @@ RenderWidgetHostView* ExtensionHostMac::CreateNewWidgetInternal( static_cast<RenderWidgetHostViewMac*>(widget_view); [widget_view_mac->native_view() retain]; - // |widget_view_mac| needs to know how to position itself in our view. - widget_view_mac->set_parent_view(view()->native_view()); - return widget_view; } diff --git a/chrome/browser/extensions/extension_icon_manager.cc b/chrome/browser/extensions/extension_icon_manager.cc index 53c8923..887dc3f 100644 --- a/chrome/browser/extensions/extension_icon_manager.cc +++ b/chrome/browser/extensions/extension_icon_manager.cc @@ -45,7 +45,7 @@ ExtensionIconManager::ExtensionIconManager() ExtensionIconManager::~ExtensionIconManager() { } -void ExtensionIconManager::LoadIcon(Extension* extension) { +void ExtensionIconManager::LoadIcon(const Extension* extension) { ExtensionResource icon_resource = extension->GetIconResource( Extension::EXTENSION_ICON_BITTY, ExtensionIconSet::MATCH_BIGGER); if (!icon_resource.extension_root().empty()) { diff --git a/chrome/browser/extensions/extension_icon_manager.h b/chrome/browser/extensions/extension_icon_manager.h index 876f5e0..cc0deb9 100644 --- a/chrome/browser/extensions/extension_icon_manager.h +++ b/chrome/browser/extensions/extension_icon_manager.h @@ -23,7 +23,7 @@ class ExtensionIconManager : public ImageLoadingTracker::Observer { virtual ~ExtensionIconManager(); // Start loading the icon for the given extension. - void LoadIcon(Extension* extension); + void LoadIcon(const Extension* extension); // This returns a bitmap of width/height kFavIconSize, loaded either from an // entry specified in the extension's 'icon' section of the manifest, or a diff --git a/chrome/browser/extensions/extension_icon_manager_unittest.cc b/chrome/browser/extensions/extension_icon_manager_unittest.cc index 51c76ea..37f3453 100644 --- a/chrome/browser/extensions/extension_icon_manager_unittest.cc +++ b/chrome/browser/extensions/extension_icon_manager_unittest.cc @@ -109,26 +109,26 @@ TEST_F(ExtensionIconManagerTest, LoadRemoveLoad) { static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL))); ASSERT_TRUE(manifest.get() != NULL); - Extension extension(manifest_path.DirName()); - ASSERT_TRUE(extension.InitFromValue(*manifest.get(), - false /* require_key */, - NULL /* errors */)); + scoped_refptr<Extension> extension(Extension::Create( + manifest_path.DirName(), Extension::INVALID, *manifest.get(), + false, NULL)); + ASSERT_TRUE(extension.get()); TestIconManager icon_manager(this); // Load the icon and grab the bitmap. - icon_manager.LoadIcon(&extension); + icon_manager.LoadIcon(extension.get()); WaitForImageLoad(); - SkBitmap first_icon = icon_manager.GetIcon(extension.id()); + SkBitmap first_icon = icon_manager.GetIcon(extension->id()); EXPECT_FALSE(gfx::BitmapsAreEqual(first_icon, default_icon)); // Remove the icon from the manager. - icon_manager.RemoveIcon(extension.id()); + icon_manager.RemoveIcon(extension->id()); // Now re-load the icon - we should get the same result bitmap (and not the // default icon). - icon_manager.LoadIcon(&extension); + icon_manager.LoadIcon(extension.get()); WaitForImageLoad(); - SkBitmap second_icon = icon_manager.GetIcon(extension.id()); + SkBitmap second_icon = icon_manager.GetIcon(extension->id()); EXPECT_FALSE(gfx::BitmapsAreEqual(second_icon, default_icon)); EXPECT_TRUE(gfx::BitmapsAreEqual(first_icon, second_icon)); diff --git a/chrome/browser/extensions/extension_info_map.cc b/chrome/browser/extensions/extension_info_map.cc index f9f90aa..313e573 100644 --- a/chrome/browser/extensions/extension_info_map.cc +++ b/chrome/browser/extensions/extension_info_map.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/extension_info_map.h" #include "chrome/browser/browser_thread.h" +#include "chrome/common/extensions/extension.h" #include "chrome/common/url_constants.h" namespace { @@ -21,13 +22,13 @@ ExtensionInfoMap::ExtensionInfoMap() { ExtensionInfoMap::~ExtensionInfoMap() { } -void ExtensionInfoMap::AddExtension(const Extension::StaticData* data) { +void ExtensionInfoMap::AddExtension(const Extension* extension) { CheckOnValidThread(); - extension_info_[data->id] = data; + extension_info_[extension->id()] = extension; // Our map has already added a reference. Balance the reference given at the // call-site. - data->Release(); + extension->Release(); } void ExtensionInfoMap::RemoveExtension(const std::string& id) { @@ -48,7 +49,7 @@ void ExtensionInfoMap::RemoveExtension(const std::string& id) { std::string ExtensionInfoMap::GetNameForExtension(const std::string& id) const { Map::const_iterator iter = extension_info_.find(id); if (iter != extension_info_.end()) - return iter->second->name; + return iter->second->name(); else return std::string(); } @@ -56,21 +57,22 @@ std::string ExtensionInfoMap::GetNameForExtension(const std::string& id) const { FilePath ExtensionInfoMap::GetPathForExtension(const std::string& id) const { Map::const_iterator iter = extension_info_.find(id); if (iter != extension_info_.end()) - return iter->second->path; + return iter->second->path(); else return FilePath(); } bool ExtensionInfoMap::ExtensionHasWebExtent(const std::string& id) const { Map::const_iterator iter = extension_info_.find(id); - return iter != extension_info_.end() && !iter->second->extent.is_empty(); + return iter != extension_info_.end() && + !iter->second->web_extent().is_empty(); } bool ExtensionInfoMap::ExtensionCanLoadInIncognito( const std::string& id) const { Map::const_iterator iter = extension_info_.find(id); // Only split-mode extensions can load in incognito profiles. - return iter != extension_info_.end() && iter->second->incognito_split_mode; + return iter != extension_info_.end() && iter->second->incognito_split_mode(); } std::string ExtensionInfoMap::GetDefaultLocaleForExtension( @@ -78,7 +80,7 @@ std::string ExtensionInfoMap::GetDefaultLocaleForExtension( Map::const_iterator iter = extension_info_.find(id); std::string result; if (iter != extension_info_.end()) - result = iter->second->default_locale; + result = iter->second->default_locale(); return result; } @@ -88,7 +90,7 @@ ExtensionExtent ExtensionInfoMap::GetEffectiveHostPermissionsForExtension( Map::const_iterator iter = extension_info_.find(id); ExtensionExtent result; if (iter != extension_info_.end()) - result = iter->second->effective_host_permissions; + result = iter->second->GetEffectiveHostPermissions(); return result; } @@ -106,15 +108,14 @@ bool ExtensionInfoMap::CheckURLAccessToExtensionPermission( // disallowed, so only one will match. info = extension_info_.begin(); while (info != extension_info_.end() && - !info->second->extent.ContainsURL(url)) + !info->second->web_extent().ContainsURL(url)) ++info; } if (info == extension_info_.end()) return false; - const std::set<std::string>& api_permissions = info->second->api_permissions; - return api_permissions.count(permission_name) != 0; + return info->second->api_permissions().count(permission_name) != 0; } bool ExtensionInfoMap::URLIsForExtensionIcon(const GURL& url) const { @@ -127,5 +128,5 @@ bool ExtensionInfoMap::URLIsForExtensionIcon(const GURL& url) const { std::string path = url.path(); DCHECK(path.length() > 0 && path[0] == '/'); path = path.substr(1); - return iter->second->icons.ContainsPath(path); + return iter->second->icons().ContainsPath(path); } diff --git a/chrome/browser/extensions/extension_info_map.h b/chrome/browser/extensions/extension_info_map.h index fb03162..7d872ba 100644 --- a/chrome/browser/extensions/extension_info_map.h +++ b/chrome/browser/extensions/extension_info_map.h @@ -12,9 +12,11 @@ #include "base/basictypes.h" #include "base/file_path.h" #include "base/ref_counted.h" -#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_extent.h" #include "googleurl/src/gurl.h" +class Extension; + // Contains extension data that needs to be accessed on the IO thread. It can // be created/destroyed on any thread, but all other methods must be called on // the IO thread. @@ -27,7 +29,7 @@ class ExtensionInfoMap : public base::RefCountedThreadSafe<ExtensionInfoMap> { ~ExtensionInfoMap(); // Callback for when new extensions are loaded. - void AddExtension(const Extension::StaticData* data); + void AddExtension(const Extension* extension); // Callback for when an extension is unloaded. void RemoveExtension(const std::string& id); @@ -63,8 +65,7 @@ class ExtensionInfoMap : public base::RefCountedThreadSafe<ExtensionInfoMap> { private: // Map of extension info by extension id. - typedef std::map<std::string, scoped_refptr<const Extension::StaticData> > - Map; + typedef std::map<std::string, scoped_refptr<const Extension> > Map; Map extension_info_; }; diff --git a/chrome/browser/extensions/extension_info_map_unittest.cc b/chrome/browser/extensions/extension_info_map_unittest.cc index 559febb..d4892d1 100644 --- a/chrome/browser/extensions/extension_info_map_unittest.cc +++ b/chrome/browser/extensions/extension_info_map_unittest.cc @@ -7,6 +7,7 @@ #include "chrome/browser/browser_thread.h" #include "chrome/browser/extensions/extension_info_map.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/extensions/extension.h" #include "chrome/common/json_value_serializer.h" #include "testing/gtest/include/gtest/gtest.h" @@ -28,27 +29,27 @@ class ExtensionInfoMapTest : public testing::Test { }; // Returns a barebones test Extension object with the given name. -static Extension* CreateExtension(const std::string& name) { +static scoped_refptr<Extension> CreateExtension(const std::string& name) { #if defined(OS_WIN) FilePath path(FILE_PATH_LITERAL("c:\\foo")); #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - scoped_ptr<Extension> extension(new Extension(path.AppendASCII(name))); - DictionaryValue manifest; manifest.SetString(keys::kVersion, "1.0.0.0"); manifest.SetString(keys::kName, name); std::string error; - EXPECT_TRUE(extension->InitFromValue(manifest, false, &error)) << error; + scoped_refptr<Extension> extension = Extension::Create( + path.AppendASCII(name), Extension::INVALID, manifest, false, &error); + EXPECT_TRUE(extension) << error; - return extension.release(); + return extension; } -static Extension* LoadManifest(const std::string& dir, - const std::string& test_file) { +static scoped_refptr<Extension> LoadManifest(const std::string& dir, + const std::string& test_file) { FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); path = path.AppendASCII("extensions") @@ -61,61 +62,61 @@ static Extension* LoadManifest(const std::string& dir, return NULL; std::string error; - scoped_ptr<Extension> extension(new Extension(path)); - EXPECT_TRUE(extension->InitFromValue( - *static_cast<DictionaryValue*>(result.get()), false, &error)) << error; + scoped_refptr<Extension> extension = Extension::Create( + path, Extension::INVALID, *static_cast<DictionaryValue*>(result.get()), + false, &error); + EXPECT_TRUE(extension) << error; - return extension.release(); + return extension; } // Test that the ExtensionInfoMap handles refcounting properly. TEST_F(ExtensionInfoMapTest, RefCounting) { scoped_refptr<ExtensionInfoMap> info_map(new ExtensionInfoMap()); - // New extensions should have a single reference holding onto their static - // data. - scoped_ptr<Extension> extension1(CreateExtension("extension1")); - scoped_ptr<Extension> extension2(CreateExtension("extension2")); - scoped_ptr<Extension> extension3(CreateExtension("extension3")); - EXPECT_TRUE(extension1->static_data()->HasOneRef()); - EXPECT_TRUE(extension2->static_data()->HasOneRef()); - EXPECT_TRUE(extension3->static_data()->HasOneRef()); + // New extensions should have a single reference holding onto them. + scoped_refptr<Extension> extension1(CreateExtension("extension1")); + scoped_refptr<Extension> extension2(CreateExtension("extension2")); + scoped_refptr<Extension> extension3(CreateExtension("extension3")); + EXPECT_TRUE(extension1->HasOneRef()); + EXPECT_TRUE(extension2->HasOneRef()); + EXPECT_TRUE(extension3->HasOneRef()); // Add a ref to each extension and give it to the info map. The info map // expects the caller to add a ref for it, but then assumes ownership of that // reference. - extension1->static_data()->AddRef(); - info_map->AddExtension(extension1->static_data()); - extension2->static_data()->AddRef(); - info_map->AddExtension(extension2->static_data()); - extension3->static_data()->AddRef(); - info_map->AddExtension(extension3->static_data()); - - // Delete extension1, and the info map should have the only ref. - const Extension::StaticData* data1 = extension1->static_data(); - extension1.reset(); - EXPECT_TRUE(data1->HasOneRef()); + extension1->AddRef(); + info_map->AddExtension(extension1); + extension2->AddRef(); + info_map->AddExtension(extension2); + extension3->AddRef(); + info_map->AddExtension(extension3); + + // Release extension1, and the info map should have the only ref. + const Extension* weak_extension1 = extension1; + extension1 = NULL; + EXPECT_TRUE(weak_extension1->HasOneRef()); // Remove extension2, and the extension2 object should have the only ref. info_map->RemoveExtension(extension2->id()); - EXPECT_TRUE(extension2->static_data()->HasOneRef()); + EXPECT_TRUE(extension2->HasOneRef()); // Delete the info map, and the extension3 object should have the only ref. info_map = NULL; - EXPECT_TRUE(extension3->static_data()->HasOneRef()); + EXPECT_TRUE(extension3->HasOneRef()); } // Tests that we can query a few extension properties from the ExtensionInfoMap. TEST_F(ExtensionInfoMapTest, Properties) { scoped_refptr<ExtensionInfoMap> info_map(new ExtensionInfoMap()); - scoped_ptr<Extension> extension1(CreateExtension("extension1")); - scoped_ptr<Extension> extension2(CreateExtension("extension2")); + scoped_refptr<Extension> extension1(CreateExtension("extension1")); + scoped_refptr<Extension> extension2(CreateExtension("extension2")); - extension1->static_data()->AddRef(); - info_map->AddExtension(extension1->static_data()); - extension2->static_data()->AddRef(); - info_map->AddExtension(extension2->static_data()); + extension1->AddRef(); + info_map->AddExtension(extension1); + extension2->AddRef(); + info_map->AddExtension(extension2); EXPECT_EQ(extension1->name(), info_map->GetNameForExtension(extension1->id())); @@ -132,19 +133,19 @@ TEST_F(ExtensionInfoMapTest, Properties) { TEST_F(ExtensionInfoMapTest, CheckPermissions) { scoped_refptr<ExtensionInfoMap> info_map(new ExtensionInfoMap()); - scoped_ptr<Extension> app(LoadManifest("manifest_tests", + scoped_refptr<Extension> app(LoadManifest("manifest_tests", "valid_app.json")); - scoped_ptr<Extension> extension(LoadManifest("manifest_tests", + scoped_refptr<Extension> extension(LoadManifest("manifest_tests", "tabs_extension.json")); GURL app_url("http://www.google.com/mail/foo.html"); ASSERT_TRUE(app->is_app()); ASSERT_TRUE(app->web_extent().ContainsURL(app_url)); - app->static_data()->AddRef(); - info_map->AddExtension(app->static_data()); - extension->static_data()->AddRef(); - info_map->AddExtension(extension->static_data()); + app->AddRef(); + info_map->AddExtension(app); + extension->AddRef(); + info_map->AddExtension(extension); // The app should have the notifications permission, either from a // chrome-extension URL or from its web extent. diff --git a/chrome/browser/extensions/extension_infobar_apitest.cc b/chrome/browser/extensions/extension_infobar_apitest.cc index a7afb17..5932185 100644 --- a/chrome/browser/extensions/extension_infobar_apitest.cc +++ b/chrome/browser/extensions/extension_infobar_apitest.cc @@ -6,10 +6,11 @@ #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/common/chrome_switches.h" -#if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX) +#if defined(TOOLKIT_VIEWS) #define MAYBE_Infobars Infobars #else // Need to finish port to Linux. See http://crbug.com/39916 for details. +// Temporarily marked as DISABLED on OSX too. See http://crbug.com/60990 for details. #define MAYBE_Infobars DISABLED_Infobars #endif diff --git a/chrome/browser/extensions/extension_infobar_delegate.cc b/chrome/browser/extensions/extension_infobar_delegate.cc index b3b8a39..0aea7ce 100644 --- a/chrome/browser/extensions/extension_infobar_delegate.cc +++ b/chrome/browser/extensions/extension_infobar_delegate.cc @@ -15,7 +15,7 @@ ExtensionInfoBarDelegate::ExtensionInfoBarDelegate(Browser* browser, TabContents* tab_contents, - Extension* extension, + const Extension* extension, const GURL& url) : InfoBarDelegate(tab_contents), observer_(NULL), @@ -78,7 +78,7 @@ void ExtensionInfoBarDelegate::Observe(NotificationType type, break; } case NotificationType::EXTENSION_UNLOADED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (extension_ == extension) tab_contents_->RemoveInfoBar(this); break; diff --git a/chrome/browser/extensions/extension_infobar_delegate.h b/chrome/browser/extensions/extension_infobar_delegate.h index dae7aa9..949b655 100644 --- a/chrome/browser/extensions/extension_infobar_delegate.h +++ b/chrome/browser/extensions/extension_infobar_delegate.h @@ -28,10 +28,10 @@ class ExtensionInfoBarDelegate : public InfoBarDelegate, }; ExtensionInfoBarDelegate(Browser* browser, TabContents* contents, - Extension* extension, const GURL& url); + const Extension* extension, const GURL& url); ~ExtensionInfoBarDelegate(); - Extension* extension() { return extension_; } + const Extension* extension() { return extension_; } ExtensionHost* extension_host() { return extension_host_.get(); } void set_observer(DelegateObserver* observer) { observer_ = observer; } @@ -60,7 +60,7 @@ class ExtensionInfoBarDelegate : public InfoBarDelegate, // The observer monitoring when the delegate dies. DelegateObserver* observer_; - Extension* extension_; + const Extension* extension_; TabContents* tab_contents_; diff --git a/chrome/browser/extensions/extension_infobar_module.cc b/chrome/browser/extensions/extension_infobar_module.cc index 590bfc1..6662bea 100644 --- a/chrome/browser/extensions/extension_infobar_module.cc +++ b/chrome/browser/extensions/extension_infobar_module.cc @@ -32,7 +32,7 @@ bool ShowInfoBarFunction::RunImpl() { std::string html_path; EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kHtmlPath, &html_path)); - Extension* extension = GetExtension(); + const Extension* extension = GetExtension(); GURL url = extension->GetResourceURL(extension->url(), html_path); Browser* browser = NULL; diff --git a/chrome/browser/extensions/extension_install_ui.cc b/chrome/browser/extensions/extension_install_ui.cc index b1b2205..3c5f819 100644 --- a/chrome/browser/extensions/extension_install_ui.cc +++ b/chrome/browser/extensions/extension_install_ui.cc @@ -82,7 +82,7 @@ ExtensionInstallUI::~ExtensionInstallUI() { } void ExtensionInstallUI::ConfirmInstall(Delegate* delegate, - Extension* extension) { + const Extension* extension) { DCHECK(ui_loop_ == MessageLoop::current()); extension_ = extension; delegate_ = delegate; @@ -92,7 +92,7 @@ void ExtensionInstallUI::ConfirmInstall(Delegate* delegate, // to allow the user to revert if they don't like it. if (extension->is_theme()) { // Remember the current theme in case the user pressed undo. - Extension* previous_theme = profile_->GetTheme(); + const Extension* previous_theme = profile_->GetTheme(); if (previous_theme) previous_theme_id_ = previous_theme->id(); @@ -113,7 +113,7 @@ void ExtensionInstallUI::ConfirmInstall(Delegate* delegate, } void ExtensionInstallUI::ConfirmUninstall(Delegate* delegate, - Extension* extension) { + const Extension* extension) { DCHECK(ui_loop_ == MessageLoop::current()); extension_ = extension; delegate_ = delegate; @@ -121,7 +121,7 @@ void ExtensionInstallUI::ConfirmUninstall(Delegate* delegate, ShowConfirmation(UNINSTALL_PROMPT); } -void ExtensionInstallUI::OnInstallSuccess(Extension* extension) { +void ExtensionInstallUI::OnInstallSuccess(const Extension* extension) { if (extension->is_theme()) { ShowThemeInfoBar(previous_theme_id_, previous_use_system_theme_, extension, profile_); @@ -226,7 +226,7 @@ void ExtensionInstallUI::OnImageLoaded( void ExtensionInstallUI::ShowThemeInfoBar( const std::string& previous_theme_id, bool previous_use_system_theme, - Extension* new_theme, Profile* profile) { + const Extension* new_theme, Profile* profile) { if (!new_theme->is_theme()) return; @@ -283,7 +283,7 @@ void ExtensionInstallUI::ShowConfirmation(PromptType prompt_type) { #if defined(OS_MACOSX) void ExtensionInstallUI::ShowGenericExtensionInstalledInfoBar( - Extension* new_extension) { + const Extension* new_extension) { Browser* browser = BrowserList::GetLastActiveWithProfile(profile_); if (!browser) return; @@ -304,7 +304,7 @@ void ExtensionInstallUI::ShowGenericExtensionInstalledInfoBar( #endif InfoBarDelegate* ExtensionInstallUI::GetNewThemeInstalledInfoBarDelegate( - TabContents* tab_contents, Extension* new_theme, + TabContents* tab_contents, const Extension* new_theme, const std::string& previous_theme_id, bool previous_use_system_theme) { #if defined(TOOLKIT_GTK) return new GtkThemeInstalledInfoBarDelegate(tab_contents, new_theme, diff --git a/chrome/browser/extensions/extension_install_ui.h b/chrome/browser/extensions/extension_install_ui.h index 7ab2c02..6cc8876 100644 --- a/chrome/browser/extensions/extension_install_ui.h +++ b/chrome/browser/extensions/extension_install_ui.h @@ -58,17 +58,17 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { // // We *MUST* eventually call either Proceed() or Abort() // on |delegate|. - virtual void ConfirmInstall(Delegate* delegate, Extension* extension); + virtual void ConfirmInstall(Delegate* delegate, const Extension* extension); // This is called by the extensions management page to verify whether the // uninstallation should proceed. This is declared virtual for testing. // // We *MUST* eventually call either Proceed() or Abort() // on |delegate|. - virtual void ConfirmUninstall(Delegate* delegate, Extension* extension); + virtual void ConfirmUninstall(Delegate* delegate, const Extension* extension); // Installation was successful. This is declared virtual for testing. - virtual void OnInstallSuccess(Extension* extension); + virtual void OnInstallSuccess(const Extension* extension); // Installation failed. This is declared virtual for testing. virtual void OnInstallFailure(const std::string& error); @@ -85,7 +85,7 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { // GetNewThemeInstalledInfoBarDelegate()). static void ShowThemeInfoBar( const std::string& previous_theme_id, bool previous_use_system_theme, - Extension* new_theme, Profile* profile); + const Extension* new_theme, Profile* profile); private: // Starts the process of showing a confirmation UI, which is split into two. @@ -96,25 +96,25 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { #if defined(OS_MACOSX) // When an extension is installed on Mac with neither browser action nor // page action icons, show an infobar instead of a popup bubble. - void ShowGenericExtensionInstalledInfoBar(Extension* new_extension); + void ShowGenericExtensionInstalledInfoBar(const Extension* new_extension); #endif // Returns the delegate to control the browser's info bar. This is // within its own function due to its platform-specific nature. static InfoBarDelegate* GetNewThemeInstalledInfoBarDelegate( - TabContents* tab_contents, Extension* new_theme, + TabContents* tab_contents, const Extension* new_theme, const std::string& previous_theme_id, bool previous_use_system_theme); // Implements the showing of the install/uninstall dialog prompt. // NOTE: The implementations of this function is platform-specific. static void ShowExtensionInstallUIPromptImpl( - Profile* profile, Delegate* delegate, Extension* extension, + Profile* profile, Delegate* delegate, const Extension* extension, SkBitmap* icon, PromptType type); // Implements the showing of the new install dialog. The implementations of // this function are platform-specific. static void ShowExtensionInstallUIPrompt2Impl( - Profile* profile, Delegate* delegate, Extension* extension, + Profile* profile, Delegate* delegate, const Extension* extension, SkBitmap* icon, const std::vector<string16>& permissions); Profile* profile_; @@ -125,7 +125,7 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { bool previous_use_system_theme_; SkBitmap icon_; // The extensions installation icon. - Extension* extension_; // The extension we are showing the UI for. + const Extension* extension_; // The extension we are showing the UI for. Delegate* delegate_; // The delegate we will call Proceed/Abort on after // confirmation UI. PromptType prompt_type_; // The type of prompt we are going to show. diff --git a/chrome/browser/extensions/extension_install_ui_browsertest.cc b/chrome/browser/extensions/extension_install_ui_browsertest.cc index 14a6a5b..9a5a9f2 100644 --- a/chrome/browser/extensions/extension_install_ui_browsertest.cc +++ b/chrome/browser/extensions/extension_install_ui_browsertest.cc @@ -39,7 +39,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionInstallUIBrowserTest, // Install theme once and undo to verify we go back to default theme. FilePath theme_path = test_data_dir_.AppendASCII("theme.crx"); ASSERT_TRUE(InstallExtensionWithUI(theme_path, 1)); - Extension* theme = browser()->profile()->GetTheme(); + const Extension* theme = browser()->profile()->GetTheme(); ASSERT_TRUE(theme); ASSERT_EQ(theme_crx, theme->id()); VerifyThemeInfoBarAndUndoInstall(); diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc index 50777db..44fd63f 100644 --- a/chrome/browser/extensions/extension_management_api.cc +++ b/chrome/browser/extensions/extension_management_api.cc @@ -104,10 +104,28 @@ bool GetAllExtensionsFunction::RunImpl() { return true; } +bool GetExtensionByIdFunction::RunImpl() { + std::string extension_id; + EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); + const Extension* extension = service()->GetExtensionById(extension_id, true); + if (!extension) { + error_ = ExtensionErrorUtils::FormatErrorMessage(kNoExtensionError, + extension_id); + return false; + } + bool enabled = service()->extension_prefs()-> + GetExtensionState(extension_id) == Extension::ENABLED; + + DictionaryValue* result = CreateExtensionInfo(*extension, enabled); + result_.reset(result); + + return true; +} + bool LaunchAppFunction::RunImpl() { std::string extension_id; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); - Extension* extension = service()->GetExtensionById(extension_id, true); + const Extension* extension = service()->GetExtensionById(extension_id, true); if (!extension) { error_ = ExtensionErrorUtils::FormatErrorMessage(kNoExtensionError, extension_id); @@ -219,7 +237,7 @@ void ExtensionManagementEventRouter::Observe( Details<UninstalledExtensionInfo>(details).ptr()->extension_id; args.Append(Value::CreateStringValue(extension_id)); } else { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); CHECK(extension); ExtensionsService* service = profile->GetExtensionsService(); bool enabled = service->GetExtensionById(extension->id(), false) != NULL; diff --git a/chrome/browser/extensions/extension_management_api.h b/chrome/browser/extensions/extension_management_api.h index 3176aef..864741f 100644 --- a/chrome/browser/extensions/extension_management_api.h +++ b/chrome/browser/extensions/extension_management_api.h @@ -24,6 +24,12 @@ class GetAllExtensionsFunction : public ExtensionManagementFunction { DECLARE_EXTENSION_FUNCTION_NAME("management.getAll"); }; +class GetExtensionByIdFunction : public ExtensionManagementFunction { + ~GetExtensionByIdFunction() {} + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("management.get"); +}; + class LaunchAppFunction : public ExtensionManagementFunction { ~LaunchAppFunction() {} virtual bool RunImpl(); diff --git a/chrome/browser/extensions/extension_management_browsertest.cc b/chrome/browser/extensions/extension_management_browsertest.cc index e6b1f8f..7931e91 100644 --- a/chrome/browser/extensions/extension_management_browsertest.cc +++ b/chrome/browser/extensions/extension_management_browsertest.cc @@ -25,7 +25,7 @@ class ExtensionManagementTest : public ExtensionBrowserTest { // in the extension's manifest. We use the version as reported by the // background page to test how overinstalling crx files with the same // manifest version works. - bool IsExtensionAtVersion(Extension* extension, + bool IsExtensionAtVersion(const Extension* extension, const std::string& expected_version) { // Test that the extension's version from the manifest and reported by the // background page is correct. This is to ensure that the processes are in @@ -171,7 +171,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, DisableEnable) { .AppendASCII("1.0"))); ASSERT_EQ(size_before + 1, service->extensions()->size()); EXPECT_EQ(0u, service->disabled_extensions()->size()); - Extension* extension = service->extensions()->at(size_before); + const Extension* extension = service->extensions()->at(size_before); EXPECT_TRUE(manager->GetBackgroundHostForExtension(extension)); ASSERT_TRUE(service->HasInstalledExtensions()); @@ -274,7 +274,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, ExternalUrlUpdate) { // is race-prone, because instantating the ExtensionService starts a read // of external_extensions.json before this test function starts. service->AddPendingExtensionFromExternalUpdateUrl( - kExtensionId, GURL("http://localhost/autoupdate/manifest")); + kExtensionId, GURL("http://localhost/autoupdate/manifest"), + Extension::EXTERNAL_PREF_DOWNLOAD); // Run autoupdate and make sure version 2 of the extension was installed. service->updater()->CheckNow(); diff --git a/chrome/browser/extensions/extension_menu_manager.cc b/chrome/browser/extensions/extension_menu_manager.cc index 307c339..eae74f6 100644 --- a/chrome/browser/extensions/extension_menu_manager.cc +++ b/chrome/browser/extensions/extension_menu_manager.cc @@ -125,7 +125,7 @@ const ExtensionMenuItem::List* ExtensionMenuManager::MenuItems( return NULL; } -bool ExtensionMenuManager::AddContextItem(Extension* extension, +bool ExtensionMenuManager::AddContextItem(const Extension* extension, ExtensionMenuItem* item) { const std::string& extension_id = item->extension_id(); // The item must have a non-empty extension id, and not have already been @@ -276,8 +276,10 @@ bool ExtensionMenuManager::RemoveContextMenuItem( items_by_id_.erase(*removed_iter); } - if (list.empty()) + if (list.empty()) { + context_items_.erase(extension_id); icon_manager_.RemoveIcon(extension_id); + } return result; } @@ -387,9 +389,9 @@ void ExtensionMenuManager::ExecuteCommand( ListValue args; DictionaryValue* properties = new DictionaryValue(); - properties->SetInteger("menuItemId", item->id().second); + properties->SetInteger("menuItemId", item->id().uid); if (item->parent_id()) - properties->SetInteger("parentMenuItemId", item->parent_id()->second); + properties->SetInteger("parentMenuItemId", item->parent_id()->uid); switch (params.media_type) { case WebKit::WebContextMenuData::MediaTypeImage: @@ -453,7 +455,7 @@ void ExtensionMenuManager::Observe(NotificationType type, NOTREACHED(); return; } - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (ContainsKey(context_items_, extension->id())) { RemoveAllContextItems(extension->id()); } @@ -469,3 +471,36 @@ bool ExtensionMenuManager::HasAllowedScheme(const GURL& url) { URLPattern pattern(kAllowedSchemes); return pattern.SetScheme(url.scheme()); } + +ExtensionMenuItem::Id::Id() + : profile(NULL), uid(0) { +} + +ExtensionMenuItem::Id::Id(Profile* profile, std::string extension_id, int uid) + : profile(profile), extension_id(extension_id), uid(uid) { +} + +ExtensionMenuItem::Id::~Id() { +} + +bool ExtensionMenuItem::Id::operator==(const Id& other) const { + return (profile == other.profile && + extension_id == other.extension_id && + uid == other.uid); +} + +bool ExtensionMenuItem::Id::operator!=(const Id& other) const { + return !(*this == other); +} + +bool ExtensionMenuItem::Id::operator<(const Id& other) const { + if (profile < other.profile) + return true; + if (profile == other.profile) { + if (extension_id < other.extension_id) + return true; + if (extension_id == other.extension_id) + return uid < other.uid; + } + return false; +} diff --git a/chrome/browser/extensions/extension_menu_manager.h b/chrome/browser/extensions/extension_menu_manager.h index a58c1df..c8224d1 100644 --- a/chrome/browser/extensions/extension_menu_manager.h +++ b/chrome/browser/extensions/extension_menu_manager.h @@ -33,9 +33,20 @@ class ExtensionMenuItem { // A list of ExtensionMenuItem's. typedef std::vector<ExtensionMenuItem*> List; - // An Id is a pair of |extension id|, |int| where the |int| is unique per - // extension. - typedef std::pair<std::string, int> Id; + // An Id uniquely identifies a context menu item registered by an extension. + struct Id { + Id(); + Id(Profile* profile, std::string extension_id, int uid); + ~Id(); + + bool operator==(const Id& other) const; + bool operator!=(const Id& other) const; + bool operator<(const Id& other) const; + + Profile* profile; + std::string extension_id; + int uid; + }; // For context menus, these are the contexts where an item can appear. enum Context { @@ -93,7 +104,7 @@ class ExtensionMenuItem { virtual ~ExtensionMenuItem(); // Simple accessor methods. - const std::string& extension_id() const { return id_.first; } + const std::string& extension_id() const { return id_.extension_id; } const std::string& title() const { return title_; } const List& children() { return children_; } const Id& id() const { return id_; } @@ -198,7 +209,7 @@ class ExtensionMenuManager : public NotificationObserver { // Adds a top-level menu item for an extension, requiring the |extension| // pointer so it can load the icon for the extension. Takes ownership of // |item|. Returns a boolean indicating success or failure. - bool AddContextItem(Extension* extension, ExtensionMenuItem* item); + bool AddContextItem(const Extension* extension, ExtensionMenuItem* item); // Add an item as a child of another item which has been previously added, and // takes ownership of |item|. Returns a boolean indicating success or failure. @@ -244,6 +255,7 @@ class ExtensionMenuManager : public NotificationObserver { private: FRIEND_TEST_ALL_PREFIXES(ExtensionMenuManagerTest, DeleteParent); + FRIEND_TEST_ALL_PREFIXES(ExtensionMenuManagerTest, RemoveOneByOne); // This is a helper function which takes care of de-selecting any other radio // items in the same group (i.e. that are adjacent in the list). diff --git a/chrome/browser/extensions/extension_menu_manager_unittest.cc b/chrome/browser/extensions/extension_menu_manager_unittest.cc index 0b653ba..cbd9a52 100644 --- a/chrome/browser/extensions/extension_menu_manager_unittest.cc +++ b/chrome/browser/extensions/extension_menu_manager_unittest.cc @@ -37,21 +37,21 @@ class ExtensionMenuManagerTest : public testing::Test { ExtensionMenuItem* CreateTestItem(Extension* extension) { ExtensionMenuItem::Type type = ExtensionMenuItem::NORMAL; ExtensionMenuItem::ContextList contexts(ExtensionMenuItem::ALL); - ExtensionMenuItem::Id id(extension->id(), next_id_++); + ExtensionMenuItem::Id id(NULL, extension->id(), next_id_++); return new ExtensionMenuItem(id, "test", false, type, contexts); } // Creates and returns a test Extension. The caller does *not* own the return // value. Extension* AddExtension(std::string name) { - Extension* extension = prefs_.AddExtension(name); + scoped_refptr<Extension> extension = prefs_.AddExtension(name); extensions_.push_back(extension); return extension; } protected: ExtensionMenuManager manager_; - ScopedVector<Extension> extensions_; + ExtensionList extensions_; TestExtensionPrefs prefs_; int next_id_; @@ -94,7 +94,7 @@ TEST_F(ExtensionMenuManagerTest, AddGetRemoveItems) { ASSERT_EQ(2u, manager_.MenuItems(extension_id)->size()); // Make sure removing a non-existent item returns false. - ExtensionMenuItem::Id id(extension->id(), id3.second + 50); + ExtensionMenuItem::Id id(NULL, extension->id(), id3.uid + 50); ASSERT_FALSE(manager_.RemoveContextMenuItem(id)); } @@ -211,7 +211,7 @@ TEST_F(ExtensionMenuManagerTest, DeleteParent) { // Now remove item1 and make sure item2 and item3 are gone as well. ASSERT_TRUE(manager_.RemoveContextMenuItem(item1_id)); - ASSERT_EQ(0u, manager_.MenuItems(extension->id())->size()); + ASSERT_EQ(NULL, manager_.MenuItems(extension->id())); ASSERT_EQ(0u, manager_.items_by_id_.size()); ASSERT_EQ(NULL, manager_.GetItemById(item1_id)); ASSERT_EQ(NULL, manager_.GetItemById(item2_id)); @@ -323,7 +323,7 @@ TEST_F(ExtensionMenuManagerTest, ExtensionUnloadRemovesMenuItems) { // gone. notifier->Notify(NotificationType::EXTENSION_UNLOADED, Source<Profile>(NULL), - Details<Extension>(extension1)); + Details<const Extension>(extension1)); ASSERT_EQ(NULL, manager_.MenuItems(extension1->id())); ASSERT_EQ(1u, manager_.MenuItems(extension2->id())->size()); ASSERT_TRUE(manager_.GetItemById(id1) == NULL); @@ -389,6 +389,23 @@ TEST_F(ExtensionMenuManagerTest, RemoveAll) { EXPECT_EQ(NULL, manager_.MenuItems(extension1->id())); } +// Tests that removing all items one-by-one doesn't leave an entry around. +TEST_F(ExtensionMenuManagerTest, RemoveOneByOne) { + // Add 2 test items. + Extension* extension1 = AddExtension("1111"); + ExtensionMenuItem* item1 = CreateTestItem(extension1); + ExtensionMenuItem* item2 = CreateTestItem(extension1); + ASSERT_TRUE(manager_.AddContextItem(extension1, item1)); + ASSERT_TRUE(manager_.AddContextItem(extension1, item2)); + + ASSERT_FALSE(manager_.context_items_.empty()); + + manager_.RemoveContextMenuItem(item1->id()); + manager_.RemoveContextMenuItem(item2->id()); + + ASSERT_TRUE(manager_.context_items_.empty()); +} + TEST_F(ExtensionMenuManagerTest, ExecuteCommand) { MessageLoopForUI message_loop; BrowserThread ui_thread(BrowserThread::UI, &message_loop); @@ -444,7 +461,7 @@ TEST_F(ExtensionMenuManagerTest, ExecuteCommand) { int tmp_id = 0; ASSERT_TRUE(info->GetInteger("menuItemId", &tmp_id)); - ASSERT_EQ(id.second, tmp_id); + ASSERT_EQ(id.uid, tmp_id); std::string tmp; ASSERT_TRUE(info->GetString("mediaType", &tmp)); diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc index a3a13c1..01faa1d 100644 --- a/chrome/browser/extensions/extension_message_service.cc +++ b/chrome/browser/extensions/extension_message_service.cc @@ -74,9 +74,11 @@ static void DispatchOnConnect(const ExtensionMessageService::MessagePort& port, } static void DispatchOnDisconnect( - const ExtensionMessageService::MessagePort& port, int source_port_id) { + const ExtensionMessageService::MessagePort& port, int source_port_id, + bool connection_error) { ListValue args; args.Set(0, Value::CreateIntegerValue(source_port_id)); + args.Set(1, Value::CreateBooleanValue(connection_error)); port.sender->Send(new ViewMsg_ExtensionMessageInvoke(port.routing_id, "", ExtensionMessageService::kDispatchOnDisconnect, args, GURL())); } @@ -183,7 +185,7 @@ void ExtensionMessageService::OpenChannelToTab( // The tab isn't loaded yet. Don't attempt to connect. Treat this as a // disconnect. DispatchOnDisconnect(MessagePort(source, MSG_ROUTING_CONTROL), - GET_OPPOSITE_PORT_ID(receiver_port_id)); + GET_OPPOSITE_PORT_ID(receiver_port_id), true); return; } @@ -209,14 +211,13 @@ bool ExtensionMessageService::OpenChannelImpl( const std::string& source_extension_id, const std::string& target_extension_id, const std::string& channel_name) { - // TODO(mpcomplete): notify source if receiver doesn't exist if (!source) return false; // Closed while in flight. if (!receiver.sender) { // Treat it as a disconnect. DispatchOnDisconnect(MessagePort(source, MSG_ROUTING_CONTROL), - GET_OPPOSITE_PORT_ID(receiver_port_id)); + GET_OPPOSITE_PORT_ID(receiver_port_id), true); return false; } @@ -303,7 +304,7 @@ void ExtensionMessageService::CloseChannelImpl( channel_iter->second->receiver : channel_iter->second->opener; if (notify_other_port) - DispatchOnDisconnect(port, GET_OPPOSITE_PORT_ID(closing_port_id)); + DispatchOnDisconnect(port, GET_OPPOSITE_PORT_ID(closing_port_id), false); delete channel_iter->second; channels_.erase(channel_iter); } diff --git a/chrome/browser/extensions/extension_metrics_apitest.cc b/chrome/browser/extensions/extension_metrics_apitest.cc index ee4761c..c9464d2 100644 --- a/chrome/browser/extensions/extension_metrics_apitest.cc +++ b/chrome/browser/extensions/extension_metrics_apitest.cc @@ -146,7 +146,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Metrics) { UserActionObserver observer; ASSERT_TRUE(RunExtensionTest("metrics")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension); observer.ValidateUserActions(extension, diff --git a/chrome/browser/extensions/extension_omnibox_api.cc b/chrome/browser/extensions/extension_omnibox_api.cc index 3e4887d..93b44bd 100644 --- a/chrome/browser/extensions/extension_omnibox_api.cc +++ b/chrome/browser/extensions/extension_omnibox_api.cc @@ -160,7 +160,7 @@ ExtensionOmniboxSuggestion::ExtensionOmniboxSuggestion() {} ExtensionOmniboxSuggestion::~ExtensionOmniboxSuggestion() {} -ExtensionOmniboxSuggestions::ExtensionOmniboxSuggestions() {} +ExtensionOmniboxSuggestions::ExtensionOmniboxSuggestions() : request_id(0) {} ExtensionOmniboxSuggestions::~ExtensionOmniboxSuggestions() {} diff --git a/chrome/browser/extensions/extension_omnibox_api.h b/chrome/browser/extensions/extension_omnibox_api.h index 538ce85..9d0dc99 100644 --- a/chrome/browser/extensions/extension_omnibox_api.h +++ b/chrome/browser/extensions/extension_omnibox_api.h @@ -7,7 +7,7 @@ #pragma once #include "base/string16.h" -#include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/extensions/extension_function.h" // Event router class for events related to the omnibox API. diff --git a/chrome/browser/extensions/extension_omnibox_apitest.cc b/chrome/browser/extensions/extension_omnibox_apitest.cc index f48d9a1..9f9c6a0 100644 --- a/chrome/browser/extensions/extension_omnibox_apitest.cc +++ b/chrome/browser/extensions/extension_omnibox_apitest.cc @@ -8,6 +8,7 @@ #include "chrome/browser/autocomplete/autocomplete.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/autocomplete/autocomplete_edit_view.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_window.h" diff --git a/chrome/browser/extensions/extension_popup_apitest.cc b/chrome/browser/extensions/extension_popup_apitest.cc index 1bc4aed..d4afd66 100644 --- a/chrome/browser/extensions/extension_popup_apitest.cc +++ b/chrome/browser/extensions/extension_popup_apitest.cc @@ -6,8 +6,7 @@ #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/common/chrome_switches.h" -// Times out. See http://crbug.com/46601. -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_Popup) { +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Popup) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); diff --git a/chrome/browser/extensions/extension_pref_store.cc b/chrome/browser/extensions/extension_pref_store.cc index 773e94e..27719ed 100644 --- a/chrome/browser/extensions/extension_pref_store.cc +++ b/chrome/browser/extensions/extension_pref_store.cc @@ -25,7 +25,7 @@ ExtensionPrefStore::~ExtensionPrefStore() { notification_registrar_.RemoveAll(); } -void ExtensionPrefStore::InstallExtensionPref(Extension* extension, +void ExtensionPrefStore::InstallExtensionPref(const Extension* extension, const char* new_pref_path, Value* new_pref_value) { ExtensionStack::iterator i; @@ -62,7 +62,7 @@ void ExtensionPrefStore::InstallExtensionPref(Extension* extension, UpdateOnePref(new_pref_path); } -void ExtensionPrefStore::UninstallExtension(Extension* extension) { +void ExtensionPrefStore::UninstallExtension(const Extension* extension) { // Remove this extension from the stack. for (ExtensionStack::iterator i = extension_stack_.begin(); i != extension_stack_.end(); ++i) { @@ -178,7 +178,7 @@ void ExtensionPrefStore::Observe(NotificationType type, } case NotificationType::EXTENSION_UNLOADED: { Profile* extension_profile = Source<Profile>(source).ptr(); - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); // The ExtensionPrefStore for the local state watches all profiles. if (profile_ == NULL || profile_ == extension_profile) UninstallExtension(extension); @@ -190,7 +190,7 @@ void ExtensionPrefStore::Observe(NotificationType type, } } -ExtensionPrefStore::ExtensionPrefs::ExtensionPrefs(Extension* extension, +ExtensionPrefStore::ExtensionPrefs::ExtensionPrefs(const Extension* extension, PrefValueMap* values) : extension(extension), pref_values(values) {} ExtensionPrefStore::ExtensionPrefs::~ExtensionPrefs() { diff --git a/chrome/browser/extensions/extension_pref_store.h b/chrome/browser/extensions/extension_pref_store.h index 926f29e..f176758 100644 --- a/chrome/browser/extensions/extension_pref_store.h +++ b/chrome/browser/extensions/extension_pref_store.h @@ -33,32 +33,35 @@ class Value; class ExtensionPrefStore : public PrefStore, public NotificationObserver { public: + // Maps preference paths to their values. + typedef std::map<const char*, Value*> PrefValueMap; + + // The type passed as Details for an EXTENSION_PREF_CHANGED notification. + // The nested pairs are <extension, <pref_path, pref_value> >. This is here, + // rather than in (say) notification_type.h, to keep the dependency on + // std::pair out of the many places that include notification_type.h. + typedef std::pair<const Extension*, std::pair<const char*, Value*> > + ExtensionPrefDetails; + ExtensionPrefStore(Profile* profile, PrefNotifier::PrefStoreType type); virtual ~ExtensionPrefStore(); // Begins tracking the preference and value an extension wishes to set. This // must be called each time an extension API tries to set a preference. // The ExtensionPrefStore will take ownership of the |pref_value|. - virtual void InstallExtensionPref(Extension* extension, + virtual void InstallExtensionPref(const Extension* extension, const char* pref_path, Value* pref_value); // Removes an extension and all its preference settings from this PrefStore. // This must be called when an extension is uninstalled or disabled. - virtual void UninstallExtension(Extension* extension); + virtual void UninstallExtension(const Extension* extension); // PrefStore methods: - virtual DictionaryValue* prefs() { return prefs_.get(); } + virtual DictionaryValue* prefs() const { return prefs_.get(); } virtual PrefReadError ReadPrefs() { return PREF_READ_ERROR_NONE; } - // The type passed as Details for an EXTENSION_PREF_CHANGED notification. - // The nested pairs are <extension, <pref_path, pref_value> >. This is here, - // rather than in (say) notification_type.h, to keep the dependency on - // std::pair out of the many places that include notification_type.h. - typedef std::pair<Extension*, std::pair<const char*, Value*> > - ExtensionPrefDetails; - protected: // Returns a vector of the extension IDs in the extension_stack_. // This should only be accessed by subclasses for unit-testing. @@ -70,8 +73,18 @@ class ExtensionPrefStore : public PrefStore, virtual PrefService* GetPrefService(); private: - // Maps preference paths to their values. - typedef std::map<const char*, Value*> PrefValueMap; + // Associates an extension with the prefs it sets. Owns the pref values. + struct ExtensionPrefs { + ExtensionPrefs(const Extension* extension, PrefValueMap* values); + ~ExtensionPrefs(); + + const Extension* extension; + PrefValueMap* pref_values; + }; + + // A pseudo-stack of extensions and their preferences. Extensions are always + // added to the head, but may be removed from the middle. + typedef std::list<ExtensionPrefs*> ExtensionStack; // Applies the highest-priority extension's setting for the given preference // path to the |prefs_| store, or clears the setting there if no extensions @@ -93,18 +106,6 @@ class ExtensionPrefStore : public PrefStore, // extension is controlling, for quick read access. Owns the stored values. scoped_ptr<DictionaryValue> prefs_; - // Associates an extension with the prefs it sets. Owns the pref values. - struct ExtensionPrefs { - ExtensionPrefs(Extension* extension, PrefValueMap* values); - ~ExtensionPrefs(); - - Extension* extension; - PrefValueMap* pref_values; - }; - - // A pseudo-stack of extensions and their preferences. Extensions are always - // added to the head, but may be removed from the middle. - typedef std::list<ExtensionPrefs*> ExtensionStack; ExtensionStack extension_stack_; NotificationRegistrar notification_registrar_; diff --git a/chrome/browser/extensions/extension_pref_store_unittest.cc b/chrome/browser/extensions/extension_pref_store_unittest.cc index aebe5e6..90bed62 100644 --- a/chrome/browser/extensions/extension_pref_store_unittest.cc +++ b/chrome/browser/extensions/extension_pref_store_unittest.cc @@ -17,6 +17,8 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +namespace keys = extension_manifest_keys; + namespace { class TestExtensionPrefStore : public ExtensionPrefStore { @@ -32,20 +34,25 @@ class TestExtensionPrefStore : public ExtensionPrefStore { ADD_FAILURE() << "Failed to create temp dir"; return; } - DictionaryValue empty_dict; + DictionaryValue simple_dict; std::string error; - ext1_scoped_.reset(new Extension(temp_dir_.path().AppendASCII("ext1"))); - ext2_scoped_.reset(new Extension(temp_dir_.path().AppendASCII("ext2"))); - ext3_scoped_.reset(new Extension(temp_dir_.path().AppendASCII("ext3"))); + simple_dict.SetString(keys::kVersion, "1.0.0.0"); + simple_dict.SetString(keys::kName, "unused"); + + ext1_scoped_ = Extension::Create( + temp_dir_.path().AppendASCII("ext1"), Extension::INVALID, + simple_dict, false, &error); + ext2_scoped_ = Extension::Create( + temp_dir_.path().AppendASCII("ext2"), Extension::INVALID, + simple_dict, false, &error); + ext3_scoped_ = Extension::Create( + temp_dir_.path().AppendASCII("ext3"), Extension::INVALID, + simple_dict, false, &error); ext1 = ext1_scoped_.get(); ext2 = ext2_scoped_.get(); ext3 = ext3_scoped_.get(); - - EXPECT_FALSE(ext1->InitFromValue(empty_dict, false, &error)); - EXPECT_FALSE(ext2->InitFromValue(empty_dict, false, &error)); - EXPECT_FALSE(ext3->InitFromValue(empty_dict, false, &error)); } typedef std::vector<std::string> ExtensionIDs; @@ -70,9 +77,9 @@ class TestExtensionPrefStore : public ExtensionPrefStore { private: ScopedTempDir temp_dir_; - scoped_ptr<Extension> ext1_scoped_; - scoped_ptr<Extension> ext2_scoped_; - scoped_ptr<Extension> ext3_scoped_; + scoped_refptr<Extension> ext1_scoped_; + scoped_refptr<Extension> ext2_scoped_; + scoped_refptr<Extension> ext3_scoped_; // Weak reference. PrefService* pref_service_; diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc index 5ebae6a..bf80550 100644 --- a/chrome/browser/extensions/extension_prefs.cc +++ b/chrome/browser/extensions/extension_prefs.cc @@ -320,7 +320,7 @@ bool ExtensionPrefs::DidExtensionEscalatePermissions( } void ExtensionPrefs::SetDidExtensionEscalatePermissions( - Extension* extension, bool did_escalate) { + const Extension* extension, bool did_escalate) { UpdateExtensionPref(extension->id(), kExtensionDidEscalatePermissions, Value::CreateBooleanValue(did_escalate)); prefs_->ScheduleSavePersistentPrefs(); @@ -518,7 +518,7 @@ void ExtensionPrefs::SetToolbarOrder( } void ExtensionPrefs::OnExtensionInstalled( - Extension* extension, Extension::State initial_state, + const Extension* extension, Extension::State initial_state, bool initial_incognito_enabled) { const std::string& id = extension->id(); UpdateExtensionPref(id, kPrefState, @@ -575,7 +575,7 @@ Extension::State ExtensionPrefs::GetExtensionState( return static_cast<Extension::State>(state); } -void ExtensionPrefs::SetExtensionState(Extension* extension, +void ExtensionPrefs::SetExtensionState(const Extension* extension, Extension::State state) { UpdateExtensionPref(extension->id(), kPrefState, Value::CreateIntegerValue(state)); @@ -596,7 +596,7 @@ std::string ExtensionPrefs::GetVersionString(const std::string& extension_id) { return version; } -void ExtensionPrefs::UpdateManifest(Extension* extension) { +void ExtensionPrefs::UpdateManifest(const Extension* extension) { if (extension->location() != Extension::LOAD) { UpdateExtensionPref(extension->id(), kPrefManifest, extension->manifest_value()->DeepCopy()); @@ -903,5 +903,6 @@ void ExtensionPrefs::RegisterUserPrefs(PrefService* prefs) { prefs->RegisterDictionaryPref(kExtensionsBlacklistUpdate); prefs->RegisterListPref(prefs::kExtensionInstallAllowList); prefs->RegisterListPref(prefs::kExtensionInstallDenyList); + prefs->RegisterListPref(prefs::kExtensionInstallForceList); prefs->RegisterStringPref(kWebStoreLogin, std::string() /* default_value */); } diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h index 97547ea..eb16329 100644 --- a/chrome/browser/extensions/extension_prefs.h +++ b/chrome/browser/extensions/extension_prefs.h @@ -63,7 +63,7 @@ class ExtensionPrefs { void SetToolbarOrder(const std::vector<std::string>& extension_ids); // Called when an extension is installed, so that prefs get created. - void OnExtensionInstalled(Extension* extension, + void OnExtensionInstalled(const Extension* extension, Extension::State initial_state, bool initial_incognito_enabled); @@ -76,14 +76,14 @@ class ExtensionPrefs { Extension::State GetExtensionState(const std::string& extension_id); // Called to change the extension's state when it is enabled/disabled. - void SetExtensionState(Extension* extension, Extension::State); + void SetExtensionState(const Extension* extension, Extension::State); // Did the extension ask to escalate its permission during an upgrade? bool DidExtensionEscalatePermissions(const std::string& id); // If |did_escalate| is true, the preferences for |extension| will be set to // require the install warning when the user tries to enable. - void SetDidExtensionEscalatePermissions(Extension* extension, + void SetDidExtensionEscalatePermissions(const Extension* extension, bool did_escalate); // Returns the version string for the currently installed extension, or @@ -92,7 +92,7 @@ class ExtensionPrefs { // Re-writes the extension manifest into the prefs. // Called to change the extension's manifest when it's re-localized. - void UpdateManifest(Extension* extension); + void UpdateManifest(const Extension* extension); // Returns extension path based on extension ID, or empty FilePath on error. FilePath GetExtensionPath(const std::string& extension_id); diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc index 8275a99..c8d1967 100644 --- a/chrome/browser/extensions/extension_prefs_unittest.cc +++ b/chrome/browser/extensions/extension_prefs_unittest.cc @@ -114,7 +114,7 @@ TEST_F(ExtensionPrefsToolbarOrder, ToolbarOrder) {} class ExtensionPrefsExtensionState : public ExtensionPrefsTest { public: virtual void Initialize() { - extension.reset(prefs_.AddExtension("test")); + extension = prefs_.AddExtension("test"); prefs()->SetExtensionState(extension.get(), Extension::DISABLED); } @@ -123,7 +123,7 @@ class ExtensionPrefsExtensionState : public ExtensionPrefsTest { } private: - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; }; TEST_F(ExtensionPrefsExtensionState, ExtensionState) {} @@ -131,7 +131,7 @@ TEST_F(ExtensionPrefsExtensionState, ExtensionState) {} class ExtensionPrefsEscalatePermissions : public ExtensionPrefsTest { public: virtual void Initialize() { - extension.reset(prefs_.AddExtension("test")); + extension = prefs_.AddExtension("test"); prefs()->SetDidExtensionEscalatePermissions(extension.get(), true); } @@ -140,7 +140,7 @@ class ExtensionPrefsEscalatePermissions : public ExtensionPrefsTest { } private: - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; }; TEST_F(ExtensionPrefsEscalatePermissions, EscalatePermissions) {} @@ -149,7 +149,7 @@ TEST_F(ExtensionPrefsEscalatePermissions, EscalatePermissions) {} class ExtensionPrefsVersionString : public ExtensionPrefsTest { public: virtual void Initialize() { - extension.reset(prefs_.AddExtension("test")); + extension = prefs_.AddExtension("test"); EXPECT_EQ("0.1", prefs()->GetVersionString(extension->id())); prefs()->OnExtensionUninstalled(extension->id(), Extension::INTERNAL, false); @@ -160,7 +160,7 @@ class ExtensionPrefsVersionString : public ExtensionPrefsTest { } private: - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; }; TEST_F(ExtensionPrefsVersionString, VersionString) {} @@ -173,11 +173,11 @@ class ExtensionPrefsBlacklist : public ExtensionPrefsTest { // Install 5 extensions. for (int i = 0; i < 5; i++) { std::string name = "test" + base::IntToString(i); - extensions_.push_back(linked_ptr<Extension>(prefs_.AddExtension(name))); + extensions_.push_back(prefs_.AddExtension(name)); } EXPECT_EQ(NULL, prefs()->GetInstalledExtensionInfo(not_installed_id_)); - std::vector<linked_ptr<Extension> >::const_iterator iter; + ExtensionList::const_iterator iter; for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) { EXPECT_FALSE(prefs()->IsExtensionBlacklisted((*iter)->id())); } @@ -194,7 +194,7 @@ class ExtensionPrefsBlacklist : public ExtensionPrefsTest { EXPECT_TRUE(prefs()->IsExtensionBlacklisted(not_installed_id_)); // Make sure the other id's are not blacklisted. - std::vector<linked_ptr<Extension> >::const_iterator iter; + ExtensionList::const_iterator iter; for (iter = extensions_.begin() + 1; iter != extensions_.end(); ++iter) { EXPECT_FALSE(prefs()->IsExtensionBlacklisted((*iter)->id())); } @@ -212,7 +212,7 @@ class ExtensionPrefsBlacklist : public ExtensionPrefsTest { } private: - std::vector<linked_ptr<Extension> > extensions_; + ExtensionList extensions_; // An id we'll make up that doesn't match any installed extension id. std::string not_installed_id_; @@ -315,7 +315,7 @@ TEST_F(ExtensionPrefsIdleInstallInfo, IdleInstallInfo) {} class ExtensionPrefsOnExtensionInstalled : public ExtensionPrefsTest { public: virtual void Initialize() { - extension_.reset(prefs_.AddExtension("on_extension_installed")); + extension_ = prefs_.AddExtension("on_extension_installed"); EXPECT_EQ(Extension::ENABLED, prefs()->GetExtensionState(extension_->id())); EXPECT_FALSE(prefs()->IsIncognitoEnabled(extension_->id())); @@ -330,7 +330,7 @@ class ExtensionPrefsOnExtensionInstalled : public ExtensionPrefsTest { } private: - scoped_ptr<Extension> extension_; + scoped_refptr<Extension> extension_; }; TEST_F(ExtensionPrefsOnExtensionInstalled, ExtensionPrefsOnExtensionInstalled) {} @@ -341,7 +341,7 @@ public: // No extensions yet. EXPECT_EQ(0, prefs()->GetNextAppLaunchIndex()); - extension_.reset(prefs_.AddExtension("on_extension_installed")); + extension_ = prefs_.AddExtension("on_extension_installed"); EXPECT_EQ(Extension::ENABLED, prefs()->GetExtensionState(extension_->id())); prefs()->OnExtensionInstalled(extension_.get(), @@ -364,6 +364,6 @@ public: } private: - scoped_ptr<Extension> extension_; + scoped_refptr<Extension> extension_; }; TEST_F(ExtensionPrefsAppLaunchIndex, ExtensionPrefsAppLaunchIndex) {} diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc index 5a28617..c4e0799 100644 --- a/chrome/browser/extensions/extension_process_manager.cc +++ b/chrome/browser/extensions/extension_process_manager.cc @@ -32,11 +32,12 @@ class IncognitoExtensionProcessManager : public ExtensionProcessManager { public: explicit IncognitoExtensionProcessManager(Profile* profile); virtual ~IncognitoExtensionProcessManager() {} - virtual ExtensionHost* CreateView(Extension* extension, + virtual ExtensionHost* CreateView(const Extension* extension, const GURL& url, Browser* browser, ViewType::Type view_type); - virtual void CreateBackgroundHost(Extension* extension, const GURL& url); + virtual void CreateBackgroundHost(const Extension* extension, + const GURL& url); virtual SiteInstance* GetSiteInstanceForURL(const GURL& url); virtual RenderProcessHost* GetExtensionProcess(const GURL& url); @@ -48,7 +49,7 @@ class IncognitoExtensionProcessManager : public ExtensionProcessManager { // Returns the extension for an URL, which can either be a chrome-extension // URL or a web app URL. - Extension* GetExtensionOrAppByURL(const GURL& url); + const Extension* GetExtensionOrAppByURL(const GURL& url); // Returns true if the extension is allowed to run in incognito mode. bool IsIncognitoEnabled(const Extension* extension); @@ -57,7 +58,7 @@ class IncognitoExtensionProcessManager : public ExtensionProcessManager { }; static void CreateBackgroundHost( - ExtensionProcessManager* manager, Extension* extension) { + ExtensionProcessManager* manager, const Extension* extension) { // Start the process for the master page, if it exists. if (extension->background_url().is_valid()) manager->CreateBackgroundHost(extension, extension->background_url()); @@ -105,12 +106,12 @@ ExtensionProcessManager::ExtensionProcessManager(Profile* profile) } ExtensionProcessManager::~ExtensionProcessManager() { - LOG_IF(INFO, g_log_bug53991) << "~ExtensionProcessManager: " << this; + VLOG_IF(1, g_log_bug53991) << "~ExtensionProcessManager: " << this; CloseBackgroundHosts(); DCHECK(background_hosts_.empty()); } -ExtensionHost* ExtensionProcessManager::CreateView(Extension* extension, +ExtensionHost* ExtensionProcessManager::CreateView(const Extension* extension, const GURL& url, Browser* browser, ViewType::Type view_type) { @@ -137,14 +138,14 @@ ExtensionHost* ExtensionProcessManager::CreateView(const GURL& url, ExtensionsService* service = browsing_instance_->profile()->GetExtensionsService(); if (service) { - Extension* extension = service->GetExtensionByURL(url); + const Extension* extension = service->GetExtensionByURL(url); if (extension) return CreateView(extension, url, browser, view_type); } return NULL; } -ExtensionHost* ExtensionProcessManager::CreatePopup(Extension* extension, +ExtensionHost* ExtensionProcessManager::CreatePopup(const Extension* extension, const GURL& url, Browser* browser) { return CreateView(extension, url, browser, ViewType::EXTENSION_POPUP); @@ -155,9 +156,8 @@ ExtensionHost* ExtensionProcessManager::CreatePopup(const GURL& url, return CreateView(url, browser, ViewType::EXTENSION_POPUP); } -ExtensionHost* ExtensionProcessManager::CreateInfobar(Extension* extension, - const GURL& url, - Browser* browser) { +ExtensionHost* ExtensionProcessManager::CreateInfobar( + const Extension* extension, const GURL& url, Browser* browser) { return CreateView(extension, url, browser, ViewType::EXTENSION_INFOBAR); } @@ -167,7 +167,7 @@ ExtensionHost* ExtensionProcessManager::CreateInfobar(const GURL& url, } void ExtensionProcessManager::CreateBackgroundHost( - Extension* extension, const GURL& url) { + const Extension* extension, const GURL& url) { // Don't create multiple background hosts for an extension. if (GetBackgroundHostForExtension(extension)) return; @@ -185,7 +185,7 @@ void ExtensionProcessManager::CreateBackgroundHost( OnExtensionHostCreated(host, true); } -void ExtensionProcessManager::OpenOptionsPage(Extension* extension, +void ExtensionProcessManager::OpenOptionsPage(const Extension* extension, Browser* browser) { DCHECK(!extension->options_url().is_empty()); @@ -203,7 +203,7 @@ void ExtensionProcessManager::OpenOptionsPage(Extension* extension, } ExtensionHost* ExtensionProcessManager::GetBackgroundHostForExtension( - Extension* extension) { + const Extension* extension) { for (ExtensionHostSet::iterator iter = background_hosts_.begin(); iter != background_hosts_.end(); ++iter) { ExtensionHost* host = *iter; @@ -230,7 +230,7 @@ void ExtensionProcessManager::RegisterExtensionProcess( browsing_instance_->profile()->GetExtensionsService(); std::vector<std::string> page_action_ids; - Extension* extension = + const Extension* extension = extension_service->GetExtensionById(extension_id, false); if (extension->page_action()) page_action_ids.push_back(extension->page_action()->id()); @@ -254,8 +254,8 @@ RenderProcessHost* ExtensionProcessManager::GetExtensionProcess( const GURL& url) { if (!browsing_instance_->HasSiteInstance(url)) return NULL; - scoped_refptr<SiteInstance> site = - browsing_instance_->GetSiteInstanceForURL(url); + scoped_refptr<SiteInstance> site( + browsing_instance_->GetSiteInstanceForURL(url)); if (site->HasProcess()) return site->GetProcess(); return NULL; @@ -288,14 +288,14 @@ void ExtensionProcessManager::Observe(NotificationType type, ExtensionsService* service = Source<Profile>(source).ptr()->GetExtensionsService(); if (service->is_ready()) { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); ::CreateBackgroundHost(this, extension); } break; } case NotificationType::EXTENSION_UNLOADED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); for (ExtensionHostSet::iterator iter = background_hosts_.begin(); iter != background_hosts_.end(); ++iter) { ExtensionHost* host = *iter; @@ -350,7 +350,7 @@ void ExtensionProcessManager::OnExtensionHostCreated(ExtensionHost* host, } void ExtensionProcessManager::CloseBackgroundHosts() { - LOG_IF(INFO, g_log_bug53991) << "CloseBackgroundHosts: " << this; + VLOG_IF(1, g_log_bug53991) << "CloseBackgroundHosts: " << this; for (ExtensionHostSet::iterator iter = background_hosts_.begin(); iter != background_hosts_.end(); ) { ExtensionHostSet::iterator current = iter++; @@ -374,7 +374,7 @@ IncognitoExtensionProcessManager::IncognitoExtensionProcessManager( } ExtensionHost* IncognitoExtensionProcessManager::CreateView( - Extension* extension, + const Extension* extension, const GURL& url, Browser* browser, ViewType::Type view_type) { @@ -394,7 +394,7 @@ ExtensionHost* IncognitoExtensionProcessManager::CreateView( } void IncognitoExtensionProcessManager::CreateBackgroundHost( - Extension* extension, const GURL& url) { + const Extension* extension, const GURL& url) { if (extension->incognito_split_mode()) { if (IsIncognitoEnabled(extension)) ExtensionProcessManager::CreateBackgroundHost(extension, url); @@ -406,7 +406,7 @@ void IncognitoExtensionProcessManager::CreateBackgroundHost( SiteInstance* IncognitoExtensionProcessManager::GetSiteInstanceForURL( const GURL& url) { - Extension* extension = GetExtensionOrAppByURL(url); + const Extension* extension = GetExtensionOrAppByURL(url); if (!extension || extension->incognito_split_mode()) { return ExtensionProcessManager::GetSiteInstanceForURL(url); } else { @@ -416,7 +416,7 @@ SiteInstance* IncognitoExtensionProcessManager::GetSiteInstanceForURL( RenderProcessHost* IncognitoExtensionProcessManager::GetExtensionProcess( const GURL& url) { - Extension* extension = GetExtensionOrAppByURL(url); + const Extension* extension = GetExtensionOrAppByURL(url); if (!extension || extension->incognito_split_mode()) { return ExtensionProcessManager::GetExtensionProcess(url); } else { @@ -424,7 +424,7 @@ RenderProcessHost* IncognitoExtensionProcessManager::GetExtensionProcess( } } -Extension* IncognitoExtensionProcessManager::GetExtensionOrAppByURL( +const Extension* IncognitoExtensionProcessManager::GetExtensionOrAppByURL( const GURL& url) { ExtensionsService* service = browsing_instance_->profile()->GetExtensionsService(); diff --git a/chrome/browser/extensions/extension_process_manager.h b/chrome/browser/extensions/extension_process_manager.h index d284275..2d812ef 100644 --- a/chrome/browser/extensions/extension_process_manager.h +++ b/chrome/browser/extensions/extension_process_manager.h @@ -35,33 +35,34 @@ class ExtensionProcessManager : public NotificationObserver { // Creates a new ExtensionHost with its associated view, grouping it in the // appropriate SiteInstance (and therefore process) based on the URL and // profile. - virtual ExtensionHost* CreateView(Extension* extension, + virtual ExtensionHost* CreateView(const Extension* extension, const GURL& url, Browser* browser, ViewType::Type view_type); ExtensionHost* CreateView(const GURL& url, Browser* browser, ViewType::Type view_type); - ExtensionHost* CreatePopup(Extension* extension, + ExtensionHost* CreatePopup(const Extension* extension, const GURL& url, Browser* browser); ExtensionHost* CreatePopup(const GURL& url, Browser* browser); - ExtensionHost* CreateInfobar(Extension* extension, + ExtensionHost* CreateInfobar(const Extension* extension, const GURL& url, Browser* browser); ExtensionHost* CreateInfobar(const GURL& url, Browser* browser); // Open the extension's options page. - void OpenOptionsPage(Extension* extension, Browser* browser); + void OpenOptionsPage(const Extension* extension, Browser* browser); // Creates a new UI-less extension instance. Like CreateView, but not // displayed anywhere. - virtual void CreateBackgroundHost(Extension* extension, const GURL& url); + virtual void CreateBackgroundHost(const Extension* extension, + const GURL& url); // Gets the ExtensionHost for the background page for an extension, or NULL if // the extension isn't running or doesn't have a background page. - ExtensionHost* GetBackgroundHostForExtension(Extension* extension); + ExtensionHost* GetBackgroundHostForExtension(const Extension* extension); // Returns the SiteInstance that the given URL belongs to. virtual SiteInstance* GetSiteInstanceForURL(const GURL& url); diff --git a/chrome/browser/extensions/extension_protocols.cc b/chrome/browser/extensions/extension_protocols.cc index ce56589..90d198c 100644 --- a/chrome/browser/extensions/extension_protocols.cc +++ b/chrome/browser/extensions/extension_protocols.cc @@ -84,7 +84,8 @@ bool AllowExtensionResourceLoad(URLRequest* request, // chrome:// URLs are always allowed to load chrome-extension:// resources. // The app launcher in the NTP uses this feature, as does dev tools. - if (origin_url.SchemeIs(chrome::kChromeUIScheme)) + if (origin_url.SchemeIs(chrome::kChromeDevToolsScheme) || + origin_url.SchemeIs(chrome::kChromeUIScheme)) return true; // Disallow loading of packaged resources for hosted apps. We don't allow diff --git a/chrome/browser/extensions/extension_proxy_apitest.cc b/chrome/browser/extensions/extension_proxy_apitest.cc index 9c127ad..a22baf8 100644 --- a/chrome/browser/extensions/extension_proxy_apitest.cc +++ b/chrome/browser/extensions/extension_proxy_apitest.cc @@ -16,7 +16,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ProxyAutoSettings) { switches::kEnableExperimentalExtensionApis); ASSERT_TRUE(RunExtensionTest("proxy/auto")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension); PrefService* pref_service = browser()->profile()->GetPrefs(); @@ -46,7 +46,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ProxyManualSingle) { switches::kEnableExperimentalExtensionApis); ASSERT_TRUE(RunExtensionTest("proxy/single")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension); PrefService* pref_service = browser()->profile()->GetPrefs(); @@ -81,7 +81,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ProxyManualIndividual) { switches::kEnableExperimentalExtensionApis); ASSERT_TRUE(RunExtensionTest("proxy/individual")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension); PrefService* pref_service = browser()->profile()->GetPrefs(); diff --git a/chrome/browser/extensions/extension_rlz_module.cc b/chrome/browser/extensions/extension_rlz_module.cc index e31fc69..4f76bcc 100644 --- a/chrome/browser/extensions/extension_rlz_module.cc +++ b/chrome/browser/extensions/extension_rlz_module.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/extension_rlz_module.h" #include "base/scoped_ptr.h" +#include "base/thread_restrictions.h" #include "base/values.h" #include "chrome/common/extensions/extension.h" #include "rlz/win/lib/lib_values.h" @@ -70,6 +71,11 @@ bool GetEventFromName(const std::string& event_name, } // namespace bool RlzRecordProductEventFunction::RunImpl() { + // This can be slow if registry access goes to disk. Should preferably + // perform registry operations on the File thread. + // http://code.google.com/p/chromium/issues/detail?id=62098 + base::ThreadRestrictions::ScopedAllowIO allow_io; + std::string product_name; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &product_name)); rlz_lib::Product product; @@ -90,6 +96,11 @@ bool RlzRecordProductEventFunction::RunImpl() { } bool RlzGetAccessPointRlzFunction::RunImpl() { + // This can be slow if registry access goes to disk. Should preferably + // perform registry operations on the File thread. + // http://code.google.com/p/chromium/issues/detail?id=62098 + base::ThreadRestrictions::ScopedAllowIO allow_io; + std::string ap_name; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &ap_name)); rlz_lib::AccessPoint access_point; @@ -103,6 +114,11 @@ bool RlzGetAccessPointRlzFunction::RunImpl() { } bool RlzSendFinancialPingFunction::RunImpl() { + // This can be slow if registry access goes to disk. Should preferably + // perform registry operations on the File thread. + // http://code.google.com/p/chromium/issues/detail?id=62098 + base::ThreadRestrictions::ScopedAllowIO allow_io; + std::string product_name; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &product_name)); rlz_lib::Product product; @@ -159,6 +175,11 @@ bool RlzSendFinancialPingFunction::RunImpl() { } bool RlzClearProductStateFunction::RunImpl() { + // This can be slow if registry access goes to disk. Should preferably + // perform registry operations on the File thread. + // http://code.google.com/p/chromium/issues/detail?id=62098 + base::ThreadRestrictions::ScopedAllowIO allow_io; + std::string product_name; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &product_name)); rlz_lib::Product product; diff --git a/chrome/browser/extensions/extension_sidebar_api.cc b/chrome/browser/extensions/extension_sidebar_api.cc index 8b6913e..7dbb6fd 100644 --- a/chrome/browser/extensions/extension_sidebar_api.cc +++ b/chrome/browser/extensions/extension_sidebar_api.cc @@ -51,7 +51,7 @@ const char kShownState[] = "shown"; } static GURL ResolvePossiblyRelativeURL(const std::string& url_string, - Extension* extension) { + const Extension* extension) { GURL url = GURL(url_string); if (!url.is_valid()) url = extension->GetResourceURL(url_string); @@ -59,7 +59,7 @@ static GURL ResolvePossiblyRelativeURL(const std::string& url_string, return url; } -static bool CanUseHost(Extension* extension, +static bool CanUseHost(const Extension* extension, const GURL& url, std::string* error) { if (extension->HasHostPermission(url)) diff --git a/chrome/browser/extensions/extension_tabs_apitest.cc b/chrome/browser/extensions/extension_tabs_apitest.cc index df108f3..b9aba66 100644 --- a/chrome/browser/extensions/extension_tabs_apitest.cc +++ b/chrome/browser/extensions/extension_tabs_apitest.cc @@ -9,21 +9,20 @@ #include "chrome/browser/profile.h" #include "chrome/common/pref_names.h" -#if defined(OS_WIN) -// This test times out on win. -// http://crbug.com/58269 -#define MAYBE_Tabs FAILS_Tabs -#else -#define MAYBE_Tabs Tabs -#endif - // Possible race in ChromeURLDataManager. http://crbug.com/59198 #if defined(OS_MACOSX) || defined(OS_LINUX) -#define MAYBE_TabOnRemoved FLAKY_TabOnRemoved +#define MAYBE_TabOnRemoved DISABLED_TabOnRemoved #else #define MAYBE_TabOnRemoved TabOnRemoved #endif +// Crashes on linux views. http://crbug.com/61592 +#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) +#define MAYBE_Tabs DISABLED_Tabs +#else +#define MAYBE_Tabs Tabs +#endif + IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tabs) { ASSERT_TRUE(test_server()->Start()); @@ -33,7 +32,28 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tabs) { browser()->profile()->GetPrefs()->SetBoolean( prefs::kHomePageIsNewTabPage, true); - ASSERT_TRUE(RunExtensionTest("tabs/basics")) << message_; + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "crud.html")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabPinned) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "pinned.html")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabMove) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "move.html")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabEvents) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "events.html")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabRelativeURLs) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "relative_urls.html")) + << message_; } IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabGetCurrent) { diff --git a/chrome/browser/extensions/extension_tabs_module.cc b/chrome/browser/extensions/extension_tabs_module.cc index 8da37de..8157f52 100644 --- a/chrome/browser/extensions/extension_tabs_module.cc +++ b/chrome/browser/extensions/extension_tabs_module.cc @@ -11,6 +11,7 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/extensions/extension_function_dispatcher.h" #include "chrome/browser/extensions/extension_host.h" @@ -65,7 +66,7 @@ static bool GetTabById(int tab_id, Profile* profile, // but because the api shipped with urls resolved relative to their extension // base, we decided it wasn't worth breaking existing extensions to fix. static GURL ResolvePossiblyRelativeURL(std::string url_string, - Extension* extension); + const Extension* extension); // Return the type name for a browser window type. static std::string GetWindowTypeText(Browser::Type type); @@ -89,14 +90,10 @@ int ExtensionTabUtil::GetWindowIdOfTab(const TabContents* tab_contents) { DictionaryValue* ExtensionTabUtil::CreateTabValue( const TabContents* contents) { // Find the tab strip and index of this guy. - for (BrowserList::const_iterator it = BrowserList::begin(); - it != BrowserList::end(); ++it) { - TabStripModel* tab_strip = (*it)->tabstrip_model(); - int tab_index = tab_strip->GetIndexOfTabContents(contents); - if (tab_index != -1) { - return ExtensionTabUtil::CreateTabValue(contents, tab_strip, tab_index); - } - } + TabStripModel* tab_strip = NULL; + int tab_index; + if (ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index)) + return ExtensionTabUtil::CreateTabValue(contents, tab_strip, tab_index); // Couldn't find it. This can happen if the tab is being dragged. return ExtensionTabUtil::CreateTabValue(contents, NULL, -1); @@ -124,6 +121,8 @@ DictionaryValue* ExtensionTabUtil::CreateTabValue( result->SetString(keys::kStatusKey, GetTabStatusText(contents->is_loading())); result->SetBoolean(keys::kSelectedKey, tab_strip && tab_index == tab_strip->selected_index()); + result->SetBoolean(keys::kPinnedKey, + tab_strip && tab_strip->IsTabPinned(tab_index)); result->SetString(keys::kTitleKey, contents->GetTitle()); result->SetBoolean(keys::kIncognitoKey, contents->profile()->IsOffTheRecord()); @@ -165,6 +164,27 @@ DictionaryValue* ExtensionTabUtil::CreateWindowValue(const Browser* browser, return result; } +bool ExtensionTabUtil::GetTabStripModel(const TabContents* tab_contents, + TabStripModel** tab_strip_model, + int* tab_index) { + DCHECK(tab_contents); + DCHECK(tab_strip_model); + DCHECK(tab_index); + + for (BrowserList::const_iterator it = BrowserList::begin(); + it != BrowserList::end(); ++it) { + TabStripModel* tab_strip = (*it)->tabstrip_model(); + int index = tab_strip->GetIndexOfTabContents(tab_contents); + if (index != -1) { + *tab_strip_model = tab_strip; + *tab_index = index; + return true; + } + } + + return false; +} + bool ExtensionTabUtil::GetDefaultTab(Browser* browser, TabContents** contents, int* tab_id) { DCHECK(browser); @@ -406,11 +426,8 @@ bool CreateWindowFunction::RunImpl() { } Browser* new_window = Browser::CreateForType(window_type, window_profile); - for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) { - Browser::AddTabWithURLParams addTabParams = - Browser::AddTabWithURLParams(*i, PageTransition::LINK); - new_window->AddTabWithURL(&addTabParams); - } + for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) + new_window->AddSelectedTabWithURL(*i, PageTransition::LINK); if (urls.size() == 0) new_window->NewTab(); new_window->SelectNumberedTab(0); @@ -600,9 +617,16 @@ bool CreateTabFunction::RunImpl() { EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kSelectedKey, &selected)); - // We can't load extension URLs into incognito windows. Special case to - // fall back to a normal window. + // Default to not pinning the tab. Setting the 'pinned' property to true + // will override this default. + bool pinned = false; + if (args->HasKey(keys::kPinnedKey)) + EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kPinnedKey, &pinned)); + + // We can't load extension URLs into incognito windows unless the extension + // uses split mode. Special case to fall back to a normal window. if (url.SchemeIs(chrome::kExtensionScheme) && + !GetExtension()->incognito_split_mode() && browser->profile()->IsOffTheRecord()) { Profile* profile = browser->profile()->GetOriginalProfile(); browser = BrowserList::FindBrowserWithType(profile, @@ -626,20 +650,25 @@ bool CreateTabFunction::RunImpl() { int add_types = selected ? TabStripModel::ADD_SELECTED : TabStripModel::ADD_NONE; add_types |= TabStripModel::ADD_FORCE_INDEX; - Browser::AddTabWithURLParams params(url, PageTransition::LINK); - params.index = index; - params.add_types = add_types; - TabContents* contents = browser->AddTabWithURL(¶ms); - index = browser->tabstrip_model()->GetIndexOfTabContents(contents); + if (pinned) + add_types |= TabStripModel::ADD_PINNED; + browser::NavigateParams params(browser, url, PageTransition::LINK); + params.disposition = selected ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; + params.tabstrip_index = index; + params.tabstrip_add_types = add_types; + browser::Navigate(¶ms); if (selected) - contents->view()->SetInitialFocus(); + params.target_contents->view()->SetInitialFocus(); // Return data about the newly created tab. - if (has_callback()) - result_.reset(ExtensionTabUtil::CreateTabValue(contents, - browser->tabstrip_model(), - index)); + if (has_callback()) { + result_.reset(ExtensionTabUtil::CreateTabValue( + params.target_contents, + params.browser->tabstrip_model(), + params.browser->tabstrip_model()->GetIndexOfTabContents( + params.target_contents))); + } return true; } @@ -705,7 +734,7 @@ bool UpdateTabFunction::RunImpl() { // JavaScript URLs can do the same kinds of things as cross-origin XHR, so // we need to check host permissions before allowing them. if (url.SchemeIs(chrome::kJavaScriptScheme)) { - Extension* extension = GetExtension(); + const Extension* extension = GetExtension(); const std::vector<URLPattern> host_permissions = extension->host_permissions(); if (!Extension::CanExecuteScriptOnPage( @@ -722,12 +751,6 @@ bool UpdateTabFunction::RunImpl() { // controller->GetURL()? } - if (tab_strip->IsTabPinned(tab_index)) { - // Don't allow changing the url of pinned tabs. - error_ = keys::kCannotUpdatePinnedTab; - return false; - } - controller.LoadURL(url, GURL(), PageTransition::LINK); // The URL of a tab contents never actually changes to a JavaScript URL, so @@ -752,6 +775,16 @@ bool UpdateTabFunction::RunImpl() { } } + bool pinned = false; + if (update_props->HasKey(keys::kPinnedKey)) { + EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(keys::kPinnedKey, + &pinned)); + tab_strip->SetTabPinned(tab_index, pinned); + + // Update the tab index because it may move when being pinned. + tab_index = tab_strip->GetIndexOfTabContents(contents); + } + if (has_callback()) result_.reset(ExtensionTabUtil::CreateTabValue(contents, tab_strip, tab_index)); @@ -942,7 +975,7 @@ bool CaptureVisibleTabFunction::CaptureSnapshotFromBackingStore( &temp_canvas)) { return false; } - LOG(INFO) << "captureVisibleTab() Got image from backing store."; + VLOG(1) << "captureVisibleTab() got image from backing store."; SendResultFromBitmap( temp_canvas.getTopPlatformDevice().accessBitmap(false)); @@ -964,7 +997,7 @@ void CaptureVisibleTabFunction::Observe(NotificationType type, error_ = keys::kInternalVisibleTabCaptureError; SendResponse(false); } else { - LOG(INFO) << "captureVisibleTab() Got image from renderer."; + VLOG(1) << "captureVisibleTab() got image from renderer."; SendResultFromBitmap(*screen_capture); } @@ -1149,7 +1182,7 @@ static std::string GetWindowTypeText(Browser::Type type) { } static GURL ResolvePossiblyRelativeURL(std::string url_string, - Extension* extension) { + const Extension* extension) { GURL url = GURL(url_string); if (!url.is_valid()) url = extension->GetResourceURL(url_string); diff --git a/chrome/browser/extensions/extension_tabs_module.h b/chrome/browser/extensions/extension_tabs_module.h index cee8a0d..9bf36cb 100644 --- a/chrome/browser/extensions/extension_tabs_module.h +++ b/chrome/browser/extensions/extension_tabs_module.h @@ -34,7 +34,10 @@ class ExtensionTabUtil { int tab_index); static DictionaryValue* CreateWindowValue(const Browser* browser, bool populate_tabs); - + // Gets the |tab_strip_model| and |tab_index| for the given |tab_contents|. + static bool GetTabStripModel(const TabContents* tab_contents, + TabStripModel** tab_strip_model, + int* tab_index); static bool GetDefaultTab(Browser* browser, TabContents** contents, int* tab_id); // Any out parameter (|browser|, |tab_strip|, |contents|, & |tab_index|) may diff --git a/chrome/browser/extensions/extension_tabs_module_constants.cc b/chrome/browser/extensions/extension_tabs_module_constants.cc index 9dc19b8..6405783 100644 --- a/chrome/browser/extensions/extension_tabs_module_constants.cc +++ b/chrome/browser/extensions/extension_tabs_module_constants.cc @@ -22,6 +22,7 @@ const char kNewPositionKey[] = "newPosition"; const char kNewWindowIdKey[] = "newWindowId"; const char kOldPositionKey[] = "oldPosition"; const char kOldWindowIdKey[] = "oldWindowId"; +const char kPinnedKey[] = "pinned"; const char kPopulateKey[] = "populate"; const char kQualityKey[] = "quality"; const char kSelectedKey[] = "selected"; @@ -68,7 +69,6 @@ const char kNoCodeOrFileToExecuteError[] = "No source code or file specified."; const char kMoreThanOneValuesError[] = "Code and file should not be specified " "at the same time in the second argument."; const char kLoadFileError[] = "Failed to load file: \"*\". "; -const char kCannotUpdatePinnedTab[] = "Cannot update pinned tabs"; const char kCannotDetermineLanguageOfUnloadedTab[] = "Cannot determine language: tab not loaded"; diff --git a/chrome/browser/extensions/extension_tabs_module_constants.h b/chrome/browser/extensions/extension_tabs_module_constants.h index 7ae35b7..89c1cd3 100644 --- a/chrome/browser/extensions/extension_tabs_module_constants.h +++ b/chrome/browser/extensions/extension_tabs_module_constants.h @@ -26,6 +26,7 @@ extern const char kNewPositionKey[]; extern const char kNewWindowIdKey[]; extern const char kOldPositionKey[]; extern const char kOldWindowIdKey[]; +extern const char kPinnedKey[]; extern const char kPopulateKey[]; extern const char kQualityKey[]; extern const char kSelectedKey[]; @@ -69,7 +70,6 @@ extern const char kSupportedInWindowsOnlyError[]; extern const char kNoCodeOrFileToExecuteError[]; extern const char kMoreThanOneValuesError[]; extern const char kLoadFileError[]; -extern const char kCannotUpdatePinnedTab[]; extern const char kCannotDetermineLanguageOfUnloadedTab[]; }; // namespace extension_tabs_module_constants diff --git a/chrome/browser/extensions/extension_test_message_listener.cc b/chrome/browser/extensions/extension_test_message_listener.cc index 423bf3b..8ba0683 100644 --- a/chrome/browser/extensions/extension_test_message_listener.cc +++ b/chrome/browser/extensions/extension_test_message_listener.cc @@ -43,8 +43,8 @@ void ExtensionTestMessageListener::Observe( const NotificationSource& source, const NotificationDetails& details) { const std::string& content = *Details<std::string>(details).ptr(); - function_ = Source<ExtensionTestSendMessageFunction>(source).ptr(); if (!satisfied_ && content == expected_message_) { + function_ = Source<ExtensionTestSendMessageFunction>(source).ptr(); satisfied_ = true; registrar_.RemoveAll(); // Stop listening for more messages. if (!will_reply_) { diff --git a/chrome/browser/extensions/extension_test_message_listener.h b/chrome/browser/extensions/extension_test_message_listener.h index bead446..86c4cf2 100644 --- a/chrome/browser/extensions/extension_test_message_listener.h +++ b/chrome/browser/extensions/extension_test_message_listener.h @@ -63,6 +63,8 @@ class ExtensionTestMessageListener : public NotificationObserver { const NotificationSource& source, const NotificationDetails& details); + bool was_satisfied() const { return satisfied_; } + private: NotificationRegistrar registrar_; diff --git a/chrome/browser/extensions/extension_toolbar_model.cc b/chrome/browser/extensions/extension_toolbar_model.cc index 086b8c4..9b36825 100644 --- a/chrome/browser/extensions/extension_toolbar_model.cc +++ b/chrome/browser/extensions/extension_toolbar_model.cc @@ -45,7 +45,7 @@ void ExtensionToolbarModel::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } -void ExtensionToolbarModel::MoveBrowserAction(Extension* extension, +void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension, int index) { ExtensionList::iterator pos = std::find(begin(), end(), extension); if (pos == end()) { @@ -58,7 +58,7 @@ void ExtensionToolbarModel::MoveBrowserAction(Extension* extension, bool inserted = false; for (ExtensionList::iterator iter = begin(); iter != end(); ++iter, ++i) { if (i == index) { - toolitems_.insert(iter, extension); + toolitems_.insert(iter, make_scoped_refptr(extension)); inserted = true; break; } @@ -68,7 +68,7 @@ void ExtensionToolbarModel::MoveBrowserAction(Extension* extension, DCHECK_EQ(index, static_cast<int>(toolitems_.size())); index = toolitems_.size(); - toolitems_.push_back(extension); + toolitems_.push_back(make_scoped_refptr(extension)); } FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index)); @@ -93,7 +93,7 @@ void ExtensionToolbarModel::Observe(NotificationType type, if (!service_->is_ready()) return; - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (type == NotificationType::EXTENSION_LOADED) { AddExtension(extension); } else if (type == NotificationType::EXTENSION_UNLOADED || @@ -104,18 +104,19 @@ void ExtensionToolbarModel::Observe(NotificationType type, } } -void ExtensionToolbarModel::AddExtension(Extension* extension) { +void ExtensionToolbarModel::AddExtension(const Extension* extension) { // We only care about extensions with browser actions. if (!extension->browser_action()) return; if (extension->id() == last_extension_removed_ && last_extension_removed_index_ < toolitems_.size()) { - toolitems_.insert(begin() + last_extension_removed_index_, extension); + toolitems_.insert(begin() + last_extension_removed_index_, + make_scoped_refptr(extension)); FOR_EACH_OBSERVER(Observer, observers_, BrowserActionAdded(extension, last_extension_removed_index_)); } else { - toolitems_.push_back(extension); + toolitems_.push_back(make_scoped_refptr(extension)); FOR_EACH_OBSERVER(Observer, observers_, BrowserActionAdded(extension, toolitems_.size() - 1)); } @@ -126,7 +127,7 @@ void ExtensionToolbarModel::AddExtension(Extension* extension) { UpdatePrefs(); } -void ExtensionToolbarModel::RemoveExtension(Extension* extension) { +void ExtensionToolbarModel::RemoveExtension(const Extension* extension) { ExtensionList::iterator pos = std::find(begin(), end(), extension); if (pos == end()) { return; @@ -162,7 +163,7 @@ void ExtensionToolbarModel::InitializeExtensionList() { // Create the lists. for (size_t i = 0; i < service_->extensions()->size(); ++i) { - Extension* extension = service_->extensions()->at(i); + const Extension* extension = service_->extensions()->at(i); if (!extension->browser_action()) continue; @@ -172,7 +173,7 @@ void ExtensionToolbarModel::InitializeExtensionList() { int index = std::distance(pref_order.begin(), pos); sorted[index] = extension; } else { - unsorted.push_back(extension); + unsorted.push_back(make_scoped_refptr(extension)); } } @@ -208,7 +209,7 @@ void ExtensionToolbarModel::UpdatePrefs() { service_->extension_prefs()->SetToolbarOrder(ids); } -Extension* ExtensionToolbarModel::GetExtensionByIndex(int index) const { +const Extension* ExtensionToolbarModel::GetExtensionByIndex(int index) const { return toolitems_.at(index); } diff --git a/chrome/browser/extensions/extension_toolbar_model.h b/chrome/browser/extensions/extension_toolbar_model.h index a38fc8c..37c3a08 100644 --- a/chrome/browser/extensions/extension_toolbar_model.h +++ b/chrome/browser/extensions/extension_toolbar_model.h @@ -30,13 +30,13 @@ class ExtensionToolbarModel : public NotificationObserver { public: // An extension with a browser action button has been added, and should go // in the toolbar at |index|. - virtual void BrowserActionAdded(Extension* extension, int index) {} + virtual void BrowserActionAdded(const Extension* extension, int index) {} // The browser action button for |extension| should no longer show. - virtual void BrowserActionRemoved(Extension* extension) {} + virtual void BrowserActionRemoved(const Extension* extension) {} // The browser action button for |extension| has been moved to |index|. - virtual void BrowserActionMoved(Extension* extension, int index) {} + virtual void BrowserActionMoved(const Extension* extension, int index) {} // Called when the model has finished loading. virtual void ModelLoaded() {} @@ -48,7 +48,7 @@ class ExtensionToolbarModel : public NotificationObserver { // Functions called by the view. void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); - void MoveBrowserAction(Extension* extension, int index); + void MoveBrowserAction(const Extension* extension, int index); // If count == size(), this will set the visible icon count to -1, meaning // "show all actions". void SetVisibleIconCount(int count); @@ -69,7 +69,7 @@ class ExtensionToolbarModel : public NotificationObserver { return toolitems_.end(); } - Extension* GetExtensionByIndex(int index) const; + const Extension* GetExtensionByIndex(int index) const; // Utility functions for converting between an index into the list of // incognito-enabled browser actions, and the list of all browser actions. @@ -93,8 +93,8 @@ class ExtensionToolbarModel : public NotificationObserver { // Our observers. ObserverList<Observer> observers_; - void AddExtension(Extension* extension); - void RemoveExtension(Extension* extension); + void AddExtension(const Extension* extension); + void RemoveExtension(const Extension* extension); // Our ExtensionsService, guaranteed to outlive us. ExtensionsService* service_; diff --git a/chrome/browser/extensions/extension_toolbar_model_browsertest.cc b/chrome/browser/extensions/extension_toolbar_model_browsertest.cc index 031672e..45c936e 100644 --- a/chrome/browser/extensions/extension_toolbar_model_browsertest.cc +++ b/chrome/browser/extensions/extension_toolbar_model_browsertest.cc @@ -37,19 +37,19 @@ class ExtensionToolbarModelTest : public ExtensionBrowserTest, model_->RemoveObserver(this); } - virtual void BrowserActionAdded(Extension* extension, int index) { + virtual void BrowserActionAdded(const Extension* extension, int index) { inserted_count_++; } - virtual void BrowserActionRemoved(Extension* extension) { + virtual void BrowserActionRemoved(const Extension* extension) { removed_count_++; } - virtual void BrowserActionMoved(Extension* extension, int index) { + virtual void BrowserActionMoved(const Extension* extension, int index) { moved_count_++; } - Extension* ExtensionAt(int index) { + const Extension* ExtensionAt(int index) { for (ExtensionList::iterator i = model_->begin(); i < model_->end(); ++i) { if (index-- == 0) return *i; @@ -87,7 +87,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, Basic) { // We should now find our extension in the model. EXPECT_EQ(1, inserted_count_); EXPECT_EQ(1u, model_->size()); - Extension* extension = ExtensionAt(0); + const Extension* extension = ExtensionAt(0); ASSERT_TRUE(NULL != extension); EXPECT_STREQ("A browser action with no icon that makes the page red", extension->name().c_str()); @@ -96,7 +96,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, Basic) { model_->MoveBrowserAction(extension, 0); EXPECT_EQ(1, moved_count_); EXPECT_EQ(1u, model_->size()); - Extension* extension2 = ExtensionAt(0); + const Extension* extension2 = ExtensionAt(0); EXPECT_EQ(extension, extension2); UnloadExtension(extension->id()); @@ -118,7 +118,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, ReorderAndReinsert) { // First extension loaded. EXPECT_EQ(1, inserted_count_); EXPECT_EQ(1u, model_->size()); - Extension* extensionA = ExtensionAt(0); + const Extension* extensionA = ExtensionAt(0); ASSERT_TRUE(NULL != extensionA); EXPECT_STREQ("A browser action with no icon that makes the page red", extensionA->name().c_str()); @@ -132,7 +132,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, ReorderAndReinsert) { // Second extension loaded. EXPECT_EQ(2, inserted_count_); EXPECT_EQ(2u, model_->size()); - Extension* extensionB = ExtensionAt(1); + const Extension* extensionB = ExtensionAt(1); ASSERT_TRUE(NULL != extensionB); EXPECT_STREQ("Popup tester", extensionB->name().c_str()); @@ -145,7 +145,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, ReorderAndReinsert) { // Third extension loaded. EXPECT_EQ(3, inserted_count_); EXPECT_EQ(3u, model_->size()); - Extension* extensionC = ExtensionAt(2); + const Extension* extensionC = ExtensionAt(2); ASSERT_TRUE(NULL != extensionC); EXPECT_STREQ("A page action which removes a popup.", extensionC->name().c_str()); diff --git a/chrome/browser/extensions/extension_tts_api.cc b/chrome/browser/extensions/extension_tts_api.cc index b022ec0..1654b07 100644 --- a/chrome/browser/extensions/extension_tts_api.cc +++ b/chrome/browser/extensions/extension_tts_api.cc @@ -7,68 +7,206 @@ #include <string> #include "base/float_util.h" +#include "base/message_loop.h" #include "base/values.h" namespace util = extension_tts_api_util; namespace { - const char kCrosLibraryNotLoadedError[] = - "Cros shared library not loaded."; +const char kCrosLibraryNotLoadedError[] = + "Cros shared library not loaded."; +const int kSpeechCheckDelayIntervalMs = 100; }; +// static +ExtensionTtsController* ExtensionTtsController::GetInstance() { + return Singleton<ExtensionTtsController>::get(); +} + +ExtensionTtsController::ExtensionTtsController() + : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), + current_utterance_(NULL), + platform_impl_(NULL) { +} + +void ExtensionTtsController::SpeakOrEnqueue( + Utterance* utterance, bool can_enqueue) { + if (IsSpeaking() && can_enqueue) { + utterance_queue_.push(utterance); + } else { + Stop(); + SpeakNow(utterance); + } +} + +void ExtensionTtsController::SpeakNow(Utterance* utterance) { + GetPlatformImpl()->clear_error(); + bool success = GetPlatformImpl()->Speak( + utterance->text, + utterance->language, + utterance->gender, + utterance->rate, + utterance->pitch, + utterance->volume); + if (!success) { + utterance->error = GetPlatformImpl()->error(); + utterance->failure_task->Run(); + delete utterance->success_task; + delete utterance; + return; + } + current_utterance_ = utterance; + + // Post a task to check if this utterance has completed after a delay. + MessageLoop::current()->PostDelayedTask( + FROM_HERE, method_factory_.NewRunnableMethod( + &ExtensionTtsController::CheckSpeechStatus), + kSpeechCheckDelayIntervalMs); +} + +void ExtensionTtsController::Stop() { + GetPlatformImpl()->clear_error(); + GetPlatformImpl()->StopSpeaking(); + + FinishCurrentUtterance(); + ClearUtteranceQueue(); +} + +bool ExtensionTtsController::IsSpeaking() const { + return current_utterance_ != NULL; +} + +void ExtensionTtsController::FinishCurrentUtterance() { + if (current_utterance_) { + current_utterance_->success_task->Run(); + delete current_utterance_->failure_task; + delete current_utterance_; + current_utterance_ = NULL; + } +} + +void ExtensionTtsController::ClearUtteranceQueue() { + while (!utterance_queue_.empty()) { + Utterance* utterance = utterance_queue_.front(); + utterance_queue_.pop(); + utterance->success_task->Run(); + delete utterance->failure_task; + delete utterance; + } +} + +void ExtensionTtsController::CheckSpeechStatus() { + if (!current_utterance_) + return; + + if (GetPlatformImpl()->IsSpeaking() == false) { + FinishCurrentUtterance(); + + // Start speaking the next utterance in the queue. Keep trying in case + // one fails but there are still more in the queue to try. + while (!utterance_queue_.empty() && !current_utterance_) { + Utterance* utterance = utterance_queue_.front(); + utterance_queue_.pop(); + SpeakNow(utterance); + } + } + + // If we're still speaking something (either the prevoius utterance or + // a new utterance), keep calling this method after another delay. + if (current_utterance_) { + MessageLoop::current()->PostDelayedTask( + FROM_HERE, method_factory_.NewRunnableMethod( + &ExtensionTtsController::CheckSpeechStatus), + kSpeechCheckDelayIntervalMs); + } +} + +void ExtensionTtsController::SetPlatformImpl( + ExtensionTtsPlatformImpl* platform_impl) { + platform_impl_ = platform_impl; +} + +ExtensionTtsPlatformImpl* ExtensionTtsController::GetPlatformImpl() { + if (!platform_impl_) + platform_impl_ = ExtensionTtsPlatformImpl::GetInstance(); + return platform_impl_; +} + +// +// Extension API functions +// + bool ExtensionTtsSpeakFunction::RunImpl() { - std::string utterance; - std::string language; - std::string gender; - double rate = -1.0; - double pitch = -1.0; - double volume = -1.0; + utterance_ = new ExtensionTtsController::Utterance(); + bool can_enqueue = false; DictionaryValue* speak_options = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &utterance)); + EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &utterance_->text)); if (args_->GetDictionary(1, &speak_options)) { if (speak_options->HasKey(util::kLanguageNameKey)) { - speak_options->GetString(util::kLanguageNameKey, &language); + speak_options->GetString(util::kLanguageNameKey, &utterance_->language); } if (speak_options->HasKey(util::kGenderKey)) { - speak_options->GetString(util::kGenderKey, &gender); + speak_options->GetString(util::kGenderKey, &utterance_->gender); + } + + if (speak_options->HasKey(util::kEnqueueKey)) { + speak_options->GetBoolean(util::kEnqueueKey, &can_enqueue); } - if (util::ReadNumberByKey(speak_options, util::kRateKey, &rate)) { - if (!base::IsFinite(rate) || rate < 0.0 || rate > 1.0) { - rate = -1.0; + if (util::ReadNumberByKey( + speak_options, util::kRateKey, &utterance_->rate)) { + if (!base::IsFinite(utterance_->rate) || + utterance_->rate < 0.0 || + utterance_->rate > 1.0) { + utterance_->rate = -1.0; } } - if (util::ReadNumberByKey(speak_options, util::kPitchKey, &pitch)) { - if (!base::IsFinite(pitch) || pitch < 0.0 || pitch > 1.0) { - pitch = -1.0; + if (util::ReadNumberByKey( + speak_options, util::kPitchKey, &utterance_->pitch)) { + if (!base::IsFinite(utterance_->pitch) || + utterance_->pitch < 0.0 || + utterance_->pitch > 1.0) { + utterance_->pitch = -1.0; } } - if (util::ReadNumberByKey(speak_options, util::kVolumeKey, &volume)) { - if (!base::IsFinite(volume) || volume < 0.0 || volume > 1.0) { - volume = -1.0; + if (util::ReadNumberByKey( + speak_options, util::kVolumeKey, &utterance_->volume)) { + if (!base::IsFinite(utterance_->volume) || + utterance_->volume < 0.0 || + utterance_->volume > 1.0) { + utterance_->volume = -1.0; } } } - ExtensionTtsPlatformImpl* impl = ExtensionTtsPlatformImpl::GetInstance(); - impl->clear_error(); - return impl->Speak(utterance, language, gender, rate, pitch, volume); + AddRef(); // Balanced in SpeechFinished(). + utterance_->success_task = NewRunnableMethod( + this, &ExtensionTtsSpeakFunction::SpeechFinished, true); + utterance_->failure_task = NewRunnableMethod( + this, &ExtensionTtsSpeakFunction::SpeechFinished, false); + ExtensionTtsController::GetInstance()->SpeakOrEnqueue( + utterance_, can_enqueue); + return true; +} + +void ExtensionTtsSpeakFunction::SpeechFinished(bool success) { + error_ = utterance_->error; + SendResponse(success); + Release(); // Balanced in Speak(). } bool ExtensionTtsStopSpeakingFunction::RunImpl() { - ExtensionTtsPlatformImpl* impl = ExtensionTtsPlatformImpl::GetInstance(); - impl->clear_error(); - return impl->StopSpeaking(); + ExtensionTtsController::GetInstance()->Stop(); + return true; } bool ExtensionTtsIsSpeakingFunction::RunImpl() { - ExtensionTtsPlatformImpl* impl = ExtensionTtsPlatformImpl::GetInstance(); - impl->clear_error(); - result_.reset(Value::CreateBooleanValue(impl->IsSpeaking())); + result_.reset(Value::CreateBooleanValue( + ExtensionTtsController::GetInstance()->IsSpeaking())); return true; } diff --git a/chrome/browser/extensions/extension_tts_api.h b/chrome/browser/extensions/extension_tts_api.h index 73fc887..0eedcc2 100644 --- a/chrome/browser/extensions/extension_tts_api.h +++ b/chrome/browser/extensions/extension_tts_api.h @@ -5,6 +5,10 @@ #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_TTS_API_H_ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_TTS_API_H_ +#include <queue> + +#include "base/singleton.h" +#include "base/task.h" #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/extensions/extension_tts_api_util.h" @@ -17,6 +21,11 @@ class ExtensionTtsPlatformImpl { // and return true on success. Utterance will always be nonempty. // If the user does not specify the other values, language and gender // will be empty strings, and rate, pitch, and volume will be -1.0. + // + // The ExtensionTtsController will only try to speak one utterance at + // a time. If it wants to intterupt speech, it will always call Stop + // before speaking again, otherwise it will wait until IsSpeaking + // returns false before calling Speak again. virtual bool Speak( const std::string& utterance, const std::string& language, @@ -44,23 +53,108 @@ class ExtensionTtsPlatformImpl { DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImpl); }; +// Singleton class that manages text-to-speech. +class ExtensionTtsController { + public: + // Get the single instance of this class. + static ExtensionTtsController* GetInstance(); + + struct Utterance { + Utterance() + : rate(-1.0), + pitch(-1.0), + volume(-1.0), + success_task(NULL), + failure_task(NULL) { + } + + std::string text; + std::string language; + std::string gender; + double rate; + double pitch; + double volume; + + Task* success_task; + Task* failure_task; + + std::string error; + }; + + // Returns true if we're currently speaking an utterance. + bool IsSpeaking() const; + + // Speak the given utterance. If |can_enqueue| is true and another + // utterance is in progress, adds it to the end of the queue. Otherwise, + // interrupts any current utterance and speaks this one immediately. + void SpeakOrEnqueue(Utterance* utterance, bool can_enqueue); + + // Stop all utterances and flush the queue. + void Stop(); + + // For unit testing. + void SetPlatformImpl(ExtensionTtsPlatformImpl* platform_impl); + + private: + ExtensionTtsController(); + virtual ~ExtensionTtsController() {} + + // Get the platform TTS implementation (or injected mock). + ExtensionTtsPlatformImpl* GetPlatformImpl(); + + // Start speaking the given utterance. Will either take ownership of + // |utterance| or delete it if there's an error. + void SpeakNow(Utterance* utterance); + + // Called periodically when speech is ongoing. Checks to see if the + // underlying platform speech system has finished the current utterance, + // and if so finishes it and pops the next utterance off the queue. + void CheckSpeechStatus(); + + // Clear the utterance queue. + void ClearUtteranceQueue(); + + // Finalize and delete the current utterance. + void FinishCurrentUtterance(); + + ScopedRunnableMethodFactory<ExtensionTtsController> method_factory_; + friend struct DefaultSingletonTraits<ExtensionTtsController>; + + // The current utterance being spoken. + Utterance* current_utterance_; + + // A queue of utterances to speak after the current one finishes. + std::queue<Utterance*> utterance_queue_; + + // A pointer to the platform implementation of text-to-speech, for + // dependency injection. + ExtensionTtsPlatformImpl* platform_impl_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionTtsController); +}; + // // Extension API function definitions // -class ExtensionTtsSpeakFunction : public SyncExtensionFunction { +class ExtensionTtsSpeakFunction : public AsyncExtensionFunction { + private: ~ExtensionTtsSpeakFunction() {} virtual bool RunImpl(); + void SpeechFinished(bool success); + ExtensionTtsController::Utterance* utterance_; DECLARE_EXTENSION_FUNCTION_NAME("experimental.tts.speak") }; class ExtensionTtsStopSpeakingFunction : public SyncExtensionFunction { + private: ~ExtensionTtsStopSpeakingFunction() {} virtual bool RunImpl(); DECLARE_EXTENSION_FUNCTION_NAME("experimental.tts.stop") }; class ExtensionTtsIsSpeakingFunction : public SyncExtensionFunction { + private: ~ExtensionTtsIsSpeakingFunction() {} virtual bool RunImpl(); DECLARE_EXTENSION_FUNCTION_NAME("experimental.tts.isSpeaking") diff --git a/chrome/browser/extensions/extension_tts_api_linux.cc b/chrome/browser/extensions/extension_tts_api_linux.cc index 8ee228b..7b89799 100644 --- a/chrome/browser/extensions/extension_tts_api_linux.cc +++ b/chrome/browser/extensions/extension_tts_api_linux.cc @@ -38,7 +38,7 @@ class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { // Get the single instance of this class. static ExtensionTtsPlatformImplLinux* GetInstance() { - return ExtensionTtsPlatformImplLinux::GetInstance(); + return Singleton<ExtensionTtsPlatformImplLinux>::get(); } private: diff --git a/chrome/browser/extensions/extension_tts_api_util.h b/chrome/browser/extensions/extension_tts_api_util.h index 4b41871..cf0d702 100644 --- a/chrome/browser/extensions/extension_tts_api_util.h +++ b/chrome/browser/extensions/extension_tts_api_util.h @@ -17,6 +17,7 @@ const char kGenderKey[] = "gender"; const char kRateKey[] = "rate"; const char kPitchKey[] = "pitch"; const char kVolumeKey[] = "volume"; +const char kEnqueueKey[] = "enqueue"; const char kEqualStr[] = "="; const char kDelimiter[] = ";"; diff --git a/chrome/browser/extensions/extension_tts_apitest.cc b/chrome/browser/extensions/extension_tts_apitest.cc index dcdfb9d..6f37404 100644 --- a/chrome/browser/extensions/extension_tts_apitest.cc +++ b/chrome/browser/extensions/extension_tts_apitest.cc @@ -2,19 +2,164 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/command_line.h" -#include "chrome/browser/chromeos/cros/cros_mock.h" #include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_tts_api.h" #include "chrome/common/chrome_switches.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Needed for CreateFunctor. +#define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#include "testing/gmock_mutant.h" -// This extension API is currently only supported on Chrome OS. #if defined(OS_CHROMEOS) -#define MAYBE_Tts Tts -#else -#define MAYBE_Tts DISABLED_Tts +#include "chrome/browser/chromeos/cros/cros_mock.h" #endif -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tts) { +using ::testing::CreateFunctor; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::InvokeWithoutArgs; +using ::testing::Return; +using ::testing::StrictMock; +using ::testing::_; + +class MockExtensionTtsPlatformImpl : public ExtensionTtsPlatformImpl { + public: + MOCK_METHOD6(Speak, + bool(const std::string& utterance, + const std::string& language, + const std::string& gender, + double rate, + double pitch, + double volume)); + MOCK_METHOD0(StopSpeaking, bool(void)); + MOCK_METHOD0(IsSpeaking, bool(void)); + + void SetErrorToEpicFail() { + set_error("epic fail"); + } +}; + +class TtsApiTest : public ExtensionApiTest { + public: + virtual void SetUpCommandLine(CommandLine* command_line) { + ExtensionApiTest::SetUpCommandLine(command_line); + command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis); + } + + virtual void SetUpInProcessBrowserTestFixture() { + ExtensionApiTest::SetUpInProcessBrowserTestFixture(); + ExtensionTtsController::GetInstance()->SetPlatformImpl( + &mock_platform_impl_); + } + + protected: + StrictMock<MockExtensionTtsPlatformImpl> mock_platform_impl_; +}; + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakFinishesImmediately) { + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/speak_once")) << message_; +} + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakKeepsSpeakingTwice) { + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/speak_once")) << message_; +} + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakInterrupt) { + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak("text 1", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak("text 2", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/interrupt")) << message_; +} + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakQueueInterrupt) { + // In this test, two utterances are queued, and then a third + // interrupts. Speak() never gets called on the second utterance. + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak("text 1", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak("text 3", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/queue_interrupt")) << message_; +} + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakEnqueue) { + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak("text 1", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + EXPECT_CALL(mock_platform_impl_, Speak("text 2", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/enqueue")) << message_; +} + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakError) { + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(false)); + EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _)) + .WillOnce(DoAll( + InvokeWithoutArgs( + CreateFunctor(&mock_platform_impl_, + &MockExtensionTtsPlatformImpl::SetErrorToEpicFail)), + Return(false))); + EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/speak_error")) << message_; +} + +#if defined(OS_CHROMEOS) +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TtsChromeOs) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); @@ -24,3 +169,4 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tts) { ASSERT_TRUE(RunExtensionTest("tts/chromeos")) << message_; } +#endif diff --git a/chrome/browser/extensions/extension_ui_unittest.cc b/chrome/browser/extensions/extension_ui_unittest.cc index ba429ba..1bda60a 100644 --- a/chrome/browser/extensions/extension_ui_unittest.cc +++ b/chrome/browser/extensions/extension_ui_unittest.cc @@ -33,7 +33,6 @@ namespace { #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - Extension extension(path); std::string error; FilePath manifest_path = extension_path.Append( @@ -41,7 +40,10 @@ namespace { scoped_ptr<DictionaryValue> extension_data(DeserializeJSONTestData( manifest_path, &error)); EXPECT_EQ("", error); - EXPECT_TRUE(extension.InitFromValue(*extension_data, true, &error)); + + scoped_refptr<Extension> extension(Extension::Create( + path, Extension::INVALID, *extension_data, true, &error)); + EXPECT_TRUE(extension.get()); EXPECT_EQ("", error); scoped_ptr<DictionaryValue> expected_output_data(DeserializeJSONTestData( @@ -50,7 +52,7 @@ namespace { // Produce test output. scoped_ptr<DictionaryValue> actual_output_data( - ExtensionsDOMHandler::CreateExtensionDetailValue(NULL, &extension, + ExtensionsDOMHandler::CreateExtensionDetailValue(NULL, extension.get(), pages, true)); // Compare the outputs. diff --git a/chrome/browser/extensions/extension_uitest.cc b/chrome/browser/extensions/extension_uitest.cc index 2076949..f074c9b 100644 --- a/chrome/browser/extensions/extension_uitest.cc +++ b/chrome/browser/extensions/extension_uitest.cc @@ -121,6 +121,7 @@ class ExtensionTestSimpleApiCall : public ExtensionUITest { DISALLOW_COPY_AND_ASSIGN(ExtensionTestSimpleApiCall); }; +// Flaky: http://crbug.com/44599 TEST_F(ExtensionTestSimpleApiCall, FLAKY_RunTest) { namespace keys = extension_automation_constants; diff --git a/chrome/browser/extensions/extension_updater.cc b/chrome/browser/extensions/extension_updater.cc index f4e9e94..b2b3972 100644 --- a/chrome/browser/extensions/extension_updater.cc +++ b/chrome/browser/extensions/extension_updater.cc @@ -68,7 +68,7 @@ static const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days // request. We want to stay under 2K because of proxies, etc. static const int kExtensionsManifestMaxURLSize = 2000; -ManifestFetchData::ManifestFetchData(GURL update_url) +ManifestFetchData::ManifestFetchData(const GURL& update_url) : base_url_(update_url), full_url_(update_url) { } @@ -232,9 +232,7 @@ void ManifestFetchesBuilder::AddExtensionData( PendingExtensionInfo::ExpectedCrxType crx_type, GURL update_url) { - // Only internal and external extensions can be autoupdated. - if (location != Extension::INTERNAL && - !Extension::IsExternalLocation(location)) { + if (!Extension::IsAutoUpdateableLocation(location)) { return; } @@ -520,13 +518,13 @@ void ExtensionUpdater::OnManifestFetchComplete(const GURL& url, // We want to try parsing the manifest, and if it indicates updates are // available, we want to fire off requests to fetch those updates. if (status.status() == URLRequestStatus::SUCCESS && response_code == 200) { - scoped_refptr<SafeManifestParser> safe_parser = - new SafeManifestParser(data, current_manifest_fetch_.release(), this); + scoped_refptr<SafeManifestParser> safe_parser( + new SafeManifestParser(data, current_manifest_fetch_.release(), this)); safe_parser->Start(); } else { // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546). - LOG(INFO) << "Failed to fetch manifest '" << url.possibly_invalid_spec() << - "' response code:" << response_code; + VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec() + << "' response code:" << response_code; } manifest_fetcher_.reset(); current_manifest_fetch_.reset(); @@ -620,8 +618,8 @@ void ExtensionUpdater::OnCRXFetchComplete(const GURL& url, // TODO(asargent) do things like exponential backoff, handling // 503 Service Unavailable / Retry-After headers, etc. here. // (http://crbug.com/12546). - LOG(INFO) << "Failed to fetch extension '" << - url.possibly_invalid_spec() << "' response code:" << response_code; + VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec() + << "' response code:" << response_code; } extension_fetcher_.reset(); current_extension_fetch_ = ExtensionFetch(); @@ -736,7 +734,7 @@ bool ExtensionUpdater::GetExistingVersion(const std::string& id, *version = prefs_->GetString(kExtensionBlacklistUpdateVersion); return true; } - Extension* extension = service_->GetExtensionById(id, false); + const Extension* extension = service_->GetExtensionById(id, false); if (!extension) { return false; } @@ -835,7 +833,8 @@ void ExtensionUpdater::StartUpdateCheck(ManifestFetchData* fetch_data) { URLFetcher::GET, this)); manifest_fetcher_->set_request_context(Profile::GetDefaultRequestContext()); manifest_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SAVE_COOKIES); + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DISABLE_CACHE); manifest_fetcher_->Start(); } } @@ -862,7 +861,8 @@ void ExtensionUpdater::FetchUpdatedExtension(const std::string& id, extension_fetcher_->set_request_context( Profile::GetDefaultRequestContext()); extension_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SAVE_COOKIES); + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DISABLE_CACHE); extension_fetcher_->Start(); current_extension_fetch_ = ExtensionFetch(id, url, hash, version); } diff --git a/chrome/browser/extensions/extension_updater.h b/chrome/browser/extensions/extension_updater.h index 4540dd0..79fa166 100644 --- a/chrome/browser/extensions/extension_updater.h +++ b/chrome/browser/extensions/extension_updater.h @@ -36,7 +36,7 @@ class ManifestFetchData { public: static const int kNeverPinged = -1; - explicit ManifestFetchData(GURL update_url); + explicit ManifestFetchData(const GURL& update_url); ~ManifestFetchData(); // Returns true if this extension information was successfully added. If the @@ -112,7 +112,6 @@ class ManifestFetchesBuilder { const Version& version, PendingExtensionInfo::ExpectedCrxType crx_type, GURL update_url); - ExtensionUpdateService* service_; // List of data on fetches we're going to do. We limit the number of diff --git a/chrome/browser/extensions/extension_updater_unittest.cc b/chrome/browser/extensions/extension_updater_unittest.cc index 7989e7a..f099b86 100644 --- a/chrome/browser/extensions/extension_updater_unittest.cc +++ b/chrome/browser/extensions/extension_updater_unittest.cc @@ -33,7 +33,9 @@ using base::Time; using base::TimeDelta; static int expected_load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DISABLE_CACHE; // Base class for further specialized test classes. class MockService : public ExtensionUpdateService { @@ -57,7 +59,7 @@ class MockService : public ExtensionUpdateService { FAIL(); } - virtual Extension* GetExtensionById(const std::string& id, bool) { + virtual const Extension* GetExtensionById(const std::string& id, bool) { ADD_FAILURE(); return NULL; } @@ -94,7 +96,8 @@ class MockService : public ExtensionUpdateService { base::StringPrintf("Extension %d", i)); if (update_url) manifest.SetString(extension_manifest_keys::kUpdateURL, *update_url); - Extension* e = prefs_.AddExtensionWithManifest(manifest, location); + scoped_refptr<Extension> e = + prefs_.AddExtensionWithManifest(manifest, location); ASSERT_TRUE(e != NULL); list->push_back(e); } @@ -141,7 +144,7 @@ class ServiceForManifestTests : public MockService { virtual ~ServiceForManifestTests() {} - virtual Extension* GetExtensionById(const std::string& id, bool) { + virtual const Extension* GetExtensionById(const std::string& id, bool) { for (ExtensionList::iterator iter = extensions_.begin(); iter != extensions_.end(); ++iter) { if ((*iter)->id() == id) { @@ -193,7 +196,7 @@ class ServiceForDownloadTests : public MockService { return pending_extensions_; } - virtual Extension* GetExtensionById(const std::string& id, bool) { + virtual const Extension* GetExtensionById(const std::string& id, bool) { last_inquired_extension_id_ = id; return NULL; } @@ -307,8 +310,8 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcherFactory factory; URLFetcher::set_factory(&factory); - scoped_refptr<ExtensionUpdater> updater = - new ExtensionUpdater(&service, service.pref_service(), 60*60*24); + scoped_refptr<ExtensionUpdater> updater( + new ExtensionUpdater(&service, service.pref_service(), 60*60*24)); updater->Start(); // Tell the update that it's time to do update checks. @@ -343,10 +346,6 @@ class ExtensionUpdaterTest : public testing::Test { EXPECT_EQ(extensions[0]->VersionString(), params["v"]); } EXPECT_EQ("", params["uc"]); - - if (!pending) { - STLDeleteElements(&extensions); - } } static void TestBlacklistUpdateCheckRequests() { @@ -359,8 +358,8 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcherFactory factory; URLFetcher::set_factory(&factory); - scoped_refptr<ExtensionUpdater> updater = - new ExtensionUpdater(&service, service.pref_service(), 60*60*24); + scoped_refptr<ExtensionUpdater> updater( + new ExtensionUpdater(&service, service.pref_service(), 60*60*24)); updater->Start(); // Tell the updater that it's time to do update checks. @@ -411,9 +410,9 @@ class ExtensionUpdaterTest : public testing::Test { service.set_extensions(tmp); MessageLoop message_loop; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); // Check passing an empty list of parse results to DetermineUpdates ManifestFetchData fetch_data(GURL("http://localhost/foo")); @@ -438,7 +437,6 @@ class ExtensionUpdaterTest : public testing::Test { updateable = updater->DetermineUpdates(fetch_data, updates); EXPECT_EQ(1u, updateable.size()); EXPECT_EQ(0, updateable[0]); - STLDeleteElements(&tmp); } static void TestDetermineUpdatesPending() { @@ -449,9 +447,9 @@ class ExtensionUpdaterTest : public testing::Test { service.set_pending_extensions(pending_extensions); MessageLoop message_loop; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); ManifestFetchData fetch_data(GURL("http://localhost/foo")); UpdateManifest::Results updates; @@ -483,9 +481,9 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcher* fetcher = NULL; URLFetcher::set_factory(&factory); ServiceForDownloadTests service; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); GURL url1("http://localhost/manifest1"); GURL url2("http://localhost/manifest2"); @@ -547,9 +545,9 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcher* fetcher = NULL; URLFetcher::set_factory(&factory); ServiceForDownloadTests service; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); GURL test_url("http://localhost/extension.crx"); @@ -610,9 +608,9 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcher* fetcher = NULL; URLFetcher::set_factory(&factory); ServiceForBlacklistTests service; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); service.pref_service()-> RegisterStringPref(prefs::kExtensionBlacklistUpdateVersion, "0"); GURL test_url("http://localhost/extension.crx"); @@ -657,9 +655,9 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcher* fetcher = NULL; URLFetcher::set_factory(&factory); ServiceForDownloadTests service; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); GURL url1("http://localhost/extension1.crx"); GURL url2("http://localhost/extension2.crx"); @@ -744,9 +742,9 @@ class ExtensionUpdaterTest : public testing::Test { } MessageLoop message_loop; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); updater->set_blacklist_checks_enabled(false); // Make the updater do manifest fetching, and note the urls it tries to @@ -788,8 +786,6 @@ class ExtensionUpdaterTest : public testing::Test { size_t pos = url1_query.find(search_string); EXPECT_TRUE(pos != std::string::npos); } - - STLDeleteElements(&tmp); } // This makes sure that the extension updater properly stores the results @@ -798,9 +794,9 @@ class ExtensionUpdaterTest : public testing::Test { // >= 1 day for the extension. static void TestHandleManifestResults() { ServiceForManifestTests service; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); GURL update_url("http://www.google.com/manifest"); ExtensionList tmp; @@ -809,7 +805,7 @@ class ExtensionUpdaterTest : public testing::Test { service.set_extensions(tmp); ManifestFetchData fetch_data(update_url); - Extension* extension = tmp[0]; + const Extension* extension = tmp[0]; fetch_data.AddExtension(extension->id(), extension->VersionString(), ManifestFetchData::kNeverPinged); UpdateManifest::Results results; @@ -821,8 +817,6 @@ class ExtensionUpdaterTest : public testing::Test { EXPECT_FALSE(last_ping_day.is_null()); int64 seconds_diff = (Time::Now() - last_ping_day).InSeconds(); EXPECT_LT(seconds_diff - results.daystart_elapsed_seconds, 5); - - STLDeleteElements(&tmp); } }; @@ -894,7 +888,6 @@ TEST(ExtensionUpdaterTest, TestManifestFetchesBuilderAddExtension) { ASSERT_FALSE(extensions.empty()); builder.AddExtension(*extensions[0]); EXPECT_TRUE(builder.GetFetches().empty()); - STLDeleteElements(&extensions); } // Extensions with invalid update URLs should be rejected. diff --git a/chrome/browser/extensions/extension_webnavigation_api.cc b/chrome/browser/extensions/extension_webnavigation_api.cc index 159fdb7..9a3e4ab 100644 --- a/chrome/browser/extensions/extension_webnavigation_api.cc +++ b/chrome/browser/extensions/extension_webnavigation_api.cc @@ -17,6 +17,7 @@ #include "chrome/browser/tab_contents/provisional_load_details.h" #include "chrome/common/notification_type.h" #include "chrome/common/notification_service.h" +#include "chrome/common/url_constants.h" #include "net/base/net_errors.h" namespace keys = extension_webnavigation_api_constants; @@ -36,6 +37,71 @@ double MilliSecondsFromTime(const base::Time& time) { } // namespace +FrameNavigationState::FrameNavigationState() { +} + +FrameNavigationState::~FrameNavigationState() { +} + +bool FrameNavigationState::CanSendEvents(long long frame_id) const { + FrameIdToStateMap::const_iterator frame_state = + frame_state_map_.find(frame_id); + return frame_state != frame_state_map_.end() && + !frame_state->second.error_occurred; +} + +void FrameNavigationState::TrackFrame(long long frame_id, + const GURL& url, + bool is_main_frame, + const TabContents* tab_contents) { + if (is_main_frame) + RemoveTabContentsState(tab_contents); + tab_contents_map_.insert( + TabContentsToFrameIdMap::value_type(tab_contents, frame_id)); + FrameState& frame_state = frame_state_map_[frame_id]; + frame_state.error_occurred = (url.spec() == chrome::kUnreachableWebDataURL); + frame_state.url = url; + frame_state.is_main_frame = is_main_frame; +} + +GURL FrameNavigationState::GetUrl(long long frame_id) const { + FrameIdToStateMap::const_iterator frame_state = + frame_state_map_.find(frame_id); + if (frame_state == frame_state_map_.end()) { + NOTREACHED(); + return GURL(); + } + return frame_state->second.url; +} + +bool FrameNavigationState::IsMainFrame(long long frame_id) const { + FrameIdToStateMap::const_iterator frame_state = + frame_state_map_.find(frame_id); + if (frame_state == frame_state_map_.end()) { + NOTREACHED(); + return false; + } + return frame_state->second.is_main_frame; +} + +void FrameNavigationState::ErrorOccurredInFrame(long long frame_id) { + DCHECK(frame_state_map_.find(frame_id) != frame_state_map_.end()); + frame_state_map_[frame_id].error_occurred = true; +} + +void FrameNavigationState::RemoveTabContentsState( + const TabContents* tab_contents) { + typedef TabContentsToFrameIdMap::iterator FrameIdIterator; + std::pair<FrameIdIterator, FrameIdIterator> frame_ids = + tab_contents_map_.equal_range(tab_contents); + for (FrameIdIterator frame_id = frame_ids.first; frame_id != frame_ids.second; + ++frame_id) { + frame_state_map_.erase(frame_id->second); + } + tab_contents_map_.erase(tab_contents); +} + + // static ExtensionWebNavigationEventRouter* ExtensionWebNavigationEventRouter::GetInstance() { @@ -51,8 +117,17 @@ void ExtensionWebNavigationEventRouter::Init() { NotificationType::FRAME_PROVISIONAL_LOAD_COMMITTED, NotificationService::AllSources()); registrar_.Add(this, + NotificationType::FRAME_DOM_CONTENT_LOADED, + NotificationService::AllSources()); + registrar_.Add(this, + NotificationType::FRAME_DID_FINISH_LOAD, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR, NotificationService::AllSources()); + registrar_.Add(this, + NotificationType::TAB_CONTENTS_DESTROYED, + NotificationService::AllSources()); } } @@ -71,12 +146,27 @@ void ExtensionWebNavigationEventRouter::Observe( Source<NavigationController>(source).ptr(), Details<ProvisionalLoadDetails>(details).ptr()); break; + case NotificationType::FRAME_DOM_CONTENT_LOADED: + FrameDomContentLoaded( + Source<NavigationController>(source).ptr(), + *Details<long long>(details).ptr()); + break; + case NotificationType::FRAME_DID_FINISH_LOAD: + FrameDidFinishLoad( + Source<NavigationController>(source).ptr(), + *Details<long long>(details).ptr()); + break; case NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR: FailProvisionalLoadWithError( Source<NavigationController>(source).ptr(), Details<ProvisionalLoadDetails>(details).ptr()); break; + case NotificationType::TAB_CONTENTS_DESTROYED: + navigation_state_.RemoveTabContentsState( + Source<TabContents>(source).ptr()); + break; + default: NOTREACHED(); } @@ -84,6 +174,12 @@ void ExtensionWebNavigationEventRouter::Observe( void ExtensionWebNavigationEventRouter::FrameProvisionalLoadStart( NavigationController* controller, ProvisionalLoadDetails* details) { + navigation_state_.TrackFrame(details->frame_id(), + details->url(), + details->main_frame(), + controller->tab_contents()); + if (!navigation_state_.CanSendEvents(details->frame_id())) + return; ListValue args; DictionaryValue* dict = new DictionaryValue(); dict->SetInteger(keys::kTabIdKey, @@ -103,6 +199,8 @@ void ExtensionWebNavigationEventRouter::FrameProvisionalLoadStart( void ExtensionWebNavigationEventRouter::FrameProvisionalLoadCommitted( NavigationController* controller, ProvisionalLoadDetails* details) { + if (!navigation_state_.CanSendEvents(details->frame_id())) + return; ListValue args; DictionaryValue* dict = new DictionaryValue(); dict->SetInteger(keys::kTabIdKey, @@ -124,9 +222,49 @@ void ExtensionWebNavigationEventRouter::FrameProvisionalLoadCommitted( DispatchEvent(controller->profile(), keys::kOnCommitted, json_args); } +void ExtensionWebNavigationEventRouter::FrameDomContentLoaded( + NavigationController* controller, long long frame_id) { + if (!navigation_state_.CanSendEvents(frame_id)) + return; + ListValue args; + DictionaryValue* dict = new DictionaryValue(); + dict->SetInteger(keys::kTabIdKey, + ExtensionTabUtil::GetTabId(controller->tab_contents())); + dict->SetString(keys::kUrlKey, navigation_state_.GetUrl(frame_id).spec()); + dict->SetInteger(keys::kFrameIdKey, navigation_state_.IsMainFrame(frame_id) ? + 0 : static_cast<int>(frame_id)); + dict->SetReal(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now())); + args.Append(dict); + + std::string json_args; + base::JSONWriter::Write(&args, false, &json_args); + DispatchEvent(controller->profile(), keys::kOnDOMContentLoaded, json_args); +} + +void ExtensionWebNavigationEventRouter::FrameDidFinishLoad( + NavigationController* controller, long long frame_id) { + if (!navigation_state_.CanSendEvents(frame_id)) + return; + ListValue args; + DictionaryValue* dict = new DictionaryValue(); + dict->SetInteger(keys::kTabIdKey, + ExtensionTabUtil::GetTabId(controller->tab_contents())); + dict->SetString(keys::kUrlKey, navigation_state_.GetUrl(frame_id).spec()); + dict->SetInteger(keys::kFrameIdKey, navigation_state_.IsMainFrame(frame_id) ? + 0 : static_cast<int>(frame_id)); + dict->SetReal(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now())); + args.Append(dict); + + std::string json_args; + base::JSONWriter::Write(&args, false, &json_args); + DispatchEvent(controller->profile(), keys::kOnCompleted, json_args); +} + void ExtensionWebNavigationEventRouter::FailProvisionalLoadWithError( NavigationController* controller, ProvisionalLoadDetails* details) { + if (!navigation_state_.CanSendEvents(details->frame_id())) + return; ListValue args; DictionaryValue* dict = new DictionaryValue(); dict->SetInteger(keys::kTabIdKey, @@ -141,6 +279,7 @@ void ExtensionWebNavigationEventRouter::FailProvisionalLoadWithError( std::string json_args; base::JSONWriter::Write(&args, false, &json_args); + navigation_state_.ErrorOccurredInFrame(details->frame_id()); DispatchEvent(controller->profile(), keys::kOnErrorOccurred, json_args); } diff --git a/chrome/browser/extensions/extension_webnavigation_api.h b/chrome/browser/extensions/extension_webnavigation_api.h index 21cfb16..b595aca 100644 --- a/chrome/browser/extensions/extension_webnavigation_api.h +++ b/chrome/browser/extensions/extension_webnavigation_api.h @@ -22,6 +22,53 @@ class NavigationController; class ProvisionalLoadDetails; class TabContents; +// Tracks which frames are in an error state, and no navigation events should +// be sent for. +class FrameNavigationState { + public: + FrameNavigationState(); + ~FrameNavigationState(); + + // True if navigation events for the given frame can be sent. + bool CanSendEvents(long long frame_id) const; + + // Starts to track a frame given by its |frame_id| showing the URL |url| in + // a |tab_contents|. + void TrackFrame(long long frame_id, + const GURL& url, + bool is_main_frame, + const TabContents* tab_contents); + + // Returns the URL corresponding to a tracked frame given by its |frame_id|. + GURL GetUrl(long long frame_id) const; + + // True if the frame given by its |frame_id| is the main frame of its tab. + bool IsMainFrame(long long frame_id) const; + + // Marks a frame as in an error state. + void ErrorOccurredInFrame(long long frame_id); + + // Removes state associated with this tab contents and all of its frames. + void RemoveTabContentsState(const TabContents* tab_contents); + + private: + typedef std::multimap<const TabContents*, long long> TabContentsToFrameIdMap; + struct FrameState { + bool error_occurred; // True if an error has occurred in this frame. + bool is_main_frame; // True if this is a main frame. + GURL url; // URL of this frame. + }; + typedef std::map<long long, FrameState> FrameIdToStateMap; + + // Tracks which frames belong to a given tab contents object. + TabContentsToFrameIdMap tab_contents_map_; + + // Tracks the state of known frames. + FrameIdToStateMap frame_state_map_; + + DISALLOW_COPY_AND_ASSIGN(FrameNavigationState); +}; + // Observes navigation notifications and routes them as events to the extension // system. class ExtensionWebNavigationEventRouter : public NotificationObserver { @@ -54,6 +101,15 @@ class ExtensionWebNavigationEventRouter : public NotificationObserver { void FrameProvisionalLoadCommitted(NavigationController* controller, ProvisionalLoadDetails* details); + // Handler for the FRAME_DOM_CONTENT_LOADED event. The method takes the frame + // ID and constructs a suitable JSON formatted extension event from it. + void FrameDomContentLoaded(NavigationController* controller, + long long frame_id); + + // Handler for the FRAME_DID_FINISH_LOAD event. The method takes the frame + // ID and constructs a suitable JSON formatted extension event from it. + void FrameDidFinishLoad(NavigationController* controller, long long frame_id); + // Handler for the FAIL_PROVISIONAL_LOAD_WITH_ERROR event. The method takes // the details of such an event and constructs a suitable JSON formatted // extension event from it. @@ -65,6 +121,9 @@ class ExtensionWebNavigationEventRouter : public NotificationObserver { const char* event_name, const std::string& json_args); + // Tracks the state of the frames we are sending events for. + FrameNavigationState navigation_state_; + // Used for tracking registrations to navigation notifications. NotificationRegistrar registrar_; diff --git a/chrome/browser/extensions/extension_webnavigation_apitest.cc b/chrome/browser/extensions/extension_webnavigation_apitest.cc index 5dd3f3e..b27887b 100644 --- a/chrome/browser/extensions/extension_webnavigation_apitest.cc +++ b/chrome/browser/extensions/extension_webnavigation_apitest.cc @@ -13,10 +13,16 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WebNavigation) { ASSERT_TRUE(RunExtensionTest("webnavigation/api")) << message_; } -// This test is flaky: http://crbug.com/60229 -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FLAKY_WebNavigationEvents) { +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WebNavigationEvents1) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); - ASSERT_TRUE(RunExtensionTest("webnavigation/navigation")) << message_; + ASSERT_TRUE(RunExtensionTest("webnavigation/navigation1")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WebNavigationEvents2) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + + ASSERT_TRUE(RunExtensionTest("webnavigation/navigation2")) << message_; } diff --git a/chrome/browser/extensions/extension_webnavigation_unittest.cc b/chrome/browser/extensions/extension_webnavigation_unittest.cc new file mode 100644 index 0000000..5ac378e --- /dev/null +++ b/chrome/browser/extensions/extension_webnavigation_unittest.cc @@ -0,0 +1,105 @@ +// 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. + +// Tests common functionality used by the Chrome Extensions webNavigation API +// implementation. + +#include "testing/gtest/include/gtest/gtest.h" + +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/extensions/extension_webnavigation_api.h" +#include "chrome/browser/renderer_host/test/test_render_view_host.h" +#include "chrome/browser/tab_contents/test_tab_contents.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/testing_profile.h" + +class FrameNavigationStateTest : public RenderViewHostTestHarness { +}; + +// Test that a frame is correctly tracked, and removed once the tab contents +// goes away. +TEST_F(FrameNavigationStateTest, TrackFrame) { + FrameNavigationState navigation_state; + const long long frame_id1 = 23; + const long long frame_id2 = 42; + const GURL url1("http://www.google.com/"); + const GURL url2("http://mail.google.com/"); + + // Create a main frame. + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id1)); + navigation_state.TrackFrame(frame_id1, url1, true, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id1)); + + // Add a sub frame. + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id2)); + navigation_state.TrackFrame(frame_id2, url2, false, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id2)); + + // Check frame state. + EXPECT_TRUE(navigation_state.IsMainFrame(frame_id1)); + EXPECT_EQ(url1, navigation_state.GetUrl(frame_id1)); + EXPECT_FALSE(navigation_state.IsMainFrame(frame_id2)); + EXPECT_EQ(url2, navigation_state.GetUrl(frame_id2)); + + + // Removing the tab contents should also remove all state of its frames. + navigation_state.RemoveTabContentsState(contents()); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id1)); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id2)); +} + +// Test that no events can be sent for a frame after an error occurred, but +// before a new navigation happened in this frame. +TEST_F(FrameNavigationStateTest, ErrorState) { + FrameNavigationState navigation_state; + const long long frame_id = 42; + const GURL url("http://www.google.com/"); + + navigation_state.TrackFrame(frame_id, url, true, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id)); + + // After an error occurred, no further events should be sent. + navigation_state.ErrorOccurredInFrame(frame_id); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id)); + + // Navigations to the "unreachable web data" URL should be ignored. + navigation_state.TrackFrame( + frame_id, GURL(chrome::kUnreachableWebDataURL), true, contents()); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id)); + + // However, when the frame navigates again, it should send events again. + navigation_state.TrackFrame(frame_id, url, true, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id)); +} + +// Tests that for a sub frame, no events are send after an error occurred, but +// before a new navigation happened in this frame. +TEST_F(FrameNavigationStateTest, ErrorStateFrame) { + FrameNavigationState navigation_state; + const long long frame_id1 = 23; + const long long frame_id2 = 42; + const GURL url("http://www.google.com/"); + + navigation_state.TrackFrame(frame_id1, url, true, contents()); + navigation_state.TrackFrame(frame_id2, url, false, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id1)); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id2)); + + // After an error occurred, no further events should be sent. + navigation_state.ErrorOccurredInFrame(frame_id2); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id1)); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id2)); + + // Navigations to the "unreachable web data" URL should be ignored. + navigation_state.TrackFrame( + frame_id2, GURL(chrome::kUnreachableWebDataURL), false, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id1)); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id2)); + + // However, when the frame navigates again, it should send events again. + navigation_state.TrackFrame(frame_id2, url, false, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id1)); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id2)); +} diff --git a/chrome/browser/extensions/extension_webstore_private_api.cc b/chrome/browser/extensions/extension_webstore_private_api.cc index bf965a3..092d395 100644 --- a/chrome/browser/extensions/extension_webstore_private_api.cc +++ b/chrome/browser/extensions/extension_webstore_private_api.cc @@ -7,25 +7,33 @@ #include <string> #include <vector> +#include "app/l10n_util.h" #include "base/string_util.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/profile_manager.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/net/gaia/gaia_constants.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" #include "net/base/escape.h" namespace { const char* install_base_url = extension_urls::kGalleryUpdateHttpsUrl; -const char kAlreadyLoggedInError[] = "User already logged in"; const char kLoginKey[] = "login"; const char kTokenKey[] = "token"; ProfileSyncService* test_sync_service = NULL; +BrowserSignin* test_signin = NULL; // Returns either the test sync service, or the real one from |profile|. ProfileSyncService* GetSyncService(Profile* profile) { @@ -35,13 +43,46 @@ ProfileSyncService* GetSyncService(Profile* profile) { return profile->GetProfileSyncService(); } +BrowserSignin* GetBrowserSignin(Profile* profile) { + if (test_signin) + return test_signin; + else + return profile->GetBrowserSignin(); +} + bool IsWebStoreURL(Profile* profile, const GURL& url) { ExtensionsService* service = profile->GetExtensionsService(); - Extension* store = service->GetWebStoreApp(); + const Extension* store = service->GetWebStoreApp(); DCHECK(store); return (service->GetExtensionByWebExtent(url) == store); } +// Helper to create a dictionary with login and token properties set from +// the appropriate values in the passed-in |profile|. +DictionaryValue* CreateLoginResult(Profile* profile) { + DictionaryValue* dictionary = new DictionaryValue(); + std::string username = GetBrowserSignin(profile)->GetSignedInUsername(); + dictionary->SetString(kLoginKey, username); + if (!username.empty()) { + TokenService* token_service = profile->GetTokenService(); + if (token_service->HasTokenForService(GaiaConstants::kGaiaService)) { + dictionary->SetString(kTokenKey, + token_service->GetTokenForService( + GaiaConstants::kGaiaService)); + } + } + return dictionary; +} + +// If |profile| is not off the record, returns it. Otherwise returns the real +// (not off the record) default profile. +Profile* GetDefaultProfile(Profile* profile) { + if (!profile->IsOffTheRecord()) + return profile; + else + return g_browser_process->profile_manager()->GetDefaultProfile(); +} + } // namespace // static @@ -51,6 +92,11 @@ void WebstorePrivateApi::SetTestingProfileSyncService( } // static +void WebstorePrivateApi::SetTestingBrowserSignin(BrowserSignin* signin) { + test_signin = signin; +} + +// static void InstallFunction::SetTestingInstallBaseUrl( const char* testing_install_base_url) { install_base_url = testing_install_base_url; @@ -92,13 +138,7 @@ bool InstallFunction::RunImpl() { bool GetBrowserLoginFunction::RunImpl() { if (!IsWebStoreURL(profile_, source_url())) return false; - string16 username = GetSyncService(profile_)->GetAuthenticatedUsername(); - DictionaryValue* dictionary = new DictionaryValue(); - - dictionary->SetString(kLoginKey, username); - // TODO(asargent) - send the browser login token here too if available. - - result_.reset(dictionary); + result_.reset(CreateLoginResult(GetDefaultProfile(profile_))); return true; } @@ -127,50 +167,118 @@ bool SetStoreLoginFunction::RunImpl() { return true; } -PromptBrowserLoginFunction::~PromptBrowserLoginFunction() {} +PromptBrowserLoginFunction::PromptBrowserLoginFunction() + : waiting_for_token_(false) {} + +PromptBrowserLoginFunction::~PromptBrowserLoginFunction() { +} bool PromptBrowserLoginFunction::RunImpl() { if (!IsWebStoreURL(profile_, source_url())) return false; std::string preferred_email; - ProfileSyncService* sync_service = GetSyncService(profile_); if (args_->GetSize() > 0) { EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &preferred_email)); - if (!sync_service->GetAuthenticatedUsername().empty()) { - error_ = kAlreadyLoggedInError; - return false; - } } + Profile* profile = GetDefaultProfile(profile_); + + // Login can currently only be invoked tab-modal. Since this is + // coming from the webstore, we should always have a tab, but check + // just in case. + TabContents* tab = dispatcher()->delegate()->associated_tab_contents(); + if (!tab) + return false; + // We return the result asynchronously, so we addref to keep ourself alive. - // Matched with a Release in OnStateChanged(). + // Matched with a Release in OnLoginSuccess() and OnLoginFailure(). AddRef(); - sync_service->AddObserver(this); - // TODO(mirandac/estade) - make use of |preferred_email| to pre-populate the - // browser login dialog if it was set to non-empty above. - sync_service->ShowLoginDialog(NULL); - - // The response will be sent asynchronously in OnStateChanged(). + // Start listening for notifications about the token. + TokenService* token_service = profile->GetTokenService(); + registrar_.Add(this, + NotificationType::TOKEN_AVAILABLE, + Source<TokenService>(token_service)); + registrar_.Add(this, + NotificationType::TOKEN_REQUEST_FAILED, + Source<TokenService>(token_service)); + + GetBrowserSignin(profile)->RequestSignin(tab, + ASCIIToUTF16(preferred_email), + GetLoginMessage(), + this); + + // The response will be sent asynchronously in OnLoginSuccess/OnLoginFailure. return true; } -void PromptBrowserLoginFunction::OnStateChanged() { - ProfileSyncService* sync_service = GetSyncService(profile_); - // If the setup is finished, we'll report back what happened. - if (!sync_service->SetupInProgress()) { - sync_service->RemoveObserver(this); - DictionaryValue* dictionary = new DictionaryValue(); +string16 PromptBrowserLoginFunction::GetLoginMessage() { + using l10n_util::GetStringUTF16; + using l10n_util::GetStringFUTF16; + + // TODO(johnnyg): This would be cleaner as an HTML template. + // http://crbug.com/60216 + string16 message; + message = ASCIIToUTF16("<p>") + + GetStringUTF16(IDS_WEB_STORE_LOGIN_INTRODUCTION_1) + + ASCIIToUTF16("</p>"); + message = message + ASCIIToUTF16("<p>") + + GetStringFUTF16(IDS_WEB_STORE_LOGIN_INTRODUCTION_2, + GetStringUTF16(IDS_PRODUCT_NAME)) + + ASCIIToUTF16("</p>"); + return message; +} - // TODO(asargent) - send the browser login token here too if available. - string16 username = sync_service->GetAuthenticatedUsername(); - dictionary->SetString(kLoginKey, username); +void PromptBrowserLoginFunction::OnLoginSuccess() { + // Ensure that apps are synced. + // - If the user has already setup sync, we add Apps to the current types. + // - If not, we create a new set which is just Apps. + ProfileSyncService* service = GetSyncService(GetDefaultProfile(profile_)); + syncable::ModelTypeSet types; + if (service->HasSyncSetupCompleted()) + service->GetPreferredDataTypes(&types); + types.insert(syncable::APPS); + service->ChangePreferredDataTypes(types); + service->SetSyncSetupCompleted(); + + // We'll finish up in Observe() when the token is ready. + waiting_for_token_ = true; +} + +void PromptBrowserLoginFunction::OnLoginFailure( + const GoogleServiceAuthError& error) { + SendResponse(false); + // Matches the AddRef in RunImpl(). + Release(); +} - result_.reset(dictionary); - SendResponse(true); +void PromptBrowserLoginFunction::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + // Make sure this notification is for the service we are interested in. + std::string service; + if (type == NotificationType::TOKEN_AVAILABLE) { + TokenService::TokenAvailableDetails* available = + Details<TokenService::TokenAvailableDetails>(details).ptr(); + service = available->service(); + } else if (type == NotificationType::TOKEN_REQUEST_FAILED) { + TokenService::TokenRequestFailedDetails* failed = + Details<TokenService::TokenRequestFailedDetails>(details).ptr(); + service = failed->service(); + } else { + NOTREACHED(); + } - // Matches the AddRef in RunImpl(). - Release(); + if (service != GaiaConstants::kGaiaService) { + return; } + + DCHECK(waiting_for_token_); + + result_.reset(CreateLoginResult(GetDefaultProfile(profile_))); + SendResponse(true); + + // Matches the AddRef in RunImpl(). + Release(); } diff --git a/chrome/browser/extensions/extension_webstore_private_api.h b/chrome/browser/extensions/extension_webstore_private_api.h index a47ba2a..39d883c 100644 --- a/chrome/browser/extensions/extension_webstore_private_api.h +++ b/chrome/browser/extensions/extension_webstore_private_api.h @@ -6,8 +6,11 @@ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_WEBSTORE_PRIVATE_API_H_ #pragma once +#include "chrome/browser/browser_signin.h" #include "chrome/browser/extensions/extension_function.h" -#include "chrome/browser/sync/profile_sync_service_observer.h" +#include "chrome/common/net/gaia/google_service_auth_error.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" class ProfileSyncService; @@ -16,6 +19,10 @@ class WebstorePrivateApi { // Allows you to set the ProfileSyncService the function will use for // testing purposes. static void SetTestingProfileSyncService(ProfileSyncService* service); + + // Allows you to set the BrowserSignin the function will use for + // testing purposes. + static void SetTestingBrowserSignin(BrowserSignin* signin); }; class InstallFunction : public SyncExtensionFunction { @@ -44,15 +51,33 @@ class SetStoreLoginFunction : public SyncExtensionFunction { }; class PromptBrowserLoginFunction : public AsyncExtensionFunction, - public ProfileSyncServiceObserver { + public NotificationObserver, + public BrowserSignin::SigninDelegate { public: - // Implements ProfileSyncServiceObserver interface. - virtual void OnStateChanged(); + PromptBrowserLoginFunction(); + // Implements BrowserSignin::SigninDelegate interface. + virtual void OnLoginSuccess(); + virtual void OnLoginFailure(const GoogleServiceAuthError& error); + + // Implements the NotificationObserver interface. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); protected: virtual ~PromptBrowserLoginFunction(); virtual bool RunImpl(); + private: + // Creates the message for signing in. + virtual string16 GetLoginMessage(); + + // Are we waiting for a token available notification? + bool waiting_for_token_; + + // Used for listening for TokenService notifications. + NotificationRegistrar registrar_; + DECLARE_EXTENSION_FUNCTION_NAME("webstorePrivate.promptBrowserLogin"); }; diff --git a/chrome/browser/extensions/extension_webstore_private_browsertest.cc b/chrome/browser/extensions/extension_webstore_private_browsertest.cc index db9a027..01b8e45 100644 --- a/chrome/browser/extensions/extension_webstore_private_browsertest.cc +++ b/chrome/browser/extensions/extension_webstore_private_browsertest.cc @@ -4,11 +4,17 @@ #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_signin.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_test_message_listener.h" #include "chrome/browser/extensions/extension_webstore_private_api.h" +#include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/profile_manager.h" #include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/url_constants.h" #include "chrome/test/ui_test_utils.h" #include "googleurl/src/gurl.h" @@ -26,43 +32,75 @@ const char kTestUrlHostname[] = "www.example.com"; // A fake version of ProfileSyncService used for testing. class FakeProfileSyncService : public ProfileSyncService { public: - // The |username_after_login| parameter determines what this fake - // ProfileSyncService will set the username to when ShowLoginDialog is called. - FakeProfileSyncService(const std::string& initial_username, - const std::string& username_after_login) : - username_(ASCIIToUTF16(initial_username)), - username_after_login_(username_after_login), - observer_(NULL) {} - - virtual ~FakeProfileSyncService() { - EXPECT_TRUE(observer_ == NULL); + FakeProfileSyncService() + : setup_(false) { } + virtual ~FakeProfileSyncService() {} // Overrides of virtual methods in ProfileSyncService. - virtual string16 GetAuthenticatedUsername() const { - return username_; + virtual bool HasSyncSetupCompleted() const { + return setup_; } - virtual void ShowLoginDialog(gfx::NativeWindow) { - EXPECT_TRUE(observer_ != NULL); - username_ = ASCIIToUTF16(username_after_login_); - observer_->OnStateChanged(); + virtual void ChangePreferredDataTypes(const syncable::ModelTypeSet& types) { + types_ = types; } - virtual bool SetupInProgress() const { - return false; + virtual void GetPreferredDataTypes(syncable::ModelTypeSet* types) const { + *types = types_; } - virtual void AddObserver(ProfileSyncServiceObserver* observer) { - EXPECT_TRUE(observer_ == NULL); - observer_ = observer; + virtual void SetSyncSetupCompleted() { + setup_ = true; } - virtual void RemoveObserver(ProfileSyncServiceObserver* observer) { - EXPECT_TRUE(observer == observer_); - observer_ = NULL; + + private: + bool setup_; + syncable::ModelTypeSet types_; +}; + +class FakeBrowserSignin : public BrowserSignin { + public: + // The |username_after_login| parameter determines what this fake + // BrowserSignin will set the username to when ShowLoginDialog is called. + FakeBrowserSignin(bool should_succeed, + const std::string& initial_username, + const std::string& username_after_login) + : BrowserSignin(NULL), + should_succeed_(should_succeed), + username_(initial_username), + username_after_login_(username_after_login) { + } + virtual ~FakeBrowserSignin() {} + + virtual std::string GetSignedInUsername() const { + return username_; + } + + virtual void RequestSignin(TabContents* tab_contents, + const string16& preferred_email, + const string16& message, + SigninDelegate* delegate) { + if (should_succeed_) { + // Simulate valid login. + username_ = username_after_login_; + delegate->OnLoginSuccess(); + + // Fake a token available notification. + Profile* profile = tab_contents->profile(); + if (profile->IsOffTheRecord()) { + profile = g_browser_process->profile_manager()->GetDefaultProfile(); + } + TokenService* token_service = profile->GetTokenService(); + token_service->IssueAuthTokenForTest(GaiaConstants::kGaiaService, + "new_token"); + } else { + delegate->OnLoginFailure(GoogleServiceAuthError( + GoogleServiceAuthError::REQUEST_CANCELED)); + } } private: - string16 username_; + bool should_succeed_; + std::string username_; std::string username_after_login_; - ProfileSyncServiceObserver* observer_; }; class ExtensionWebstorePrivateBrowserTest : public ExtensionBrowserTest { @@ -90,18 +128,54 @@ class ExtensionWebstorePrivateBrowserTest : public ExtensionBrowserTest { return base_url.ReplaceComponents(replacements); } - void RunLoginTest(const std::string& relative_path, - const std::string& initial_login, - const std::string& login_result) { - FakeProfileSyncService sync_service(initial_login, login_result); + void RunLoginTestImpl(bool incognito, + const std::string& relative_path, + const std::string& initial_login, + bool login_succeeds, + const std::string& login_result) { + // Clear the token service so previous tests don't affect things. + TokenService* token_service = browser()->profile()->GetTokenService(); + token_service->ResetCredentialsInMemory(); + if (!initial_login.empty()) { + // Initialize the token service with an existing token. + token_service->IssueAuthTokenForTest(GaiaConstants::kGaiaService, + "existing_token"); + } + + FakeProfileSyncService sync_service; + FakeBrowserSignin signin(login_succeeds, initial_login, login_result); WebstorePrivateApi::SetTestingProfileSyncService(&sync_service); + WebstorePrivateApi::SetTestingBrowserSignin(&signin); + ExtensionTestMessageListener listener("success", false); GURL url = GetUrl(relative_path); - ui_test_utils::NavigateToURL(browser(), url); + if (incognito) { + ui_test_utils::OpenURLOffTheRecord(browser()->profile(), url); + } else { + ui_test_utils::NavigateToURL(browser(), url); + } EXPECT_TRUE(listener.WaitUntilSatisfied()); + + WebstorePrivateApi::SetTestingBrowserSignin(NULL); WebstorePrivateApi::SetTestingProfileSyncService(NULL); } + void RunLoginTest(const std::string& relative_path, + const std::string& initial_login, + bool login_succeeds, + const std::string& login_result) { + RunLoginTestImpl(true, + relative_path, + initial_login, + login_succeeds, + login_result); + RunLoginTestImpl(false, + relative_path, + initial_login, + login_succeeds, + login_result); + } + protected: std::string test_url_base_; }; @@ -110,10 +184,13 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateBrowserTest, BrowserLogin) { host_resolver()->AddRule(kTestUrlHostname, "127.0.0.1"); ASSERT_TRUE(test_server()->Start()); - RunLoginTest("browser_login/expect_nonempty.html", "foo@bar.com", ""); - RunLoginTest("browser_login/prompt_no_preferred.html", "", ""); - RunLoginTest("browser_login/prompt_preferred.html", "", "foo@bar.com"); - RunLoginTest("browser_login/prompt_already_logged_in_error.html", - "foo@bar.com", - "foo@bar.com"); + RunLoginTest("browser_login/expect_nonempty.html", + "foo@bar.com", false, ""); + + RunLoginTest("browser_login/prompt_no_preferred.html", "", true, ""); + + RunLoginTest("browser_login/prompt_preferred.html", "", true, "foo@bar.com"); + + RunLoginTest("browser_login/prompt_login_fails.html", + "", false, "foo@bar.com"); } diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 33fade0..4227333 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -39,6 +39,7 @@ #include "chrome/browser/extensions/extension_updater.h" #include "chrome/browser/extensions/extension_webnavigation_api.h" #include "chrome/browser/extensions/external_extension_provider.h" +#include "chrome/browser/extensions/external_policy_extension_provider.h" #include "chrome/browser/extensions/external_pref_extension_provider.h" #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/prefs/pref_service.h" @@ -94,7 +95,7 @@ bool ShouldReloadExtensionManifest(const ExtensionInfo& info) { return extension_l10n_util::ShouldRelocalizeManifest(info); } -void GetExplicitOriginsInExtent(Extension* extension, +void GetExplicitOriginsInExtent(const Extension* extension, std::vector<GURL>* origins) { typedef std::vector<URLPattern> PatternList; std::set<GURL> set; @@ -150,6 +151,15 @@ PendingExtensionInfo::PendingExtensionInfo() enable_incognito_on_install(false), install_source(Extension::INVALID) {} + +ExtensionsService::ExtensionRuntimeData::ExtensionRuntimeData() + : background_page_ready(false), + being_upgraded(false) { +} + +ExtensionsService::ExtensionRuntimeData::~ExtensionRuntimeData() { +} + // ExtensionsService. const char* ExtensionsService::kInstallDirectoryName = "Extensions"; @@ -164,7 +174,8 @@ class ExtensionsServiceBackend // |install_directory| is a path where to look for extensions to load. // |load_external_extensions| indicates whether or not backend should load // external extensions listed in JSON file and Windows registry. - ExtensionsServiceBackend(const FilePath& install_directory, + ExtensionsServiceBackend(PrefService* prefs, + const FilePath& install_directory, bool load_external_extensions); // Loads a single extension from |path| where |path| is the top directory of @@ -179,22 +190,20 @@ class ExtensionsServiceBackend // Check externally updated extensions for updates and install if necessary. // Errors are reported through ExtensionErrorReporter. Succcess is not // reported. - void CheckForExternalUpdates(std::set<std::string> ids_to_ignore, + void CheckForExternalUpdates(const std::set<std::string>& ids_to_ignore, scoped_refptr<ExtensionsService> frontend); // For the extension in |version_path| with |id|, check to see if it's an // externally managed extension. If so, tell the frontend to uninstall it. void CheckExternalUninstall(scoped_refptr<ExtensionsService> frontend, - const std::string& id, - Extension::Location location); + const std::string& id); // Clear all ExternalExtensionProviders. void ClearProvidersForTesting(); - // Sets an ExternalExtensionProvider for the service to use during testing. - // |location| specifies what type of provider should be added. - void SetProviderForTesting(Extension::Location location, - ExternalExtensionProvider* test_provider); + // Adds an ExternalExtensionProvider for the service to use during testing. + // Takes ownership of |test_provider|. + void AddProviderForTesting(ExternalExtensionProvider* test_provider); // ExternalExtensionProvider::Visitor implementation. virtual void OnExternalExtensionFileFound(const std::string& id, @@ -203,7 +212,8 @@ class ExtensionsServiceBackend Extension::Location location); virtual void OnExternalExtensionUpdateUrlFound(const std::string& id, - const GURL& update_url); + const GURL& update_url, + Extension::Location location); // Reloads the given extensions from their manifests on disk (instead of what // we have cached in the prefs). @@ -226,23 +236,13 @@ class ExtensionsServiceBackend // Note: We take ownership of |extension|. void OnExtensionUnpacked(const FilePath& crx_path, const FilePath& unpacked_path, - Extension* extension, + const Extension* extension, const std::string expected_id); // Notify the frontend that there was an error loading an extension. void ReportExtensionLoadError(const FilePath& extension_path, const std::string& error); - // Lookup an external extension by |id| by going through all registered - // external extension providers until we find a provider that contains an - // extension that matches. If |version| is not NULL, the extension version - // will be returned (caller is responsible for deleting that pointer). - // |location| can also be null, if not needed. Returns true if extension is - // found, false otherwise. - bool LookupExternalExtension(const std::string& id, - Version** version, - Extension::Location* location); - // This is a naked pointer which is set by each entry point. // The entry point is responsible for ensuring lifetime. ExtensionsService* frontend_; @@ -253,12 +253,12 @@ class ExtensionsServiceBackend // Whether errors result in noisy alerts. bool alert_on_error_; - // A map from external extension type to the external extension provider - // for that type. Because a single provider may handle more than one - // external extension type, more than one key may map to the same object. - typedef std::map<Extension::Location, - linked_ptr<ExternalExtensionProvider> > ProviderMap; - ProviderMap external_extension_providers_; + // A collection of external extension providers. Each provider reads + // a source of external extension information. Examples include the + // windows registry and external_extensions.json. + typedef std::vector<linked_ptr<ExternalExtensionProvider> > + ProviderCollection; + ProviderCollection external_extension_providers_; // Set to true by OnExternalExtensionUpdateUrlFound() when an external // extension URL is found. Used in CheckForExternalUpdates() to see @@ -269,6 +269,7 @@ class ExtensionsServiceBackend }; ExtensionsServiceBackend::ExtensionsServiceBackend( + PrefService* prefs, const FilePath& install_directory, bool load_external_extensions) : frontend_(NULL), @@ -281,18 +282,20 @@ ExtensionsServiceBackend::ExtensionsServiceBackend( // TODO(aa): This ends up doing blocking IO on the UI thread because it reads // pref data in the ctor and that is called on the UI thread. Would be better // to re-read data each time we list external extensions, anyway. - external_extension_providers_[Extension::EXTERNAL_PREF] = + external_extension_providers_.push_back( linked_ptr<ExternalExtensionProvider>( - new ExternalPrefExtensionProvider()); - // EXTERNAL_PREF_DOWNLOAD and EXTERNAL_PREF extensions are handled by the - // same object. - external_extension_providers_[Extension::EXTERNAL_PREF_DOWNLOAD] = - external_extension_providers_[Extension::EXTERNAL_PREF]; + new ExternalPrefExtensionProvider())); #if defined(OS_WIN) - external_extension_providers_[Extension::EXTERNAL_REGISTRY] = + external_extension_providers_.push_back( linked_ptr<ExternalExtensionProvider>( - new ExternalRegistryExtensionProvider()); + new ExternalRegistryExtensionProvider())); #endif + ExternalPolicyExtensionProvider* policy_extension_provider = + new ExternalPolicyExtensionProvider(); + policy_extension_provider->SetPreferences(prefs); + external_extension_providers_.push_back( + linked_ptr<ExternalExtensionProvider>(policy_extension_provider)); + } ExtensionsServiceBackend::~ExtensionsServiceBackend() { @@ -300,6 +303,8 @@ ExtensionsServiceBackend::~ExtensionsServiceBackend() { void ExtensionsServiceBackend::LoadSingleExtension( const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + frontend_ = frontend; // Explicit UI loads are always noisy. @@ -309,11 +314,11 @@ void ExtensionsServiceBackend::LoadSingleExtension( file_util::AbsolutePath(&extension_path); std::string error; - Extension* extension = extension_file_util::LoadExtension( + scoped_refptr<const Extension> extension(extension_file_util::LoadExtension( extension_path, Extension::LOAD, false, // Don't require id - &error); + &error)); if (!extension) { ReportExtensionLoadError(extension_path, error); @@ -330,6 +335,7 @@ void ExtensionsServiceBackend::LoadSingleExtension( void ExtensionsServiceBackend::ReportExtensionLoadError( const FilePath& extension_path, const std::string &error) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod( @@ -338,22 +344,6 @@ void ExtensionsServiceBackend::ReportExtensionLoadError( error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_)); } -bool ExtensionsServiceBackend::LookupExternalExtension( - const std::string& id, Version** version, Extension::Location* location) { - scoped_ptr<Version> extension_version; - for (ProviderMap::const_iterator i = external_extension_providers_.begin(); - i != external_extension_providers_.end(); ++i) { - const ExternalExtensionProvider* provider = i->second.get(); - extension_version.reset(provider->RegisteredVersion(id, location)); - if (extension_version.get()) { - if (version) - *version = extension_version.release(); - return true; - } - } - return false; -} - // Some extensions will autoupdate themselves externally from Chrome. These // are typically part of some larger client application package. To support // these, the extension will register its location in the the preferences file @@ -361,8 +351,10 @@ bool ExtensionsServiceBackend::LookupExternalExtension( // check that location for a .crx file, which it will then install locally if // a new version is available. void ExtensionsServiceBackend::CheckForExternalUpdates( - std::set<std::string> ids_to_ignore, + const std::set<std::string>& ids_to_ignore, scoped_refptr<ExtensionsService> frontend) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // Note that this installation is intentionally silent (since it didn't // go through the front-end). Extensions that are registered in this // way are effectively considered 'pre-bundled', and so implicitly @@ -374,10 +366,10 @@ void ExtensionsServiceBackend::CheckForExternalUpdates( // Ask each external extension provider to give us a call back for each // extension they know about. See OnExternalExtension(File|UpdateUrl)Found. - - for (ProviderMap::const_iterator i = external_extension_providers_.begin(); + ProviderCollection::const_iterator i; + for (i = external_extension_providers_.begin(); i != external_extension_providers_.end(); ++i) { - ExternalExtensionProvider* provider = i->second.get(); + ExternalExtensionProvider* provider = i->get(); provider->VisitRegisteredExtension(this, ids_to_ignore); } @@ -390,21 +382,17 @@ void ExtensionsServiceBackend::CheckForExternalUpdates( } void ExtensionsServiceBackend::CheckExternalUninstall( - scoped_refptr<ExtensionsService> frontend, const std::string& id, - Extension::Location location) { + scoped_refptr<ExtensionsService> frontend, const std::string& id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // Check if the providers know about this extension. - ProviderMap::const_iterator i = external_extension_providers_.find(location); - if (i == external_extension_providers_.end()) { - NOTREACHED() << "CheckExternalUninstall called for non-external extension " - << location; - return; + ProviderCollection::const_iterator i; + for (i = external_extension_providers_.begin(); + i != external_extension_providers_.end(); ++i) { + if (i->get()->HasExtension(id)) + return; // Yup, known extension, don't uninstall. } - scoped_ptr<Version> version; - version.reset(i->second->RegisteredVersion(id, NULL)); - if (version.get()) - return; // Yup, known extension, don't uninstall. - // This is an external extension that we don't have registered. Uninstall. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -416,17 +404,18 @@ void ExtensionsServiceBackend::ClearProvidersForTesting() { external_extension_providers_.clear(); } -void ExtensionsServiceBackend::SetProviderForTesting( - Extension::Location location, +void ExtensionsServiceBackend::AddProviderForTesting( ExternalExtensionProvider* test_provider) { DCHECK(test_provider); - external_extension_providers_[location] = - linked_ptr<ExternalExtensionProvider>(test_provider); + external_extension_providers_.push_back( + linked_ptr<ExternalExtensionProvider>(test_provider)); } void ExtensionsServiceBackend::OnExternalExtensionFileFound( const std::string& id, const Version* version, const FilePath& path, Extension::Location location) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(version); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -437,13 +426,21 @@ void ExtensionsServiceBackend::OnExternalExtensionFileFound( void ExtensionsServiceBackend::OnExternalExtensionUpdateUrlFound( const std::string& id, - const GURL& update_url) { + const GURL& update_url, + Extension::Location location) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + if (frontend_->GetExtensionById(id, true)) { // Already installed. Do not change the update URL that the extension set. return; } - frontend_->AddPendingExtensionFromExternalUpdateUrl(id, update_url); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod( + frontend_, + &ExtensionsService::AddPendingExtensionFromExternalUpdateUrl, + id, update_url, location)); external_extension_added_ |= true; } @@ -451,6 +448,8 @@ void ExtensionsServiceBackend::ReloadExtensionManifests( ExtensionPrefs::ExtensionsInfo* extensions_to_reload, base::TimeTicks start_time, scoped_refptr<ExtensionsService> frontend) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + frontend_ = frontend; for (size_t i = 0; i < extensions_to_reload->size(); ++i) { @@ -460,7 +459,7 @@ void ExtensionsServiceBackend::ReloadExtensionManifests( // We need to reload original manifest in order to localize properly. std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<const Extension> extension(extension_file_util::LoadExtension( info->extension_path, info->extension_location, false, &error)); if (extension.get()) @@ -490,9 +489,9 @@ bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url, return true; } - Extension* download_extension = GetExtensionByWebExtent(download_url); - Extension* referrer_extension = GetExtensionByWebExtent(referrer_url); - Extension* webstore_app = GetWebStoreApp(); + const Extension* download_extension = GetExtensionByWebExtent(download_url); + const Extension* referrer_extension = GetExtensionByWebExtent(referrer_url); + const Extension* webstore_app = GetWebStoreApp(); bool referrer_valid = (referrer_extension == webstore_app); bool download_valid = (download_extension == webstore_app); @@ -547,7 +546,7 @@ bool ExtensionsService::UninstallExtensionHelper( extensions_service->UninstallExtension(extension_id, false); } else { LOG(WARNING) << "Attempted uninstallation of non-existent extension with " - << "extension with id: " << extension_id; + << "id: " << extension_id; return false; } @@ -568,6 +567,8 @@ ExtensionsService::ExtensionsService(Profile* profile, ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)), default_apps_(profile->GetPrefs()), event_routers_initialized_(false) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Figure out if extension installation should be enabled. if (command_line->HasSwitch(switches::kDisableExtensions)) { extensions_enabled_ = false; @@ -594,7 +595,8 @@ ExtensionsService::ExtensionsService(Profile* profile, update_frequency); } - backend_ = new ExtensionsServiceBackend(install_directory_, + backend_ = new ExtensionsServiceBackend(profile->GetPrefs(), + install_directory_, extensions_enabled_); // Use monochrome icons for Omnibox icons. @@ -629,6 +631,8 @@ void ExtensionsService::InitEventRouters() { } void ExtensionsService::Init() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(!ready_); DCHECK_EQ(extensions_.size(), 0u); @@ -667,6 +671,8 @@ namespace { void ExtensionsService::UpdateExtension(const std::string& id, const FilePath& extension_path, const GURL& download_url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + PendingExtensionMap::const_iterator it = pending_extensions_.find(id); bool is_pending_extension = (it != pending_extensions_.end()); @@ -718,7 +724,8 @@ void ExtensionsService::AddPendingExtensionFromSync( } void ExtensionsService::AddPendingExtensionFromExternalUpdateUrl( - const std::string& id, const GURL& update_url) { + const std::string& id, const GURL& update_url, + Extension::Location location) { // Add the extension to this list of extensions to update. const PendingExtensionInfo::ExpectedCrxType kExpectedCrxType = PendingExtensionInfo::UNKNOWN; @@ -736,7 +743,7 @@ void ExtensionsService::AddPendingExtensionFromExternalUpdateUrl( AddPendingExtensionInternal(id, update_url, kExpectedCrxType, kIsFromSync, kInstallSilently, kEnableOnInstall, kEnableIncognitoOnInstall, - Extension::EXTERNAL_PREF_DOWNLOAD); + location); } void ExtensionsService::AddPendingExtensionFromDefaultAppList( @@ -766,6 +773,27 @@ void ExtensionsService::AddPendingExtensionInternal( bool is_from_sync, bool install_silently, bool enable_on_install, bool enable_incognito_on_install, Extension::Location install_source) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // If a non-sync update is pending, a sync request should not + // overwrite it. This is important for external extensions. + // If an external extension download is pending, and the user has + // the extension in their sync profile, the install should set the + // type to be external. An external extension should not be + // rejected if it fails the safty checks for a syncable extension. + // TODO(skerner): Work out other potential overlapping conditions. + // (crbug/61000) + PendingExtensionMap::iterator it = pending_extensions_.find(id); + if (it != pending_extensions_.end()) { + VLOG(1) << "Extension id " << id + << " was entered for update more than once." + << " old is_from_sync = " << it->second.is_from_sync + << " new is_from_sync = " << is_from_sync; + if (!it->second.is_from_sync && is_from_sync) + return; + } + + pending_extensions_[id] = PendingExtensionInfo(update_url, expected_crx_type, is_from_sync, install_silently, enable_on_install, @@ -773,8 +801,9 @@ void ExtensionsService::AddPendingExtensionInternal( } void ExtensionsService::ReloadExtension(const std::string& extension_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); FilePath path; - Extension* current_extension = GetExtensionById(extension_id, false); + const Extension* current_extension = GetExtensionById(extension_id, false); // Disable the extension if it's loaded. It might not be loaded if it crashed. if (current_extension) { @@ -816,7 +845,10 @@ void ExtensionsService::ReloadExtension(const std::string& extension_id) { void ExtensionsService::UninstallExtension(const std::string& extension_id, bool external_uninstall) { - Extension* extension = GetExtensionByIdInternal(extension_id, true, true); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + const Extension* extension = + GetExtensionByIdInternal(extension_id, true, true); // Callers should not send us nonexistent extensions. DCHECK(extension); @@ -870,7 +902,10 @@ void ExtensionsService::ClearExtensionData(const GURL& extension_url) { } void ExtensionsService::EnableExtension(const std::string& extension_id) { - Extension* extension = GetExtensionByIdInternal(extension_id, false, true); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + const Extension* extension = + GetExtensionByIdInternal(extension_id, false, true); if (!extension) { return; } @@ -878,7 +913,7 @@ void ExtensionsService::EnableExtension(const std::string& extension_id) { extension_prefs_->SetExtensionState(extension, Extension::ENABLED); // Move it over to the enabled list. - extensions_.push_back(extension); + extensions_.push_back(make_scoped_refptr(extension)); ExtensionList::iterator iter = std::find(disabled_extensions_.begin(), disabled_extensions_.end(), extension); @@ -892,7 +927,10 @@ void ExtensionsService::EnableExtension(const std::string& extension_id) { } void ExtensionsService::DisableExtension(const std::string& extension_id) { - Extension* extension = GetExtensionByIdInternal(extension_id, true, false); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + const Extension* extension = + GetExtensionByIdInternal(extension_id, true, false); // The extension may have been disabled already. if (!extension) return; @@ -900,7 +938,7 @@ void ExtensionsService::DisableExtension(const std::string& extension_id) { extension_prefs_->SetExtensionState(extension, Extension::DISABLED); // Move it over to the disabled list. - disabled_extensions_.push_back(extension); + disabled_extensions_.push_back(make_scoped_refptr(extension)); ExtensionList::iterator iter = std::find(extensions_.begin(), extensions_.end(), extension); @@ -933,20 +971,19 @@ void ExtensionsService::LoadComponentExtensions() { continue; } - scoped_ptr<Extension> extension(new Extension(it->root_directory)); - extension->set_location(Extension::COMPONENT); - std::string error; - if (!extension->InitFromValue( - *static_cast<DictionaryValue*>(manifest.get()), - true, // require key - &error)) { + scoped_refptr<const Extension> extension(Extension::Create( + it->root_directory, + Extension::COMPONENT, + *static_cast<DictionaryValue*>(manifest.get()), + true, // require key + &error)); + if (!extension.get()) { NOTREACHED() << error; return; } - OnExtensionLoaded(extension.release(), false); // Don't allow privilege - // increase. + OnExtensionLoaded(extension, false); // Don't allow privilege increase. } } @@ -1075,15 +1112,14 @@ void ExtensionsService::ContinueLoadAllExtensions( void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info, bool write_to_prefs) { std::string error; - Extension* extension = NULL; + scoped_refptr<const Extension> extension(NULL); if (!extension_prefs_->IsExtensionAllowedByPolicy(info.extension_id)) { error = errors::kDisabledByPolicy; } else if (info.extension_manifest.get()) { - scoped_ptr<Extension> tmp(new Extension(info.extension_path)); bool require_key = info.extension_location != Extension::LOAD; - tmp->set_location(info.extension_location); - if (tmp->InitFromValue(*info.extension_manifest, require_key, &error)) - extension = tmp.release(); + extension = Extension::Create( + info.extension_path, info.extension_location, *info.extension_manifest, + require_key, &error); } else { error = errors::kManifestUnreadable; } @@ -1108,12 +1144,11 @@ void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info, backend_.get(), &ExtensionsServiceBackend::CheckExternalUninstall, scoped_refptr<ExtensionsService>(this), - info.extension_id, - info.extension_location)); + info.extension_id)); } } -void ExtensionsService::NotifyExtensionLoaded(Extension* extension) { +void ExtensionsService::NotifyExtensionLoaded(const Extension* extension) { // The ChromeURLRequestContexts need to be first to know that the extension // was loaded, otherwise a race can arise where a renderer that is created // for the extension may try to load an extension URL with an extension id @@ -1136,14 +1171,14 @@ void ExtensionsService::NotifyExtensionLoaded(Extension* extension) { NotificationService::current()->Notify( NotificationType::EXTENSION_LOADED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); } -void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) { +void ExtensionsService::NotifyExtensionUnloaded(const Extension* extension) { NotificationService::current()->Notify( NotificationType::EXTENSION_UNLOADED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); if (profile_) { profile_->UnregisterExtensionWithRequestContexts(extension); @@ -1159,7 +1194,7 @@ void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) { } } -void ExtensionsService::GrantProtectedStorage(Extension* extension) { +void ExtensionsService::GrantProtectedStorage(const Extension* extension) { DCHECK(extension->is_app()) << "Only Apps are allowed protected storage."; std::vector<GURL> origins; GetExplicitOriginsInExtent(extension, &origins); @@ -1167,7 +1202,7 @@ void ExtensionsService::GrantProtectedStorage(Extension* extension) { ++protected_storage_map_[origins[i]]; } -void ExtensionsService::RevokeProtectedStorage(Extension* extension) { +void ExtensionsService::RevokeProtectedStorage(const Extension* extension) { DCHECK(extension->is_app()) << "Attempting to revoke protected storage from " << " a non-app extension."; std::vector<GURL> origins; @@ -1180,7 +1215,7 @@ void ExtensionsService::RevokeProtectedStorage(Extension* extension) { } } -void ExtensionsService::GrantUnlimitedStorage(Extension* extension) { +void ExtensionsService::GrantUnlimitedStorage(const Extension* extension) { DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission)); std::vector<GURL> origins; GetExplicitOriginsInExtent(extension, &origins); @@ -1215,7 +1250,7 @@ void ExtensionsService::GrantUnlimitedStorage(Extension* extension) { } } -void ExtensionsService::RevokeUnlimitedStorage(Extension* extension) { +void ExtensionsService::RevokeUnlimitedStorage(const Extension* extension) { DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission)); std::vector<GURL> origins; GetExplicitOriginsInExtent(extension, &origins); @@ -1264,7 +1299,7 @@ void ExtensionsService::UpdateExtensionBlacklist( // Loop current extensions, unload installed extensions. for (ExtensionList::const_iterator iter = extensions_.begin(); iter != extensions_.end(); ++iter) { - Extension* extension = (*iter); + const Extension* extension = (*iter); if (blacklist_set.find(extension->id()) != blacklist_set.end()) { to_be_removed.push_back(extension->id()); } @@ -1288,7 +1323,7 @@ void ExtensionsService::CheckAdminBlacklist() { // Loop through extensions list, unload installed extensions. for (ExtensionList::const_iterator iter = extensions_.begin(); iter != extensions_.end(); ++iter) { - Extension* extension = (*iter); + const Extension* extension = (*iter); if (!extension_prefs_->IsExtensionAllowedByPolicy(extension->id())) to_be_removed.push_back(extension->id()); } @@ -1309,7 +1344,7 @@ bool ExtensionsService::IsIncognitoEnabled(const Extension* extension) { return extension_prefs_->IsIncognitoEnabled(extension->id()); } -void ExtensionsService::SetIsIncognitoEnabled(Extension* extension, +void ExtensionsService::SetIsIncognitoEnabled(const Extension* extension, bool enabled) { extension_prefs_->SetIsIncognitoEnabled(extension->id(), enabled); @@ -1323,18 +1358,26 @@ void ExtensionsService::SetIsIncognitoEnabled(Extension* extension, } } +bool ExtensionsService::CanCrossIncognito(const Extension* extension) { + // We allow the extension to see events and data from another profile iff it + // uses "spanning" behavior and it has incognito access. "split" mode + // extensions only see events for a matching profile. + return IsIncognitoEnabled(extension) && !extension->incognito_split_mode(); +} + bool ExtensionsService::AllowFileAccess(const Extension* extension) { return (CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableExtensionsFileAccessCheck) || extension_prefs_->AllowFileAccess(extension->id())); } -void ExtensionsService::SetAllowFileAccess(Extension* extension, bool allow) { +void ExtensionsService::SetAllowFileAccess(const Extension* extension, + bool allow) { extension_prefs_->SetAllowFileAccess(extension->id(), allow); NotificationService::current()->Notify( NotificationType::EXTENSION_USER_SCRIPTS_UPDATED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); } void ExtensionsService::CheckForExternalUpdates() { @@ -1352,7 +1395,7 @@ void ExtensionsService::CheckForExternalUpdates() { void ExtensionsService::UnloadExtension(const std::string& extension_id) { // Make sure the extension gets deleted after we return from this function. - scoped_ptr<Extension> extension( + scoped_refptr<const Extension> extension( GetExtensionByIdInternal(extension_id, true, true)); // Callers should not send us nonexistent extensions. @@ -1365,6 +1408,9 @@ void ExtensionsService::UnloadExtension(const std::string& extension_id) { // Clean up if the extension is meant to be enabled after a reload. disabled_extension_paths_.erase(extension->id()); + // Clean up runtime data. + extension_runtime_data_.erase(extension_id); + ExtensionDOMUI::UnregisterChromeURLOverrides(profile_, extension->GetChromeURLOverrides()); @@ -1376,7 +1422,7 @@ void ExtensionsService::UnloadExtension(const std::string& extension_id) { NotificationService::current()->Notify( NotificationType::EXTENSION_UNLOADED_DISABLED, Source<Profile>(profile_), - Details<Extension>(extension.get())); + Details<const Extension>(extension.get())); return; } @@ -1390,12 +1436,9 @@ void ExtensionsService::UnloadExtension(const std::string& extension_id) { } void ExtensionsService::UnloadAllExtensions() { - STLDeleteContainerPointers(extensions_.begin(), extensions_.end()); extensions_.clear(); - - STLDeleteContainerPointers(disabled_extensions_.begin(), - disabled_extensions_.end()); disabled_extensions_.clear(); + extension_runtime_data_.clear(); // TODO(erikkay) should there be a notification for this? We can't use // EXTENSION_UNLOADED since that implies that the extension has been disabled @@ -1436,10 +1479,10 @@ void ExtensionsService::OnLoadedInstalledExtensions() { NotificationService::NoDetails()); } -void ExtensionsService::OnExtensionLoaded(Extension* extension, +void ExtensionsService::OnExtensionLoaded(const Extension* extension, bool allow_privilege_increase) { // Ensure extension is deleted unless we transfer ownership. - scoped_ptr<Extension> scoped_extension(extension); + scoped_refptr<const Extension> scoped_extension(extension); // The extension is now loaded, remove its data from unloaded extension map. unloaded_extension_paths_.erase(extension->id()); @@ -1455,7 +1498,8 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, extension->location() == Extension::LOAD || extension->location() == Extension::COMPONENT || Extension::IsExternalLocation(extension->location())) { - Extension* old = GetExtensionByIdInternal(extension->id(), true, true); + const Extension* old = GetExtensionByIdInternal(extension->id(), + true, true); if (old) { // CrxInstaller should have guaranteed that we aren't downgrading. CHECK(extension->version()->CompareTo(*(old->version())) >= 0); @@ -1467,8 +1511,8 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, // Extensions get upgraded if silent upgrades are allowed, otherwise // they get disabled. if (allow_silent_upgrade) { - old->set_being_upgraded(true); - extension->set_being_upgraded(true); + SetBeingUpgraded(old, true); + SetBeingUpgraded(extension, true); } // To upgrade an extension in place, unload the old one and @@ -1486,7 +1530,7 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, switch (extension_prefs_->GetExtensionState(extension->id())) { case Extension::ENABLED: - extensions_.push_back(scoped_extension.release()); + extensions_.push_back(scoped_extension); NotifyExtensionLoaded(extension); @@ -1494,11 +1538,11 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, extension->GetChromeURLOverrides()); break; case Extension::DISABLED: - disabled_extensions_.push_back(scoped_extension.release()); + disabled_extensions_.push_back(scoped_extension); NotificationService::current()->Notify( NotificationType::EXTENSION_UPDATE_DISABLED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); break; default: NOTREACHED(); @@ -1506,7 +1550,7 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, } } - extension->set_being_upgraded(false); + SetBeingUpgraded(extension, false); UpdateActiveExtensionsInCrashReporter(); @@ -1524,17 +1568,20 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, void ExtensionsService::UpdateActiveExtensionsInCrashReporter() { std::set<std::string> extension_ids; for (size_t i = 0; i < extensions_.size(); ++i) { - if (!extensions_[i]->is_theme()) + if (!extensions_[i]->is_theme() && + extensions_[i]->location() != Extension::COMPONENT) extension_ids.insert(extensions_[i]->id()); } child_process_logging::SetActiveExtensions(extension_ids); } -void ExtensionsService::OnExtensionInstalled(Extension* extension, +void ExtensionsService::OnExtensionInstalled(const Extension* extension, bool allow_privilege_increase) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Ensure extension is deleted unless we transfer ownership. - scoped_ptr<Extension> scoped_extension(extension); + scoped_refptr<const Extension> scoped_extension(extension); Extension::State initial_state = Extension::DISABLED; bool initial_enable_incognito = false; PendingExtensionMap::iterator it = @@ -1656,12 +1703,12 @@ void ExtensionsService::OnExtensionInstalled(Extension* extension, NotificationService::current()->Notify( NotificationType::THEME_INSTALLED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); } else { NotificationService::current()->Notify( NotificationType::EXTENSION_INSTALLED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); } if (extension->is_app()) { @@ -1671,12 +1718,11 @@ void ExtensionsService::OnExtensionInstalled(Extension* extension, } // Transfer ownership of |extension| to OnExtensionLoaded. - OnExtensionLoaded(scoped_extension.release(), allow_privilege_increase); + OnExtensionLoaded(scoped_extension, allow_privilege_increase); } -Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id, - bool include_enabled, - bool include_disabled) { +const Extension* ExtensionsService::GetExtensionByIdInternal( + const std::string& id, bool include_enabled, bool include_disabled) { std::string lowercase_id = StringToLowerASCII(id); if (include_enabled) { for (ExtensionList::const_iterator iter = extensions_.begin(); @@ -1695,16 +1741,16 @@ Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id, return NULL; } -Extension* ExtensionsService::GetWebStoreApp() { +const Extension* ExtensionsService::GetWebStoreApp() { return GetExtensionById(extension_misc::kWebStoreAppId, false); } -Extension* ExtensionsService::GetExtensionByURL(const GURL& url) { +const Extension* ExtensionsService::GetExtensionByURL(const GURL& url) { return url.scheme() != chrome::kExtensionScheme ? NULL : GetExtensionById(url.host(), false); } -Extension* ExtensionsService::GetExtensionByWebExtent(const GURL& url) { +const Extension* ExtensionsService::GetExtensionByWebExtent(const GURL& url) { for (size_t i = 0; i < extensions_.size(); ++i) { if (extensions_[i]->web_extent().ContainsURL(url)) return extensions_[i]; @@ -1718,11 +1764,11 @@ bool ExtensionsService::ExtensionBindingsAllowed(const GURL& url) { return true; // Allow bindings for all component, hosted apps. - Extension* extension = GetExtensionByWebExtent(url); + const Extension* extension = GetExtensionByWebExtent(url); return (extension && extension->location() == Extension::COMPONENT); } -Extension* ExtensionsService::GetExtensionByOverlappingWebExtent( +const Extension* ExtensionsService::GetExtensionByOverlappingWebExtent( const ExtensionExtent& extent) { for (size_t i = 0; i < extensions_.size(); ++i) { if (extensions_[i]->web_extent().OverlapsWith(extent)) @@ -1749,13 +1795,13 @@ void ExtensionsService::ClearProvidersForTesting() { backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting)); } -void ExtensionsService::SetProviderForTesting( - Extension::Location location, ExternalExtensionProvider* test_provider) { +void ExtensionsService::AddProviderForTesting( + ExternalExtensionProvider* test_provider) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod( - backend_.get(), &ExtensionsServiceBackend::SetProviderForTesting, - location, test_provider)); + backend_.get(), &ExtensionsServiceBackend::AddProviderForTesting, + test_provider)); } void ExtensionsService::OnExternalExtensionFileFound( @@ -1763,10 +1809,12 @@ void ExtensionsService::OnExternalExtensionFileFound( const std::string& version, const FilePath& path, Extension::Location location) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Before even bothering to unpack, check and see if we already have this // version. This is important because these extensions are going to get // installed on every startup. - Extension* existing = GetExtensionById(id, true); + const Extension* existing = GetExtensionById(id, true); scoped_ptr<Version> other(Version::GetVersionFromString(version)); if (existing) { switch (existing->version()->CompareTo(*other)) { @@ -1883,3 +1931,26 @@ ExtensionIdSet ExtensionsService::GetAppIds() const { return result; } + +bool ExtensionsService::IsBackgroundPageReady(const Extension* extension) { + return (extension->background_url().is_empty() || + extension_runtime_data_[extension->id()].background_page_ready); +} + +void ExtensionsService::SetBackgroundPageReady(const Extension* extension) { + DCHECK(!extension->background_url().is_empty()); + extension_runtime_data_[extension->id()].background_page_ready = true; + NotificationService::current()->Notify( + NotificationType::EXTENSION_BACKGROUND_PAGE_READY, + Source<const Extension>(extension), + NotificationService::NoDetails()); +} + +bool ExtensionsService::IsBeingUpgraded(const Extension* extension) { + return extension_runtime_data_[extension->id()].being_upgraded; +} + +void ExtensionsService::SetBeingUpgraded(const Extension* extension, + bool value) { + extension_runtime_data_[extension->id()].being_upgraded = value; +} diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h index bf398de..d3fd80b 100644 --- a/chrome/browser/extensions/extensions_service.h +++ b/chrome/browser/extensions/extensions_service.h @@ -89,8 +89,8 @@ class ExtensionUpdateService { virtual const PendingExtensionMap& pending_extensions() const = 0; virtual void UpdateExtension(const std::string& id, const FilePath& path, const GURL& download_url) = 0; - virtual Extension* GetExtensionById(const std::string& id, - bool include_disabled) = 0; + virtual const Extension* GetExtensionById(const std::string& id, + bool include_disabled) = 0; virtual void UpdateExtensionBlacklist( const std::vector<std::string>& blacklist) = 0; virtual void CheckAdminBlacklist() = 0; @@ -179,11 +179,26 @@ class ExtensionsService // Whether this extension can run in an incognito window. bool IsIncognitoEnabled(const Extension* extension); - void SetIsIncognitoEnabled(Extension* extension, bool enabled); + void SetIsIncognitoEnabled(const Extension* extension, bool enabled); + + // Returns true if the given extension can see events and data from another + // sub-profile (incognito to original profile, or vice versa). + bool CanCrossIncognito(const Extension* extension); // Whether this extension can inject scripts into pages with file URLs. bool AllowFileAccess(const Extension* extension); - void SetAllowFileAccess(Extension* extension, bool allow); + void SetAllowFileAccess(const Extension* extension, bool allow); + + // Whether the background page, if any, is ready. We don't load other + // components until then. If there is no background page, we consider it to + // be ready. + bool IsBackgroundPageReady(const Extension* extension); + void SetBackgroundPageReady(const Extension* extension); + + // Getter and setter for the flag that specifies whether the extension is + // being upgraded. + bool IsBeingUpgraded(const Extension* extension); + void SetBeingUpgraded(const Extension* extension, bool value); // Initialize and start all installed extensions. void Init(); @@ -192,7 +207,8 @@ class ExtensionsService void InitEventRouters(); // Look up an extension by ID. - Extension* GetExtensionById(const std::string& id, bool include_disabled) { + const Extension* GetExtensionById(const std::string& id, + bool include_disabled) { return GetExtensionByIdInternal(id, true, include_disabled); } @@ -229,7 +245,8 @@ class ExtensionsService // Given an extension id and an update URL, schedule the extension // to be fetched, installed, and activated. void AddPendingExtensionFromExternalUpdateUrl(const std::string& id, - const GURL& update_url); + const GURL& update_url, + Extension::Location location); // Like the above. Always installed silently, and defaults update url // from extension id. @@ -285,18 +302,19 @@ class ExtensionsService void GarbageCollectExtensions(); // The App that represents the web store. - Extension* GetWebStoreApp(); + const Extension* GetWebStoreApp(); // Lookup an extension by |url|. - Extension* GetExtensionByURL(const GURL& url); + const Extension* GetExtensionByURL(const GURL& url); // If there is an extension for the specified url it is returned. Otherwise // returns the extension whose web extent contains |url|. - Extension* GetExtensionByWebExtent(const GURL& url); + const Extension* GetExtensionByWebExtent(const GURL& url); // Returns an extension that contains any URL that overlaps with the given // extent, if one exists. - Extension* GetExtensionByOverlappingWebExtent(const ExtensionExtent& extent); + const Extension* GetExtensionByOverlappingWebExtent( + const ExtensionExtent& extent); // Returns true if |url| should get extension api bindings and be permitted // to make api calls. Note that this is independent of what extension @@ -314,19 +332,18 @@ class ExtensionsService void ClearProvidersForTesting(); // Sets an ExternalExtensionProvider for the service to use during testing. - // |location| specifies what type of provider should be added. - void SetProviderForTesting(Extension::Location location, - ExternalExtensionProvider* test_provider); + // Takes ownership of |test_provider|. + void AddProviderForTesting(ExternalExtensionProvider* test_provider); // Called when the initial extensions load has completed. virtual void OnLoadedInstalledExtensions(); // Called when an extension has been loaded. - void OnExtensionLoaded(Extension* extension, + void OnExtensionLoaded(const Extension* extension, bool allow_privilege_increase); // Called by the backend when an extension has been installed. - void OnExtensionInstalled(Extension* extension, + void OnExtensionInstalled(const Extension* extension, bool allow_privilege_increase); // Called by the backend when an external extension is found. @@ -403,18 +420,33 @@ class ExtensionsService ExtensionIdSet GetAppIds() const; private: - virtual ~ExtensionsService(); friend class BrowserThread; friend class DeleteTask<ExtensionsService>; + // Contains Extension data that can change during the life of the process, + // but does not persist across restarts. + struct ExtensionRuntimeData { + // True if the background page is ready. + bool background_page_ready; + + // True while the extension is being upgraded. + bool being_upgraded; + + ExtensionRuntimeData(); + ~ExtensionRuntimeData(); + }; + typedef std::map<std::string, ExtensionRuntimeData> ExtensionRuntimeDataMap; + + virtual ~ExtensionsService(); + // Clear all persistent data that may have been stored by the extension. void ClearExtensionData(const GURL& extension_url); // Look up an extension by ID, optionally including either or both of enabled // and disabled extensions. - Extension* GetExtensionByIdInternal(const std::string& id, - bool include_enabled, - bool include_disabled); + const Extension* GetExtensionByIdInternal(const std::string& id, + bool include_enabled, + bool include_disabled); // Like AddPendingExtension() but assumes an extension with the same // id is not already installed. @@ -426,10 +458,10 @@ class ExtensionsService Extension::Location install_source); // Handles sending notification that |extension| was loaded. - void NotifyExtensionLoaded(Extension* extension); + void NotifyExtensionLoaded(const Extension* extension); // Handles sending notification that |extension| was unloaded. - void NotifyExtensionUnloaded(Extension* extension); + void NotifyExtensionUnloaded(const Extension* extension); // Helper that updates the active extension list used for crash reporting. void UpdateActiveExtensionsInCrashReporter(); @@ -438,10 +470,10 @@ class ExtensionsService void LoadInstalledExtension(const ExtensionInfo& info, bool write_to_prefs); // Helper methods to configure the storage services accordingly. - void GrantProtectedStorage(Extension* extension); - void RevokeProtectedStorage(Extension* extension); - void GrantUnlimitedStorage(Extension* extension); - void RevokeUnlimitedStorage(Extension* extension); + void GrantProtectedStorage(const Extension* extension); + void RevokeProtectedStorage(const Extension* extension); + void GrantUnlimitedStorage(const Extension* extension); + void RevokeUnlimitedStorage(const Extension* extension); // The profile this ExtensionsService is part of. Profile* profile_; @@ -458,6 +490,9 @@ class ExtensionsService // The set of pending extensions. PendingExtensionMap pending_extensions_; + // The map of extension IDs to their runtime data. + ExtensionRuntimeDataMap extension_runtime_data_; + // The full path to the directory where extensions are installed. FilePath install_directory_; diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc index df29995..fa8f341 100644 --- a/chrome/browser/extensions/extensions_service_unittest.cc +++ b/chrome/browser/extensions/extensions_service_unittest.cc @@ -103,7 +103,7 @@ static std::vector<std::string> GetErrors() { class MockExtensionProvider : public ExternalExtensionProvider { public: explicit MockExtensionProvider(Extension::Location location) - : location_(location) {} + : location_(location), visit_count_(0) {} virtual ~MockExtensionProvider() {} void UpdateOrAddExtension(const std::string& id, @@ -119,6 +119,7 @@ class MockExtensionProvider : public ExternalExtensionProvider { // ExternalExtensionProvider implementation: virtual void VisitRegisteredExtension( Visitor* visitor, const std::set<std::string>& ids_to_ignore) const { + visit_count_++; for (DataMap::const_iterator i = extension_map_.begin(); i != extension_map_.end(); ++i) { if (ids_to_ignore.find(i->first) != ids_to_ignore.end()) @@ -131,15 +132,28 @@ class MockExtensionProvider : public ExternalExtensionProvider { } } - virtual Version* RegisteredVersion(const std::string& id, - Extension::Location* location) const { + virtual bool HasExtension(const std::string& id) const { + return extension_map_.find(id) != extension_map_.end(); + } + + virtual bool GetExtensionDetails(const std::string& id, + Extension::Location* location, + scoped_ptr<Version>* version) const { DataMap::const_iterator it = extension_map_.find(id); if (it == extension_map_.end()) - return NULL; + return false; + + if (version) + version->reset(Version::GetVersionFromString(it->second.first)); if (location) *location = location_; - return Version::GetVersionFromString(it->second.first); + + return true; + } + int visit_count() const { return visit_count_; } + void set_visit_count(int visit_count) { + visit_count_ = visit_count; } private: @@ -147,6 +161,12 @@ class MockExtensionProvider : public ExternalExtensionProvider { DataMap extension_map_; Extension::Location location_; + // visit_count_ tracks the number of calls to VisitRegisteredExtension(). + // Mutable because it must be incremented on each call to + // VisitRegisteredExtension(), which must be a const method to inherit + // from the class being mocked. + mutable int visit_count_; + DISALLOW_COPY_AND_ASSIGN(MockExtensionProvider); }; @@ -197,10 +217,18 @@ class MockProviderVisitor : public ExternalExtensionProvider::Visitor { << "Got back ID (" << id.c_str() << ") we weren't expecting"; if (pref) { + EXPECT_TRUE(provider_->HasExtension(id)); + // Ask provider if the extension we got back is registered. Extension::Location location = Extension::INVALID; - scoped_ptr<Version> v1(provider_->RegisteredVersion(id, NULL)); - scoped_ptr<Version> v2(provider_->RegisteredVersion(id, &location)); + scoped_ptr<Version> v1; + FilePath crx_path; + + EXPECT_TRUE(provider_->GetExtensionDetails(id, NULL, &v1)); + EXPECT_STREQ(version->GetString().c_str(), v1->GetString().c_str()); + + scoped_ptr<Version> v2; + EXPECT_TRUE(provider_->GetExtensionDetails(id, &location, &v2)); EXPECT_STREQ(version->GetString().c_str(), v1->GetString().c_str()); EXPECT_STREQ(version->GetString().c_str(), v2->GetString().c_str()); EXPECT_EQ(Extension::EXTERNAL_PREF, location); @@ -210,8 +238,9 @@ class MockProviderVisitor : public ExternalExtensionProvider::Visitor { } } - virtual void OnExternalExtensionUpdateUrlFound(const std::string& id, - const GURL& update_url) { + virtual void OnExternalExtensionUpdateUrlFound( + const std::string& id, const GURL& update_url, + Extension::Location location) { ++ids_found_; DictionaryValue* pref; // This tests is to make sure that the provider only notifies us of the @@ -219,8 +248,18 @@ class MockProviderVisitor : public ExternalExtensionProvider::Visitor { // dictionary then something is wrong. EXPECT_TRUE(prefs_->GetDictionary(id, &pref)) << L"Got back ID (" << id.c_str() << ") we weren't expecting"; + EXPECT_EQ(Extension::EXTERNAL_PREF_DOWNLOAD, location); if (pref) { + EXPECT_TRUE(provider_->HasExtension(id)); + + // External extensions with update URLs do not have versions. + scoped_ptr<Version> v1; + Extension::Location location1 = Extension::INVALID; + EXPECT_TRUE(provider_->GetExtensionDetails(id, &location1, &v1)); + EXPECT_FALSE(v1.get()); + EXPECT_EQ(Extension::EXTERNAL_PREF_DOWNLOAD, location1); + // Remove it so we won't count it again. prefs_->Remove(id, NULL); } @@ -303,12 +342,6 @@ void ExtensionsServiceTestBase::InitializeExtensionsService( profile_.reset(profile); - // TODO(scherkus): Remove this when we no longer need to have Talk - // component extension state as a preference http://crbug.com/56429 - DictionaryValue* dict = - profile->GetPrefs()->GetMutableDictionary("extensions.settings"); - dict->Remove("ggnioahjipcehijkhpdjekioddnjoben", NULL); - service_ = profile->CreateExtensionsService( CommandLine::ForCurrentProcess(), extensions_install_dir); @@ -386,8 +419,8 @@ class ExtensionsServiceTest const NotificationDetails& details) { switch (type.value) { case NotificationType::EXTENSION_LOADED: { - Extension* extension = Details<Extension>(details).ptr(); - loaded_.push_back(extension); + const Extension* extension = Details<const Extension>(details).ptr(); + loaded_.push_back(make_scoped_refptr(extension)); // The tests rely on the errors being in a certain order, which can vary // depending on how filesystem iteration works. std::stable_sort(loaded_.begin(), loaded_.end(), ExtensionsOrder()); @@ -395,7 +428,7 @@ class ExtensionsServiceTest } case NotificationType::EXTENSION_UNLOADED: { - Extension* e = Details<Extension>(details).ptr(); + const Extension* e = Details<const Extension>(details).ptr(); unloaded_id_ = e->id(); ExtensionList::iterator i = std::find(loaded_.begin(), loaded_.end(), e); @@ -408,7 +441,7 @@ class ExtensionsServiceTest } case NotificationType::EXTENSION_INSTALLED: case NotificationType::THEME_INSTALLED: - installed_ = Details<Extension>(details).ptr(); + installed_ = Details<const Extension>(details).ptr(); break; default: @@ -416,9 +449,8 @@ class ExtensionsServiceTest } } - void SetMockExternalProvider(Extension::Location location, - ExternalExtensionProvider* provider) { - service_->SetProviderForTesting(location, provider); + void AddMockExternalProvider(ExternalExtensionProvider* provider) { + service_->AddProviderForTesting(provider); } protected: @@ -644,7 +676,7 @@ class ExtensionsServiceTest protected: ExtensionList loaded_; std::string unloaded_id_; - Extension* installed_; + const Extension* installed_; private: NotificationRegistrar registrar_; @@ -742,7 +774,7 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectorySuccess) { ValidateIntegerPref(good2, "state", Extension::ENABLED); ValidateIntegerPref(good2, "location", Extension::INTERNAL); - Extension* extension = loaded_[0]; + const Extension* extension = loaded_[0]; const UserScriptList& scripts = extension->content_scripts(); ASSERT_EQ(2u, scripts.size()); EXPECT_EQ(3u, scripts[0].url_patterns().size()); @@ -1223,9 +1255,8 @@ TEST_F(ExtensionsServiceTest, InstallApps) { ValidatePrefKeyCount(++pref_count); // A third app whose extent overlaps the first. Should fail. - // TODO(aa): bring this back when overlap is fixed. http://crbug.com/47445. - // PackAndInstallExtension(extensions_path.AppendASCII("app3"), false); - // ValidatePrefKeyCount(pref_count); + PackAndInstallExtension(extensions_path.AppendASCII("app3"), false); + ValidatePrefKeyCount(pref_count); } TEST_F(ExtensionsServiceTest, InstallAppsWithUnlimtedStorage) { @@ -1243,7 +1274,7 @@ TEST_F(ExtensionsServiceTest, InstallAppsWithUnlimtedStorage) { PackAndInstallExtension(extensions_path.AppendASCII("app1"), true); ValidatePrefKeyCount(++pref_count); ASSERT_EQ(1u, service_->extensions()->size()); - Extension* extension = service_->extensions()->at(0); + const Extension* extension = service_->extensions()->at(0); const std::string id1 = extension->id(); EXPECT_TRUE(extension->HasApiPermission( Extension::kUnlimitedStoragePermission)); @@ -1301,7 +1332,7 @@ TEST_F(ExtensionsServiceTest, InstallAppsAndCheckStorageProtection) { PackAndInstallExtension(extensions_path.AppendASCII("app1"), true); ValidatePrefKeyCount(++pref_count); ASSERT_EQ(1u, service_->extensions()->size()); - Extension* extension = service_->extensions()->at(0); + const Extension* extension = service_->extensions()->at(0); EXPECT_TRUE(extension->is_app()); const std::string id1 = extension->id(); EXPECT_FALSE(service_->protected_storage_map_.empty()); @@ -1430,7 +1461,7 @@ TEST_F(ExtensionsServiceTest, UpdateExtension) { FilePath path = extensions_path.AppendASCII("good.crx"); InstallExtension(path, true); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); ASSERT_EQ("1.0.0.0", good->VersionString()); ASSERT_EQ(good_crx, good->id()); @@ -1465,7 +1496,7 @@ TEST_F(ExtensionsServiceTest, UpdateWillNotDowngrade) { FilePath path = extensions_path.AppendASCII("good2.crx"); InstallExtension(path, true); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); ASSERT_EQ("1.0.0.1", good->VersionString()); ASSERT_EQ(good_crx, good->id()); @@ -1485,7 +1516,7 @@ TEST_F(ExtensionsServiceTest, UpdateToSameVersionIsNoop) { FilePath path = extensions_path.AppendASCII("good.crx"); InstallExtension(path, true); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); ASSERT_EQ(good_crx, good->id()); UpdateExtension(good_crx, path, FAILED_SILENTLY); } @@ -1500,7 +1531,7 @@ TEST_F(ExtensionsServiceTest, UpdateExtensionPreservesState) { FilePath path = extensions_path.AppendASCII("good.crx"); InstallExtension(path, true); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); ASSERT_EQ("1.0.0.0", good->VersionString()); ASSERT_EQ(good_crx, good->id()); @@ -1512,7 +1543,7 @@ TEST_F(ExtensionsServiceTest, UpdateExtensionPreservesState) { path = extensions_path.AppendASCII("good2.crx"); UpdateExtension(good_crx, path, INSTALLED); ASSERT_EQ(1u, service_->disabled_extensions()->size()); - Extension* good2 = service_->disabled_extensions()->at(0); + const Extension* good2 = service_->disabled_extensions()->at(0); ASSERT_EQ("1.0.0.1", good2->version()->GetString()); EXPECT_TRUE(service_->IsIncognitoEnabled(good2)); } @@ -1570,7 +1601,7 @@ TEST_F(ExtensionsServiceTest, UpdatePendingExtension) { EXPECT_FALSE(ContainsKey(service_->pending_extensions(), kGoodId)); - Extension* extension = service_->GetExtensionById(kGoodId, true); + const Extension* extension = service_->GetExtensionById(kGoodId, true); ASSERT_TRUE(extension); bool enabled = service_->GetExtensionById(kGoodId, false); @@ -1597,7 +1628,7 @@ TEST_F(ExtensionsServiceTest, UpdatePendingTheme) { EXPECT_FALSE(ContainsKey(service_->pending_extensions(), theme_crx)); - Extension* extension = service_->GetExtensionById(theme_crx, true); + const Extension* extension = service_->GetExtensionById(theme_crx, true); ASSERT_TRUE(extension); EXPECT_EQ(Extension::ENABLED, @@ -1610,7 +1641,8 @@ TEST_F(ExtensionsServiceTest, UpdatePendingTheme) { // or not. TEST_F(ExtensionsServiceTest, UpdatePendingExternalCrx) { InitializeEmptyExtensionsService(); - service_->AddPendingExtensionFromExternalUpdateUrl(theme_crx, GURL()); + service_->AddPendingExtensionFromExternalUpdateUrl( + theme_crx, GURL(), Extension::EXTERNAL_PREF_DOWNLOAD); EXPECT_TRUE(ContainsKey(service_->pending_extensions(), theme_crx)); @@ -1622,7 +1654,7 @@ TEST_F(ExtensionsServiceTest, UpdatePendingExternalCrx) { EXPECT_FALSE(ContainsKey(service_->pending_extensions(), theme_crx)); - Extension* extension = service_->GetExtensionById(theme_crx, true); + const Extension* extension = service_->GetExtensionById(theme_crx, true); ASSERT_TRUE(extension); EXPECT_EQ(Extension::ENABLED, @@ -1630,6 +1662,45 @@ TEST_F(ExtensionsServiceTest, UpdatePendingExternalCrx) { EXPECT_FALSE(service_->IsIncognitoEnabled(extension)); } +// Test updating a pending CRX as if the source is an external extension +// with an update URL. The external update should overwrite a sync update, +// but a sync update should not overwrite a non-sync update. +TEST_F(ExtensionsServiceTest, UpdatePendingExternalCrxWinsOverSync) { + InitializeEmptyExtensionsService(); + + // Add a crx to be installed from the update mechanism. + service_->AddPendingExtensionFromSync( + kGoodId, GURL(kGoodUpdateURL), kCrxTypeExtension, + kGoodInstallSilently, kGoodInitialState, + kGoodInitialIncognitoEnabled); + + // Check that there is a pending crx, with is_from_sync set to true. + PendingExtensionMap::const_iterator it; + it = service_->pending_extensions().find(kGoodId); + ASSERT_TRUE(it != service_->pending_extensions().end()); + EXPECT_TRUE(it->second.is_from_sync); + + // Add a crx to be updated, with the same ID, from a non-sync source. + service_->AddPendingExtensionFromExternalUpdateUrl( + kGoodId, GURL(kGoodUpdateURL), Extension::EXTERNAL_PREF_DOWNLOAD); + + // Check that there is a pending crx, with is_from_sync set to false. + it = service_->pending_extensions().find(kGoodId); + ASSERT_TRUE(it != service_->pending_extensions().end()); + EXPECT_FALSE(it->second.is_from_sync); + + // Add a crx to be installed from the update mechanism. + service_->AddPendingExtensionFromSync( + kGoodId, GURL(kGoodUpdateURL), kCrxTypeExtension, + kGoodInstallSilently, kGoodInitialState, + kGoodInitialIncognitoEnabled); + + // Check that the external, non-sync update was not overridden. + it = service_->pending_extensions().find(kGoodId); + ASSERT_TRUE(it != service_->pending_extensions().end()); + EXPECT_FALSE(it->second.is_from_sync); +} + // Updating a theme should fail if the updater is explicitly told that // the CRX is not a theme. TEST_F(ExtensionsServiceTest, UpdatePendingCrxThemeMismatch) { @@ -1649,7 +1720,7 @@ TEST_F(ExtensionsServiceTest, UpdatePendingCrxThemeMismatch) { EXPECT_FALSE(ContainsKey(service_->pending_extensions(), theme_crx)); - Extension* extension = service_->GetExtensionById(theme_crx, true); + const Extension* extension = service_->GetExtensionById(theme_crx, true); ASSERT_FALSE(extension); } @@ -1706,7 +1777,7 @@ TEST_F(ExtensionsServiceTest, UpdatePendingExtensionAlreadyInstalled) { FilePath path = extensions_path.AppendASCII("good.crx"); InstallExtension(path, true); ASSERT_EQ(1u, service_->extensions()->size()); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); EXPECT_FALSE(good->is_theme()); @@ -1761,7 +1832,7 @@ TEST_F(ExtensionsServiceTest, UnloadBlacklistedExtension) { FilePath path = extensions_path.AppendASCII("good.crx"); InstallExtension(path, true); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); EXPECT_EQ(good_crx, good->id()); UpdateExtension(good_crx, path, FAILED_SILENTLY); @@ -2094,7 +2165,7 @@ TEST_F(ExtensionsServiceTest, ClearExtensionData) { path = path.AppendASCII("extensions"); path = path.AppendASCII("good.crx"); InstallExtension(path, true); - Extension* extension = service_->GetExtensionById(good_crx, false); + const Extension* extension = service_->GetExtensionById(good_crx, false); ASSERT_TRUE(extension); GURL ext_url(extension->url()); string16 origin_id = @@ -2227,6 +2298,8 @@ void ExtensionsServiceTest::TestExternalProvider( loop_.RunAllPending(); ASSERT_EQ(0u, loaded_.size()); + provider->set_visit_count(0); + // Register a test extension externally using the mock registry provider. FilePath source_path; ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &source_path)); @@ -2342,6 +2415,8 @@ void ExtensionsServiceTest::TestExternalProvider( loop_.RunAllPending(); ASSERT_EQ(0u, loaded_.size()); ValidatePrefKeyCount(1); + + EXPECT_EQ(5, provider->visit_count()); } // Tests the external installation feature @@ -2354,7 +2429,7 @@ TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) { // Now add providers. Extension system takes ownership of the objects. MockExtensionProvider* reg_provider = new MockExtensionProvider(Extension::EXTERNAL_REGISTRY); - SetMockExternalProvider(Extension::EXTERNAL_REGISTRY, reg_provider); + AddMockExternalProvider(reg_provider); TestExternalProvider(reg_provider, Extension::EXTERNAL_REGISTRY); } #endif @@ -2367,7 +2442,8 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPref) { // Now add providers. Extension system takes ownership of the objects. MockExtensionProvider* pref_provider = new MockExtensionProvider(Extension::EXTERNAL_PREF); - SetMockExternalProvider(Extension::EXTERNAL_PREF, pref_provider); + + AddMockExternalProvider(pref_provider); TestExternalProvider(pref_provider, Extension::EXTERNAL_PREF); } @@ -2376,10 +2452,16 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPrefUpdateUrl) { InitializeEmptyExtensionsService(); set_extensions_enabled(false); - // Now add providers. Extension system takes ownership of the objects. + // TODO(skerner): The mock provider is not a good model of a provider + // that works with update URLs, because it adds file and version info. + // Extend the mock to work with update URLs. This test checks the + // behavior that is common to all external extension visitors. The + // browser test ExtensionManagementTest.ExternalUrlUpdate tests that + // what the visitor does results in an extension being downloaded and + // installed. MockExtensionProvider* pref_provider = new MockExtensionProvider(Extension::EXTERNAL_PREF_DOWNLOAD); - SetMockExternalProvider(Extension::EXTERNAL_PREF_DOWNLOAD, pref_provider); + AddMockExternalProvider(pref_provider); TestExternalProvider(pref_provider, Extension::EXTERNAL_PREF_DOWNLOAD); } diff --git a/chrome/browser/extensions/extensions_startup.cc b/chrome/browser/extensions/extensions_startup.cc index 1286179..9faeac6 100644 --- a/chrome/browser/extensions/extensions_startup.cc +++ b/chrome/browser/extensions/extensions_startup.cc @@ -83,8 +83,8 @@ bool HandlePackExtension(const CommandLine& cmd_line) { // Launch a job to perform the packing on the file thread. PackExtensionLogger pack_client; - scoped_refptr<PackExtensionJob> pack_job = - new PackExtensionJob(&pack_client, src_dir, private_key_path); + scoped_refptr<PackExtensionJob> pack_job( + new PackExtensionJob(&pack_client, src_dir, private_key_path)); pack_job->Start(); // The job will post a notification task to the current thread's message diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc index 586ec6f..d3f6a6c 100644 --- a/chrome/browser/extensions/extensions_ui.cc +++ b/chrome/browser/extensions/extensions_ui.cc @@ -58,8 +58,7 @@ namespace { -bool ShouldShowExtension(Extension* extension) { - +bool ShouldShowExtension(const Extension* extension) { // Don't show themes since this page's UI isn't really useful for themes. if (extension->is_theme()) return false; @@ -407,7 +406,7 @@ void ExtensionsDOMHandler::OnIconsLoaded(DictionaryValue* json) { } ExtensionResource ExtensionsDOMHandler::PickExtensionIcon( - Extension* extension) { + const Extension* extension) { return extension->GetIconResource(Extension::EXTENSION_ICON_MEDIUM, ExtensionIconSet::MATCH_BIGGER); } @@ -459,7 +458,7 @@ void ExtensionsDOMHandler::HandleEnableMessage(const ListValue* args) { if (enable_str == "true") { ExtensionPrefs* prefs = extensions_service_->extension_prefs(); if (prefs->DidExtensionEscalatePermissions(extension_id)) { - Extension* extension = + const Extension* extension = extensions_service_->GetExtensionById(extension_id, true); ShowExtensionDisabledDialog(extensions_service_, dom_ui_->GetProfile(), extension); @@ -476,8 +475,8 @@ void ExtensionsDOMHandler::HandleEnableIncognitoMessage(const ListValue* args) { std::string extension_id, enable_str; CHECK(args->GetString(0, &extension_id)); CHECK(args->GetString(1, &enable_str)); - Extension* extension = extensions_service_->GetExtensionById(extension_id, - true); + const Extension* extension = + extensions_service_->GetExtensionById(extension_id, true); DCHECK(extension); // Flipping the incognito bit will generate unload/load notifications for the @@ -501,15 +500,15 @@ void ExtensionsDOMHandler::HandleAllowFileAccessMessage(const ListValue* args) { std::string extension_id, allow_str; CHECK(args->GetString(0, &extension_id)); CHECK(args->GetString(1, &allow_str)); - Extension* extension = extensions_service_->GetExtensionById(extension_id, - true); + const Extension* extension = + extensions_service_->GetExtensionById(extension_id, true); DCHECK(extension); extensions_service_->SetAllowFileAccess(extension, allow_str == "true"); } void ExtensionsDOMHandler::HandleUninstallMessage(const ListValue* args) { - Extension* extension = GetExtension(args); + const Extension* extension = GetExtension(args); if (!extension) return; @@ -527,7 +526,7 @@ void ExtensionsDOMHandler::InstallUIProceed() { // The extension can be uninstalled in another window while the UI was // showing. Do nothing in that case. - Extension* extension = + const Extension* extension = extensions_service_->GetExtensionById(extension_id_prompting_, true); if (!extension) return; @@ -542,7 +541,7 @@ void ExtensionsDOMHandler::InstallUIAbort() { } void ExtensionsDOMHandler::HandleOptionsMessage(const ListValue* args) { - Extension* extension = GetExtension(args); + const Extension* extension = GetExtension(args); if (!extension || extension->options_url().is_empty()) return; dom_ui_->GetProfile()->GetExtensionProcessManager()->OpenOptionsPage( @@ -703,7 +702,7 @@ void ExtensionsDOMHandler::Observe(NotificationType type, } } -Extension* ExtensionsDOMHandler::GetExtension(const ListValue* args) { +const Extension* ExtensionsDOMHandler::GetExtension(const ListValue* args) { std::string extension_id = WideToASCII(ExtractStringValue(args)); CHECK(!extension_id.empty()); return extensions_service_->GetExtensionById(extension_id, true); @@ -840,13 +839,13 @@ DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue( extension_data->Set("views", views); extension_data->SetBoolean("hasPopupAction", extension->browser_action() || extension->page_action()); - extension_data->SetString("galleryUrl", extension->GalleryUrl().spec()); + extension_data->SetString("homepageUrl", extension->GetHomepageURL().spec()); return extension_data; } std::vector<ExtensionPage> ExtensionsDOMHandler::GetActivePagesForExtension( - Extension* extension) { + const Extension* extension) { std::vector<ExtensionPage> result; // Get the extension process's active views. @@ -872,7 +871,7 @@ std::vector<ExtensionPage> ExtensionsDOMHandler::GetActivePagesForExtension( void ExtensionsDOMHandler::GetActivePagesForExtensionProcess( RenderProcessHost* process, - Extension* extension, + const Extension* extension, std::vector<ExtensionPage> *result) { if (!process) return; diff --git a/chrome/browser/extensions/extensions_ui.h b/chrome/browser/extensions/extensions_ui.h index 5660eaa..c8c6143 100644 --- a/chrome/browser/extensions/extensions_ui.h +++ b/chrome/browser/extensions/extensions_ui.h @@ -178,7 +178,7 @@ class ExtensionsDOMHandler void HandleSelectFilePathMessage(const ListValue* args); // Utility for callbacks that get an extension ID as the sole argument. - Extension* GetExtension(const ListValue* args); + const Extension* GetExtension(const ListValue* args); // Forces a UI update if appropriate after a notification is received. void MaybeUpdateAfterNotification(); @@ -198,15 +198,16 @@ class ExtensionsDOMHandler const NotificationDetails& details); // Helper that lists the current active html pages for an extension. - std::vector<ExtensionPage> GetActivePagesForExtension(Extension* extension); + std::vector<ExtensionPage> GetActivePagesForExtension( + const Extension* extension); void GetActivePagesForExtensionProcess( RenderProcessHost* process, - Extension* extension, + const Extension* extension, std::vector<ExtensionPage> *result); // Returns the best icon to display in the UI for an extension, or an empty // ExtensionResource if no good icon exists. - ExtensionResource PickExtensionIcon(Extension* extension); + ExtensionResource PickExtensionIcon(const Extension* extension); // Loads the extension resources into the json data, then calls OnIconsLoaded. // Takes ownership of |icons|. diff --git a/chrome/browser/extensions/external_extension_provider.h b/chrome/browser/extensions/external_extension_provider.h index eddea62..1d040bd 100644 --- a/chrome/browser/extensions/external_extension_provider.h +++ b/chrome/browser/extensions/external_extension_provider.h @@ -32,7 +32,8 @@ class ExternalExtensionProvider { virtual void OnExternalExtensionUpdateUrlFound( const std::string& id, - const GURL& update_url) = 0; + const GURL& update_url, + Extension::Location location) = 0; protected: virtual ~Visitor() {} @@ -46,11 +47,16 @@ class ExternalExtensionProvider { virtual void VisitRegisteredExtension( Visitor* visitor, const std::set<std::string>& ids_to_ignore) const = 0; - // Gets the version of extension with |id| and its |location|. |location| can - // be NULL. The caller is responsible for cleaning up the Version object - // returned. This function returns NULL if the extension is not found. - virtual Version* RegisteredVersion(const std::string& id, - Extension::Location* location) const = 0; + // Test if this provider has an extension with id |id| registered. + virtual bool HasExtension(const std::string& id) const = 0; + + // Gets details of an extension by its id. Output params will be set only + // if they are not NULL. If an output parameter is not specified by the + // provider type, it will not be changed. + // This function is no longer used outside unit tests. + virtual bool GetExtensionDetails(const std::string& id, + Extension::Location* location, + scoped_ptr<Version>* version) const = 0; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTERNAL_EXTENSION_PROVIDER_H_ diff --git a/chrome/browser/extensions/external_policy_extension_provider.cc b/chrome/browser/extensions/external_policy_extension_provider.cc new file mode 100644 index 0000000..0eabccb --- /dev/null +++ b/chrome/browser/extensions/external_policy_extension_provider.cc @@ -0,0 +1,68 @@ +// 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/extensions/external_policy_extension_provider.h" + +#include "base/values.h" +#include "chrome/common/pref_names.h" +#include "chrome/browser/extensions/stateful_external_extension_provider.h" +#include "chrome/browser/prefs/pref_service.h" + +namespace { + +// Check an extension ID and an URL to be syntactically correct. +bool CheckExtension(std::string id, std::string update_url) { + GURL url(update_url); + if (!url.is_valid()) { + LOG(WARNING) << "Policy specifies invalid update URL for external " + << "extension: " << update_url; + return false; + } + if (!Extension::IdIsValid(id)) { + LOG(WARNING) << "Policy specifies invalid ID for external " + << "extension: " << id; + return false; + } + return true; +} + +} + +ExternalPolicyExtensionProvider::ExternalPolicyExtensionProvider() + : StatefulExternalExtensionProvider(Extension::INVALID, + Extension::EXTERNAL_POLICY_DOWNLOAD) { +} + +ExternalPolicyExtensionProvider::~ExternalPolicyExtensionProvider() { +} + +void ExternalPolicyExtensionProvider::SetPreferences( + PrefService* prefs) { + SetPreferences(prefs->GetList(prefs::kExtensionInstallForceList)); +} + +void ExternalPolicyExtensionProvider::SetPreferences( + const ListValue* forcelist) { + DictionaryValue* result = new DictionaryValue(); + if (forcelist != NULL) { + std::string extension_desc; + for (ListValue::const_iterator it = forcelist->begin(); + it != forcelist->end(); ++it) { + if (!(*it)->GetAsString(&extension_desc)) { + LOG(WARNING) << "Failed to read forcelist string."; + } else { + // Each string item of the list has the following form: + // extension_id_code;extension_update_url + // The update URL might also contain semicolons. + size_t pos = extension_desc.find(';'); + std::string id = extension_desc.substr(0, pos); + std::string update_url = extension_desc.substr(pos+1); + if (CheckExtension(id, update_url)) { + result->SetString(id + ".external_update_url", update_url); + } + } + } + } + prefs_.reset(result); +} diff --git a/chrome/browser/extensions/external_policy_extension_provider.h b/chrome/browser/extensions/external_policy_extension_provider.h new file mode 100644 index 0000000..8f34a34 --- /dev/null +++ b/chrome/browser/extensions/external_policy_extension_provider.h @@ -0,0 +1,35 @@ +// Copyright (c) 2009 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_EXTENSIONS_EXTERNAL_POLICY_EXTENSION_PROVIDER_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTERNAL_POLICY_EXTENSION_PROVIDER_H_ +#pragma once + +#include "chrome/browser/extensions/stateful_external_extension_provider.h" + +class ListValue; +class MockExternalPolicyExtensionProviderVisitor; +class PrefService; + +// A specialization of the ExternalExtensionProvider that uses +// prefs::kExtensionInstallForceList to look up which external extensions are +// registered. +class ExternalPolicyExtensionProvider + : public StatefulExternalExtensionProvider { + public: + explicit ExternalPolicyExtensionProvider(); + virtual ~ExternalPolicyExtensionProvider(); + + // Set the internal list of extensions based on + // prefs::kExtensionInstallForceList. + void SetPreferences(PrefService* prefs); + + private: + friend class MockExternalPolicyExtensionProviderVisitor; + + // Set the internal list of extensions based on |forcelist|. + void SetPreferences(const ListValue* forcelist); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTERNAL_POLICY_EXTENSION_PROVIDER_H_ diff --git a/chrome/browser/extensions/external_policy_extension_provider_unittest.cc b/chrome/browser/extensions/external_policy_extension_provider_unittest.cc new file mode 100644 index 0000000..4fb5117 --- /dev/null +++ b/chrome/browser/extensions/external_policy_extension_provider_unittest.cc @@ -0,0 +1,113 @@ +// 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 <string> + +#include "base/logging.h" +#include "base/values.h" +#include "base/version.h" +#include "chrome/browser/extensions/external_policy_extension_provider.h" +#include "chrome/common/extensions/extension.h" +#include "testing/gtest/include/gtest/gtest.h" + +class ExternalPolicyExtensionProviderTest : public testing::Test { +}; + +class MockExternalPolicyExtensionProviderVisitor + : public ExternalExtensionProvider::Visitor { + public: + MockExternalPolicyExtensionProviderVisitor() { + } + + // Initialize a provider with |policy_forcelist|, and check that it parses + // exactly those extensions, that are specified in |policy_validlist|. + void Visit(ListValue* policy_forcelist, + ListValue* policy_validlist, + const std::set<std::string>& ignore_list) { + provider_.reset(new ExternalPolicyExtensionProvider()); + // Give the list extensions to the provider. + provider_->SetPreferences(policy_forcelist); + + // Extensions will be removed from this list as they visited, + // so it should be emptied by the end. + remaining_extensions = policy_validlist; + provider_->VisitRegisteredExtension(this, ignore_list); + EXPECT_EQ(0u, remaining_extensions->GetSize()); + } + + virtual void OnExternalExtensionFileFound(const std::string& id, + const Version* version, + const FilePath& path, + Extension::Location unused) { + ADD_FAILURE() << "There should be no external extensions from files."; + } + + virtual void OnExternalExtensionUpdateUrlFound( + const std::string& id, const GURL& update_url, + Extension::Location location) { + // Extension has the correct location. + EXPECT_EQ(Extension::EXTERNAL_POLICY_DOWNLOAD, location); + + // Provider returns the correct location when asked. + Extension::Location location1; + scoped_ptr<Version> version1; + provider_->GetExtensionDetails(id, &location1, &version1); + EXPECT_EQ(Extension::EXTERNAL_POLICY_DOWNLOAD, location1); + EXPECT_FALSE(version1.get()); + + // Remove the extension from our list. + StringValue ext_str(id + ";" + update_url.spec()); + EXPECT_NE(-1, remaining_extensions->Remove(ext_str)); + } + + private: + ListValue* remaining_extensions; + + scoped_ptr<ExternalPolicyExtensionProvider> provider_; + + DISALLOW_COPY_AND_ASSIGN(MockExternalPolicyExtensionProviderVisitor); +}; + +TEST_F(ExternalPolicyExtensionProviderTest, PolicyIsParsed) { + ListValue forced_extensions; + forced_extensions.Append(Value::CreateStringValue( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;http://www.example.com/crx?a=5;b=6")); + forced_extensions.Append(Value::CreateStringValue( + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;" + "https://clients2.google.com/service/update2/crx")); + + MockExternalPolicyExtensionProviderVisitor mv; + std::set<std::string> empty; + mv.Visit(&forced_extensions, &forced_extensions, empty); +} + +TEST_F(ExternalPolicyExtensionProviderTest, InvalidPolicyIsNotParsed) { + ListValue forced_extensions, valid_extensions; + StringValue valid( + "cccccccccccccccccccccccccccccccc;http://www.example.com/crx"); + valid_extensions.Append(valid.DeepCopy()); + forced_extensions.Append(valid.DeepCopy()); + // Add invalid strings: + forced_extensions.Append(Value::CreateStringValue("")); + forced_extensions.Append(Value::CreateStringValue(";")); + forced_extensions.Append(Value::CreateStringValue(";;")); + forced_extensions.Append(Value::CreateStringValue( + ";http://www.example.com/crx")); + forced_extensions.Append(Value::CreateStringValue( + ";aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + forced_extensions.Append(Value::CreateStringValue( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;")); + forced_extensions.Append(Value::CreateStringValue( + "http://www.example.com/crx;aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + forced_extensions.Append(Value::CreateStringValue( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;http#//www.example.com/crx")); + forced_extensions.Append(Value::CreateStringValue( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + forced_extensions.Append(Value::CreateStringValue( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahttp#//www.example.com/crx")); + + MockExternalPolicyExtensionProviderVisitor mv; + std::set<std::string> empty; + mv.Visit(&forced_extensions, &valid_extensions, empty); +} diff --git a/chrome/browser/extensions/external_pref_extension_provider.cc b/chrome/browser/extensions/external_pref_extension_provider.cc index 3eaafc9..7580388 100644 --- a/chrome/browser/extensions/external_pref_extension_provider.cc +++ b/chrome/browser/extensions/external_pref_extension_provider.cc @@ -7,20 +7,14 @@ #include "app/app_paths.h" #include "base/file_path.h" #include "base/file_util.h" +#include "base/logging.h" #include "base/path_service.h" -#include "base/string_util.h" -#include "base/utf_string_conversions.h" -#include "base/version.h" +#include "chrome/browser/extensions/stateful_external_extension_provider.h" #include "chrome/common/json_value_serializer.h" -// Constants for keeping track of extension preferences. -const char kLocation[] = "location"; -const char kState[] = "state"; -const char kExternalCrx[] = "external_crx"; -const char kExternalVersion[] = "external_version"; -const char kExternalUpdateUrl[] = "external_update_url"; - -ExternalPrefExtensionProvider::ExternalPrefExtensionProvider() { +ExternalPrefExtensionProvider::ExternalPrefExtensionProvider() + : StatefulExternalExtensionProvider(Extension::EXTERNAL_PREF, + Extension::EXTERNAL_PREF_DOWNLOAD) { FilePath json_file; PathService::Get(app::DIR_EXTERNAL_EXTENSIONS, &json_file); json_file = json_file.Append(FILE_PATH_LITERAL("external_extensions.json")); @@ -42,100 +36,6 @@ void ExternalPrefExtensionProvider::SetPreferencesForTesting( SetPreferences(&serializer); } -void ExternalPrefExtensionProvider::VisitRegisteredExtension( - Visitor* visitor, const std::set<std::string>& ids_to_ignore) const { - for (DictionaryValue::key_iterator i = prefs_->begin_keys(); - i != prefs_->end_keys(); ++i) { - const std::string& extension_id = *i; - if (ids_to_ignore.find(extension_id) != ids_to_ignore.end()) - continue; - - DictionaryValue* extension; - if (!prefs_->GetDictionaryWithoutPathExpansion(extension_id, &extension)) - continue; - - FilePath::StringType external_crx; - std::string external_version; - std::string external_update_url; - - bool has_external_crx = extension->GetString(kExternalCrx, &external_crx); - bool has_external_version = extension->GetString(kExternalVersion, - &external_version); - bool has_external_update_url = extension->GetString(kExternalUpdateUrl, - &external_update_url); - if (has_external_crx != has_external_version) { - LOG(WARNING) << "Malformed extension dictionary for extension: " - << extension_id.c_str() << ". " << kExternalCrx - << " and " << kExternalVersion << " must be used together."; - continue; - } - - if (has_external_crx == has_external_update_url) { - LOG(WARNING) << "Malformed extension dictionary for extension: " - << extension_id.c_str() << ". Exactly one of the " - << "followng keys should be used: " << kExternalCrx - << ", " << kExternalUpdateUrl << "."; - continue; - } - - if (has_external_crx) { - if (external_crx.find(FilePath::kParentDirectory) != - base::StringPiece::npos) { - LOG(WARNING) << "Path traversal not allowed in path: " - << external_crx.c_str(); - continue; - } - - // If the path is relative, make it absolute. - FilePath path(external_crx); - if (!path.IsAbsolute()) { - // Try path as relative path from external extension dir. - FilePath base_path; - PathService::Get(app::DIR_EXTERNAL_EXTENSIONS, &base_path); - path = base_path.Append(external_crx); - } - - scoped_ptr<Version> version; - version.reset(Version::GetVersionFromString(external_version)); - if (!version.get()) { - LOG(ERROR) << "Malformed extension dictionary for extension: " - << extension_id.c_str() << ". Invalid version string \"" - << external_version << "\"."; - continue; - } - visitor->OnExternalExtensionFileFound(extension_id, version.get(), path, - Extension::EXTERNAL_PREF); - continue; - } - - DCHECK(has_external_update_url); // Checking of keys above ensures this. - GURL update_url(external_update_url); - if (!update_url.is_valid()) { - LOG(WARNING) << "Malformed extension dictionary for extension: " - << extension_id.c_str() << ". " << kExternalUpdateUrl - << " must be a valid URL. Saw \"" << external_update_url - << "\"."; - continue; - } - visitor->OnExternalExtensionUpdateUrlFound(extension_id, update_url); - } -} - -Version* ExternalPrefExtensionProvider::RegisteredVersion( - const std::string& id, Extension::Location* location) const { - DictionaryValue* extension = NULL; - if (!prefs_->GetDictionary(id, &extension)) - return NULL; - - std::string external_version; - if (!extension->GetString(kExternalVersion, &external_version)) - return NULL; - - if (location) - *location = Extension::EXTERNAL_PREF; - return Version::GetVersionFromString(external_version); -} - void ExternalPrefExtensionProvider::SetPreferences( ValueSerializer* serializer) { std::string error_msg; diff --git a/chrome/browser/extensions/external_pref_extension_provider.h b/chrome/browser/extensions/external_pref_extension_provider.h index aef6b17..8b9eb0b 100644 --- a/chrome/browser/extensions/external_pref_extension_provider.h +++ b/chrome/browser/extensions/external_pref_extension_provider.h @@ -6,18 +6,11 @@ #define CHROME_BROWSER_EXTENSIONS_EXTERNAL_PREF_EXTENSION_PROVIDER_H_ #pragma once -#include <set> -#include <string> - -#include "chrome/browser/extensions/external_extension_provider.h" - -class DictionaryValue; -class ValueSerializer; -class Version; +#include "chrome/browser/extensions/stateful_external_extension_provider.h" // A specialization of the ExternalExtensionProvider that uses a json file to // look up which external extensions are registered. -class ExternalPrefExtensionProvider : public ExternalExtensionProvider { +class ExternalPrefExtensionProvider : public StatefulExternalExtensionProvider { public: explicit ExternalPrefExtensionProvider(); virtual ~ExternalPrefExtensionProvider(); @@ -26,15 +19,6 @@ class ExternalPrefExtensionProvider : public ExternalExtensionProvider { // but instead parse a json file specified by the test. void SetPreferencesForTesting(const std::string& json_data_for_testing); - // ExternalExtensionProvider implementation: - virtual void VisitRegisteredExtension( - Visitor* visitor, const std::set<std::string>& ids_to_ignore) const; - - virtual Version* RegisteredVersion(const std::string& id, - Extension::Location* location) const; - protected: - scoped_ptr<DictionaryValue> prefs_; - private: void SetPreferences(ValueSerializer* serializer); }; diff --git a/chrome/browser/extensions/external_registry_extension_provider_win.cc b/chrome/browser/extensions/external_registry_extension_provider_win.cc index 02ae850..00bea09 100644 --- a/chrome/browser/extensions/external_registry_extension_provider_win.cc +++ b/chrome/browser/extensions/external_registry_extension_provider_win.cc @@ -10,6 +10,8 @@ #include "base/version.h" #include "base/win/registry.h" +namespace { + // The Registry hive where to look for external extensions. const HKEY kRegRoot = HKEY_LOCAL_MACHINE; @@ -22,6 +24,16 @@ const wchar_t kRegistryExtensionPath[] = L"path"; // Registry value of that key that defines the current version of the .crx file. const wchar_t kRegistryExtensionVersion[] = L"version"; +bool OpenKeyById(const std::string& id, base::win::RegKey *key) { + std::wstring key_path = ASCIIToWide(kRegistryExtensions); + key_path.append(L"\\"); + key_path.append(ASCIIToWide(id)); + + return key->Open(kRegRoot, key_path.c_str(), KEY_READ); +} + +} // namespace + ExternalRegistryExtensionProvider::ExternalRegistryExtensionProvider() { } @@ -54,6 +66,7 @@ void ExternalRegistryExtensionProvider::VisitRegisteredExtension( if (!version.get()) { LOG(ERROR) << "Invalid version value " << extension_version << " for key " << key_path; + ++iterator; continue; } @@ -75,22 +88,29 @@ void ExternalRegistryExtensionProvider::VisitRegisteredExtension( } } -Version* ExternalRegistryExtensionProvider::RegisteredVersion( - const std::string& id, - Extension::Location* location) const { + +bool ExternalRegistryExtensionProvider::HasExtension( + const std::string& id) const { base::win::RegKey key; - std::wstring key_path = ASCIIToWide(kRegistryExtensions); - key_path.append(L"\\"); - key_path.append(ASCIIToWide(id)); + return OpenKeyById(id, &key); +} - if (!key.Open(kRegRoot, key_path.c_str(), KEY_READ)) - return NULL; +bool ExternalRegistryExtensionProvider::GetExtensionDetails( + const std::string& id, + Extension::Location* location, + scoped_ptr<Version>* version) const { + base::win::RegKey key; + if (!OpenKeyById(id, &key)) + return false; std::wstring extension_version; if (!key.ReadValue(kRegistryExtensionVersion, &extension_version)) - return NULL; + return false; + + if (version) + version->reset(Version::GetVersionFromString(extension_version)); if (location) *location = Extension::EXTERNAL_REGISTRY; - return Version::GetVersionFromString(extension_version); + return true; } diff --git a/chrome/browser/extensions/external_registry_extension_provider_win.h b/chrome/browser/extensions/external_registry_extension_provider_win.h index b5b44bb..bb60106 100644 --- a/chrome/browser/extensions/external_registry_extension_provider_win.h +++ b/chrome/browser/extensions/external_registry_extension_provider_win.h @@ -24,8 +24,11 @@ class ExternalRegistryExtensionProvider : public ExternalExtensionProvider { virtual void VisitRegisteredExtension( Visitor* visitor, const std::set<std::string>& ids_to_ignore) const; - virtual Version* RegisteredVersion(const std::string& id, - Extension::Location* location) const; + virtual bool HasExtension(const std::string& id) const; + + virtual bool GetExtensionDetails(const std::string& id, + Extension::Location* location, + scoped_ptr<Version>* version) const; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTERNAL_REGISTRY_EXTENSION_PROVIDER_WIN_H_ diff --git a/chrome/browser/extensions/image_loading_tracker.cc b/chrome/browser/extensions/image_loading_tracker.cc index f4f9bfd..496c3ad 100644 --- a/chrome/browser/extensions/image_loading_tracker.cc +++ b/chrome/browser/extensions/image_loading_tracker.cc @@ -134,7 +134,7 @@ ImageLoadingTracker::~ImageLoadingTracker() { loader_->StopTracking(); } -void ImageLoadingTracker::LoadImage(Extension* extension, +void ImageLoadingTracker::LoadImage(const Extension* extension, const ExtensionResource& resource, const gfx::Size& max_size, CacheParam cache) { @@ -187,7 +187,7 @@ void ImageLoadingTracker::Observe(NotificationType type, DCHECK(type == NotificationType::EXTENSION_UNLOADED || type == NotificationType::EXTENSION_UNLOADED_DISABLED); - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); // Remove all entries in the load_map_ referencing the extension. This ensures // we don't attempt to cache the image when the load completes. diff --git a/chrome/browser/extensions/image_loading_tracker.h b/chrome/browser/extensions/image_loading_tracker.h index bac2f04..0ce3703 100644 --- a/chrome/browser/extensions/image_loading_tracker.h +++ b/chrome/browser/extensions/image_loading_tracker.h @@ -64,13 +64,13 @@ class ImageLoadingTracker : public NotificationObserver { // |max_size| it will be resized to those dimensions. IMPORTANT NOTE: this // function may call back your observer synchronously (ie before it returns) // if the image was found in the cache. - void LoadImage(Extension* extension, + void LoadImage(const Extension* extension, const ExtensionResource& resource, const gfx::Size& max_size, CacheParam cache); private: - typedef std::map<int, Extension*> LoadMap; + typedef std::map<int, const Extension*> LoadMap; class ImageLoader; diff --git a/chrome/browser/extensions/image_loading_tracker_unittest.cc b/chrome/browser/extensions/image_loading_tracker_unittest.cc index f796a36..57b5268 100644 --- a/chrome/browser/extensions/image_loading_tracker_unittest.cc +++ b/chrome/browser/extensions/image_loading_tracker_unittest.cc @@ -51,7 +51,7 @@ class ImageLoadingTrackerTest : public testing::Test, return result; } - Extension* CreateExtension() { + scoped_refptr<Extension> CreateExtension() { // Create and load an extension. FilePath test_file; if (!PathService::Get(chrome::DIR_TEST_DATA, &test_file)) { @@ -74,11 +74,8 @@ class ImageLoadingTrackerTest : public testing::Test, if (!valid_value.get()) return NULL; - scoped_ptr<Extension> extension(new Extension(test_file)); - if (!extension->InitFromValue(*valid_value, false, &error)) - return NULL; - - return extension.release(); + return Extension::Create( + test_file, Extension::INVALID, *valid_value, false, &error); } SkBitmap image_; @@ -99,7 +96,7 @@ class ImageLoadingTrackerTest : public testing::Test, // Tests asking ImageLoadingTracker to cache pushes the result to the Extension. TEST_F(ImageLoadingTrackerTest, Cache) { - scoped_ptr<Extension> extension(CreateExtension()); + scoped_refptr<Extension> extension(CreateExtension()); ASSERT_TRUE(extension.get() != NULL); ExtensionResource image_resource = @@ -146,7 +143,7 @@ TEST_F(ImageLoadingTrackerTest, Cache) { // Tests deleting an extension while waiting for the image to load doesn't cause // problems. TEST_F(ImageLoadingTrackerTest, DeleteExtensionWhileWaitingForCache) { - scoped_ptr<Extension> extension(CreateExtension()); + scoped_refptr<Extension> extension(CreateExtension()); ASSERT_TRUE(extension.get() != NULL); ExtensionResource image_resource = @@ -166,11 +163,11 @@ TEST_F(ImageLoadingTrackerTest, DeleteExtensionWhileWaitingForCache) { NotificationService::current()->Notify( NotificationType::EXTENSION_UNLOADED, NotificationService::AllSources(), - Details<Extension>(extension.get())); + Details<const Extension>(extension.get())); // Chuck the extension, that way if anyone tries to access it we should crash // or get valgrind errors. - extension.reset(); + extension = NULL; WaitForImageLoad(); diff --git a/chrome/browser/extensions/page_action_apitest.cc b/chrome/browser/extensions/page_action_apitest.cc index c03c030..95e029e 100644 --- a/chrome/browser/extensions/page_action_apitest.cc +++ b/chrome/browser/extensions/page_action_apitest.cc @@ -18,7 +18,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageAction) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(RunExtensionTest("page_action/basics")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; { // Tell the extension to update the page action state. @@ -62,7 +62,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageAction) { IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageActionAddPopup) { // Load the extension, which has no default popup. ASSERT_TRUE(RunExtensionTest("page_action/add_popup")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents()); @@ -107,7 +107,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageActionAddPopup) { IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageActionRemovePopup) { // Load the extension, which has a page action with a default popup. ASSERT_TRUE(RunExtensionTest("page_action/remove_popup")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents()); @@ -136,7 +136,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageActionRemovePopup) { // break. IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OldPageActions) { ASSERT_TRUE(RunExtensionTest("page_action/old_api")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // Have the extension enable the page action. @@ -161,7 +161,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OldPageActions) { // Tests popups in page actions. IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ShowPageActionPopup) { ASSERT_TRUE(RunExtensionTest("page_action/popup")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.cc b/chrome/browser/extensions/sandboxed_extension_unpacker.cc index fc8aea8..c1702b1 100644 --- a/chrome/browser/extensions/sandboxed_extension_unpacker.cc +++ b/chrome/browser/extensions/sandboxed_extension_unpacker.cc @@ -9,6 +9,7 @@ #include "base/base64.h" #include "base/crypto/signature_verifier.h" #include "base/file_util.h" +#include "base/file_util_proxy.h" #include "base/message_loop.h" #include "base/scoped_handle.h" #include "base/task.h" @@ -116,7 +117,13 @@ void SandboxedExtensionUnpacker::Start() { } } -SandboxedExtensionUnpacker::~SandboxedExtensionUnpacker() {} +SandboxedExtensionUnpacker::~SandboxedExtensionUnpacker() { + base::FileUtilProxy::Delete( + BrowserThread::GetMessageLoopProxyForThread(thread_identifier_), + temp_dir_.Take(), + true, + NULL); +} void SandboxedExtensionUnpacker::StartProcessOnIOThread( const FilePath& temp_crx_path) { @@ -140,18 +147,20 @@ void SandboxedExtensionUnpacker::OnUnpackExtensionSucceeded( // extension was unpacked to. We use this until the extension is finally // installed. For example, the install UI shows images from inside the // extension. - extension_.reset(new Extension(extension_root_)); // Localize manifest now, so confirm UI gets correct extension name. std::string error; - if (!extension_l10n_util::LocalizeExtension(extension_.get(), + if (!extension_l10n_util::LocalizeExtension(extension_root_, final_manifest.get(), &error)) { ReportFailure(error); return; } - if (!extension_->InitFromValue(*final_manifest, true, &error)) { + extension_ = Extension::Create( + extension_root_, Extension::INTERNAL, *final_manifest, true, &error); + + if (!extension_.get()) { ReportFailure(std::string("Manifest is invalid: ") + error); return; } @@ -270,8 +279,8 @@ void SandboxedExtensionUnpacker::ReportFailure(const std::string& error) { void SandboxedExtensionUnpacker::ReportSuccess() { // Client takes ownership of temporary directory and extension. - client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_, - extension_.release()); + client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_, extension_); + extension_ = NULL; } DictionaryValue* SandboxedExtensionUnpacker::RewriteManifestFile( diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.h b/chrome/browser/extensions/sandboxed_extension_unpacker.h index 8df1414..8438482 100644 --- a/chrome/browser/extensions/sandboxed_extension_unpacker.h +++ b/chrome/browser/extensions/sandboxed_extension_unpacker.h @@ -29,7 +29,7 @@ class SandboxedExtensionUnpackerClient // for deleting this memory. virtual void OnUnpackSuccess(const FilePath& temp_dir, const FilePath& extension_root, - Extension* extension) = 0; + const Extension* extension) = 0; virtual void OnUnpackFailure(const std::string& error) = 0; protected: @@ -160,7 +160,7 @@ class SandboxedExtensionUnpacker : public UtilityProcessHost::Client { FilePath extension_root_; // Represents the extension we're unpacking. - scoped_ptr<Extension> extension_; + scoped_refptr<Extension> extension_; // Whether we've received a response from the utility process yet. bool got_response_; diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc b/chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc index d461ff4..1842e00 100644 --- a/chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc +++ b/chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc @@ -25,8 +25,7 @@ using testing::Invoke; void OnUnpackSuccess(const FilePath& temp_dir, const FilePath& extension_root, - Extension* extension) { - delete extension; + const Extension* extension) { // Don't delete temp_dir here, we need to do some post op checking. } @@ -38,7 +37,7 @@ class MockSandboxedExtensionUnpackerClient MOCK_METHOD3(OnUnpackSuccess, void(const FilePath& temp_dir, const FilePath& extension_root, - Extension* extension)); + const Extension* extension)); MOCK_METHOD1(OnUnpackFailure, void(const std::string& error)); @@ -52,12 +51,17 @@ class MockSandboxedExtensionUnpackerClient class SandboxedExtensionUnpackerTest : public testing::Test { public: virtual void SetUp() { + file_thread_.reset(new BrowserThread(BrowserThread::FILE, &loop_)); // It will delete itself. client_ = new MockSandboxedExtensionUnpackerClient; client_->DelegateToFake(); } virtual void TearDown() { + // Need to destruct SandboxedExtensionUnpacker before the message loop since + // it posts a task to it. + sandboxed_unpacker_ = NULL; + loop_.RunAllPending(); // Clean up finally. ASSERT_TRUE(file_util::Delete(install_dir_, true)) << install_dir_.value(); @@ -94,6 +98,9 @@ class SandboxedExtensionUnpackerTest : public testing::Test { sandboxed_unpacker_ = new SandboxedExtensionUnpacker(crx_path, temp_dir_, NULL, client_); + // Hack since SandboxedExtensionUnpacker gets its background thread id from + // the Start call, but we don't call it here. + sandboxed_unpacker_->thread_identifier_ = BrowserThread::FILE; PrepareUnpackerEnv(); } @@ -145,6 +152,8 @@ class SandboxedExtensionUnpackerTest : public testing::Test { MockSandboxedExtensionUnpackerClient* client_; scoped_ptr<ExtensionUnpacker> unpacker_; scoped_refptr<SandboxedExtensionUnpacker> sandboxed_unpacker_; + MessageLoop loop_; + scoped_ptr<BrowserThread> file_thread_; }; TEST_F(SandboxedExtensionUnpackerTest, NoCatalogsSuccess) { diff --git a/chrome/browser/extensions/stateful_external_extension_provider.cc b/chrome/browser/extensions/stateful_external_extension_provider.cc new file mode 100644 index 0000000..63899da --- /dev/null +++ b/chrome/browser/extensions/stateful_external_extension_provider.cc @@ -0,0 +1,159 @@ +// 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/extensions/stateful_external_extension_provider.h" + +#include "app/app_paths.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/values.h" +#include "base/version.h" + +namespace { + +// Constants for keeping track of extension preferences. +const char kLocation[] = "location"; +const char kState[] = "state"; +const char kExternalCrx[] = "external_crx"; +const char kExternalVersion[] = "external_version"; +const char kExternalUpdateUrl[] = "external_update_url"; + +} + +StatefulExternalExtensionProvider::StatefulExternalExtensionProvider( + Extension::Location crx_location, + Extension::Location download_location) + : crx_location_(crx_location), + download_location_(download_location) { +} + +StatefulExternalExtensionProvider::~StatefulExternalExtensionProvider() { +} + +void StatefulExternalExtensionProvider::VisitRegisteredExtension( + Visitor* visitor, const std::set<std::string>& ids_to_ignore) const { + for (DictionaryValue::key_iterator i = prefs_->begin_keys(); + i != prefs_->end_keys(); ++i) { + const std::string& extension_id = *i; + if (ids_to_ignore.find(extension_id) != ids_to_ignore.end()) + continue; + + DictionaryValue* extension; + if (!prefs_->GetDictionaryWithoutPathExpansion(extension_id, &extension)) + continue; + + FilePath::StringType external_crx; + std::string external_version; + std::string external_update_url; + + bool has_external_crx = extension->GetString(kExternalCrx, &external_crx); + bool has_external_version = extension->GetString(kExternalVersion, + &external_version); + bool has_external_update_url = extension->GetString(kExternalUpdateUrl, + &external_update_url); + if (has_external_crx != has_external_version) { + LOG(WARNING) << "Malformed extension dictionary for extension: " + << extension_id.c_str() << ". " << kExternalCrx + << " and " << kExternalVersion << " must be used together."; + continue; + } + + if (has_external_crx == has_external_update_url) { + LOG(WARNING) << "Malformed extension dictionary for extension: " + << extension_id.c_str() << ". Exactly one of the " + << "followng keys should be used: " << kExternalCrx + << ", " << kExternalUpdateUrl << "."; + continue; + } + + if (has_external_crx) { + if (crx_location_ == Extension::INVALID) { + LOG(WARNING) << "This provider does not support installing external " + << "extensions from crx files."; + continue; + } + if (external_crx.find(FilePath::kParentDirectory) != + base::StringPiece::npos) { + LOG(WARNING) << "Path traversal not allowed in path: " + << external_crx.c_str(); + continue; + } + + // If the path is relative, make it absolute. + FilePath path(external_crx); + if (!path.IsAbsolute()) { + // Try path as relative path from external extension dir. + FilePath base_path; + PathService::Get(app::DIR_EXTERNAL_EXTENSIONS, &base_path); + path = base_path.Append(external_crx); + } + + scoped_ptr<Version> version; + version.reset(Version::GetVersionFromString(external_version)); + if (!version.get()) { + LOG(WARNING) << "Malformed extension dictionary for extension: " + << extension_id.c_str() << ". Invalid version string \"" + << external_version << "\"."; + continue; + } + visitor->OnExternalExtensionFileFound(extension_id, version.get(), path, + crx_location_); + } else { // if (has_external_update_url) + DCHECK(has_external_update_url); // Checking of keys above ensures this. + if (download_location_ == Extension::INVALID) { + LOG(WARNING) << "This provider does not support installing external " + << "extensions from update URLs."; + continue; + } + GURL update_url(external_update_url); + if (!update_url.is_valid()) { + LOG(WARNING) << "Malformed extension dictionary for extension: " + << extension_id.c_str() << ". " << kExternalUpdateUrl + << " must be a valid URL. Saw \"" << external_update_url + << "\"."; + continue; + } + visitor->OnExternalExtensionUpdateUrlFound( + extension_id, update_url, download_location_); + } + } +} + +bool StatefulExternalExtensionProvider::HasExtension( + const std::string& id) const { + return prefs_->HasKey(id); +} + +bool StatefulExternalExtensionProvider::GetExtensionDetails( + const std::string& id, Extension::Location* location, + scoped_ptr<Version>* version) const { + DictionaryValue* extension = NULL; + if (!prefs_->GetDictionary(id, &extension)) + return false; + + Extension::Location loc = Extension::INVALID; + if (extension->HasKey(kExternalUpdateUrl)) { + loc = download_location_; + + } else if (extension->HasKey(kExternalCrx)) { + loc = crx_location_; + + std::string external_version; + if (!extension->GetString(kExternalVersion, &external_version)) + return false; + + if (version) + version->reset(Version::GetVersionFromString(external_version)); + + } else { + NOTREACHED(); // Chrome should not allow prefs to get into this state. + return false; + } + + if (location) + *location = loc; + + return true; +} diff --git a/chrome/browser/extensions/stateful_external_extension_provider.h b/chrome/browser/extensions/stateful_external_extension_provider.h new file mode 100644 index 0000000..506873d --- /dev/null +++ b/chrome/browser/extensions/stateful_external_extension_provider.h @@ -0,0 +1,57 @@ +// Copyright (c) 2009 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_EXTENSIONS_STATEFUL_EXTERNAL_EXTENSION_PROVIDER_H_ +#define CHROME_BROWSER_EXTENSIONS_STATEFUL_EXTERNAL_EXTENSION_PROVIDER_H_ +#pragma once + +#include <set> +#include <string> + +#include "chrome/browser/extensions/external_extension_provider.h" + +class DictionaryValue; +class ValueSerializer; +class Version; + +// A specialization of the ExternalExtensionProvider that stores the registered +// external extensions in a dictionary. This dictionary (|prefs_|) will be used +// by HasExtension() and GetExtensionDetails() to return information about the +// stored external extensions. It is the responsibility of specialized +// subclasses to initialize this internal dictionary. +// This provider can provide external extensions from two sources: crx files +// and udpate URLs. The locations that the provider will report for these +// are specified at the constructor. +class StatefulExternalExtensionProvider : public ExternalExtensionProvider { + public: + // Initialize the location for external extensions originating from crx + // files: |crx_location|, and originating from update URLs: + // |download_location|. If either of the origins is not supported by this + // provider, then it should be initialized as Extensions::INVALID. + StatefulExternalExtensionProvider( + Extension::Location crx_location, + Extension::Location download_location); + virtual ~StatefulExternalExtensionProvider(); + + // ExternalExtensionProvider implementation: + virtual void VisitRegisteredExtension( + Visitor* visitor, const std::set<std::string>& ids_to_ignore) const; + + virtual bool HasExtension(const std::string& id) const; + + virtual bool GetExtensionDetails(const std::string& id, + Extension::Location* location, + scoped_ptr<Version>* version) const; + protected: + // Location for external extensions that are provided by this provider from + // local crx files. + const Extension::Location crx_location_; + // Location for external extensions that are provided by this provider from + // update URLs. + const Extension::Location download_location_; + // Dictionary of the external extensions that are provided by this provider. + scoped_ptr<DictionaryValue> prefs_; +}; + +#endif // CHROME_BROWSER_EXTENSIONS_STATEFUL_EXTERNAL_EXTENSION_PROVIDER_H_ diff --git a/chrome/browser/extensions/test_extension_prefs.cc b/chrome/browser/extensions/test_extension_prefs.cc index a32f847..2cbb2d1 100644 --- a/chrome/browser/extensions/test_extension_prefs.cc +++ b/chrome/browser/extensions/test_extension_prefs.cc @@ -45,22 +45,25 @@ void TestExtensionPrefs::RecreateExtensionPrefs() { prefs_.reset(new ExtensionPrefs(pref_service_.get(), temp_dir_.path())); } -Extension* TestExtensionPrefs::AddExtension(std::string name) { +scoped_refptr<Extension> TestExtensionPrefs::AddExtension(std::string name) { DictionaryValue dictionary; dictionary.SetString(extension_manifest_keys::kName, name); dictionary.SetString(extension_manifest_keys::kVersion, "0.1"); return AddExtensionWithManifest(dictionary, Extension::INTERNAL); } -Extension* TestExtensionPrefs::AddExtensionWithManifest( +scoped_refptr<Extension> TestExtensionPrefs::AddExtensionWithManifest( const DictionaryValue& manifest, Extension::Location location) { std::string name; EXPECT_TRUE(manifest.GetString(extension_manifest_keys::kName, &name)); FilePath path = extensions_dir_.AppendASCII(name); - Extension* extension = new Extension(path); std::string errors; - extension->set_location(location); - EXPECT_TRUE(extension->InitFromValue(manifest, false, &errors)); + scoped_refptr<Extension> extension = Extension::Create( + path, location, manifest, false, &errors); + EXPECT_TRUE(extension); + if (!extension) + return NULL; + EXPECT_TRUE(Extension::IdIsValid(extension->id())); const bool kInitialIncognitoEnabled = false; prefs_->OnExtensionInstalled(extension, Extension::ENABLED, @@ -69,6 +72,6 @@ Extension* TestExtensionPrefs::AddExtensionWithManifest( } std::string TestExtensionPrefs::AddExtensionAndReturnId(std::string name) { - scoped_ptr<Extension> extension(AddExtension(name)); + scoped_refptr<Extension> extension(AddExtension(name)); return extension->id(); } diff --git a/chrome/browser/extensions/test_extension_prefs.h b/chrome/browser/extensions/test_extension_prefs.h index 1523bee..cb723b2 100644 --- a/chrome/browser/extensions/test_extension_prefs.h +++ b/chrome/browser/extensions/test_extension_prefs.h @@ -34,11 +34,11 @@ class TestExtensionPrefs { // Creates a new Extension with the given name in our temp dir, adds it to // our ExtensionPrefs, and returns it. - Extension* AddExtension(std::string name); + scoped_refptr<Extension> AddExtension(std::string name); // Similar to AddExtension, but takes a dictionary with manifest values. - Extension* AddExtensionWithManifest(const DictionaryValue& manifest, - Extension::Location location); + scoped_refptr<Extension> AddExtensionWithManifest( + const DictionaryValue& manifest, Extension::Location location); // Similar to AddExtension, this adds a new test Extension. This is useful for // cases when you don't need the Extension object, but just the id it was diff --git a/chrome/browser/extensions/theme_installed_infobar_delegate.cc b/chrome/browser/extensions/theme_installed_infobar_delegate.cc index 6d00c22..ebfbe98 100644 --- a/chrome/browser/extensions/theme_installed_infobar_delegate.cc +++ b/chrome/browser/extensions/theme_installed_infobar_delegate.cc @@ -81,7 +81,7 @@ bool ThemeInstalledInfoBarDelegate::Cancel() { if (!previous_theme_id_.empty()) { ExtensionsService* service = profile_->GetExtensionsService(); if (service) { - Extension* previous_theme = + const Extension* previous_theme = service->GetExtensionById(previous_theme_id_, true); if (previous_theme) { profile_->SetTheme(previous_theme); @@ -102,7 +102,7 @@ void ThemeInstalledInfoBarDelegate::Observe( case NotificationType::BROWSER_THEME_CHANGED: { // If the new theme is different from what this info bar is associated // with, close this info bar since it is no longer relevant. - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (!extension || theme_id_ != extension->id()) tab_contents_->RemoveInfoBar(this); break; @@ -113,6 +113,6 @@ void ThemeInstalledInfoBarDelegate::Observe( } } -bool ThemeInstalledInfoBarDelegate::MatchesTheme(Extension* theme) { +bool ThemeInstalledInfoBarDelegate::MatchesTheme(const Extension* theme) { return (theme && theme->id() == theme_id_); } diff --git a/chrome/browser/extensions/theme_installed_infobar_delegate.h b/chrome/browser/extensions/theme_installed_infobar_delegate.h index 2c54fa5..b1ed99e 100644 --- a/chrome/browser/extensions/theme_installed_infobar_delegate.h +++ b/chrome/browser/extensions/theme_installed_infobar_delegate.h @@ -33,7 +33,7 @@ class ThemeInstalledInfoBarDelegate : public ConfirmInfoBarDelegate, // Returns true if the given theme is the same as the one associated with this // info bar. - bool MatchesTheme(Extension* theme); + bool MatchesTheme(const Extension* theme); // NotificationObserver implementation. virtual void Observe(NotificationType type, diff --git a/chrome/browser/extensions/user_script_listener.cc b/chrome/browser/extensions/user_script_listener.cc index 4e84045..c553b1f 100644 --- a/chrome/browser/extensions/user_script_listener.cc +++ b/chrome/browser/extensions/user_script_listener.cc @@ -98,7 +98,7 @@ void UserScriptListener::ReplaceURLPatterns(const URLPatterns& patterns) { url_patterns_ = patterns; } -void UserScriptListener::CollectURLPatterns(Extension* extension, +void UserScriptListener::CollectURLPatterns(const Extension* extension, URLPatterns* patterns) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -118,12 +118,13 @@ void UserScriptListener::Observe(NotificationType type, switch (type.value) { case NotificationType::EXTENSION_LOADED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (extension->content_scripts().empty()) return; // no new patterns from this extension. URLPatterns new_patterns; - CollectURLPatterns(Details<Extension>(details).ptr(), &new_patterns); + CollectURLPatterns(Details<const Extension>(details).ptr(), + &new_patterns); if (!new_patterns.empty()) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, @@ -134,7 +135,8 @@ void UserScriptListener::Observe(NotificationType type, } case NotificationType::EXTENSION_UNLOADED: { - Extension* unloaded_extension = Details<Extension>(details).ptr(); + const Extension* unloaded_extension = + Details<const Extension>(details).ptr(); if (unloaded_extension->content_scripts().empty()) return; // no patterns to delete for this extension. diff --git a/chrome/browser/extensions/user_script_listener.h b/chrome/browser/extensions/user_script_listener.h index 05de21a..3ac83ed 100644 --- a/chrome/browser/extensions/user_script_listener.h +++ b/chrome/browser/extensions/user_script_listener.h @@ -82,7 +82,7 @@ class UserScriptListener // Helper to collect the extension's user script URL patterns in a list and // return it. - void CollectURLPatterns(Extension* extension, URLPatterns* patterns); + void CollectURLPatterns(const Extension* extension, URLPatterns* patterns); // NotificationObserver virtual void Observe(NotificationType type, diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc index 560fda3..1712bb6 100644 --- a/chrome/browser/extensions/user_script_master.cc +++ b/chrome/browser/extensions/user_script_master.cc @@ -108,7 +108,7 @@ bool UserScriptMaster::ScriptReloader::ParseMetadataHeader( script->set_description(value); } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) { URLPattern pattern(UserScript::kValidUserScriptSchemes); - if (!pattern.Parse(value)) + if (URLPattern::PARSE_SUCCESS != pattern.Parse(value)) return false; script->add_url_pattern(pattern); } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) { @@ -253,14 +253,7 @@ static base::SharedMemory* Serialize(const UserScriptList& scripts) { // Create the shared memory object. scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); - if (!shared_memory->Create(std::string(), // anonymous - false, // read-only - false, // open existing - pickle.size())) - return NULL; - - // Map into our process. - if (!shared_memory->Map(pickle.size())) + if (!shared_memory->CreateAndMapAnonymous(pickle.size())) return NULL; // Copy the pickle to shared memory. @@ -346,7 +339,7 @@ void UserScriptMaster::Observe(NotificationType type, break; case NotificationType::EXTENSION_LOADED: { // Add any content scripts inside the extension. - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); bool incognito_enabled = profile_->GetExtensionsService()-> IsIncognitoEnabled(extension); bool allow_file_access = profile_->GetExtensionsService()-> @@ -364,7 +357,7 @@ void UserScriptMaster::Observe(NotificationType type, } case NotificationType::EXTENSION_UNLOADED: { // Remove any content scripts. - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); UserScriptList new_lone_scripts; for (UserScriptList::iterator iter = lone_scripts_.begin(); iter != lone_scripts_.end(); ++iter) { @@ -380,7 +373,7 @@ void UserScriptMaster::Observe(NotificationType type, break; } case NotificationType::EXTENSION_USER_SCRIPTS_UPDATED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); UserScriptList new_lone_scripts; bool incognito_enabled = profile_->GetExtensionsService()-> IsIncognitoEnabled(extension); diff --git a/chrome/browser/extensions/window_open_apitest.cc b/chrome/browser/extensions/window_open_apitest.cc index 27c1b96..5de474b 100644 --- a/chrome/browser/extensions/window_open_apitest.cc +++ b/chrome/browser/extensions/window_open_apitest.cc @@ -11,13 +11,7 @@ #include "chrome/test/ui_test_utils.h" #include "net/base/mock_host_resolver.h" -// crbug.com/60156 -#if defined(OS_MACOSX) -// On mac, this test basically never succeeds. -#define FLAKY_WindowOpen DISABLED_WindowOpen -#endif - -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FLAKY_WindowOpen) { +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowOpen) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); diff --git a/chrome/browser/external_tab_container_win.cc b/chrome/browser/external_tab_container_win.cc index ffa0183..eb336b9 100644 --- a/chrome/browser/external_tab_container_win.cc +++ b/chrome/browser/external_tab_container_win.cc @@ -7,10 +7,12 @@ #include <string> #include "app/l10n_util.h" +#include "app/win/scoped_prop.h" +#include "base/debug/trace_event.h" #include "base/i18n/rtl.h" #include "base/logging.h" -#include "base/trace_event.h" #include "base/win_util.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/automation/automation_provider.h" #include "chrome/browser/automation/automation_extension_function.h" @@ -57,10 +59,10 @@ class ExternalTabPageInfoBubbleView : public PageInfoBubbleView { bool show_history) : PageInfoBubbleView(parent_window, profile, url, ssl, show_history), container_(container) { - DLOG(INFO) << __FUNCTION__; + DVLOG(1) << __FUNCTION__; } virtual ~ExternalTabPageInfoBubbleView() { - DLOG(INFO) << __FUNCTION__; + DVLOG(1) << __FUNCTION__; } // LinkController methods: virtual void LinkActivated(views::Link* source, int event_flags) { @@ -129,9 +131,8 @@ bool ExternalTabContainer::Init(Profile* profile, // TODO(jcampan): limit focus traversal to contents. - // We don't ever remove the prop because the lifetime of this object - // is the same as the lifetime of the window - SetProp(GetNativeView(), kWindowObjectKey, this); + prop_.reset( + new app::win::ScopedProp(GetNativeView(), kWindowObjectKey, this)); if (existing_contents) { tab_contents_ = existing_contents; @@ -618,7 +619,7 @@ bool ExternalTabContainer::HandleContextMenu(const ContextMenuParams& params) { POINT screen_pt = { params.x, params.y }; MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1); - IPC::ContextMenuParams ipc_params; + IPC::MiniContextMenuParams ipc_params; ipc_params.screen_x = screen_pt.x; ipc_params.screen_y = screen_pt.y; ipc_params.link_url = params.link_url; @@ -793,6 +794,7 @@ LRESULT ExternalTabContainer::OnCreate(LPCREATESTRUCT create_struct) { } void ExternalTabContainer::OnDestroy() { + prop_.reset(); Uninitialize(); WidgetWin::OnDestroy(); if (browser_.get()) { diff --git a/chrome/browser/external_tab_container_win.h b/chrome/browser/external_tab_container_win.h index f1168f2..226c01d 100644 --- a/chrome/browser/external_tab_container_win.h +++ b/chrome/browser/external_tab_container_win.h @@ -11,6 +11,7 @@ #include <vector> #include "base/lazy_instance.h" +#include "base/scoped_ptr.h" #include "chrome/browser/automation/automation_resource_message_filter.h" #include "chrome/browser/browser.h" #include "chrome/browser/net/chrome_url_request_context.h" @@ -29,6 +30,12 @@ class Profile; class TabContentsContainer; class RenderViewContextMenuViews; +namespace app { +namespace win { +class ScopedProp; +} +} + namespace IPC { struct NavigationInfo; } @@ -332,6 +339,8 @@ class ExternalTabContainer : public TabContentsDelegate, // page without chrome frame. bool route_all_top_level_navigations_; + scoped_ptr<app::win::ScopedProp> prop_; + DISALLOW_COPY_AND_ASSIGN(ExternalTabContainer); }; diff --git a/chrome/browser/file_path_watcher.cc b/chrome/browser/file_path_watcher.cc new file mode 100644 index 0000000..19aff7a --- /dev/null +++ b/chrome/browser/file_path_watcher.cc @@ -0,0 +1,19 @@ +// 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. + +// Cross platform methods for FilePathWatcher. See the various platform +// specific implementaiton files, too. + +#include "chrome/browser/file_path_watcher.h" + +FilePathWatcher::~FilePathWatcher() { + impl_->Cancel(); +} + +bool FilePathWatcher::Watch(const FilePath& path, + Delegate* delegate) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(path.IsAbsolute()); + return impl_->Watch(path, delegate); +} diff --git a/chrome/browser/file_path_watcher.h b/chrome/browser/file_path_watcher.h index da123c6..51d25cf 100644 --- a/chrome/browser/file_path_watcher.h +++ b/chrome/browser/file_path_watcher.h @@ -33,17 +33,11 @@ class FilePathWatcher { }; FilePathWatcher(); - ~FilePathWatcher() { - impl_->Cancel(); - } + ~FilePathWatcher(); // Register interest in any changes on |path|. OnPathChanged will be called // back for each change. Returns true on success. - bool Watch(const FilePath& path, Delegate* delegate) WARN_UNUSED_RESULT { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(path.IsAbsolute()); - return impl_->Watch(path, delegate); - } + bool Watch(const FilePath& path, Delegate* delegate) WARN_UNUSED_RESULT; // Used internally to encapsulate different members on different platforms. class PlatformDelegate diff --git a/chrome/browser/file_path_watcher_browsertest.cc b/chrome/browser/file_path_watcher_browsertest.cc index 0ae4fc1..a337ae3 100644 --- a/chrome/browser/file_path_watcher_browsertest.cc +++ b/chrome/browser/file_path_watcher_browsertest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// 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. @@ -44,7 +44,7 @@ class NotificationCollector loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &NotificationCollector::RecordChange, - delegate)); + make_scoped_refptr(delegate))); } void Register(TestDelegate* delegate) { @@ -303,15 +303,15 @@ TEST_F(FilePathWatcherTest, NonExistentDirectory) { ASSERT_TRUE(file_util::CreateDirectory(dir)); ASSERT_TRUE(WriteFile(file, "content")); - LOG(INFO) << "Waiting for file creation"; + VLOG(1) << "Waiting for file creation"; WaitForEvents(); ASSERT_TRUE(WriteFile(file, "content v2")); - LOG(INFO) << "Waiting for file change"; + VLOG(1) << "Waiting for file change"; WaitForEvents(); ASSERT_TRUE(file_util::Delete(file, false)); - LOG(INFO) << "Waiting for file deletion"; + VLOG(1) << "Waiting for file deletion"; WaitForEvents(); } @@ -338,11 +338,11 @@ TEST_F(FilePathWatcherTest, DirectoryChain) { ASSERT_TRUE(file_util::CreateDirectory(sub_path)); } ASSERT_TRUE(WriteFile(file, "content")); - LOG(INFO) << "Waiting for file creation"; + VLOG(1) << "Waiting for file creation"; WaitForEvents(); ASSERT_TRUE(WriteFile(file, "content v2")); - LOG(INFO) << "Waiting for file modification"; + VLOG(1) << "Waiting for file modification"; WaitForEvents(); } @@ -367,11 +367,11 @@ TEST_F(FilePathWatcherTest, DeleteAndRecreate) { SetupWatch(test_file(), &watcher, delegate.get()); ASSERT_TRUE(file_util::Delete(test_file(), false)); - LOG(INFO) << "Waiting for file deletion"; + VLOG(1) << "Waiting for file deletion"; WaitForEvents(); ASSERT_TRUE(WriteFile(test_file(), "content")); - LOG(INFO) << "Waiting for file creation"; + VLOG(1) << "Waiting for file creation"; WaitForEvents(); } @@ -384,23 +384,23 @@ TEST_F(FilePathWatcherTest, WatchDirectory) { SetupWatch(dir, &watcher, delegate.get()); ASSERT_TRUE(file_util::CreateDirectory(dir)); - LOG(INFO) << "Waiting for directory creation"; + VLOG(1) << "Waiting for directory creation"; WaitForEvents(); ASSERT_TRUE(WriteFile(file1, "content")); - LOG(INFO) << "Waiting for file1 creation"; + VLOG(1) << "Waiting for file1 creation"; WaitForEvents(); ASSERT_TRUE(WriteFile(file1, "content v2")); - LOG(INFO) << "Waiting for file1 modification"; + VLOG(1) << "Waiting for file1 modification"; WaitForEvents(); ASSERT_TRUE(file_util::Delete(file1, false)); - LOG(INFO) << "Waiting for file1 deletion"; + VLOG(1) << "Waiting for file1 deletion"; WaitForEvents(); ASSERT_TRUE(WriteFile(file2, "content")); - LOG(INFO) << "Waiting for file2 creation"; + VLOG(1) << "Waiting for file2 creation"; WaitForEvents(); } @@ -419,12 +419,12 @@ TEST_F(FilePathWatcherTest, MoveParent) { // Setup a directory hierarchy. ASSERT_TRUE(file_util::CreateDirectory(subdir)); ASSERT_TRUE(WriteFile(file, "content")); - LOG(INFO) << "Waiting for file creation"; + VLOG(1) << "Waiting for file creation"; WaitForEvents(); // Move the parent directory. file_util::Move(dir, dest); - LOG(INFO) << "Waiting for directory move"; + VLOG(1) << "Waiting for directory move"; WaitForEvents(); } diff --git a/chrome/browser/file_system/browser_file_system_callback_dispatcher.cc b/chrome/browser/file_system/browser_file_system_callback_dispatcher.cc index 756bef7..b95930f 100644 --- a/chrome/browser/file_system/browser_file_system_callback_dispatcher.cc +++ b/chrome/browser/file_system/browser_file_system_callback_dispatcher.cc @@ -29,7 +29,7 @@ void BrowserFileSystemCallbackDispatcher::DidReadMetadata( } void BrowserFileSystemCallbackDispatcher::DidReadDirectory( - const std::vector<base::file_util_proxy::Entry>& entries, bool has_more) { + const std::vector<base::FileUtilProxy::Entry>& entries, bool has_more) { dispatcher_host_->Send(new ViewMsg_FileSystem_DidReadDirectory( request_id_, entries, has_more)); dispatcher_host_->RemoveCompletedOperation(request_id_); diff --git a/chrome/browser/file_system/browser_file_system_callback_dispatcher.h b/chrome/browser/file_system/browser_file_system_callback_dispatcher.h index 3220577..7e85b41 100644 --- a/chrome/browser/file_system/browser_file_system_callback_dispatcher.h +++ b/chrome/browser/file_system/browser_file_system_callback_dispatcher.h @@ -20,7 +20,7 @@ class BrowserFileSystemCallbackDispatcher virtual void DidSucceed(); virtual void DidReadMetadata(const base::PlatformFileInfo& file_info); virtual void DidReadDirectory( - const std::vector<base::file_util_proxy::Entry>& entries, + const std::vector<base::FileUtilProxy::Entry>& entries, bool has_more); virtual void DidOpenFileSystem(const std::string& name, const FilePath& root_path); diff --git a/chrome/browser/file_system/file_system_dispatcher_host.cc b/chrome/browser/file_system/file_system_dispatcher_host.cc index f1ef3ca..cb13bd0 100644 --- a/chrome/browser/file_system/file_system_dispatcher_host.cc +++ b/chrome/browser/file_system/file_system_dispatcher_host.cc @@ -31,9 +31,11 @@ class FileSystemDispatcherHost::OpenFileSystemTask { int request_id, const GURL& origin_url, fileapi::FileSystemType type, + bool create, FileSystemDispatcherHost* dispatcher_host) { // The task is self-destructed. - new OpenFileSystemTask(request_id, origin_url, type, dispatcher_host); + new OpenFileSystemTask( + request_id, origin_url, type, create, dispatcher_host); } private: @@ -54,12 +56,13 @@ class FileSystemDispatcherHost::OpenFileSystemTask { int request_id, const GURL& origin_url, fileapi::FileSystemType type, + bool create, FileSystemDispatcherHost* dispatcher_host) : request_id_(request_id), dispatcher_host_(dispatcher_host), callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { dispatcher_host->context_->path_manager()->GetFileSystemRootPath( - origin_url, type, true /* create */, + origin_url, type, create, callback_factory_.NewCallback(&OpenFileSystemTask::DidGetRootPath)); } @@ -138,7 +141,7 @@ bool FileSystemDispatcherHost::OnMessageReceived( void FileSystemDispatcherHost::OnOpenFileSystem( int request_id, const GURL& origin_url, fileapi::FileSystemType type, - int64 requested_size) { + int64 requested_size, bool create) { ContentSetting content_setting = host_content_settings_map_->GetContentSetting( origin_url, CONTENT_SETTINGS_TYPE_COOKIES, ""); @@ -153,7 +156,7 @@ void FileSystemDispatcherHost::OnOpenFileSystem( return; } - OpenFileSystemTask::Start(request_id, origin_url, type, this); + OpenFileSystemTask::Start(request_id, origin_url, type, create, this); } void FileSystemDispatcherHost::OnMove( diff --git a/chrome/browser/file_system/file_system_dispatcher_host.h b/chrome/browser/file_system/file_system_dispatcher_host.h index 94d0cf4..f51e7f2 100644 --- a/chrome/browser/file_system/file_system_dispatcher_host.h +++ b/chrome/browser/file_system/file_system_dispatcher_host.h @@ -48,7 +48,8 @@ class FileSystemDispatcherHost void OnOpenFileSystem(int request_id, const GURL& origin_url, fileapi::FileSystemType type, - int64 requested_size); + int64 requested_size, + bool create); void OnMove(int request_id, const FilePath& src_path, const FilePath& dest_path); diff --git a/chrome/browser/file_system/file_system_host_context.cc b/chrome/browser/file_system/file_system_host_context.cc index 54f8056..34698db 100644 --- a/chrome/browser/file_system/file_system_host_context.cc +++ b/chrome/browser/file_system/file_system_host_context.cc @@ -15,15 +15,15 @@ FileSystemHostContext::FileSystemHostContext( const FilePath& data_path, bool is_incognito) - : quota_manager_(new fileapi::FileSystemQuota()), - path_manager_(new fileapi::FileSystemPathManager( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), - data_path, is_incognito, allow_file_access_from_files_)) { + : quota_manager_(new fileapi::FileSystemQuota()) { CommandLine* command_line = CommandLine::ForCurrentProcess(); allow_file_access_from_files_ = command_line->HasSwitch(switches::kAllowFileAccessFromFiles); unlimited_quota_ = command_line->HasSwitch(switches::kUnlimitedQuotaForFiles); + path_manager_.reset(new fileapi::FileSystemPathManager( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), + data_path, is_incognito, allow_file_access_from_files_)); } bool FileSystemHostContext::CheckOriginQuota(const GURL& url, int64 growth) { diff --git a/chrome/browser/find_bar_host_browsertest.cc b/chrome/browser/find_bar_host_browsertest.cc index 62bae77..f36dd32 100644 --- a/chrome/browser/find_bar_host_browsertest.cc +++ b/chrome/browser/find_bar_host_browsertest.cc @@ -7,6 +7,7 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/find_bar.h" #include "chrome/browser/find_bar_controller.h" @@ -823,9 +824,13 @@ IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, PreferPreviousSearch) { EXPECT_EQ(1, FindInPageWchar(tab1, L"Default", kFwd, kIgnoreCase, &ordinal)); // Create a second tab. - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - browser()->AddTabWithURL(¶ms); - EXPECT_EQ(browser(), params.target); + // For some reason we can't use AddSelectedTabWithURL here on ChromeOS. It + // could be some delicate assumption about the tab starting off unselected or + // something relating to user gesture. + browser::NavigateParams params(browser(), url, PageTransition::TYPED); + params.disposition = NEW_BACKGROUND_TAB; + params.tabstrip_add_types = TabStripModel::ADD_NONE; + browser::Navigate(¶ms); browser()->SelectTabContentsAt(1, false); TabContents* tab2 = browser()->GetSelectedTabContents(); EXPECT_NE(tab1, tab2); @@ -899,10 +904,7 @@ IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, PrepopulateInNewTab) { EXPECT_EQ(1, FindInPageWchar(tab1, L"page", kFwd, kIgnoreCase, &ordinal)); // Now create a second tab and load the same page. - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - browser()->AddTabWithURL(¶ms); - EXPECT_EQ(browser(), params.target); - browser()->SelectTabContentsAt(1, false); + browser()->AddSelectedTabWithURL(url, PageTransition::TYPED); TabContents* tab2 = browser()->GetSelectedTabContents(); EXPECT_NE(tab1, tab2); @@ -944,9 +946,10 @@ IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, PrepopulatePreserveLast) { FindBarController::kKeepSelection); // Now create a second tab and load the same page. - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - browser()->AddTabWithURL(¶ms); - EXPECT_EQ(browser(), params.target); + browser::NavigateParams params(browser(), url, PageTransition::TYPED); + params.disposition = NEW_BACKGROUND_TAB; + params.tabstrip_add_types = TabStripModel::ADD_NONE; + browser::Navigate(¶ms); browser()->SelectTabContentsAt(1, false); TabContents* tab2 = browser()->GetSelectedTabContents(); EXPECT_NE(tab1, tab2); @@ -1018,9 +1021,7 @@ IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, MAYBE_NoIncognitoPrepopulate) { // Open a new incognito window and navigate to the same page. Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile(); Browser* incognito_browser = Browser::Create(incognito_profile); - Browser::AddTabWithURLParams params1(url, PageTransition::START_PAGE); - incognito_browser->AddTabWithURL(¶ms1); - EXPECT_EQ(incognito_browser, params1.target); + incognito_browser->AddSelectedTabWithURL(url, PageTransition::START_PAGE); ui_test_utils::WaitForNavigation( &incognito_browser->GetSelectedTabContents()->controller()); incognito_browser->window()->Show(); @@ -1040,10 +1041,7 @@ IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, MAYBE_NoIncognitoPrepopulate) { FindBarController::kKeepSelection); // Now open a new tab in the original (non-incognito) browser. - Browser::AddTabWithURLParams params2(url, PageTransition::TYPED); - browser()->AddTabWithURL(¶ms2); - EXPECT_EQ(browser(), params2.target); - browser()->SelectTabContentsAt(1, false); + browser()->AddSelectedTabWithURL(url, PageTransition::TYPED); TabContents* tab2 = browser()->GetSelectedTabContents(); EXPECT_NE(tab1, tab2); diff --git a/chrome/browser/first_run/first_run.cc b/chrome/browser/first_run/first_run.cc index 54f030e..ce89af5 100644 --- a/chrome/browser/first_run/first_run.cc +++ b/chrome/browser/first_run/first_run.cc @@ -28,6 +28,7 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/installer/util/master_preferences.h" +#include "chrome/installer/util/master_preferences_constants.h" #include "chrome/installer/util/util_constants.h" namespace { @@ -95,28 +96,26 @@ bool FirstRun::ProcessMasterPreferences(const FilePath& user_data_dir, return true; master_prefs = master_prefs.AppendASCII(installer_util::kDefaultMasterPrefs); - scoped_ptr<DictionaryValue> prefs( - installer_util::ParseDistributionPreferences(master_prefs)); - if (!prefs.get()) + installer_util::MasterPreferences prefs(master_prefs); + if (!prefs.read_from_file()) return true; - out_prefs->new_tabs = installer_util::GetFirstRunTabs(prefs.get()); + out_prefs->new_tabs = prefs.GetFirstRunTabs(); bool value = false; #if defined(OS_WIN) // RLZ is currently a Windows-only phenomenon. When it comes to the Mac/ // Linux, enable it here. - if (!installer_util::GetDistroIntegerPreference(prefs.get(), - installer_util::master_preferences::kDistroPingDelay, - &out_prefs->ping_delay)) { + if (!prefs.GetInt(installer_util::master_preferences::kDistroPingDelay, + &out_prefs->ping_delay)) { // 90 seconds is the default that we want to use in case master // preferences is missing, corrupt or ping_delay is missing. out_prefs->ping_delay = 90; } - if (installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kRequireEula, &value) && value) { + if (prefs.GetBool(installer_util::master_preferences::kRequireEula, &value) && + value) { // Show the post-installation EULA. This is done by setup.exe and the // result determines if we continue or not. We wait here until the user // dismisses the dialog. @@ -143,9 +142,10 @@ bool FirstRun::ProcessMasterPreferences(const FilePath& user_data_dir, } #endif - if (installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kAltFirstRunBubble, &value) && value) + if (prefs.GetBool(installer_util::master_preferences::kAltFirstRunBubble, + &value) && value) { FirstRun::SetOEMFirstRunBubblePref(); + } FilePath user_prefs = GetDefaultPrefFilePath(true, user_data_dir); if (user_prefs.empty()) @@ -158,14 +158,14 @@ bool FirstRun::ProcessMasterPreferences(const FilePath& user_data_dir, #if defined(OS_WIN) DictionaryValue* extensions = 0; - if (installer_util::HasExtensionsBlock(prefs.get(), &extensions)) { + if (prefs.GetExtensionsBlock(&extensions)) { VLOG(1) << "Extensions block found in master preferences"; DoDelayedInstallExtensions(); } #endif - if (installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kDistroImportSearchPref, &value)) { + if (prefs.GetBool(installer_util::master_preferences::kDistroImportSearchPref, + &value)) { if (value) { out_prefs->do_import_items |= importer::SEARCH_ENGINES; } else { @@ -174,22 +174,25 @@ bool FirstRun::ProcessMasterPreferences(const FilePath& user_data_dir, } // Check to see if search engine logos should be randomized. - if (installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kSearchEngineExperimentRandomizePref, - &value) && value) + if (prefs.GetBool( + installer_util::master_preferences:: + kSearchEngineExperimentRandomizePref, + &value) && value) { out_prefs->randomize_search_engine_experiment = true; + } // If we're suppressing the first-run bubble, set that preference now. // Otherwise, wait until the user has completed first run to set it, so the // user is guaranteed to see the bubble iff he or she has completed the first // run process. - if (installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kDistroSuppressFirstRunBubble, - &value) && value) + if (prefs.GetBool( + installer_util::master_preferences::kDistroSuppressFirstRunBubble, + &value) && value) FirstRun::SetShowFirstRunBubblePref(false); - if (installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kDistroImportHistoryPref, &value)) { + if (prefs.GetBool( + installer_util::master_preferences::kDistroImportHistoryPref, + &value)) { if (value) { out_prefs->do_import_items |= importer::HISTORY; } else { @@ -198,10 +201,11 @@ bool FirstRun::ProcessMasterPreferences(const FilePath& user_data_dir, } std::string not_used; - out_prefs->homepage_defined = prefs->GetString(prefs::kHomePage, ¬_used); + out_prefs->homepage_defined = prefs.GetString(prefs::kHomePage, ¬_used); - if (installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kDistroImportHomePagePref, &value)) { + if (prefs.GetBool( + installer_util::master_preferences::kDistroImportHomePagePref, + &value)) { if (value) { out_prefs->do_import_items |= importer::HOME_PAGE; } else { @@ -210,25 +214,27 @@ bool FirstRun::ProcessMasterPreferences(const FilePath& user_data_dir, } // Bookmarks are never imported unless specifically turned on. - if (installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kDistroImportBookmarksPref, &value) - && value) { + if (prefs.GetBool( + installer_util::master_preferences::kDistroImportBookmarksPref, + &value) && value) { out_prefs->do_import_items |= importer::FAVORITES; } - if (installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kMakeChromeDefaultForUser, &value) && - value) + if (prefs.GetBool( + installer_util::master_preferences::kMakeChromeDefaultForUser, + &value) && value) { out_prefs->make_chrome_default = true; + } // TODO(mirandac): Refactor skip-first-run-ui process into regular first run // import process. http://crbug.com/49647 // Note we are skipping all other master preferences if skip-first-run-ui // is *not* specified. (That is, we continue only if skipping first run ui.) - if (!installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kDistroSkipFirstRunPref, &value) || - !value) + if (!prefs.GetBool( + installer_util::master_preferences::kDistroSkipFirstRunPref, + &value) || !value) { return true; + } #if !defined(OS_WIN) // From here on we won't show first run so we need to do the work to show the @@ -242,13 +248,13 @@ bool FirstRun::ProcessMasterPreferences(const FilePath& user_data_dir, if (!FirstRun::CreateSentinel()) return false; - if (installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kDistroShowWelcomePage, &value) && - value) + if (prefs.GetBool(installer_util::master_preferences::kDistroShowWelcomePage, + &value) && value) { FirstRun::SetShowWelcomePagePref(); + } std::string import_bookmarks_path; - installer_util::GetDistroStringPreference(prefs.get(), + prefs.GetString( installer_util::master_preferences::kDistroImportBookmarksFromFilePref, &import_bookmarks_path); @@ -295,10 +301,11 @@ bool FirstRun::ProcessMasterPreferences(const FilePath& user_data_dir, } #endif - if (installer_util::GetDistroBooleanPreference(prefs.get(), - installer_util::master_preferences::kMakeChromeDefaultForUser, &value) && - value) + if (prefs.GetBool( + installer_util::master_preferences::kMakeChromeDefaultForUser, + &value) && value) { ShellIntegration::SetAsDefaultBrowser(); + } return false; } @@ -405,7 +412,7 @@ int FirstRun::ImportFromFile(Profile* profile, const CommandLine& cmdline) { NOTREACHED(); return false; } - scoped_refptr<ImporterHost> importer_host = new ImporterHost(); + scoped_refptr<ImporterHost> importer_host(new ImporterHost()); FirstRunImportObserver observer; importer_host->set_headless(); diff --git a/chrome/browser/first_run/first_run_win.cc b/chrome/browser/first_run/first_run_win.cc index a7f7a3c..35037d5 100644 --- a/chrome/browser/first_run/first_run_win.cc +++ b/chrome/browser/first_run/first_run_win.cc @@ -561,7 +561,7 @@ int FirstRun::ImportFromBrowser(Profile* profile, StartImportingWithUI( parent_window, - items_to_import, + static_cast<uint16>(items_to_import), importer_host, importer_host->GetSourceProfileInfoForBrowserType(browser_type), profile, diff --git a/chrome/browser/geolocation/access_token_store.cc b/chrome/browser/geolocation/access_token_store.cc index af76e54..9b4eea7 100644 --- a/chrome/browser/geolocation/access_token_store.cc +++ b/chrome/browser/geolocation/access_token_store.cc @@ -94,8 +94,8 @@ void AccessTokenStore::RegisterPrefs(PrefService* prefs) { AccessTokenStore::Handle AccessTokenStore::LoadAccessTokens( CancelableRequestConsumerBase* consumer, LoadAccessTokensCallbackType* callback) { - scoped_refptr<CancelableRequest<LoadAccessTokensCallbackType> > request = - new CancelableRequest<LoadAccessTokensCallbackType>(callback); + scoped_refptr<CancelableRequest<LoadAccessTokensCallbackType> > request( + new CancelableRequest<LoadAccessTokensCallbackType>(callback)); AddRequest(request, consumer); DCHECK(request->handle()); diff --git a/chrome/browser/geolocation/access_token_store_browsertest.cc b/chrome/browser/geolocation/access_token_store_browsertest.cc index b4449e5..802d16d 100644 --- a/chrome/browser/geolocation/access_token_store_browsertest.cc +++ b/chrome/browser/geolocation/access_token_store_browsertest.cc @@ -57,7 +57,7 @@ struct TokenLoadClientForTest { void RunCancelTestInClientTread() { ASSERT_TRUE(BrowserThread::CurrentlyOn(kExpectedClientThreadId)); - scoped_refptr<AccessTokenStore> store = NewChromePrefsAccessTokenStore(); + scoped_refptr<AccessTokenStore> store(NewChromePrefsAccessTokenStore()); CancelableRequestConsumer consumer; TokenLoadClientForTest load_client; @@ -117,8 +117,8 @@ void GeolocationAccessTokenStoreTest::OnAccessTokenStoresLoaded( } if (token_to_set_) { - scoped_refptr<AccessTokenStore> store = - NewChromePrefsAccessTokenStore(); + scoped_refptr<AccessTokenStore> store( + NewChromePrefsAccessTokenStore()); store->SaveAccessToken(ref_url_, *token_to_set_); } BrowserThread::PostTask( diff --git a/chrome/browser/geolocation/device_data_provider.cc b/chrome/browser/geolocation/device_data_provider.cc new file mode 100644 index 0000000..9fe592a --- /dev/null +++ b/chrome/browser/geolocation/device_data_provider.cc @@ -0,0 +1,105 @@ +// 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/geolocation/device_data_provider.h" + +namespace { + +bool CellDataMatches(const CellData &data1, const CellData &data2) { + return data1.Matches(data2); +} + +} // namespace + +CellData::CellData() + : cell_id(kint32min), + location_area_code(kint32min), + mobile_network_code(kint32min), + mobile_country_code(kint32min), + radio_signal_strength(kint32min), + timing_advance(kint32min) { +} + +RadioData::RadioData() + : home_mobile_network_code(kint32min), + home_mobile_country_code(kint32min), + radio_type(RADIO_TYPE_UNKNOWN) { +} + +RadioData::~RadioData() {} + +bool RadioData::Matches(const RadioData &other) const { + if (cell_data.size() != other.cell_data.size()) { + return false; + } + if (!std::equal(cell_data.begin(), cell_data.end(), other.cell_data.begin(), + CellDataMatches)) { + return false; + } + return device_id == other.device_id && + home_mobile_network_code == other.home_mobile_network_code && + home_mobile_country_code == other.home_mobile_country_code && + radio_type == other.radio_type && + carrier == other.carrier; +} + +AccessPointData::AccessPointData() + : radio_signal_strength(kint32min), + channel(kint32min), + signal_to_noise(kint32min) { +} + +AccessPointData::~AccessPointData() {} + +WifiData::WifiData() {} + +WifiData::~WifiData() {} + +bool WifiData::DiffersSignificantly(const WifiData& other) const { + // More than 4 or 50% of access points added or removed is significant. + static const size_t kMinChangedAccessPoints = 4; + const size_t min_ap_count = + std::min(access_point_data.size(), other.access_point_data.size()); + const size_t max_ap_count = + std::max(access_point_data.size(), other.access_point_data.size()); + const size_t difference_threadhold = std::min(kMinChangedAccessPoints, + min_ap_count / 2); + if (max_ap_count > min_ap_count + difference_threadhold) + return true; + // Compute size of interesction of old and new sets. + size_t num_common = 0; + for (AccessPointDataSet::const_iterator iter = access_point_data.begin(); + iter != access_point_data.end(); + iter++) { + if (other.access_point_data.find(*iter) != + other.access_point_data.end()) { + ++num_common; + } + } + DCHECK(num_common <= min_ap_count); + + // Test how many have changed. + return max_ap_count > num_common + difference_threadhold; +} + +GatewayData::GatewayData() {} + +GatewayData::~GatewayData() {} + +bool GatewayData::DiffersSignificantly(const GatewayData& other) const { + // Any change is significant. + if (this->router_data.size() != other.router_data.size()) + return true; + RouterDataSet::const_iterator iter1 = router_data.begin(); + RouterDataSet::const_iterator iter2 = other.router_data.begin(); + while (iter1 != router_data.end()) { + if (iter1->mac_address != iter2->mac_address) { + // There is a difference between the sets of routers. + return true; + } + ++iter1; + ++iter2; + } + return false; +} diff --git a/chrome/browser/geolocation/device_data_provider.h b/chrome/browser/geolocation/device_data_provider.h index 9cd4390..b6a3882 100644 --- a/chrome/browser/geolocation/device_data_provider.h +++ b/chrome/browser/geolocation/device_data_provider.h @@ -44,13 +44,7 @@ // Cell radio data relating to a single cell tower. struct CellData { - CellData() - : cell_id(kint32min), - location_area_code(kint32min), - mobile_network_code(kint32min), - mobile_country_code(kint32min), - radio_signal_strength(kint32min), - timing_advance(kint32min) {} + CellData(); bool Matches(const CellData &other) const { // Ignore radio_signal_strength when matching. return cell_id == other.cell_id && @@ -70,10 +64,6 @@ struct CellData { // meters. }; -static bool CellDataMatches(const CellData &data1, const CellData &data2) { - return data1.Matches(data2); -} - enum RadioType { RADIO_TYPE_UNKNOWN, RADIO_TYPE_GSM, @@ -83,24 +73,11 @@ enum RadioType { // All data for the cell radio. struct RadioData { - RadioData() - : home_mobile_network_code(kint32min), - home_mobile_country_code(kint32min), - radio_type(RADIO_TYPE_UNKNOWN) {} - bool Matches(const RadioData &other) const { - if (cell_data.size() != other.cell_data.size()) { - return false; - } - if (!std::equal(cell_data.begin(), cell_data.end(), other.cell_data.begin(), - CellDataMatches)) { - return false; - } - return device_id == other.device_id && - home_mobile_network_code == other.home_mobile_network_code && - home_mobile_country_code == other.home_mobile_country_code && - radio_type == other.radio_type && - carrier == other.carrier; - } + RadioData(); + ~RadioData(); + + bool Matches(const RadioData &other) const; + // Determines whether a new set of radio data differs significantly from this. bool DiffersSignificantly(const RadioData &other) const { // This is required by MockDeviceDataProviderImpl. @@ -113,15 +90,14 @@ struct RadioData { int home_mobile_network_code; // For the device's home network. int home_mobile_country_code; // For the device's home network. RadioType radio_type; // Mobile radio type. - string16 carrier; // Carrier name. + string16 carrier; // Carrier name. }; // Wifi data relating to a single access point. struct AccessPointData { - AccessPointData() - : radio_signal_strength(kint32min), - channel(kint32min), - signal_to_noise(kint32min) {} + AccessPointData(); + ~AccessPointData(); + // MAC address, formatted as per MacAddressAsString16. string16 mac_address; int radio_signal_strength; // Measured in dBm @@ -141,33 +117,11 @@ struct AccessPointDataLess { // All data for wifi. struct WifiData { - // Determines whether a new set of WiFi data differs significantly from this. - bool DiffersSignificantly(const WifiData& other) const { - // More than 4 or 50% of access points added or removed is significant. - static const size_t kMinChangedAccessPoints = 4; - const size_t min_ap_count = - std::min(access_point_data.size(), other.access_point_data.size()); - const size_t max_ap_count = - std::max(access_point_data.size(), other.access_point_data.size()); - const size_t difference_threadhold = std::min(kMinChangedAccessPoints, - min_ap_count / 2); - if (max_ap_count > min_ap_count + difference_threadhold) - return true; - // Compute size of interesction of old and new sets. - size_t num_common = 0; - for (AccessPointDataSet::const_iterator iter = access_point_data.begin(); - iter != access_point_data.end(); - iter++) { - if (other.access_point_data.find(*iter) != - other.access_point_data.end()) { - ++num_common; - } - } - DCHECK(num_common <= min_ap_count); + WifiData(); + ~WifiData(); - // Test how many have changed. - return max_ap_count > num_common + difference_threadhold; - } + // Determines whether a new set of WiFi data differs significantly from this. + bool DiffersSignificantly(const WifiData& other) const; // Store access points as a set, sorted by MAC address. This allows quick // comparison of sets for detecting changes and for caching. @@ -177,7 +131,6 @@ struct WifiData { // Gateway data relating to a single router. struct RouterData { - RouterData() {} // MAC address, formatted as per MacAddressAsString16. string16 mac_address; }; @@ -193,24 +146,12 @@ struct RouterDataLess { // All gateway data for routers. struct GatewayData { + GatewayData(); + ~GatewayData(); + // Determines whether a new set of gateway data differs significantly // from this. - bool DiffersSignificantly(const GatewayData& other) const { - // Any change is significant. - if (this->router_data.size() != other.router_data.size()) - return true; - RouterDataSet::const_iterator iter1 = router_data.begin(); - RouterDataSet::const_iterator iter2 = other.router_data.begin(); - while (iter1 != router_data.end()) { - if (iter1->mac_address != iter2->mac_address) { - // There is a difference between the sets of routers. - return true; - } - ++iter1; - ++iter2; - } - return false; - } + bool DiffersSignificantly(const GatewayData& other) const; // Store routers as a set, sorted by MAC address. This allows quick // comparison of sets for detecting changes and for caching. diff --git a/chrome/browser/geolocation/device_data_provider_unittest.cc b/chrome/browser/geolocation/device_data_provider_unittest.cc new file mode 100644 index 0000000..f6e9d96 --- /dev/null +++ b/chrome/browser/geolocation/device_data_provider_unittest.cc @@ -0,0 +1,39 @@ +// 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/geolocation/device_data_provider.h" + +#include "chrome/browser/geolocation/wifi_data_provider_common.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +class NullWifiDataListenerInterface + : public WifiDataProviderCommon::ListenerInterface { + public: + // ListenerInterface + virtual void DeviceDataUpdateAvailable( + DeviceDataProvider<WifiData>* provider) {} +}; +} + +TEST(GeolocationDeviceDataProviderWifiData, CreateDestroy) { + // See http://crbug.com/59913 . The main_message_loop is not required to be + // run for correct behaviour, but we run it in this test to help smoke out + // any race conditions between processing in the main loop and the setup / + // tear down of the DeviceDataProvider thread. + MessageLoopForUI main_message_loop; + NullWifiDataListenerInterface listener; + for (int i = 0; i < 10; i++) { + DeviceDataProvider<WifiData>::Register(&listener); + for (int j = 0; j < 10; j++) { + PlatformThread::Sleep(0); + main_message_loop.RunAllPending(); // See comment above + } + DeviceDataProvider<WifiData>::Unregister(&listener); + for (int j = 0; j < 10; j++) { + PlatformThread::Sleep(0); + main_message_loop.RunAllPending(); // See comment above + } + } +} diff --git a/chrome/browser/geolocation/geolocation_browsertest.cc b/chrome/browser/geolocation/geolocation_browsertest.cc index 255dad2..30510ac 100644 --- a/chrome/browser/geolocation/geolocation_browsertest.cc +++ b/chrome/browser/geolocation/geolocation_browsertest.cc @@ -516,7 +516,7 @@ IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, IFramesWithCachedPosition) { // See http://crbug.com/56033 IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, - FLAKY_CancelPermissionForFrame) { + DISABLED_CancelPermissionForFrame) { html_for_tests_ = "files/geolocation/iframes_different_origin.html"; ASSERT_TRUE(Initialize(INITIALIZATION_IFRAMES)); LOG(WARNING) << "frames loaded"; diff --git a/chrome/browser/geolocation/geolocation_content_settings_map.cc b/chrome/browser/geolocation/geolocation_content_settings_map.cc index 0bf798c..31a0a21 100644 --- a/chrome/browser/geolocation/geolocation_content_settings_map.cc +++ b/chrome/browser/geolocation/geolocation_content_settings_map.cc @@ -117,11 +117,6 @@ void GeolocationContentSettingsMap::SetDefaultContentSetting( profile_->GetPrefs()->SetInteger(prefs::kGeolocationDefaultContentSetting, setting == CONTENT_SETTING_DEFAULT ? kDefaultSetting : setting); - - NotificationService::current()->Notify( - NotificationType::GEOLOCATION_DEFAULT_CHANGED, - Source<GeolocationContentSettingsMap>(this), - NotificationService::NoDetails()); } void GeolocationContentSettingsMap::SetContentSetting( @@ -162,11 +157,6 @@ void GeolocationContentSettingsMap::SetContentSetting( requesting_origin_settings_dictionary->SetWithoutPathExpansion( embedding_origin.spec(), Value::CreateIntegerValue(setting)); } - - NotificationService::current()->Notify( - NotificationType::GEOLOCATION_SETTINGS_CHANGED, - Source<GeolocationContentSettingsMap>(this), - NotificationService::NoDetails()); } void GeolocationContentSettingsMap::ResetToDefault() { diff --git a/chrome/browser/geolocation/geolocation_dispatcher_host.cc b/chrome/browser/geolocation/geolocation_dispatcher_host_old.cc index 5e8209c..0af76e0 100644 --- a/chrome/browser/geolocation/geolocation_dispatcher_host.cc +++ b/chrome/browser/geolocation/geolocation_dispatcher_host_old.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/geolocation/geolocation_dispatcher_host.h" +#include "chrome/browser/geolocation/geolocation_dispatcher_host_old.h" #include <map> #include <set> @@ -19,14 +19,14 @@ #include "ipc/ipc_message.h" namespace { -class GeolocationDispatcherHostImpl : public GeolocationDispatcherHost, - public GeolocationObserver { +class GeolocationDispatcherHostOldImpl : public GeolocationDispatcherHostOld, + public GeolocationObserver { public: - GeolocationDispatcherHostImpl( + GeolocationDispatcherHostOldImpl( int resource_message_filter_process_id, GeolocationPermissionContext* geolocation_permission_context); - // GeolocationDispatcherHost + // GeolocationDispatcherHostOld // Called to possibly handle the incoming IPC message. Returns true if // handled. Called in the browser process. virtual bool OnMessageReceived(const IPC::Message& msg, bool* msg_was_ok); @@ -35,8 +35,8 @@ class GeolocationDispatcherHostImpl : public GeolocationDispatcherHost, virtual void OnLocationUpdate(const Geoposition& position); private: - friend class base::RefCountedThreadSafe<GeolocationDispatcherHostImpl>; - virtual ~GeolocationDispatcherHostImpl(); + friend class base::RefCountedThreadSafe<GeolocationDispatcherHostOldImpl>; + virtual ~GeolocationDispatcherHostOldImpl(); void OnRegisterDispatcher(int render_view_id); void OnUnregisterDispatcher(int render_view_id); @@ -70,10 +70,10 @@ class GeolocationDispatcherHostImpl : public GeolocationDispatcherHost, // Only set whilst we are registered with the arbitrator. GeolocationProvider* location_provider_; - DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcherHostImpl); + DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcherHostOldImpl); }; -GeolocationDispatcherHostImpl::GeolocationDispatcherHostImpl( +GeolocationDispatcherHostOldImpl::GeolocationDispatcherHostOldImpl( int resource_message_filter_process_id, GeolocationPermissionContext* geolocation_permission_context) : resource_message_filter_process_id_(resource_message_filter_process_id), @@ -84,17 +84,17 @@ GeolocationDispatcherHostImpl::GeolocationDispatcherHostImpl( // a javascript geolocation object is actually initialized. } -GeolocationDispatcherHostImpl::~GeolocationDispatcherHostImpl() { +GeolocationDispatcherHostOldImpl::~GeolocationDispatcherHostOldImpl() { if (location_provider_) location_provider_->RemoveObserver(this); } -bool GeolocationDispatcherHostImpl::OnMessageReceived( +bool GeolocationDispatcherHostOldImpl::OnMessageReceived( const IPC::Message& msg, bool* msg_was_ok) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); *msg_was_ok = true; bool handled = true; - IPC_BEGIN_MESSAGE_MAP_EX(GeolocationDispatcherHostImpl, msg, *msg_was_ok) + IPC_BEGIN_MESSAGE_MAP_EX(GeolocationDispatcherHostOldImpl, msg, *msg_was_ok) IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_RegisterDispatcher, OnRegisterDispatcher) IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_UnregisterDispatcher, @@ -116,7 +116,7 @@ bool GeolocationDispatcherHostImpl::OnMessageReceived( return handled; } -void GeolocationDispatcherHostImpl::OnLocationUpdate( +void GeolocationDispatcherHostOldImpl::OnLocationUpdate( const Geoposition& geoposition) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); for (std::set<int>::iterator it = geolocation_renderer_ids_.begin(); @@ -128,19 +128,21 @@ void GeolocationDispatcherHostImpl::OnLocationUpdate( } } -void GeolocationDispatcherHostImpl::OnRegisterDispatcher(int render_view_id) { +void GeolocationDispatcherHostOldImpl::OnRegisterDispatcher( + int render_view_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK_EQ(0u, geolocation_renderer_ids_.count(render_view_id)); geolocation_renderer_ids_.insert(render_view_id); } -void GeolocationDispatcherHostImpl::OnUnregisterDispatcher(int render_view_id) { +void GeolocationDispatcherHostOldImpl::OnUnregisterDispatcher( + int render_view_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK_EQ(1u, geolocation_renderer_ids_.count(render_view_id)); geolocation_renderer_ids_.erase(render_view_id); } -void GeolocationDispatcherHostImpl::OnRequestPermission( +void GeolocationDispatcherHostOldImpl::OnRequestPermission( int render_view_id, int bridge_id, const GURL& requesting_frame) { @@ -152,7 +154,7 @@ void GeolocationDispatcherHostImpl::OnRequestPermission( requesting_frame); } -void GeolocationDispatcherHostImpl::OnCancelPermissionRequest( +void GeolocationDispatcherHostOldImpl::OnCancelPermissionRequest( int render_view_id, int bridge_id, const GURL& requesting_frame) { @@ -164,7 +166,7 @@ void GeolocationDispatcherHostImpl::OnCancelPermissionRequest( requesting_frame); } -void GeolocationDispatcherHostImpl::OnStartUpdating( +void GeolocationDispatcherHostOldImpl::OnStartUpdating( int render_view_id, int bridge_id, const GURL& requesting_frame, @@ -183,8 +185,8 @@ void GeolocationDispatcherHostImpl::OnStartUpdating( RefreshGeolocationObserverOptions(); } -void GeolocationDispatcherHostImpl::OnStopUpdating(int render_view_id, - int bridge_id) { +void GeolocationDispatcherHostOldImpl::OnStopUpdating(int render_view_id, + int bridge_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DVLOG(1) << __FUNCTION__ << " " << resource_message_filter_process_id_ << ":" << render_view_id << ":" << bridge_id; @@ -194,23 +196,23 @@ void GeolocationDispatcherHostImpl::OnStopUpdating(int render_view_id, resource_message_filter_process_id_, render_view_id, bridge_id); } -void GeolocationDispatcherHostImpl::OnSuspend(int render_view_id, - int bridge_id) { +void GeolocationDispatcherHostOldImpl::OnSuspend(int render_view_id, + int bridge_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DVLOG(1) << __FUNCTION__ << " " << resource_message_filter_process_id_ << ":" << render_view_id << ":" << bridge_id; // TODO(bulach): connect this with GeolocationArbitrator. } -void GeolocationDispatcherHostImpl::OnResume(int render_view_id, - int bridge_id) { +void GeolocationDispatcherHostOldImpl::OnResume(int render_view_id, + int bridge_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DVLOG(1) << __FUNCTION__ << " " << resource_message_filter_process_id_ << ":" << render_view_id << ":" << bridge_id; // TODO(bulach): connect this with GeolocationArbitrator. } -void GeolocationDispatcherHostImpl::RefreshGeolocationObserverOptions() { +void GeolocationDispatcherHostOldImpl::RefreshGeolocationObserverOptions() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (bridge_update_options_.empty()) { if (location_provider_) { @@ -221,16 +223,17 @@ void GeolocationDispatcherHostImpl::RefreshGeolocationObserverOptions() { if (!location_provider_) location_provider_ = GeolocationProvider::GetInstance(); // Re-add to re-establish our options, in case they changed. - location_provider_->AddObserver(this, - GeolocationObserverOptions::Collapse( - bridge_update_options_)); + location_provider_->AddObserver( + this, + GeolocationObserverOptions::Collapse(bridge_update_options_)); } } } // namespace -GeolocationDispatcherHost* GeolocationDispatcherHost::New( +GeolocationDispatcherHostOld* GeolocationDispatcherHostOld::New( int resource_message_filter_process_id, GeolocationPermissionContext* geolocation_permission_context) { - return new GeolocationDispatcherHostImpl(resource_message_filter_process_id, - geolocation_permission_context); + return new GeolocationDispatcherHostOldImpl( + resource_message_filter_process_id, + geolocation_permission_context); } diff --git a/chrome/browser/geolocation/geolocation_dispatcher_host.h b/chrome/browser/geolocation/geolocation_dispatcher_host_old.h index 38b79ef..655049a 100644 --- a/chrome/browser/geolocation/geolocation_dispatcher_host.h +++ b/chrome/browser/geolocation/geolocation_dispatcher_host_old.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_GEOLOCATION_GEOLOCATION_DISPATCHER_HOST_H_ -#define CHROME_BROWSER_GEOLOCATION_GEOLOCATION_DISPATCHER_HOST_H_ +#ifndef CHROME_BROWSER_GEOLOCATION_GEOLOCATION_DISPATCHER_HOST_OLD_H_ +#define CHROME_BROWSER_GEOLOCATION_GEOLOCATION_DISPATCHER_HOST_OLD_H_ #pragma once #include "base/ref_counted.h" @@ -11,13 +11,16 @@ class GeolocationPermissionContext; namespace IPC { class Message; } -// GeolocationDispatcherHost is a delegate for Geolocation messages used by +// GeolocationDispatcherHostOld is a delegate for Geolocation messages used by // ResourceMessageFilter. // It's the complement of GeolocationDispatcher (owned by RenderView). -class GeolocationDispatcherHost - : public base::RefCountedThreadSafe<GeolocationDispatcherHost> { + +// TODO(jknotten): Remove this class once the new client-based implementation is +// checked in (see http://crbug.com/59908). +class GeolocationDispatcherHostOld + : public base::RefCountedThreadSafe<GeolocationDispatcherHostOld> { public: - static GeolocationDispatcherHost* New( + static GeolocationDispatcherHostOld* New( int resource_message_filter_process_id, GeolocationPermissionContext* geolocation_permission_context); @@ -26,11 +29,11 @@ class GeolocationDispatcherHost virtual bool OnMessageReceived(const IPC::Message& msg, bool* msg_was_ok) = 0; protected: - friend class base::RefCountedThreadSafe<GeolocationDispatcherHost>; - GeolocationDispatcherHost() {} - virtual ~GeolocationDispatcherHost() {} + friend class base::RefCountedThreadSafe<GeolocationDispatcherHostOld>; + GeolocationDispatcherHostOld() {} + virtual ~GeolocationDispatcherHostOld() {} - DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcherHost); + DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcherHostOld); }; -#endif // CHROME_BROWSER_GEOLOCATION_GEOLOCATION_DISPATCHER_HOST_H_ +#endif // CHROME_BROWSER_GEOLOCATION_GEOLOCATION_DISPATCHER_HOST_OLD_H_ diff --git a/chrome/browser/geolocation/geolocation_exceptions_table_model_unittest.cc b/chrome/browser/geolocation/geolocation_exceptions_table_model_unittest.cc index f24a403..68bc752 100644 --- a/chrome/browser/geolocation/geolocation_exceptions_table_model_unittest.cc +++ b/chrome/browser/geolocation/geolocation_exceptions_table_model_unittest.cc @@ -40,8 +40,8 @@ class GeolocationExceptionsTableModelTest : public RenderViewHostTestHarness { } void CreateAllowedSamples() { - scoped_refptr<GeolocationContentSettingsMap> map = - profile()->GetGeolocationContentSettingsMap(); + scoped_refptr<GeolocationContentSettingsMap> map( + profile()->GetGeolocationContentSettingsMap()); map->SetContentSetting(kUrl0, kUrl0, CONTENT_SETTING_ALLOW); map->SetContentSetting(kUrl0, kUrl1, CONTENT_SETTING_ALLOW); map->SetContentSetting(kUrl0, kUrl2, CONTENT_SETTING_ALLOW); @@ -57,8 +57,8 @@ class GeolocationExceptionsTableModelTest : public RenderViewHostTestHarness { TEST_F(GeolocationExceptionsTableModelTest, CanRemoveException) { EXPECT_EQ(0, model_->RowCount()); - scoped_refptr<GeolocationContentSettingsMap> map = - profile()->GetGeolocationContentSettingsMap(); + scoped_refptr<GeolocationContentSettingsMap> map( + profile()->GetGeolocationContentSettingsMap()); // Ensure a single entry can be removed. map->SetContentSetting(kUrl0, kUrl0, CONTENT_SETTING_ALLOW); @@ -86,8 +86,8 @@ TEST_F(GeolocationExceptionsTableModelTest, CanRemoveException) { TEST_F(GeolocationExceptionsTableModelTest, RemoveExceptions) { CreateAllowedSamples(); - scoped_refptr<GeolocationContentSettingsMap> map = - profile()->GetGeolocationContentSettingsMap(); + scoped_refptr<GeolocationContentSettingsMap> map( + profile()->GetGeolocationContentSettingsMap()); // Test removing parent exception. GeolocationExceptionsTableModel::Rows rows; @@ -113,8 +113,8 @@ TEST_F(GeolocationExceptionsTableModelTest, RemoveExceptions) { TEST_F(GeolocationExceptionsTableModelTest, RemoveAll) { CreateAllowedSamples(); - scoped_refptr<GeolocationContentSettingsMap> map = - profile()->GetGeolocationContentSettingsMap(); + scoped_refptr<GeolocationContentSettingsMap> map( + profile()->GetGeolocationContentSettingsMap()); model_->RemoveAll(); EXPECT_EQ(CONTENT_SETTING_ASK, map->GetContentSetting(kUrl0, kUrl0)); diff --git a/chrome/browser/geolocation/geolocation_permission_context.cc b/chrome/browser/geolocation/geolocation_permission_context.cc index 8a71b47..4d7c848 100644 --- a/chrome/browser/geolocation/geolocation_permission_context.cc +++ b/chrome/browser/geolocation/geolocation_permission_context.cc @@ -11,7 +11,7 @@ #include "chrome/browser/browser_thread.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/geolocation/geolocation_content_settings_map.h" -#include "chrome/browser/geolocation/geolocation_dispatcher_host.h" +#include "chrome/browser/geolocation/geolocation_dispatcher_host_old.h" #include "chrome/browser/geolocation/geolocation_provider.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" @@ -362,7 +362,7 @@ void GeolocationPermissionContext::RequestGeolocationPermission( ExtensionsService* extensions = profile_->GetExtensionsService(); if (extensions) { - Extension* ext = extensions->GetExtensionByURL(requesting_frame); + const Extension* ext = extensions->GetExtensionByURL(requesting_frame); if (!ext) ext = extensions->GetExtensionByWebExtent(requesting_frame); if (ext && ext->HasApiPermission(Extension::kGeolocationPermission)) { diff --git a/chrome/browser/geolocation/geolocation_permission_context.h b/chrome/browser/geolocation/geolocation_permission_context.h index 2fc7da1..432c9a2 100644 --- a/chrome/browser/geolocation/geolocation_permission_context.h +++ b/chrome/browser/geolocation/geolocation_permission_context.h @@ -10,7 +10,6 @@ #include "base/ref_counted.h" #include "base/scoped_ptr.h" -class GeolocationDispatcherHost; class GeolocationInfoBarQueueController; class GeolocationPermissionContext; class GURL; diff --git a/chrome/browser/geolocation/network_location_provider.cc b/chrome/browser/geolocation/network_location_provider.cc index 6c985e1..37c3a38 100644 --- a/chrome/browser/geolocation/network_location_provider.cc +++ b/chrome/browser/geolocation/network_location_provider.cc @@ -17,6 +17,10 @@ const int kDataCompleteWaitPeriod = 1000 * 2; // 2 seconds // static const size_t NetworkLocationProvider::PositionCache::kMaximumSize = 10; +NetworkLocationProvider::PositionCache::PositionCache() {} + +NetworkLocationProvider::PositionCache::~PositionCache() {} + bool NetworkLocationProvider::PositionCache::CachePosition( const GatewayData& gateway_data, const WifiData& wifi_data, diff --git a/chrome/browser/geolocation/network_location_provider.h b/chrome/browser/geolocation/network_location_provider.h index 47267f2..1e2485b 100644 --- a/chrome/browser/geolocation/network_location_provider.h +++ b/chrome/browser/geolocation/network_location_provider.h @@ -35,6 +35,9 @@ class NetworkLocationProvider // device data. static const size_t kMaximumSize; + PositionCache(); + ~PositionCache(); + // Caches the current position response for the current set of cell ID and // WiFi data. In the case of the cache exceeding kMaximumSize this will // evict old entries in FIFO orderer of being added. diff --git a/chrome/browser/geolocation/wifi_data_provider_common_unittest.cc b/chrome/browser/geolocation/wifi_data_provider_common_unittest.cc index b830040..256fbcb 100644 --- a/chrome/browser/geolocation/wifi_data_provider_common_unittest.cc +++ b/chrome/browser/geolocation/wifi_data_provider_common_unittest.cc @@ -216,7 +216,6 @@ TEST_F(GeolocationWifiDataProviderCommonTest, DoScanWithResults) { EXPECT_EQ(single_access_point.ssid, data.access_point_data.begin()->ssid); } -// TODO(Joth) Convert to gmock style TEST_F(GeolocationWifiDataProviderCommonTest, StartThreadViaDeviceDataProvider) { MessageLoopQuitListener quit_listener(&main_message_loop_); diff --git a/chrome/browser/geolocation/wifi_data_provider_linux.cc b/chrome/browser/geolocation/wifi_data_provider_linux.cc index 6c043e0..5c0fbb0 100644 --- a/chrome/browser/geolocation/wifi_data_provider_linux.cc +++ b/chrome/browser/geolocation/wifi_data_provider_linux.cc @@ -11,6 +11,7 @@ #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <dbus/dbus.h> +#include <dlfcn.h> #include <glib.h> #include "base/scoped_ptr.h" @@ -31,6 +32,12 @@ const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager"; // From http://projects.gnome.org/NetworkManager/developers/spec.html enum { NM_DEVICE_TYPE_WIFI = 2 }; +// Function type matching dbus_g_bus_get_private so that we can +// dynamically determine the presence of this symbol (see +// NetworkManagerWlanApi::Init) +typedef DBusGConnection* DBusGBusGetPrivateFunc( + DBusBusType type, GMainContext* context, GError** error); + // Utility wrappers to make various GLib & DBus structs into scoped objects. class ScopedGPtrArrayFree { public: @@ -69,6 +76,23 @@ class ScopedGValue { GValue v; }; +// Use ScopedDLHandle to automatically clean up after dlopen. +class ScopedDLHandle { + public: + ScopedDLHandle(void* handle) + : handle_(handle) { + } + ~ScopedDLHandle() { + if (handle_) + dlclose(handle_); + } + void* get() { + return handle_; + } + private: + void *handle_; +}; + // Wifi API binding to NetworkManager, to allow reuse of the polling behavior // defined in WifiDataProviderCommon. // TODO(joth): NetworkManager also allows for notification based handling, @@ -115,7 +139,9 @@ class NetworkManagerWlanApi : public WifiDataProviderCommon::WlanApiInterface { GError* error_; // Connection to the dbus system bus. DBusGConnection* connection_; - // Proxy to the network maanger dbus service. + // Main context + GMainContext* context_; + // Proxy to the network manager dbus service. ScopedDBusGProxyPtr proxy_; DISALLOW_COPY_AND_ASSIGN(NetworkManagerWlanApi); @@ -135,14 +161,21 @@ int frquency_in_khz_to_channel(int frequency_khz) { } NetworkManagerWlanApi::NetworkManagerWlanApi() - : error_(NULL), connection_(NULL) { + : error_(NULL), + connection_(NULL), + context_(NULL) +{ } NetworkManagerWlanApi::~NetworkManagerWlanApi() { proxy_.reset(); if (connection_) { + dbus_connection_close(dbus_g_connection_get_connection(connection_)); dbus_g_connection_unref(connection_); } + if (context_) + g_main_context_unref(context_); + DCHECK(!error_) << "Missing a call to CheckError() to clear |error_|"; } @@ -151,19 +184,35 @@ bool NetworkManagerWlanApi::Init() { // get caught up with that nonsense here, lets just assert our requirement. CHECK(g_thread_supported()); - // Get a connection to the session bus. - connection_ = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error_); + // We require a private bus to ensure that we don't interfere with the + // default loop on the main thread. Unforunately this functionality is only + // available in dbus-glib-0.84 and later. We do a dynamic symbol lookup + // to determine if dbus_g_bus_get_private is available. See bug + // http://code.google.com/p/chromium/issues/detail?id=59913 for more + // information. + ScopedDLHandle handle(dlopen(NULL, RTLD_LAZY)); + if (!handle.get()) + return false; + + DBusGBusGetPrivateFunc *my_dbus_g_bus_get_private = + (DBusGBusGetPrivateFunc *) dlsym(handle.get(), "dbus_g_bus_get_private"); + + if (!my_dbus_g_bus_get_private) { + LOG(ERROR) << "We need dbus-glib >= 0.84 for wifi geolocation."; + return false; + } + // Get a private connection to the session bus. + context_ = g_main_context_new(); + DCHECK(context_); + connection_ = my_dbus_g_bus_get_private(DBUS_BUS_SYSTEM, context_, &error_); + if (CheckError()) return false; DCHECK(connection_); - // dbus-glib queues timers that get fired on the default loop, unfortunately - // it isn't thread safe in it's handling of these timers. We can't easily - // tell it which loop to queue them on instead, but as we only make - // blocking sync calls we don't need timers anyway, so disable them. - // See http://crbug.com/40803 TODO(joth): This is not an ideal solution, as - // we're reconfiguring the process global system bus connection, so could - // impact other users of DBus. + // Disable timers on the connection since we don't need them and + // we're not going to run them anyway as the connection is associated + // with a private context. See bug http://crbug.com/40803 dbus_bool_t ok = dbus_connection_set_timeout_functions( dbus_g_connection_get_connection(connection_), NULL, NULL, NULL, NULL, NULL); @@ -376,4 +425,3 @@ PollingPolicyInterface* WifiDataProviderLinux::NewPollingPolicy() { kTwoNoChangePollingIntervalMilliseconds, kNoWifiPollingIntervalMilliseconds>; } - diff --git a/chrome/browser/geolocation/wifi_data_provider_mac.cc b/chrome/browser/geolocation/wifi_data_provider_mac.cc index 804f9a6..fbac4cd 100644 --- a/chrome/browser/geolocation/wifi_data_provider_mac.cc +++ b/chrome/browser/geolocation/wifi_data_provider_mac.cc @@ -103,6 +103,7 @@ bool Apple80211Api::GetAccessPointData(WifiData::AccessPointDataSet* data) { DCHECK(WirelessScanSplit_function_); CFArrayRef managed_access_points = NULL; CFArrayRef adhoc_access_points = NULL; + // Arrays returned here are owned by the caller. WIErr err = (*WirelessScanSplit_function_)(wifi_context_, &managed_access_points, &adhoc_access_points, @@ -144,6 +145,12 @@ bool Apple80211Api::GetAccessPointData(WifiData::AccessPointDataSet* data) { } data->insert(access_point_data); } + + if (managed_access_points) + CFRelease(managed_access_points); + if (adhoc_access_points) + CFRelease(adhoc_access_points); + return true; } } // namespace diff --git a/chrome/browser/global_keyboard_shortcuts_mac.mm b/chrome/browser/global_keyboard_shortcuts_mac.mm index 38b5636..6e3f206 100644 --- a/chrome/browser/global_keyboard_shortcuts_mac.mm +++ b/chrome/browser/global_keyboard_shortcuts_mac.mm @@ -9,7 +9,7 @@ #include "base/basictypes.h" #include "base/logging.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" // Basically, there are two kinds of keyboard shortcuts: Ones that should work // only if the tab contents is focused (BrowserKeyboardShortcut), and ones that diff --git a/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm b/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm index 36ab618..4e3ab07 100644 --- a/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm +++ b/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm @@ -7,7 +7,7 @@ #include "chrome/browser/global_keyboard_shortcuts_mac.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "testing/gtest/include/gtest/gtest.h" TEST(GlobalKeyboardShortcuts, ShortcutsToWindowCommand) { diff --git a/chrome/browser/gpu_process_host.cc b/chrome/browser/gpu_process_host.cc index a33ef5c..171c404 100644 --- a/chrome/browser/gpu_process_host.cc +++ b/chrome/browser/gpu_process_host.cc @@ -23,6 +23,7 @@ #include "media/base/media_switches.h" #if defined(OS_LINUX) +#include "app/x11_util.h" #include "gfx/gtk_native_view_id_manager.h" #endif @@ -94,6 +95,7 @@ bool GpuProcessHost::Init() { static const char* const kSwitchNames[] = { switches::kUseGL, switches::kDisableGpuVsync, + switches::kDisableGpuWatchdog, switches::kDisableLogging, switches::kEnableAcceleratedDecoding, switches::kEnableLogging, @@ -126,16 +128,6 @@ GpuProcessHost* GpuProcessHost::Get() { return sole_instance_; } -// static -void GpuProcessHost::SendAboutGpuCrash() { - Get()->Send(new GpuMsg_Crash()); -} - -// static -void GpuProcessHost::SendAboutGpuHang() { - Get()->Send(new GpuMsg_Hang()); -} - bool GpuProcessHost::Send(IPC::Message* msg) { if (!EnsureInitialized()) return false; @@ -237,7 +229,7 @@ void SendDelayedReply(IPC::Message* reply_msg) { } void GetViewXIDDispatcher(gfx::NativeViewId id, IPC::Message* reply_msg) { - unsigned long xid; + XID xid; GtkNativeViewManager* manager = Singleton<GtkNativeViewManager>::get(); if (!manager->GetPermanentXIDForId(&xid, id)) { @@ -253,11 +245,11 @@ void GetViewXIDDispatcher(gfx::NativeViewId id, IPC::Message* reply_msg) { NewRunnableFunction(&SendDelayedReply, reply_msg)); } -} +} // namespace void GpuProcessHost::OnGetViewXID(gfx::NativeViewId id, IPC::Message *reply_msg) { - // Have to request a permanent overlay from UI thread. + // Have to request a permanent XID from UI thread. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableFunction(&GetViewXIDDispatcher, id, reply_msg)); @@ -308,10 +300,14 @@ namespace { class BuffersSwappedDispatcher : public Task { public: BuffersSwappedDispatcher( - int32 renderer_id, int32 render_view_id, gfx::PluginWindowHandle window) + int32 renderer_id, + int32 render_view_id, + gfx::PluginWindowHandle window, + uint64 surface_id) : renderer_id_(renderer_id), render_view_id_(render_view_id), - window_(window) { + window_(window), + surface_id_(surface_id) { } void Run() { @@ -322,13 +318,14 @@ class BuffersSwappedDispatcher : public Task { RenderWidgetHostView* view = host->view(); if (!view) return; - view->AcceleratedSurfaceBuffersSwapped(window_); + view->AcceleratedSurfaceBuffersSwapped(window_, surface_id_); } private: int32 renderer_id_; int32 render_view_id_; gfx::PluginWindowHandle window_; + uint64 surface_id_; DISALLOW_COPY_AND_ASSIGN(BuffersSwappedDispatcher); }; @@ -338,10 +335,12 @@ class BuffersSwappedDispatcher : public Task { void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( int32 renderer_id, int32 render_view_id, - gfx::PluginWindowHandle window) { + gfx::PluginWindowHandle window, + uint64 surface_id) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - new BuffersSwappedDispatcher(renderer_id, render_view_id, window)); + new BuffersSwappedDispatcher( + renderer_id, render_view_id, window, surface_id)); } #endif diff --git a/chrome/browser/gpu_process_host.h b/chrome/browser/gpu_process_host.h index 0e14402..f9aa06f 100644 --- a/chrome/browser/gpu_process_host.h +++ b/chrome/browser/gpu_process_host.h @@ -28,13 +28,6 @@ class GpuProcessHost : public BrowserChildProcessHost { // Getter for the singleton. This will return NULL on failure. static GpuProcessHost* Get(); - // Tells the GPU process to crash. Useful for testing. - static void SendAboutGpuCrash(); - - // Tells the GPU process to let its main thread enter an infinite loop. - // Useful for testing. - static void SendAboutGpuHang(); - // Shutdown routine, which should only be called upon process // termination. static void Shutdown(); @@ -102,7 +95,8 @@ class GpuProcessHost : public BrowserChildProcessHost { const GpuHostMsg_AcceleratedSurfaceSetIOSurface_Params& params); void OnAcceleratedSurfaceBuffersSwapped(int32 renderer_id, int32 render_view_id, - gfx::PluginWindowHandle window); + gfx::PluginWindowHandle window, + uint64 surface_id); #endif void ReplyToRenderer(const IPC::ChannelHandle& channel, diff --git a/chrome/browser/gpu_process_host_ui_shim.cc b/chrome/browser/gpu_process_host_ui_shim.cc index 71db44b..3d1223d 100644 --- a/chrome/browser/gpu_process_host_ui_shim.cc +++ b/chrome/browser/gpu_process_host_ui_shim.cc @@ -36,13 +36,6 @@ GpuProcessHostUIShim* GpuProcessHostUIShim::Get() { return Singleton<GpuProcessHostUIShim>::get(); } -int32 GpuProcessHostUIShim::NewRenderWidgetHostView( - GpuNativeWindowHandle parent) { - int32 routing_id = GetNextRoutingId(); - Send(new GpuMsg_NewRenderWidgetHostView(parent, routing_id)); - return routing_id; -} - bool GpuProcessHostUIShim::Send(IPC::Message* msg) { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, @@ -74,3 +67,19 @@ void GpuProcessHostUIShim::CollectGraphicsInfoAsynchronously() { FROM_HERE, new SendOnIOThreadTask(new GpuMsg_CollectGraphicsInfo())); } + +void GpuProcessHostUIShim::SendAboutGpuCrash() { + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + new SendOnIOThreadTask(new GpuMsg_Crash())); +} + +void GpuProcessHostUIShim::SendAboutGpuHang() { + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + new SendOnIOThreadTask(new GpuMsg_Hang())); +} diff --git a/chrome/browser/gpu_process_host_ui_shim.h b/chrome/browser/gpu_process_host_ui_shim.h index 3e5d93b..f43b41d 100644 --- a/chrome/browser/gpu_process_host_ui_shim.h +++ b/chrome/browser/gpu_process_host_ui_shim.h @@ -12,7 +12,6 @@ // shuttling messages between the browser and GPU processes. #include "base/singleton.h" -#include "chrome/common/gpu_native_window_handle.h" #include "chrome/common/message_router.h" #include "ipc/ipc_channel.h" #include "gfx/native_widget_types.h" @@ -25,10 +24,6 @@ class GpuProcessHostUIShim : public IPC::Channel::Sender, int32 GetNextRoutingId(); - // Creates the new remote view and returns the routing ID for the view, or 0 - // on failure. - int32 NewRenderWidgetHostView(GpuNativeWindowHandle parent); - // IPC::Channel::Sender implementation. virtual bool Send(IPC::Message* msg); @@ -46,6 +41,13 @@ class GpuProcessHostUIShim : public IPC::Channel::Sender, // graphics card. void CollectGraphicsInfoAsynchronously(); + // Tells the GPU process to crash. Useful for testing. + void SendAboutGpuCrash(); + + // Tells the GPU process to let its main thread enter an infinite loop. + // Useful for testing. + void SendAboutGpuHang(); + private: friend struct DefaultSingletonTraits<GpuProcessHostUIShim>; diff --git a/chrome/browser/gtk/about_chrome_dialog.cc b/chrome/browser/gtk/about_chrome_dialog.cc index 29f9159..cf22f23 100644 --- a/chrome/browser/gtk/about_chrome_dialog.cc +++ b/chrome/browser/gtk/about_chrome_dialog.cc @@ -22,7 +22,6 @@ #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_version_info.h" #include "chrome/common/url_constants.h" -#include "gfx/gtk_util.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" @@ -149,7 +148,7 @@ void ShowAboutDialogForProfile(GtkWindow* parent, Profile* profile) { GtkWidget* text_vbox = gtk_vbox_new(FALSE, kExtraLineSpacing); - GdkColor black = gfx::kGdkBlack; + GdkColor black = gtk_util::kGdkBlack; GtkWidget* product_label = MakeMarkupLabel( "<span font_desc=\"18\" style=\"normal\">%s</span>", l10n_util::GetStringUTF8(IDS_PRODUCT_NAME)); diff --git a/chrome/browser/gtk/accelerators_gtk.cc b/chrome/browser/gtk/accelerators_gtk.cc index 9d7a5b9..c0a2f73 100644 --- a/chrome/browser/gtk/accelerators_gtk.cc +++ b/chrome/browser/gtk/accelerators_gtk.cc @@ -8,7 +8,7 @@ #include <gdk/gdkkeysyms.h> #include <X11/XF86keysym.h> -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" namespace { diff --git a/chrome/browser/gtk/back_forward_button_gtk.cc b/chrome/browser/gtk/back_forward_button_gtk.cc index b55b587..29d6ddd 100644 --- a/chrome/browser/gtk/back_forward_button_gtk.cc +++ b/chrome/browser/gtk/back_forward_button_gtk.cc @@ -8,7 +8,7 @@ #include "app/l10n_util.h" #include "base/message_loop.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/back_forward_menu_model.h" #include "chrome/browser/browser.h" #include "chrome/browser/gtk/gtk_theme_provider.h" diff --git a/chrome/browser/gtk/bookmark_bar_gtk_interactive_uitest.cc b/chrome/browser/gtk/bookmark_bar_gtk_interactive_uitest.cc index f3bffa2..0fc9101 100644 --- a/chrome/browser/gtk/bookmark_bar_gtk_interactive_uitest.cc +++ b/chrome/browser/gtk/bookmark_bar_gtk_interactive_uitest.cc @@ -36,9 +36,7 @@ IN_PROC_BROWSER_TEST_F(BookmarkBarGtkBrowserTest, FindBarTest) { // Create new tab with an arbitrary URL. GURL url = test_server()->GetURL(kSimplePage); - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - browser()->AddTabWithURL(¶ms); - EXPECT_EQ(browser(), params.target); + browser()->AddSelectedTabWithURL(url, PageTransition::TYPED); // Switch back to the NTP with the active findbar. browser()->SelectTabContentsAt(1, false); diff --git a/chrome/browser/gtk/bookmark_bar_gtk_unittest.cc b/chrome/browser/gtk/bookmark_bar_gtk_unittest.cc index 1fd0611..fa04251 100644 --- a/chrome/browser/gtk/bookmark_bar_gtk_unittest.cc +++ b/chrome/browser/gtk/bookmark_bar_gtk_unittest.cc @@ -41,11 +41,12 @@ class BookmarkBarGtkUnittest : public ::testing::Test { } virtual void TearDown() { + message_loop_.RunAllPending(); + bookmark_bar_.reset(); origin_provider_.reset(); browser_.reset(); profile_.reset(); - message_loop_.RunAllPending(); } MessageLoopForUI message_loop_; diff --git a/chrome/browser/gtk/bookmark_bubble_gtk.cc b/chrome/browser/gtk/bookmark_bubble_gtk.cc index 6c57dc0..33cf4f5 100644 --- a/chrome/browser/gtk/bookmark_bubble_gtk.cc +++ b/chrome/browser/gtk/bookmark_bubble_gtk.cc @@ -24,7 +24,6 @@ #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/profile.h" #include "chrome/common/notification_service.h" -#include "gfx/gtk_util.h" #include "grit/generated_resources.h" namespace { @@ -78,7 +77,7 @@ void BookmarkBubbleGtk::Observe(NotificationType type, } else { for (std::vector<GtkWidget*>::iterator it = labels_.begin(); it != labels_.end(); ++it) { - gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, &gfx::kGdkBlack); + gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, >k_util::kGdkBlack); } } } diff --git a/chrome/browser/gtk/bookmark_menu_controller_gtk.cc b/chrome/browser/gtk/bookmark_menu_controller_gtk.cc index f966719..38fc8ef 100644 --- a/chrome/browser/gtk/bookmark_menu_controller_gtk.cc +++ b/chrome/browser/gtk/bookmark_menu_controller_gtk.cc @@ -140,7 +140,9 @@ void BookmarkMenuController::BuildMenu(const BookmarkNode* parent, start_child_index < parent->GetChildCount()); g_signal_connect(menu, "button-press-event", - G_CALLBACK(OnButtonPressedThunk), this); + G_CALLBACK(OnMenuButtonPressedOrReleasedThunk), this); + g_signal_connect(menu, "button-release-event", + G_CALLBACK(OnMenuButtonPressedOrReleasedThunk), this); for (int i = start_child_index; i < parent->GetChildCount(); ++i) { const BookmarkNode* node = parent->GetChild(i); @@ -203,18 +205,22 @@ void BookmarkMenuController::BuildMenu(const BookmarkNode* parent, } } -gboolean BookmarkMenuController::OnButtonPressed( +gboolean BookmarkMenuController::OnMenuButtonPressedOrReleased( GtkWidget* sender, GdkEventButton* event) { - if (event->button == 1) + // Handle middle mouse downs and right mouse ups. + if (!((event->button == 2 && event->type == GDK_BUTTON_RELEASE) || + (event->button == 3 && event->type == GDK_BUTTON_PRESS))) { return FALSE; + } ignore_button_release_ = false; GtkMenuShell* menu_shell = GTK_MENU_SHELL(sender); // If the cursor is outside our bounds, pass this event up to the parent. if (!gtk_util::WidgetContainsCursor(sender)) { if (menu_shell->parent_menu_shell) { - return OnButtonPressed(menu_shell->parent_menu_shell, event); + return OnMenuButtonPressedOrReleased(menu_shell->parent_menu_shell, + event); } else { // We are the top level menu; we can propagate no further. return FALSE; @@ -235,7 +241,7 @@ gboolean BookmarkMenuController::OnButtonPressed( const BookmarkNode* node = menu_item ? GetNodeFromMenuItem(menu_item) : NULL; - if (event->button == 2 && node) { + if (event->button == 2 && node && node->is_folder()) { bookmark_utils::OpenAll(parent_window_, profile_, page_navigator_, node, NEW_BACKGROUND_TAB); diff --git a/chrome/browser/gtk/bookmark_menu_controller_gtk.h b/chrome/browser/gtk/bookmark_menu_controller_gtk.h index 21f2bb9..cf8443c 100644 --- a/chrome/browser/gtk/bookmark_menu_controller_gtk.h +++ b/chrome/browser/gtk/bookmark_menu_controller_gtk.h @@ -68,12 +68,11 @@ class BookmarkMenuController : public BaseBookmarkModelObserver, void NavigateToMenuItem(GtkWidget* menu_item, WindowOpenDisposition disposition); - // Button press and release events for a GtkMenu and GtkMenuItem, - // respectively. We have to override these separate from OnMenuItemActivated - // because we need to handle right clicks and opening bookmarks with - // different dispositions. - CHROMEGTK_CALLBACK_1(BookmarkMenuController, gboolean, OnButtonPressed, - GdkEventButton*); + // Button press and release events for a GtkMenu. + CHROMEGTK_CALLBACK_1(BookmarkMenuController, gboolean, + OnMenuButtonPressedOrReleased, GdkEventButton*); + + // Button release event for a GtkMenuItem. CHROMEGTK_CALLBACK_1(BookmarkMenuController, gboolean, OnButtonReleased, GdkEventButton*); diff --git a/chrome/browser/gtk/bookmark_utils_gtk.cc b/chrome/browser/gtk/bookmark_utils_gtk.cc index 73190b0..db7aa98 100644 --- a/chrome/browser/gtk/bookmark_utils_gtk.cc +++ b/chrome/browser/gtk/bookmark_utils_gtk.cc @@ -282,7 +282,13 @@ void SetButtonTextColors(GtkWidget* label, GtkThemeProvider* provider) { } else { GdkColor color = provider->GetGdkColor( BrowserThemeProvider::COLOR_BOOKMARK_TEXT); - gtk_util::SetLabelColor(label, &color); + gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &color); + gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &color); + + // Because the prelight state is a white image that doesn't change by the + // theme, force the text color to black when it would be used. + gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, >k_util::kGdkBlack); + gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, >k_util::kGdkBlack); } } diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc index 53776cf..4ee92b1 100644 --- a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc +++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc @@ -83,7 +83,7 @@ class BrowserActionButton : public NotificationObserver, public MenuGtk::Delegate { public: BrowserActionButton(BrowserActionsToolbarGtk* toolbar, - Extension* extension, + const Extension* extension, GtkThemeProvider* theme_provider) : toolbar_(toolbar), extension_(extension), @@ -143,7 +143,7 @@ class BrowserActionButton : public NotificationObserver, GtkWidget* widget() { return alignment_.get(); } - Extension* extension() { return extension_; } + const Extension* extension() { return extension_; } // NotificationObserver implementation. void Observe(NotificationType type, @@ -312,7 +312,7 @@ class BrowserActionButton : public NotificationObserver, BrowserActionsToolbarGtk* toolbar_; // The extension that contains this browser action. - Extension* extension_; + const Extension* extension_; // The button for this browser action. scoped_ptr<CustomDrawButton> button_; @@ -497,8 +497,8 @@ void BrowserActionsToolbarGtk::SetContainerWidth() { SetButtonHBoxWidth(WidthForIconCount(showing_actions)); } -void BrowserActionsToolbarGtk::CreateButtonForExtension(Extension* extension, - int index) { +void BrowserActionsToolbarGtk::CreateButtonForExtension( + const Extension* extension, int index) { if (!ShouldDisplayBrowserAction(extension)) return; @@ -535,7 +535,7 @@ void BrowserActionsToolbarGtk::CreateButtonForExtension(Extension* extension, } GtkWidget* BrowserActionsToolbarGtk::GetBrowserActionWidget( - Extension* extension) { + const Extension* extension) { ExtensionButtonMap::iterator it = extension_button_map_.find( extension->id()); if (it == extension_button_map_.end()) @@ -544,7 +544,8 @@ GtkWidget* BrowserActionsToolbarGtk::GetBrowserActionWidget( return it->second.get()->widget(); } -void BrowserActionsToolbarGtk::RemoveButtonForExtension(Extension* extension) { +void BrowserActionsToolbarGtk::RemoveButtonForExtension( + const Extension* extension) { if (extension_button_map_.erase(extension->id())) UpdateVisibility(); UpdateChevronVisibility(); @@ -558,7 +559,7 @@ void BrowserActionsToolbarGtk::UpdateVisibility() { } bool BrowserActionsToolbarGtk::ShouldDisplayBrowserAction( - Extension* extension) { + const Extension* extension) { // Only display incognito-enabled extensions while in incognito mode. return (!profile_->IsOffTheRecord() || profile_->GetExtensionsService()->IsIncognitoEnabled(extension)); @@ -577,7 +578,7 @@ void BrowserActionsToolbarGtk::AnimateToShowNIcons(int count) { resize_animation_.Show(); } -void BrowserActionsToolbarGtk::BrowserActionAdded(Extension* extension, +void BrowserActionsToolbarGtk::BrowserActionAdded(const Extension* extension, int index) { overflow_menu_.reset(); @@ -594,7 +595,8 @@ void BrowserActionsToolbarGtk::BrowserActionAdded(Extension* extension, } } -void BrowserActionsToolbarGtk::BrowserActionRemoved(Extension* extension) { +void BrowserActionsToolbarGtk::BrowserActionRemoved( + const Extension* extension) { overflow_menu_.reset(); if (drag_button_ != NULL) { @@ -610,7 +612,7 @@ void BrowserActionsToolbarGtk::BrowserActionRemoved(Extension* extension) { } } -void BrowserActionsToolbarGtk::BrowserActionMoved(Extension* extension, +void BrowserActionsToolbarGtk::BrowserActionMoved(const Extension* extension, int index) { // We initiated this move action, and have already moved the button. if (drag_button_ != NULL) @@ -648,7 +650,7 @@ void BrowserActionsToolbarGtk::AnimationEnded(const Animation* animation) { } void BrowserActionsToolbarGtk::ExecuteCommand(int command_id) { - Extension* extension = model_->GetExtensionByIndex(command_id); + const Extension* extension = model_->GetExtensionByIndex(command_id); ExtensionAction* browser_action = extension->browser_action(); int tab_id = GetCurrentTabId(); @@ -870,7 +872,7 @@ gboolean BrowserActionsToolbarGtk::OnOverflowButtonPress( if (profile_->IsOffTheRecord()) model_index = model_->IncognitoIndexToOriginal(i); - Extension* extension = model_->GetExtensionByIndex(model_index); + const Extension* extension = model_->GetExtensionByIndex(model_index); BrowserActionButton* button = extension_button_map_[extension->id()].get(); overflow_menu_model_->AddItem(model_index, UTF8ToUTF16(extension->name())); @@ -910,7 +912,7 @@ gboolean BrowserActionsToolbarGtk::OnOverflowMenuButtonPress( if (profile_->IsOffTheRecord()) item_index = model_->IncognitoIndexToOriginal(item_index); - Extension* extension = model_->GetExtensionByIndex(item_index); + const Extension* extension = model_->GetExtensionByIndex(item_index); ExtensionButtonMap::iterator it = extension_button_map_.find( extension->id()); if (it == extension_button_map_.end()) { diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.h b/chrome/browser/gtk/browser_actions_toolbar_gtk.h index 455efee..72cf960 100644 --- a/chrome/browser/gtk/browser_actions_toolbar_gtk.h +++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.h @@ -47,7 +47,7 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer, // Returns the widget in use by the BrowserActionButton corresponding to // |extension|. Used in positioning the ExtensionInstalledBubble for // BrowserActions. - GtkWidget* GetBrowserActionWidget(Extension* extension); + GtkWidget* GetBrowserActionWidget(const Extension* extension); int button_count() { return extension_button_map_.size(); } @@ -83,10 +83,10 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer, // Create the UI for a single browser action. This will stick the button // at the end of the toolbar. - void CreateButtonForExtension(Extension* extension, int index); + void CreateButtonForExtension(const Extension* extension, int index); // Delete resources associated with UI for a browser action. - void RemoveButtonForExtension(Extension* extension); + void RemoveButtonForExtension(const Extension* extension); // Change the visibility of widget() based on whether we have any buttons // to show. @@ -102,12 +102,12 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer, // Returns true if this extension should be shown in this toolbar. This can // return false if we are in an incognito window and the extension is disabled // for incognito. - bool ShouldDisplayBrowserAction(Extension* extension); + bool ShouldDisplayBrowserAction(const Extension* extension); // ExtensionToolbarModel::Observer implementation. - virtual void BrowserActionAdded(Extension* extension, int index); - virtual void BrowserActionRemoved(Extension* extension); - virtual void BrowserActionMoved(Extension* extension, int index); + virtual void BrowserActionAdded(const Extension* extension, int index); + virtual void BrowserActionRemoved(const Extension* extension); + virtual void BrowserActionMoved(const Extension* extension, int index); virtual void ModelLoaded(); // AnimationDelegate implementation. diff --git a/chrome/browser/gtk/browser_titlebar.cc b/chrome/browser/gtk/browser_titlebar.cc index 9556ae6..dea24e6 100644 --- a/chrome/browser/gtk/browser_titlebar.cc +++ b/chrome/browser/gtk/browser_titlebar.cc @@ -17,7 +17,7 @@ #include "base/string_piece.h" #include "base/string_tokenizer.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/encoding_menu_controller.h" #include "chrome/browser/gtk/accelerators_gtk.h" @@ -201,6 +201,8 @@ BrowserTitlebar::BrowserTitlebar(BrowserWindowGtk* browser_window, titlebar_right_buttons_vbox_(NULL), titlebar_left_buttons_hbox_(NULL), titlebar_right_buttons_hbox_(NULL), + titlebar_left_spy_frame_(NULL), + titlebar_right_spy_frame_(NULL), top_padding_left_(NULL), top_padding_right_(NULL), app_mode_favicon_(NULL), @@ -214,17 +216,17 @@ BrowserTitlebar::BrowserTitlebar(BrowserWindowGtk* browser_window, void BrowserTitlebar::Init() { // The widget hierarchy is shown below. // - // +- EventBox (container_) -------------------------------------+ - // +- HBox (container_hbox_) ------------------------------------+ - // |+ VBox ---++- Algn. -++- Alignment --------------++ VBox ---+| - // || titlebar||+ Image +|| (titlebar_alignment_) || titlebar|| - // || left ||| ||| || right || - // || button |||spy_guy||| || button || - // || vbox ||| |||+- TabStripGtk ---------+|| vbox || - // || ||| )8\ |||| tab tab tabclose ||| || - // || ||+-------+||+------------------------+|| || - // |+---------++---------++--------------------------++---------+| - // +-------------------------------------------------------------+ + // +- EventBox (container_) ------------------------------------------------+ + // +- HBox (container_hbox_) -----------------------------------------------+ + // |+ VBox ---++- Algn. -++- Alignment --------------++- Algn. -++ VBox ---+| + // || titlebar||titlebar || (titlebar_alignment_) ||titlebar || titlebar|| + // || left ||left || ||right || right || + // || button ||spy || ||spy || button || + // || vbox ||frame ||+- TabStripGtk ---------+||frame || vbox || + // || || || tab tab tabclose ||| || || + // || || ||+------------------------+|| || || + // |+---------++---------++--------------------------++---------++---------+| + // +------------------------------------------------------------------------+ // // There are two vboxes on either side of |container_hbox_| because when the // desktop is GNOME, the button placement is configurable based on a metacity @@ -246,6 +248,9 @@ void BrowserTitlebar::Init() { // |+---------------------------------------------+| // +-----------------------------------------------+ // + // The two spy alignments are only allocated if this window is an incognito + // window. Only one of them holds the spy image. + // // If we're a popup window or in app mode, we don't display the spy guy or // the tab strip. Instead, put an hbox in titlebar_alignment_ in place of // the tab strip. @@ -269,10 +274,27 @@ void BrowserTitlebar::Init() { g_signal_connect(window_, "window-state-event", G_CALLBACK(OnWindowStateChangedThunk), this); - // Allocate the two button boxes on the left and right parts of the bar. + // Allocate the two button boxes on the left and right parts of the bar, and + // spyguy frames in case of incognito mode. titlebar_left_buttons_vbox_ = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(container_hbox_), titlebar_left_buttons_vbox_, FALSE, FALSE, 0); + if (browser_window_->browser()->profile()->IsOffTheRecord() && + browser_window_->browser()->type() == Browser::TYPE_NORMAL) { + titlebar_left_spy_frame_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); + gtk_widget_set_no_show_all(titlebar_left_spy_frame_, TRUE); + gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_left_spy_frame_), 0, + kOTRBottomSpacing, kOTRSideSpacing, kOTRSideSpacing); + gtk_box_pack_start(GTK_BOX(container_hbox_), titlebar_left_spy_frame_, + FALSE, FALSE, 0); + + titlebar_right_spy_frame_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); + gtk_widget_set_no_show_all(titlebar_right_spy_frame_, TRUE); + gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_right_spy_frame_), 0, + kOTRBottomSpacing, kOTRSideSpacing, kOTRSideSpacing); + gtk_box_pack_end(GTK_BOX(container_hbox_), titlebar_right_spy_frame_, + FALSE, FALSE, 0); + } titlebar_right_buttons_vbox_ = gtk_vbox_new(FALSE, 0); gtk_box_pack_end(GTK_BOX(container_hbox_), titlebar_right_buttons_vbox_, FALSE, FALSE, 0); @@ -281,20 +303,6 @@ void BrowserTitlebar::Init() { // the default value (anywhere else). Singleton<GConfTitlebarListener>()->SetTitlebarButtons(this); - if (browser_window_->browser()->profile()->IsOffTheRecord() && - browser_window_->browser()->type() == Browser::TYPE_NORMAL) { - GtkWidget* spy_guy = gtk_image_new_from_pixbuf(GetOTRAvatar()); - gtk_misc_set_alignment(GTK_MISC(spy_guy), 0.0, 1.0); - GtkWidget* spy_frame = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); - // We use this alignment rather than setting padding on the GtkImage because - // the image's intrinsic padding doesn't clip the pixbuf during painting. - gtk_alignment_set_padding(GTK_ALIGNMENT(spy_frame), 0, - kOTRBottomSpacing, kOTRSideSpacing, kOTRSideSpacing); - gtk_widget_set_size_request(spy_guy, -1, 0); - gtk_container_add(GTK_CONTAINER(spy_frame), spy_guy); - gtk_box_pack_start(GTK_BOX(container_hbox_), spy_frame, FALSE, FALSE, 0); - } - // We use an alignment to control the titlebar height. titlebar_alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); if (browser_window_->browser()->type() == Browser::TYPE_NORMAL) { @@ -374,6 +382,8 @@ void BrowserTitlebar::BuildButtons(const std::string& button_string) { bool left_side = true; StringTokenizer tokenizer(button_string, ":,"); tokenizer.set_options(StringTokenizer::RETURN_DELIMS); + int left_count = 0; + int right_count = 0; while (tokenizer.GetNext()) { if (tokenizer.token_is_delim()) { if (*tokenizer.token_begin() == ':') @@ -381,6 +391,7 @@ void BrowserTitlebar::BuildButtons(const std::string& button_string) { } else { base::StringPiece token = tokenizer.token_piece(); if (token == "minimize") { + (left_side ? left_count : right_count)++; GtkWidget* parent_box = GetButtonHBox(left_side); minimize_button_.reset( BuildTitlebarButton(IDR_MINIMIZE, IDR_MINIMIZE_P, @@ -390,6 +401,7 @@ void BrowserTitlebar::BuildButtons(const std::string& button_string) { gtk_widget_size_request(minimize_button_->widget(), &minimize_button_req_); } else if (token == "maximize") { + (left_side ? left_count : right_count)++; GtkWidget* parent_box = GetButtonHBox(left_side); restore_button_.reset( BuildTitlebarButton(IDR_RESTORE, IDR_RESTORE_P, @@ -405,6 +417,7 @@ void BrowserTitlebar::BuildButtons(const std::string& button_string) { gtk_widget_size_request(restore_button_->widget(), &restore_button_req_); } else if (token == "close") { + (left_side ? left_count : right_count)++; GtkWidget* parent_box = GetButtonHBox(left_side); close_button_.reset( BuildTitlebarButton(IDR_CLOSE, IDR_CLOSE_P, @@ -419,6 +432,30 @@ void BrowserTitlebar::BuildButtons(const std::string& button_string) { } } + // If we are in incognito mode, add the spy guy to either the end of the left + // or the beginning of the right depending on which side has fewer buttons. + if (browser_window_->browser()->profile()->IsOffTheRecord() && + browser_window_->browser()->type() == Browser::TYPE_NORMAL) { + GtkWidget* spy_guy = gtk_image_new_from_pixbuf(GetOTRAvatar()); + gtk_misc_set_alignment(GTK_MISC(spy_guy), 0.0, 1.0); + gtk_widget_set_size_request(spy_guy, -1, 0); + gtk_widget_show(spy_guy); + + // Remove previous state. + gtk_util::RemoveAllChildren(titlebar_left_spy_frame_); + gtk_util::RemoveAllChildren(titlebar_right_spy_frame_); + + if (right_count > left_count) { + gtk_container_add(GTK_CONTAINER(titlebar_left_spy_frame_), spy_guy); + gtk_widget_show(titlebar_left_spy_frame_); + gtk_widget_hide(titlebar_right_spy_frame_); + } else { + gtk_container_add(GTK_CONTAINER(titlebar_right_spy_frame_), spy_guy); + gtk_widget_show(titlebar_right_spy_frame_); + gtk_widget_hide(titlebar_left_spy_frame_); + } + } + // Now show the correct widgets in the two hierarchies. gtk_widget_show_all(titlebar_left_buttons_vbox_); gtk_widget_show_all(titlebar_right_buttons_vbox_); @@ -644,10 +681,10 @@ void BrowserTitlebar::UpdateTextColor() { BrowserThemeProvider::COLOR_FRAME_INACTIVE); } GdkColor text_color = PickLuminosityContrastingColor( - &frame_color, &gfx::kGdkWhite, &gfx::kGdkBlack); + &frame_color, >k_util::kGdkWhite, >k_util::kGdkBlack); gtk_util::SetLabelColor(app_mode_title_, &text_color); } else { - gtk_util::SetLabelColor(app_mode_title_, &gfx::kGdkWhite); + gtk_util::SetLabelColor(app_mode_title_, >k_util::kGdkWhite); } } diff --git a/chrome/browser/gtk/browser_titlebar.h b/chrome/browser/gtk/browser_titlebar.h index 431eddf..c6da855 100644 --- a/chrome/browser/gtk/browser_titlebar.h +++ b/chrome/browser/gtk/browser_titlebar.h @@ -177,6 +177,12 @@ class BrowserTitlebar : public NotificationObserver, GtkWidget* titlebar_left_buttons_hbox_; GtkWidget* titlebar_right_buttons_hbox_; + // Spy frame. One of these frames holds the spy guy in incognito mode. It's + // the side with the least buttons. These are NULL when we aren't in + // incognito mode. + GtkWidget* titlebar_left_spy_frame_; + GtkWidget* titlebar_right_spy_frame_; + // Padding between the titlebar buttons and the top of the screen. Only show // when not maximized. GtkWidget* top_padding_left_; diff --git a/chrome/browser/gtk/browser_toolbar_gtk.cc b/chrome/browser/gtk/browser_toolbar_gtk.cc index a710bdc..09fb457 100644 --- a/chrome/browser/gtk/browser_toolbar_gtk.cc +++ b/chrome/browser/gtk/browser_toolbar_gtk.cc @@ -17,7 +17,7 @@ #include "base/logging.h" #include "base/path_service.h" #include "base/singleton.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/encoding_menu_controller.h" #include "chrome/browser/gtk/accelerators_gtk.h" diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc index 1bbc883..6d537c9 100644 --- a/chrome/browser/gtk/browser_window_gtk.cc +++ b/chrome/browser/gtk/browser_window_gtk.cc @@ -8,7 +8,6 @@ #include <string> -#include "app/gtk_util.h" #include "app/keyboard_codes.h" #include "app/l10n_util.h" #include "base/base_paths.h" @@ -21,7 +20,7 @@ #include "base/string_util.h" #include "base/time.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/app_modal_dialog_queue.h" #include "chrome/browser/autocomplete/autocomplete_edit_view.h" #include "chrome/browser/bookmarks/bookmark_utils.h" @@ -81,16 +80,12 @@ #include "chrome/common/native_web_keyboard_event.h" #include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" -#include "gfx/canvas_skia_paint.h" -#include "gfx/color_utils.h" -#include "gfx/gtk_util.h" #include "gfx/rect.h" #include "gfx/skia_utils_gtk.h" #include "grit/app_resources.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" -#include "third_party/skia/include/effects/SkGradientShader.h" namespace { @@ -101,6 +96,8 @@ const int kLoadingAnimationFrameTimeMs = 30; // matches the value in Views. const int kDefaultDevToolsHeight = 200; +const int kMinDevToolsHeight = 50; + const char* kBrowserWindowKey = "__BROWSER_WINDOW_GTK__"; // The frame border is only visible in restored mode and is hardcoded to 4 px @@ -261,10 +258,6 @@ GdkCursorType GdkWindowEdgeToGdkCursorType(GdkWindowEdge edge) { return GDK_LAST_CURSOR; } -GdkColor SkColorToGdkColor(const SkColor& color) { - return gfx::SkColorToGdkColor(color); -} - // A helper method for setting the GtkWindow size that should be used in place // of calling gtk_window_resize directly. This is done to avoid a WM "feature" // where setting the window size to the monitor size causes the WM to set the @@ -345,7 +338,7 @@ BrowserWindowGtk::BrowserWindowGtk(Browser* browser) maximize_after_show_(false), suppress_window_raise_(false), accel_group_(NULL), - infobar_animation_(this) { + infobar_arrow_model_(this) { // We register first so that other views like the toolbar can use the // is_active() function in their ActiveWindowChanged() handlers. ActiveWindowWatcherX::AddObserver(this); @@ -389,8 +382,6 @@ BrowserWindowGtk::BrowserWindowGtk(Browser* browser) SetBackgroundColor(); HideUnsupportedWindowFeatures(); - infobar_animation_.SetTweenType(Tween::LINEAR); - registrar_.Add(this, NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, NotificationService::AllSources()); } @@ -1137,19 +1128,15 @@ void BrowserWindowGtk::Paste() { } void BrowserWindowGtk::ShowInstant(TabContents* preview_contents) { - // TODO: implement me - NOTIMPLEMENTED(); + contents_container_->SetPreviewContents(preview_contents); } void BrowserWindowGtk::HideInstant() { - // TODO: implement me - NOTIMPLEMENTED(); + contents_container_->PopPreviewContents(); } gfx::Rect BrowserWindowGtk::GetInstantBounds() { - // TODO: implement me - NOTIMPLEMENTED(); - return gfx::Rect(); + return gtk_util::GetWidgetScreenBounds(contents_container_->widget()); } void BrowserWindowGtk::ConfirmBrowserCloseWithPendingDownloads() { @@ -1621,6 +1608,8 @@ void BrowserWindowGtk::InitWidgets() { int split_offset = g_browser_process->local_state()->GetInteger( prefs::kDevToolsSplitLocation); if (split_offset != -1) { + if (split_offset < kMinDevToolsHeight) + split_offset = kMinDevToolsHeight; gtk_paned_set_position(GTK_PANED(contents_split_), split_offset); } else { gtk_widget_set_size_request(devtools_container_->widget(), -1, @@ -1632,7 +1621,7 @@ void BrowserWindowGtk::InitWidgets() { // Set a white background so during startup the user sees white in the // content area before we get a TabContents in place. gtk_widget_modify_bg(render_area_event_box_, GTK_STATE_NORMAL, - &gfx::kGdkWhite); + >k_util::kGdkWhite); gtk_container_add(GTK_CONTAINER(render_area_event_box_), render_area_floating_container_); gtk_widget_show(render_area_event_box_); @@ -1684,7 +1673,7 @@ void BrowserWindowGtk::SetBackgroundColor() { SkColor frame_color = theme_provider->GetColor(frame_color_id); // Paint the frame color on the left, right and bottom. - GdkColor frame_color_gdk = SkColorToGdkColor(frame_color); + GdkColor frame_color_gdk = gfx::SkColorToGdkColor(frame_color); gtk_widget_modify_bg(GTK_WIDGET(window_), GTK_STATE_NORMAL, &frame_color_gdk); @@ -1696,7 +1685,8 @@ void BrowserWindowGtk::SetBackgroundColor() { // color, override the prelight also. color_utils::HSL hsl = { -1, 0.5, 0.65 }; SkColor frame_prelight_color = color_utils::HSLShift(frame_color, hsl); - GdkColor frame_prelight_color_gdk = SkColorToGdkColor(frame_prelight_color); + GdkColor frame_prelight_color_gdk = + gfx::SkColorToGdkColor(frame_prelight_color); gtk_widget_modify_bg(contents_split_, GTK_STATE_PRELIGHT, &frame_prelight_color_gdk); @@ -1802,179 +1792,74 @@ void BrowserWindowGtk::SaveWindowPosition() { window_preferences->SetInteger("work_area_bottom", work_area.bottom()); } -void BrowserWindowGtk::AnimationEnded(const Animation* animation) { - InvalidateInfoBarBits(); -} - -void BrowserWindowGtk::AnimationProgressed(const Animation* animation) { - InvalidateInfoBarBits(); +void BrowserWindowGtk::SetInfoBarShowing(InfoBar* bar, bool animate) { + infobar_arrow_model_.ShowArrowFor(bar, animate); } -void BrowserWindowGtk::AnimationCanceled(const Animation* animation) { +void BrowserWindowGtk::PaintStateChanged() { InvalidateInfoBarBits(); } -void BrowserWindowGtk::SetInfoBarShowing( - const std::pair<SkColor, SkColor>* colors, - bool animate) { - if (colors) { - infobar_colors_ = *colors; - - if (animate) - infobar_animation_.Show(); - else - infobar_animation_.Reset(1.0); - } else { - if (animate) - infobar_animation_.Hide(); - else - infobar_animation_.Reset(); - } - - InvalidateInfoBarBits(); -} - -bool BrowserWindowGtk::ShouldDrawInfobarDropShadowOnRenderView() { - return infobar_animation_.GetCurrentValue() != 0.0 && - !(bookmark_bar_.get() && bookmark_bar_is_floating_); -} - void BrowserWindowGtk::InvalidateInfoBarBits() { gtk_widget_queue_draw(toolbar_border_); gtk_widget_queue_draw(toolbar_->widget()); - if (bookmark_bar_.get()) + if (bookmark_bar_.get() && !bookmark_bar_is_floating_) gtk_widget_queue_draw(bookmark_bar_->widget()); +} + +int BrowserWindowGtk::GetXPositionOfLocationIcon(GtkWidget* relative_to) { + GtkWidget* location_icon = toolbar_->GetLocationBarView()-> + location_icon_widget(); + int x = 0; + gtk_widget_translate_coordinates( + location_icon, relative_to, + (location_icon->allocation.width + 1) / 2, + 0, &x, NULL); - // Redraw the top bit of the render view area. - int width = contents_container_->widget()->allocation.width; - int height = gtk_util::kInfoBarDropShadowHeight; - gtk_widget_queue_draw_area(contents_container_->widget(), - 0, 0, width, height); + if (GTK_WIDGET_NO_WINDOW(relative_to)) + x += relative_to->allocation.x; + + return x; } gboolean BrowserWindowGtk::OnExposeDrawInfobarBits(GtkWidget* sender, GdkEventExpose* expose) { - double alpha = infobar_animation_.GetCurrentValue(); - if (alpha == 0.0) + if (!infobar_arrow_model_.NeedToDrawInfoBarArrow()) return FALSE; - GtkWidget* location_icon = toolbar_->GetLocationBarView()-> - location_icon_widget(); - int x = 0; - int top_y = 0; - // The offset between the vertical center of the location icon and the top - // of the arrow. - const int kArrowVerticalOffset = 4; - gtk_widget_translate_coordinates( - location_icon, sender, - location_icon->allocation.width / 2, - location_icon->allocation.height / 2 + kArrowVerticalOffset, - &x, &top_y); + int x = GetXPositionOfLocationIcon(sender); gfx::Rect toolbar_border(toolbar_border_->allocation); int y = 0; gtk_widget_translate_coordinates(toolbar_border_, sender, 0, toolbar_border.bottom(), NULL, &y); - - int arrow_height = y - top_y; - // The width of half the arrow. - const int kArrowWidth = 15; - - if (GTK_WIDGET_NO_WINDOW(sender)) { - x += sender->allocation.x; + if (GTK_WIDGET_NO_WINDOW(sender)) y += sender->allocation.y; - } - - SkPath path; - path.moveTo(SkPoint::Make(x - kArrowWidth, y)); - path.lineTo(SkPoint::Make(x, y - arrow_height)); - path.lineTo(SkPoint::Make(x + kArrowWidth, y)); - path.close(); - - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - - SkPoint grad_points[2]; - grad_points[0].set(SkIntToScalar(0), SkIntToScalar(y)); - grad_points[1].set(SkIntToScalar(0), - SkIntToScalar(y + InfoBar::kInfoBarHeight)); - - SkColor grad_colors[2]; - grad_colors[0] = SkColorSetA(infobar_colors_.first, alpha * 0xff); - grad_colors[1] = SkColorSetA(infobar_colors_.second, alpha * 0xff); - - SkShader* gradient_shader = SkGradientShader::CreateLinear( - grad_points, grad_colors, NULL, 2, SkShader::kMirror_TileMode); - paint.setShader(gradient_shader); - gradient_shader->unref(); - - gfx::CanvasSkiaPaint canvas(expose, false); - canvas.drawPath(path, paint); - - paint.setShader(NULL); - paint.setStyle(SkPaint::kStroke_Style); - // Smooth out the shadow. - paint.setAntiAlias(true); - - const int kMaxShading = 100; - const int kShadingPixels = 5; - - // The goal of all this mathematical trickery is to create a shadow for the - // arrow that looks decent. We want a shadow of a set width, and the thickest - // direction of that shadow needs to be perpendicular to the sides of the - // arrow. - double scale_factor = sqrt(static_cast<double>( - (arrow_height * arrow_height + kArrowWidth * kArrowWidth))) / - kShadingPixels; - double x_scale = arrow_height / scale_factor / kShadingPixels; - double y_scale = kArrowWidth / scale_factor / kShadingPixels; - for (int i = 1; i <= kShadingPixels; ++i) { - double x_backoff = x_scale * i; - double y_backoff = y_scale * i; - SkPath shadow_path; - shadow_path.moveTo(0, y - i); - // The +1 below makes sure the shadow hugs the left side of the arrow. - shadow_path.rLineTo(x - kArrowWidth - x_backoff + 1, 0); - shadow_path.rMoveTo(0, i - y_backoff); - shadow_path.rLineTo(kArrowWidth, -arrow_height); - // The -1.5 below makes sure the shadow hugs the right side of the arrow. - shadow_path.rLineTo(2 * x_backoff - 1.5, 0); - shadow_path.rLineTo(kArrowWidth, arrow_height); - shadow_path.rMoveTo(0, y_backoff - i); - shadow_path.rLineTo(sender->allocation.width, 0); - - int shading = (kShadingPixels - i + 1) * kMaxShading / kShadingPixels; - paint.setColor(SkColorSetARGB(alpha * shading, 0, 0, 0)); - canvas.drawPath(shadow_path, paint); - } + Profile* profile = browser()->profile(); + infobar_arrow_model_.Paint( + sender, expose, gfx::Point(x, y), + GtkThemeProvider::GetFrom(profile)->GetBorderColor()); return FALSE; } gboolean BrowserWindowGtk::OnBookmarkBarExpose(GtkWidget* sender, GdkEventExpose* expose) { - if (infobar_animation_.GetCurrentValue() == 0.0) + if (!infobar_arrow_model_.NeedToDrawInfoBarArrow()) return FALSE; - // This shares the same draw path as the other widgets when it's not floating. - if (!bookmark_bar_is_floating_) - return OnExposeDrawInfobarBits(sender, expose); + if (bookmark_bar_is_floating_) + return FALSE; - gfx::Point origin; - if (GTK_WIDGET_NO_WINDOW(sender)) - origin = gfx::Point(sender->allocation.x, sender->allocation.y); - cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(expose->window)); - gtk_util::DrawTopDropShadowForRenderView(cr, origin, gfx::Rect(expose->area)); - cairo_destroy(cr); - return FALSE; + return OnExposeDrawInfobarBits(sender, expose); } void BrowserWindowGtk::OnBookmarkBarSizeAllocate(GtkWidget* sender, GtkAllocation* allocation) { // The size of the bookmark bar affects how the infobar arrow is drawn on // the toolbar. - if (infobar_animation_.GetCurrentValue() != 0.0) + if (infobar_arrow_model_.NeedToDrawInfoBarArrow()) gtk_widget_queue_draw(toolbar_->widget()); } diff --git a/chrome/browser/gtk/browser_window_gtk.h b/chrome/browser/gtk/browser_window_gtk.h index ce6fd20..ce8e582 100644 --- a/chrome/browser/gtk/browser_window_gtk.h +++ b/chrome/browser/gtk/browser_window_gtk.h @@ -12,12 +12,12 @@ #include "app/active_window_watcher_x.h" #include "app/gtk_signal.h" -#include "app/slide_animation.h" #include "app/x11_util.h" #include "base/scoped_ptr.h" #include "base/timer.h" #include "build/build_config.h" #include "chrome/browser/browser_window.h" +#include "chrome/browser/gtk/infobar_arrow_model.h" #include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/tabs/tab_strip_model_observer.h" #include "chrome/common/notification_registrar.h" @@ -45,7 +45,7 @@ class BrowserWindowGtk : public BrowserWindow, public NotificationObserver, public TabStripModelObserver, public ActiveWindowWatcherX::Observer, - public AnimationDelegate { + public InfoBarArrowModel::Observer { public: explicit BrowserWindowGtk(Browser* browser); virtual ~BrowserWindowGtk(); @@ -145,10 +145,8 @@ class BrowserWindowGtk : public BrowserWindow, // Overridden from ActiveWindowWatcher::Observer. virtual void ActiveWindowChanged(GdkWindow* active_window); - // Overridden from AnimationDelegate. - virtual void AnimationEnded(const Animation* animation); - virtual void AnimationProgressed(const Animation* animation); - virtual void AnimationCanceled(const Animation* animation); + // Overridden from InfoBarArrowModel::Observer. + virtual void PaintStateChanged(); // Accessor for the tab strip. TabStripGtk* tabstrip() const { return tabstrip_.get(); } @@ -178,16 +176,9 @@ class BrowserWindowGtk : public BrowserWindow, // else for the custom frame. void ResetCustomFrameCursor(); - // Toggles whether an infobar is showing. If |colors| is NULL, then no infobar - // is showing. When non-NULL, |colors| describes the gradient stop colors for - // the showing infobar. - // |animate| controls whether we animate to the new state set by |colors|. - void SetInfoBarShowing(const std::pair<SkColor, SkColor>* colors, - bool animate); - - // Called by the RenderViewHostDelegate::View (TabContentsViewGtk in our case) - // to decide whether to draw a drop shadow on the render view. - bool ShouldDrawInfobarDropShadowOnRenderView(); + // Toggles whether an infobar is showing. + // |animate| controls whether we animate to the new state set by |bar|. + void SetInfoBarShowing(InfoBar* bar, bool animate); // Returns the BrowserWindowGtk registered with |window|. static BrowserWindowGtk* GetBrowserWindowForNativeWindow( @@ -221,6 +212,10 @@ class BrowserWindowGtk : public BrowserWindow, // redraw when it should. void QueueToolbarRedraw(); + // Get the position where the infobar arrow should be anchored in + // |relative_to| coordinates. This is the middle of the omnibox location icon. + int GetXPositionOfLocationIcon(GtkWidget* relative_to); + protected: virtual void DestroyBrowser(); // Top level window. @@ -480,11 +475,9 @@ class BrowserWindowGtk : public BrowserWindow, scoped_ptr<FullscreenExitBubbleGtk> fullscreen_exit_bubble_; - // The top and bottom colors for the infobar gradient, if there is an - // infobar showing. - std::pair<SkColor, SkColor> infobar_colors_; - - SlideAnimation infobar_animation_; + // The model that tracks the paint state of the arrow for the infobar + // directly below the toolbar. + InfoBarArrowModel infobar_arrow_model_; DISALLOW_COPY_AND_ASSIGN(BrowserWindowGtk); }; diff --git a/chrome/browser/gtk/certificate_viewer.cc b/chrome/browser/gtk/certificate_viewer.cc index ca58b5c..905eee3 100644 --- a/chrome/browser/gtk/certificate_viewer.cc +++ b/chrome/browser/gtk/certificate_viewer.cc @@ -10,7 +10,6 @@ #include <vector> #include "app/l10n_util.h" -#include "base/gtk_util.h" #include "base/i18n/time_formatting.h" #include "base/nss_util.h" #include "base/scoped_ptr.h" @@ -20,6 +19,7 @@ #include "chrome/browser/gtk/certificate_dialogs.h" #include "chrome/browser/gtk/gtk_util.h" #include "chrome/common/net/x509_certificate_model.h" +#include "gfx/gtk_util.h" #include "grit/generated_resources.h" #include "net/base/x509_certificate.h" @@ -161,7 +161,7 @@ CertificateViewer::CertificateViewer( GTK_NOTEBOOK(notebook_), general_page_vbox_, gtk_label_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8( IDS_CERT_INFO_GENERAL_TAB_LABEL)).c_str())); @@ -169,7 +169,7 @@ CertificateViewer::CertificateViewer( GTK_NOTEBOOK(notebook_), details_page_vbox_, gtk_label_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8( IDS_CERT_INFO_DETAILS_TAB_LABEL)).c_str())); @@ -606,7 +606,7 @@ void CertificateViewer::InitDetailsPage() { gtk_box_pack_start(GTK_BOX(details_page_vbox_), export_hbox, FALSE, FALSE, 0); export_button_ = gtk_button_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8( IDS_CERT_DETAILS_EXPORT_CERTIFICATE)).c_str()); g_signal_connect(export_button_, "clicked", diff --git a/chrome/browser/gtk/constrained_html_delegate_gtk.cc b/chrome/browser/gtk/constrained_html_delegate_gtk.cc index f23c82d..350bf31 100644 --- a/chrome/browser/gtk/constrained_html_delegate_gtk.cc +++ b/chrome/browser/gtk/constrained_html_delegate_gtk.cc @@ -4,16 +4,16 @@ #include "chrome/browser/dom_ui/constrained_html_ui.h" -#include "gfx/gtk_util.h" -#include "gfx/rect.h" #include "chrome/browser/dom_ui/html_dialog_tab_contents_delegate.h" #include "chrome/browser/dom_ui/html_dialog_ui.h" #include "chrome/browser/gtk/constrained_window_gtk.h" +#include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/gtk/tab_contents_container_gtk.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_source.h" +#include "gfx/rect.h" #include "ipc/ipc_message.h" class ConstrainedHtmlDelegateGtk : public ConstrainedWindowGtkDelegate, @@ -30,6 +30,7 @@ class ConstrainedHtmlDelegateGtk : public ConstrainedWindowGtkDelegate, return tab_contents_container_.widget(); } virtual void DeleteDelegate() { + html_delegate_->OnDialogClosed(""); delete this; } @@ -37,7 +38,7 @@ class ConstrainedHtmlDelegateGtk : public ConstrainedWindowGtkDelegate, virtual HtmlDialogUIDelegate* GetHtmlDialogUIDelegate(); virtual void OnDialogClose(); virtual bool GetBackgroundColor(GdkColor* color) { - *color = gfx::kGdkWhite; + *color = gtk_util::kGdkWhite; return true; } diff --git a/chrome/browser/gtk/content_setting_bubble_gtk.cc b/chrome/browser/gtk/content_setting_bubble_gtk.cc index 21331f8..657b82a 100644 --- a/chrome/browser/gtk/content_setting_bubble_gtk.cc +++ b/chrome/browser/gtk/content_setting_bubble_gtk.cc @@ -5,6 +5,7 @@ #include "chrome/browser/gtk/content_setting_bubble_gtk.h" #include "app/l10n_util.h" +#include "app/text_elider.h" #include "base/i18n/rtl.h" #include "base/utf_string_conversions.h" #include "chrome/browser/blocked_content_container.h" @@ -24,8 +25,24 @@ #include "grit/generated_resources.h" #include "webkit/glue/plugins/plugin_list.h" +namespace { + // Padding between content and edge of info bubble. -static const int kContentBorder = 7; +const int kContentBorder = 7; + +// The maximum width of a title entry in the content box. We elide anything +// longer than this. +const int kMaxLinkPixelSize = 500; + +std::string BuildElidedText(const std::string& input) { + return UTF16ToUTF8(gfx::ElideText( + UTF8ToUTF16(input), + gfx::Font(), + kMaxLinkPixelSize, + false)); +} + +} // namespace ContentSettingBubbleGtk::ContentSettingBubbleGtk( GtkWidget* anchor, @@ -95,7 +112,7 @@ void ContentSettingBubbleGtk::BuildBubble() { else name = *it; - GtkWidget* label = gtk_label_new(name.c_str()); + GtkWidget* label = gtk_label_new(BuildElidedText(name).c_str()); GtkWidget* label_box = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(label_box), label, FALSE, FALSE, 0); @@ -134,7 +151,8 @@ void ContentSettingBubbleGtk::BuildBubble() { gtk_util::kControlSpacing / 2); } - GtkWidget* button = gtk_chrome_link_button_new(i->title.c_str()); + GtkWidget* button = gtk_chrome_link_button_new( + BuildElidedText(i->title).c_str()); popup_links_[button] = i -popup_items.begin(); g_signal_connect(button, "clicked", G_CALLBACK(OnPopupLinkClickedThunk), this); @@ -153,12 +171,13 @@ void ContentSettingBubbleGtk::BuildBubble() { for (ContentSettingBubbleModel::RadioItems::const_iterator i = radio_group.radio_items.begin(); i != radio_group.radio_items.end(); ++i) { + std::string elided = BuildElidedText(*i); GtkWidget* radio = radio_group_gtk_.empty() ? - gtk_radio_button_new_with_label(NULL, i->c_str()) : + gtk_radio_button_new_with_label(NULL, elided.c_str()) : gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(radio_group_gtk_[0]), - i->c_str()); + elided.c_str()); gtk_box_pack_start(GTK_BOX(bubble_content), radio, FALSE, FALSE, 0); if (i - radio_group.radio_items.begin() == radio_group.default_item) { // We must set the default value before we attach the signal handlers @@ -184,7 +203,7 @@ void ContentSettingBubbleGtk::BuildBubble() { // Put each list into its own vbox to allow spacing between lists. GtkWidget* list_content = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); - GtkWidget* label = gtk_label_new(i->title.c_str()); + GtkWidget* label = gtk_label_new(BuildElidedText(i->title).c_str()); gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); GtkWidget* label_box = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(label_box), label, FALSE, FALSE, 0); diff --git a/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.cc b/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.cc index dbde270..d748622 100644 --- a/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.cc +++ b/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.cc @@ -6,7 +6,6 @@ #include <string> -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "base/environment.h" #include "base/utf_string_conversions.h" diff --git a/chrome/browser/gtk/custom_drag.cc b/chrome/browser/gtk/custom_drag.cc index 9eb3b85..6556e85 100644 --- a/chrome/browser/gtk/custom_drag.cc +++ b/chrome/browser/gtk/custom_drag.cc @@ -26,7 +26,8 @@ void OnDragDataGetForDownloadItem(GtkSelectionData* selection_data, const DownloadItem* download_item) { GURL url = net::FilePathToFileURL(download_item->full_path()); gtk_dnd_util::WriteURLWithName(selection_data, url, - UTF8ToUTF16(download_item->GetFileName().value()), target_type); + UTF8ToUTF16(download_item->GetFileNameToReportUser().value()), + target_type); } void OnDragDataGetStandalone(GtkWidget* widget, GdkDragContext* context, @@ -43,7 +44,6 @@ void OnDragDataGetStandalone(GtkWidget* widget, GdkDragContext* context, CustomDrag::CustomDrag(SkBitmap* icon, int code_mask, GdkDragAction action) : drag_widget_(gtk_invisible_new()), pixbuf_(icon ? gfx::GdkPixbufFromSkBitmap(icon) : NULL) { - g_object_ref_sink(drag_widget_); g_signal_connect(drag_widget_, "drag-data-get", G_CALLBACK(OnDragDataGetThunk), this); g_signal_connect(drag_widget_, "drag-begin", @@ -62,7 +62,7 @@ CustomDrag::CustomDrag(SkBitmap* icon, int code_mask, GdkDragAction action) CustomDrag::~CustomDrag() { if (pixbuf_) g_object_unref(pixbuf_); - g_object_unref(drag_widget_); + gtk_widget_destroy(drag_widget_); } void CustomDrag::OnDragBegin(GtkWidget* widget, GdkDragContext* drag_context) { @@ -147,4 +147,3 @@ void BookmarkDrag::BeginDrag(Profile* profile, const std::vector<const BookmarkNode*>& nodes) { new BookmarkDrag(profile, nodes); } - diff --git a/chrome/browser/gtk/custom_drag.h b/chrome/browser/gtk/custom_drag.h index 90cfde1..679fac0 100644 --- a/chrome/browser/gtk/custom_drag.h +++ b/chrome/browser/gtk/custom_drag.h @@ -40,7 +40,10 @@ class CustomDrag { target_type, time); } + // Can't use a OwnedWidgetGtk because the initialization of GtkInvisible + // sinks the reference. GtkWidget* drag_widget_; + GdkPixbuf* pixbuf_; DISALLOW_COPY_AND_ASSIGN(CustomDrag); diff --git a/chrome/browser/gtk/dialogs_gtk.cc b/chrome/browser/gtk/dialogs_gtk.cc index bd0c8b6..4872f89 100644 --- a/chrome/browser/gtk/dialogs_gtk.cc +++ b/chrome/browser/gtk/dialogs_gtk.cc @@ -13,8 +13,9 @@ #include "base/message_loop.h" #include "base/mime_util.h" #include "base/sys_string_conversions.h" -#include "base/utf_string_conversions.h" #include "base/thread.h" +#include "base/thread_restrictions.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/shell_dialogs.h" @@ -96,6 +97,17 @@ class SelectFileDialogImpl : public SelectFileDialog { gint response_id, bool allow_folder); + // Common function for CreateFileOpenDialog and CreateMultiFileOpenDialog. + GtkWidget* CreateFileOpenHelper(const std::string& title, + const FilePath& default_path, + gfx::NativeWindow parent); + + // Wrapper for file_util::DirectoryExists() that allow access on the UI + // thread. Use this only in the file dialog functions, where it's ok + // because the file dialog has to do many stats anyway. One more won't + // hurt too badly and it's likely already cached. + bool CallDirectoryExistsOnUIThread(const FilePath& path); + // Callback for when the user responds to a Save As or Open File dialog. CHROMEGTK_CALLBACK_1(SelectFileDialogImpl, void, OnSelectSingleFileDialogResponse, gint); @@ -329,49 +341,55 @@ void SelectFileDialogImpl::FileNotSelected(GtkWidget* dialog) { gtk_widget_destroy(dialog); } -GtkWidget* SelectFileDialogImpl::CreateSelectFolderDialog( +bool SelectFileDialogImpl::CallDirectoryExistsOnUIThread(const FilePath& path) { + base::ThreadRestrictions::ScopedAllowIO allow_io; + return file_util::DirectoryExists(path); +} + +GtkWidget* SelectFileDialogImpl::CreateFileOpenHelper( const std::string& title, const FilePath& default_path, gfx::NativeWindow parent) { - std::string title_string = !title.empty() ? title : - l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); - GtkWidget* dialog = - gtk_file_chooser_dialog_new(title_string.c_str(), parent, - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + gtk_file_chooser_dialog_new(title.c_str(), parent, + GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); + AddFilters(GTK_FILE_CHOOSER(dialog)); if (!default_path.empty()) { - gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), - default_path.value().c_str()); + if (CallDirectoryExistsOnUIThread(default_path)) { + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), + default_path.value().c_str()); + } else { + // If the file doesn't exist, this will just switch to the correct + // directory. That's good enough. + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), + default_path.value().c_str()); + } } else if (!last_opened_path_->empty()) { gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), last_opened_path_->value().c_str()); } - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); - g_signal_connect(dialog, "response", - G_CALLBACK(OnSelectSingleFolderDialogResponseThunk), this); return dialog; } -GtkWidget* SelectFileDialogImpl::CreateFileOpenDialog(const std::string& title, - const FilePath& default_path, gfx::NativeWindow parent) { +GtkWidget* SelectFileDialogImpl::CreateSelectFolderDialog( + const std::string& title, + const FilePath& default_path, + gfx::NativeWindow parent) { std::string title_string = !title.empty() ? title : - l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE); + l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); GtkWidget* dialog = gtk_file_chooser_dialog_new(title_string.c_str(), parent, - GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); - AddFilters(GTK_FILE_CHOOSER(dialog)); if (!default_path.empty()) { - // If the file doesn't exist, this will just switch to the correct - // directory. That's good enough. gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), default_path.value().c_str()); } else if (!last_opened_path_->empty()) { @@ -380,6 +398,19 @@ GtkWidget* SelectFileDialogImpl::CreateFileOpenDialog(const std::string& title, } gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); g_signal_connect(dialog, "response", + G_CALLBACK(OnSelectSingleFolderDialogResponseThunk), this); + return dialog; +} + +GtkWidget* SelectFileDialogImpl::CreateFileOpenDialog( + const std::string& title, + const FilePath& default_path, + gfx::NativeWindow parent) { + std::string title_string = !title.empty() ? title : + l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE); + GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); + g_signal_connect(dialog, "response", G_CALLBACK(OnSelectSingleFileDialogResponseThunk), this); return dialog; } @@ -390,24 +421,7 @@ GtkWidget* SelectFileDialogImpl::CreateMultiFileOpenDialog( gfx::NativeWindow parent) { std::string title_string = !title.empty() ? title : l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE); - - GtkWidget* dialog = - gtk_file_chooser_dialog_new(title_string.c_str(), parent, - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, - NULL); - - AddFilters(GTK_FILE_CHOOSER(dialog)); - if (!default_path.empty()) { - // If the file doesn't exist, this will just switch to the correct - // directory. That's good enough. - gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), - default_path.value().c_str()); - } else if (!last_opened_path_->empty()) { - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), - last_opened_path_->value().c_str()); - } + GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); g_signal_connect(dialog, "response", G_CALLBACK(OnSelectMultiFileDialogResponseThunk), this); @@ -505,10 +519,7 @@ void SelectFileDialogImpl::SelectSingleFileHelper(GtkWidget* dialog, return; } - // We're accessing the disk from the UI thread here, but in this case it's - // ok because we may have just done lots of stats in the file selection - // dialog. One more won't hurt too badly. - if (file_util::DirectoryExists(path)) + if (CallDirectoryExistsOnUIThread(path)) FileNotSelected(dialog); else FileSelected(dialog, path); @@ -541,7 +552,7 @@ void SelectFileDialogImpl::OnSelectMultiFileDialogResponse( for (GSList* iter = filenames; iter != NULL; iter = g_slist_next(iter)) { FilePath path(static_cast<char*>(iter->data)); g_free(iter->data); - if (file_util::DirectoryExists(path)) + if (CallDirectoryExistsOnUIThread(path)) continue; filenames_fp.push_back(path); } diff --git a/chrome/browser/gtk/download_item_gtk.cc b/chrome/browser/gtk/download_item_gtk.cc index 63d3093..844c751 100644 --- a/chrome/browser/gtk/download_item_gtk.cc +++ b/chrome/browser/gtk/download_item_gtk.cc @@ -90,8 +90,7 @@ class DownloadShelfContextMenuGtk : public DownloadShelfContextMenu, DownloadShelfContextMenuGtk(BaseDownloadItemModel* model, DownloadItemGtk* download_item) : DownloadShelfContextMenu(model), - download_item_(download_item), - method_factory_(this) { + download_item_(download_item) { } ~DownloadShelfContextMenuGtk() { @@ -143,8 +142,6 @@ class DownloadShelfContextMenuGtk : public DownloadShelfContextMenu, // The download item that created us. DownloadItemGtk* download_item_; - - ScopedRunnableMethodFactory<DownloadShelfContextMenuGtk> method_factory_; }; // DownloadItemGtk ------------------------------------------------------------- @@ -358,8 +355,8 @@ void DownloadItemGtk::OnDownloadUpdated(DownloadItem* download) { parent_shelf_->MaybeShowMoreDownloadItems(); } - if (download->full_path() != icon_filepath_) { - // Turns out the file path is "unconfirmed %d.download" for dangerous + if (download->GetUserVerifiedFileName() != icon_filename_) { + // Turns out the file path is "unconfirmed %d.crdownload" for dangerous // downloads. When the download is confirmed, the file is renamed on // another thread, so reload the icon if the download filename changes. LoadIcon(); @@ -513,18 +510,18 @@ void DownloadItemGtk::OnLoadLargeIconComplete(IconManager::Handle handle, void DownloadItemGtk::LoadIcon() { icon_consumer_.CancelAllRequests(); IconManager* im = g_browser_process->icon_manager(); - icon_filepath_ = get_download()->full_path(); - im->LoadIcon(icon_filepath_, + icon_filename_ = get_download()->GetUserVerifiedFileName(); + im->LoadIcon(icon_filename_, IconLoader::SMALL, &icon_consumer_, NewCallback(this, &DownloadItemGtk::OnLoadSmallIconComplete)); - im->LoadIcon(icon_filepath_, + im->LoadIcon(icon_filename_, IconLoader::LARGE, &icon_consumer_, NewCallback(this, &DownloadItemGtk::OnLoadLargeIconComplete)); } void DownloadItemGtk::UpdateTooltip() { string16 elided_filename = gfx::ElideFilename( - get_download()->GetFileName(), + get_download()->GetFileNameToReportUser(), gfx::Font(), kTooltipMaxWidth); gtk_widget_set_tooltip_text(body_.get(), UTF16ToUTF8(elided_filename).c_str()); @@ -536,7 +533,7 @@ void DownloadItemGtk::UpdateNameLabel() { // much padding when we set the size request. We need to either use gfx::Font // or somehow extend TextElider. string16 elided_filename = gfx::ElideFilename( - get_download()->GetFileName(), + get_download()->GetFileNameToReportUser(), gfx::Font(), kTextWidth); GdkColor color = theme_provider_->GetGdkColor( @@ -587,7 +584,7 @@ void DownloadItemGtk::UpdateDangerWarning() { l10n_util::GetStringUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION); } else { string16 elided_filename = gfx::ElideFilename( - get_download()->original_name(), gfx::Font(), kTextWidth); + get_download()->target_name(), gfx::Font(), kTextWidth); dangerous_warning = l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD, diff --git a/chrome/browser/gtk/download_item_gtk.h b/chrome/browser/gtk/download_item_gtk.h index 5e4d64e..f73592a 100644 --- a/chrome/browser/gtk/download_item_gtk.h +++ b/chrome/browser/gtk/download_item_gtk.h @@ -202,8 +202,8 @@ class DownloadItemGtk : public DownloadItem::Observer, SkBitmap* icon_small_; SkBitmap* icon_large_; - // The last download file path for which we requested an icon. - FilePath icon_filepath_; + // The last download file name for which we requested an icon. + FilePath icon_filename_; NotificationRegistrar registrar_; diff --git a/chrome/browser/gtk/extension_infobar_gtk.cc b/chrome/browser/gtk/extension_infobar_gtk.cc index 7140afc..35d86c0 100644 --- a/chrome/browser/gtk/extension_infobar_gtk.cc +++ b/chrome/browser/gtk/extension_infobar_gtk.cc @@ -47,7 +47,7 @@ void ExtensionInfoBarGtk::OnImageLoaded( void ExtensionInfoBarGtk::BuildWidgets() { // Start loading the image for the menu button. - Extension* extension = delegate_->extension_host()->extension(); + const Extension* extension = delegate_->extension_host()->extension(); ExtensionResource icon_resource = extension->GetIconResource( Extension::EXTENSION_ICON_BITTY, ExtensionIconSet::MATCH_EXACTLY); if (!icon_resource.relative_path().empty()) { @@ -66,7 +66,6 @@ void ExtensionInfoBarGtk::BuildWidgets() { g_signal_connect(view_->native_view(), "size_allocate", G_CALLBACK(&OnSizeAllocateThunk), this); - gtk_widget_show_all(border_bin_.get()); } void ExtensionInfoBarGtk::OnSizeAllocate(GtkWidget* widget, diff --git a/chrome/browser/gtk/extension_install_prompt2_gtk.cc b/chrome/browser/gtk/extension_install_prompt2_gtk.cc index 6d7008c..cf78028 100644 --- a/chrome/browser/gtk/extension_install_prompt2_gtk.cc +++ b/chrome/browser/gtk/extension_install_prompt2_gtk.cc @@ -4,7 +4,6 @@ #include <gtk/gtk.h> -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" @@ -54,7 +53,7 @@ void OnDialogResponse(GtkDialog* dialog, int response_id, } void ShowInstallPromptDialog2(GtkWindow* parent, SkBitmap* skia_icon, - Extension* extension, + const Extension* extension, ExtensionInstallUI::Delegate *delegate, const std::vector<string16>& permissions) { // Build the dialog. @@ -173,7 +172,10 @@ void ShowInstallPromptDialog2(GtkWindow* parent, SkBitmap* skia_icon, } // namespace void ExtensionInstallUI::ShowExtensionInstallUIPrompt2Impl( - Profile* profile, Delegate* delegate, Extension* extension, SkBitmap* icon, + Profile* profile, + Delegate* delegate, + const Extension* extension, + SkBitmap* icon, const std::vector<string16>& permissions) { Browser* browser = BrowserList::GetLastActiveWithProfile(profile); if (!browser) { diff --git a/chrome/browser/gtk/extension_install_prompt_gtk.cc b/chrome/browser/gtk/extension_install_prompt_gtk.cc index bcb7c0a..a1166d0 100644 --- a/chrome/browser/gtk/extension_install_prompt_gtk.cc +++ b/chrome/browser/gtk/extension_install_prompt_gtk.cc @@ -38,7 +38,7 @@ void OnDialogResponse(GtkDialog* dialog, int response_id, } void ShowInstallPromptDialog(GtkWindow* parent, SkBitmap* skia_icon, - Extension *extension, + const Extension* extension, ExtensionInstallUI::Delegate *delegate, ExtensionInstallUI::PromptType type) { // Build the dialog. @@ -87,7 +87,10 @@ void ShowInstallPromptDialog(GtkWindow* parent, SkBitmap* skia_icon, } // namespace void ExtensionInstallUI::ShowExtensionInstallUIPromptImpl( - Profile* profile, Delegate* delegate, Extension* extension, SkBitmap* icon, + Profile* profile, + Delegate* delegate, + const Extension* extension, + SkBitmap* icon, ExtensionInstallUI::PromptType type) { Browser* browser = BrowserList::GetLastActiveWithProfile(profile); if (!browser) { diff --git a/chrome/browser/gtk/extension_installed_bubble_gtk.cc b/chrome/browser/gtk/extension_installed_bubble_gtk.cc index c3ad8d6..af9c915 100644 --- a/chrome/browser/gtk/extension_installed_bubble_gtk.cc +++ b/chrome/browser/gtk/extension_installed_bubble_gtk.cc @@ -43,14 +43,14 @@ const int kContentBorder = 7; } // namespace -void ExtensionInstalledBubbleGtk::Show(Extension* extension, Browser* browser, +void ExtensionInstalledBubbleGtk::Show(const Extension* extension, + Browser* browser, SkBitmap icon) { new ExtensionInstalledBubbleGtk(extension, browser, icon); } -ExtensionInstalledBubbleGtk::ExtensionInstalledBubbleGtk(Extension *extension, - Browser *browser, - SkBitmap icon) +ExtensionInstalledBubbleGtk::ExtensionInstalledBubbleGtk( + const Extension* extension, Browser *browser, SkBitmap icon) : extension_(extension), browser_(browser), icon_(icon), @@ -72,7 +72,9 @@ ExtensionInstalledBubbleGtk::ExtensionInstalledBubbleGtk(Extension *extension, // be sure that a browser action or page action has had views created which we // can inspect for the purpose of pointing to them. registrar_.Add(this, NotificationType::EXTENSION_LOADED, - NotificationService::AllSources()); + Source<Profile>(browser->profile())); + registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, + Source<Profile>(browser->profile())); } ExtensionInstalledBubbleGtk::~ExtensionInstalledBubbleGtk() {} @@ -81,12 +83,16 @@ void ExtensionInstalledBubbleGtk::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { if (type == NotificationType::EXTENSION_LOADED) { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (extension == extension_) { // PostTask to ourself to allow all EXTENSION_LOADED Observers to run. MessageLoopForUI::current()->PostTask(FROM_HERE, NewRunnableMethod(this, &ExtensionInstalledBubbleGtk::ShowInternal)); } + } else if (type == NotificationType::EXTENSION_UNLOADED) { + const Extension* extension = Details<const Extension>(details).ptr(); + if (extension == extension_) + extension_ = NULL; } else { NOTREACHED() << L"Received unexpected notification"; } @@ -248,7 +254,7 @@ void ExtensionInstalledBubbleGtk::OnButtonClick(GtkWidget* button, // InfoBubbleDelegate void ExtensionInstalledBubbleGtk::InfoBubbleClosing(InfoBubbleGtk* info_bubble, bool closed_by_escape) { - if (extension_->page_action()) { + if (extension_ && type_ == PAGE_ACTION) { // Turn the page action preview off. BrowserWindowGtk* browser_window = BrowserWindowGtk::GetBrowserWindowForNativeWindow( diff --git a/chrome/browser/gtk/extension_installed_bubble_gtk.h b/chrome/browser/gtk/extension_installed_bubble_gtk.h index 9b8483c..08d6fec 100644 --- a/chrome/browser/gtk/extension_installed_bubble_gtk.h +++ b/chrome/browser/gtk/extension_installed_bubble_gtk.h @@ -45,13 +45,13 @@ class ExtensionInstalledBubbleGtk // the extension has loaded. |extension| is the installed extension. |browser| // is the browser window which will host the bubble. |icon| is the install // icon of the extension. - static void Show(Extension *extension, Browser *browser, SkBitmap icon); + static void Show(const Extension* extension, Browser *browser, SkBitmap icon); private: friend class base::RefCountedThreadSafe<ExtensionInstalledBubbleGtk>; // Private ctor. Registers a listener for EXTENSION_LOADED. - ExtensionInstalledBubbleGtk(Extension *extension, Browser *browser, + ExtensionInstalledBubbleGtk(const Extension* extension, Browser *browser, SkBitmap icon); virtual ~ExtensionInstalledBubbleGtk(); @@ -74,7 +74,7 @@ class ExtensionInstalledBubbleGtk static void OnButtonClick(GtkWidget* button, ExtensionInstalledBubbleGtk* toolbar); - Extension *extension_; + const Extension* extension_; Browser *browser_; SkBitmap icon_; NotificationRegistrar registrar_; diff --git a/chrome/browser/gtk/find_bar_gtk.cc b/chrome/browser/gtk/find_bar_gtk.cc index 47f9b0c..8afba9a 100644 --- a/chrome/browser/gtk/find_bar_gtk.cc +++ b/chrome/browser/gtk/find_bar_gtk.cc @@ -35,7 +35,6 @@ #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/native_web_keyboard_event.h" #include "chrome/common/notification_service.h" -#include "gfx/gtk_util.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" @@ -43,11 +42,11 @@ namespace { // Used as the color of the text in the entry box and the text for the results // label for failure searches. -const GdkColor kEntryTextColor = gfx::kGdkBlack; +const GdkColor kEntryTextColor = gtk_util::kGdkBlack; // Used as the color of the background of the entry box and the background of // the find label for successful searches. -const GdkColor kEntryBackgroundColor = gfx::kGdkWhite; +const GdkColor kEntryBackgroundColor = gtk_util::kGdkWhite; const GdkColor kFindFailureBackgroundColor = GDK_COLOR_RGB(255, 102, 102); const GdkColor kFindSuccessTextColor = GDK_COLOR_RGB(178, 178, 178); @@ -500,7 +499,8 @@ void FindBarGtk::Observe(NotificationType type, gtk_misc_set_alignment(GTK_MISC(match_count_label_), 0.5, 0.5); } else { - gtk_widget_modify_cursor(text_entry_, &gfx::kGdkBlack, &gfx::kGdkGray); + gtk_widget_modify_cursor( + text_entry_, >k_util::kGdkBlack, >k_util::kGdkGray); gtk_widget_modify_base(text_entry_, GTK_STATE_NORMAL, &kEntryBackgroundColor); gtk_widget_modify_text(text_entry_, GTK_STATE_NORMAL, diff --git a/chrome/browser/gtk/first_run_bubble.cc b/chrome/browser/gtk/first_run_bubble.cc index fb23e14..81daf73 100644 --- a/chrome/browser/gtk/first_run_bubble.cc +++ b/chrome/browser/gtk/first_run_bubble.cc @@ -6,7 +6,6 @@ #include <gtk/gtk.h> -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "base/command_line.h" #include "base/i18n/rtl.h" @@ -17,7 +16,6 @@ #include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/search_engines/util.h" #include "chrome/common/notification_service.h" -#include "gfx/gtk_util.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" @@ -63,7 +61,7 @@ void FirstRunBubble::Observe(NotificationType type, } else { for (std::vector<GtkWidget*>::iterator it = labels_.begin(); it != labels_.end(); ++it) { - gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, &gfx::kGdkBlack); + gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, >k_util::kGdkBlack); } } } diff --git a/chrome/browser/gtk/first_run_dialog.cc b/chrome/browser/gtk/first_run_dialog.cc index 45b0589..230fee3 100644 --- a/chrome/browser/gtk/first_run_dialog.cc +++ b/chrome/browser/gtk/first_run_dialog.cc @@ -22,7 +22,6 @@ #include "chrome/browser/shell_integration.h" #include "chrome/common/pref_names.h" #include "chrome/installer/util/google_update_settings.h" -#include "gfx/gtk_util.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" @@ -191,7 +190,7 @@ void FirstRunDialog::ShowSearchEngineWindow() { GtkWidget* bubble_area_background = gtk_event_box_new(); gtk_widget_modify_bg(bubble_area_background, - GTK_STATE_NORMAL, &gfx::kGdkWhite); + GTK_STATE_NORMAL, >k_util::kGdkWhite); GtkWidget* bubble_area_box = gtk_vbox_new(FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(bubble_area_box), @@ -203,7 +202,7 @@ void FirstRunDialog::ShowSearchEngineWindow() { l10n_util::GetStringFUTF8(IDS_FR_SEARCH_TEXT, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)).c_str()); gtk_misc_set_alignment(GTK_MISC(explanation), 0, 0.5); - gtk_util::SetLabelColor(explanation, &gfx::kGdkBlack); + gtk_util::SetLabelColor(explanation, >k_util::kGdkBlack); gtk_util::SetLabelWidth(explanation, kExplanationWidth); gtk_box_pack_start(GTK_BOX(bubble_area_box), explanation, FALSE, FALSE, 0); diff --git a/chrome/browser/gtk/fullscreen_exit_bubble_gtk.cc b/chrome/browser/gtk/fullscreen_exit_bubble_gtk.cc index 848cb59..ee452b8 100644 --- a/chrome/browser/gtk/fullscreen_exit_bubble_gtk.cc +++ b/chrome/browser/gtk/fullscreen_exit_bubble_gtk.cc @@ -9,7 +9,6 @@ #include "chrome/browser/gtk/gtk_floating_container.h" #include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/gtk/rounded_window.h" -#include "gfx/gtk_util.h" #include "grit/app_strings.h" #include "grit/generated_resources.h" @@ -54,9 +53,10 @@ void FullscreenExitBubbleGtk::InitWidgets() { FALSE); signals_.Connect(link, "clicked", G_CALLBACK(OnLinkClickedThunk), this); - GtkWidget* container = gtk_util::CreateGtkBorderBin(link, &gfx::kGdkBlack, + GtkWidget* container = gtk_util::CreateGtkBorderBin( + link, >k_util::kGdkBlack, kPaddingPixels, kPaddingPixels, kPaddingPixels, kPaddingPixels); - gtk_util::ActAsRoundedWindow(container, gfx::kGdkGreen, kPaddingPixels, + gtk_util::ActAsRoundedWindow(container, gtk_util::kGdkGreen, kPaddingPixels, gtk_util::ROUNDED_BOTTOM_LEFT | gtk_util::ROUNDED_BOTTOM_RIGHT, gtk_util::BORDER_NONE); diff --git a/chrome/browser/gtk/gtk_chrome_cookie_view.cc b/chrome/browser/gtk/gtk_chrome_cookie_view.cc index aa4d344..07413f6 100644 --- a/chrome/browser/gtk/gtk_chrome_cookie_view.cc +++ b/chrome/browser/gtk/gtk_chrome_cookie_view.cc @@ -181,7 +181,6 @@ void SetAppCacheDetailsSensitivity(GtkChromeCookieView *self, void SetIndexedDBDetailsSensitivity(GtkChromeCookieView *self, gboolean enabled) { - gtk_widget_set_sensitive(self->indexed_db_name_entry_, enabled); gtk_widget_set_sensitive(self->indexed_db_origin_entry_, enabled); gtk_widget_set_sensitive(self->indexed_db_size_entry_, enabled); gtk_widget_set_sensitive(self->indexed_db_last_modified_entry_, enabled); @@ -383,9 +382,6 @@ void BuildWidgets(GtkChromeCookieView *self, gboolean editable_expiration) { gtk_util::kLabelSpacing); row = 0; - InitDetailRow(row++, IDS_COOKIES_COOKIE_NAME_LABEL, - self->indexed_db_details_table_, - &self->indexed_db_name_entry_); InitDetailRow(row++, IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL, self->indexed_db_details_table_, &self->indexed_db_origin_entry_); @@ -602,11 +598,6 @@ void gtk_chrome_cookie_view_display_indexed_db( const BrowsingDataIndexedDBHelper::IndexedDBInfo& indexed_db_info) { UpdateVisibleDetailedInfo(self, self->indexed_db_details_table_); - gtk_entry_set_text(GTK_ENTRY(self->indexed_db_name_entry_), - indexed_db_info.database_name.empty() ? - l10n_util::GetStringUTF8( - IDS_COOKIES_WEB_DATABASE_UNNAMED_NAME).c_str() : - indexed_db_info.database_name.c_str()); gtk_entry_set_text(GTK_ENTRY(self->indexed_db_origin_entry_), indexed_db_info.origin.c_str()); gtk_entry_set_text(GTK_ENTRY(self->indexed_db_size_entry_), diff --git a/chrome/browser/gtk/gtk_chrome_cookie_view.h b/chrome/browser/gtk/gtk_chrome_cookie_view.h index 049d621..93bc96c 100644 --- a/chrome/browser/gtk/gtk_chrome_cookie_view.h +++ b/chrome/browser/gtk/gtk_chrome_cookie_view.h @@ -96,7 +96,6 @@ typedef struct { // The IndexedDB details widgets. GtkWidget* indexed_db_details_table_; - GtkWidget* indexed_db_name_entry_; GtkWidget* indexed_db_origin_entry_; GtkWidget* indexed_db_size_entry_; GtkWidget* indexed_db_last_modified_entry_; diff --git a/chrome/browser/gtk/gtk_theme_provider.cc b/chrome/browser/gtk/gtk_theme_provider.cc index 4a6b0c9..6d04b57 100644 --- a/chrome/browser/gtk/gtk_theme_provider.cc +++ b/chrome/browser/gtk/gtk_theme_provider.cc @@ -15,6 +15,7 @@ #include "base/nix/xdg_util.h" #include "chrome/browser/gtk/cairo_cached_surface.h" #include "chrome/browser/gtk/gtk_chrome_button.h" +#include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/gtk/hover_controller_gtk.h" #include "chrome/browser/gtk/meta_frames.h" #include "chrome/browser/metrics/user_metrics.h" @@ -318,7 +319,7 @@ void GtkThemeProvider::InitThemesFor(NotificationObserver* observer) { NotificationService::NoDetails()); } -void GtkThemeProvider::SetTheme(Extension* extension) { +void GtkThemeProvider::SetTheme(const Extension* extension) { profile()->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, false); LoadDefaultValues(); BrowserThemeProvider::SetTheme(extension); @@ -579,7 +580,7 @@ void GtkThemeProvider::LoadThemePrefs() { RebuildMenuIconSets(); } -void GtkThemeProvider::NotifyThemeChanged(Extension* extension) { +void GtkThemeProvider::NotifyThemeChanged(const Extension* extension) { BrowserThemeProvider::NotifyThemeChanged(extension); // Notify all GtkChromeButtons of their new rendering mode: @@ -685,6 +686,16 @@ void GtkThemeProvider::LoadGtkValues() { SetThemeTintFromGtk(BrowserThemeProvider::TINT_FRAME_INCOGNITO, &frame_color); SetThemeTintFromGtk(BrowserThemeProvider::TINT_BACKGROUND_TAB, &frame_color); + // The inactive color/tint is special: We *must* use the exact insensitive + // color for all inactive windows, otherwise we end up neon pink half the + // time. + SetThemeColorFromGtk(BrowserThemeProvider::COLOR_FRAME_INACTIVE, + &inactive_frame_color); + SetTintToExactColor(BrowserThemeProvider::TINT_FRAME_INACTIVE, + &inactive_frame_color); + SetTintToExactColor(BrowserThemeProvider::TINT_FRAME_INCOGNITO_INACTIVE, + &inactive_frame_color); + SetThemeColorFromGtk(BrowserThemeProvider::COLOR_FRAME, &frame_color); BuildTintedFrameColor(BrowserThemeProvider::COLOR_FRAME_INACTIVE, BrowserThemeProvider::TINT_FRAME_INACTIVE); @@ -720,16 +731,6 @@ void GtkThemeProvider::LoadGtkValues() { colors_[BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT] = color_utils::HSLToSkColor(inactive_tab_text_hsl, 255); - // The inactive color/tint is special: We *must* use the exact insensitive - // color for all inactive windows, otherwise we end up neon pink half the - // time. - SetThemeColorFromGtk(BrowserThemeProvider::COLOR_FRAME_INACTIVE, - &inactive_frame_color); - SetTintToExactColor(BrowserThemeProvider::TINT_FRAME_INACTIVE, - &inactive_frame_color); - SetTintToExactColor(BrowserThemeProvider::TINT_FRAME_INCOGNITO_INACTIVE, - &inactive_frame_color); - // We pick the text and background colors for the NTP out of the colors for a // GtkEntry. We do this because GtkEntries background color is never the same // as |toolbar_color|, is usually a white, and when it isn't a white, @@ -840,8 +841,9 @@ void GtkThemeProvider::SetThemeTintFromGtk(int id, const GdkColor* color) { } void GtkThemeProvider::BuildTintedFrameColor(int color_id, int tint_id) { - colors_[color_id] = HSLShift(colors_[BrowserThemeProvider::COLOR_FRAME], - tints_[tint_id]); + colors_[color_id] = HSLShift( + GetDefaultColor(BrowserThemeProvider::COLOR_FRAME), + tints_[tint_id]); } void GtkThemeProvider::SetTintToExactColor(int id, const GdkColor* color) { diff --git a/chrome/browser/gtk/gtk_theme_provider.h b/chrome/browser/gtk/gtk_theme_provider.h index 9a4d59c..f31d974 100644 --- a/chrome/browser/gtk/gtk_theme_provider.h +++ b/chrome/browser/gtk/gtk_theme_provider.h @@ -51,7 +51,7 @@ class GtkThemeProvider : public BrowserThemeProvider, virtual SkBitmap* GetBitmapNamed(int id) const; virtual SkColor GetColor(int id) const; virtual bool HasCustomImage(int id) const; - virtual void SetTheme(Extension* extension); + virtual void SetTheme(const Extension* extension); virtual void UseDefaultTheme(); virtual void SetNativeTheme(); virtual bool UsingDefaultTheme(); @@ -153,7 +153,7 @@ class GtkThemeProvider : public BrowserThemeProvider, virtual void LoadThemePrefs(); // Let all the browser views know that themes have changed. - virtual void NotifyThemeChanged(Extension* extension); + virtual void NotifyThemeChanged(const Extension* extension); // Additionally frees the CairoCachedSurfaces. virtual void FreePlatformCaches(); diff --git a/chrome/browser/gtk/gtk_util.cc b/chrome/browser/gtk/gtk_util.cc index babed07..74b7423 100644 --- a/chrome/browser/gtk/gtk_util.cc +++ b/chrome/browser/gtk/gtk_util.cc @@ -11,21 +11,24 @@ #include <cstdarg> #include <map> -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "app/x11_util.h" -#include "base/gtk_util.h" +#include "base/environment.h" #include "base/i18n/rtl.h" #include "base/linux_util.h" #include "base/logging.h" +#include "base/nix/xdg_util.h" +#include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/gtk/cairo_cached_surface.h" #include "chrome/browser/gtk/gtk_theme_provider.h" #include "chrome/common/renderer_preferences.h" +#include "gfx/gtk_util.h" #include "googleurl/src/gurl.h" #include "grit/theme_resources.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -80,11 +83,32 @@ gboolean OnMouseButtonReleased(GtkWidget* widget, GdkEventButton* event, return TRUE; } +// Returns the approximate number of characters that can horizontally fit in +// |pixel_width| pixels. +int GetCharacterWidthForPixels(GtkWidget* widget, int pixel_width) { + DCHECK(GTK_WIDGET_REALIZED(widget)) + << " widget must be realized to compute font metrics correctly"; + + PangoContext* context = gtk_widget_create_pango_context(widget); + PangoFontMetrics* metrics = pango_context_get_metrics(context, + widget->style->font_desc, pango_context_get_language(context)); + + // This technique (max of char and digit widths) matches the code in + // gtklabel.c. + int char_width = pixel_width * PANGO_SCALE / + std::max(pango_font_metrics_get_approximate_char_width(metrics), + pango_font_metrics_get_approximate_digit_width(metrics)); + + pango_font_metrics_unref(metrics); + g_object_unref(context); + + return char_width; +} + void OnLabelRealize(GtkWidget* label, gpointer pixel_width) { gtk_label_set_width_chars( GTK_LABEL(label), - gtk_util::GetCharacterWidthForPixels(label, - GPOINTER_TO_INT(pixel_width))); + GetCharacterWidthForPixels(label,GPOINTER_TO_INT(pixel_width))); } // Ownership of |icon_list| is passed to the caller. @@ -166,6 +190,11 @@ WindowOpenDisposition DispositionFromEventFlags(guint event_flags) { namespace gtk_util { +const GdkColor kGdkWhite = GDK_COLOR_RGB(0xff, 0xff, 0xff); +const GdkColor kGdkGray = GDK_COLOR_RGB(0x7f, 0x7f, 0x7f); +const GdkColor kGdkBlack = GDK_COLOR_RGB(0x00, 0x00, 0x00); +const GdkColor kGdkGreen = GDK_COLOR_RGB(0x00, 0xff, 0x00); + GtkWidget* CreateLabeledControlsGroup(std::vector<GtkWidget*>* labels, const char* text, ...) { va_list ap; @@ -224,6 +253,46 @@ GtkWidget* CreateBoldLabel(const std::string& text) { return LeftAlignMisc(label); } +void GetWidgetSizeFromCharacters( + GtkWidget* widget, double width_chars, double height_lines, + int* width, int* height) { + DCHECK(GTK_WIDGET_REALIZED(widget)) + << " widget must be realized to compute font metrics correctly"; + PangoContext* context = gtk_widget_create_pango_context(widget); + PangoFontMetrics* metrics = pango_context_get_metrics(context, + widget->style->font_desc, pango_context_get_language(context)); + if (width) { + *width = static_cast<int>( + pango_font_metrics_get_approximate_char_width(metrics) * + width_chars / PANGO_SCALE); + } + if (height) { + *height = static_cast<int>( + (pango_font_metrics_get_ascent(metrics) + + pango_font_metrics_get_descent(metrics)) * + height_lines / PANGO_SCALE); + } + pango_font_metrics_unref(metrics); + g_object_unref(context); +} + +void GetWidgetSizeFromResources( + GtkWidget* widget, int width_chars, int height_lines, + int* width, int* height) { + DCHECK(GTK_WIDGET_REALIZED(widget)) + << " widget must be realized to compute font metrics correctly"; + + double chars = 0; + if (width) + base::StringToDouble(l10n_util::GetStringUTF8(width_chars), &chars); + + double lines = 0; + if (height) + base::StringToDouble(l10n_util::GetStringUTF8(height_lines), &lines); + + GetWidgetSizeFromCharacters(widget, chars, lines, width, height); +} + void SetWindowSizeFromResources(GtkWindow* window, int width_id, int height_id, bool resizable) { int width = -1; @@ -556,7 +625,7 @@ GtkWidget* AddButtonToDialog(GtkWidget* dialog, const gchar* text, GtkWidget* BuildDialogButton(GtkWidget* dialog, int ids_id, const gchar* stock_id) { GtkWidget* button = gtk_button_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8(ids_id)).c_str()); gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(stock_id, @@ -1136,25 +1205,14 @@ WebDragOperationsMask GdkDragActionToWebDragOp(GdkDragAction action) { return op; } -void DrawTopDropShadowForRenderView(cairo_t* cr, const gfx::Point& origin, - const gfx::Rect& paint_rect) { - gfx::Rect shadow_rect(paint_rect.x(), origin.y(), - paint_rect.width(), kInfoBarDropShadowHeight); - - // Avoid this extra work if we can. - if (!shadow_rect.Intersects(paint_rect)) - return; - - cairo_pattern_t* shadow = - cairo_pattern_create_linear(0.0, shadow_rect.y(), - 0.0, shadow_rect.bottom()); - cairo_pattern_add_color_stop_rgba(shadow, 0, 0, 0, 0, 0.6); - cairo_pattern_add_color_stop_rgba(shadow, 1, 0, 0, 0, 0.1); - cairo_rectangle(cr, shadow_rect.x(), shadow_rect.y(), - shadow_rect.width(), shadow_rect.height()); - cairo_set_source(cr, shadow); - cairo_fill(cr); - cairo_pattern_destroy(shadow); +void ApplyMessageDialogQuirks(GtkWidget* dialog) { + if (gtk_window_get_modal(GTK_WINDOW(dialog))) { + // Work around a KDE 3 window manager bug. + scoped_ptr<base::Environment> env(base::Environment::Create()); + if (base::nix::DESKTOP_ENVIRONMENT_KDE3 == + base::nix::GetDesktopEnvironment(env.get())) + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE); + } } } // namespace gtk_util diff --git a/chrome/browser/gtk/gtk_util.h b/chrome/browser/gtk/gtk_util.h index 466ba7e..77beb1f 100644 --- a/chrome/browser/gtk/gtk_util.h +++ b/chrome/browser/gtk/gtk_util.h @@ -18,6 +18,7 @@ #include "webkit/glue/window_open_disposition.h" typedef struct _cairo cairo_t; +typedef struct _GdkColor GdkColor; typedef struct _GtkWidget GtkWidget; class GtkThemeProvider; @@ -25,6 +26,14 @@ class GURL; class Profile; struct RendererPreferences; // from common/renderer_preferences.h +const int kSkiaToGDKMultiplier = 257; + +// Define a macro for creating GdkColors from RGB values. This is a macro to +// allow static construction of literals, etc. Use this like: +// GdkColor white = GDK_COLOR_RGB(0xff, 0xff, 0xff); +#define GDK_COLOR_RGB(r, g, b) {0, r * kSkiaToGDKMultiplier, \ + g * kSkiaToGDKMultiplier, b * kSkiaToGDKMultiplier} + namespace event_utils { // Translates event flags into what kind of disposition they represent. @@ -36,6 +45,11 @@ WindowOpenDisposition DispositionFromEventFlags(guint state); namespace gtk_util { +extern const GdkColor kGdkWhite; +extern const GdkColor kGdkGray; +extern const GdkColor kGdkBlack; +extern const GdkColor kGdkGreen; + // Constants relating to the layout of dialog windows: // (See http://library.gnome.org/devel/hig-book/stable/design-window.html.en) @@ -57,9 +71,6 @@ const int kContentAreaSpacing = 18; // Horizontal Spacing between controls in a form. const int kFormControlSpacing = 10; -// Height for the infobar drop shadow. -const int kInfoBarDropShadowHeight = 6; - // Create a table of labeled controls, using proper spacing and alignment. // Arguments should be pairs of const char*, GtkWidget*, concluding with a // NULL. The first argument is a vector in which to place all labels @@ -87,6 +98,20 @@ GtkWidget* LeftAlignMisc(GtkWidget* misc); // Create a left-aligned label with the given text in bold. GtkWidget* CreateBoldLabel(const std::string& text); +// As above, but uses number of characters/lines directly rather than looking up +// a resource. +void GetWidgetSizeFromCharacters(GtkWidget* widget, + double width_chars, double height_lines, + int* width, int* height); + +// Calculates the size of given widget based on the size specified in number of +// characters/lines (in locale specific resource file) and font metrics. +// NOTE: Make sure to realize |widget| before using this method, or a default +// font size will be used instead of the actual font size. +void GetWidgetSizeFromResources(GtkWidget* widget, + int width_chars, int height_lines, + int* width, int* height); + // As above, but a convenience method for configuring dialog size. // |width_id| and |height_id| are resource IDs for the size. If either of these // are set to -1, the respective size will be set to the widget default. @@ -344,10 +369,10 @@ void InitLabelSizeRequestAndEllipsizeMode(GtkWidget* label); GdkDragAction WebDragOpToGdkDragAction(WebKit::WebDragOperationsMask op); WebKit::WebDragOperationsMask GdkDragActionToWebDragOp(GdkDragAction action); -// Code to draw the drop shadow below an infobar (at the top of the render -// view). -void DrawTopDropShadowForRenderView(cairo_t* cr, const gfx::Point& origin, - const gfx::Rect& paint_rect); +// A helper function for gtk_message_dialog_new() to work around a few KDE 3 +// window manager bugs. You should always call it after creating a dialog with +// gtk_message_dialog_new. +void ApplyMessageDialogQuirks(GtkWidget* dialog); } // namespace gtk_util diff --git a/chrome/browser/gtk/html_dialog_gtk.h b/chrome/browser/gtk/html_dialog_gtk.h index c004624..aba28f2 100644 --- a/chrome/browser/gtk/html_dialog_gtk.h +++ b/chrome/browser/gtk/html_dialog_gtk.h @@ -46,6 +46,7 @@ class HtmlDialogGtk : public HtmlDialogTabContentsDelegate, virtual std::string GetDialogArgs() const; virtual void OnDialogClosed(const std::string& json_retval); virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { } + virtual bool ShouldShowDialogTitle() const { return true; } // Overridden from TabContentsDelegate: virtual void MoveContents(TabContents* source, const gfx::Rect& pos); diff --git a/chrome/browser/gtk/import_progress_dialog_gtk.cc b/chrome/browser/gtk/import_progress_dialog_gtk.cc index b405b13..c70561b 100644 --- a/chrome/browser/gtk/import_progress_dialog_gtk.cc +++ b/chrome/browser/gtk/import_progress_dialog_gtk.cc @@ -216,7 +216,7 @@ void ImportProgressDialogGtk::ShowDialog() { void StartImportingWithUI(GtkWindow* parent, - int16 items, + uint16 items, ImporterHost* importer_host, const ProfileInfo& browser_profile, Profile* profile, diff --git a/chrome/browser/gtk/infobar_arrow_model.cc b/chrome/browser/gtk/infobar_arrow_model.cc new file mode 100644 index 0000000..e66bb02 --- /dev/null +++ b/chrome/browser/gtk/infobar_arrow_model.cc @@ -0,0 +1,124 @@ +// 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/infobar_arrow_model.h" + +#include "chrome/browser/gtk/infobar_gtk.h" +#include "gfx/canvas_skia_paint.h" +#include "gfx/color_utils.h" +#include "gfx/point.h" +#include "gfx/skia_utils_gtk.h" +#include "third_party/skia/include/effects/SkGradientShader.h" + +InfoBarArrowModel::InfoBarArrowModel(Observer* observer) + : observer_(observer), + animation_(this) { + animation_.SetTweenType(Tween::LINEAR); + animation_.Reset(1.0); + target_colors_.top = target_colors_.bottom = SkColorSetARGB(0, 0, 0, 0); + previous_colors_ = target_colors_; +} + +InfoBarArrowModel::~InfoBarArrowModel() { +} + +InfoBarArrowModel::InfoBarColors InfoBarArrowModel::CurrentInfoBarColors() { + double alpha = animation_.GetCurrentValue(); + InfoBarColors colors = { + color_utils::AlphaBlend(target_colors_.top, + previous_colors_.top, + alpha * 0xff), + color_utils::AlphaBlend(target_colors_.bottom, + previous_colors_.bottom, + alpha * 0xff)}; + return colors; +} + +bool InfoBarArrowModel::NeedToDrawInfoBarArrow() { + return SkColorGetA(CurrentInfoBarColors().top) != 0; +} + +void InfoBarArrowModel::ShowArrowFor(InfoBar* bar, bool animate) { + scoped_ptr<std::pair<SkColor, SkColor> > colors; + + previous_colors_ = CurrentInfoBarColors(); + + if (bar) { + double r, g, b; + bar->GetTopColor(bar->delegate()->GetInfoBarType(), &r, &g, &b); + target_colors_.top = SkColorSetRGB(r * 0xff, g * 0xff, b * 0xff); + bar->GetBottomColor(bar->delegate()->GetInfoBarType(), &r, &g, &b); + target_colors_.bottom = SkColorSetRGB(r * 0xff, g * 0xff, b * 0xff); + } else { + target_colors_.bottom = target_colors_.top = SkColorSetARGB(0, 0, 0, 0); + } + + if (animate) { + // Fade from the current color to the target color. + animation_.Reset(); + animation_.Show(); + } else { + // Skip straight to showing the target color. + animation_.Reset(1.0); + } + + observer_->PaintStateChanged(); +} + +void InfoBarArrowModel::Paint(GtkWidget* widget, + GdkEventExpose* expose, + const gfx::Point& origin, + const GdkColor& border_color) { + if (!NeedToDrawInfoBarArrow()) + return; + + // The size of the arrow (its height; also half its width). + const int kArrowSize = 10; + + SkPath path; + path.moveTo(SkPoint::Make(origin.x() - kArrowSize, origin.y())); + path.rLineTo(kArrowSize, -kArrowSize); + path.rLineTo(kArrowSize, kArrowSize); + path.close(); + + SkPaint paint; + paint.setStrokeWidth(1); + paint.setStyle(SkPaint::kFill_Style); + + SkPoint grad_points[2]; + grad_points[0].set(SkIntToScalar(0), SkIntToScalar(origin.y())); + grad_points[1].set(SkIntToScalar(0), + SkIntToScalar(origin.y() + InfoBar::kInfoBarHeight)); + + InfoBarColors colors = CurrentInfoBarColors(); + SkColor grad_colors[2]; + grad_colors[0] = colors.top; + grad_colors[1] = colors.bottom; + + SkShader* gradient_shader = SkGradientShader::CreateLinear( + grad_points, grad_colors, NULL, 2, SkShader::kMirror_TileMode); + paint.setShader(gradient_shader); + gradient_shader->unref(); + + gfx::CanvasSkiaPaint canvas(expose, false); + canvas.drawPath(path, paint); + + paint.setShader(NULL); + paint.setColor(SkColorSetA(gfx::GdkColorToSkColor(border_color), + SkColorGetA(colors.top))); + paint.setStyle(SkPaint::kStroke_Style); + canvas.drawPath(path, paint); +} + +void InfoBarArrowModel::AnimationEnded(const Animation* animation) { + observer_->PaintStateChanged(); +} + +void InfoBarArrowModel::AnimationProgressed(const Animation* animation) { + observer_->PaintStateChanged(); +} + +void InfoBarArrowModel::AnimationCanceled(const Animation* animation) { + observer_->PaintStateChanged(); +} diff --git a/chrome/browser/gtk/infobar_arrow_model.h b/chrome/browser/gtk/infobar_arrow_model.h new file mode 100644 index 0000000..369e586 --- /dev/null +++ b/chrome/browser/gtk/infobar_arrow_model.h @@ -0,0 +1,77 @@ +// 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_INFOBAR_ARROW_MODEL_H_ +#define CHROME_BROWSER_GTK_INFOBAR_ARROW_MODEL_H_ + +#include <gtk/gtk.h> + +#include "app/slide_animation.h" +#include "third_party/skia/include/core/SkPaint.h" + +namespace gfx { +class Point; +} + +class InfoBar; + +// A helper class that tracks the state of an infobar arrow and provides a +// utility to draw it. +class InfoBarArrowModel : public AnimationDelegate { + public: + class Observer { + public: + // The arrow has changed states; relevant widgets need to be repainted. + virtual void PaintStateChanged() = 0; + }; + + explicit InfoBarArrowModel(Observer* observer); + virtual ~InfoBarArrowModel(); + + // An infobar has been added or removed that will affect the state of this + // arrow. + void ShowArrowFor(InfoBar* bar, bool animate); + + // Returns true if the arrow is showing at all. + bool NeedToDrawInfoBarArrow(); + + // Paints the arrow on |widget|, in response to |expose|, with the bottom + // center of the arrow at |origin|, drawing a border with |border_color|. + void Paint(GtkWidget* widget, + GdkEventExpose* expose, + const gfx::Point& origin, + const GdkColor& border_color); + + // Overridden from AnimationDelegate. + virtual void AnimationEnded(const Animation* animation); + virtual void AnimationProgressed(const Animation* animation); + virtual void AnimationCanceled(const Animation* animation); + + private: + // A pair of colors used to draw a gradient for an arrow. + struct InfoBarColors { + SkColor top; + SkColor bottom; + }; + + // Calculates the currently showing arrow color, which is a blend of the new + // arrow color and the old arrow color. + InfoBarColors CurrentInfoBarColors(); + + // The view that owns us. + Observer* observer_; + + // An animation that tracks the progress of the transition from the last color + // to the new color. + SlideAnimation animation_; + + // The color we are animating towards. + InfoBarColors target_colors_; + // The last color we showed (the one we are animating away from). + InfoBarColors previous_colors_; + + DISALLOW_COPY_AND_ASSIGN(InfoBarArrowModel); +}; + +#endif // CHROME_BROWSER_GTK_INFOBAR_ARROW_MODEL_H_ diff --git a/chrome/browser/gtk/infobar_container_gtk.cc b/chrome/browser/gtk/infobar_container_gtk.cc index 01c6ccc..b5c95e3 100644 --- a/chrome/browser/gtk/infobar_container_gtk.cc +++ b/chrome/browser/gtk/infobar_container_gtk.cc @@ -143,6 +143,36 @@ void InfoBarContainerGtk::UpdateInfoBars() { } } +void InfoBarContainerGtk::ShowArrowForDelegate(InfoBarDelegate* delegate, + bool animate) { + GList* children = gtk_container_get_children(GTK_CONTAINER(widget())); + if (!children) + return; + + // Iterate through the infobars and find the last one that isn't closing. + InfoBar* last_bar = NULL; + InfoBar* this_bar = NULL; + for (GList* iter = children; iter != NULL; iter = iter->next) { + this_bar = reinterpret_cast<InfoBar*>( + g_object_get_data(G_OBJECT(iter->data), kInfoBar)); + + if (this_bar->delegate() == delegate) + break; + + if (!this_bar->IsClosing()) + last_bar = this_bar; + + this_bar = NULL; + } + + if (last_bar) + last_bar->ShowArrowFor(this_bar, animate); + else + UpdateToolbarInfoBarState(this_bar, animate); + + g_list_free(children); +} + void InfoBarContainerGtk::AddInfoBar(InfoBarDelegate* delegate, bool animate) { InfoBar* infobar = delegate->CreateInfoBar(); infobar->set_container(this); @@ -155,8 +185,7 @@ void InfoBarContainerGtk::AddInfoBar(InfoBarDelegate* delegate, bool animate) { else infobar->Open(); - if (tab_contents_->GetInfoBarDelegateAt(0) == delegate) - UpdateToolbarInfoBarState(infobar, animate); + ShowArrowForDelegate(delegate, animate); } void InfoBarContainerGtk::RemoveInfoBar(InfoBarDelegate* delegate, @@ -169,45 +198,22 @@ void InfoBarContainerGtk::RemoveInfoBar(InfoBarDelegate* delegate, delegate); } - if (tab_contents_->GetInfoBarDelegateAt(0) == delegate) { - InfoBar* bar = NULL; - // Get the next infobar, if it exists, so we can change the color of the - // arrow to it. - GList* children = gtk_container_get_children(GTK_CONTAINER(widget())); - if (children) { - if (children->next) { - bar = reinterpret_cast<InfoBar*>( - g_object_get_data(G_OBJECT(children->next->data), kInfoBar)); - } - g_list_free(children); + InfoBarDelegate* next_delegate = NULL; + for (int i = 1; i < tab_contents_->infobar_delegate_count(); ++i) { + if (tab_contents_->GetInfoBarDelegateAt(i - 1) == delegate) { + next_delegate = tab_contents_->GetInfoBarDelegateAt(i); + break; } - - UpdateToolbarInfoBarState(bar, animate); } + + ShowArrowForDelegate(next_delegate, animate); } void InfoBarContainerGtk::UpdateToolbarInfoBarState( InfoBar* infobar, bool animate) { - if (!CommandLine::ForCurrentProcess()-> - HasSwitch(switches::kEnableSecureInfoBars)) { - return; - } - - scoped_ptr<std::pair<SkColor, SkColor> > colors; - - if (infobar) { - double r, g, b; - infobar->GetTopColor(infobar->delegate()->GetInfoBarType(), &r, &g, &b); - SkColor top = SkColorSetRGB(r * 0xff, g * 0xff, b * 0xff); - infobar->GetBottomColor(infobar->delegate()->GetInfoBarType(), &r, &g, &b); - SkColor bottom = SkColorSetRGB(r * 0xff, g * 0xff, b * 0xff); - - colors.reset(new std::pair<SkColor, SkColor>(top, bottom)); - } - GtkWindow* parent = platform_util::GetTopLevel(widget()); BrowserWindowGtk* browser_window = BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent); if (browser_window) - browser_window->SetInfoBarShowing(colors.get(), animate); + browser_window->SetInfoBarShowing(infobar, animate); } diff --git a/chrome/browser/gtk/infobar_container_gtk.h b/chrome/browser/gtk/infobar_container_gtk.h index cb81580..ba676a9 100644 --- a/chrome/browser/gtk/infobar_container_gtk.h +++ b/chrome/browser/gtk/infobar_container_gtk.h @@ -51,6 +51,10 @@ class InfoBarContainerGtk : public NotificationObserver { // this process. void UpdateInfoBars(); + // Makes the calls to show an arrow for |delegate| (either on the browser + // toolbar or on the next infobar up). + void ShowArrowForDelegate(InfoBarDelegate* delegate, bool animate); + // Adds an InfoBar for the specified delegate, in response to a notification // from the selected TabContents. void AddInfoBar(InfoBarDelegate* delegate, bool animate); diff --git a/chrome/browser/gtk/infobar_gtk.cc b/chrome/browser/gtk/infobar_gtk.cc index ce420ac..5a061d0 100644 --- a/chrome/browser/gtk/infobar_gtk.cc +++ b/chrome/browser/gtk/infobar_gtk.cc @@ -7,12 +7,14 @@ #include <gtk/gtk.h> #include "base/utf_string_conversions.h" +#include "chrome/browser/gtk/browser_window_gtk.h" #include "chrome/browser/gtk/custom_button.h" #include "chrome/browser/gtk/gtk_chrome_link_button.h" #include "chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h" #include "chrome/browser/gtk/gtk_theme_provider.h" #include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/gtk/infobar_container_gtk.h" +#include "chrome/browser/platform_util.h" #include "chrome/common/notification_service.h" #include "gfx/gtk_util.h" @@ -37,7 +39,8 @@ const int kRightPadding = 5; InfoBar::InfoBar(InfoBarDelegate* delegate) : container_(NULL), delegate_(delegate), - theme_provider_(NULL) { + theme_provider_(NULL), + arrow_model_(this) { // Create |hbox_| and pad the sides. hbox_ = gtk_hbox_new(FALSE, kElementPadding); @@ -48,17 +51,13 @@ InfoBar::InfoBar(InfoBarDelegate* delegate) gtk_alignment_set_padding(GTK_ALIGNMENT(padding), 0, 0, kLeftPadding, kRightPadding); - GtkWidget* bg_box = gtk_event_box_new(); - gtk_widget_set_app_paintable(bg_box, TRUE); - g_signal_connect(bg_box, "expose-event", + bg_box_ = gtk_event_box_new(); + gtk_widget_set_app_paintable(bg_box_, TRUE); + g_signal_connect(bg_box_, "expose-event", G_CALLBACK(OnBackgroundExposeThunk), this); gtk_container_add(GTK_CONTAINER(padding), hbox_); - gtk_container_add(GTK_CONTAINER(bg_box), padding); - // The -1 on the kInfoBarHeight is to account for the border. - gtk_widget_set_size_request(bg_box, -1, kInfoBarHeight - 1); - - border_bin_.Own(gtk_util::CreateGtkBorderBin(bg_box, NULL, - 0, 1, 0, 0)); + gtk_container_add(GTK_CONTAINER(bg_box_), padding); + gtk_widget_set_size_request(bg_box_, -1, kInfoBarHeight); // Add the icon on the left, if any. SkBitmap* icon = delegate->GetIcon(); @@ -69,13 +68,12 @@ InfoBar::InfoBar(InfoBarDelegate* delegate) gtk_box_pack_start(GTK_BOX(hbox_), image, FALSE, FALSE, 0); } - // TODO(erg): GTK theme the info bar. close_button_.reset(CustomDrawButton::CloseButton(NULL)); gtk_util::CenterWidgetInHBox(hbox_, close_button_->widget(), true, 0); g_signal_connect(close_button_->widget(), "clicked", G_CALLBACK(OnCloseButtonThunk), this); - slide_widget_.reset(new SlideAnimatorGtk(border_bin_.get(), + slide_widget_.reset(new SlideAnimatorGtk(bg_box_, SlideAnimatorGtk::DOWN, 0, true, true, this)); // We store a pointer back to |this| so we can refer to it from the infobar @@ -84,7 +82,6 @@ InfoBar::InfoBar(InfoBarDelegate* delegate) } InfoBar::~InfoBar() { - border_bin_.Destroy(); } GtkWidget* InfoBar::widget() { @@ -93,14 +90,18 @@ GtkWidget* InfoBar::widget() { void InfoBar::AnimateOpen() { slide_widget_->Open(); - if (border_bin_->window) - gdk_window_lower(border_bin_->window); + + gtk_widget_show_all(bg_box_); + if (bg_box_->window) + gdk_window_lower(bg_box_->window); } void InfoBar::Open() { slide_widget_->OpenWithoutAnimation(); - if (border_bin_->window) - gdk_window_lower(border_bin_->window); + + gtk_widget_show_all(bg_box_); + if (bg_box_->window) + gdk_window_lower(bg_box_->window); } void InfoBar::AnimateClose() { @@ -119,6 +120,18 @@ bool InfoBar::IsAnimating() { return slide_widget_->IsAnimating(); } +bool InfoBar::IsClosing() { + return slide_widget_->IsClosing(); +} + +void InfoBar::ShowArrowFor(InfoBar* other, bool animate) { + arrow_model_.ShowArrowFor(other, animate); +} + +void InfoBar::PaintStateChanged() { + gtk_widget_queue_draw(widget()); +} + void InfoBar::RemoveInfoBar() const { container_->RemoveDelegate(delegate_); } @@ -153,6 +166,8 @@ void InfoBar::AddLabelWithInlineLink(const string16& display_text, UTF16ToUTF8(link_text).c_str()); gtk_chrome_link_button_set_use_gtk_theme( GTK_CHROME_LINK_BUTTON(link_button), FALSE); + gtk_util::ForceFontSizePixels( + GTK_CHROME_LINK_BUTTON(link_button)->label, 13.4); DCHECK(callback); g_signal_connect(link_button, "clicked", callback, this); gtk_util::SetButtonTriggersNavigation(link_button); @@ -169,11 +184,14 @@ void InfoBar::AddLabelWithInlineLink(const string16& display_text, GtkWidget* trailing_label = gtk_label_new( UTF16ToUTF8(display_text.substr(link_offset)).c_str()); - // TODO(joth): Unlike the AddLabalAndLink below, none of the label widgets + gtk_util::ForceFontSizePixels(initial_label, 13.4); + gtk_util::ForceFontSizePixels(trailing_label, 13.4); + + // TODO(joth): Unlike the AddLabelAndLink below, none of the label widgets // are set as shrinkable here, meaning the text will run under the close // button etc. when the width is restricted, rather than eliding. - gtk_widget_modify_fg(initial_label, GTK_STATE_NORMAL, &gfx::kGdkBlack); - gtk_widget_modify_fg(trailing_label, GTK_STATE_NORMAL, &gfx::kGdkBlack); + gtk_widget_modify_fg(initial_label, GTK_STATE_NORMAL, >k_util::kGdkBlack); + gtk_widget_modify_fg(trailing_label, GTK_STATE_NORMAL, >k_util::kGdkBlack); // We don't want any spacing between the elements, so we pack them into // this hbox that doesn't use kElementPadding. @@ -208,12 +226,13 @@ void InfoBar::AddLabelAndLink(const string16& display_text, if (link_button) gtk_box_pack_end(GTK_BOX(hbox), link_button, FALSE, FALSE, 0); GtkWidget* label = gtk_label_new(UTF16ToUTF8(display_text).c_str()); + gtk_util::ForceFontSizePixels(label, 13.4); // In order to avoid the link_button and the label overlapping with each // other, we make the label shrinkable. gtk_widget_set_size_request(label, 0, -1); gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gfx::kGdkBlack); + gtk_widget_modify_fg(label, GTK_STATE_NORMAL, >k_util::kGdkBlack); gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); } @@ -253,8 +272,7 @@ void InfoBar::GetBottomColor(InfoBarDelegate::Type type, } void InfoBar::UpdateBorderColor() { - GdkColor border_color = theme_provider_->GetBorderColor(); - gtk_widget_modify_bg(border_bin_.get(), GTK_STATE_NORMAL, &border_color); + gtk_widget_queue_draw(widget()); } void InfoBar::OnCloseButton(GtkWidget* button) { @@ -263,11 +281,11 @@ void InfoBar::OnCloseButton(GtkWidget* button) { RemoveInfoBar(); } -gboolean InfoBar::OnBackgroundExpose(GtkWidget* widget, +gboolean InfoBar::OnBackgroundExpose(GtkWidget* sender, GdkEventExpose* event) { - const int height = widget->allocation.height; + const int height = sender->allocation.height; - cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(widget->window)); + cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(sender->window)); gdk_cairo_rectangle(cr, &event->area); cairo_clip(cr); @@ -285,8 +303,30 @@ gboolean InfoBar::OnBackgroundExpose(GtkWidget* widget, cairo_paint(cr); cairo_pattern_destroy(pattern); + // Draw the bottom border. + GdkColor border_color = theme_provider_->GetBorderColor(); + cairo_set_source_rgb(cr, border_color.red / 65535.0, + border_color.green / 65535.0, + border_color.blue / 65535.0); + cairo_set_line_width(cr, 1.0); + int y = sender->allocation.height; + cairo_move_to(cr, 0, y - 0.5); + cairo_rel_line_to(cr, sender->allocation.width, 0); + cairo_stroke(cr); + cairo_destroy(cr); + if (!arrow_model_.NeedToDrawInfoBarArrow()) + return FALSE; + + GtkWindow* parent = platform_util::GetTopLevel(widget()); + BrowserWindowGtk* browser_window = + BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent); + int x = browser_window ? + browser_window->GetXPositionOfLocationIcon(sender) : 0; + + arrow_model_.Paint(sender, event, gfx::Point(x, y), border_color); + return FALSE; } @@ -297,7 +337,6 @@ class AlertInfoBar : public InfoBar { explicit AlertInfoBar(AlertInfoBarDelegate* delegate) : InfoBar(delegate) { AddLabelAndLink(delegate->GetMessageText(), string16(), NULL); - gtk_widget_show_all(border_bin_.get()); } }; @@ -312,7 +351,6 @@ class LinkInfoBar : public InfoBar { string16 link_text = delegate->GetLinkText(); AddLabelWithInlineLink(display_text, link_text, link_offset, G_CALLBACK(OnLinkClick)); - gtk_widget_show_all(border_bin_.get()); } private: @@ -352,9 +390,10 @@ ConfirmInfoBar::ConfirmInfoBar(ConfirmInfoBarDelegate* delegate) std::string label_text = UTF16ToUTF8(delegate->GetMessageText()); GtkWidget* label = gtk_label_new(label_text.c_str()); + gtk_util::ForceFontSizePixels(label, 13.4); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_util::CenterWidgetInHBox(confirm_hbox_, label, false, kEndOfLabelSpacing); - gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gfx::kGdkBlack); + gtk_widget_modify_fg(label, GTK_STATE_NORMAL, >k_util::kGdkBlack); g_signal_connect(label, "map", G_CALLBACK(gtk_util::InitLabelSizeRequestAndEllipsizeMode), NULL); @@ -371,8 +410,6 @@ ConfirmInfoBar::ConfirmInfoBar(ConfirmInfoBarDelegate* delegate) // 13.4px == 10pt @ 96dpi gtk_util::ForceFontSizePixels(GTK_CHROME_LINK_BUTTON(link)->label, 13.4); gtk_util::CenterWidgetInHBox(hbox_, link, true, kEndOfLabelSpacing); - - gtk_widget_show_all(border_bin_.get()); } void ConfirmInfoBar::AddButton(ConfirmInfoBarDelegate::InfoBarButton type) { diff --git a/chrome/browser/gtk/infobar_gtk.h b/chrome/browser/gtk/infobar_gtk.h index 5589f9e..4293ea8 100644 --- a/chrome/browser/gtk/infobar_gtk.h +++ b/chrome/browser/gtk/infobar_gtk.h @@ -7,13 +7,16 @@ #pragma once #include "app/gtk_signal.h" +#include "app/slide_animation.h" #include "base/basictypes.h" #include "base/scoped_ptr.h" +#include "chrome/browser/gtk/infobar_arrow_model.h" #include "chrome/browser/gtk/owned_widget_gtk.h" #include "chrome/browser/gtk/slide_animator_gtk.h" #include "chrome/browser/tab_contents/infobar_delegate.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" +#include "third_party/skia/include/core/SkPaint.h" class CustomDrawButton; class GtkThemeProvider; @@ -21,7 +24,8 @@ class InfoBarContainerGtk; class InfoBarDelegate; class InfoBar : public SlideAnimatorGtk::Delegate, - public NotificationObserver { + public NotificationObserver, + public InfoBarArrowModel::Observer { public: explicit InfoBar(InfoBarDelegate* delegate); virtual ~InfoBar(); @@ -53,12 +57,22 @@ class InfoBar : public SlideAnimatorGtk::Delegate, // Returns true if the infobar is showing the its open or close animation. bool IsAnimating(); + // Returns true if the infobar is showing the close animation. + bool IsClosing(); + void SetThemeProvider(GtkThemeProvider* theme_provider); + // Show an arrow that originates from another infobar (i.e. a bar was added + // below this one). If |other| is NULL, stop showing the arrow. + void ShowArrowFor(InfoBar* other, bool animate); + + // InfoBarArrowModel::Observer implementation. + virtual void PaintStateChanged(); + // SlideAnimatorGtk::Delegate implementation. virtual void Closed(); - // NotificationOPbserver implementation. + // NotificationObserver implementation. virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details); @@ -95,8 +109,8 @@ class InfoBar : public SlideAnimatorGtk::Delegate, // The top level widget of the infobar. scoped_ptr<SlideAnimatorGtk> slide_widget_; - // The second highest level widget of the infobar. - OwnedWidgetGtk border_bin_; + // The second highest widget in the hierarchy (after the slide widget). + GtkWidget* bg_box_; // The hbox that holds infobar elements (button, text, icon, etc.). GtkWidget* hbox_; @@ -113,6 +127,10 @@ class InfoBar : public SlideAnimatorGtk::Delegate, // The theme provider, used for getting border colors. GtkThemeProvider* theme_provider_; + // The model that tracks the paint state of the arrow for the infobar + // below this one (if it exists). + InfoBarArrowModel arrow_model_; + NotificationRegistrar registrar_; private: diff --git a/chrome/browser/gtk/instant_confirm_dialog_gtk.cc b/chrome/browser/gtk/instant_confirm_dialog_gtk.cc new file mode 100644 index 0000000..c313cec --- /dev/null +++ b/chrome/browser/gtk/instant_confirm_dialog_gtk.cc @@ -0,0 +1,82 @@ +// 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/instant_confirm_dialog_gtk.h" + +#include <gtk/gtk.h> + +#include "app/l10n_util.h" +#include "chrome/browser/gtk/gtk_chrome_link_button.h" +#include "chrome/browser/gtk/gtk_util.h" +#include "chrome/browser/instant/instant_confirm_dialog.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/show_options_url.h" +#include "chrome/common/pref_names.h" +#include "googleurl/src/gurl.h" +#include "grit/generated_resources.h" + +namespace browser { + +void ShowInstantConfirmDialog(GtkWindow* parent, Profile* profile) { + new InstantConfirmDialogGtk(parent, profile); +} + +} // namespace browser + +InstantConfirmDialogGtk::InstantConfirmDialogGtk( + GtkWindow* parent, Profile* profile) : profile_(profile) { + dialog_ = gtk_dialog_new_with_buttons( + l10n_util::GetStringUTF8(IDS_INSTANT_OPT_IN_TITLE).c_str(), + parent, + static_cast<GtkDialogFlags>(GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR), + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + NULL); + g_signal_connect(dialog_, "response", + G_CALLBACK(OnDialogResponseThunk), this); + + GtkBox* vbox = GTK_BOX(GTK_DIALOG(dialog_)->vbox); + gtk_box_set_spacing(vbox, gtk_util::kControlSpacing); + + GtkWidget* label = gtk_label_new( + l10n_util::GetStringUTF8(IDS_INSTANT_OPT_IN_MESSAGE).c_str()); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_box_pack_start(vbox, label, FALSE, FALSE, 0); + + GtkWidget* link_button = gtk_chrome_link_button_new( + l10n_util::GetStringUTF8(IDS_OPTIONS_LEARN_MORE_LABEL).c_str()); + g_signal_connect(link_button, "clicked", + G_CALLBACK(OnLinkButtonClickedThunk), this); + + GtkWidget* action_area = GTK_DIALOG(dialog_)->action_area; + gtk_container_add(GTK_CONTAINER(action_area), link_button); + gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(action_area), + link_button, + TRUE); + + gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_ACCEPT); + gtk_widget_show_all(dialog_); +} + +InstantConfirmDialogGtk::~InstantConfirmDialogGtk() { + gtk_widget_destroy(dialog_); +} + +void InstantConfirmDialogGtk::OnDialogResponse(GtkWidget* dialog, + int response) { + if (response == GTK_RESPONSE_ACCEPT) { + PrefService* service = profile_->GetPrefs(); + if (service) { + service->SetBoolean(prefs::kInstantEnabled, true); + service->SetBoolean(prefs::kInstantConfirmDialogShown, true); + } + } + + delete this; +} + +void InstantConfirmDialogGtk::OnLinkButtonClicked(GtkWidget* button) { + browser::ShowOptionsURL(profile_, GURL(browser::kInstantLearnMoreURL)); +} diff --git a/chrome/browser/gtk/instant_confirm_dialog_gtk.h b/chrome/browser/gtk/instant_confirm_dialog_gtk.h new file mode 100644 index 0000000..29a184e --- /dev/null +++ b/chrome/browser/gtk/instant_confirm_dialog_gtk.h @@ -0,0 +1,31 @@ +// 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_INSTANT_CONFIRM_DIALOG_GTK_H_ +#define CHROME_BROWSER_GTK_INSTANT_CONFIRM_DIALOG_GTK_H_ + +#include "app/gtk_signal.h" +#include "base/basictypes.h" + +class Profile; +typedef struct _GtkWindow GtkWindow; + +// A dialog that explains some of the privacy implications surrounding instant. +// Shown when the user enables instant for the first time. +class InstantConfirmDialogGtk { + public: + InstantConfirmDialogGtk(GtkWindow* parent, Profile* profile); + ~InstantConfirmDialogGtk(); + + private: + CHROMEGTK_CALLBACK_1(InstantConfirmDialogGtk, void, OnDialogResponse, int); + CHROMEGTK_CALLBACK_0(InstantConfirmDialogGtk, void, OnLinkButtonClicked); + + GtkWidget* dialog_; + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(InstantConfirmDialogGtk); +}; + +#endif // CHROME_BROWSER_GTK_INSTANT_CONFIRM_DIALOG_GTK_H_ diff --git a/chrome/browser/gtk/js_modal_dialog_gtk.cc b/chrome/browser/gtk/js_modal_dialog_gtk.cc index 3689356..b4a0953 100644 --- a/chrome/browser/gtk/js_modal_dialog_gtk.cc +++ b/chrome/browser/gtk/js_modal_dialog_gtk.cc @@ -6,7 +6,6 @@ #include <gtk/gtk.h> -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "app/message_box_flags.h" #include "base/logging.h" diff --git a/chrome/browser/gtk/keyword_editor_view.cc b/chrome/browser/gtk/keyword_editor_view.cc index 8967f4e..0753e28 100644 --- a/chrome/browser/gtk/keyword_editor_view.cc +++ b/chrome/browser/gtk/keyword_editor_view.cc @@ -7,7 +7,6 @@ #include <string> #include "app/l10n_util.h" -#include "base/gtk_util.h" #include "base/message_loop.h" #include "base/utf_string_conversions.h" #include "chrome/browser/gtk/accessible_widget_helper_gtk.h" @@ -168,7 +167,7 @@ void KeywordEditorView::Init() { gtk_box_pack_start(GTK_BOX(hbox), button_box, FALSE, FALSE, 0); add_button_ = gtk_button_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8( IDS_SEARCH_ENGINES_EDITOR_NEW_BUTTON)).c_str()); g_signal_connect(add_button_, "clicked", @@ -182,7 +181,7 @@ void KeywordEditorView::Init() { gtk_box_pack_start(GTK_BOX(button_box), edit_button_, FALSE, FALSE, 0); remove_button_ = gtk_button_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8( IDS_SEARCH_ENGINES_EDITOR_REMOVE_BUTTON)).c_str()); g_signal_connect(remove_button_, "clicked", diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc index 6f02d2c..b207665 100644 --- a/chrome/browser/gtk/location_bar_view_gtk.cc +++ b/chrome/browser/gtk/location_bar_view_gtk.cc @@ -14,10 +14,11 @@ #include "base/logging.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/accessibility_events.h" #include "chrome/browser/alternate_nav_url_fetcher.h" #include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h" +#include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/command_updater.h" @@ -38,6 +39,7 @@ #include "chrome/browser/gtk/nine_box.h" #include "chrome/browser/gtk/rounded_window.h" #include "chrome/browser/gtk/view_id_util.h" +#include "chrome/browser/instant/instant_controller.h" #include "chrome/browser/location_bar_util.h" #include "chrome/browser/profile.h" #include "chrome/browser/search_engines/template_url.h" @@ -143,7 +145,8 @@ LocationBarViewGtk::LocationBarViewGtk(Browser* browser) hbox_width_(0), entry_box_width_(0), show_selected_keyword_(false), - show_keyword_hint_(false) { + show_keyword_hint_(false), + update_instant_(true) { } LocationBarViewGtk::~LocationBarViewGtk() { @@ -429,39 +432,87 @@ void LocationBarViewGtk::Update(const TabContents* contents) { } } -void LocationBarViewGtk::OnAutocompleteAccept(const GURL& url, - WindowOpenDisposition disposition, - PageTransition::Type transition, - const GURL& alternate_nav_url) { - if (!url.is_valid()) +void LocationBarViewGtk::OnAutocompleteWillClosePopup() { + if (!update_instant_) return; - location_input_ = UTF8ToWide(url.spec()); - disposition_ = disposition; - transition_ = transition; + InstantController* instant = browser_->instant(); + if (instant && !instant->commit_on_mouse_up()) + instant->DestroyPreviewContents(); +} - if (!command_updater_) - return; +void LocationBarViewGtk::OnAutocompleteLosingFocus( + gfx::NativeView view_gaining_focus) { + SetSuggestedText(string16()); - if (!alternate_nav_url.is_valid()) { - command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); - return; - } + InstantController* instant = browser_->instant(); + if (instant) + instant->OnAutocompleteLostFocus(view_gaining_focus); +} - AlternateNavURLFetcher* fetcher = - new AlternateNavURLFetcher(alternate_nav_url); - // The AlternateNavURLFetcher will listen for the pending navigation - // notification that will be issued as a result of the "open URL." It - // will automatically install itself into that navigation controller. - command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); - if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) { - // I'm not sure this should be reachable, but I'm not also sure enough - // that it shouldn't to stick in a NOTREACHED(). In any case, this is - // harmless. - delete fetcher; - } else { - // The navigation controller will delete the fetcher. +void LocationBarViewGtk::OnAutocompleteWillAccept() { + update_instant_ = false; +} + +bool LocationBarViewGtk::OnCommitSuggestedText( + const std::wstring& typed_text) { + InstantController* instant = browser_->instant(); + if (!instant) + return false; + + bool updating_instant = update_instant_; + update_instant_ = false; + bool rv = location_entry_->CommitInstantSuggestion(); + update_instant_ = updating_instant; + return rv; +} + +void LocationBarViewGtk::OnSetSuggestedSearchText( + const string16& suggested_text) { + SetSuggestedText(suggested_text); +} + +void LocationBarViewGtk::OnPopupBoundsChanged(const gfx::Rect& bounds) { + InstantController* instant = browser_->instant(); + if (instant) + instant->SetOmniboxBounds(bounds); +} + +void LocationBarViewGtk::OnAutocompleteAccept(const GURL& url, + WindowOpenDisposition disposition, + PageTransition::Type transition, + const GURL& alternate_nav_url) { + if (url.is_valid()) { + location_input_ = UTF8ToWide(url.spec()); + disposition_ = disposition; + transition_ = transition; + + if (command_updater_) { + if (!alternate_nav_url.is_valid()) { + command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); + } else { + AlternateNavURLFetcher* fetcher = + new AlternateNavURLFetcher(alternate_nav_url); + // The AlternateNavURLFetcher will listen for the pending navigation + // notification that will be issued as a result of the "open URL." It + // will automatically install itself into that navigation controller. + command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); + if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) { + // I'm not sure this should be reachable, but I'm not also sure enough + // that it shouldn't to stick in a NOTREACHED(). In any case, this is + // harmless. + delete fetcher; + } else { + // The navigation controller will delete the fetcher. + } + } + } } + + if (browser_->instant()) + browser_->instant()->DestroyPreviewContents(); + + update_instant_ = true; } void LocationBarViewGtk::OnChanged() { @@ -479,6 +530,22 @@ void LocationBarViewGtk::OnChanged() { SetKeywordHintLabel(keyword); AdjustChildrenVisibility(); + + InstantController* instant = browser_->instant(); + string16 suggested_text; + if (update_instant_ && instant && GetTabContents()) { + if (location_entry_->model()->user_input_in_progress() && + location_entry_->model()->popup_model()->IsOpen()) { + instant->Update(GetTabContents(), + location_entry_->model()->CurrentMatch(), + WideToUTF16(location_entry_->GetText()), + &suggested_text); + } else { + instant->DestroyPreviewContents(); + } + } + + SetSuggestedText(suggested_text); } void LocationBarViewGtk::CreateStarButton() { @@ -545,8 +612,7 @@ void LocationBarViewGtk::ShowFirstRunBubble(FirstRun::BubbleType bubble_type) { } void LocationBarViewGtk::SetSuggestedText(const string16& text) { - // TODO: implement me. - NOTIMPLEMENTED(); + location_entry_->SetInstantSuggestion(UTF16ToUTF8(text)); } std::wstring LocationBarViewGtk::GetInputString() const { @@ -742,8 +808,8 @@ void LocationBarViewGtk::Observe(NotificationType type, gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_, kKeywordBorderColor); - gtk_util::SetLabelColor(tab_to_search_full_label_, &gfx::kGdkBlack); - gtk_util::SetLabelColor(tab_to_search_partial_label_, &gfx::kGdkBlack); + gtk_util::SetLabelColor(tab_to_search_full_label_, >k_util::kGdkBlack); + gtk_util::SetLabelColor(tab_to_search_partial_label_, >k_util::kGdkBlack); gtk_util::SetLabelColor(tab_to_search_hint_leading_label_, &kHintTextColor); gtk_util::SetLabelColor(tab_to_search_hint_trailing_label_, @@ -1261,8 +1327,8 @@ LocationBarViewGtk::PageActionViewGtk::PageActionViewGtk( image_.Own(gtk_image_new()); gtk_container_add(GTK_CONTAINER(event_box_.get()), image_.get()); - Extension* extension = profile->GetExtensionsService()->GetExtensionById( - page_action->extension_id(), false); + const Extension* extension = profile->GetExtensionsService()-> + GetExtensionById(page_action->extension_id(), false); DCHECK(extension); // Load all the icons declared in the manifest. This is the contents of the @@ -1425,8 +1491,8 @@ gboolean LocationBarViewGtk::PageActionViewGtk::OnButtonPressed( event->button.button); } } else { - Extension* extension = profile_->GetExtensionsService()->GetExtensionById( - page_action()->extension_id(), false); + const Extension* extension = profile_->GetExtensionsService()-> + GetExtensionById(page_action()->extension_id(), false); context_menu_model_ = new ExtensionContextMenuModel(extension, owner_->browser_, this); diff --git a/chrome/browser/gtk/location_bar_view_gtk.h b/chrome/browser/gtk/location_bar_view_gtk.h index a0a3484..0759549 100644 --- a/chrome/browser/gtk/location_bar_view_gtk.h +++ b/chrome/browser/gtk/location_bar_view_gtk.h @@ -88,13 +88,13 @@ class LocationBarViewGtk : public AutocompleteEditController, void SetStarred(bool starred); // Implement the AutocompleteEditController interface. - virtual void OnAutocompleteWillClosePopup() {} - virtual void OnAutocompleteLosingFocus(gfx::NativeView view_gaining_focus) {} - virtual void OnAutocompleteWillAccept() {} - virtual bool OnCommitSuggestedText(const std::wstring& typed_text) { - return false; - } - virtual void OnPopupBoundsChanged(const gfx::Rect& bounds) {} + virtual void OnAutocompleteWillClosePopup(); + virtual void OnAutocompleteLosingFocus(gfx::NativeView view_gaining_focus); + virtual void OnAutocompleteWillAccept(); + // For this implementation, the parameter is ignored. + virtual bool OnCommitSuggestedText(const std::wstring& typed_text); + virtual void OnSetSuggestedSearchText(const string16& suggested_text); + virtual void OnPopupBoundsChanged(const gfx::Rect& bounds); virtual void OnAutocompleteAccept(const GURL& url, WindowOpenDisposition disposition, PageTransition::Type transition, @@ -421,6 +421,9 @@ class LocationBarViewGtk : public AutocompleteEditController, // The last search keyword that was shown via the |tab_to_search_box_|. std::wstring last_keyword_; + // True if we should update the instant controller when the edit text changes. + bool update_instant_; + DISALLOW_COPY_AND_ASSIGN(LocationBarViewGtk); }; diff --git a/chrome/browser/gtk/menu_gtk.cc b/chrome/browser/gtk/menu_gtk.cc index 5d56621..86a1ba1 100644 --- a/chrome/browser/gtk/menu_gtk.cc +++ b/chrome/browser/gtk/menu_gtk.cc @@ -9,22 +9,18 @@ #include "app/menus/accelerator_gtk.h" #include "app/menus/button_menu_item_model.h" #include "app/menus/menu_model.h" -#include "base/gtk_util.h" #include "base/i18n/rtl.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/stl_util-inl.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/gtk/gtk_custom_menu.h" #include "chrome/browser/gtk/gtk_custom_menu_item.h" #include "chrome/browser/gtk/gtk_util.h" #include "gfx/gtk_util.h" #include "third_party/skia/include/core/SkBitmap.h" -using gtk_util::ConvertAcceleratorsFromWindowsStyle; -using gtk_util::RemoveWindowsStyleAccelerators; - bool MenuGtk::block_activation_ = false; namespace { @@ -289,7 +285,7 @@ void MenuGtk::ConnectSignalHandlers() { GtkWidget* MenuGtk::AppendMenuItemWithLabel(int command_id, const std::string& label) { - std::string converted_label = ConvertAcceleratorsFromWindowsStyle(label); + std::string converted_label = gfx::ConvertAcceleratorsFromWindowsStyle(label); GtkWidget* menu_item = BuildMenuItemWithLabel(label, command_id); return AppendMenuItem(command_id, menu_item); } @@ -297,14 +293,14 @@ GtkWidget* MenuGtk::AppendMenuItemWithLabel(int command_id, GtkWidget* MenuGtk::AppendMenuItemWithIcon(int command_id, const std::string& label, const SkBitmap& icon) { - std::string converted_label = ConvertAcceleratorsFromWindowsStyle(label); + std::string converted_label = gfx::ConvertAcceleratorsFromWindowsStyle(label); GtkWidget* menu_item = BuildMenuItemWithImage(converted_label, icon); return AppendMenuItem(command_id, menu_item); } GtkWidget* MenuGtk::AppendCheckMenuItemWithLabel(int command_id, const std::string& label) { - std::string converted_label = ConvertAcceleratorsFromWindowsStyle(label); + std::string converted_label = gfx::ConvertAcceleratorsFromWindowsStyle(label); GtkWidget* menu_item = gtk_check_menu_item_new_with_mnemonic(converted_label.c_str()); return AppendMenuItem(command_id, menu_item); @@ -432,7 +428,8 @@ void MenuGtk::BuildSubmenuFromModel(menus::MenuModel* model, GtkWidget* menu) { for (int i = 0; i < model->GetItemCount(); ++i) { SkBitmap icon; std::string label = - ConvertAcceleratorsFromWindowsStyle(UTF16ToUTF8(model->GetLabelAt(i))); + gfx::ConvertAcceleratorsFromWindowsStyle( + UTF16ToUTF8(model->GetLabelAt(i))); bool connect_to_activate = true; switch (model->GetTypeAt(i)) { @@ -508,7 +505,7 @@ void MenuGtk::BuildSubmenuFromModel(menus::MenuModel* model, GtkWidget* menu) { GtkWidget* MenuGtk::BuildButtomMenuItem(menus::ButtonMenuItemModel* model, GtkWidget* menu) { GtkWidget* menu_item = gtk_custom_menu_item_new( - RemoveWindowsStyleAccelerators(UTF16ToUTF8(model->label())).c_str()); + gfx::RemoveWindowsStyleAccelerators(UTF16ToUTF8(model->label())).c_str()); // Set up the callback to the model for when it is clicked. g_object_set_data(G_OBJECT(menu_item), "button-model", model); @@ -537,7 +534,7 @@ GtkWidget* MenuGtk::BuildButtomMenuItem(menus::ButtonMenuItemModel* model, } else { gtk_button_set_label( GTK_BUTTON(button), - RemoveWindowsStyleAccelerators( + gfx::RemoveWindowsStyleAccelerators( UTF16ToUTF8(model->GetLabelAt(i))).c_str()); } @@ -550,7 +547,7 @@ GtkWidget* MenuGtk::BuildButtomMenuItem(menus::ButtonMenuItemModel* model, model->GetCommandIdAt(i)); gtk_button_set_label( GTK_BUTTON(button), - RemoveWindowsStyleAccelerators( + gfx::RemoveWindowsStyleAccelerators( UTF16ToUTF8(model->GetLabelAt(i))).c_str()); SetupButtonShowHandler(button, model, i); break; @@ -719,7 +716,7 @@ void MenuGtk::SetButtonItemInfo(GtkWidget* button, gpointer userdata) { if (model->IsLabelDynamicAt(index)) { std::string label = - ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( UTF16ToUTF8(model->GetLabelAt(index))); gtk_button_set_label(GTK_BUTTON(button), label.c_str()); } @@ -778,7 +775,7 @@ void MenuGtk::SetMenuItemInfo(GtkWidget* widget, gpointer userdata) { // Update the menu item label if it is dynamic. if (model->IsLabelDynamicAt(id)) { std::string label = - ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( UTF16ToUTF8(model->GetLabelAt(id))); #if GTK_CHECK_VERSION(2, 16, 0) diff --git a/chrome/browser/gtk/notifications/balloon_view_gtk.cc b/chrome/browser/gtk/notifications/balloon_view_gtk.cc index 24a04cf..795b4ea 100644 --- a/chrome/browser/gtk/notifications/balloon_view_gtk.cc +++ b/chrome/browser/gtk/notifications/balloon_view_gtk.cc @@ -38,7 +38,6 @@ #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" #include "gfx/canvas.h" -#include "gfx/gtk_util.h" #include "gfx/insets.h" #include "gfx/native_widget_types.h" #include "grit/generated_resources.h" @@ -298,7 +297,7 @@ void BalloonViewImpl::Show(Balloon* balloon) { NotificationService::AllSources()); // We don't do InitThemesFor() because it just forces a redraw. - gtk_util::ActAsRoundedWindow(frame_container_, gfx::kGdkBlack, 3, + gtk_util::ActAsRoundedWindow(frame_container_, gtk_util::kGdkBlack, 3, gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL); diff --git a/chrome/browser/gtk/options/advanced_contents_gtk.cc b/chrome/browser/gtk/options/advanced_contents_gtk.cc index fa1c1c9..f98490e 100644 --- a/chrome/browser/gtk/options/advanced_contents_gtk.cc +++ b/chrome/browser/gtk/options/advanced_contents_gtk.cc @@ -11,7 +11,6 @@ #include <vector> #include "app/gtk_signal.h" -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "base/basictypes.h" #include "base/command_line.h" diff --git a/chrome/browser/gtk/options/advanced_page_gtk.cc b/chrome/browser/gtk/options/advanced_page_gtk.cc index 71c8052..8351322 100644 --- a/chrome/browser/gtk/options/advanced_page_gtk.cc +++ b/chrome/browser/gtk/options/advanced_page_gtk.cc @@ -4,7 +4,6 @@ #include "chrome/browser/gtk/options/advanced_page_gtk.h" -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/options_util.h" diff --git a/chrome/browser/gtk/options/content_page_gtk.cc b/chrome/browser/gtk/options/content_page_gtk.cc index a119904..c75028a 100644 --- a/chrome/browser/gtk/options/content_page_gtk.cc +++ b/chrome/browser/gtk/options/content_page_gtk.cc @@ -6,7 +6,6 @@ #include <string> -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "base/command_line.h" #include "base/utf_string_conversions.h" diff --git a/chrome/browser/gtk/options/cookies_view.cc b/chrome/browser/gtk/options/cookies_view.cc index 415dedb..557b22a 100644 --- a/chrome/browser/gtk/options/cookies_view.cc +++ b/chrome/browser/gtk/options/cookies_view.cc @@ -9,7 +9,6 @@ #include <string> #include "app/l10n_util.h" -#include "base/gtk_util.h" #include "base/message_loop.h" #include "base/string_util.h" #include "chrome/browser/cookies_tree_model.h" @@ -115,7 +114,7 @@ void CookiesView::Init(GtkWindow* parent) { remove_button_ = gtk_util::AddButtonToDialog( dialog_, - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8(IDS_COOKIES_REMOVE_LABEL)).c_str(), GTK_STOCK_REMOVE, RESPONSE_REMOVE); @@ -127,7 +126,7 @@ void CookiesView::Init(GtkWindow* parent) { remove_all_button_ = gtk_util::AddButtonToDialog( dialog_, - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8(IDS_COOKIES_REMOVE_ALL_LABEL)).c_str(), GTK_STOCK_CLEAR, RESPONSE_REMOVE_ALL); @@ -156,7 +155,7 @@ void CookiesView::Init(GtkWindow* parent) { gtk_box_pack_start(GTK_BOX(filter_hbox), filter_entry_, TRUE, TRUE, 0); filter_clear_button_ = gtk_button_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8(IDS_COOKIES_CLEAR_SEARCH_LABEL)).c_str()); g_signal_connect(filter_clear_button_, "clicked", G_CALLBACK(OnFilterClearButtonClickedThunk), this); diff --git a/chrome/browser/gtk/options/cookies_view_unittest.cc b/chrome/browser/gtk/options/cookies_view_unittest.cc index 76461a1..0c78d7f 100644 --- a/chrome/browser/gtk/options/cookies_view_unittest.cc +++ b/chrome/browser/gtk/options/cookies_view_unittest.cc @@ -90,8 +90,6 @@ class CookiesViewTest : public testing::Test { GTK_WIDGET_SENSITIVE(display->appcache_last_accessed_entry_)); // IndexedDB EXPECT_EQ(expected_indexed_db, - GTK_WIDGET_SENSITIVE(display->indexed_db_name_entry_)); - EXPECT_EQ(expected_indexed_db, GTK_WIDGET_SENSITIVE(display->indexed_db_origin_entry_)); EXPECT_EQ(expected_indexed_db, GTK_WIDGET_SENSITIVE(display->indexed_db_size_entry_)); diff --git a/chrome/browser/gtk/options/fonts_languages_window_gtk.cc b/chrome/browser/gtk/options/fonts_languages_window_gtk.cc index d51c10e..ce046a9 100644 --- a/chrome/browser/gtk/options/fonts_languages_window_gtk.cc +++ b/chrome/browser/gtk/options/fonts_languages_window_gtk.cc @@ -63,8 +63,7 @@ FontsLanguagesWindowGtk::FontsLanguagesWindowGtk(Profile* profile) fonts_page_(profile_), languages_page_(profile_) { dialog_ = gtk_dialog_new_with_buttons( - l10n_util::GetStringFUTF8(IDS_FONT_LANGUAGE_SETTING_WINDOWS_TITLE, - l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)).c_str(), + l10n_util::GetStringUTF8(IDS_FONT_LANGUAGE_SETTING_WINDOWS_TITLE).c_str(), // Prefs window is shared between all browser windows. NULL, // Non-modal. diff --git a/chrome/browser/gtk/options/fonts_page_gtk.cc b/chrome/browser/gtk/options/fonts_page_gtk.cc index 15ebf9b..555f098 100644 --- a/chrome/browser/gtk/options/fonts_page_gtk.cc +++ b/chrome/browser/gtk/options/fonts_page_gtk.cc @@ -5,6 +5,7 @@ #include "chrome/browser/gtk/options/fonts_page_gtk.h" #include "app/l10n_util.h" +#include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/default_encoding_combo_model.h" @@ -28,8 +29,8 @@ std::string MakeFontName(std::string family_name, int pixel_size) { // TODO(mattm): We can pass in the size in pixels (px), and the font button // actually honors it, but when you open the selector it interprets it as // points. See crbug.com/17857 - SStringPrintf(&fontname, "%s, %dpx", WideToUTF8(actual_family_name).c_str(), - pixel_size); + base::SStringPrintf(&fontname, "%s, %dpx", + WideToUTF8(actual_family_name).c_str(), pixel_size); return fontname; } diff --git a/chrome/browser/gtk/options/general_page_gtk.cc b/chrome/browser/gtk/options/general_page_gtk.cc index 1cc1ef9..01aef9e 100644 --- a/chrome/browser/gtk/options/general_page_gtk.cc +++ b/chrome/browser/gtk/options/general_page_gtk.cc @@ -9,21 +9,25 @@ #include "app/l10n_util.h" #include "base/callback.h" -#include "base/gtk_util.h" +#include "base/command_line.h" #include "base/utf_string_conversions.h" #include "chrome/browser/custom_home_pages_table_model.h" #include "chrome/browser/gtk/accessible_widget_helper_gtk.h" +#include "chrome/browser/gtk/gtk_chrome_link_button.h" #include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/gtk/keyword_editor_view.h" #include "chrome/browser/gtk/options/managed_prefs_banner_gtk.h" #include "chrome/browser/gtk/options/options_layout_gtk.h" #include "chrome/browser/gtk/options/url_picker_dialog_gtk.h" +#include "chrome/browser/instant/instant_confirm_dialog.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prefs/session_startup_pref.h" #include "chrome/browser/profile.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" +#include "chrome/browser/show_options_url.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" @@ -69,6 +73,7 @@ bool IsNewTabUIURLString(const GURL& url) { GeneralPageGtk::GeneralPageGtk(Profile* profile) : OptionsPageBase(profile), template_url_model_(NULL), + instant_checkbox_(NULL), default_search_initializing_(true), initializing_(true), default_browser_worker_( @@ -104,6 +109,8 @@ GeneralPageGtk::GeneralPageGtk(Profile* profile) homepage_.Init(prefs::kHomePage, profile->GetPrefs(), this); show_home_button_.Init(prefs::kShowHomeButton, profile->GetPrefs(), this); + instant_.Init(prefs::kInstantEnabled, profile->GetPrefs(), this); + // Load initial values NotifyPrefChanged(NULL); } @@ -115,6 +122,10 @@ GeneralPageGtk::~GeneralPageGtk() { default_browser_worker_->ObserverDestroyed(); } +GtkWindow* GeneralPageGtk::GetWindow() { + return GTK_WINDOW(gtk_widget_get_toplevel(page_)); +} + /////////////////////////////////////////////////////////////////////////////// // GeneralPageGtk, OptionsPageBase overrides: @@ -187,6 +198,12 @@ void GeneralPageGtk::NotifyPrefChanged(const std::string* pref_name) { !show_home_button_.IsManaged()); } + if ((!pref_name || *pref_name == prefs::kInstantEnabled) && + instant_checkbox_) { + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(instant_checkbox_), instant_.GetValue()); + } + initializing_ = false; } @@ -277,21 +294,21 @@ GtkWidget* GeneralPageGtk::InitStartupGroup() { FALSE, FALSE, 0); startup_add_custom_page_button_ = gtk_button_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8(IDS_OPTIONS_STARTUP_ADD_BUTTON)).c_str()); g_signal_connect(startup_add_custom_page_button_, "clicked", G_CALLBACK(OnStartupAddCustomPageClickedThunk), this); gtk_box_pack_start(GTK_BOX(url_list_buttons), startup_add_custom_page_button_, FALSE, FALSE, 0); startup_remove_custom_page_button_ = gtk_button_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8(IDS_OPTIONS_STARTUP_REMOVE_BUTTON)).c_str()); g_signal_connect(startup_remove_custom_page_button_, "clicked", G_CALLBACK(OnStartupRemoveCustomPageClickedThunk), this); gtk_box_pack_start(GTK_BOX(url_list_buttons), startup_remove_custom_page_button_, FALSE, FALSE, 0); startup_use_current_page_button_ = gtk_button_new_with_mnemonic( - gtk_util::ConvertAcceleratorsFromWindowsStyle( + gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8(IDS_OPTIONS_STARTUP_USE_CURRENT)).c_str()); g_signal_connect(startup_use_current_page_button_, "clicked", G_CALLBACK(OnStartupUseCurrentPageClickedThunk), this); @@ -337,7 +354,9 @@ GtkWidget* GeneralPageGtk::InitHomepageGroup() { } GtkWidget* GeneralPageGtk::InitDefaultSearchGroup() { - GtkWidget* hbox = gtk_hbox_new(FALSE, gtk_util::kControlSpacing); + GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); + GtkWidget* search_hbox = gtk_hbox_new(FALSE, gtk_util::kControlSpacing); + gtk_box_pack_start(GTK_BOX(vbox), search_hbox, FALSE, FALSE, 0); default_search_engines_model_ = gtk_list_store_new(SEARCH_ENGINES_COL_COUNT, G_TYPE_UINT, @@ -347,7 +366,8 @@ GtkWidget* GeneralPageGtk::InitDefaultSearchGroup() { g_object_unref(default_search_engines_model_); g_signal_connect(default_search_engine_combobox_, "changed", G_CALLBACK(OnDefaultSearchEngineChangedThunk), this); - gtk_container_add(GTK_CONTAINER(hbox), default_search_engine_combobox_); + gtk_container_add(GTK_CONTAINER(search_hbox), + default_search_engine_combobox_); accessible_widget_helper_->SetWidgetName( default_search_engine_combobox_, IDS_OPTIONS_DEFAULTSEARCH_GROUP_NAME); @@ -371,10 +391,46 @@ GtkWidget* GeneralPageGtk::InitDefaultSearchGroup() { IDS_OPTIONS_DEFAULTSEARCH_MANAGE_ENGINES_LINK).c_str()); g_signal_connect(default_search_manage_engines_button_, "clicked", G_CALLBACK(OnDefaultSearchManageEnginesClickedThunk), this); - gtk_box_pack_end(GTK_BOX(hbox), default_search_manage_engines_button_, - FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(search_hbox), + default_search_manage_engines_button_, FALSE, FALSE, 0); + + // When the instant lab is on, add some options for instant. We want the + // warning text and link to align with the pref's checkbox's label. + // Need a new vbox as we don't want any spacing between these labels. + GtkWidget* instant_vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), instant_vbox, FALSE, FALSE, 0); + + instant_checkbox_ = gtk_check_button_new_with_label( + l10n_util::GetStringUTF8(IDS_INSTANT_PREF).c_str()); + g_signal_connect(instant_checkbox_, "toggled", + G_CALLBACK(OnInstantToggledThunk), this); + gtk_box_pack_start(GTK_BOX(instant_vbox), instant_checkbox_, FALSE, FALSE, 0); + + // Relies on knowledge of GTK+ internals to find the checkbox's label child + // and then make the indent below match its vertical spacing. + GtkWidget* instant_label = gtk_bin_get_child(GTK_BIN(instant_checkbox_)); + if (instant_label && GTK_IS_LABEL(instant_label)) { + g_signal_connect(instant_label, "size-allocate", + G_CALLBACK(OnInstantLabelSizeAllocateThunk), this); + } - return hbox; + instant_indent_ = gtk_fixed_new(); + GtkWidget* explanation_box = gtk_hbox_new(FALSE, 0); + GtkWidget* explanation = gtk_label_new(( + l10n_util::GetStringUTF8(IDS_INSTANT_PREF_WARNING) + " ").c_str()); + GtkWidget* learn_more_link = gtk_chrome_link_button_new( + l10n_util::GetStringUTF8(IDS_LEARN_MORE).c_str()); + g_signal_connect(learn_more_link, "clicked", + G_CALLBACK(OnSearchLearnMoreClickedThunk), this); + gtk_box_pack_start(GTK_BOX(explanation_box), instant_indent_, + FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(explanation_box), explanation, + FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(explanation_box), learn_more_link, + FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(instant_vbox), explanation_box, FALSE, FALSE, 0); + + return vbox; } GtkWidget* GeneralPageGtk::InitDefaultBrowserGroup() { @@ -430,7 +486,7 @@ void GeneralPageGtk::OnStartupAddCustomPageClicked(GtkWidget* button) { new UrlPickerDialogGtk( NewCallback(this, &GeneralPageGtk::OnAddCustomUrl), profile(), - GTK_WINDOW(gtk_widget_get_toplevel(page_))); + GetWindow()); } void GeneralPageGtk::OnStartupRemoveCustomPageClicked(GtkWidget* button) { @@ -488,6 +544,23 @@ void GeneralPageGtk::OnShowHomeButtonToggled(GtkWidget* toggle_button) { } } +void GeneralPageGtk::OnInstantToggled(GtkWidget* toggle_button) { + if (initializing_) + return; + + bool enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle_button)); + + if (enabled) { + if (!instant_.GetValue()) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(instant_checkbox_), false); + browser::ShowInstantConfirmDialogIfNecessary(GetWindow(), profile()); + } else { + instant_.SetValue(enabled); + } + + // TODO(estade): UMA? +} + void GeneralPageGtk::OnDefaultSearchEngineChanged(GtkWidget* combo_box) { if (default_search_initializing_) return; @@ -719,3 +792,16 @@ void GeneralPageGtk::SetDefaultBrowserUIState( gtk_widget_set_sensitive(default_browser_use_as_default_button_, state == ShellIntegration::STATE_NOT_DEFAULT); } + +void GeneralPageGtk::OnInstantLabelSizeAllocate(GtkWidget* sender, + GtkAllocation* allocation) { + int desired_width = allocation->x - sender->parent->allocation.x; + GtkRequisition req; + gtk_widget_size_request(instant_indent_, &req); + if (req.width != desired_width) + gtk_widget_set_size_request(instant_indent_, desired_width, -1); +} + +void GeneralPageGtk::OnSearchLearnMoreClicked(GtkWidget* sender) { + browser::ShowOptionsURL(profile(), GURL(browser::kInstantLearnMoreURL)); +} diff --git a/chrome/browser/gtk/options/general_page_gtk.h b/chrome/browser/gtk/options/general_page_gtk.h index 143f447..0050b2b 100644 --- a/chrome/browser/gtk/options/general_page_gtk.h +++ b/chrome/browser/gtk/options/general_page_gtk.h @@ -36,6 +36,8 @@ class GeneralPageGtk : public OptionsPageBase, GtkWidget* get_page_widget() const { return page_; } private: + GtkWindow* GetWindow(); + // Overridden from OptionsPageBase virtual void NotifyPrefChanged(const std::string* pref_name); virtual void HighlightGroup(OptionsGroup highlight_group); @@ -92,7 +94,11 @@ class GeneralPageGtk : public OptionsPageBase, CHROMEGTK_CALLBACK_0(GeneralPageGtk, void, OnDefaultSearchEngineChanged); CHROMEGTK_CALLBACK_0(GeneralPageGtk, void, OnDefaultSearchManageEnginesClicked); + CHROMEGTK_CALLBACK_0(GeneralPageGtk, void, OnInstantToggled); CHROMEGTK_CALLBACK_0(GeneralPageGtk, void, OnBrowserUseAsDefaultClicked); + CHROMEGTK_CALLBACK_1(GeneralPageGtk, void, OnInstantLabelSizeAllocate, + GtkAllocation*); + CHROMEGTK_CALLBACK_0(GeneralPageGtk, void, OnSearchLearnMoreClicked); CHROMEG_CALLBACK_0(GeneralPageGtk, void, OnStartupPagesSelectionChanged, GtkTreeSelection*); @@ -137,6 +143,10 @@ class GeneralPageGtk : public OptionsPageBase, GtkListStore* default_search_engines_model_; GtkWidget* default_search_manage_engines_button_; TemplateURLModel* template_url_model_; + GtkWidget* instant_checkbox_; + // This widget acts as the indent for the instant warning label. + GtkWidget* instant_indent_; + BooleanPrefMember instant_; // Widgets of the default browser group GtkWidget* default_browser_status_label_; diff --git a/chrome/browser/gtk/options/languages_page_gtk.cc b/chrome/browser/gtk/options/languages_page_gtk.cc index 443cca8..eda179f 100644 --- a/chrome/browser/gtk/options/languages_page_gtk.cc +++ b/chrome/browser/gtk/options/languages_page_gtk.cc @@ -9,7 +9,6 @@ #include <vector> #include "app/gtk_signal.h" -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "base/command_line.h" #include "base/message_loop.h" diff --git a/chrome/browser/gtk/options/passwords_exceptions_page_gtk.cc b/chrome/browser/gtk/options/passwords_exceptions_page_gtk.cc index 7bb5909..d8f94b5 100644 --- a/chrome/browser/gtk/options/passwords_exceptions_page_gtk.cc +++ b/chrome/browser/gtk/options/passwords_exceptions_page_gtk.cc @@ -186,7 +186,10 @@ gint PasswordsExceptionsPageGtk::CompareSite(GtkTreeModel* model, void PasswordsExceptionsPageGtk::ExceptionListPopulater::populate() { DCHECK(!pending_login_query_); PasswordStore* store = page_->GetPasswordStore(); - pending_login_query_ = store->GetBlacklistLogins(this); + if (store != NULL) + pending_login_query_ = store->GetBlacklistLogins(this); + else + LOG(ERROR) << "No password store! Cannot display exceptions."; } void diff --git a/chrome/browser/gtk/options/passwords_page_gtk.cc b/chrome/browser/gtk/options/passwords_page_gtk.cc index 02f53b3..2051eb6 100644 --- a/chrome/browser/gtk/options/passwords_page_gtk.cc +++ b/chrome/browser/gtk/options/passwords_page_gtk.cc @@ -6,7 +6,6 @@ #include <string> -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "base/stl_util-inl.h" #include "base/utf_string_conversions.h" @@ -353,7 +352,10 @@ gint PasswordsPageGtk::CompareUsername(GtkTreeModel* model, void PasswordsPageGtk::PasswordListPopulater::populate() { DCHECK(!pending_login_query_); PasswordStore* store = page_->GetPasswordStore(); - pending_login_query_ = store->GetAutofillableLogins(this); + if (store != NULL) + pending_login_query_ = store->GetAutofillableLogins(this); + else + LOG(ERROR) << "No password store! Cannot display passwords."; } void PasswordsPageGtk::PasswordListPopulater::OnPasswordStoreRequestDone( diff --git a/chrome/browser/gtk/options/simple_content_exceptions_window.cc b/chrome/browser/gtk/options/simple_content_exceptions_window.cc index 68c211a..682cca1 100644 --- a/chrome/browser/gtk/options/simple_content_exceptions_window.cc +++ b/chrome/browser/gtk/options/simple_content_exceptions_window.cc @@ -153,6 +153,8 @@ SimpleContentExceptionsWindow::SimpleContentExceptionsWindow( g_signal_connect(dialog_, "destroy", G_CALLBACK(OnWindowDestroyThunk), this); } +SimpleContentExceptionsWindow::~SimpleContentExceptionsWindow() {} + void SimpleContentExceptionsWindow::SetColumnValues(int row, GtkTreeIter* iter) { std::wstring hostname = model_->GetText(row, IDS_EXCEPTIONS_HOSTNAME_HEADER); diff --git a/chrome/browser/gtk/options/simple_content_exceptions_window.h b/chrome/browser/gtk/options/simple_content_exceptions_window.h index c18dab1..2d3b068 100644 --- a/chrome/browser/gtk/options/simple_content_exceptions_window.h +++ b/chrome/browser/gtk/options/simple_content_exceptions_window.h @@ -23,6 +23,8 @@ class SimpleContentExceptionsWindow RemoveRowsTableModel* model, int tile_message_id); + virtual ~SimpleContentExceptionsWindow(); + // gtk_tree::TableAdapter::Delegate implementation: virtual void SetColumnValues(int row, GtkTreeIter* iter); virtual void OnAnyModelUpdateStart(); diff --git a/chrome/browser/gtk/options/url_picker_dialog_gtk.cc b/chrome/browser/gtk/options/url_picker_dialog_gtk.cc index 30059ba..e9cef31 100644 --- a/chrome/browser/gtk/options/url_picker_dialog_gtk.cc +++ b/chrome/browser/gtk/options/url_picker_dialog_gtk.cc @@ -4,7 +4,6 @@ #include <gtk/gtk.h> -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "base/message_loop.h" #include "base/utf_string_conversions.h" diff --git a/chrome/browser/gtk/page_info_bubble_gtk.cc b/chrome/browser/gtk/page_info_bubble_gtk.cc index fd9b7b6..cfb54de 100644 --- a/chrome/browser/gtk/page_info_bubble_gtk.cc +++ b/chrome/browser/gtk/page_info_bubble_gtk.cc @@ -23,7 +23,6 @@ #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" -#include "gfx/gtk_util.h" #include "googleurl/src/gurl.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" @@ -165,7 +164,7 @@ void PageInfoBubbleGtk::Observe(NotificationType type, } else { for (std::vector<GtkWidget*>::iterator it = labels_.begin(); it != labels_.end(); ++it) { - gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, &gfx::kGdkBlack); + gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, >k_util::kGdkBlack); } } } diff --git a/chrome/browser/gtk/process_singleton_dialog.cc b/chrome/browser/gtk/process_singleton_dialog.cc index cdd4eab..26fc1b4 100644 --- a/chrome/browser/gtk/process_singleton_dialog.cc +++ b/chrome/browser/gtk/process_singleton_dialog.cc @@ -4,9 +4,9 @@ #include "chrome/browser/gtk/process_singleton_dialog.h" -#include "app/gtk_util.h" #include "app/l10n_util.h" #include "base/message_loop.h" +#include "chrome/browser/gtk/gtk_util.h" #include "grit/chromium_strings.h" // static diff --git a/chrome/browser/gtk/reload_button_gtk.cc b/chrome/browser/gtk/reload_button_gtk.cc index e5e8857..70ae71e 100644 --- a/chrome/browser/gtk/reload_button_gtk.cc +++ b/chrome/browser/gtk/reload_button_gtk.cc @@ -6,7 +6,7 @@ #include "app/l10n_util.h" #include "base/logging.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/gtk/gtk_chrome_button.h" #include "chrome/browser/gtk/gtk_theme_provider.h" @@ -21,19 +21,23 @@ // doesn't change sizes when switching between the two. static int GtkButtonWidth = 0; +//////////////////////////////////////////////////////////////////////////////// +// ReloadButton, public: + ReloadButtonGtk::ReloadButtonGtk(LocationBarViewGtk* location_bar, Browser* browser) : location_bar_(location_bar), browser_(browser), - button_delay_(0), - pretend_timer_is_running_for_unittest_(false), intended_mode_(MODE_RELOAD), visible_mode_(MODE_RELOAD), theme_provider_(browser ? GtkThemeProvider::GetFrom(browser->profile()) : NULL), reload_(theme_provider_, IDR_RELOAD, IDR_RELOAD_P, IDR_RELOAD_H, 0), stop_(theme_provider_, IDR_STOP, IDR_STOP_P, IDR_STOP_H, IDR_STOP_D), - widget_(gtk_chrome_button_new()) { + widget_(gtk_chrome_button_new()), + stop_to_reload_timer_delay_(base::TimeDelta::FromMilliseconds(1350)), + testing_mouse_hovered_(false), + testing_reload_count_(0) { gtk_widget_set_size_request(widget(), reload_.Width(), reload_.Height()); gtk_widget_set_app_paintable(widget(), TRUE); @@ -57,6 +61,13 @@ ReloadButtonGtk::ReloadButtonGtk(LocationBarViewGtk* location_bar, NotificationType::BROWSER_THEME_CHANGED, Source<GtkThemeProvider>(theme_provider_)); } + + // Set the default double-click timer delay to the system double-click time. + int timer_delay_ms; + GtkSettings* settings = gtk_settings_get_default(); + g_object_get(G_OBJECT(settings), "gtk-double-click-time", &timer_delay_ms, + NULL); + double_click_timer_delay_ = base::TimeDelta::FromMilliseconds(timer_delay_ms); } ReloadButtonGtk::~ReloadButtonGtk() { @@ -69,10 +80,11 @@ void ReloadButtonGtk::ChangeMode(Mode mode, bool force) { // If the change is forced, or the user isn't hovering the icon, or it's safe // to change it to the other image type, make the change immediately; // otherwise we'll let it happen later. - if (force || GTK_WIDGET_STATE(widget()) == GTK_STATE_NORMAL || - ((mode == MODE_STOP) ? - !timer_running() : (visible_mode_ != MODE_STOP))) { - timer_.Stop(); + if (force || ((GTK_WIDGET_STATE(widget()) == GTK_STATE_NORMAL) && + !testing_mouse_hovered_) || ((mode == MODE_STOP) ? + !double_click_timer_.IsRunning() : (visible_mode_ != MODE_STOP))) { + double_click_timer_.Stop(); + stop_to_reload_timer_.Stop(); visible_mode_ = mode; stop_.set_paint_override(-1); @@ -99,12 +111,22 @@ void ReloadButtonGtk::ChangeMode(Mode mode, bool force) { // If we're in GTK theme mode, we need to also render the correct icon for // the stop/insensitive since we won't be using |stop_| to render the icon. UpdateThemeButtons(); + + // Go ahead and change to reload after a bit, which allows repeated reloads + // without moving the mouse. + if (!stop_to_reload_timer_.IsRunning()) { + stop_to_reload_timer_.Start(stop_to_reload_timer_delay_, this, + &ReloadButtonGtk::OnStopToReloadTimer); + } } } +//////////////////////////////////////////////////////////////////////////////// +// ReloadButtonGtk, NotificationObserver implementation: + void ReloadButtonGtk::Observe(NotificationType type, const NotificationSource& source, - const NotificationDetails& details) { + const NotificationDetails& /* details */) { DCHECK(NotificationType::BROWSER_THEME_CHANGED == type); GtkThemeProvider* provider = static_cast<GtkThemeProvider*>( @@ -114,14 +136,13 @@ void ReloadButtonGtk::Observe(NotificationType type, UpdateThemeButtons(); } -void ReloadButtonGtk::OnButtonTimer() { - ChangeMode(intended_mode_, false); -} +//////////////////////////////////////////////////////////////////////////////// +// ReloadButtonGtk, private: -void ReloadButtonGtk::OnClicked(GtkWidget* sender) { +void ReloadButtonGtk::OnClicked(GtkWidget* /* sender */) { if (visible_mode_ == MODE_STOP) { - // The stop button is disabled because the user hovered over the button - // until the stop action is no longer selectable. + // Do nothing if Stop was disabled due to an attempt to change back to + // RELOAD mode while hovered. if (stop_.paint_override() == GTK_STATE_INSENSITIVE) return; @@ -131,7 +152,7 @@ void ReloadButtonGtk::OnClicked(GtkWidget* sender) { // The user has clicked, so we can feel free to update the button, // even if the mouse is still hovering. ChangeMode(MODE_RELOAD, true); - } else if (!timer_running()) { + } else if (!double_click_timer_.IsRunning()) { // Shift-clicking or Ctrl-clicking the reload button means we should ignore // any cached content. int command; @@ -148,31 +169,24 @@ void ReloadButtonGtk::OnClicked(GtkWidget* sender) { WindowOpenDisposition disposition = event_utils::DispositionFromEventFlags(modifier_state_uint); - if (disposition == CURRENT_TAB) { + if ((disposition == CURRENT_TAB) && location_bar_) { // Forcibly reset the location bar, since otherwise it won't discard any // ongoing user edits, since it doesn't realize this is a user-initiated // action. location_bar_->Revert(); } - // Figure out the system double-click time. - if (button_delay_ == 0) { - GtkSettings* settings = gtk_settings_get_default(); - g_object_get(G_OBJECT(settings), "gtk-double-click-time", &button_delay_, - NULL); - } - // Start a timer - while this timer is running, the reload button cannot be // changed to a stop button. We do not set |intended_mode_| to MODE_STOP // here as the browser will do that when it actually starts loading (which // may happen synchronously, thus the need to do this before telling the // browser to execute the reload command). - timer_.Stop(); - timer_.Start(base::TimeDelta::FromMilliseconds(button_delay_), this, - &ReloadButtonGtk::OnButtonTimer); + double_click_timer_.Start(double_click_timer_delay_, this, + &ReloadButtonGtk::OnDoubleClickTimer); if (browser_) browser_->ExecuteCommandWithDisposition(command, disposition); + ++testing_reload_count_; } } @@ -184,16 +198,16 @@ gboolean ReloadButtonGtk::OnExpose(GtkWidget* widget, widget, e, hover_controller_.GetCurrentValue()); } -gboolean ReloadButtonGtk::OnLeaveNotify(GtkWidget* widget, - GdkEventCrossing* event) { +gboolean ReloadButtonGtk::OnLeaveNotify(GtkWidget* /* widget */, + GdkEventCrossing* /* event */) { ChangeMode(intended_mode_, true); return FALSE; } -gboolean ReloadButtonGtk::OnQueryTooltip(GtkWidget* sender, - gint x, - gint y, - gboolean keyboard_mode, +gboolean ReloadButtonGtk::OnQueryTooltip(GtkWidget* /* sender */, + gint /* x */, + gint /* y */, + gboolean /* keyboard_mode */, GtkTooltip* tooltip) { // |location_bar_| can be NULL in tests. if (!location_bar_) @@ -253,3 +267,11 @@ void ReloadButtonGtk::UpdateThemeButtons() { gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(widget()), use_gtk); } + +void ReloadButtonGtk::OnDoubleClickTimer() { + ChangeMode(intended_mode_, false); +} + +void ReloadButtonGtk::OnStopToReloadTimer() { + ChangeMode(intended_mode_, true); +} diff --git a/chrome/browser/gtk/reload_button_gtk.h b/chrome/browser/gtk/reload_button_gtk.h index 2a152a4..b641a13 100644 --- a/chrome/browser/gtk/reload_button_gtk.h +++ b/chrome/browser/gtk/reload_button_gtk.h @@ -37,48 +37,46 @@ class ReloadButtonGtk : public NotificationObserver { // Provide NotificationObserver implementation. virtual void Observe(NotificationType type, const NotificationSource& source, - const NotificationDetails& details); + const NotificationDetails& /* details */); private: - friend class ReloadButtonGtkPeer; + friend class ReloadButtonGtkTest; CHROMEGTK_CALLBACK_0(ReloadButtonGtk, void, OnClicked); CHROMEGTK_CALLBACK_1(ReloadButtonGtk, gboolean, OnExpose, GdkEventExpose*); - CHROMEGTK_CALLBACK_1(ReloadButtonGtk, gboolean, OnLeaveNotify, + CHROMEGTK_CALLBACK_1(ReloadButtonGtk, + gboolean, + OnLeaveNotify, GdkEventCrossing*); - CHROMEGTK_CALLBACK_4(ReloadButtonGtk, gboolean, OnQueryTooltip, gint, gint, - gboolean, GtkTooltip*); - - void SetToggled(); - - bool timer_running() const { - return timer_.IsRunning() || pretend_timer_is_running_for_unittest_; - } - - void OnButtonTimer(); + CHROMEGTK_CALLBACK_4(ReloadButtonGtk, + gboolean, + OnQueryTooltip, + gint, + gint, + gboolean, + GtkTooltip*); void UpdateThemeButtons(); - // Used to listen for theme change notifications. - NotificationRegistrar registrar_; + void OnDoubleClickTimer(); + void OnStopToReloadTimer(); - LocationBarViewGtk* const location_bar_; + base::OneShotTimer<ReloadButtonGtk> double_click_timer_; + base::OneShotTimer<ReloadButtonGtk> stop_to_reload_timer_; - // Keep a pointer to the Browser object to execute commands on it. + // These may be NULL when testing. + LocationBarViewGtk* const location_bar_; Browser* const browser_; - // Delay time to wait before allowing a mode change. This is to prevent a - // mode switch while the user is double clicking. - int button_delay_; - base::OneShotTimer<ReloadButtonGtk> timer_; - bool pretend_timer_is_running_for_unittest_; - - // The mode we should be in. + // The mode we should be in assuming no timers are running. Mode intended_mode_; - // The currently-visible mode - this may different from the intended mode. + // The currently-visible mode - this may differ from the intended mode. Mode visible_mode_; + // Used to listen for theme change notifications. + NotificationRegistrar registrar_; + GtkThemeProvider* theme_provider_; CustomDrawButtonBase reload_; @@ -87,6 +85,18 @@ class ReloadButtonGtk : public NotificationObserver { OwnedWidgetGtk widget_; + // The delay times for the timers. These are members so that tests can modify + // them. + base::TimeDelta double_click_timer_delay_; + base::TimeDelta stop_to_reload_timer_delay_; + + // TESTING ONLY + // True if we should pretend the button is hovered. + bool testing_mouse_hovered_; + // Increments when we would tell the browser to "reload", so + // test code can tell whether we did so (as there may be no |browser_|). + int testing_reload_count_; + DISALLOW_IMPLICIT_CONSTRUCTORS(ReloadButtonGtk); }; diff --git a/chrome/browser/gtk/reload_button_gtk_unittest.cc b/chrome/browser/gtk/reload_button_gtk_unittest.cc index 12384b6..fddb358 100644 --- a/chrome/browser/gtk/reload_button_gtk_unittest.cc +++ b/chrome/browser/gtk/reload_button_gtk_unittest.cc @@ -2,137 +2,144 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/message_loop.h" #include "chrome/browser/gtk/reload_button_gtk.h" #include "testing/gtest/include/gtest/gtest.h" -class ReloadButtonGtkPeer { +class ReloadButtonGtkTest : public testing::Test { public: - explicit ReloadButtonGtkPeer(ReloadButtonGtk* reload) : reload_(reload) { } - - // const accessors for internal state - ReloadButtonGtk::Mode intended_mode() const { - return reload_->intended_mode_; - } - ReloadButtonGtk::Mode visible_mode() const { return reload_->visible_mode_; } + ReloadButtonGtkTest(); - // mutators for internal state - void SetState(GtkStateType state) { - gtk_widget_set_state(reload_->widget(), state); - } - void set_intended_mode(ReloadButtonGtk::Mode mode) { - reload_->intended_mode_ = mode; - } - void set_visible_mode(ReloadButtonGtk::Mode mode) { - reload_->visible_mode_ = mode; - } - void set_timer_running() { - reload_->pretend_timer_is_running_for_unittest_ = true; - } - void set_timer_stopped() { - reload_->pretend_timer_is_running_for_unittest_ = false; - } + void CheckState(bool enabled, + ReloadButtonGtk::Mode intended_mode, + ReloadButtonGtk::Mode visible_mode, + bool double_click_timer_running, + bool stop_to_reload_timer_running); - // forwarders to private methods - gboolean OnLeave() { - return reload_->OnLeaveNotify(reload_->widget(), NULL); + // These accessors eliminate the need to declare each testcase as a friend. + void set_mouse_hovered(bool hovered) { + reload_.testing_mouse_hovered_ = hovered; } + int reload_count() { return reload_.testing_reload_count_; } + void fake_mouse_leave() { reload_.OnLeaveNotify(reload_.widget(), NULL); } - void OnClicked() { - reload_->OnClicked(reload_->widget()); - } - - private: - ReloadButtonGtk* const reload_; -}; - -namespace { - -class ReloadButtonGtkTest : public testing::Test { protected: - ReloadButtonGtkTest() : reload_(NULL, NULL), peer_(&reload_) { } + // We need a message loop for the timers to post events. + MessageLoop loop_; - protected: ReloadButtonGtk reload_; - ReloadButtonGtkPeer peer_; }; -TEST_F(ReloadButtonGtkTest, ChangeModeReload) { - reload_.ChangeMode(ReloadButtonGtk::MODE_RELOAD, true); - EXPECT_EQ(ReloadButtonGtk::MODE_RELOAD, peer_.intended_mode()); - EXPECT_EQ(ReloadButtonGtk::MODE_RELOAD, peer_.visible_mode()); +ReloadButtonGtkTest::ReloadButtonGtkTest() : reload_(NULL, NULL) { + // Set the timer delays to 0 so that timers will fire as soon as we tell the + // message loop to run pending tasks. + reload_.double_click_timer_delay_ = base::TimeDelta(); + reload_.stop_to_reload_timer_delay_ = base::TimeDelta(); } -TEST_F(ReloadButtonGtkTest, ChangeModeStop) { - reload_.ChangeMode(ReloadButtonGtk::MODE_STOP, true); - EXPECT_EQ(ReloadButtonGtk::MODE_STOP, peer_.intended_mode()); - EXPECT_EQ(ReloadButtonGtk::MODE_STOP, peer_.visible_mode()); +void ReloadButtonGtkTest::CheckState(bool enabled, + ReloadButtonGtk::Mode intended_mode, + ReloadButtonGtk::Mode visible_mode, + bool double_click_timer_running, + bool stop_to_reload_timer_running) { + EXPECT_NE(enabled, reload_.stop_.paint_override() == GTK_STATE_INSENSITIVE); + EXPECT_EQ(intended_mode, reload_.intended_mode_); + EXPECT_EQ(visible_mode, reload_.visible_mode_); + EXPECT_EQ(double_click_timer_running, + reload_.double_click_timer_.IsRunning()); + EXPECT_EQ(stop_to_reload_timer_running, + reload_.stop_to_reload_timer_.IsRunning()); } -TEST_F(ReloadButtonGtkTest, ScheduleChangeModeNormalReload) { - peer_.set_visible_mode(ReloadButtonGtk::MODE_STOP); - peer_.SetState(GTK_STATE_NORMAL); - reload_.ChangeMode(ReloadButtonGtk::MODE_RELOAD, false); - EXPECT_EQ(ReloadButtonGtk::MODE_RELOAD, peer_.intended_mode()); - EXPECT_EQ(ReloadButtonGtk::MODE_RELOAD, peer_.visible_mode()); -} +TEST_F(ReloadButtonGtkTest, Basic) { + // The stop/reload button starts in the "enabled reload" state with no timers + // running. + CheckState(true, ReloadButtonGtk::MODE_RELOAD, ReloadButtonGtk::MODE_RELOAD, + false, false); -TEST_F(ReloadButtonGtkTest, ScheduleChangeModeHotReload) { - peer_.set_visible_mode(ReloadButtonGtk::MODE_STOP); - peer_.SetState(GTK_STATE_PRELIGHT); - reload_.ChangeMode(ReloadButtonGtk::MODE_RELOAD, false); - EXPECT_EQ(ReloadButtonGtk::MODE_RELOAD, peer_.intended_mode()); - EXPECT_EQ(ReloadButtonGtk::MODE_STOP, peer_.visible_mode()); -} + // Press the button. This should start the double-click timer. + gtk_button_clicked(GTK_BUTTON(reload_.widget())); + CheckState(true, ReloadButtonGtk::MODE_RELOAD, ReloadButtonGtk::MODE_RELOAD, + true, false); -TEST_F(ReloadButtonGtkTest, ScheduleChangeModeNormalStop) { - peer_.set_visible_mode(ReloadButtonGtk::MODE_RELOAD); - peer_.SetState(GTK_STATE_NORMAL); + // Now change the mode (as if the browser had started loading the page). This + // should cancel the double-click timer since the button is not hovered. reload_.ChangeMode(ReloadButtonGtk::MODE_STOP, false); - EXPECT_EQ(ReloadButtonGtk::MODE_STOP, peer_.intended_mode()); - EXPECT_EQ(ReloadButtonGtk::MODE_STOP, peer_.visible_mode()); + CheckState(true, ReloadButtonGtk::MODE_STOP, ReloadButtonGtk::MODE_STOP, + false, false); + + // Press the button again. This should change back to reload. + gtk_button_clicked(GTK_BUTTON(reload_.widget())); + CheckState(true, ReloadButtonGtk::MODE_RELOAD, ReloadButtonGtk::MODE_RELOAD, + false, false); } -TEST_F(ReloadButtonGtkTest, ScheduleChangeModeHotStop) { - peer_.set_visible_mode(ReloadButtonGtk::MODE_RELOAD); - peer_.SetState(GTK_STATE_PRELIGHT); +TEST_F(ReloadButtonGtkTest, DoubleClickTimer) { + // Start by pressing the button. + gtk_button_clicked(GTK_BUTTON(reload_.widget())); + + // Try to press the button again. This should do nothing because the timer is + // running. + int original_reload_count = reload_count(); + gtk_button_clicked(GTK_BUTTON(reload_.widget())); + CheckState(true, ReloadButtonGtk::MODE_RELOAD, ReloadButtonGtk::MODE_RELOAD, + true, false); + EXPECT_EQ(original_reload_count, reload_count()); + + // Hover the button, and change mode. The visible mode should not change, + // again because the timer is running. + set_mouse_hovered(true); reload_.ChangeMode(ReloadButtonGtk::MODE_STOP, false); - EXPECT_EQ(ReloadButtonGtk::MODE_STOP, peer_.intended_mode()); - EXPECT_EQ(ReloadButtonGtk::MODE_STOP, peer_.visible_mode()); + CheckState(true, ReloadButtonGtk::MODE_STOP, ReloadButtonGtk::MODE_RELOAD, + true, false); + + // Now fire the timer. This should complete the mode change. + loop_.RunAllPending(); + CheckState(true, ReloadButtonGtk::MODE_STOP, ReloadButtonGtk::MODE_STOP, + false, false); } -TEST_F(ReloadButtonGtkTest, ScheduleChangeModeTimerHotStop) { - peer_.set_visible_mode(ReloadButtonGtk::MODE_RELOAD); - peer_.SetState(GTK_STATE_PRELIGHT); - peer_.set_timer_running(); +TEST_F(ReloadButtonGtkTest, DisableOnHover) { + // Change to stop and hover. + gtk_button_clicked(GTK_BUTTON(reload_.widget())); reload_.ChangeMode(ReloadButtonGtk::MODE_STOP, false); - peer_.set_timer_stopped(); - EXPECT_EQ(ReloadButtonGtk::MODE_STOP, peer_.intended_mode()); - EXPECT_EQ(ReloadButtonGtk::MODE_RELOAD, peer_.visible_mode()); -} + set_mouse_hovered(true); -TEST_F(ReloadButtonGtkTest, OnLeaveIntendedStop) { - peer_.SetState(GTK_STATE_PRELIGHT); - peer_.set_visible_mode(ReloadButtonGtk::MODE_RELOAD); - peer_.set_intended_mode(ReloadButtonGtk::MODE_STOP); - peer_.OnLeave(); - EXPECT_EQ(ReloadButtonGtk::MODE_STOP, peer_.visible_mode()); - EXPECT_EQ(ReloadButtonGtk::MODE_STOP, peer_.intended_mode()); + // Now change back to reload. This should result in a disabled stop button + // due to the hover. + reload_.ChangeMode(ReloadButtonGtk::MODE_RELOAD, false); + CheckState(false, ReloadButtonGtk::MODE_RELOAD, ReloadButtonGtk::MODE_STOP, + false, true); + + // Un-hover the button, which should allow it to reset. + set_mouse_hovered(false); + fake_mouse_leave(); + CheckState(true, ReloadButtonGtk::MODE_RELOAD, ReloadButtonGtk::MODE_RELOAD, + false, false); } -TEST_F(ReloadButtonGtkTest, OnLeaveIntendedReload) { - peer_.SetState(GTK_STATE_PRELIGHT); - peer_.set_visible_mode(ReloadButtonGtk::MODE_STOP); - peer_.set_intended_mode(ReloadButtonGtk::MODE_RELOAD); - peer_.OnLeave(); - EXPECT_EQ(ReloadButtonGtk::MODE_RELOAD, peer_.visible_mode()); - EXPECT_EQ(ReloadButtonGtk::MODE_RELOAD, peer_.intended_mode()); -} +TEST_F(ReloadButtonGtkTest, ResetOnClick) { + // Change to stop and hover. + gtk_button_clicked(GTK_BUTTON(reload_.widget())); + reload_.ChangeMode(ReloadButtonGtk::MODE_STOP, false); + set_mouse_hovered(true); -TEST_F(ReloadButtonGtkTest, OnClickedStop) { - peer_.set_visible_mode(ReloadButtonGtk::MODE_STOP); - peer_.OnClicked(); - EXPECT_EQ(ReloadButtonGtk::MODE_RELOAD, peer_.visible_mode()); - EXPECT_EQ(ReloadButtonGtk::MODE_RELOAD, peer_.intended_mode()); + // Press the button. This should change back to reload despite the hover, + // because it's a direct user action. + gtk_button_clicked(GTK_BUTTON(reload_.widget())); + CheckState(true, ReloadButtonGtk::MODE_RELOAD, ReloadButtonGtk::MODE_RELOAD, + false, false); } -} // namespace +TEST_F(ReloadButtonGtkTest, ResetOnTimer) { + // Change to stop, hover, and change back to reload. + gtk_button_clicked(GTK_BUTTON(reload_.widget())); + reload_.ChangeMode(ReloadButtonGtk::MODE_STOP, false); + set_mouse_hovered(true); + reload_.ChangeMode(ReloadButtonGtk::MODE_RELOAD, false); + + // Now fire the stop-to-reload timer. This should reset the button. + loop_.RunAllPending(); + CheckState(true, ReloadButtonGtk::MODE_RELOAD, ReloadButtonGtk::MODE_RELOAD, + false, false); +} diff --git a/chrome/browser/gtk/sad_tab_gtk.cc b/chrome/browser/gtk/sad_tab_gtk.cc index 982259b..569c53f 100644 --- a/chrome/browser/gtk/sad_tab_gtk.cc +++ b/chrome/browser/gtk/sad_tab_gtk.cc @@ -9,8 +9,8 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "chrome/browser/gtk/gtk_chrome_link_button.h" +#include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/tab_contents/tab_contents.h" -#include "gfx/gtk_util.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" #include "grit/theme_resources.h" @@ -34,7 +34,7 @@ GtkWidget* MakeWhiteMarkupLabel(const char* format, const std::string& str) { gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); // Set text to white. - GdkColor white = gfx::kGdkWhite; + GdkColor white = gtk_util::kGdkWhite; gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &white); return label; @@ -96,7 +96,7 @@ SadTabGtk::SadTabGtk(TabContents* tab_contents) GtkWidget* link = gtk_chrome_link_button_new( l10n_util::GetStringUTF8(IDS_LEARN_MORE).c_str()); gtk_chrome_link_button_set_normal_color(GTK_CHROME_LINK_BUTTON(link), - &gfx::kGdkWhite); + >k_util::kGdkWhite); g_signal_connect(link, "clicked", G_CALLBACK(OnLinkButtonClickThunk), this); GtkWidget* link_alignment = gtk_alignment_new(0.5, 0.5, 0.0, 0.0); gtk_container_add(GTK_CONTAINER(link_alignment), link); diff --git a/chrome/browser/gtk/status_bubble_gtk.cc b/chrome/browser/gtk/status_bubble_gtk.cc index bc6f9b3..fbb8989 100644 --- a/chrome/browser/gtk/status_bubble_gtk.cc +++ b/chrome/browser/gtk/status_bubble_gtk.cc @@ -17,7 +17,6 @@ #include "chrome/browser/gtk/rounded_window.h" #include "chrome/browser/gtk/slide_animator_gtk.h" #include "chrome/common/notification_service.h" -#include "gfx/gtk_util.h" namespace { @@ -106,8 +105,8 @@ void StatusBubbleGtk::SetStatusTextToURL() { // TODO(tc): We don't actually use gfx::Font as the font in the status // bubble. We should extend gfx::ElideUrl to take some sort of pango font. - url_text_ = WideToUTF8(gfx::ElideUrl(url_, gfx::Font(), desired_width, - UTF16ToWideHack(languages_))); + url_text_ = UTF16ToUTF8(gfx::ElideUrl(url_, gfx::Font(), desired_width, + UTF16ToWideHack(languages_))); SetStatusTextTo(url_text_); } @@ -251,7 +250,7 @@ void StatusBubbleGtk::InitWidgets() { container_.Own(gtk_event_box_new()); gtk_util::ActAsRoundedWindow( - container_.get(), gfx::kGdkWhite, kCornerSize, + container_.get(), gtk_util::kGdkWhite, kCornerSize, gtk_util::ROUNDED_TOP_RIGHT, gtk_util::BORDER_TOP | gtk_util::BORDER_RIGHT); gtk_widget_set_name(container_.get(), "status-bubble"); diff --git a/chrome/browser/gtk/tab_contents_container_gtk.cc b/chrome/browser/gtk/tab_contents_container_gtk.cc index 1f4a3cc..48ca9e2 100644 --- a/chrome/browser/gtk/tab_contents_container_gtk.cc +++ b/chrome/browser/gtk/tab_contents_container_gtk.cc @@ -15,6 +15,7 @@ TabContentsContainerGtk::TabContentsContainerGtk(StatusBubbleGtk* status_bubble) : tab_contents_(NULL), + preview_contents_(NULL), status_bubble_(status_bubble) { Init(); } @@ -57,54 +58,101 @@ void TabContentsContainerGtk::Init() { } void TabContentsContainerGtk::SetTabContents(TabContents* tab_contents) { + HideTabContents(tab_contents_); if (tab_contents_) { - gfx::NativeView widget = tab_contents_->GetNativeView(); - if (widget) - gtk_widget_hide(widget); - - tab_contents_->WasHidden(); - - registrar_.Remove(this, NotificationType::RENDER_VIEW_HOST_CHANGED, - Source<NavigationController>(&tab_contents_->controller())); registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, Source<TabContents>(tab_contents_)); } tab_contents_ = tab_contents; - // When detaching the last tab of the browser SetTabContents is invoked - // with NULL. Don't attempt to do anything in that case. - if (tab_contents_) { - // TabContents can change their RenderViewHost and hence the GtkWidget that - // is shown. I'm not entirely sure that we need to observe this event under - // GTK, but am putting a stub implementation and a comment saying that if - // we crash after that NOTIMPLEMENTED(), we'll need it. - registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CHANGED, - Source<NavigationController>(&tab_contents_->controller())); + if (tab_contents_ == preview_contents_) { + // If the preview contents is becoming the new permanent tab contents, we + // just reassign some pointers. + preview_contents_ = NULL; + } else if (tab_contents_) { + // Otherwise we actually have to add it to the widget hierarchy. + PackTabContents(tab_contents); registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, Source<TabContents>(tab_contents_)); + } +} + +void TabContentsContainerGtk::SetPreviewContents(TabContents* preview) { + if (preview_contents_) + RemovePreviewContents(); + else + HideTabContents(tab_contents_); + + preview_contents_ = preview; + + PackTabContents(preview); + registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(preview_contents_)); +} + +void TabContentsContainerGtk::RemovePreviewContents() { + if (!preview_contents_) + return; + + HideTabContents(preview_contents_); + + GtkWidget* preview_widget = preview_contents_->GetNativeView(); + if (preview_widget) + gtk_container_remove(GTK_CONTAINER(expanded_), preview_widget); + + registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(preview_contents_)); + preview_contents_ = NULL; +} + +void TabContentsContainerGtk::PopPreviewContents() { + if (!preview_contents_) + return; + + RemovePreviewContents(); - gfx::NativeView widget = tab_contents_->GetNativeView(); - if (widget) { - if (widget->parent != expanded_) - gtk_container_add(GTK_CONTAINER(expanded_), widget); - gtk_widget_show(widget); - } - - // We need to make sure that we are below the findbar. - // Sometimes the content native view will be null. - // TODO(estade): will this case ever cause findbar occlusion problems? - if (tab_contents_->GetContentNativeView()) { - GdkWindow* content_gdk_window = - tab_contents_->GetContentNativeView()->window; - if (content_gdk_window) - gdk_window_lower(content_gdk_window); - } + PackTabContents(tab_contents_); +} + +void TabContentsContainerGtk::PackTabContents(TabContents* contents) { + if (!contents) + return; + + gfx::NativeView widget = contents->GetNativeView(); + if (widget) { + if (widget->parent != expanded_) + gtk_container_add(GTK_CONTAINER(expanded_), widget); + gtk_widget_show(widget); + } + + // We need to make sure that we are below the findbar. + // Sometimes the content native view will be null. + if (contents->GetContentNativeView()) { + GdkWindow* content_gdk_window = + contents->GetContentNativeView()->window; + if (content_gdk_window) + gdk_window_lower(content_gdk_window); } + + contents->ShowContents(); +} + +void TabContentsContainerGtk::HideTabContents(TabContents* contents) { + if (!contents) + return; + + gfx::NativeView widget = contents->GetNativeView(); + if (widget) + gtk_widget_hide(widget); + + contents->WasHidden(); + } void TabContentsContainerGtk::DetachTabContents(TabContents* tab_contents) { gfx::NativeView widget = tab_contents->GetNativeView(); + // It is possible to detach an unrealized, unparented TabContents if you // slow things down enough in valgrind. Might happen in the real world, too. if (widget && widget->parent) { @@ -116,30 +164,20 @@ void TabContentsContainerGtk::DetachTabContents(TabContents* tab_contents) { void TabContentsContainerGtk::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - if (type == NotificationType::RENDER_VIEW_HOST_CHANGED) { - RenderViewHostSwitchedDetails* switched_details = - Details<RenderViewHostSwitchedDetails>(details).ptr(); - RenderViewHostChanged(switched_details->old_host, - switched_details->new_host); - } else if (type == NotificationType::TAB_CONTENTS_DESTROYED) { - TabContentsDestroyed(Source<TabContents>(source).ptr()); - } else { - NOTREACHED(); - } -} + DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); -void TabContentsContainerGtk::RenderViewHostChanged(RenderViewHost* old_host, - RenderViewHost* new_host) { - // TODO(port): Remove this method and the logic where we subscribe to the - // RENDER_VIEW_HOST_CHANGED notification. This was used on Windows for focus - // issues, and I'm not entirely convinced that this isn't necessary. + TabContentsDestroyed(Source<TabContents>(source).ptr()); } void TabContentsContainerGtk::TabContentsDestroyed(TabContents* contents) { // Sometimes, a TabContents is destroyed before we know about it. This allows // us to clean up our state in case this happens. - DCHECK(contents == tab_contents_); - SetTabContents(NULL); + if (contents == preview_contents_) + PopPreviewContents(); + else if (contents == tab_contents_) + SetTabContents(NULL); + else + NOTREACHED(); } // ----------------------------------------------------------------------------- diff --git a/chrome/browser/gtk/tab_contents_container_gtk.h b/chrome/browser/gtk/tab_contents_container_gtk.h index 0b3a959..ee28b0f 100644 --- a/chrome/browser/gtk/tab_contents_container_gtk.h +++ b/chrome/browser/gtk/tab_contents_container_gtk.h @@ -32,6 +32,9 @@ class TabContentsContainerGtk : public NotificationObserver, void SetTabContents(TabContents* tab_contents); TabContents* GetTabContents() const { return tab_contents_; } + void SetPreviewContents(TabContents* preview); + void PopPreviewContents(); + // Remove the tab from the hierarchy. void DetachTabContents(TabContents* tab_contents); @@ -46,15 +49,6 @@ class TabContentsContainerGtk : public NotificationObserver, virtual GtkWidget* GetWidgetForViewID(ViewID id); private: - // Add or remove observers for events that we care about. - void AddObservers(); - void RemoveObservers(); - - // Called when the RenderViewHost of the hosted TabContents has changed, e.g. - // to show an interstitial page. - void RenderViewHostChanged(RenderViewHost* old_host, - RenderViewHost* new_host); - // Called when a TabContents is destroyed. This gives us a chance to clean // up our internal state if the TabContents is somehow destroyed before we // get notified. @@ -66,11 +60,25 @@ class TabContentsContainerGtk : public NotificationObserver, GtkFloatingContainer* container, GtkAllocation* allocation, TabContentsContainerGtk* tab_contents_container); + // Add |contents| to the container and start showing it. + void PackTabContents(TabContents* contents); + + // Stop showing |contents|. + void HideTabContents(TabContents* contents); + + // Removes |preview_contents_|. + void RemovePreviewContents(); + NotificationRegistrar registrar_; - // The currently visible TabContents. + // The TabContents for the currently selected tab. This will be showing unless + // there is a preview contents. TabContents* tab_contents_; + // The current preview contents (for instant). If non-NULL, it will be + // visible. + TabContents* preview_contents_; + // The status bubble manager. Always non-NULL. StatusBubbleGtk* status_bubble_; diff --git a/chrome/browser/gtk/tab_contents_drag_source.cc b/chrome/browser/gtk/tab_contents_drag_source.cc index 393fc99..13e2196 100644 --- a/chrome/browser/gtk/tab_contents_drag_source.cc +++ b/chrome/browser/gtk/tab_contents_drag_source.cc @@ -34,7 +34,6 @@ TabContentsDragSource::TabContentsDragSource( drag_failed_(false), drag_widget_(gtk_invisible_new()), drag_icon_(gtk_window_new(GTK_WINDOW_POPUP)) { - g_object_ref(drag_widget_); signals_.Connect(drag_widget_, "drag-failed", G_CALLBACK(OnDragFailedThunk), this); signals_.Connect(drag_widget_, "drag-begin", @@ -45,7 +44,6 @@ TabContentsDragSource::TabContentsDragSource( signals_.Connect(drag_widget_, "drag-data-get", G_CALLBACK(OnDragDataGetThunk), this); - g_object_ref(drag_icon_); signals_.Connect(drag_icon_, "expose-event", G_CALLBACK(OnDragIconExposeThunk), this); } @@ -59,8 +57,8 @@ TabContentsDragSource::~TabContentsDragSource() { drop_data_.reset(); } - g_object_unref(drag_widget_); - g_object_unref(drag_icon_); + gtk_widget_destroy(drag_widget_); + gtk_widget_destroy(drag_icon_); } TabContents* TabContentsDragSource::tab_contents() const { diff --git a/chrome/browser/gtk/tab_contents_drag_source.h b/chrome/browser/gtk/tab_contents_drag_source.h index 7b4cc97..261c677 100644 --- a/chrome/browser/gtk/tab_contents_drag_source.h +++ b/chrome/browser/gtk/tab_contents_drag_source.h @@ -80,7 +80,8 @@ class TabContentsDragSource : public MessageLoopForUI::Observer { // This is the widget we use to initiate drags. Since we don't use the // renderer widget, we can persist drags even when our contents is switched - // out. + // out. We can't use an OwnedWidgetGtk because the GtkInvisible widget + // initialization code sinks the reference. GtkWidget* drag_widget_; // The file mime type for a drag-out download. @@ -92,7 +93,9 @@ class TabContentsDragSource : public MessageLoopForUI::Observer { // The URL to download from for a drag-out download. GURL download_url_; - // The widget that provides visual feedback for the drag. + // The widget that provides visual feedback for the drag. We can't use + // an OwnedWidgetGtk because the GtkWindow initialization code sinks + // the reference. GtkWidget* drag_icon_; GtkSignalRegistrar signals_; diff --git a/chrome/browser/gtk/tabs/tab_gtk.cc b/chrome/browser/gtk/tabs/tab_gtk.cc index f07b723..b59fa84 100644 --- a/chrome/browser/gtk/tabs/tab_gtk.cc +++ b/chrome/browser/gtk/tabs/tab_gtk.cc @@ -10,7 +10,7 @@ #include "app/menus/accelerator_gtk.h" #include "base/singleton.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/gtk/accelerators_gtk.h" #include "chrome/browser/gtk/menu_gtk.h" #include "chrome/browser/tab_menu_model.h" diff --git a/chrome/browser/gtk/tabs/tab_renderer_gtk.cc b/chrome/browser/gtk/tabs/tab_renderer_gtk.cc index 22bdcff..318fcbe 100644 --- a/chrome/browser/gtk/tabs/tab_renderer_gtk.cc +++ b/chrome/browser/gtk/tabs/tab_renderer_gtk.cc @@ -417,6 +417,19 @@ void TabRendererGtk::PaintFavIconArea(GdkEventExpose* event) { PaintIcon(&canvas); } +bool TabRendererGtk::ShouldShowIcon() const { + if (mini() && height() >= GetMinimumUnselectedSize().height()) { + return true; + } else if (!data_.show_icon) { + return false; + } else if (IsSelected()) { + // The selected tab clips favicon before close button. + return IconCapacity() >= 2; + } + // Non-selected tabs clip close button before favicon. + return IconCapacity() >= 1; +} + // static gfx::Size TabRendererGtk::GetMinimumUnselectedSize() { InitResources(); @@ -960,19 +973,6 @@ int TabRendererGtk::IconCapacity() const { return (width() - kLeftPadding - kRightPadding) / kFavIconSize; } -bool TabRendererGtk::ShouldShowIcon() const { - if (mini() && height() >= GetMinimumUnselectedSize().height()) { - return true; - } else if (!data_.show_icon) { - return false; - } else if (IsSelected()) { - // The selected tab clips favicon before close button. - return IconCapacity() >= 2; - } - // Non-selected tabs clip close button before favicon. - return IconCapacity() >= 1; -} - bool TabRendererGtk::ShouldShowCloseBox() const { // The selected tab never clips close button. return !mini() && (IsSelected() || IconCapacity() >= 3); diff --git a/chrome/browser/gtk/tabs/tab_renderer_gtk.h b/chrome/browser/gtk/tabs/tab_renderer_gtk.h index b25e001..51f11b7 100644 --- a/chrome/browser/gtk/tabs/tab_renderer_gtk.h +++ b/chrome/browser/gtk/tabs/tab_renderer_gtk.h @@ -171,6 +171,9 @@ class TabRendererGtk : public AnimationDelegate, // Repaint only the area of the tab that contains the favicon. void PaintFavIconArea(GdkEventExpose* event); + // Returns whether the Tab should display a favicon. + bool ShouldShowIcon() const; + // Returns the minimum possible size of a single unselected Tab. static gfx::Size GetMinimumUnselectedSize(); // Returns the minimum possible size of a selected Tab. Selected tabs must @@ -340,8 +343,6 @@ class TabRendererGtk : public AnimationDelegate, // current size. int IconCapacity() const; - // Returns whether the Tab should display a favicon. - bool ShouldShowIcon() const; // Returns whether the Tab should display a close button. bool ShouldShowCloseBox() const; diff --git a/chrome/browser/gtk/tabs/tab_strip_gtk.cc b/chrome/browser/gtk/tabs/tab_strip_gtk.cc index 3d026b1..0547815 100644 --- a/chrome/browser/gtk/tabs/tab_strip_gtk.cc +++ b/chrome/browser/gtk/tabs/tab_strip_gtk.cc @@ -1050,6 +1050,10 @@ void TabStripGtk::TabReplacedAt(TabContents* old_contents, } void TabStripGtk::TabMiniStateChanged(TabContents* contents, int index) { + // Don't do anything if we've already picked up the change from TabMoved. + if (GetTabAt(index)->mini() == model_->IsMiniTab(index)) + return; + GetTabAt(index)->set_mini(model_->IsMiniTab(index)); // Don't animate if the window isn't visible yet. The window won't be visible // when dragging a mini-tab to a new window. @@ -2007,7 +2011,8 @@ bool TabStripGtk::CanPaintOnlyFavIcons(const GdkRectangle* rects, for (int r = 0; r < num_rects; ++r) { while (t < GetTabCount()) { TabGtk* tab = GetTabAt(t); - if (GdkRectMatchesTabFavIconBounds(rects[r], tab)) { + if (GdkRectMatchesTabFavIconBounds(rects[r], tab) && + tab->ShouldShowIcon()) { tabs_to_paint->push_back(t); ++t; break; diff --git a/chrome/browser/gtk/theme_install_bubble_view_gtk.cc b/chrome/browser/gtk/theme_install_bubble_view_gtk.cc index 651ab29..4708211 100644 --- a/chrome/browser/gtk/theme_install_bubble_view_gtk.cc +++ b/chrome/browser/gtk/theme_install_bubble_view_gtk.cc @@ -11,7 +11,6 @@ #include "chrome/browser/gtk/rounded_window.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" -#include "gfx/gtk_util.h" #include "grit/generated_resources.h" // Roundedness of bubble. @@ -94,7 +93,7 @@ void ThemeInstallBubbleViewGtk::InitWidgets() { gtk_label_set_markup(GTK_LABEL(label), markup); g_free(markup); - gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gfx::kGdkWhite); + gtk_widget_modify_fg(label, GTK_STATE_NORMAL, >k_util::kGdkWhite); gtk_container_add(GTK_CONTAINER(widget_), label); // We need to show the label so we'll know the widget's actual size when we @@ -119,7 +118,7 @@ void ThemeInstallBubbleViewGtk::InitWidgets() { G_CALLBACK(HandleExposeEventThunk), this); gtk_widget_realize(widget_); } else { - gtk_widget_modify_bg(widget_, GTK_STATE_NORMAL, &gfx::kGdkBlack); + gtk_widget_modify_bg(widget_, GTK_STATE_NORMAL, >k_util::kGdkBlack); GdkColor color; gtk_util::ActAsRoundedWindow(widget_, color, kBubbleCornerRadius, gtk_util::ROUNDED_ALL, gtk_util::BORDER_NONE); diff --git a/chrome/browser/gtk/translate/after_translate_infobar_gtk.cc b/chrome/browser/gtk/translate/after_translate_infobar_gtk.cc index 6fefadd..914f2ca 100644 --- a/chrome/browser/gtk/translate/after_translate_infobar_gtk.cc +++ b/chrome/browser/gtk/translate/after_translate_infobar_gtk.cc @@ -62,8 +62,6 @@ void AfterTranslateInfoBar::Init() { l10n_util::GetStringUTF8(IDS_TRANSLATE_INFOBAR_REVERT).c_str()); g_signal_connect(button, "clicked",G_CALLBACK(&OnRevertPressedThunk), this); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); - - gtk_widget_show_all(border_bin_.get()); } void AfterTranslateInfoBar::OnOriginalLanguageModified(GtkWidget* sender) { diff --git a/chrome/browser/gtk/translate/before_translate_infobar_gtk.cc b/chrome/browser/gtk/translate/before_translate_infobar_gtk.cc index c3a9b8a..75c668e 100644 --- a/chrome/browser/gtk/translate/before_translate_infobar_gtk.cc +++ b/chrome/browser/gtk/translate/before_translate_infobar_gtk.cc @@ -73,8 +73,6 @@ void BeforeTranslateInfoBar::Init() { G_CALLBACK(&OnAlwaysTranslatePressedThunk), this); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); } - - gtk_widget_show_all(border_bin_.get()); } void BeforeTranslateInfoBar::OnLanguageModified(GtkWidget* sender) { diff --git a/chrome/browser/gtk/translate/translate_infobar_base_gtk.cc b/chrome/browser/gtk/translate/translate_infobar_base_gtk.cc index e7a94b8..530d215 100644 --- a/chrome/browser/gtk/translate/translate_infobar_base_gtk.cc +++ b/chrome/browser/gtk/translate/translate_infobar_base_gtk.cc @@ -15,7 +15,6 @@ #include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/gtk/menu_gtk.h" #include "gfx/canvas.h" -#include "gfx/gtk_util.h" #include "grit/generated_resources.h" namespace { @@ -127,7 +126,7 @@ void TranslateInfoBarBase::AnimationProgressed(const Animation* animation) { GtkWidget* TranslateInfoBarBase::CreateLabel(const std::string& text) { GtkWidget* label = gtk_label_new(text.c_str()); - gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gfx::kGdkBlack); + gtk_widget_modify_fg(label, GTK_STATE_NORMAL, >k_util::kGdkBlack); return label; } diff --git a/chrome/browser/gtk/translate/translate_message_infobar_gtk.cc b/chrome/browser/gtk/translate/translate_message_infobar_gtk.cc index 22e800d..0f61bca 100644 --- a/chrome/browser/gtk/translate/translate_message_infobar_gtk.cc +++ b/chrome/browser/gtk/translate/translate_message_infobar_gtk.cc @@ -31,8 +31,6 @@ void TranslateMessageInfoBar::Init() { g_signal_connect(button, "clicked",G_CALLBACK(&OnButtonPressedThunk), this); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); } - - gtk_widget_show_all(border_bin_.get()); } void TranslateMessageInfoBar::OnButtonPressed(GtkWidget* sender) { diff --git a/chrome/browser/hang_monitor/hung_plugin_action.cc b/chrome/browser/hang_monitor/hung_plugin_action.cc index 5fdcfd1..8399464 100644 --- a/chrome/browser/hang_monitor/hung_plugin_action.cc +++ b/chrome/browser/hang_monitor/hung_plugin_action.cc @@ -80,6 +80,7 @@ bool HungPluginAction::OnHungWindowDetected(HWND hung_window, if (child_window_message_timeout) { child_window_message_timeout *= 2; #pragma warning(disable:4312) + // TODO: this leaks. SetProp(hung_window, HungWindowDetector::kHungChildWindowTimeout, reinterpret_cast<HANDLE>(child_window_message_timeout)); #pragma warning(default:4312) diff --git a/chrome/browser/hang_monitor/hung_window_detector.cc b/chrome/browser/hang_monitor/hung_window_detector.cc index 73b84a8..a9bdead 100644 --- a/chrome/browser/hang_monitor/hung_window_detector.cc +++ b/chrome/browser/hang_monitor/hung_window_detector.cc @@ -101,6 +101,7 @@ bool HungWindowDetector::CheckChildWindow(HWND child_window) { HungWindowNotification::ActionOnHungWindow action = HungWindowNotification::HUNG_WINDOW_IGNORE; #pragma warning(disable:4312) + // TODO: this leaks. SetProp(child_window, kHungChildWindowTimeout, reinterpret_cast<HANDLE>(child_window_message_timeout)); #pragma warning(default:4312) diff --git a/chrome/browser/history/archived_database.cc b/chrome/browser/history/archived_database.cc index 72fa998..08a742f 100644 --- a/chrome/browser/history/archived_database.cc +++ b/chrome/browser/history/archived_database.cc @@ -62,6 +62,7 @@ bool ArchivedDatabase::Init(const FilePath& file_name) { return false; } CreateMainURLIndex(); + CreateKeywordSearchTermsIndices(); if (EnsureCurrentVersion() != sql::INIT_OK) { db_.Close(); diff --git a/chrome/browser/history/expire_history_backend_unittest.cc b/chrome/browser/history/expire_history_backend_unittest.cc index 91129ed..3177dde 100644 --- a/chrome/browser/history/expire_history_backend_unittest.cc +++ b/chrome/browser/history/expire_history_backend_unittest.cc @@ -8,6 +8,7 @@ #include "base/file_util.h" #include "base/path_service.h" #include "base/scoped_ptr.h" +#include "base/scoped_temp_dir.h" #include "base/string16.h" #include "base/utf_string_conversions.h" #include "chrome/browser/bookmarks/bookmark_model.h" @@ -31,7 +32,6 @@ using base::TimeDelta; using base::TimeTicks; // Filename constants. -static const FilePath::CharType kTestDir[] = FILE_PATH_LITERAL("ExpireTest"); static const FilePath::CharType kHistoryFile[] = FILE_PATH_LITERAL("History"); static const FilePath::CharType kArchivedHistoryFile[] = FILE_PATH_LITERAL("Archived History"); @@ -49,6 +49,8 @@ class ExpireHistoryTest : public testing::Test, public: ExpireHistoryTest() : bookmark_model_(NULL), + ui_thread_(BrowserThread::UI, &message_loop_), + db_thread_(BrowserThread::DB, &message_loop_), ALLOW_THIS_IN_INITIALIZER_LIST(expirer_(this, &bookmark_model_)), now_(Time::Now()) { } @@ -85,9 +87,17 @@ class ExpireHistoryTest : public testing::Test, static bool IsStringInFile(const FilePath& filename, const char* str); + // Returns the path the db files are created in. + const FilePath& path() const { return tmp_dir_.path(); } + + // This must be destroyed last. + ScopedTempDir tmp_dir_; + BookmarkModel bookmark_model_; - MessageLoop message_loop_; + MessageLoopForUI message_loop_; + BrowserThread ui_thread_; + BrowserThread db_thread_; ExpireHistoryBackend expirer_; @@ -108,43 +118,40 @@ class ExpireHistoryTest : public testing::Test, NotificationList; NotificationList notifications_; - // Directory for the history files. - FilePath dir_; - private: void SetUp() { - FilePath temp_dir; - PathService::Get(base::DIR_TEMP, &temp_dir); - dir_ = temp_dir.Append(kTestDir); - file_util::Delete(dir_, true); - file_util::CreateDirectory(dir_); + ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir()); - FilePath history_name = dir_.Append(kHistoryFile); + FilePath history_name = path().Append(kHistoryFile); main_db_.reset(new HistoryDatabase); if (main_db_->Init(history_name, FilePath()) != sql::INIT_OK) main_db_.reset(); - FilePath archived_name = dir_.Append(kArchivedHistoryFile); + FilePath archived_name = path().Append(kArchivedHistoryFile); archived_db_.reset(new ArchivedDatabase); if (!archived_db_->Init(archived_name)) archived_db_.reset(); - FilePath thumb_name = dir_.Append(kThumbnailFile); + FilePath thumb_name = path().Append(kThumbnailFile); thumb_db_.reset(new ThumbnailDatabase); if (thumb_db_->Init(thumb_name, NULL) != sql::INIT_OK) thumb_db_.reset(); - text_db_.reset(new TextDatabaseManager(dir_, + text_db_.reset(new TextDatabaseManager(path(), main_db_.get(), main_db_.get())); if (!text_db_->Init(NULL)) text_db_.reset(); expirer_.SetDatabases(main_db_.get(), archived_db_.get(), thumb_db_.get(), text_db_.get()); + profile_.CreateTopSites(); + profile_.BlockUntilTopSitesLoaded(); top_sites_ = profile_.GetTopSites(); } void TearDown() { + top_sites_ = NULL; + ClearLastNotifications(); expirer_.SetDatabases(NULL, NULL, NULL, NULL); @@ -153,8 +160,6 @@ class ExpireHistoryTest : public testing::Test, archived_db_.reset(); thumb_db_.reset(); text_db_.reset(); - TopSites::DeleteTopSites(top_sites_); - file_util::Delete(dir_, true); } // BroadcastNotificationDelegate implementation. @@ -309,11 +314,13 @@ bool ExpireHistoryTest::HasFavIcon(FavIconID favicon_id) { } bool ExpireHistoryTest::HasThumbnail(URLID url_id) { + // TODO(sky): fix this. This test isn't really valid for TopSites. For + // TopSites we should be checking URL always, not the id. URLRow info; if (!main_db_->GetURLRow(url_id, &info)) return false; GURL url = info.url(); - RefCountedBytes *data; + scoped_refptr<RefCountedBytes> data; return top_sites_->GetPageThumbnail(url, &data); } @@ -350,7 +357,8 @@ void ExpireHistoryTest::EnsureURLInfoGone(const URLRow& row) { EXPECT_EQ(0U, visits.size()); // Thumbnail should be gone. - EXPECT_FALSE(HasThumbnail(row.id())); + // TODO(sky): fix this, see comment in HasThumbnail. + // EXPECT_FALSE(HasThumbnail(row.id())); // Check the notifications. There should be a delete notification with this // URL in it. There should also be a "typed URL changed" notification if the @@ -439,7 +447,8 @@ TEST_F(ExpireHistoryTest, FLAKY_DeleteURLAndFavicon) { URLRow last_row; ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &last_row)); EXPECT_TRUE(HasFavIcon(last_row.favicon_id())); - EXPECT_TRUE(HasThumbnail(url_ids[2])); + // TODO(sky): fix this, see comment in HasThumbnail. + // EXPECT_TRUE(HasThumbnail(url_ids[2])); VisitVector visits; main_db_->GetVisitsForURL(url_ids[2], &visits); @@ -452,14 +461,14 @@ TEST_F(ExpireHistoryTest, FLAKY_DeleteURLAndFavicon) { visits[0].visit_time); // Compute the text DB filename. - FilePath fts_filename = dir_.Append( + FilePath fts_filename = path().Append( TextDatabase::IDToFileName(text_db_->TimeToID(visit_times[3]))); // When checking the file, the database must be closed. We then re-initialize // it just like the test set-up did. text_db_.reset(); EXPECT_TRUE(IsStringInFile(fts_filename, "goats")); - text_db_.reset(new TextDatabaseManager(dir_, + text_db_.reset(new TextDatabaseManager(path(), main_db_.get(), main_db_.get())); ASSERT_TRUE(text_db_->Init(NULL)); expirer_.SetDatabases(main_db_.get(), archived_db_.get(), thumb_db_.get(), @@ -472,7 +481,7 @@ TEST_F(ExpireHistoryTest, FLAKY_DeleteURLAndFavicon) { // doesn't remove it from the file, we want to be sure we're doing the latter. text_db_.reset(); EXPECT_FALSE(IsStringInFile(fts_filename, "goats")); - text_db_.reset(new TextDatabaseManager(dir_, + text_db_.reset(new TextDatabaseManager(path(), main_db_.get(), main_db_.get())); ASSERT_TRUE(text_db_->Init(NULL)); expirer_.SetDatabases(main_db_.get(), archived_db_.get(), thumb_db_.get(), @@ -500,7 +509,8 @@ TEST_F(ExpireHistoryTest, DeleteURLWithoutFavicon) { URLRow last_row; ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &last_row)); EXPECT_TRUE(HasFavIcon(last_row.favicon_id())); - EXPECT_TRUE(HasThumbnail(url_ids[1])); + // TODO(sky): fix this, see comment in HasThumbnail. + // EXPECT_TRUE(HasThumbnail(url_ids[1])); VisitVector visits; main_db_->GetVisitsForURL(url_ids[1], &visits); @@ -546,7 +556,8 @@ TEST_F(ExpireHistoryTest, DontDeleteStarredURL) { ASSERT_EQ(0U, visits.size()); // Should still have the thumbnail. - ASSERT_TRUE(HasThumbnail(url_row.id())); + // TODO(sky): fix this, see comment in HasThumbnail. + // ASSERT_TRUE(HasThumbnail(url_row.id())); // Unstar the URL and delete again. bookmark_model_.SetURLStarred(url, string16(), false); @@ -604,7 +615,8 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarred) { // Verify that the middle URL's favicon and thumbnail is still there. EXPECT_TRUE(HasFavIcon(url_row1.favicon_id())); - EXPECT_TRUE(HasThumbnail(url_row1.id())); + // TODO(sky): fix this, see comment in HasThumbnail. + // EXPECT_TRUE(HasThumbnail(url_row1.id())); // Verify that the last URL was deleted. EnsureURLInfoGone(url_row2); @@ -660,12 +672,14 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted) { // Verify that the middle URL's favicon and thumbnail is still there. EXPECT_TRUE(HasFavIcon(url_row1.favicon_id())); - EXPECT_TRUE(HasThumbnail(url_row1.id())); + // TODO(sky): fix this, see comment in HasThumbnail. + // EXPECT_TRUE(HasThumbnail(url_row1.id())); // Verify that the last URL was not touched. EXPECT_TRUE(main_db_->GetURLRow(url_ids[2], &temp_row)); EXPECT_TRUE(HasFavIcon(url_row2.favicon_id())); - EXPECT_TRUE(HasThumbnail(url_row2.id())); + // TODO(sky): fix this, see comment in HasThumbnail. + // EXPECT_TRUE(HasThumbnail(url_row2.id())); } // Expire a starred URL, it shouldn't get deleted @@ -706,9 +720,11 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) { // exists in history, this should not be a privacy problem, we only update // the visit counts in this case for consistency anyway. EXPECT_TRUE(HasFavIcon(new_url_row1.favicon_id())); - EXPECT_TRUE(HasThumbnail(new_url_row1.id())); + // TODO(sky): fix this, see comment in HasThumbnail. + // EXPECT_TRUE(HasThumbnail(new_url_row1.id())); EXPECT_TRUE(HasFavIcon(new_url_row2.favicon_id())); - EXPECT_TRUE(HasThumbnail(new_url_row2.id())); + // TODO(sky): fix this, see comment in HasThumbnail. + // EXPECT_TRUE(HasThumbnail(new_url_row2.id())); } TEST_F(ExpireHistoryTest, ArchiveHistoryBeforeUnstarred) { diff --git a/chrome/browser/history/history.cc b/chrome/browser/history/history.cc index bc5db7d..53a5420 100644 --- a/chrome/browser/history/history.cc +++ b/chrome/browser/history/history.cc @@ -127,12 +127,8 @@ HistoryService::HistoryService() profile_(NULL), backend_loaded_(false), bookmark_service_(NULL), - no_db_(false) { - // Is NULL when running generate_profile. - if (NotificationService::current()) { - registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, - Source<Profile>(profile_)); - } + no_db_(false), + needs_top_sites_migration_(false) { } HistoryService::HistoryService(Profile* profile) @@ -140,9 +136,13 @@ HistoryService::HistoryService(Profile* profile) profile_(profile), backend_loaded_(false), bookmark_service_(NULL), - no_db_(false) { + no_db_(false), + needs_top_sites_migration_(false) { + DCHECK(profile_); registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, Source<Profile>(profile_)); + registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED, + Source<Profile>(profile_)); } HistoryService::~HistoryService() { @@ -609,30 +609,40 @@ HistoryService::Handle HistoryService::QueryMostVisitedURLs( void HistoryService::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - if (type != NotificationType::HISTORY_URLS_DELETED) { - NOTREACHED(); + if (!thread_) return; - } - // Update the visited link system for deleted URLs. We will update the - // visited link system for added URLs as soon as we get the add - // notification (we don't have to wait for the backend, which allows us to - // be faster to update the state). - // - // For deleted URLs, we don't typically know what will be deleted since - // delete notifications are by time. We would also like to be more - // respectful of privacy and never tell the user something is gone when it - // isn't. Therefore, we update the delete URLs after the fact. - if (!profile_) - return; // No profile, probably unit testing. - Details<history::URLsDeletedDetails> deleted_details(details); - VisitedLinkMaster* visited_links = profile_->GetVisitedLinkMaster(); - if (!visited_links) - return; // Nobody to update. - if (deleted_details->all_history) - visited_links->DeleteAllURLs(); - else // Delete individual ones. - visited_links->DeleteURLs(deleted_details->urls); + switch (type.value) { + case NotificationType::HISTORY_URLS_DELETED: { + // Update the visited link system for deleted URLs. We will update the + // visited link system for added URLs as soon as we get the add + // notification (we don't have to wait for the backend, which allows us to + // be faster to update the state). + // + // For deleted URLs, we don't typically know what will be deleted since + // delete notifications are by time. We would also like to be more + // respectful of privacy and never tell the user something is gone when it + // isn't. Therefore, we update the delete URLs after the fact. + if (!profile_) + return; // No profile, probably unit testing. + Details<history::URLsDeletedDetails> deleted_details(details); + VisitedLinkMaster* visited_links = profile_->GetVisitedLinkMaster(); + if (!visited_links) + return; // Nobody to update. + if (deleted_details->all_history) + visited_links->DeleteAllURLs(); + else // Delete individual ones. + visited_links->DeleteURLs(deleted_details->urls); + break; + } + + case NotificationType::TEMPLATE_URL_REMOVED: + DeleteAllSearchTermsForKeyword(*(Details<TemplateURLID>(details).ptr())); + break; + + default: + NOTREACHED(); + } } bool HistoryService::Init(const FilePath& history_dir, @@ -673,6 +683,7 @@ bool HistoryService::CanAddURL(const GURL& url) { // typed. Right now, however, these are marked as typed even when triggered // by a shortcut or menu action. if (url.SchemeIs(chrome::kJavaScriptScheme) || + url.SchemeIs(chrome::kChromeDevToolsScheme) || url.SchemeIs(chrome::kChromeUIScheme) || url.SchemeIs(chrome::kViewSourceScheme) || url.SchemeIs(chrome::kChromeInternalScheme)) @@ -733,6 +744,9 @@ void HistoryService::BroadcastNotifications( if (!g_browser_process) return; + if (!thread_) + return; + // The source of all of our notifications is the profile. Note that this // pointer is NULL in unit tests. Source<Profile> source(profile_); @@ -769,12 +783,21 @@ void HistoryService::OnDBLoaded() { NotificationService::current()->Notify(NotificationType::HISTORY_LOADED, Source<Profile>(profile_), Details<HistoryService>(this)); + if (thread_ && profile_ && history::TopSites::IsEnabled()) { + // We don't want to force creation of TopSites. + history::TopSites* ts = profile_->GetTopSitesWithoutCreating(); + if (ts) + ts->HistoryLoaded(); + } } void HistoryService::StartTopSitesMigration() { - if (history::TopSites::IsEnabled()) { - history::TopSites* ts = profile_->GetTopSites(); - ts->StartMigration(); + needs_top_sites_migration_ = true; + if (thread_ && profile_ && history::TopSites::IsEnabled()) { + // We don't want to force creation of TopSites. + history::TopSites* ts = profile_->GetTopSitesWithoutCreating(); + if (ts) + ts->MigrateFromHistory(); } } diff --git a/chrome/browser/history/history.h b/chrome/browser/history/history.h index 83e629e..9d2fd20 100644 --- a/chrome/browser/history/history.h +++ b/chrome/browser/history/history.h @@ -118,6 +118,9 @@ class HistoryService : public CancelableRequestProvider, // it's finished loading. bool BackendLoaded(); + // Returns true if the backend has finished loading. + bool backend_loaded() const { return backend_loaded_; } + // Unloads the backend without actually shutting down the history service. // This can be used to temporarily reduce the browser process' memory // footprint. @@ -405,7 +408,8 @@ class HistoryService : public CancelableRequestProvider, // Implemented by the caller of 'CreateDownload' below, and is called when the // history service has created a new entry for a download in the history db. - typedef Callback2<DownloadCreateInfo, int64>::Type DownloadCreateCallback; + typedef Callback2<const DownloadCreateInfo&, int64>::Type + DownloadCreateCallback; // Begins a history request to create a new persistent entry for a download. // 'info' contains all the download's creation state, and 'callback' runs @@ -515,6 +519,10 @@ class HistoryService : public CancelableRequestProvider, virtual Handle ScheduleDBTask(HistoryDBTask* task, CancelableRequestConsumerBase* consumer); + // Returns true if top sites needs to be migrated out of history into its own + // db. + bool needs_top_sites_migration() const { return needs_top_sites_migration_; } + // Testing ------------------------------------------------------------------- // Designed for unit tests, this passes the given task on to the history @@ -819,7 +827,8 @@ class HistoryService : public CancelableRequestProvider, // when done. We use this internal consumer for this purpose. CancelableRequestConsumer internal_consumer_; - // The thread used by the history service to run complicated operations + // The thread used by the history service to run complicated operations. + // |thread_| is NULL once |Cleanup| is NULL. base::Thread* thread_; // This class has most of the implementation and runs on the 'thread_'. @@ -847,6 +856,9 @@ class HistoryService : public CancelableRequestProvider, BookmarkService* bookmark_service_; bool no_db_; + // True if needs top site migration. + bool needs_top_sites_migration_; + DISALLOW_COPY_AND_ASSIGN(HistoryService); }; diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc index fb8e3d3..0bf6045 100644 --- a/chrome/browser/history/history_backend.cc +++ b/chrome/browser/history/history_backend.cc @@ -581,12 +581,9 @@ void HistoryBackend::InitImpl(const std::string& languages) { // Thumbnail database. thumbnail_db_.reset(new ThumbnailDatabase()); - if (history::TopSites::IsEnabled()) { - // TODO(sky): once we reenable top sites this needs to be fixed. - // if (!db_->needs_version_18_migration()) { + if (history::TopSites::IsEnabled() && !db_->GetNeedsThumbnailMigration()) { // No convertion needed - use new filename right away. - // thumbnail_name = GetFaviconsFileName(); - // } + thumbnail_name = GetFaviconsFileName(); } if (thumbnail_db_->Init(thumbnail_name, history_publisher_.get()) != sql::INIT_OK) { @@ -599,12 +596,9 @@ void HistoryBackend::InitImpl(const std::string& languages) { thumbnail_db_.reset(); } - if (history::TopSites::IsEnabled()) { - // TODO(sky): fix when reenabling top sites migration. - // if (db_->needs_version_18_migration()) { - // VLOG(1) << "Starting TopSites migration"; - // delegate_->StartTopSitesMigration(); - // } + if (history::TopSites::IsEnabled() && db_->GetNeedsThumbnailMigration()) { + VLOG(1) << "Starting TopSites migration"; + delegate_->StartTopSitesMigration(); } // Archived database. @@ -1041,6 +1035,14 @@ void HistoryBackend::SetKeywordSearchTermsForURL(const GURL& url, } db_->SetKeywordSearchTermsForURL(url_row.id(), keyword_id, term); + + // details is deleted by BroadcastNotifications. + KeywordSearchTermDetails* details = new KeywordSearchTermDetails; + details->url = url; + details->keyword_id = keyword_id; + details->term = term; + BroadcastNotifications(NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED, + details); ScheduleCommit(); } @@ -1341,6 +1343,16 @@ void HistoryBackend::QueryMostVisitedURLs( } MostVisitedURLList* result = &request->value; + QueryMostVisitedURLsImpl(result_count, days_back, result); + request->ForwardResult(QueryMostVisitedURLsRequest::TupleType( + request->handle(), *result)); +} + +void HistoryBackend::QueryMostVisitedURLsImpl(int result_count, + int days_back, + MostVisitedURLList* result) { + if (!db_.get()) + return; ScopedVector<PageUsageData> data; db_->QuerySegmentUsage(base::Time::Now() - @@ -1354,9 +1366,6 @@ void HistoryBackend::QueryMostVisitedURLs( MostVisitedURL url = MakeMostVisitedURL(*current_data, redirects); result->push_back(url); } - - request->ForwardResult(QueryMostVisitedURLsRequest::TupleType( - request->handle(), *result)); } void HistoryBackend::GetRedirectsFromSpecificVisit( @@ -1516,6 +1525,19 @@ void HistoryBackend::GetPageThumbnailDirectly( } } +void HistoryBackend::MigrateThumbnailsDatabase() { + // If there is no History DB, we can't record that the migration was done. + // It will be recorded on the next run. + if (db_.get()) { + // If there is no thumbnail DB, we can still record a successful migration. + if (thumbnail_db_.get()) { + thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), + GetFaviconsFileName()); + } + db_->ThumbnailMigrationDone(); + } +} + bool HistoryBackend::GetThumbnailFromOlderRedirect( const GURL& page_url, std::vector<unsigned char>* data) { @@ -2149,17 +2171,4 @@ BookmarkService* HistoryBackend::GetBookmarkService() { return bookmark_service_; } -void HistoryBackend::MigrateThumbnailsDatabase() { - // If there is no History DB, we can't record that the migration was done. - // It will be recorded on the next run. - if (db_.get()) { - // If there is no thumbnail DB, we can still record a successful migration. - if (thumbnail_db_.get()) { - thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), - GetFaviconsFileName()); - } - db_->MigrationToTopSitesDone(); - } -} - } // namespace history diff --git a/chrome/browser/history/history_backend.h b/chrome/browser/history/history_backend.h index 3a5cea3..54cbf29 100644 --- a/chrome/browser/history/history_backend.h +++ b/chrome/browser/history/history_backend.h @@ -162,6 +162,11 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, int result_count, int days_back); + // QueryMostVisitedURLs without the request. + void QueryMostVisitedURLsImpl(int result_count, + int days_back, + MostVisitedURLList* result); + // Computes the most recent URL(s) that the given canonical URL has // redirected to and returns true on success. There may be more than one // redirect in a row, so this function will fill the given array with the diff --git a/chrome/browser/history/history_backend_unittest.cc b/chrome/browser/history/history_backend_unittest.cc index 70760bd..ad16e9c 100644 --- a/chrome/browser/history/history_backend_unittest.cc +++ b/chrome/browser/history/history_backend_unittest.cc @@ -139,7 +139,8 @@ class HistoryBackendTest : public testing::Test { backend_->Init(std::string(), false); } virtual void TearDown() { - backend_->Closing(); + if (backend_.get()) + backend_->Closing(); backend_ = NULL; mem_backend_.reset(); file_util::Delete(test_dir_, true); @@ -792,6 +793,8 @@ TEST_F(HistoryBackendTest, RemoveVisitsSource) { // Test for migration of adding visit_source table. TEST_F(HistoryBackendTest, MigrationVisitSource) { ASSERT_TRUE(backend_.get()); + backend_->Closing(); + backend_ = NULL; FilePath old_history_path; ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path)); @@ -806,11 +809,12 @@ TEST_F(HistoryBackendTest, MigrationVisitSource) { FilePath new_history_file = new_history_path.Append(chrome::kHistoryFilename); ASSERT_TRUE(file_util::CopyFile(old_history_path, new_history_file)); - backend_->Closing(); backend_ = new HistoryBackend(new_history_path, new HistoryBackendTestDelegate(this), &bookmark_model_); backend_->Init(std::string(), false); + backend_->Closing(); + backend_ = NULL; // Now the database should already be migrated. // Check version first. diff --git a/chrome/browser/history/history_database.cc b/chrome/browser/history/history_database.cc index 56ee054..f0c546c 100644 --- a/chrome/browser/history/history_database.cc +++ b/chrome/browser/history/history_database.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -10,15 +10,16 @@ #include "app/sql/transaction.h" #include "base/command_line.h" #include "base/file_util.h" -#if defined(OS_MACOSX) -#include "base/mac_util.h" -#endif #include "base/metrics/histogram.h" #include "base/rand_util.h" #include "base/string_util.h" #include "chrome/browser/diagnostics/sqlite_diagnostics.h" #include "chrome/common/chrome_switches.h" +#if defined(OS_MACOSX) +#include "base/mac_util.h" +#endif + namespace history { namespace { @@ -26,10 +27,14 @@ namespace { // Current version number. We write databases at the "current" version number, // but any previous version that can read the "compatible" one can make do with // or database without *too* many bad effects. -static const int kCurrentVersionNumber = 19; +static const int kCurrentVersionNumber = 20; static const int kCompatibleVersionNumber = 16; static const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold"; +// Key in the meta table used to determine if we need to migrate thumbnails out +// of history. +static const char kNeedsThumbnailMigrationKey[] = "needs_thumbnail_migration"; + void ComputeDatabaseMetrics(const FilePath& history_name, sql::Connection& db) { if (base::RandInt(1, 100) != 50) @@ -114,6 +119,7 @@ sql::InitStatus HistoryDatabase::Init(const FilePath& history_name, !InitSegmentTables()) return sql::INIT_FAILURE; CreateMainURLIndex(); + CreateKeywordSearchTermsIndices(); CreateSupplimentaryURLIndices(); // Version check. @@ -164,6 +170,7 @@ bool HistoryDatabase::RecreateAllTablesButURL() { // over parts of the URL table that weren't automatically created when the // temporary URL table was CreateSupplimentaryURLIndices(); + CreateKeywordSearchTermsIndices(); return true; } @@ -173,6 +180,16 @@ void HistoryDatabase::Vacuum() { db_.Execute("VACUUM"); } +void HistoryDatabase::ThumbnailMigrationDone() { + meta_table_.SetValue(kNeedsThumbnailMigrationKey, 0); +} + +bool HistoryDatabase::GetNeedsThumbnailMigration() { + int value = 0; + return (meta_table_.GetValue(kNeedsThumbnailMigrationKey, &value) && + value != 0); +} + bool HistoryDatabase::SetSegmentID(VisitID visit_id, SegmentID segment_id) { sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, "UPDATE visits SET segment_id = ? WHERE id = ?")); @@ -289,6 +306,14 @@ sql::InitStatus HistoryDatabase::EnsureCurrentVersion( meta_table_.SetVersionNumber(cur_version); } + if (cur_version == 19) { + cur_version++; + meta_table_.SetVersionNumber(cur_version); + // Set a key indicating we need to migrate thumbnails. When successfull the + // key is removed (ThumbnailMigrationDone). + meta_table_.SetValue(kNeedsThumbnailMigrationKey, 1); + } + // When the version is too old, we just try to continue anyway, there should // not be a released product that makes a database too old for us to handle. LOG_IF(WARNING, cur_version < GetCurrentVersion()) << @@ -322,8 +347,4 @@ void HistoryDatabase::MigrateTimeEpoch() { } #endif -void HistoryDatabase::MigrationToTopSitesDone() { - // TODO(sky): implement me. -} - } // namespace history diff --git a/chrome/browser/history/history_database.h b/chrome/browser/history/history_database.h index fe74e89..1f0c219 100644 --- a/chrome/browser/history/history_database.h +++ b/chrome/browser/history/history_database.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -123,8 +123,11 @@ class HistoryDatabase : public DownloadDatabase, return needs_version_17_migration_; } - // Update the database version after the TopSites migration. - void MigrationToTopSitesDone(); + // Marks the database as no longer needing migration. + void ThumbnailMigrationDone(); + + // Returns true if thumbnails needs to be migrated. + bool GetNeedsThumbnailMigration(); // Visit table functions ---------------------------------------------------- diff --git a/chrome/browser/history/history_notifications.cc b/chrome/browser/history/history_notifications.cc index c846eaf..0fdfa94 100644 --- a/chrome/browser/history/history_notifications.cc +++ b/chrome/browser/history/history_notifications.cc @@ -28,4 +28,8 @@ FavIconChangeDetails::FavIconChangeDetails() {} FavIconChangeDetails::~FavIconChangeDetails() {} +KeywordSearchTermDetails::KeywordSearchTermDetails() : keyword_id(0) {} + +KeywordSearchTermDetails::~KeywordSearchTermDetails() {} + } // namespace history diff --git a/chrome/browser/history/history_notifications.h b/chrome/browser/history/history_notifications.h index 7a2404c..0d82135 100644 --- a/chrome/browser/history/history_notifications.h +++ b/chrome/browser/history/history_notifications.h @@ -13,6 +13,7 @@ #include "googleurl/src/gurl.h" #include "chrome/browser/history/history_types.h" +#include "chrome/browser/search_engines/template_url_id.h" namespace history { @@ -27,7 +28,7 @@ struct HistoryDetails { // Details for HISTORY_URL_VISITED. struct URLVisitedDetails : public HistoryDetails { URLVisitedDetails(); - ~URLVisitedDetails(); + virtual ~URLVisitedDetails(); PageTransition::Type transition; URLRow row; @@ -42,7 +43,7 @@ struct URLVisitedDetails : public HistoryDetails { // Details for NOTIFY_HISTORY_TYPED_URLS_MODIFIED. struct URLsModifiedDetails : public HistoryDetails { URLsModifiedDetails(); - ~URLsModifiedDetails(); + virtual ~URLsModifiedDetails(); // Lists the information for each of the URLs affected. std::vector<URLRow> changed_urls; @@ -51,7 +52,7 @@ struct URLsModifiedDetails : public HistoryDetails { // Details for NOTIFY_HISTORY_URLS_DELETED. struct URLsDeletedDetails : public HistoryDetails { URLsDeletedDetails(); - ~URLsDeletedDetails(); + virtual ~URLsDeletedDetails(); // Set when all history was deleted. False means just a subset was deleted. bool all_history; @@ -65,7 +66,7 @@ struct URLsDeletedDetails : public HistoryDetails { // Details for NOTIFY_URLS_STARRED. struct URLsStarredDetails : public HistoryDetails { explicit URLsStarredDetails(bool being_starred); - ~URLsStarredDetails(); + virtual ~URLsStarredDetails(); // The new starred state of the list of URLs. True when they are being // starred, false when they are being unstarred. @@ -78,11 +79,21 @@ struct URLsStarredDetails : public HistoryDetails { // Details for NOTIFY_FAVICON_CHANGED. struct FavIconChangeDetails : public HistoryDetails { FavIconChangeDetails(); - ~FavIconChangeDetails(); + virtual ~FavIconChangeDetails(); std::set<GURL> urls; }; +// Details for HISTORY_KEYWORD_SEARCH_TERM_UPDATED. +struct KeywordSearchTermDetails : public HistoryDetails { + KeywordSearchTermDetails(); + ~KeywordSearchTermDetails(); + + GURL url; + TemplateURLID keyword_id; + string16 term; +}; + } // namespace history #endif // CHROME_BROWSER_HISTORY_HISTORY_NOTIFICATIONS_H__ diff --git a/chrome/browser/history/history_types.cc b/chrome/browser/history/history_types.cc index 408bb93..eebe5a7 100644 --- a/chrome/browser/history/history_types.cc +++ b/chrome/browser/history/history_types.cc @@ -366,4 +366,7 @@ HistoryAddPageArgs* HistoryAddPageArgs::Clone() const { visit_source, did_replace_entry); } +MostVisitedThumbnails::MostVisitedThumbnails() { +} + } // namespace history diff --git a/chrome/browser/history/history_types.h b/chrome/browser/history/history_types.h index 831c0b0..536064a 100644 --- a/chrome/browser/history/history_types.h +++ b/chrome/browser/history/history_types.h @@ -544,20 +544,6 @@ struct MostVisitedURL { } }; -// Used by TopSites to store the thumbnails. -struct Images { - Images(); - ~Images(); - - scoped_refptr<RefCountedBytes> thumbnail; - ThumbnailScore thumbnail_score; - - // TODO(brettw): this will eventually store the favicon. - // scoped_refptr<RefCountedBytes> favicon; -}; - -typedef std::vector<MostVisitedURL> MostVisitedURLList; - // Navigation ----------------------------------------------------------------- // Marshalling structure for AddPage. @@ -598,6 +584,61 @@ class HistoryAddPageArgs DISALLOW_COPY_AND_ASSIGN(HistoryAddPageArgs); }; +// TopSites ------------------------------------------------------------------- + +typedef std::vector<MostVisitedURL> MostVisitedURLList; + +// Used by TopSites to store the thumbnails. +struct Images { + Images(); + ~Images(); + + scoped_refptr<RefCountedBytes> thumbnail; + ThumbnailScore thumbnail_score; + + // TODO(brettw): this will eventually store the favicon. + // scoped_refptr<RefCountedBytes> favicon; +}; + +typedef std::vector<MostVisitedURL> MostVisitedURLList; + +struct MostVisitedURLWithRank { + MostVisitedURL url; + int rank; +}; + +typedef std::vector<MostVisitedURLWithRank> MostVisitedURLWithRankList; + +struct TopSitesDelta { + MostVisitedURLList deleted; + MostVisitedURLWithRankList added; + MostVisitedURLWithRankList moved; +}; + +typedef std::map<GURL, scoped_refptr<RefCountedBytes> > URLToThumbnailMap; + +// Used when migrating most visited thumbnails out of history and into topsites. +struct ThumbnailMigration { + MostVisitedURLList most_visited; + URLToThumbnailMap url_to_thumbnail_map; +}; + +typedef std::map<GURL, Images> URLToImagesMap; + +class MostVisitedThumbnails + : public base::RefCountedThreadSafe<MostVisitedThumbnails> { + public: + MostVisitedThumbnails(); + + MostVisitedURLList most_visited; + URLToImagesMap url_to_images_map; + + private: + friend class base::RefCountedThreadSafe<MostVisitedThumbnails>; + + DISALLOW_COPY_AND_ASSIGN(MostVisitedThumbnails); +}; + } // namespace history #endif // CHROME_BROWSER_HISTORY_HISTORY_TYPES_H_ diff --git a/chrome/browser/history/in_memory_database.cc b/chrome/browser/history/in_memory_database.cc index 3227a7a..168a544 100644 --- a/chrome/browser/history/in_memory_database.cc +++ b/chrome/browser/history/in_memory_database.cc @@ -41,6 +41,13 @@ bool InMemoryDatabase::InitDB() { return false; } + // Create the keyword search terms table. + if (!InitKeywordSearchTermsTable()) { + NOTREACHED() << "Unable to create keyword search terms"; + db_.Close(); + return false; + } + return true; } @@ -51,6 +58,7 @@ bool InMemoryDatabase::InitFromScratch() { // InitDB doesn't create the index so in the disk-loading case, it can be // added afterwards. CreateMainURLIndex(); + CreateKeywordSearchTermsIndices(); return true; } @@ -87,6 +95,36 @@ bool InMemoryDatabase::InitFromDisk(const FilePath& history_name) { end_load - begin_load); UMA_HISTOGRAM_COUNTS("History.InMemoryDBItemCount", db_.GetLastChangeCount()); + // Insert keyword search related URLs. + begin_load = base::TimeTicks::Now(); + if (!db_.Execute( + "INSERT INTO urls SELECT u.id, u.url, u.title, u.visit_count, " + "u.typed_count, u.last_visit_time, u.hidden, u.favicon_id " + "FROM history.urls u JOIN history.keyword_search_terms kst " + "WHERE u.typed_count = 0 AND u.id = kst.url_id")) { + // Unable to get data from the history database. This is OK, the file may + // just not exist yet. + } + end_load = base::TimeTicks::Now(); + UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBKeywordURLPopulate", + end_load - begin_load); + UMA_HISTOGRAM_COUNTS("History.InMemoryDBKeywordURLItemCount", + db_.GetLastChangeCount()); + + // Copy search terms to memory. + begin_load = base::TimeTicks::Now(); + if (!db_.Execute( + "INSERT INTO keyword_search_terms SELECT * FROM " + "history.keyword_search_terms")) { + // Unable to get data from the history database. This is OK, the file may + // just not exist yet. + } + end_load = base::TimeTicks::Now(); + UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBKeywordTermsPopulate", + end_load - begin_load); + UMA_HISTOGRAM_COUNTS("History.InMemoryDBKeywordTermsCount", + db_.GetLastChangeCount()); + // Detach from the history database on disk. if (!db_.Execute("DETACH history")) { NOTREACHED() << "Unable to detach from history database."; @@ -96,6 +134,7 @@ bool InMemoryDatabase::InitFromDisk(const FilePath& history_name) { // Index the table, this is faster than creating the index first and then // inserting into it. CreateMainURLIndex(); + CreateKeywordSearchTermsIndices(); return true; } diff --git a/chrome/browser/history/in_memory_history_backend.cc b/chrome/browser/history/in_memory_history_backend.cc index 6827a78..be82f58 100644 --- a/chrome/browser/history/in_memory_history_backend.cc +++ b/chrome/browser/history/in_memory_history_backend.cc @@ -72,6 +72,9 @@ void InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) { registrar_.Add(this, NotificationType::HISTORY_URL_VISITED, source); registrar_.Add(this, NotificationType::HISTORY_TYPED_URLS_MODIFIED, source); registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, source); + registrar_.Add(this, NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED, + source); + registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED, source); } void InMemoryHistoryBackend::Observe(NotificationType type, @@ -80,13 +83,20 @@ void InMemoryHistoryBackend::Observe(NotificationType type, switch (type.value) { case NotificationType::HISTORY_URL_VISITED: { Details<history::URLVisitedDetails> visited_details(details); - if (visited_details->row.typed_count() > 0) { + PageTransition::Type primary_type = + PageTransition::StripQualifier(visited_details->transition); + if (visited_details->row.typed_count() > 0 || + primary_type == PageTransition::KEYWORD) { URLsModifiedDetails modified_details; modified_details.changed_urls.push_back(visited_details->row); OnTypedURLsModified(modified_details); } break; } + case NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED: + OnKeywordSearchTermUpdated( + *Details<history::KeywordSearchTermDetails>(details).ptr()); + break; case NotificationType::HISTORY_TYPED_URLS_MODIFIED: OnTypedURLsModified( *Details<history::URLsModifiedDetails>(details).ptr()); @@ -94,6 +104,10 @@ void InMemoryHistoryBackend::Observe(NotificationType type, case NotificationType::HISTORY_URLS_DELETED: OnURLsDeleted(*Details<history::URLsDeletedDetails>(details).ptr()); break; + case NotificationType::TEMPLATE_URL_REMOVED: + db_->DeleteAllSearchTermsForKeyword( + *(Details<TemplateURLID>(details).ptr())); + break; default: // For simplicity, the unit tests send us all notifications, even when // we haven't registered for them, so don't assert here. @@ -145,4 +159,27 @@ void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) { } } +void InMemoryHistoryBackend::OnKeywordSearchTermUpdated( + const KeywordSearchTermDetails& details) { + // The url won't exist for new search terms (as the user hasn't typed it), so + // we force it to be added. If we end up adding a URL it won't be + // autocompleted as the typed count is 0. + URLRow url_row; + URLID url_id; + if (!db_->GetRowForURL(details.url, &url_row)) { + // Because this row won't have a typed count the title and other stuff + // doesn't matter. If the user ends up typing the url we'll update the title + // in OnTypedURLsModified. + URLRow new_row(details.url); + new_row.set_last_visit(base::Time::Now()); + url_id = db_->AddURL(new_row); + if (!url_id) + return; // Error adding. + } else { + url_id = url_row.id(); + } + + db_->SetKeywordSearchTermsForURL(url_id, details.keyword_id, details.term); +} + } // namespace history diff --git a/chrome/browser/history/in_memory_history_backend.h b/chrome/browser/history/in_memory_history_backend.h index 4fdc3a5..c775800 100644 --- a/chrome/browser/history/in_memory_history_backend.h +++ b/chrome/browser/history/in_memory_history_backend.h @@ -30,6 +30,7 @@ namespace history { class InMemoryDatabase; class InMemoryURLIndex; +struct KeywordSearchTermDetails; class URLDatabase; struct URLsDeletedDetails; struct URLsModifiedDetails; @@ -73,6 +74,9 @@ class InMemoryHistoryBackend : public NotificationObserver { // Handler for NOTIFY_HISTORY_URLS_DELETED. void OnURLsDeleted(const URLsDeletedDetails& details); + // Handler for HISTORY_KEYWORD_SEARCH_TERM_UPDATED. + void OnKeywordSearchTermUpdated(const KeywordSearchTermDetails& details); + NotificationRegistrar registrar_; scoped_ptr<InMemoryDatabase> db_; diff --git a/chrome/browser/history/in_memory_url_index.cc b/chrome/browser/history/in_memory_url_index.cc index 654afdc..98c2acd 100644 --- a/chrome/browser/history/in_memory_url_index.cc +++ b/chrome/browser/history/in_memory_url_index.cc @@ -34,7 +34,9 @@ ScoredHistoryMatch::ScoredHistoryMatch(const URLRow& url_info, } struct InMemoryURLIndex::TermCharWordSet { - TermCharWordSet(Char16Set char_set, WordIDSet word_id_set, bool used) + TermCharWordSet(const Char16Set& char_set, + const WordIDSet& word_id_set, + bool used) : char_set_(char_set), word_id_set_(word_id_set), used_(used) {} @@ -81,7 +83,7 @@ bool InMemoryURLIndex::Init(history::URLDatabase* history_db, return true; } -bool InMemoryURLIndex::IndexRow(URLRow row) { +bool InMemoryURLIndex::IndexRow(const URLRow& row) { const GURL& gurl(row.url()); string16 url(net::FormatUrl(gurl, languages_, net::kFormatUrlOmitUsernamePassword, diff --git a/chrome/browser/history/in_memory_url_index.h b/chrome/browser/history/in_memory_url_index.h index a2ac0f3..81336f1 100644 --- a/chrome/browser/history/in_memory_url_index.h +++ b/chrome/browser/history/in_memory_url_index.h @@ -159,7 +159,7 @@ class InMemoryURLIndex { // URL History indexing support functions. // Index one URL history item. - bool IndexRow(URLRow row); + bool IndexRow(const URLRow& row); // Break a string down into its individual characters. // Note that this is temporarily intended to work on a single word, but diff --git a/chrome/browser/history/text_database.cc b/chrome/browser/history/text_database.cc index 84367d3..60aa7fd 100644 --- a/chrome/browser/history/text_database.cc +++ b/chrome/browser/history/text_database.cc @@ -112,8 +112,8 @@ TextDatabase::DBIdent TextDatabase::FileNameToID(const FilePath& file_path) { } int year, month; - base::StringToInt(suffix.substr(0, 4), &year); - base::StringToInt(suffix.substr(5, 2), &month); + base::StringToInt(suffix.begin(), suffix.begin() + 4, &year); + base::StringToInt(suffix.begin() + 5, suffix.begin() + 7, &month); return year * 100 + month; } diff --git a/chrome/browser/history/thumbnail_database.cc b/chrome/browser/history/thumbnail_database.cc index 9cd3f9c..e1054ac 100644 --- a/chrome/browser/history/thumbnail_database.cc +++ b/chrome/browser/history/thumbnail_database.cc @@ -104,21 +104,10 @@ sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Connection* db, // Set the exceptional sqlite error handler. db->set_error_delegate(GetErrorHandlerForThumbnailDb()); - // Set the database page size to something larger to give us - // better performance (we're typically seek rather than bandwidth limited). - // This only has an effect before any tables have been created, otherwise - // this is a NOP. Must be a power of 2 and a max of 8192. We use a bigger - // one because we're storing larger data (4-16K) in it, so we want a few - // blocks per element. - db->set_page_size(4096); - - // The UI is generally designed to work well when the thumbnail database is - // slow, so we can tolerate much less caching. The file is also very large - // and so caching won't save a significant percentage of it for us, - // reducing the benefit of caching in the first place. With the default cache - // size of 2000 pages, it will take >8MB of memory, so reducing it can be a - // big savings. - db->set_cache_size(64); + // Thumbnails db now only stores favicons, so we don't need that big a page + // size or cache. + db->set_page_size(2048); + db->set_cache_size(32); // Run the database in exclusive mode. Nobody else should be accessing the // database while we're running, and this will give somewhat improved perf. @@ -310,7 +299,6 @@ bool ThumbnailDatabase::GetPageThumbnail(URLID id, bool ThumbnailDatabase::DeleteThumbnail(URLID id) { if (use_top_sites_) { - LOG(WARNING) << "Use TopSites instead."; return true; // Not possible after migration to TopSites. } diff --git a/chrome/browser/history/top_sites.cc b/chrome/browser/history/top_sites.cc index 31ce4ac..8bdfe34 100644 --- a/chrome/browser/history/top_sites.cc +++ b/chrome/browser/history/top_sites.cc @@ -5,10 +5,10 @@ #include "chrome/browser/history/top_sites.h" #include <algorithm> +#include <set> #include "app/l10n_util.h" #include "base/command_line.h" -#include "base/file_util.h" #include "base/logging.h" #include "base/md5.h" #include "base/string_util.h" @@ -16,14 +16,17 @@ #include "base/values.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/dom_ui/most_visited_handler.h" +#include "chrome/browser/history/history_backend.h" #include "chrome/browser/history/history_notifications.h" #include "chrome/browser/history/page_usage_data.h" -#include "chrome/browser/history/top_sites_database.h" +#include "chrome/browser/history/top_sites_backend.h" +#include "chrome/browser/history/top_sites_cache.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/navigation_controller.h" #include "chrome/browser/tab_contents/navigation_entry.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" #include "chrome/common/thumbnail_score.h" #include "gfx/codec/jpeg_codec.h" @@ -36,6 +39,11 @@ namespace history { // How many top sites to store in the cache. static const size_t kTopSitesNumber = 20; + +// Max number of temporary images we'll cache. See comment above +// temp_images_ for details. +static const size_t kMaxTempTopImages = 8; + static const size_t kTopSitesShown = 8; static const int kDaysOfHistory = 90; // Time from startup to first HistoryService query. @@ -44,14 +52,91 @@ static const int64 kUpdateIntervalSecs = 15; static const int64 kMinUpdateIntervalMinutes = 1; static const int64 kMaxUpdateIntervalMinutes = 60; +// IDs of the sites we force into top sites. +static const int kPrepopulatePageIDs[] = + { IDS_CHROME_WELCOME_URL, IDS_THEMES_GALLERY_URL }; + +// Favicons of the sites we force into top sites. +static const char kPrepopulateFaviconURLs[][54] = + { "chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_FAVICON", + "chrome://theme/IDR_NEWTAB_THEMES_GALLERY_FAVICON" }; + +static const int kPrepopulateTitleIDs[] = + { IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE, + IDS_NEW_TAB_THEMES_GALLERY_PAGE_TITLE }; + +namespace { + +// HistoryDBTask used during migration of thumbnails from history to top sites. +// When run on the history thread it collects the top sites and the +// corresponding thumbnails. When run back on the ui thread it calls into +// TopSites::FinishHistoryMigration. +class LoadThumbnailsFromHistoryTask : public HistoryDBTask { + public: + LoadThumbnailsFromHistoryTask(TopSites* top_sites, + int result_count) + : top_sites_(top_sites), + result_count_(result_count) { + // l10n_util isn't thread safe, so cache for use on the db thread. + ignore_urls_.insert(l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL)); + ignore_urls_.insert(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)); + } + + virtual bool RunOnDBThread(history::HistoryBackend* backend, + history::HistoryDatabase* db) { + // Get the most visited urls. + backend->QueryMostVisitedURLsImpl(result_count_, + kDaysOfHistory, + &data_.most_visited); + + // And fetch the thumbnails. + for (size_t i = 0; i < data_.most_visited.size(); ++i) { + const GURL& url = data_.most_visited[i].url; + if (ShouldFetchThumbnailFor(url)) { + scoped_refptr<RefCountedBytes> data; + backend->GetPageThumbnailDirectly(url, &data); + data_.url_to_thumbnail_map[url] = data; + } + } + return true; + } + + virtual void DoneRunOnMainThread() { + top_sites_->FinishHistoryMigration(data_); + } + + private: + bool ShouldFetchThumbnailFor(const GURL& url) { + return ignore_urls_.find(url.spec()) == ignore_urls_.end(); + } + + // Set of URLs we don't load thumbnails for. This is created on the UI thread + // and used on the history thread. + std::set<std::string> ignore_urls_; + + scoped_refptr<TopSites> top_sites_; + + // Number of results to request from history. + const int result_count_; + + ThumbnailMigration data_; + + DISALLOW_COPY_AND_ASSIGN(LoadThumbnailsFromHistoryTask); +}; + +} // namespace -TopSites::TopSites(Profile* profile) : profile_(profile), - mock_history_service_(NULL), - last_num_urls_changed_(0), - migration_in_progress_(false), - waiting_for_results_(true), - blacklist_(NULL), - pinned_urls_(NULL) { +TopSites::TopSites(Profile* profile) + : backend_(new TopSitesBackend()), + cache_(new TopSitesCache()), + thread_safe_cache_(new TopSitesCache()), + profile_(profile), + last_num_urls_changed_(0), + blacklist_(NULL), + pinned_urls_(NULL), + history_state_(HISTORY_LOADING), + top_sites_state_(TOP_SITES_LOADING), + loaded_(false) { if (!profile_) return; @@ -70,61 +155,42 @@ TopSites::TopSites(Profile* profile) : profile_(profile), // static bool TopSites::IsEnabled() { - return CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableTopSites); -} - -TopSites::~TopSites() { - timer_.Stop(); + std::string switch_value = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kEnableTopSites); + return switch_value.empty() || switch_value == "true"; } void TopSites::Init(const FilePath& db_name) { - db_path_ = db_name; - db_.reset(new TopSitesDatabaseImpl()); - if (!db_->Init(db_name)) { - NOTREACHED() << "Failed to initialize database."; - return; - } + backend_->Init(db_name); + backend_->GetMostVisitedThumbnails( + &cancelable_consumer_, + NewCallback(this, &TopSites::OnGotMostVisitedThumbnails)); - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, NewRunnableMethod( - this, &TopSites::ReadDatabase)); - - // Start the one-shot timer. - timer_.Start(base::TimeDelta::FromSeconds(kUpdateIntervalSecs), this, - &TopSites::StartQueryForMostVisited); -} - -void TopSites::ReadDatabase() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - std::map<GURL, Images> thumbnails; - - DCHECK(db_.get()); - MostVisitedURLList top_urls; - db_->GetPageThumbnails(&top_urls, &thumbnails); - MostVisitedURLList copy(top_urls); // StoreMostVisited destroys the list. - StoreMostVisited(&top_urls); - if (AddPrepopulatedPages(©)) - UpdateMostVisited(copy); - - AutoLock lock(lock_); - for (size_t i = 0; i < top_sites_.size(); i++) { - GURL url = top_sites_[i].url; - Images thumbnail = thumbnails[url]; - if (thumbnail.thumbnail.get() && thumbnail.thumbnail->size()) { - SetPageThumbnailNoDB(url, thumbnail.thumbnail, - thumbnail.thumbnail_score); - } + // History may have already finished loading by the time we're created. + HistoryService* history = profile_->GetHistoryServiceWithoutCreating(); + if (history && history->backend_loaded()) { + if (history->needs_top_sites_migration()) + MigrateFromHistory(); + else + history_state_ = HISTORY_LOADED; } } -// Public function that encodes the bitmap into RefCountedBytes and -// updates the database. bool TopSites::SetPageThumbnail(const GURL& url, const SkBitmap& thumbnail, const ThumbnailScore& score) { - AutoLock lock(lock_); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!loaded_) { + // TODO(sky): I need to cache these and apply them after the load + // completes. + return false; + } + bool add_temp_thumbnail = false; - if (canonical_urls_.find(url) == canonical_urls_.end()) { - if (top_sites_.size() < kTopSitesNumber) { + if (!cache_->IsKnownURL(url)) { + if (cache_->top_sites().size() < kTopSitesNumber) { add_temp_thumbnail = true; } else { return false; // This URL is not known to us. @@ -134,18 +200,14 @@ bool TopSites::SetPageThumbnail(const GURL& url, if (!HistoryService::CanAddURL(url)) return false; // It's not a real webpage. - scoped_refptr<RefCountedBytes> thumbnail_data = new RefCountedBytes; - SkAutoLockPixels thumbnail_lock(thumbnail); - bool encoded = gfx::JPEGCodec::Encode( - reinterpret_cast<unsigned char*>(thumbnail.getAddr32(0, 0)), - gfx::JPEGCodec::FORMAT_SkBitmap, thumbnail.width(), - thumbnail.height(), - static_cast<int>(thumbnail.rowBytes()), 90, - &thumbnail_data->data); - if (!encoded) + scoped_refptr<RefCountedBytes> thumbnail_data; + if (!EncodeBitmap(thumbnail, &thumbnail_data)) return false; if (add_temp_thumbnail) { + // Always remove the existing entry and then add it back. That way if we end + // up with too many temp thumbnails we'll prune the oldest first. + RemoveTemporaryThumbnailByURL(url); AddTemporaryThumbnail(url, thumbnail_data, score); return true; } @@ -153,59 +215,243 @@ bool TopSites::SetPageThumbnail(const GURL& url, return SetPageThumbnailEncoded(url, thumbnail_data, score); } -bool TopSites::SetPageThumbnailEncoded(const GURL& url, - const RefCountedBytes* thumbnail, - const ThumbnailScore& score) { - lock_.AssertAcquired(); - if (!SetPageThumbnailNoDB(url, thumbnail, score)) - return false; +void TopSites::GetMostVisitedURLs(CancelableRequestConsumer* consumer, + GetTopSitesCallback* callback) { + // WARNING: this may be invoked on any thread. + scoped_refptr<CancelableRequest<GetTopSitesCallback> > request( + new CancelableRequest<GetTopSitesCallback>(callback)); + // This ensures cancelation of requests when either the consumer or the + // provider is deleted. Deletion of requests is also guaranteed. + AddRequest(request, consumer); + MostVisitedURLList filtered_urls; + { + AutoLock lock(lock_); + if (!loaded_) { + // A request came in before we finished loading. Put the request in + // pending_callbacks_ and we'll notify it when we finish loading. + pending_callbacks_.insert(request); + return; + } - // Update the database. - if (!db_.get()) - return true; - std::map<GURL, size_t>::iterator found = canonical_urls_.find(url); - if (found == canonical_urls_.end()) - return false; - size_t index = found->second; + filtered_urls = thread_safe_cache_->top_sites(); + } + request->ForwardResult(GetTopSitesCallback::TupleType(filtered_urls)); +} - MostVisitedURL& most_visited = top_sites_[index]; - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, NewRunnableMethod( - this, &TopSites::WriteThumbnailToDB, - most_visited, index, top_images_[most_visited.url])); - return true; +bool TopSites::GetPageThumbnail(const GURL& url, + scoped_refptr<RefCountedBytes>* bytes) { + // WARNING: this may be invoked on any thread. + AutoLock lock(lock_); + return thread_safe_cache_->GetPageThumbnail(url, bytes); +} + +// Returns the index of |url| in |urls|, or -1 if not found. +static int IndexOf(const MostVisitedURLList& urls, const GURL& url) { + for (size_t i = 0; i < urls.size(); i++) { + if (urls[i].url == url) + return i; + } + return -1; } -void TopSites::WriteThumbnailToDB(const MostVisitedURL& url, - int url_rank, - const Images& thumbnail) { - DCHECK(db_.get()); - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - db_->SetPageThumbnail(url, url_rank, thumbnail); +void TopSites::MigrateFromHistory() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(history_state_, HISTORY_LOADING); + + history_state_ = HISTORY_MIGRATING; + profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)->ScheduleDBTask( + new LoadThumbnailsFromHistoryTask( + this, + num_results_to_request_from_history()), + &cancelable_consumer_); + MigratePinnedURLs(); } -// private -bool TopSites::SetPageThumbnailNoDB(const GURL& url, - const RefCountedBytes* thumbnail_data, - const ThumbnailScore& score) { - lock_.AssertAcquired(); +void TopSites::FinishHistoryMigration(const ThumbnailMigration& data) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(history_state_, HISTORY_MIGRATING); - std::map<GURL, size_t>::iterator found = canonical_urls_.find(url); - if (found == canonical_urls_.end()) { - if (top_sites_.size() >= kTopSitesNumber) - return false; // This URL is not known to us. + history_state_ = HISTORY_LOADED; - // We don't have enough Top Sites - add this one too. - MostVisitedURL mv; - mv.url = url; - mv.redirects.push_back(url); - top_sites_.push_back(mv); - size_t index = top_sites_.size() - 1; - StoreRedirectChain(top_sites_[index].redirects, index); - found = canonical_urls_.find(url); + SetTopSites(data.most_visited); + + for (size_t i = 0; i < data.most_visited.size(); ++i) { + URLToThumbnailMap::const_iterator image_i = + data.url_to_thumbnail_map.find(data.most_visited[i].url); + if (image_i != data.url_to_thumbnail_map.end()) { + SetPageThumbnailEncoded(data.most_visited[i].url, + image_i->second, + ThumbnailScore()); + } } - MostVisitedURL& most_visited = top_sites_[found->second]; - Images& image = top_images_[most_visited.url]; + MoveStateToLoaded(); + + ResetThreadSafeImageCache(); + + // We've scheduled all the thumbnails and top sites to be written to the top + // sites db, but it hasn't happened yet. Schedule a request on the db thread + // that notifies us when done. When done we'll know everything was written and + // we can tell history to finish its part of migration. + backend_->DoEmptyRequest( + &cancelable_consumer_, + NewCallback(this, &TopSites::OnHistoryMigrationWrittenToDisk)); +} + +void TopSites::HistoryLoaded() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_NE(history_state_, HISTORY_LOADED); + + if (history_state_ != HISTORY_MIGRATING) { + // No migration from history is needed. + history_state_ = HISTORY_LOADED; + if (top_sites_state_ == TOP_SITES_LOADED_WAITING_FOR_HISTORY) { + // TopSites thought it needed migration, but it really didn't. This + // typically happens the first time a profile is run with Top Sites + // enabled + SetTopSites(MostVisitedURLList()); + MoveStateToLoaded(); + } + } +} + +bool TopSites::HasBlacklistedItems() const { + return !blacklist_->empty(); +} + +void TopSites::AddBlacklistedURL(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + RemovePinnedURL(url); + Value* dummy = Value::CreateNullValue(); + blacklist_->SetWithoutPathExpansion(GetURLHash(url), dummy); + + ResetThreadSafeCache(); +} + +void TopSites::RemoveBlacklistedURL(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + blacklist_->RemoveWithoutPathExpansion(GetURLHash(url), NULL); + ResetThreadSafeCache(); +} + +bool TopSites::IsBlacklisted(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return blacklist_->HasKey(GetURLHash(url)); +} + +void TopSites::ClearBlacklistedURLs() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + blacklist_->Clear(); + ResetThreadSafeCache(); +} + +void TopSites::AddPinnedURL(const GURL& url, size_t pinned_index) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + GURL old; + if (GetPinnedURLAtIndex(pinned_index, &old)) + RemovePinnedURL(old); + + if (IsURLPinned(url)) + RemovePinnedURL(url); + + Value* index = Value::CreateIntegerValue(pinned_index); + pinned_urls_->SetWithoutPathExpansion(GetURLString(url), index); + + ResetThreadSafeCache(); +} + +bool TopSites::IsURLPinned(const GURL& url) { + int tmp; + return pinned_urls_->GetIntegerWithoutPathExpansion(GetURLString(url), &tmp); +} + +void TopSites::RemovePinnedURL(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + pinned_urls_->RemoveWithoutPathExpansion(GetURLString(url), NULL); + + ResetThreadSafeCache(); +} + +bool TopSites::GetPinnedURLAtIndex(size_t index, GURL* url) { + for (DictionaryValue::key_iterator it = pinned_urls_->begin_keys(); + it != pinned_urls_->end_keys(); ++it) { + int current_index; + if (pinned_urls_->GetIntegerWithoutPathExpansion(*it, ¤t_index)) { + if (static_cast<size_t>(current_index) == index) { + *url = GURL(*it); + return true; + } + } + } + return false; +} + +void TopSites::Shutdown() { + profile_ = NULL; + // Cancel all requests so that the service doesn't callback to us after we've + // invoked Shutdown (this could happen if we have a pending request and + // Shutdown is invoked). + cancelable_consumer_.CancelAllRequests(); + backend_->Shutdown(); +} + +// static +void TopSites::DiffMostVisited(const MostVisitedURLList& old_list, + const MostVisitedURLList& new_list, + TopSitesDelta* delta) { + // Add all the old URLs for quick lookup. This maps URLs to the corresponding + // index in the input. + std::map<GURL, size_t> all_old_urls; + for (size_t i = 0; i < old_list.size(); i++) + all_old_urls[old_list[i].url] = i; + + // Check all the URLs in the new set to see which ones are new or just moved. + // When we find a match in the old set, we'll reset its index to our special + // marker. This allows us to quickly identify the deleted ones in a later + // pass. + const size_t kAlreadyFoundMarker = static_cast<size_t>(-1); + for (size_t i = 0; i < new_list.size(); i++) { + std::map<GURL, size_t>::iterator found = all_old_urls.find(new_list[i].url); + if (found == all_old_urls.end()) { + MostVisitedURLWithRank added; + added.url = new_list[i]; + added.rank = i; + delta->added.push_back(added); + } else { + if (found->second != i) { + MostVisitedURLWithRank moved; + moved.url = new_list[i]; + moved.rank = i; + delta->moved.push_back(moved); + } + found->second = kAlreadyFoundMarker; + } + } + + // Any member without the special marker in the all_old_urls list means that + // there wasn't a "new" URL that mapped to it, so it was deleted. + for (std::map<GURL, size_t>::const_iterator i = all_old_urls.begin(); + i != all_old_urls.end(); ++i) { + if (i->second != kAlreadyFoundMarker) + delta->deleted.push_back(old_list[i->second]); + } +} + +TopSites::~TopSites() { +} + +bool TopSites::SetPageThumbnailNoDB(const GURL& url, + const RefCountedBytes* thumbnail_data, + const ThumbnailScore& score) { + // This should only be invoked when we know about the url. + DCHECK(cache_->IsKnownURL(url)); + + const MostVisitedURL& most_visited = + cache_->top_sites()[cache_->GetURLIndex(url)]; + Images* image = cache_->GetImage(url); // When comparing the thumbnail scores, we need to take into account the // redirect hops, which are not generated when the thumbnail is because the @@ -214,95 +460,136 @@ bool TopSites::SetPageThumbnailNoDB(const GURL& url, new_score_with_redirects.redirect_hops_from_dest = GetRedirectDistanceForURL(most_visited, url); - if (!ShouldReplaceThumbnailWith(image.thumbnail_score, + if (!ShouldReplaceThumbnailWith(image->thumbnail_score, new_score_with_redirects) && - image.thumbnail.get()) + image->thumbnail.get()) return false; // The one we already have is better. - // Take ownership of the thumbnail data. - image.thumbnail = const_cast<RefCountedBytes*>(thumbnail_data); - image.thumbnail_score = new_score_with_redirects; + image->thumbnail = const_cast<RefCountedBytes*>(thumbnail_data); + image->thumbnail_score = new_score_with_redirects; + ResetThreadSafeImageCache(); return true; } -void TopSites::GetMostVisitedURLs(CancelableRequestConsumer* consumer, - GetTopSitesCallback* callback) { - scoped_refptr<CancelableRequest<GetTopSitesCallback> > request( - new CancelableRequest<GetTopSitesCallback>(callback)); - // This ensures cancelation of requests when either the consumer or the - // provider is deleted. Deletion of requests is also guaranteed. - AddRequest(request, consumer); - MostVisitedURLList filtered_urls; - { - AutoLock lock(lock_); - if (waiting_for_results_) { - // A request came in before we have any top sites. - // We have to keep track of the requests ourselves. - pending_callbacks_.insert(request); +bool TopSites::SetPageThumbnailEncoded(const GURL& url, + const RefCountedBytes* thumbnail, + const ThumbnailScore& score) { + if (!SetPageThumbnailNoDB(url, thumbnail, score)) + return false; + + // Update the database. + if (!cache_->IsKnownURL(url)) + return false; + + size_t index = cache_->GetURLIndex(url); + const MostVisitedURL& most_visited = cache_->top_sites()[index]; + backend_->SetPageThumbnail(most_visited, + index, + *(cache_->GetImage(most_visited.url))); + return true; +} + +// static +bool TopSites::EncodeBitmap(const SkBitmap& bitmap, + scoped_refptr<RefCountedBytes>* bytes) { + *bytes = new RefCountedBytes(); + SkAutoLockPixels bitmap_lock(bitmap); + std::vector<unsigned char> data; + if (!gfx::JPEGCodec::Encode( + reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)), + gfx::JPEGCodec::FORMAT_BGRA, bitmap.width(), + bitmap.height(), + static_cast<int>(bitmap.rowBytes()), 90, + &data)) { + return false; + } + // As we're going to cache this data, make sure the vector is only as big as + // it needs to be. + (*bytes)->data = data; + return true; +} + +void TopSites::RemoveTemporaryThumbnailByURL(const GURL& url) { + for (TempImages::iterator i = temp_images_.begin(); i != temp_images_.end(); + ++i) { + if (i->first == url) { + temp_images_.erase(i); return; } - if (request->canceled()) - return; - - ApplyBlacklistAndPinnedURLs(top_sites_, &filtered_urls); } - request->ForwardResult(GetTopSitesCallback::TupleType(filtered_urls)); } -bool TopSites::GetPageThumbnail(const GURL& url, RefCountedBytes** data) const { - AutoLock lock(lock_); - std::map<GURL, Images>::const_iterator found = - top_images_.find(GetCanonicalURL(url)); - if (found == top_images_.end()) { - found = temp_thumbnails_map_.find(url); - if (found == temp_thumbnails_map_.end()) - return false; // No thumbnail for this URL. +void TopSites::AddTemporaryThumbnail(const GURL& url, + const RefCountedBytes* thumbnail, + const ThumbnailScore& score) { + if (temp_images_.size() == kMaxTempTopImages) + temp_images_.erase(temp_images_.begin()); + + TempImage image; + image.first = url; + image.second.thumbnail = const_cast<RefCountedBytes*>(thumbnail); + image.second.thumbnail_score = score; + temp_images_.push_back(image); +} + +void TopSites::StartQueryForMostVisited() { + if (!profile_) + return; + + HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); + // |hs| may be null during unit tests. + if (hs) { + hs->QueryMostVisitedURLs( + num_results_to_request_from_history(), + kDaysOfHistory, + &cancelable_consumer_, + NewCallback(this, &TopSites::OnTopSitesAvailableFromHistory)); } +} - Images image = found->second; - *data = image.thumbnail.get(); - return true; +// static +int TopSites::GetRedirectDistanceForURL(const MostVisitedURL& most_visited, + const GURL& url) { + for (size_t i = 0; i < most_visited.redirects.size(); i++) { + if (most_visited.redirects[i] == url) + return static_cast<int>(most_visited.redirects.size() - i - 1); + } + NOTREACHED() << "URL should always be found."; + return 0; } -static int IndexOf(const MostVisitedURLList& urls, const GURL& url) { - for (size_t i = 0; i < urls.size(); i++) { - if (urls[i].url == url) - return i; +// static +MostVisitedURLList TopSites::GetPrepopulatePages() { + MostVisitedURLList urls; + urls.resize(arraysize(kPrepopulatePageIDs)); + for (size_t i = 0; i < arraysize(kPrepopulatePageIDs); ++i) { + MostVisitedURL& url = urls[i]; + url.url = GURL(l10n_util::GetStringUTF8(kPrepopulatePageIDs[i])); + url.redirects.push_back(url.url); + url.favicon_url = GURL(kPrepopulateFaviconURLs[i]); + url.title = l10n_util::GetStringUTF16(kPrepopulateTitleIDs[i]); } - return -1; + return urls; } +// static bool TopSites::AddPrepopulatedPages(MostVisitedURLList* urls) { - // TODO(arv): This needs to get the data from some configurable place. - // http://crbug.com/17630 bool added = false; - GURL welcome_url(l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL)); - if (urls->size() < kTopSitesNumber && IndexOf(*urls, welcome_url) == -1) { - MostVisitedURL url( - welcome_url, - GURL("chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_FAVICON"), - l10n_util::GetStringUTF16(IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE)); - url.redirects.push_back(welcome_url); - urls->push_back(url); - added = true; - } - - GURL themes_url(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)); - if (urls->size() < kTopSitesNumber && IndexOf(*urls, themes_url) == -1) { - MostVisitedURL url( - themes_url, - GURL("chrome://theme/IDR_NEWTAB_THEMES_GALLERY_FAVICON"), - l10n_util::GetStringUTF16(IDS_NEW_TAB_THEMES_GALLERY_PAGE_TITLE)); - url.redirects.push_back(themes_url); - urls->push_back(url); - added = true; + MostVisitedURLList prepopulate_urls = GetPrepopulatePages(); + for (size_t i = 0; i < prepopulate_urls.size(); ++i) { + if (urls->size() < kTopSitesNumber && + IndexOf(*urls, prepopulate_urls[i].url) == -1) { + urls->push_back(prepopulate_urls[i]); + added = true; + } } - return added; } void TopSites::MigratePinnedURLs() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + std::map<GURL, size_t> tmp_map; for (DictionaryValue::key_iterator it = pinned_urls_->begin_keys(); it != pinned_urls_->end_keys(); ++it) { @@ -327,7 +614,6 @@ void TopSites::MigratePinnedURLs() { void TopSites::ApplyBlacklistAndPinnedURLs(const MostVisitedURLList& urls, MostVisitedURLList* out) { - lock_.AssertAcquired(); MostVisitedURLList urls_copy; for (size_t i = 0; i < urls.size(); i++) { if (!IsBlacklisted(urls[i].url)) @@ -375,521 +661,247 @@ void TopSites::ApplyBlacklistAndPinnedURLs(const MostVisitedURLList& urls, } std::string TopSites::GetURLString(const GURL& url) { - lock_.AssertAcquired(); - return GetCanonicalURL(url).spec(); + return cache_->GetCanonicalURL(url).spec(); } std::string TopSites::GetURLHash(const GURL& url) { - lock_.AssertAcquired(); // We don't use canonical URLs here to be able to blacklist only one of // the two 'duplicate' sites, e.g. 'gmail.com' and 'mail.google.com'. return MD5String(url.spec()); } -void TopSites::UpdateMostVisited(MostVisitedURLList most_visited) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); +base::TimeDelta TopSites::GetUpdateDelay() { + if (cache_->top_sites().size() <= arraysize(kPrepopulateTitleIDs)) + return base::TimeDelta::FromSeconds(30); - std::vector<size_t> added; // Indices into most_visited. - std::vector<size_t> deleted; // Indices into top_sites_. - std::vector<size_t> moved; // Indices into most_visited. + int64 range = kMaxUpdateIntervalMinutes - kMinUpdateIntervalMinutes; + int64 minutes = kMaxUpdateIntervalMinutes - + last_num_urls_changed_ * range / cache_->top_sites().size(); + return base::TimeDelta::FromMinutes(minutes); +} - DiffMostVisited(top_sites_, most_visited, &added, &deleted, &moved); +// static +void TopSites::ProcessPendingCallbacks( + const PendingCallbackSet& pending_callbacks, + const MostVisitedURLList& urls) { + PendingCallbackSet::const_iterator i; + for (i = pending_callbacks.begin(); + i != pending_callbacks.end(); ++i) { + scoped_refptr<CancelableRequest<GetTopSitesCallback> > request = *i; + if (!request->canceled()) + request->ForwardResult(GetTopSitesCallback::TupleType(urls)); + } +} - // #added == #deleted; #added + #moved = total. - last_num_urls_changed_ = added.size() + moved.size(); +void TopSites::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (!loaded_) + return; - { - AutoLock lock(lock_); + if (type == NotificationType::HISTORY_URLS_DELETED) { + Details<history::URLsDeletedDetails> deleted_details(details); + if (deleted_details->all_history) { + SetTopSites(MostVisitedURLList()); + backend_->ResetDatabase(); + } else { + std::set<size_t> indices_to_delete; // Indices into top_sites_. + for (std::set<GURL>::iterator i = deleted_details->urls.begin(); + i != deleted_details->urls.end(); ++i) { + if (cache_->IsKnownURL(*i)) + indices_to_delete.insert(cache_->GetURLIndex(*i)); + } - // Process the diff: delete from images and disk, add to disk. - // Delete all the thumbnails associated with URLs that were deleted. - for (size_t i = 0; i < deleted.size(); i++) { - const MostVisitedURL& deleted_url = top_sites_[deleted[i]]; - std::map<GURL, Images>::iterator found = - top_images_.find(deleted_url.url); - if (found != top_images_.end()) - top_images_.erase(found); - } - } + if (indices_to_delete.empty()) + return; - // Write the updates to the DB. - if (db_.get()) { - for (size_t i = 0; i < deleted.size(); i++) { - const MostVisitedURL& deleted_url = top_sites_[deleted[i]]; - if (db_.get()) - db_->RemoveURL(deleted_url); - } - for (size_t i = 0; i < added.size(); i++) { - const MostVisitedURL& added_url = most_visited[added[i]]; - db_->SetPageThumbnail(added_url, added[i], Images()); - } - for (size_t i = 0; i < moved.size(); i++) { - const MostVisitedURL& moved_url = most_visited[moved[i]]; - db_->UpdatePageRank(moved_url, moved[i]); + MostVisitedURLList new_top_sites(cache_->top_sites()); + for (std::set<size_t>::reverse_iterator i = indices_to_delete.rbegin(); + i != indices_to_delete.rend(); i++) { + size_t index = *i; + RemovePinnedURL(new_top_sites[index].url); + new_top_sites.erase(new_top_sites.begin() + index); + } + SetTopSites(new_top_sites); } - } - - StoreMostVisited(&most_visited); - if (migration_in_progress_) { - // Copy all thumnbails from the history service. - for (size_t i = 0; i < top_sites_.size(); i++) { - GURL& url = top_sites_[i].url; - Images& img = top_images_[url]; - if (!img.thumbnail.get() || !img.thumbnail->size()) { - StartQueryForThumbnail(i); + StartQueryForMostVisited(); + } else if (type == NotificationType::NAV_ENTRY_COMMITTED) { + if (cache_->top_sites().size() < kTopSitesNumber) { + NavigationController::LoadCommittedDetails* load_details = + Details<NavigationController::LoadCommittedDetails>(details).ptr(); + if (!load_details) + return; + const GURL& url = load_details->entry->url(); + if (!cache_->IsKnownURL(url) && HistoryService::CanAddURL(url)) { + // To avoid slamming history we throttle requests when the url updates. + // To do otherwise negatively impacts perf tests. + RestartQueryForTopSitesTimer(GetUpdateDelay()); } } } - - // If we are not expecting any thumbnails, migration is done. - if (migration_in_progress_ && migration_pending_urls_.empty()) - OnMigrationDone(); - - timer_.Stop(); - timer_.Start(GetUpdateDelay(), this, - &TopSites::StartQueryForMostVisited); } -void TopSites::OnMigrationDone() { - migration_in_progress_ = false; - if (!profile_) - return; +void TopSites::SetTopSites(const MostVisitedURLList& new_top_sites) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - // |hs| may be null during unit tests. - if (!hs) - return; - hs->OnTopSitesReady(); -} - -void TopSites::AddTemporaryThumbnail(const GURL& url, - const RefCountedBytes* thumbnail, - const ThumbnailScore& score) { - Images& img = temp_thumbnails_map_[url]; - img.thumbnail = const_cast<RefCountedBytes*>(thumbnail); - img.thumbnail_score = score; -} - -void TopSites::StartQueryForThumbnail(size_t index) { - DCHECK(migration_in_progress_); - if (top_sites_[index].url.spec() == - l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL) || - top_sites_[index].url.spec() == - l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)) - return; // Don't need thumbnails for prepopulated URLs. - - migration_pending_urls_.insert(top_sites_[index].url); - - if (mock_history_service_) { - // Testing with a mockup. - // QueryMostVisitedURLs is not virtual, so we have to duplicate the code. - // This calls SetClientData. - mock_history_service_->GetPageThumbnail( - top_sites_[index].url, - &cancelable_consumer_, - NewCallback(this, &TopSites::OnThumbnailAvailable), - index); - return; - } + MostVisitedURLList top_sites(new_top_sites); + AddPrepopulatedPages(&top_sites); - if (!profile_) - return; + TopSitesDelta delta; + DiffMostVisited(cache_->top_sites(), top_sites, &delta); + if (!delta.deleted.empty() || !delta.added.empty() || !delta.moved.empty()) + backend_->UpdateTopSites(delta); - HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - // |hs| may be null during unit tests. - if (!hs) - return; - HistoryService::Handle handle = - hs->GetPageThumbnail(top_sites_[index].url, - &cancelable_consumer_, - NewCallback(this, &TopSites::OnThumbnailAvailable)); - cancelable_consumer_.SetClientData(hs, handle, index); -} + last_num_urls_changed_ = delta.added.size() + delta.moved.size(); -void TopSites::GenerateCanonicalURLs() { - lock_.AssertAcquired(); - canonical_urls_.clear(); - for (size_t i = 0; i < top_sites_.size(); i++) { - const MostVisitedURL& mv = top_sites_[i]; - StoreRedirectChain(mv.redirects, i); - } -} + // We always do the following steps (setting top sites in cache, and resetting + // thread safe cache ...) as this method is invoked during startup at which + // point the caches haven't been updated yet. + cache_->SetTopSites(top_sites); -void TopSites::StoreMostVisited(MostVisitedURLList* most_visited) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - MostVisitedURLList filtered_urls; - PendingCallbackSet callbacks; - { - AutoLock lock(lock_); - top_sites_.clear(); - // Take ownership of the most visited data. - top_sites_.swap(*most_visited); - waiting_for_results_ = false; - - // Save the redirect information for quickly mapping to the canonical URLs. - GenerateCanonicalURLs(); - - for (size_t i = 0; i < top_sites_.size(); i++) { - const MostVisitedURL& mv = top_sites_[i]; - std::map<GURL, Images>::iterator it = temp_thumbnails_map_.begin(); - GURL canonical_url = GetCanonicalURL(mv.url); - for (; it != temp_thumbnails_map_.end(); it++) { - // Must map all temp URLs to canonical ones. - // temp_thumbnails_map_ contains non-canonical URLs, because - // when we add a temp thumbnail, redirect chain is not known. - // This is slow, but temp_thumbnails_map_ should have very few URLs. - if (canonical_url == GetCanonicalURL(it->first)) { - SetPageThumbnailEncoded(mv.url, it->second.thumbnail, + // See if we have any tmp thumbnails for the new sites. + if (!temp_images_.empty()) { + for (size_t i = 0; i < top_sites.size(); ++i) { + const MostVisitedURL& mv = top_sites[i]; + GURL canonical_url = cache_->GetCanonicalURL(mv.url); + // At the time we get the thumbnail redirects aren't known, so we have to + // iterate through all the images. + for (TempImages::iterator it = temp_images_.begin(); + it != temp_images_.end(); ++it) { + if (canonical_url == cache_->GetCanonicalURL(it->first)) { + SetPageThumbnailEncoded(mv.url, + it->second.thumbnail, it->second.thumbnail_score); - temp_thumbnails_map_.erase(it); + temp_images_.erase(it); break; } } } - if (top_sites_.size() >= kTopSitesNumber) - temp_thumbnails_map_.clear(); + } - if (pending_callbacks_.empty()) - return; + if (top_sites.size() >= kTopSitesNumber) + temp_images_.clear(); - ApplyBlacklistAndPinnedURLs(top_sites_, &filtered_urls); - callbacks.swap(pending_callbacks_); - } // lock_ is released. - // Process callbacks outside the lock - ForwardResults may cause - // thread switches. - ProcessPendingCallbacks(callbacks, filtered_urls); -} + ResetThreadSafeCache(); + ResetThreadSafeImageCache(); -void TopSites::StoreRedirectChain(const RedirectList& redirects, - size_t destination) { - lock_.AssertAcquired(); - if (redirects.empty()) { - NOTREACHED(); - return; - } - - // Map all the redirected URLs to the destination. - for (size_t i = 0; i < redirects.size(); i++) { - // If this redirect is already known, don't replace it with a new one. - if (canonical_urls_.find(redirects[i]) == canonical_urls_.end()) - canonical_urls_[redirects[i]] = destination; - } + // Restart the timer that queries history for top sites. This is done to + // ensure we stay in sync with history. + RestartQueryForTopSitesTimer(GetUpdateDelay()); } -GURL TopSites::GetCanonicalURL(const GURL& url) const { - lock_.AssertAcquired(); - std::map<GURL, size_t>::const_iterator found = canonical_urls_.find(url); - if (found == canonical_urls_.end()) - return url; // Unknown URL - return unchanged. - return top_sites_[found->second].url; -} +int TopSites::num_results_to_request_from_history() const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); -// static -int TopSites::GetRedirectDistanceForURL(const MostVisitedURL& most_visited, - const GURL& url) { - for (size_t i = 0; i < most_visited.redirects.size(); i++) { - if (most_visited.redirects[i] == url) - return static_cast<int>(most_visited.redirects.size() - i - 1); - } - NOTREACHED() << "URL should always be found."; - return 0; + return kTopSitesNumber + blacklist_->size(); } -// static -void TopSites::DiffMostVisited(const MostVisitedURLList& old_list, - const MostVisitedURLList& new_list, - std::vector<size_t>* added_urls, - std::vector<size_t>* deleted_urls, - std::vector<size_t>* moved_urls) { - added_urls->clear(); - deleted_urls->clear(); - moved_urls->clear(); - - // Add all the old URLs for quick lookup. This maps URLs to the corresponding - // index in the input. - std::map<GURL, size_t> all_old_urls; - for (size_t i = 0; i < old_list.size(); i++) - all_old_urls[old_list[i].url] = i; +void TopSites::MoveStateToLoaded() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - // Check all the URLs in the new set to see which ones are new or just moved. - // When we find a match in the old set, we'll reset its index to our special - // marker. This allows us to quickly identify the deleted ones in a later - // pass. - const size_t kAlreadyFoundMarker = static_cast<size_t>(-1); - for (size_t i = 0; i < new_list.size(); i++) { - std::map<GURL, size_t>::iterator found = all_old_urls.find(new_list[i].url); - if (found == all_old_urls.end()) { - added_urls->push_back(i); - } else { - if (found->second != i) - moved_urls->push_back(i); - found->second = kAlreadyFoundMarker; - } - } + MostVisitedURLList filtered_urls; + PendingCallbackSet pending_callbacks; + { + AutoLock lock(lock_); - // Any member without the special marker in the all_old_urls list means that - // there wasn't a "new" URL that mapped to it, so it was deleted. - for (std::map<GURL, size_t>::const_iterator i = all_old_urls.begin(); - i != all_old_urls.end(); ++i) { - if (i->second != kAlreadyFoundMarker) - deleted_urls->push_back(i->second); - } -} + if (loaded_) + return; // Don't do anything if we're already loaded. + loaded_ = true; -void TopSites::StartQueryForMostVisited() { - if (mock_history_service_) { - // Testing with a mockup. - // QueryMostVisitedURLs is not virtual, so we have to duplicate the code. - mock_history_service_->QueryMostVisitedURLs( - kTopSitesNumber + blacklist_->size(), - kDaysOfHistory, - &cancelable_consumer_, - NewCallback(this, &TopSites::OnTopSitesAvailable)); - } else { - if (!profile_) - return; - - HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - // |hs| may be null during unit tests. - if (hs) { - hs->QueryMostVisitedURLs( - kTopSitesNumber + blacklist_->size(), - kDaysOfHistory, - &cancelable_consumer_, - NewCallback(this, &TopSites::OnTopSitesAvailable)); - } else { - VLOG(1) << "History Service not available."; + // Now that we're loaded we can service the queued up callbacks. Copy them + // here and service them outside the lock. + if (!pending_callbacks_.empty()) { + filtered_urls = thread_safe_cache_->top_sites(); + pending_callbacks.swap(pending_callbacks_); } } -} -void TopSites::StartMigration() { - VLOG(1) << "Starting migration to TopSites."; - migration_in_progress_ = true; - StartQueryForMostVisited(); - MigratePinnedURLs(); -} + ProcessPendingCallbacks(pending_callbacks, filtered_urls); -bool TopSites::HasBlacklistedItems() const { - AutoLock lock(lock_); - return !blacklist_->empty(); + NotificationService::current()->Notify(NotificationType::TOP_SITES_LOADED, + Source<Profile>(profile_), + Details<TopSites>(this)); } -void TopSites::AddBlacklistedURL(const GURL& url) { +void TopSites::ResetThreadSafeCache() { AutoLock lock(lock_); - RemovePinnedURLLocked(url); - Value* dummy = Value::CreateNullValue(); - blacklist_->SetWithoutPathExpansion(GetURLHash(url), dummy); + MostVisitedURLList cached; + ApplyBlacklistAndPinnedURLs(cache_->top_sites(), &cached); + thread_safe_cache_->SetTopSites(cached); } -bool TopSites::IsBlacklisted(const GURL& url) { - lock_.AssertAcquired(); - bool result = blacklist_->HasKey(GetURLHash(url)); - return result; -} - -void TopSites::RemoveBlacklistedURL(const GURL& url) { +void TopSites::ResetThreadSafeImageCache() { AutoLock lock(lock_); - blacklist_->RemoveWithoutPathExpansion(GetURLHash(url), NULL); -} - -void TopSites::ClearBlacklistedURLs() { - blacklist_->Clear(); + thread_safe_cache_->SetThumbnails(cache_->images()); + thread_safe_cache_->RemoveUnreferencedThumbnails(); } -void TopSites::AddPinnedURL(const GURL& url, size_t pinned_index) { - GURL old; - if (GetPinnedURLAtIndex(pinned_index, &old)) { - RemovePinnedURL(old); - } - - if (IsURLPinned(url)) { - RemovePinnedURL(url); +void TopSites::RestartQueryForTopSitesTimer(base::TimeDelta delta) { + if (timer_.IsRunning() && ((timer_start_time_ + timer_.GetCurrentDelay()) < + (base::TimeTicks::Now() + delta))) { + return; } - Value* index = Value::CreateIntegerValue(pinned_index); - AutoLock lock(lock_); - pinned_urls_->SetWithoutPathExpansion(GetURLString(url), index); -} - -void TopSites::RemovePinnedURL(const GURL& url) { - AutoLock lock(lock_); - RemovePinnedURLLocked(url); -} - -void TopSites::RemovePinnedURLLocked(const GURL& url) { - lock_.AssertAcquired(); - pinned_urls_->RemoveWithoutPathExpansion(GetURLString(url), NULL); + timer_start_time_ = base::TimeTicks::Now(); + timer_.Stop(); + timer_.Start(delta, this, &TopSites::StartQueryForMostVisited); } -bool TopSites::IsURLPinned(const GURL& url) { - AutoLock lock(lock_); - int tmp; - bool result = pinned_urls_->GetIntegerWithoutPathExpansion( - GetURLString(url), &tmp); - return result; -} +void TopSites::OnHistoryMigrationWrittenToDisk(TopSitesBackend::Handle handle) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); -bool TopSites::GetPinnedURLAtIndex(size_t index, GURL* url) { - for (DictionaryValue::key_iterator it = pinned_urls_->begin_keys(); - it != pinned_urls_->end_keys(); ++it) { - int current_index; - if (pinned_urls_->GetIntegerWithoutPathExpansion(*it, ¤t_index)) { - if (static_cast<size_t>(current_index) == index) { - *url = GURL(*it); - return true; - } - } - } - return false; -} - -// static -void TopSites::DeleteTopSites(scoped_refptr<TopSites>& ptr) { - if (!ptr.get() || !MessageLoop::current()) + if (!profile_) return; - if (BrowserThread::IsWellKnownThread(BrowserThread::UI)) { - ptr = NULL; - } else { - // Need to roll our own UI thread. - BrowserThread ui_loop(BrowserThread::UI, MessageLoop::current()); - ptr = NULL; - MessageLoop::current()->RunAllPending(); - } -} - -void TopSites::ClearProfile() { - profile_ = NULL; -} - -base::TimeDelta TopSites::GetUpdateDelay() { - AutoLock lock(lock_); - if (top_sites_.size() == 0) - return base::TimeDelta::FromSeconds(30); - int64 range = kMaxUpdateIntervalMinutes - kMinUpdateIntervalMinutes; - int64 minutes = kMaxUpdateIntervalMinutes - - last_num_urls_changed_ * range / top_sites_.size(); - return base::TimeDelta::FromMinutes(minutes); + HistoryService* history = + profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); + if (history) + history->OnTopSitesReady(); } -void TopSites::OnTopSitesAvailable( +void TopSites::OnGotMostVisitedThumbnails( CancelableRequestProvider::Handle handle, - MostVisitedURLList pages) { - AddPrepopulatedPages(&pages); - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, NewRunnableMethod( - this, &TopSites::UpdateMostVisited, pages)); -} + scoped_refptr<MostVisitedThumbnails> data, + bool may_need_history_migration) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(top_sites_state_, TOP_SITES_LOADING); -// static -void TopSites::ProcessPendingCallbacks(PendingCallbackSet pending_callbacks, - const MostVisitedURLList& urls) { - PendingCallbackSet::iterator i; - for (i = pending_callbacks.begin(); - i != pending_callbacks.end(); ++i) { - scoped_refptr<CancelableRequest<GetTopSitesCallback> > request = *i; - if (!request->canceled()) - request->ForwardResult(GetTopSitesCallback::TupleType(urls)); - } - pending_callbacks.clear(); -} + if (!may_need_history_migration) { + top_sites_state_ = TOP_SITES_LOADED; -void TopSites::OnThumbnailAvailable(CancelableRequestProvider::Handle handle, - scoped_refptr<RefCountedBytes> thumbnail) { - size_t index; - if (mock_history_service_) { - index = handle; - } else { - if (!profile_) - return; + // Set the top sites directly in the cache so that SetTopSites diffs + // correctly. + cache_->SetTopSites(data->most_visited); + SetTopSites(data->most_visited); + cache_->SetThumbnails(data->url_to_images_map); - HistoryService* hs = profile_ ->GetHistoryService(Profile::EXPLICIT_ACCESS); - if (!hs) - return; - index = cancelable_consumer_.GetClientData(hs, handle); - } - DCHECK(static_cast<size_t>(index) < top_sites_.size()); + ResetThreadSafeImageCache(); - if (migration_in_progress_) - migration_pending_urls_.erase(top_sites_[index].url); + MoveStateToLoaded(); - if (thumbnail.get() && thumbnail->size()) { - const MostVisitedURL& url = top_sites_[index]; - AutoLock lock(lock_); - SetPageThumbnailEncoded(url.url, thumbnail, ThumbnailScore()); - } - - if (migration_in_progress_ && migration_pending_urls_.empty() && - !mock_history_service_) - OnMigrationDone(); -} - -void TopSites::SetMockHistoryService(MockHistoryService* mhs) { - mock_history_service_ = mhs; -} - -void TopSites::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - AutoLock lock(lock_); - if (type == NotificationType::HISTORY_URLS_DELETED) { - Details<history::URLsDeletedDetails> deleted_details(details); - if (deleted_details->all_history) { - top_sites_.clear(); - BrowserThread::PostTask( - BrowserThread::DB, FROM_HERE, - NewRunnableMethod(this, &TopSites::ResetDatabase)); + // Start a timer that refreshes top sites from history. + RestartQueryForTopSitesTimer( + base::TimeDelta::FromSeconds(kUpdateIntervalSecs)); + } else { + // The top sites file didn't exist or is the wrong version. We need to wait + // for history to finish loading to know if we really needed to migrate. + if (history_state_ == HISTORY_LOADED) { + top_sites_state_ = TOP_SITES_LOADED; + SetTopSites(MostVisitedURLList()); + MoveStateToLoaded(); } else { - std::set<size_t> indices_to_delete; // Indices into top_sites_. - std::set<GURL>::iterator it; - for (it = deleted_details->urls.begin(); - it != deleted_details->urls.end(); ++it) { - std::map<GURL,size_t>::const_iterator found = canonical_urls_.find(*it); - if (found != canonical_urls_.end()) - indices_to_delete.insert(found->second); - } - - for (std::set<size_t>::reverse_iterator i = indices_to_delete.rbegin(); - i != indices_to_delete.rend(); i++) { - size_t index = *i; - RemovePinnedURLLocked(top_sites_[index].url); - top_sites_.erase(top_sites_.begin() + index); - } - } - // Canonical URLs are not valid any more. - GenerateCanonicalURLs(); - StartQueryForMostVisited(); - } else if (type == NotificationType::NAV_ENTRY_COMMITTED) { - if (top_sites_.size() < kTopSitesNumber) { - NavigationController::LoadCommittedDetails* load_details = - Details<NavigationController::LoadCommittedDetails>(details).ptr(); - if (!load_details) - return; - GURL url = load_details->entry->url(); - if (canonical_urls_.find(url) == canonical_urls_.end() && - HistoryService::CanAddURL(url)) { - // Add this page to the known pages in case the thumbnail comes - // in before we get the results. - MostVisitedURL mv; - mv.url = url; - mv.redirects.push_back(url); - top_sites_.push_back(mv); - size_t index = top_sites_.size() - 1; - StoreRedirectChain(top_sites_[index].redirects, index); - } - StartQueryForMostVisited(); + top_sites_state_ = TOP_SITES_LOADED_WAITING_FOR_HISTORY; + // Ask for history just in case it hasn't been loaded yet. When history + // finishes loading we'll do migration and/or move to loaded. + profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); } } } -void TopSites::ResetDatabase() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - db_.reset(new TopSitesDatabaseImpl()); - file_util::Delete(db_path_, false); - if (!db_->Init(db_path_)) { - NOTREACHED() << "Failed to initialize database."; - return; - } +void TopSites::OnTopSitesAvailableFromHistory( + CancelableRequestProvider::Handle handle, + MostVisitedURLList pages) { + SetTopSites(pages); } } // namespace history diff --git a/chrome/browser/history/top_sites.h b/chrome/browser/history/top_sites.h index f080c05..bd406f1 100644 --- a/chrome/browser/history/top_sites.h +++ b/chrome/browser/history/top_sites.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -6,14 +6,14 @@ #define CHROME_BROWSER_HISTORY_TOP_SITES_H_ #pragma once -#include <map> +#include <list> #include <set> #include <string> -#include <vector> #include "base/basictypes.h" #include "base/gtest_prod_util.h" #include "base/lock.h" +#include "base/time.h" #include "base/timer.h" #include "base/ref_counted.h" #include "base/ref_counted_memory.h" @@ -27,30 +27,26 @@ #include "googleurl/src/gurl.h" class DictionaryValue; +class FilePath; class SkBitmap; class Profile; namespace history { +class TopSitesCache; class TopSitesBackend; -class TopSitesDatabase; class TopSitesTest; -typedef std::vector<MostVisitedURL> MostVisitedURLList; - // Stores the data for the top "most visited" sites. This includes a cache of // the most visited data from history, as well as the corresponding thumbnails // of those sites. // -// This class IS threadsafe. It is designed to be used from the UI thread of -// the browser (where history requests must be kicked off and received from) -// and from the I/O thread (where new tab page requests come in). Handling the -// new tab page requests on the I/O thread without proxying to the UI thread is -// a nontrivial performance win, especially when the browser is starting and -// the UI thread is busy. -class TopSites : - public base::RefCountedThreadSafe<TopSites, - BrowserThread::DeleteOnUIThread>, +// This class allows requests for most visited urls and thumbnails on any +// thread. All other methods must be invoked on the UI thread. All mutations +// to internal state happen on the UI thread and are scheduled to update the +// db using TopSitesBackend. +class TopSites + : public base::RefCountedThreadSafe<TopSites>, public NotificationObserver, public CancelableRequestProvider { public: @@ -59,21 +55,6 @@ class TopSites : // Returns whether top sites is enabled. static bool IsEnabled(); - class MockHistoryService { - // A mockup of a HistoryService used for testing TopSites. - public: - virtual HistoryService::Handle QueryMostVisitedURLs( - int result_count, int days_back, - CancelableRequestConsumerBase* consumer, - HistoryService::QueryMostVisitedURLsCallback* callback) = 0; - virtual ~MockHistoryService() {} - virtual void GetPageThumbnail( - const GURL& page_url, - CancelableRequestConsumerTSimple<size_t>* consumer, - HistoryService::ThumbnailDataCallback* callback, - size_t index) = 0; - }; - // Initializes TopSites. void Init(const FilePath& db_name); @@ -85,23 +66,33 @@ class TopSites : const ThumbnailScore& score); // Callback for GetMostVisitedURLs. - typedef Callback1<MostVisitedURLList>::Type GetTopSitesCallback; + typedef Callback1<const MostVisitedURLList&>::Type GetTopSitesCallback; typedef std::set<scoped_refptr<CancelableRequest<GetTopSitesCallback> > > PendingCallbackSet; // Returns a list of most visited URLs via a callback. + // This may be invoked on any thread. // NOTE: the callback may be called immediately if we have the data cached. void GetMostVisitedURLs(CancelableRequestConsumer* consumer, GetTopSitesCallback* callback); // Get a thumbnail for a given page. Returns true iff we have the thumbnail. - bool GetPageThumbnail(const GURL& url, RefCountedBytes** data) const; + // This may be invoked on any thread. + // As this method may be invoked on any thread the ref count needs to be + // upped before this method returns, so this takes a scoped_refptr*. + bool GetPageThumbnail(const GURL& url, + scoped_refptr<RefCountedBytes>* bytes); + + // Invoked from History if migration is needed. If this is invoked it will + // be before HistoryLoaded is invoked. + void MigrateFromHistory(); - // For testing with a HistoryService mock. - void SetMockHistoryService(MockHistoryService* mhs); + // Invoked with data from migrating thumbnails out of history. + void FinishHistoryMigration(const ThumbnailMigration& data); - // Start reading thumbnails from the ThumbnailDatabase. - void StartMigration(); + // Invoked from history when it finishes loading. If MigrateFromHistory was + // not invoked at this point then we load from the top sites service. + void HistoryLoaded(); // Blacklisted URLs @@ -114,6 +105,9 @@ class TopSites : // Removes a URL from the blacklist. void RemoveBlacklistedURL(const GURL& url); + // Returns true if the URL is blacklisted. + bool IsBlacklisted(const GURL& url); + // Clear the blacklist. void ClearBlacklistedURLs(); @@ -132,34 +126,53 @@ class TopSites : // is a URL pinned at |index|. bool GetPinnedURLAtIndex(size_t index, GURL* out); - // TopSites must be deleted on a UI thread. This happens - // automatically in a real browser, but in unit_tests we don't have - // a real UI thread. Use this function to delete a TopSites object. - static void DeleteTopSites(scoped_refptr<TopSites>& ptr); + // Shuts down top sites. + void Shutdown(); - // Sets the profile pointer to NULL. This is for the case where - // TopSites outlives the profile, since TopSites is refcounted. - void ClearProfile(); + // Generates the diff of things that happened between "old" and "new." + // + // The URLs that are in "new" but not "old" will be have their index into + // "new" put in |added_urls|. The URLs that are in "old" but not "new" will + // have their index into "old" put into |deleted_urls|. + // + // URLs appearing in both old and new lists but having different indices will + // have their index into "new" be put into |moved_urls|. + static void DiffMostVisited(const MostVisitedURLList& old_list, + const MostVisitedURLList& new_list, + TopSitesDelta* delta); private: - friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; - friend class DeleteTask<TopSites>; + friend class base::RefCountedThreadSafe<TopSites>; friend class TopSitesTest; - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, GetMostVisited); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, RealDatabase); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, MockDatabase); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, DeleteNotifications); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, PinnedURLsDeleted); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, GetUpdateDelay); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, Migration); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, QueueingRequestsForTopSites); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, CancelingRequestsForTopSites); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, AddTemporaryThumbnail); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, Blacklisting); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, PinnedURLs); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, BlacklistingAndPinnedURLs); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, AddPrepopulatedPages); - FRIEND_TEST_ALL_PREFIXES(TopSitesTest, GetPageThumbnail); + + typedef std::pair<GURL, Images> TempImage; + typedef std::list<TempImage> TempImages; + + // Enumeration of the possible states history can be in. + enum HistoryLoadState { + // We're waiting for history to finish loading. + HISTORY_LOADING, + + // History finished loading and we need to migrate top sites out of history. + HISTORY_MIGRATING, + + // History is loaded. + HISTORY_LOADED + }; + + // Enumeration of possible states the top sites backend can be in. + enum TopSitesLoadState { + // We're waiting for the backend to finish loading. + TOP_SITES_LOADING, + + // The backend finished loading, but we may need to migrate. This is true if + // the top sites db didn't exist, or if the db existed but is from an old + // version. + TOP_SITES_LOADED_WAITING_FOR_HISTORY, + + // Top sites is loaded. + TOP_SITES_LOADED + }; ~TopSites(); @@ -176,44 +189,22 @@ class TopSites : const RefCountedBytes* thumbnail, const ThumbnailScore& score); - // Query history service for the list of available thumbnails. - void StartQueryForMostVisited(); - - // Query history service for the thumbnail for a given url. |index| - // is the index into top_sites_. - void StartQueryForThumbnail(size_t index); - - // Called when history service returns a list of top URLs. - void OnTopSitesAvailable(CancelableRequestProvider::Handle handle, - MostVisitedURLList data); - - // Returns a list of urls to each pending callback. - static void ProcessPendingCallbacks(PendingCallbackSet pending_callbacks, - const MostVisitedURLList& urls); - - // Called when history service returns a thumbnail. - void OnThumbnailAvailable(CancelableRequestProvider::Handle handle, - scoped_refptr<RefCountedBytes> thumbnail); + // Encodes the bitmap to bytes for storage to the db. Returns true if the + // bitmap was successfully encoded. + static bool EncodeBitmap(const SkBitmap& bitmap, + scoped_refptr<RefCountedBytes>* bytes); - // Sets canonical_urls_ from top_sites_. - void GenerateCanonicalURLs(); + // Removes the cached thumbnail for url. Does nothing if |url| if not cached + // in |temp_images_|. + void RemoveTemporaryThumbnailByURL(const GURL& url); - // Saves the set of the top URLs visited by this user. The 0th item is the - // most popular. - // DANGER! This will clear all data from the input argument. - void StoreMostVisited(MostVisitedURLList* most_visited); - - // Saves the given set of redirects. The redirects are in order of the - // given vector, so [0] -> [1] -> [2]. - void StoreRedirectChain(const RedirectList& redirects, - size_t destination); + // Add a thumbnail for an unknown url. See temp_thumbnails_map_. + void AddTemporaryThumbnail(const GURL& url, + const RefCountedBytes* thumbnail, + const ThumbnailScore& score); - // Each item in the most visited view can redirect elsewhere. This returns - // the canonical URL one identifying the site if the given URL does appear - // in the "top sites" list. - // - // If the given URL is not in the top sites, this will return an empty GURL. - GURL GetCanonicalURL(const GURL& url) const; + // Query history service for the list of available thumbnails. + void StartQueryForMostVisited(); // Finds the given URL in the redirect chain for the given TopSite, and // returns the distance from the destination in hops that the given URL is. @@ -221,131 +212,114 @@ class TopSites : static int GetRedirectDistanceForURL(const MostVisitedURL& most_visited, const GURL& url); - // Generates the diff of things that happened between "old" and "new." - // - // The URLs that are in "new" but not "old" will be have their index into - // "new" put in |added_urls|. The URLs that are in "old" but not "new" will - // have their index into "old" put into |deleted_urls|. - // - // URLs appearing in both old and new lists but having different indices will - // have their index into "new" be put into |moved_urls|. - static void DiffMostVisited(const MostVisitedURLList& old_list, - const MostVisitedURLList& new_list, - std::vector<size_t>* added_urls, - std::vector<size_t>* deleted_urls, - std::vector<size_t>* moved_urls); + // Returns the set of prepopulate pages. + static MostVisitedURLList GetPrepopulatePages(); - // Implementation of NotificationObserver. - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); + // Add prepopulated pages: 'welcome to Chrome' and themes gallery to |urls|. + // Returns true if any pages were added. + static bool AddPrepopulatedPages(MostVisitedURLList* urls); - // Returns true if the URL is blacklisted. - bool IsBlacklisted(const GURL& url); + // Convert pinned_urls_ dictionary to the new format. Use URLs as + // dictionary keys. + void MigratePinnedURLs(); + + // Takes |urls|, produces it's copy in |out| after removing + // blacklisted URLs and reordering pinned URLs. + void ApplyBlacklistAndPinnedURLs(const MostVisitedURLList& urls, + MostVisitedURLList* out); - // A variant of RemovePinnedURL that must be called within a lock. - void RemovePinnedURLLocked(const GURL& url); + // Converts a url into a canonical string representation. + std::string GetURLString(const GURL& url); + + // Returns an MD5 hash of the URL. Hashing is required for blacklisted URLs. + std::string GetURLHash(const GURL& url); // Returns the delay until the next update of history is needed. // Uses num_urls_changed base::TimeDelta GetUpdateDelay(); - // The following methods must be run on the DB thread since they - // access the database. + // Executes all of the callbacks in |pending_callbacks|. This is used after + // we finish loading if any requests came in before we loaded. + static void ProcessPendingCallbacks( + const PendingCallbackSet& pending_callbacks, + const MostVisitedURLList& urls); - // Reads the database from disk. Called on startup to get the last - // known top sites. - void ReadDatabase(); + // Implementation of NotificationObserver. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); - // Write a thumbnail to database. - void WriteThumbnailToDB(const MostVisitedURL& url, - int url_rank, - const Images& thumbnail); + // Resets top_sites_ and updates the db (in the background). All mutations to + // top_sites_ *must* go through this. + void SetTopSites(const MostVisitedURLList& new_top_sites); - // Updates the top sites list and writes the difference to disk. - void UpdateMostVisited(MostVisitedURLList most_visited); + // Returns the number of most visted results to request from history. This + // changes depending upon how many urls have been blacklisted. + int num_results_to_request_from_history() const; - // Deletes the database file, then reinitializes the database. - void ResetDatabase(); + // Invoked when transitioning to LOADED. Notifies any queued up callbacks. + void MoveStateToLoaded(); - // Called after TopSites completes migration. - void OnMigrationDone(); + void ResetThreadSafeCache(); - // Add a thumbnail for an unknown url. See temp_thumbnails_map_. - void AddTemporaryThumbnail(const GURL& url, - const RefCountedBytes* thumbnail, - const ThumbnailScore& score); + void ResetThreadSafeImageCache(); - // Add prepopulated pages: 'welcome to Chrome' and themes gallery. - // Returns true if any pages were added. - bool AddPrepopulatedPages(MostVisitedURLList* urls); + // Stops and starts timer with a delay of |delta|. + void RestartQueryForTopSitesTimer(base::TimeDelta delta); - // Convert pinned_urls_ dictionary to the new format. Use URLs as - // dictionary keys. - void MigratePinnedURLs(); + // Callback after TopSitesBackend has finished migration. This tells history + // to finish it's side of migration (nuking thumbnails on disk). + void OnHistoryMigrationWrittenToDisk( + CancelableRequestProvider::Handle handle); - // Takes |urls|, produces it's copy in |out| after removing - // blacklisted URLs and reordering pinned URLs. - void ApplyBlacklistAndPinnedURLs(const MostVisitedURLList& urls, - MostVisitedURLList* out); + // Callback from TopSites with the top sites/thumbnails. + void OnGotMostVisitedThumbnails(CancelableRequestProvider::Handle handle, + scoped_refptr<MostVisitedThumbnails> data, + bool may_need_history_migration); - // Converts a url into a canonical string representation. - std::string GetURLString(const GURL& url); + // Called when history service returns a list of top URLs. + void OnTopSitesAvailableFromHistory(CancelableRequestProvider::Handle handle, + MostVisitedURLList data); - // Returns an MD5 hash of the URL. Hashing is required for blacklisted URLs. - std::string GetURLHash(const GURL& url); + scoped_refptr<TopSitesBackend> backend_; - Profile* profile_; - // A mockup to use for testing. If NULL, use the real HistoryService - // from the profile_. See SetMockHistoryService. - MockHistoryService* mock_history_service_; - CancelableRequestConsumerTSimple<size_t> cancelable_consumer_; - mutable Lock lock_; + // The top sites data. + scoped_ptr<TopSitesCache> cache_; - // The cached version of the top sites. The 0th item in this vector is the - // #1 site. - MostVisitedURLList top_sites_; + // Copy of the top sites data that may be accessed on any thread (assuming + // you hold |lock_|). The data in |thread_safe_cache_| has blacklisted and + // pinned urls applied (|cache_| does not). + scoped_ptr<TopSitesCache> thread_safe_cache_; - // The images corresponding to the top_sites. This is indexed by the URL of - // the top site, so this doesn't have to be shuffled around when the ordering - // changes of the top sites. Some top_sites_ entries may not have images. - std::map<GURL, Images> top_images_; + Profile* profile_; + + // Lock used to access |thread_safe_cache_|. + mutable Lock lock_; - // Generated from the redirects to and from the most visited pages, this - // maps the redirects to the index into top_sites_ that contains it. - std::map<GURL, size_t> canonical_urls_; + CancelableRequestConsumer cancelable_consumer_; - // Timer for updating TopSites data. + // Timer that asks history for the top sites. This is used to make sure our + // data stays in sync with history. base::OneShotTimer<TopSites> timer_; - scoped_ptr<TopSitesDatabase> db_; - FilePath db_path_; + // The time we started |timer_| at. Only valid if |timer_| is running. + base::TimeTicks timer_start_time_; NotificationRegistrar registrar_; // The number of URLs changed on the last update. size_t last_num_urls_changed_; - // Are we in the middle of migration from ThumbnailsDatabase to - // TopSites? - bool migration_in_progress_; - - // URLs for which we are expecting thumbnails. - std::set<GURL> migration_pending_urls_; - // The map of requests for the top sites list. Can only be // non-empty at startup. After we read the top sites from the DB, we'll // always have a cached list. PendingCallbackSet pending_callbacks_; - // Are we waiting for the top sites from HistoryService? - bool waiting_for_results_; - // Stores thumbnails for unknown pages. When SetPageThumbnail is // called, if we don't know about that URL yet and we don't have // enough Top Sites (new profile), we store it until the next - // UpdateMostVisitedURLs call. - std::map<GURL, Images> temp_thumbnails_map_; + // SetTopSites call. + TempImages temp_images_; // Blacklisted and pinned URLs are stored in Preferences. @@ -356,12 +330,20 @@ class TopSites : // PrefService. DictionaryValue* blacklist_; - // This is a dictionary for the pinned URLs for the the most visited - // part of the new tab page. Key is the URL, value is - // index where it is pinned at (may be the same as key). This is - // owned by the PrefService. + // This is a dictionary for the pinned URLs for the the most visited part of + // the new tab page. Key is the URL, value is index where it is pinned at (may + // be the same as key). This is owned by the PrefService. DictionaryValue* pinned_urls_; + // See description above HistoryLoadState. + HistoryLoadState history_state_; + + // See description above TopSitesLoadState. + TopSitesLoadState top_sites_state_; + + // Are we loaded? + bool loaded_; + DISALLOW_COPY_AND_ASSIGN(TopSites); }; diff --git a/chrome/browser/history/top_sites_backend.cc b/chrome/browser/history/top_sites_backend.cc new file mode 100644 index 0000000..eb27a5e --- /dev/null +++ b/chrome/browser/history/top_sites_backend.cc @@ -0,0 +1,153 @@ +// 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/history/top_sites_backend.h" + +#include "base/file_path.h" +#include "base/file_util.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/history/top_sites_database.h" + +namespace history { + +TopSitesBackend::TopSitesBackend() + : db_(new TopSitesDatabase()) { +} + +void TopSitesBackend::Init(const FilePath& path) { + db_path_ = path; + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, NewRunnableMethod( + this, &TopSitesBackend::InitDBOnDBThread, path)); +} + +void TopSitesBackend::Shutdown() { + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, NewRunnableMethod( + this, &TopSitesBackend::ShutdownDBOnDBThread)); +} + +TopSitesBackend::Handle TopSitesBackend::GetMostVisitedThumbnails( + CancelableRequestConsumerBase* consumer, + GetMostVisitedThumbnailsCallback* callback) { + GetMostVisitedThumbnailsRequest* request = + new GetMostVisitedThumbnailsRequest(callback); + request->value = new MostVisitedThumbnails; + AddRequest(request, consumer); + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, NewRunnableMethod( + this, + &TopSitesBackend::GetMostVisitedThumbnailsOnDBThread, + scoped_refptr<GetMostVisitedThumbnailsRequest>(request))); + return request->handle(); +} + +void TopSitesBackend::UpdateTopSites(const TopSitesDelta& delta) { + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, NewRunnableMethod( + this, &TopSitesBackend::UpdateTopSitesOnDBThread, delta)); +} + +void TopSitesBackend::SetPageThumbnail(const MostVisitedURL& url, + int url_rank, + const Images& thumbnail) { + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, NewRunnableMethod( + this, &TopSitesBackend::SetPageThumbnailOnDBThread, url, + url_rank, thumbnail)); +} + +void TopSitesBackend::ResetDatabase() { + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, NewRunnableMethod( + this, &TopSitesBackend::ResetDatabaseOnDBThread, db_path_)); +} + +TopSitesBackend::Handle TopSitesBackend::DoEmptyRequest( + CancelableRequestConsumerBase* consumer, + EmptyRequestCallback* callback) { + EmptyRequestRequest* request = new EmptyRequestRequest(callback); + AddRequest(request, consumer); + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, NewRunnableMethod( + this, + &TopSitesBackend::DoEmptyRequestOnDBThread, + scoped_refptr<EmptyRequestRequest>(request))); + return request->handle(); +} + +TopSitesBackend::~TopSitesBackend() { + DCHECK(!db_.get()); // Shutdown should have happened first (which results in + // nulling out db). +} + +void TopSitesBackend::InitDBOnDBThread(const FilePath& path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + if (!db_->Init(path)) { + NOTREACHED() << "Failed to initialize database."; + db_.reset(); + } +} + +void TopSitesBackend::ShutdownDBOnDBThread() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + db_.reset(); +} + +void TopSitesBackend::GetMostVisitedThumbnailsOnDBThread( + scoped_refptr<GetMostVisitedThumbnailsRequest> request) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + + if (request->canceled()) + return; + + bool may_need_history_migration = false; + if (db_.get()) { + db_->GetPageThumbnails(&(request->value->most_visited), + &(request->value->url_to_images_map)); + may_need_history_migration = db_->may_need_history_migration(); + } + request->ForwardResult(GetMostVisitedThumbnailsRequest::TupleType( + request->handle(), + request->value, + may_need_history_migration)); +} + +void TopSitesBackend::UpdateTopSitesOnDBThread(const TopSitesDelta& delta) { + if (!db_.get()) + return; + + for (size_t i = 0; i < delta.deleted.size(); ++i) + db_->RemoveURL(delta.deleted[i]); + + for (size_t i = 0; i < delta.added.size(); ++i) + db_->SetPageThumbnail(delta.added[i].url, delta.added[i].rank, Images()); + + for (size_t i = 0; i < delta.moved.size(); ++i) + db_->UpdatePageRank(delta.moved[i].url, delta.moved[i].rank); +} + +void TopSitesBackend::SetPageThumbnailOnDBThread(const MostVisitedURL& url, + int url_rank, + const Images& thumbnail) { + if (!db_.get()) + return; + + db_->SetPageThumbnail(url, url_rank, thumbnail); +} + +void TopSitesBackend::ResetDatabaseOnDBThread(const FilePath& file_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + db_.reset(NULL); + file_util::Delete(db_path_, false); + db_.reset(new TopSitesDatabase()); + InitDBOnDBThread(db_path_); +} + +void TopSitesBackend::DoEmptyRequestOnDBThread( + scoped_refptr<EmptyRequestRequest> request) { + request->ForwardResult(EmptyRequestRequest::TupleType(request->handle())); +} + +} // namespace history diff --git a/chrome/browser/history/top_sites_backend.h b/chrome/browser/history/top_sites_backend.h new file mode 100644 index 0000000..50d0867 --- /dev/null +++ b/chrome/browser/history/top_sites_backend.h @@ -0,0 +1,106 @@ +// 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_HISTORY_TOP_SITES_BACKEND_H_ +#define CHROME_BROWSER_HISTORY_TOP_SITES_BACKEND_H_ +#pragma once + +#include "base/file_path.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "chrome/browser/cancelable_request.h" +#include "chrome/browser/history/history_types.h" + +class FilePath; + +namespace history { + +class TopSitesDatabase; + +// Service used by TopSites to have db interaction happen on the DB thread. All +// public methods are invoked on the ui thread and get funneled to the DB +// thread. +class TopSitesBackend + : public base::RefCountedThreadSafe<TopSitesBackend>, + public CancelableRequestProvider { + public: + TopSitesBackend(); + + void Init(const FilePath& path); + + // Schedules the db to be shutdown. + void Shutdown(); + + // The boolean parameter indicates if the DB existed on disk or needs to be + // migrated. + typedef Callback3<Handle, scoped_refptr<MostVisitedThumbnails>, bool >::Type + GetMostVisitedThumbnailsCallback; + typedef CancelableRequest1<TopSitesBackend::GetMostVisitedThumbnailsCallback, + scoped_refptr<MostVisitedThumbnails> > + GetMostVisitedThumbnailsRequest; + + // Fetches MostVisitedThumbnails. + Handle GetMostVisitedThumbnails(CancelableRequestConsumerBase* consumer, + GetMostVisitedThumbnailsCallback* callback); + + // Updates top sites database from the specified delta. + void UpdateTopSites(const TopSitesDelta& delta); + + // Sets the thumbnail. + void SetPageThumbnail(const MostVisitedURL& url, + int url_rank, + const Images& thumbnail); + + // Deletes the database and recreates it. + void ResetDatabase(); + + typedef Callback1<Handle>::Type EmptyRequestCallback; + typedef CancelableRequest<TopSitesBackend::EmptyRequestCallback> + EmptyRequestRequest; + + // Schedules a request that does nothing on the DB thread, but then notifies + // the callback on the calling thread. This is used to make sure the db has + // finished processing a request. + Handle DoEmptyRequest(CancelableRequestConsumerBase* consumer, + EmptyRequestCallback* callback); + + private: + friend class base::RefCountedThreadSafe<TopSitesBackend>; + + ~TopSitesBackend(); + + // Invokes Init on the db_. + void InitDBOnDBThread(const FilePath& path); + + // Shuts down the db. + void ShutdownDBOnDBThread(); + + // Does the work of getting the most visted thumbnails. + void GetMostVisitedThumbnailsOnDBThread( + scoped_refptr<GetMostVisitedThumbnailsRequest> request); + + // Updates top sites. + void UpdateTopSitesOnDBThread(const TopSitesDelta& delta); + + // Sets the thumbnail. + void SetPageThumbnailOnDBThread(const MostVisitedURL& url, + int url_rank, + const Images& thumbnail); + + // Resets the database. + void ResetDatabaseOnDBThread(const FilePath& file_path); + + // Notifies the request. + void DoEmptyRequestOnDBThread(scoped_refptr<EmptyRequestRequest> request); + + FilePath db_path_; + + scoped_ptr<TopSitesDatabase> db_; + + DISALLOW_COPY_AND_ASSIGN(TopSitesBackend); +}; + +} // namespace history + +#endif // CHROME_BROWSER_HISTORY_TOP_SITES_BACKEND_H_ diff --git a/chrome/browser/history/top_sites_cache.cc b/chrome/browser/history/top_sites_cache.cc new file mode 100644 index 0000000..828b701 --- /dev/null +++ b/chrome/browser/history/top_sites_cache.cc @@ -0,0 +1,110 @@ +// 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/history/top_sites_cache.h" + +#include "base/logging.h" +#include "base/ref_counted_memory.h" + +namespace history { + +TopSitesCache::TopSitesCache() { +} + +TopSitesCache::~TopSitesCache() { +} + +void TopSitesCache::SetTopSites(const MostVisitedURLList& top_sites) { + top_sites_ = top_sites; + GenerateCanonicalURLs(); +} + +void TopSitesCache::SetThumbnails(const URLToImagesMap& images) { + images_ = images; +} + +void TopSitesCache::SetPageThumbnail(const GURL& url, + RefCountedBytes* thumbnail, + const ThumbnailScore& score) { + Images& img = images_[GetCanonicalURL(url)]; + img.thumbnail = thumbnail; + img.thumbnail_score = score; +} + +Images* TopSitesCache::GetImage(const GURL& url) { + return &images_[GetCanonicalURL(url)]; +} + +bool TopSitesCache::GetPageThumbnail(const GURL& url, + scoped_refptr<RefCountedBytes>* bytes) { + std::map<GURL, Images>::const_iterator found = + images_.find(GetCanonicalURL(url)); + if (found != images_.end()) { + *bytes = found->second.thumbnail.get(); + return true; + } + return false; +} + +GURL TopSitesCache::GetCanonicalURL(const GURL& url) { + CanonicalURLs::iterator i = TopSitesCache::GetCanonicalURLsIterator(url); + return i == canonical_urls_.end() ? url : i->first.first->url; +} + +bool TopSitesCache::IsKnownURL(const GURL& url) { + return GetCanonicalURLsIterator(url) != canonical_urls_.end(); +} + +size_t TopSitesCache::GetURLIndex(const GURL& url) { + DCHECK(IsKnownURL(url)); + return GetCanonicalURLsIterator(url)->second; +} + +void TopSitesCache::RemoveUnreferencedThumbnails() { + for (URLToImagesMap::iterator i = images_.begin(); i != images_.end(); ) { + if (IsKnownURL(i->first)) { + ++i; + } else { + URLToImagesMap::iterator next_i = i; + ++next_i; + images_.erase(i); + i = next_i; + } + } +} + +void TopSitesCache::GenerateCanonicalURLs() { + canonical_urls_.clear(); + for (size_t i = 0; i < top_sites_.size(); i++) + StoreRedirectChain(top_sites_[i].redirects, i); +} + +void TopSitesCache::StoreRedirectChain(const RedirectList& redirects, + size_t destination) { + // redirects is empty if the user pinned a site and there are not enough top + // sites before the pinned site. + + // Map all the redirected URLs to the destination. + for (size_t i = 0; i < redirects.size(); i++) { + // If this redirect is already known, don't replace it with a new one. + if (!IsKnownURL(redirects[i])) { + CanonicalURLEntry entry; + entry.first = &(top_sites_[destination]); + entry.second = i; + canonical_urls_[entry] = destination; + } + } +} + +TopSitesCache::CanonicalURLs::iterator TopSitesCache::GetCanonicalURLsIterator( + const GURL& url) { + MostVisitedURL most_visited_url; + most_visited_url.redirects.push_back(url); + CanonicalURLEntry entry; + entry.first = &most_visited_url; + entry.second = 0u; + return canonical_urls_.find(entry); +} + +} // namespace history diff --git a/chrome/browser/history/top_sites_cache.h b/chrome/browser/history/top_sites_cache.h new file mode 100644 index 0000000..4c5d79a --- /dev/null +++ b/chrome/browser/history/top_sites_cache.h @@ -0,0 +1,108 @@ +// 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_HISTORY_TOP_SITES_CACHE_H_ +#define CHROME_BROWSER_HISTORY_TOP_SITES_CACHE_H_ +#pragma once + +#include <algorithm> +#include <map> +#include <string> + +#include "base/ref_counted.h" +#include "chrome/browser/history/history_types.h" + +class RefCountedBytes; + +namespace history { + +// TopSitesCache caches the top sites and thumbnails for TopSites. +class TopSitesCache { + public: + TopSitesCache(); + ~TopSitesCache(); + + // The top sites. + void SetTopSites(const MostVisitedURLList& top_sites); + const MostVisitedURLList& top_sites() const { return top_sites_; } + + // The thumbnails. + void SetThumbnails(const URLToImagesMap& images); + const URLToImagesMap& images() const { return images_; } + + // Set a thumbnail. + void SetPageThumbnail(const GURL& url, + RefCountedBytes* thumbnail, + const ThumbnailScore& score); + + // Returns the thumbnail as an Image for the specified url. This adds an entry + // for |url| if one has not yet been added. + Images* GetImage(const GURL& url); + + // Fetches the thumbnail for the specified url. Returns true if there is a + // thumbnail for the specified url. + bool GetPageThumbnail(const GURL& url, + scoped_refptr<RefCountedBytes>* bytes); + + // Returns the canonical URL for |url|. + GURL GetCanonicalURL(const GURL& url); + + // Returns true if |url| is known. + bool IsKnownURL(const GURL& url); + + // Returns the index into |top_sites_| for |url|. + size_t GetURLIndex(const GURL& url); + + // Removes any thumbnails that are no longer referenced by the top sites. + void RemoveUnreferencedThumbnails(); + + private: + // The entries in CanonicalURLs, see CanonicalURLs for details. The second + // argument gives the index of the URL into MostVisitedURLs redirects. + typedef std::pair<MostVisitedURL*, size_t> CanonicalURLEntry; + + // Comparator used for CanonicalURLs. + class CanonicalURLComparator { + public: + bool operator()(const CanonicalURLEntry& e1, + const CanonicalURLEntry& e2) const { + return e1.first->redirects[e1.second] < e2.first->redirects[e2.second]; + } + }; + + // This is used to map from redirect url to the MostVisitedURL the redirect is + // from. Ideally this would be map<GURL, size_t> (second param indexing into + // top_sites_), but this results in duplicating all redirect urls. As some + // sites have a lot of redirects, we instead use the MostVisitedURL* and the + // index of the redirect as the key, and the index into top_sites_ as the + // value. This way we aren't duplicating GURLs. CanonicalURLComparator + // enforces the ordering as if we were using GURLs. + typedef std::map<CanonicalURLEntry, size_t, + CanonicalURLComparator> CanonicalURLs; + + // Generates the set of canonical urls from |top_sites_|. + void GenerateCanonicalURLs(); + + // Stores a set of redirects. This is used by GenerateCanonicalURLs. + void StoreRedirectChain(const RedirectList& redirects, size_t destination); + + // Returns the iterator into canconical_urls_ for the specified url. + CanonicalURLs::iterator GetCanonicalURLsIterator(const GURL& url); + + // The top sites. + MostVisitedURLList top_sites_; + + // The images. These map from canonical url to image. + URLToImagesMap images_; + + // Generated from the redirects to and from the most visited pages. See + // description above typedef for details. + CanonicalURLs canonical_urls_; + + DISALLOW_COPY_AND_ASSIGN(TopSitesCache); +}; + +} // namespace history + +#endif // CHROME_BROWSER_HISTORY_TOP_SITES_CACHE_H_ diff --git a/chrome/browser/history/top_sites_database.cc b/chrome/browser/history/top_sites_database.cc index 05a588a..8bfbcaf 100644 --- a/chrome/browser/history/top_sites_database.cc +++ b/chrome/browser/history/top_sites_database.cc @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "app/sql/connection.h" #include "app/sql/transaction.h" +#include "base/file_util.h" #include "base/string_util.h" #include "chrome/browser/diagnostics/sqlite_diagnostics.h" #include "chrome/browser/history/history_types.h" @@ -11,56 +13,85 @@ namespace history { -TopSitesDatabaseImpl::TopSitesDatabaseImpl() { +static const int kVersionNumber = 1; + +TopSitesDatabase::TopSitesDatabase() : may_need_history_migration_(false) { } -TopSitesDatabaseImpl::~TopSitesDatabaseImpl() { +TopSitesDatabase::~TopSitesDatabase() { } -bool TopSitesDatabaseImpl::Init(const FilePath& db_name) { - // Settings copied from ThumbnailDatabase. - db_.set_error_delegate(GetErrorHandlerForThumbnailDb()); - db_.set_page_size(4096); - db_.set_cache_size(64); +bool TopSitesDatabase::Init(const FilePath& db_name) { + bool file_existed = file_util::PathExists(db_name); + + if (!file_existed) + may_need_history_migration_ = true; - if (!db_.Open(db_name)) { - LOG(WARNING) << db_.GetErrorMessage(); + db_.reset(CreateDB(db_name)); + if (!db_.get()) return false; + + bool does_meta_exist = sql::MetaTable::DoesTableExist(db_.get()); + if (!does_meta_exist && file_existed) { + may_need_history_migration_ = true; + + // If the meta file doesn't exist, this version is old. We could remove all + // the entries as they are no longer applicable, but it's safest to just + // remove the file and start over. + db_.reset(NULL); + if (!file_util::Delete(db_name, false) && + !file_util::Delete(db_name, false)) { + // Try to delete twice. If we can't, fail. + LOG(ERROR) << "unable to delete old TopSites file"; + return false; + } + db_.reset(CreateDB(db_name)); + if (!db_.get()) + return false; } - return InitThumbnailTable(); + if (!meta_table_.Init(db_.get(), kVersionNumber, kVersionNumber)) + return false; + + if (!InitThumbnailTable()) + return false; + + // Version check. + if (meta_table_.GetVersionNumber() != kVersionNumber) + return false; + + return true; } -bool TopSitesDatabaseImpl::InitThumbnailTable() { - if (!db_.DoesTableExist("thumbnails")) { - if (!db_.Execute("CREATE TABLE thumbnails (" - "url LONGVARCHAR PRIMARY KEY," - "url_rank INTEGER ," - "title LONGVARCHAR," - "thumbnail BLOB," - "redirects LONGVARCHAR," - "boring_score DOUBLE DEFAULT 1.0, " - "good_clipping INTEGER DEFAULT 0, " - "at_top INTEGER DEFAULT 0, " - "last_updated INTEGER DEFAULT 0) ")) { - LOG(WARNING) << db_.GetErrorMessage(); +bool TopSitesDatabase::InitThumbnailTable() { + if (!db_->DoesTableExist("thumbnails")) { + if (!db_->Execute("CREATE TABLE thumbnails (" + "url LONGVARCHAR PRIMARY KEY," + "url_rank INTEGER ," + "title LONGVARCHAR," + "thumbnail BLOB," + "redirects LONGVARCHAR," + "boring_score DOUBLE DEFAULT 1.0, " + "good_clipping INTEGER DEFAULT 0, " + "at_top INTEGER DEFAULT 0, " + "last_updated INTEGER DEFAULT 0) ")) { + LOG(WARNING) << db_->GetErrorMessage(); return false; } } return true; } -void TopSitesDatabaseImpl::GetPageThumbnails(MostVisitedURLList* urls, - std::map<GURL, - Images>* thumbnails) { - sql::Statement statement(db_.GetCachedStatement( +void TopSitesDatabase::GetPageThumbnails(MostVisitedURLList* urls, + URLToImagesMap* thumbnails) { + sql::Statement statement(db_->GetCachedStatement( SQL_FROM_HERE, "SELECT url, url_rank, title, thumbnail, redirects, " "boring_score, good_clipping, at_top, last_updated " "FROM thumbnails ORDER BY url_rank ")); if (!statement) { - LOG(WARNING) << db_.GetErrorMessage(); + LOG(WARNING) << db_->GetErrorMessage(); return; } @@ -92,7 +123,7 @@ void TopSitesDatabaseImpl::GetPageThumbnails(MostVisitedURLList* urls, } // static -std::string TopSitesDatabaseImpl::GetRedirects(const MostVisitedURL& url) { +std::string TopSitesDatabase::GetRedirects(const MostVisitedURL& url) { std::vector<std::string> redirects; for (size_t i = 0; i < url.redirects.size(); i++) redirects.push_back(url.redirects[i].spec()); @@ -100,18 +131,18 @@ std::string TopSitesDatabaseImpl::GetRedirects(const MostVisitedURL& url) { } // static -void TopSitesDatabaseImpl::SetRedirects(const std::string& redirects, - MostVisitedURL* url) { +void TopSitesDatabase::SetRedirects(const std::string& redirects, + MostVisitedURL* url) { std::vector<std::string> redirects_vector; SplitStringAlongWhitespace(redirects, &redirects_vector); for (size_t i = 0; i < redirects_vector.size(); i++) url->redirects.push_back(GURL(redirects_vector[i])); } -void TopSitesDatabaseImpl::SetPageThumbnail(const MostVisitedURL& url, +void TopSitesDatabase::SetPageThumbnail(const MostVisitedURL& url, int new_rank, const Images& thumbnail) { - sql::Transaction transaction(&db_); + sql::Transaction transaction(db_.get()); transaction.Begin(); int rank = GetURLRank(url); @@ -125,9 +156,9 @@ void TopSitesDatabaseImpl::SetPageThumbnail(const MostVisitedURL& url, transaction.Commit(); } -void TopSitesDatabaseImpl::UpdatePageThumbnail( +void TopSitesDatabase::UpdatePageThumbnail( const MostVisitedURL& url, const Images& thumbnail) { - sql::Statement statement(db_.GetCachedStatement( + sql::Statement statement(db_->GetCachedStatement( SQL_FROM_HERE, "UPDATE thumbnails SET " "title = ?, thumbnail = ?, redirects = ?, " @@ -137,9 +168,9 @@ void TopSitesDatabaseImpl::UpdatePageThumbnail( return; statement.BindString16(0, url.title); - if (thumbnail.thumbnail.get()) { - statement.BindBlob(1, &thumbnail.thumbnail->data.front(), - static_cast<int>(thumbnail.thumbnail->data.size())); + if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) { + statement.BindBlob(1, thumbnail.thumbnail->front(), + static_cast<int>(thumbnail.thumbnail->size())); } statement.BindString(2, GetRedirects(url)); const ThumbnailScore& score = thumbnail.thumbnail_score; @@ -149,15 +180,15 @@ void TopSitesDatabaseImpl::UpdatePageThumbnail( statement.BindInt64(6, score.time_at_snapshot.ToInternalValue()); statement.BindString(7, url.url.spec()); if (!statement.Run()) - NOTREACHED() << db_.GetErrorMessage(); + NOTREACHED() << db_->GetErrorMessage(); } -void TopSitesDatabaseImpl::AddPageThumbnail(const MostVisitedURL& url, +void TopSitesDatabase::AddPageThumbnail(const MostVisitedURL& url, int new_rank, const Images& thumbnail) { int count = GetRowCount(); - sql::Statement statement(db_.GetCachedStatement( + sql::Statement statement(db_->GetCachedStatement( SQL_FROM_HERE, "INSERT OR REPLACE INTO thumbnails " "(url, url_rank, title, thumbnail, redirects, " @@ -169,9 +200,9 @@ void TopSitesDatabaseImpl::AddPageThumbnail(const MostVisitedURL& url, statement.BindString(0, url.url.spec()); statement.BindInt(1, count); // Make it the last url. statement.BindString16(2, url.title); - if (thumbnail.thumbnail.get()) { - statement.BindBlob(3, &thumbnail.thumbnail->data.front(), - static_cast<int>(thumbnail.thumbnail->data.size())); + if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) { + statement.BindBlob(3, thumbnail.thumbnail->front(), + static_cast<int>(thumbnail.thumbnail->size())); } statement.BindString(4, GetRedirects(url)); const ThumbnailScore& score = thumbnail.thumbnail_score; @@ -180,21 +211,21 @@ void TopSitesDatabaseImpl::AddPageThumbnail(const MostVisitedURL& url, statement.BindBool(7, score.at_top); statement.BindInt64(8, score.time_at_snapshot.ToInternalValue()); if (!statement.Run()) - NOTREACHED() << db_.GetErrorMessage(); + NOTREACHED() << db_->GetErrorMessage(); UpdatePageRankNoTransaction(url, new_rank); } -void TopSitesDatabaseImpl::UpdatePageRank(const MostVisitedURL& url, +void TopSitesDatabase::UpdatePageRank(const MostVisitedURL& url, int new_rank) { - sql::Transaction transaction(&db_); + sql::Transaction transaction(db_.get()); transaction.Begin(); UpdatePageRankNoTransaction(url, new_rank); transaction.Commit(); } // Caller should have a transaction open. -void TopSitesDatabaseImpl::UpdatePageRankNoTransaction( +void TopSitesDatabase::UpdatePageRankNoTransaction( const MostVisitedURL& url, int new_rank) { int prev_rank = GetURLRank(url); if (prev_rank == -1) { @@ -205,7 +236,7 @@ void TopSitesDatabaseImpl::UpdatePageRankNoTransaction( // Shift the ranks. if (prev_rank > new_rank) { // Shift up - sql::Statement shift_statement(db_.GetCachedStatement( + sql::Statement shift_statement(db_->GetCachedStatement( SQL_FROM_HERE, "UPDATE thumbnails " "SET url_rank = url_rank + 1 " @@ -216,7 +247,7 @@ void TopSitesDatabaseImpl::UpdatePageRankNoTransaction( shift_statement.Run(); } else if (prev_rank < new_rank) { // Shift down - sql::Statement shift_statement(db_.GetCachedStatement( + sql::Statement shift_statement(db_->GetCachedStatement( SQL_FROM_HERE, "UPDATE thumbnails " "SET url_rank = url_rank - 1 " @@ -228,7 +259,7 @@ void TopSitesDatabaseImpl::UpdatePageRankNoTransaction( } // Set the url's rank. - sql::Statement set_statement(db_.GetCachedStatement( + sql::Statement set_statement(db_->GetCachedStatement( SQL_FROM_HERE, "UPDATE thumbnails " "SET url_rank = ? " @@ -239,15 +270,15 @@ void TopSitesDatabaseImpl::UpdatePageRankNoTransaction( set_statement.Run(); } -bool TopSitesDatabaseImpl::GetPageThumbnail(const GURL& url, +bool TopSitesDatabase::GetPageThumbnail(const GURL& url, Images* thumbnail) { - sql::Statement statement(db_.GetCachedStatement( + sql::Statement statement(db_->GetCachedStatement( SQL_FROM_HERE, "SELECT thumbnail, boring_score, good_clipping, at_top, last_updated " "FROM thumbnails WHERE url=?")); if (!statement) { - LOG(WARNING) << db_.GetErrorMessage(); + LOG(WARNING) << db_->GetErrorMessage(); return false; } @@ -266,13 +297,13 @@ bool TopSitesDatabaseImpl::GetPageThumbnail(const GURL& url, return true; } -int TopSitesDatabaseImpl::GetRowCount() { +int TopSitesDatabase::GetRowCount() { int result = 0; - sql::Statement select_statement(db_.GetCachedStatement( + sql::Statement select_statement(db_->GetCachedStatement( SQL_FROM_HERE, "SELECT COUNT (url) FROM thumbnails")); if (!select_statement) { - LOG(WARNING) << db_.GetErrorMessage(); + LOG(WARNING) << db_->GetErrorMessage(); return result; } @@ -282,14 +313,14 @@ int TopSitesDatabaseImpl::GetRowCount() { return result; } -int TopSitesDatabaseImpl::GetURLRank(const MostVisitedURL& url) { +int TopSitesDatabase::GetURLRank(const MostVisitedURL& url) { int result = -1; - sql::Statement select_statement(db_.GetCachedStatement( + sql::Statement select_statement(db_->GetCachedStatement( SQL_FROM_HERE, "SELECT url_rank " "FROM thumbnails WHERE url=?")); if (!select_statement) { - LOG(WARNING) << db_.GetErrorMessage(); + LOG(WARNING) << db_->GetErrorMessage(); return result; } @@ -301,15 +332,15 @@ int TopSitesDatabaseImpl::GetURLRank(const MostVisitedURL& url) { } // Remove the record for this URL. Returns true iff removed successfully. -bool TopSitesDatabaseImpl::RemoveURL(const MostVisitedURL& url) { +bool TopSitesDatabase::RemoveURL(const MostVisitedURL& url) { int old_rank = GetURLRank(url); if (old_rank < 0) return false; - sql::Transaction transaction(&db_); + sql::Transaction transaction(db_.get()); transaction.Begin(); // Decrement all following ranks. - sql::Statement shift_statement(db_.GetCachedStatement( + sql::Statement shift_statement(db_->GetCachedStatement( SQL_FROM_HERE, "UPDATE thumbnails " "SET url_rank = url_rank - 1 " @@ -320,8 +351,8 @@ bool TopSitesDatabaseImpl::RemoveURL(const MostVisitedURL& url) { shift_statement.Run(); sql::Statement delete_statement( - db_.GetCachedStatement(SQL_FROM_HERE, - "DELETE FROM thumbnails WHERE url = ?")); + db_->GetCachedStatement(SQL_FROM_HERE, + "DELETE FROM thumbnails WHERE url = ?")); if (!delete_statement) return false; delete_statement.BindString(0, url.url.spec()); @@ -330,4 +361,19 @@ bool TopSitesDatabaseImpl::RemoveURL(const MostVisitedURL& url) { return transaction.Commit(); } +sql::Connection* TopSitesDatabase::CreateDB(const FilePath& db_name) { + scoped_ptr<sql::Connection> db(new sql::Connection()); + // Settings copied from ThumbnailDatabase. + db->set_error_delegate(GetErrorHandlerForThumbnailDb()); + db->set_page_size(4096); + db->set_cache_size(32); + + if (!db->Open(db_name)) { + LOG(ERROR) << db->GetErrorMessage(); + return NULL; + } + + return db.release(); +} + } // namespace history diff --git a/chrome/browser/history/top_sites_database.h b/chrome/browser/history/top_sites_database.h index edfb4e5..8450889 100644 --- a/chrome/browser/history/top_sites_database.h +++ b/chrome/browser/history/top_sites_database.h @@ -9,94 +9,61 @@ #include <map> #include <string> -#include "app/sql/connection.h" +#include "app/sql/meta_table.h" #include "base/ref_counted.h" +#include "chrome/browser/history/history_types.h" #include "chrome/browser/history/url_database.h" // For DBCloseScoper. class FilePath; class RefCountedMemory; class SkBitmap; -class Images; -namespace base { -class Time; +namespace app { +class Connection; } namespace history { -// Interface to be implemented by the real storage layer as well as -// the mockup database for testing. class TopSitesDatabase { public: - virtual ~TopSitesDatabase() {} - virtual bool Init(const FilePath& filename) { - return true; - } - - // Returns a list of all URLs currently in the table. - virtual void GetPageThumbnails(MostVisitedURLList* urls, - std::map<GURL, - Images>* thumbnails) = 0; - - // Set a thumbnail for a URL. |url_rank| is the position of the URL - // in the list of TopURLs, zero-based. - // If the URL is not in the table, add it. If it is, replace its - // thumbnail. - virtual void SetPageThumbnail(const MostVisitedURL& url, - int url_rank, - const Images& thumbnail) = 0; - - // Update rank of a URL that's already in the database. - virtual void UpdatePageRank(const MostVisitedURL& url, int new_rank) = 0; - - // Convenience wrapper. - bool GetPageThumbnail(const MostVisitedURL& url, - Images* thumbnail) { - return GetPageThumbnail(url.url, thumbnail); - } - - // Get a thumbnail for a given page. Returns true iff we have the thumbnail. - virtual bool GetPageThumbnail(const GURL& url, - Images* thumbnail) = 0; - - // Remove the record for this URL. Returns true iff removed successfully. - virtual bool RemoveURL(const MostVisitedURL& url) = 0; -}; - -class TopSitesDatabaseImpl : public TopSitesDatabase { - public: - TopSitesDatabaseImpl(); - virtual ~TopSitesDatabaseImpl(); + TopSitesDatabase(); + ~TopSitesDatabase(); // Must be called after creation but before any other methods are called. // Returns true on success. If false, no other functions should be called. - virtual bool Init(const FilePath& db_name); + bool Init(const FilePath& db_name); + + // Returns true if migration of top sites from history may be needed. A value + // of true means either migration is definitely needed (the top sites file is + // old) or doesn't exist (as would happen for a new user). + bool may_need_history_migration() const { + return may_need_history_migration_; + } // Thumbnails ---------------------------------------------------------------- // Returns a list of all URLs currently in the table. // WARNING: clears both input arguments. - virtual void GetPageThumbnails(MostVisitedURLList* urls, - std::map<GURL, Images>* thumbnails); + void GetPageThumbnails(MostVisitedURLList* urls, + std::map<GURL, Images>* thumbnails); // Set a thumbnail for a URL. |url_rank| is the position of the URL // in the list of TopURLs, zero-based. // If the URL is not in the table, add it. If it is, replace its // thumbnail and rank. Shift the ranks of other URLs if necessary. - virtual void SetPageThumbnail(const MostVisitedURL& url, - int new_rank, - const Images& thumbnail); + void SetPageThumbnail(const MostVisitedURL& url, + int new_rank, + const Images& thumbnail); // Sets the rank for a given URL. The URL must be in the database. // Use SetPageThumbnail if it's not. - virtual void UpdatePageRank(const MostVisitedURL& url, int new_rank); + void UpdatePageRank(const MostVisitedURL& url, int new_rank); // Get a thumbnail for a given page. Returns true iff we have the thumbnail. - virtual bool GetPageThumbnail(const GURL& url, - Images* thumbnail); + bool GetPageThumbnail(const GURL& url, Images* thumbnail); // Remove the record for this URL. Returns true iff removed successfully. - virtual bool RemoveURL(const MostVisitedURL& url); + bool RemoveURL(const MostVisitedURL& url); private: // Creates the thumbnail table, returning true if the table already exists @@ -121,13 +88,21 @@ class TopSitesDatabaseImpl : public TopSitesDatabase { // Returns the number of URLs (rows) in the database. int GetRowCount(); + sql::Connection* CreateDB(const FilePath& db_name); + // Encodes redirects into a string. static std::string GetRedirects(const MostVisitedURL& url); // Decodes redirects from a string and sets them for the url. static void SetRedirects(const std::string& redirects, MostVisitedURL* url); - sql::Connection db_; + scoped_ptr<sql::Connection> db_; + sql::MetaTable meta_table_; + + // See description above class. + bool may_need_history_migration_; + + DISALLOW_COPY_AND_ASSIGN(TopSitesDatabase); }; } // namespace history diff --git a/chrome/browser/history/top_sites_unittest.cc b/chrome/browser/history/top_sites_unittest.cc index 160b275..f908646 100644 --- a/chrome/browser/history/top_sites_unittest.cc +++ b/chrome/browser/history/top_sites_unittest.cc @@ -3,18 +3,26 @@ // found in the LICENSE file. #include "app/l10n_util.h" +#include "base/command_line.h" #include "base/file_util.h" +#include "base/path_service.h" #include "base/scoped_temp_dir.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/browser_thread.h" -#include "chrome/browser/history/top_sites.h" #include "chrome/browser/dom_ui/most_visited_handler.h" +#include "chrome/browser/history/history_backend.h" +#include "chrome/browser/history/history_database.h" #include "chrome/browser/history/history_marshaling.h" -#include "chrome/browser/history/top_sites_database.h" #include "chrome/browser/history/history_notifications.h" +#include "chrome/browser/history/top_sites.h" +#include "chrome/browser/history/top_sites_backend.h" +#include "chrome/browser/history/top_sites_cache.h" +#include "chrome/browser/history/top_sites_database.h" +#include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" #include "chrome/test/testing_profile.h" #include "chrome/tools/profiles/thumbnail-inl.h" #include "gfx/codec/jpeg_codec.h" @@ -25,246 +33,362 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" - namespace history { -static const unsigned char kBlob[] = - "12346102356120394751634516591348710478123649165419234519234512349134"; +namespace { -class TopSitesTest : public testing::Test { +// Used by WaitForHistory, see it for details. +class WaitForHistoryTask : public HistoryDBTask { public: - TopSitesTest() : number_of_callbacks_(0) { + WaitForHistoryTask() {} + + virtual bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) { + return true; } - ~TopSitesTest() { + + virtual void DoneRunOnMainThread() { + MessageLoop::current()->Quit(); } - TopSites& top_sites() { return *top_sites_; } - MostVisitedURLList& urls() { return urls_; } - Profile& profile() {return *profile_;} - FilePath& file_name() { return file_name_; } - RefCountedBytes* google_thumbnail() { return google_thumbnail_; } - RefCountedBytes* random_thumbnail() { return random_thumbnail_; } - RefCountedBytes* weewar_thumbnail() { return weewar_thumbnail_; } - CancelableRequestConsumer* consumer() { return &consumer_; } - size_t number_of_callbacks() {return number_of_callbacks_; } - // Prepopulated URLs - added at the back of TopSites. - GURL welcome_url() { - return GURL(l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL)); + private: + DISALLOW_COPY_AND_ASSIGN(WaitForHistoryTask); +}; + +// Used for querying top sites. Either runs sequentially, or runs a nested +// nested message loop until the response is complete. The later is used when +// TopSites is queried before it finishes loading. +class TopSitesQuerier { + public: + TopSitesQuerier() : number_of_callbacks_(0), waiting_(false) {} + + // Queries top sites. If |wait| is true a nested message loop is run until the + // callback is notified. + void QueryTopSites(TopSites* top_sites, bool wait) { + int start_number_of_callbacks = number_of_callbacks_; + top_sites->GetMostVisitedURLs( + &consumer_, + NewCallback(this, &TopSitesQuerier::OnTopSitesAvailable)); + if (wait && start_number_of_callbacks == number_of_callbacks_) { + waiting_ = true; + MessageLoop::current()->Run(); + } } - GURL themes_url() { - return GURL(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)); + + void CancelRequest() { + consumer_.CancelAllRequests(); + } + + void set_urls(const MostVisitedURLList& urls) { urls_ = urls; } + const MostVisitedURLList& urls() const { return urls_; } + + int number_of_callbacks() const { return number_of_callbacks_; } + + private: + // Callback for TopSites::GetMostVisitedURLs. + void OnTopSitesAvailable(const history::MostVisitedURLList& data) { + urls_ = data; + number_of_callbacks_++; + if (waiting_) { + MessageLoop::current()->Quit(); + waiting_ = false; + } + } + + CancelableRequestConsumer consumer_; + MostVisitedURLList urls_; + int number_of_callbacks_; + bool waiting_; + + DISALLOW_COPY_AND_ASSIGN(TopSitesQuerier); +}; + +// Extracts the data from |t1| into a SkBitmap. This is intended for usage of +// thumbnail data, which is stored as jpgs. +SkBitmap ExtractThumbnail(const RefCountedBytes& t1) { + scoped_ptr<SkBitmap> image(gfx::JPEGCodec::Decode(t1.front(), + t1.data.size())); + return image.get() ? *image : SkBitmap(); +} + +// Returns true if t1 and t2 contain the same data. +bool ThumbnailsAreEqual(RefCountedBytes* t1, RefCountedBytes* t2) { + if (!t1 || !t2) + return false; + if (t1->data.size() != t2->data.size()) + return false; + return std::equal(t1->data.begin(), + t1->data.end(), + t2->data.begin()); +} + +} // namespace + +class TopSitesTest : public testing::Test { + public: + TopSitesTest() + : ui_thread_(BrowserThread::UI, &message_loop_), + db_thread_(BrowserThread::DB, &message_loop_), + original_command_line_(*CommandLine::ForCurrentProcess()) { } virtual void SetUp() { + CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableTopSites); profile_.reset(new TestingProfile); - top_sites_ = new TopSites(profile_.get()); - - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - file_name_ = temp_dir_.path().AppendASCII("TopSites.db"); - EXPECT_TRUE(file_util::Delete(file_name_, false)); - - std::vector<unsigned char> random_data(kBlob, kBlob + sizeof(kBlob)); - std::vector<unsigned char> google_data(kGoogleThumbnail, - kGoogleThumbnail + - sizeof(kGoogleThumbnail)); - std::vector<unsigned char> weewar_data(kWeewarThumbnail, - kWeewarThumbnail + - sizeof(kWeewarThumbnail)); - random_thumbnail_ = new RefCountedBytes(random_data); - google_thumbnail_ = new RefCountedBytes(google_data); - weewar_thumbnail_ = new RefCountedBytes(weewar_data); + if (CreateHistoryAndTopSites()) { + profile_->CreateHistoryService(false, false); + profile_->CreateTopSites(); + profile_->BlockUntilTopSitesLoaded(); + } } virtual void TearDown() { profile_.reset(); - TopSites::DeleteTopSites(top_sites_); - EXPECT_TRUE(file_util::Delete(file_name_, false)); + *CommandLine::ForCurrentProcess() = original_command_line_; } - // Callback for TopSites::GetMostVisitedURLs. - void OnTopSitesAvailable(history::MostVisitedURLList data) { - urls_ = data; - number_of_callbacks_++; + // Returns true if history and top sites should be created in SetUp. + virtual bool CreateHistoryAndTopSites() { + return true; } - // Wrappers that allow private TopSites functions to be called from the - // individual tests without making them all be friends. - GURL GetCanonicalURL(const GURL& url) const { - AutoLock lock(top_sites_->lock_); // The function asserts it's in the lock. - return top_sites_->GetCanonicalURL(url); + // Gets the thumbnail for |url| from TopSites. + SkBitmap GetThumbnail(const GURL& url) { + scoped_refptr<RefCountedBytes> data; + return top_sites()->GetPageThumbnail(url, &data) ? + ExtractThumbnail(*data.get()) : SkBitmap(); } - void StoreMostVisited(std::vector<MostVisitedURL>* urls) { - top_sites_->StoreMostVisited(urls); + // Creates a bitmap of the specified color. + SkBitmap CreateBitmap(SkColor color) { + SkBitmap thumbnail; + thumbnail.setConfig(SkBitmap::kARGB_8888_Config, 4, 4); + thumbnail.allocPixels(); + thumbnail.eraseColor(color); + return thumbnail; } - static void DiffMostVisited(const std::vector<MostVisitedURL>& old_list, - const std::vector<MostVisitedURL>& new_list, - std::vector<size_t>* added_urls, - std::vector<size_t>* deleted_urls, - std::vector<size_t>* moved_urls) { - TopSites::DiffMostVisited(old_list, new_list, - added_urls, deleted_urls, moved_urls); + // Forces top sites to load top sites from history, then recreates top sites. + // Recreating top sites makes sure the changes from history are saved and + // loaded from the db. + void RefreshTopSitesAndRecreate() { + StartQueryForMostVisited(); + WaitForHistory(); + RecreateTopSitesAndBlock(); } - Lock& lock() { - return top_sites_->lock_; + // Blocks the caller until history processes a task. This is useful if you + // need to wait until you know history has processed a task. + void WaitForHistory() { + history_service()->ScheduleDBTask(new WaitForHistoryTask(), &consumer_); + MessageLoop::current()->Run(); } - private: - scoped_refptr<TopSites> top_sites_; - MostVisitedURLList urls_; - size_t number_of_callbacks_; - scoped_ptr<TestingProfile> profile_; - ScopedTempDir temp_dir_; - FilePath file_name_; // Database filename. - scoped_refptr<RefCountedBytes> google_thumbnail_; - scoped_refptr<RefCountedBytes> random_thumbnail_; - scoped_refptr<RefCountedBytes> weewar_thumbnail_; - MessageLoop message_loop_; - CancelableRequestConsumer consumer_; + // Waits for top sites to finish processing a task. This is useful if you need + // to wait until top sites finishes processing a task. + void WaitForTopSites() { + top_sites()->backend_->DoEmptyRequest( + &consumer_, + NewCallback(this, &TopSitesTest::QuitCallback)); + MessageLoop::current()->Run(); + } - DISALLOW_COPY_AND_ASSIGN(TopSitesTest); -}; + TopSites* top_sites() { return profile_->GetTopSites(); } + CancelableRequestConsumer* consumer() { return &consumer_; } + TestingProfile* profile() {return profile_.get();} + HistoryService* history_service() { + return profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); + } + + MostVisitedURLList GetPrepopulatePages() { + return TopSites::GetPrepopulatePages(); + } + // Returns true if the TopSitesQuerier contains the prepopulate data starting + // at |start_index|. + void ContainsPrepopulatePages(const TopSitesQuerier& querier, + size_t start_index) { + MostVisitedURLList prepopulate_urls = GetPrepopulatePages(); + ASSERT_LE(start_index + prepopulate_urls.size(), querier.urls().size()); + for (size_t i = 0; i < prepopulate_urls.size(); ++i) { + EXPECT_EQ(prepopulate_urls[i].url.spec(), + querier.urls()[start_index + i].url.spec()) << " @ index " << + i; + } + } -// A mockup of a HistoryService used for testing TopSites. -class MockHistoryServiceImpl : public TopSites::MockHistoryService { - public: - MockHistoryServiceImpl() : num_thumbnail_requests_(0) {} - - // Calls the callback directly with the results. - HistoryService::Handle QueryMostVisitedURLs( - int result_count, int days_back, - CancelableRequestConsumerBase* consumer, - HistoryService::QueryMostVisitedURLsCallback* callback) { - callback->Run(CancelableRequestProvider::Handle(0), // Handle is unused. - most_visited_urls_); - delete callback; - return 0; + // Used for callbacks from history. + void EmptyCallback() { } - // Add a page to the end of the pages list. - void AppendMockPage(const GURL& url, - const string16& title) { - MostVisitedURL page; - page.url = url; - page.title = title; - page.redirects = RedirectList(); - page.redirects.push_back(url); - most_visited_urls_.push_back(page); + // Quit the current message loop when invoked. Useful when running a nested + // message loop. + void QuitCallback(TopSitesBackend::Handle handle) { + MessageLoop::current()->Quit(); } - // Removes the last URL in the list. - void RemoveMostVisitedURL() { - most_visited_urls_.pop_back(); + // Adds a page to history. + void AddPageToHistory(const GURL& url) { + RedirectList redirects; + redirects.push_back(url); + history_service()->AddPage( + url, static_cast<void*>(this), 0, GURL(), PageTransition::TYPED, + redirects, history::SOURCE_BROWSED, false); } - virtual void GetPageThumbnail( - const GURL& url, - CancelableRequestConsumerTSimple<size_t>* consumer, - HistoryService::ThumbnailDataCallback* callback, - size_t index) { - num_thumbnail_requests_++; - MostVisitedURL mvu; - mvu.url = url; - MostVisitedURLList::iterator pos = std::find(most_visited_urls_.begin(), - most_visited_urls_.end(), - mvu); - EXPECT_TRUE(pos != most_visited_urls_.end()) << url.spec(); - scoped_refptr<RefCountedBytes> thumbnail; - callback->Run(index, thumbnail); - delete callback; + // Adds a page to history. + void AddPageToHistory(const GURL& url, const string16& title) { + RedirectList redirects; + redirects.push_back(url); + history_service()->AddPage( + url, static_cast<void*>(this), 0, GURL(), PageTransition::TYPED, + redirects, history::SOURCE_BROWSED, false); + history_service()->SetPageTitle(url, title); } - void ResetNumberOfThumbnailRequests() { - num_thumbnail_requests_ = 0; + // Adds a page to history. + void AddPageToHistory(const GURL& url, + const string16& title, + const history::RedirectList& redirects, + base::Time time) { + history_service()->AddPage( + url, time, static_cast<void*>(this), 0, GURL(), PageTransition::TYPED, + redirects, history::SOURCE_BROWSED, false); + history_service()->SetPageTitle(url, title); } - int GetNumberOfThumbnailRequests() { - return num_thumbnail_requests_; + // Delets a url. + void DeleteURL(const GURL& url) { + history_service()->DeleteURL(url); } - private: - MostVisitedURLList most_visited_urls_; - int num_thumbnail_requests_; // Number of calls to GetPageThumbnail. -}; + // Returns true if the thumbnail equals the specified bytes. + bool ThumbnailEqualsBytes(const SkBitmap& image, RefCountedBytes* bytes) { + scoped_refptr<RefCountedBytes> encoded_image; + EncodeBitmap(image, &encoded_image); + return ThumbnailsAreEqual(encoded_image, bytes); + } + // Recreates top sites. This forces top sites to reread from the db. + void RecreateTopSitesAndBlock() { + // Recreate TopSites and wait for it to load. + profile()->CreateTopSites(); + // As history already loaded we have to fake this call. + profile()->BlockUntilTopSitesLoaded(); + } -// A mockup of a TopSitesDatabase used for testing TopSites. -class MockTopSitesDatabaseImpl : public TopSitesDatabase { - public: - virtual void GetPageThumbnails(MostVisitedURLList* urls, - std::map<GURL, Images>* thumbnails) { - // Return a copy of the vector. - *urls = top_sites_list_; - *thumbnails = thumbnails_map_; + // Wrappers that allow private TopSites functions to be called from the + // individual tests without making them all be friends. + GURL GetCanonicalURL(const GURL& url) { + return top_sites()->cache_->GetCanonicalURL(url); } - virtual void SetPageThumbnail(const MostVisitedURL& url, int url_rank, - const Images& thumbnail) { - SetPageRank(url, url_rank); - // Update thubmnail - thumbnails_map_[url.url] = thumbnail; + void SetTopSites(const MostVisitedURLList& new_top_sites) { + top_sites()->SetTopSites(new_top_sites); } - virtual void UpdatePageRank(const MostVisitedURL& url, int new_rank) { - MostVisitedURLList::iterator pos = std::find(top_sites_list_.begin(), - top_sites_list_.end(), - url); - // Is it in the right position? - int rank = pos - top_sites_list_.begin(); - if (rank != new_rank) { - // Move the URL to a new position. - top_sites_list_.erase(pos); - top_sites_list_.insert(top_sites_list_.begin() + new_rank, url); - } + void StartQueryForMostVisited() { + top_sites()->StartQueryForMostVisited(); } - virtual void SetPageRank(const MostVisitedURL& url, int url_rank) { - // Check if this url is in the list, and at which position. - MostVisitedURLList::iterator pos = std::find(top_sites_list_.begin(), - top_sites_list_.end(), - url); - if (pos == top_sites_list_.end()) { - // Add it to the list. - top_sites_list_.insert(top_sites_list_.begin() + url_rank, url); - } else { - UpdatePageRank(url, url_rank); - } + bool EncodeBitmap(const SkBitmap& image, + scoped_refptr<RefCountedBytes>* bytes) { + return TopSites::EncodeBitmap(image, bytes); + } + + void SetLastNumUrlsChanged(size_t value) { + top_sites()->last_num_urls_changed_ = value; } - // Get a thumbnail for a given page. Returns true iff we have the thumbnail. - virtual bool GetPageThumbnail(const GURL& url, - Images* thumbnail) { - std::map<GURL, Images>::const_iterator found = - thumbnails_map_.find(url); - if (found == thumbnails_map_.end()) - return false; // No thumbnail for this URL. + size_t last_num_urls_changed() { return top_sites()->last_num_urls_changed_; } - thumbnail->thumbnail = found->second.thumbnail; - thumbnail->thumbnail_score = found->second.thumbnail_score; - return true; + base::TimeDelta GetUpdateDelay() { + return top_sites()->GetUpdateDelay(); } - virtual bool RemoveURL(const MostVisitedURL& url) { - // Comparison by url. - MostVisitedURLList::iterator pos = std::find(top_sites_list_.begin(), - top_sites_list_.end(), - url); - if (pos == top_sites_list_.end()) { - return false; - } - top_sites_list_.erase(pos); - thumbnails_map_.erase(url.url); - return true; + bool IsTopSitesLoaded() { return top_sites()->loaded_; } + + bool AddPrepopulatedPages(MostVisitedURLList* urls) { + return TopSites::AddPrepopulatedPages(urls); } private: - MostVisitedURLList top_sites_list_; // Keeps the URLs sorted by score (rank). - std::map<GURL, Images> thumbnails_map_; -}; + MessageLoopForUI message_loop_; + BrowserThread ui_thread_; + BrowserThread db_thread_; + scoped_ptr<TestingProfile> profile_; + CancelableRequestConsumer consumer_; + CommandLine original_command_line_; + + DISALLOW_COPY_AND_ASSIGN(TopSitesTest); +}; // Class TopSitesTest +class TopSitesMigrationTest : public TopSitesTest { + public: + TopSitesMigrationTest() {} + + virtual void SetUp() { + TopSitesTest::SetUp(); + + FilePath data_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); + data_path = data_path.AppendASCII("top_sites"); + + // Set up history and thumbnails as they would be before migration. + ASSERT_NO_FATAL_FAILURE( + ExecuteSQL(data_path.AppendASCII("history.19.sql"), + profile()->GetPath().Append(chrome::kHistoryFilename))); + ASSERT_NO_FATAL_FAILURE( + ExecuteSQL(data_path.AppendASCII("thumbnails.3.sql"), + profile()->GetPath().Append(chrome::kThumbnailsFilename))); + + profile()->CreateHistoryService(false, false); + profile()->CreateTopSites(); + profile()->BlockUntilTopSitesLoaded(); + } + + // Returns true if history and top sites should be created in SetUp. + virtual bool CreateHistoryAndTopSites() { + return false; + } + + protected: + // Assertions for the migration test. This is extracted into a standalone + // method so that it can be invoked twice. + void MigrationAssertions() { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + + // We shouldn't have gotten a callback. + EXPECT_EQ(1, querier.number_of_callbacks()); + + // The data we loaded should contain google and yahoo. + ASSERT_EQ(2u + GetPrepopulatePages().size(), querier.urls().size()); + EXPECT_EQ(GURL("http://google.com/"), querier.urls()[0].url); + EXPECT_EQ(GURL("http://yahoo.com/"), querier.urls()[1].url); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2)); + + SkBitmap goog_thumbnail = GetThumbnail(GURL("http://google.com/")); + EXPECT_EQ(1, goog_thumbnail.width()); + + SkBitmap yahoo_thumbnail = GetThumbnail(GURL("http://yahoo.com/")); + EXPECT_EQ(2, yahoo_thumbnail.width()); + + // Favicon assertions are handled in ThumbnailDatabase. + } + + private: + // Executes the sql from the file |sql_path| in the database at |db_path|. + void ExecuteSQL(const FilePath& sql_path, + const FilePath& db_path) { + std::string sql; + ASSERT_TRUE(file_util::ReadFileToString(sql_path, &sql)); + sql::Connection connection; + ASSERT_TRUE(connection.Open(db_path)); + ASSERT_TRUE(connection.Execute(sql.c_str())); + } + + DISALLOW_COPY_AND_ASSIGN(TopSitesMigrationTest); +}; // Helper function for appending a URL to a vector of "most visited" URLs, // using the default values for everything but the URL. @@ -276,18 +400,6 @@ static void AppendMostVisitedURL(std::vector<MostVisitedURL>* list, list->push_back(mv); } -// Returns true if t1 and t2 contain the same data. -static bool ThumbnailsAreEqual(RefCountedBytes* t1, - RefCountedBytes* t2) { - if (!t1 || !t2) - return false; - if (t1->data.size() != t2->data.size()) - return false; - return std::equal(t1->data.begin(), - t1->data.end(), - t2->data.begin()); -} - // Same as AppendMostVisitedURL except that it adds a redirect from the first // URL to the second. static void AppendMostVisitedURLWithRedirect( @@ -300,8 +412,8 @@ static void AppendMostVisitedURLWithRedirect( list->push_back(mv); } +// Tests GetCanonicalURL. TEST_F(TopSitesTest, GetCanonicalURL) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); // Have two chains: // google.com -> www.google.com // news.google.com (no redirects) @@ -312,7 +424,7 @@ TEST_F(TopSitesTest, GetCanonicalURL) { std::vector<MostVisitedURL> most_visited; AppendMostVisitedURLWithRedirect(&most_visited, source, dest); AppendMostVisitedURL(&most_visited, news); - StoreMostVisited(&most_visited); + SetTopSites(most_visited); // Random URLs not in the database are returned unchanged. GURL result = GetCanonicalURL(GURL("http://fark.com/")); @@ -331,6 +443,7 @@ TEST_F(TopSitesTest, GetCanonicalURL) { EXPECT_EQ(dest, result); } +// Tests DiffMostVisited. TEST_F(TopSitesTest, DiffMostVisited) { GURL stays_the_same("http://staysthesame/"); GURL gets_added_1("http://getsadded1/"); @@ -349,26 +462,25 @@ TEST_F(TopSitesTest, DiffMostVisited) { AppendMostVisitedURL(&new_list, gets_added_2); // 2 (added) AppendMostVisitedURL(&new_list, gets_moved_1); // 3 (moved from 2) - std::vector<size_t> added; - std::vector<size_t> deleted; - std::vector<size_t> moved; - DiffMostVisited(old_list, new_list, &added, &deleted, &moved); + history::TopSitesDelta delta; + history::TopSites::DiffMostVisited(old_list, new_list, &delta); - ASSERT_EQ(2u, added.size()); - ASSERT_EQ(1u, deleted.size()); - ASSERT_EQ(1u, moved.size()); + ASSERT_EQ(2u, delta.added.size()); + ASSERT_TRUE(gets_added_1 == delta.added[0].url.url); + ASSERT_EQ(1, delta.added[0].rank); + ASSERT_TRUE(gets_added_2 == delta.added[1].url.url); + ASSERT_EQ(2, delta.added[1].rank); - // There should be 2 URLs added, we don't assume what order they're in inside - // the result vector. - EXPECT_TRUE(added[0] == 1 || added[1] == 1); - EXPECT_TRUE(added[0] == 2 || added[1] == 2); + ASSERT_EQ(1u, delta.deleted.size()); + ASSERT_TRUE(gets_deleted_1 == delta.deleted[0].url); - EXPECT_EQ(1u, deleted[0]); - EXPECT_EQ(3u, moved[0]); + ASSERT_EQ(1u, delta.moved.size()); + ASSERT_TRUE(gets_moved_1 == delta.moved[0].url.url); + ASSERT_EQ(3, delta.moved[0].rank); } +// Tests SetPageThumbnail. TEST_F(TopSitesTest, SetPageThumbnail) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); GURL url1a("http://google.com/"); GURL url1b("http://www.google.com/"); GURL url2("http://images.google.com/"); @@ -384,13 +496,10 @@ TEST_F(TopSitesTest, SetPageThumbnail) { list.push_back(mv); // Save our most visited data containing that one site. - StoreMostVisited(&list); + SetTopSites(list); // Create a dummy thumbnail. - SkBitmap thumbnail; - thumbnail.setConfig(SkBitmap::kARGB_8888_Config, 4, 4); - thumbnail.allocPixels(); - thumbnail.eraseRGB(0x00, 0x00, 0x00); + SkBitmap thumbnail(CreateBitmap(SK_ColorWHITE)); base::Time now = base::Time::Now(); ThumbnailScore low_score(1.0, true, true, now); @@ -398,96 +507,117 @@ TEST_F(TopSitesTest, SetPageThumbnail) { ThumbnailScore high_score(0.0, true, true, now); // Setting the thumbnail for invalid pages should fail. - EXPECT_FALSE(top_sites().SetPageThumbnail(invalid_url, - thumbnail, medium_score)); + EXPECT_FALSE(top_sites()->SetPageThumbnail(invalid_url, + thumbnail, medium_score)); // Setting the thumbnail for url2 should succeed, lower scores shouldn't // replace it, higher scores should. - EXPECT_TRUE(top_sites().SetPageThumbnail(url2, thumbnail, medium_score)); - EXPECT_FALSE(top_sites().SetPageThumbnail(url2, thumbnail, low_score)); - EXPECT_TRUE(top_sites().SetPageThumbnail(url2, thumbnail, high_score)); + EXPECT_TRUE(top_sites()->SetPageThumbnail(url2, thumbnail, medium_score)); + EXPECT_FALSE(top_sites()->SetPageThumbnail(url2, thumbnail, low_score)); + EXPECT_TRUE(top_sites()->SetPageThumbnail(url2, thumbnail, high_score)); // Set on the redirect source should succeed. It should be replacable by // the same score on the redirect destination, which in turn should not // be replaced by the source again. - EXPECT_TRUE(top_sites().SetPageThumbnail(url1a, thumbnail, medium_score)); - EXPECT_TRUE(top_sites().SetPageThumbnail(url1b, thumbnail, medium_score)); - EXPECT_FALSE(top_sites().SetPageThumbnail(url1a, thumbnail, medium_score)); + EXPECT_TRUE(top_sites()->SetPageThumbnail(url1a, thumbnail, medium_score)); + EXPECT_TRUE(top_sites()->SetPageThumbnail(url1b, thumbnail, medium_score)); + EXPECT_FALSE(top_sites()->SetPageThumbnail(url1a, thumbnail, medium_score)); +} + +// Makes sure a thumbnail is correctly removed when the page is removed. +TEST_F(TopSitesTest, ThumbnailRemoved) { + GURL url("http://google.com/"); + + // Configure top sites with 'google.com'. + std::vector<MostVisitedURL> list; + AppendMostVisitedURL(&list, url); + SetTopSites(list); + + // Create a dummy thumbnail. + SkBitmap thumbnail(CreateBitmap(SK_ColorRED)); + + base::Time now = base::Time::Now(); + ThumbnailScore low_score(1.0, true, true, now); + ThumbnailScore medium_score(0.5, true, true, now); + ThumbnailScore high_score(0.0, true, true, now); + + // Set the thumbnail. + EXPECT_TRUE(top_sites()->SetPageThumbnail(url, thumbnail, medium_score)); + + // Make sure the thumbnail was actually set. + scoped_refptr<RefCountedBytes> result; + EXPECT_TRUE(top_sites()->GetPageThumbnail(url, &result)); + EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, result.get())); + + // Reset the thumbnails and make sure we don't get it back. + SetTopSites(MostVisitedURLList()); + EXPECT_FALSE(top_sites()->GetPageThumbnail(url, &result)); } +// Tests GetPageThumbnail. TEST_F(TopSitesTest, GetPageThumbnail) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); MostVisitedURLList url_list; - MostVisitedURL url1(GURL("http://asdf.com"), GURL(), string16()); + MostVisitedURL url1; + url1.url = GURL("http://asdf.com"); url1.redirects.push_back(url1.url); url_list.push_back(url1); - MostVisitedURL url2(GURL("http://gmail.com"), GURL(), string16()); + MostVisitedURL url2; + url2.url = GURL("http://gmail.com"); url2.redirects.push_back(url2.url); url2.redirects.push_back(GURL("http://mail.google.com")); url_list.push_back(url2); - top_sites().UpdateMostVisited(url_list); - MessageLoop::current()->RunAllPending(); + SetTopSites(url_list); // Create a dummy thumbnail. - SkBitmap thumbnail; - thumbnail.setConfig(SkBitmap::kARGB_8888_Config, 4, 4); - thumbnail.allocPixels(); - thumbnail.eraseRGB(0x00, 0x00, 0x00); + SkBitmap thumbnail(CreateBitmap(SK_ColorWHITE)); ThumbnailScore score(0.5, true, true, base::Time::Now()); - RefCountedBytes* result = NULL; - EXPECT_TRUE(top_sites().SetPageThumbnail(url1.url, thumbnail, score)); - EXPECT_TRUE(top_sites().GetPageThumbnail(url1.url, &result)); + scoped_refptr<RefCountedBytes> result; + EXPECT_TRUE(top_sites()->SetPageThumbnail(url1.url, thumbnail, score)); + EXPECT_TRUE(top_sites()->GetPageThumbnail(url1.url, &result)); - EXPECT_TRUE(top_sites().SetPageThumbnail(GURL("http://gmail.com"), + EXPECT_TRUE(top_sites()->SetPageThumbnail(GURL("http://gmail.com"), thumbnail, score)); - EXPECT_TRUE(top_sites().GetPageThumbnail(GURL("http://gmail.com"), - &result)); + EXPECT_TRUE(top_sites()->GetPageThumbnail(GURL("http://gmail.com"), + &result)); // Get a thumbnail via a redirect. - EXPECT_TRUE(top_sites().GetPageThumbnail(GURL("http://mail.google.com"), - &result)); + EXPECT_TRUE(top_sites()->GetPageThumbnail(GURL("http://mail.google.com"), + &result)); - EXPECT_TRUE(top_sites().SetPageThumbnail(GURL("http://mail.google.com"), + EXPECT_TRUE(top_sites()->SetPageThumbnail(GURL("http://mail.google.com"), thumbnail, score)); - EXPECT_TRUE(top_sites().GetPageThumbnail(url2.url, &result)); + EXPECT_TRUE(top_sites()->GetPageThumbnail(url2.url, &result)); - scoped_ptr<SkBitmap> out_bitmap(gfx::JPEGCodec::Decode(result->front(), - result->size())); - EXPECT_EQ(0, memcmp(thumbnail.getPixels(), out_bitmap->getPixels(), - thumbnail.getSize())); + EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, result.get())); } +// Tests GetMostVisitedURLs. TEST_F(TopSitesTest, GetMostVisited) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); GURL news("http://news.google.com/"); GURL google("http://google.com/"); - MockHistoryServiceImpl hs; - hs.AppendMockPage(news, ASCIIToUTF16("Google News")); - hs.AppendMockPage(google, ASCIIToUTF16("Google")); - top_sites().SetMockHistoryService(&hs); - - top_sites().StartQueryForMostVisited(); - MessageLoop::current()->RunAllPending(); - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); + AddPageToHistory(news); + AddPageToHistory(google); + + StartQueryForMostVisited(); + WaitForHistory(); + + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + + ASSERT_EQ(1, querier.number_of_callbacks()); + // 2 extra prepopulated URLs. - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ(news, urls()[0].url); - EXPECT_EQ(google, urls()[1].url); - EXPECT_EQ(welcome_url(), urls()[2].url); - EXPECT_EQ(themes_url(), urls()[3].url); + ASSERT_EQ(2u + GetPrepopulatePages().size(), querier.urls().size()); + EXPECT_EQ(news, querier.urls()[0].url); + EXPECT_EQ(google, querier.urls()[1].url); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2)); } -TEST_F(TopSitesTest, MockDatabase) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); - MockTopSitesDatabaseImpl* db = new MockTopSitesDatabaseImpl; - // |db| is destroyed when the top_sites is destroyed in TearDown. - top_sites().db_.reset(db); +// Makes sure changes done to top sites get mirrored to the db. +TEST_F(TopSitesTest, SaveToDB) { MostVisitedURL url; GURL asdf_url("http://asdf.com"); string16 asdf_title(ASCIIToUTF16("ASDF")); @@ -496,155 +626,62 @@ TEST_F(TopSitesTest, MockDatabase) { GURL news_url("http://news.google.com"); string16 news_title(ASCIIToUTF16("Google News")); - url.url = asdf_url; - url.title = asdf_title; - url.redirects.push_back(url.url); - Images thumbnail; - db->SetPageThumbnail(url, 0, thumbnail); + // Add asdf_url to history. + AddPageToHistory(asdf_url, asdf_title); + + // Make TopSites reread from the db. + StartQueryForMostVisited(); + WaitForHistory(); - top_sites().ReadDatabase(); + // Add a thumbnail. + SkBitmap tmp_bitmap(CreateBitmap(SK_ColorBLUE)); + ASSERT_TRUE(top_sites()->SetPageThumbnail(asdf_url, tmp_bitmap, + ThumbnailScore())); - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - ASSERT_EQ(3u, urls().size()); - EXPECT_EQ(asdf_url, urls()[0].url); - EXPECT_EQ(asdf_title, urls()[0].title); - EXPECT_EQ(welcome_url(), urls()[1].url); - EXPECT_EQ(themes_url(), urls()[2].url); + RecreateTopSitesAndBlock(); + + { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + ASSERT_EQ(1u + GetPrepopulatePages().size(), querier.urls().size()); + EXPECT_EQ(asdf_url, querier.urls()[0].url); + EXPECT_EQ(asdf_title, querier.urls()[0].title); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1)); + + scoped_refptr<RefCountedBytes> read_data; + EXPECT_TRUE(top_sites()->GetPageThumbnail(asdf_url, &read_data)); + EXPECT_TRUE(ThumbnailEqualsBytes(tmp_bitmap, read_data.get())); + } MostVisitedURL url2; url2.url = google_url; url2.title = google_title; url2.redirects.push_back(url2.url); - // Add new thumbnail at rank 0 and shift the other result to 1. - db->SetPageThumbnail(url2, 0, thumbnail); - - top_sites().ReadDatabase(); - - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ(google_url, urls()[0].url); - EXPECT_EQ(google_title, urls()[0].title); - EXPECT_EQ(asdf_url, urls()[1].url); - EXPECT_EQ(asdf_title, urls()[1].title); - EXPECT_EQ(welcome_url(), urls()[2].url); - EXPECT_EQ(themes_url(), urls()[3].url); - - MockHistoryServiceImpl hs; - // Add one old, one new URL to the history. - hs.AppendMockPage(google_url, google_title); - hs.AppendMockPage(news_url, news_title); - top_sites().SetMockHistoryService(&hs); - - // This writes the new data to the DB. - top_sites().StartQueryForMostVisited(); - MessageLoop::current()->RunAllPending(); - - std::map<GURL, Images> thumbnails; - MostVisitedURLList result; - db->GetPageThumbnails(&result, &thumbnails); - ASSERT_EQ(4u, result.size()); - EXPECT_EQ(google_title, result[0].title); - EXPECT_EQ(news_title, result[1].title); -} - -// Test TopSitesDatabaseImpl. -TEST_F(TopSitesTest, TopSitesDB) { - TopSitesDatabaseImpl db; + AddPageToHistory(url2.url, url2.title); - ASSERT_TRUE(db.Init(file_name())); + // Add new thumbnail at rank 0 and shift the other result to 1. + ASSERT_TRUE(top_sites()->SetPageThumbnail(google_url, + tmp_bitmap, + ThumbnailScore())); - MostVisitedURL url; - GURL asdf_url("http://asdf.com"); - string16 asdf_title(ASCIIToUTF16("ASDF")); - GURL google_url("http://google.com"); - string16 google_title(ASCIIToUTF16("Google")); - GURL news_url("http://news.google.com"); - string16 news_title(ASCIIToUTF16("Google News")); + // Make TopSites reread from the db. + RefreshTopSitesAndRecreate(); - url.url = asdf_url; - url.title = asdf_title; - url.redirects.push_back(url.url); - Images thumbnail; - thumbnail.thumbnail = random_thumbnail(); - // Add asdf at rank 0. - db.SetPageThumbnail(url, 0, thumbnail); - - Images result; - EXPECT_TRUE(db.GetPageThumbnail(url.url, &result)); - EXPECT_EQ(thumbnail.thumbnail->data.size(), result.thumbnail->data.size()); - EXPECT_TRUE(ThumbnailsAreEqual(thumbnail.thumbnail, result.thumbnail)); - - MostVisitedURLList urls; - std::map<GURL, Images> thumbnails; - db.GetPageThumbnails(&urls, &thumbnails); - ASSERT_EQ(1u, urls.size()); - EXPECT_EQ(asdf_url, urls[0].url); - EXPECT_EQ(asdf_title, urls[0].title); - - url.url = google_url; - url.title = google_title; - - // Add google at rank 1 - no rank shifting. - db.SetPageThumbnail(url, 1, thumbnail); - db.GetPageThumbnails(&urls, &thumbnails); - ASSERT_EQ(2u, urls.size()); - EXPECT_EQ(asdf_url, urls[0].url); - EXPECT_EQ(asdf_title, urls[0].title); - EXPECT_EQ(google_url, urls[1].url); - EXPECT_EQ(google_title, urls[1].title); - - url.url = news_url; - url.title = news_title; - - // Add news at rank 1 - shift google to rank 2. - db.SetPageThumbnail(url, 1, thumbnail); - db.GetPageThumbnails(&urls, &thumbnails); - ASSERT_EQ(3u, urls.size()); - EXPECT_EQ(asdf_url, urls[0].url); - EXPECT_EQ(news_url, urls[1].url); - EXPECT_EQ(google_url, urls[2].url); - - // Move news at rank 0 - shift the rest up. - db.SetPageThumbnail(url, 0, thumbnail); - db.GetPageThumbnails(&urls, &thumbnails); - ASSERT_EQ(3u, urls.size()); - EXPECT_EQ(news_url, urls[0].url); - EXPECT_EQ(asdf_url, urls[1].url); - EXPECT_EQ(google_url, urls[2].url); - - // Move news at rank 2 - shift the rest down. - db.SetPageThumbnail(url, 2, thumbnail); - db.GetPageThumbnails(&urls, &thumbnails); - ASSERT_EQ(3u, urls.size()); - EXPECT_EQ(asdf_url, urls[0].url); - EXPECT_EQ(google_url, urls[1].url); - EXPECT_EQ(news_url, urls[2].url); - - // Delete asdf. - url.url = asdf_url; - db.RemoveURL(url); - - db.GetPageThumbnails(&urls, &thumbnails); - ASSERT_EQ(2u, urls.size()); - EXPECT_EQ(google_url, urls[0].url); - EXPECT_EQ(news_url, urls[1].url); + { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + ASSERT_EQ(2u + GetPrepopulatePages().size(), querier.urls().size()); + EXPECT_EQ(asdf_url, querier.urls()[0].url); + EXPECT_EQ(asdf_title, querier.urls()[0].title); + EXPECT_EQ(google_url, querier.urls()[1].url); + EXPECT_EQ(google_title, querier.urls()[1].title); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2)); + } } -// Test TopSites with a real database. +// More permutations of saving to db. TEST_F(TopSitesTest, RealDatabase) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); - TopSitesDatabaseImpl* db = new TopSitesDatabaseImpl; - - ASSERT_TRUE(db->Init(file_name())); - // |db| is destroyed when the top_sites is destroyed in TearDown. - top_sites().db_.reset(db); MostVisitedURL url; GURL asdf_url("http://asdf.com"); string16 asdf_title(ASCIIToUTF16("ASDF")); @@ -658,130 +695,106 @@ TEST_F(TopSitesTest, RealDatabase) { url.url = asdf_url; url.title = asdf_title; url.redirects.push_back(url.url); - Images thumbnail; - thumbnail.thumbnail = random_thumbnail(); - db->SetPageThumbnail(url, 0, thumbnail); - - top_sites().ReadDatabase(); - - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - ASSERT_EQ(3u, urls().size()); - EXPECT_EQ(asdf_url, urls()[0].url); - EXPECT_EQ(asdf_title, urls()[0].title); - EXPECT_EQ(welcome_url(), urls()[1].url); - EXPECT_EQ(themes_url(), urls()[2].url); - - Images img_result; - db->GetPageThumbnail(asdf_url, &img_result); - EXPECT_TRUE(img_result.thumbnail != NULL); - EXPECT_TRUE(ThumbnailsAreEqual(random_thumbnail(), img_result.thumbnail)); - - RefCountedBytes* thumbnail_result; - EXPECT_TRUE(top_sites().GetPageThumbnail(asdf_url, &thumbnail_result)); - EXPECT_TRUE(thumbnail_result != NULL); - EXPECT_TRUE(ThumbnailsAreEqual(random_thumbnail(), thumbnail_result)); + SkBitmap asdf_thumbnail(CreateBitmap(SK_ColorRED)); + ASSERT_TRUE(top_sites()->SetPageThumbnail( + asdf_url, asdf_thumbnail, ThumbnailScore())); + + base::Time add_time(base::Time::Now()); + AddPageToHistory(url.url, url.title, url.redirects, add_time); + + RefreshTopSitesAndRecreate(); + + { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + + ASSERT_EQ(1u + GetPrepopulatePages().size(), querier.urls().size()); + EXPECT_EQ(asdf_url, querier.urls()[0].url); + EXPECT_EQ(asdf_title, querier.urls()[0].title); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1)); + + scoped_refptr<RefCountedBytes> read_data; + EXPECT_TRUE(top_sites()->GetPageThumbnail(asdf_url, &read_data)); + EXPECT_TRUE(ThumbnailEqualsBytes(asdf_thumbnail, read_data.get())); + } MostVisitedURL url2; - url2.url = google1_url; + url2.url = google3_url; url2.title = google_title; url2.redirects.push_back(google1_url); url2.redirects.push_back(google2_url); url2.redirects.push_back(google3_url); - // Add new thumbnail at rank 0 and shift the other result to 1. - Images g_thumbnail; - g_thumbnail.thumbnail = google_thumbnail(); - db->SetPageThumbnail(url2, 0, g_thumbnail); - - top_sites().ReadDatabase(); - - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ(google1_url, urls()[0].url); - EXPECT_EQ(google_title, urls()[0].title); - EXPECT_TRUE(top_sites().GetPageThumbnail(google1_url, &thumbnail_result)); - EXPECT_TRUE(ThumbnailsAreEqual(google_thumbnail(), thumbnail_result)); - ASSERT_EQ(3u, urls()[0].redirects.size()); - EXPECT_EQ(google1_url, urls()[0].redirects[0]); - EXPECT_EQ(google2_url, urls()[0].redirects[1]); - EXPECT_EQ(google3_url, urls()[0].redirects[2]); - - EXPECT_EQ(asdf_url, urls()[1].url); - EXPECT_EQ(asdf_title, urls()[1].title); - EXPECT_EQ(welcome_url(), urls()[2].url); - EXPECT_EQ(themes_url(), urls()[3].url); - - MockHistoryServiceImpl hs; - // Add one old, one new URL to the history. - hs.AppendMockPage(google1_url, google_title); - hs.AppendMockPage(news_url, news_title); - top_sites().SetMockHistoryService(&hs); - - // This requests data from History Service and writes it to the DB. - top_sites().StartQueryForMostVisited(); - MessageLoop::current()->RunAllPending(); - - std::map<GURL, Images> thumbnails; - MostVisitedURLList results; - db->GetPageThumbnails(&results, &thumbnails); - ASSERT_EQ(4u, results.size()); - EXPECT_EQ(google_title, results[0].title); - EXPECT_EQ(news_title, results[1].title); - - scoped_ptr<SkBitmap> weewar_bitmap( - gfx::JPEGCodec::Decode(weewar_thumbnail()->front(), - weewar_thumbnail()->size())); + AddPageToHistory(google3_url, url2.title, url2.redirects, + add_time - base::TimeDelta::FromMinutes(1)); + // Add google twice so that it becomes the first visited site. + AddPageToHistory(google3_url, url2.title, url2.redirects, + add_time - base::TimeDelta::FromMinutes(2)); - base::Time now = base::Time::Now(); - ThumbnailScore low_score(1.0, true, true, now); - ThumbnailScore medium_score(0.5, true, true, now); - ThumbnailScore high_score(0.0, true, true, now); + SkBitmap google_thumbnail(CreateBitmap(SK_ColorBLUE)); + ASSERT_TRUE(top_sites()->SetPageThumbnail( + url2.url, google_thumbnail, ThumbnailScore())); - // 1. Set to weewar. (Writes the thumbnail to the DB.) - EXPECT_TRUE(top_sites().SetPageThumbnail(google1_url, - *weewar_bitmap, - medium_score)); - RefCountedBytes* out_1; - Images out_2; - EXPECT_TRUE(top_sites().GetPageThumbnail(google1_url, &out_1)); + RefreshTopSitesAndRecreate(); - MessageLoop::current()->RunAllPending(); + { + scoped_refptr<RefCountedBytes> read_data; + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + + ASSERT_EQ(2u + GetPrepopulatePages().size(), querier.urls().size()); + EXPECT_EQ(google1_url, querier.urls()[0].url); + EXPECT_EQ(google_title, querier.urls()[0].title); + ASSERT_EQ(3u, querier.urls()[0].redirects.size()); + EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, &read_data)); + EXPECT_TRUE(ThumbnailEqualsBytes(google_thumbnail, read_data.get())); + + EXPECT_EQ(asdf_url, querier.urls()[1].url); + EXPECT_EQ(asdf_title, querier.urls()[1].title); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2)); + } - db->GetPageThumbnail(url2.url, &out_2); - EXPECT_TRUE(ThumbnailsAreEqual(out_1, out_2.thumbnail)); + SkBitmap weewar_bitmap(CreateBitmap(SK_ColorYELLOW)); - scoped_ptr<SkBitmap> google_bitmap( - gfx::JPEGCodec::Decode(google_thumbnail()->front(), - google_thumbnail()->size())); + base::Time thumbnail_time(base::Time::Now()); + ThumbnailScore low_score(1.0, true, true, thumbnail_time); + ThumbnailScore medium_score(0.5, true, true, thumbnail_time); + ThumbnailScore high_score(0.0, true, true, thumbnail_time); + + // 1. Set to weewar. (Writes the thumbnail to the DB.) + EXPECT_TRUE(top_sites()->SetPageThumbnail(google3_url, + weewar_bitmap, + medium_score)); + RefreshTopSitesAndRecreate(); + { + scoped_refptr<RefCountedBytes> read_data; + EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, &read_data)); + EXPECT_TRUE(ThumbnailEqualsBytes(weewar_bitmap, read_data.get())); + } + + SkBitmap google_bitmap(CreateBitmap(SK_ColorGREEN)); // 2. Set to google - low score. - EXPECT_FALSE(top_sites().SetPageThumbnail(google1_url, - *google_bitmap, - low_score)); + EXPECT_FALSE(top_sites()->SetPageThumbnail(google3_url, + google_bitmap, + low_score)); // 3. Set to google - high score. - EXPECT_TRUE(top_sites().SetPageThumbnail(google1_url, - *google_bitmap, - high_score)); + EXPECT_TRUE(top_sites()->SetPageThumbnail(google1_url, + google_bitmap, + high_score)); + // Check that the thumbnail was updated. - EXPECT_TRUE(top_sites().GetPageThumbnail(google1_url, &out_1)); - EXPECT_FALSE(ThumbnailsAreEqual(out_1, out_2.thumbnail)); - MessageLoop::current()->RunAllPending(); - - // Read the new thumbnail from the DB - should match what's in TopSites. - db->GetPageThumbnail(url2.url, &out_2); - EXPECT_TRUE(ThumbnailsAreEqual(out_1, out_2.thumbnail)); - EXPECT_TRUE(high_score.Equals(out_2.thumbnail_score)); + RefreshTopSitesAndRecreate(); + { + scoped_refptr<RefCountedBytes> read_data; + EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, &read_data)); + EXPECT_FALSE(ThumbnailEqualsBytes(weewar_bitmap, read_data.get())); + EXPECT_TRUE(ThumbnailEqualsBytes(google_bitmap, read_data.get())); + } } TEST_F(TopSitesTest, DeleteNotifications) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); GURL google1_url("http://google.com"); GURL google2_url("http://google.com/redirect"); GURL google3_url("http://www.google.com"); @@ -789,61 +802,70 @@ TEST_F(TopSitesTest, DeleteNotifications) { GURL news_url("http://news.google.com"); string16 news_title(ASCIIToUTF16("Google News")); - MockHistoryServiceImpl hs; + AddPageToHistory(google1_url, google_title); + AddPageToHistory(news_url, news_title); - top_sites().Init(file_name()); + RefreshTopSitesAndRecreate(); - hs.AppendMockPage(google1_url, google_title); - hs.AppendMockPage(news_url, news_title); - top_sites().SetMockHistoryService(&hs); + { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); - top_sites().StartQueryForMostVisited(); - MessageLoop::current()->RunAllPending(); + ASSERT_EQ(4u, querier.urls().size()); + } - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - // 2 extra prepopulated URLs. - ASSERT_EQ(4u, urls().size()); - - hs.RemoveMostVisitedURL(); - - history::URLsDeletedDetails history_details; - history_details.all_history = false; - Details<URLsDeletedDetails> details(&history_details); - top_sites().Observe(NotificationType::HISTORY_URLS_DELETED, - Source<Profile> (&profile()), - details); - MessageLoop::current()->RunAllPending(); - - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - ASSERT_EQ(3u, urls().size()); - EXPECT_EQ(google_title, urls()[0].title); - EXPECT_EQ(welcome_url(), urls()[1].url); - EXPECT_EQ(themes_url(), urls()[2].url); - - hs.RemoveMostVisitedURL(); - history_details.all_history = true; - details = Details<HistoryDetails>(&history_details); - top_sites().Observe(NotificationType::HISTORY_URLS_DELETED, - Source<Profile> (&profile()), - details); - MessageLoop::current()->RunAllPending(); - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - ASSERT_EQ(2u, urls().size()); - EXPECT_EQ(welcome_url(), urls()[0].url); - EXPECT_EQ(themes_url(), urls()[1].url); + DeleteURL(news_url); + + // Wait for history to process the deletion. + WaitForHistory(); + + { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + + ASSERT_EQ(1u + GetPrepopulatePages().size(), querier.urls().size()); + EXPECT_EQ(google_title, querier.urls()[0].title); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1)); + } + + // Now reload. This verifies topsites actually wrote the deletion to disk. + RefreshTopSitesAndRecreate(); + + { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + + ASSERT_EQ(1u + GetPrepopulatePages().size(), querier.urls().size()); + EXPECT_EQ(google_title, querier.urls()[0].title); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1)); + } + + DeleteURL(google1_url); + + // Wait for history to process the deletion. + WaitForHistory(); + + { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + + ASSERT_EQ(GetPrepopulatePages().size(), querier.urls().size()); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 0)); + } + + // Now reload. This verifies topsites actually wrote the deletion to disk. + RefreshTopSitesAndRecreate(); + + { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + + ASSERT_EQ(GetPrepopulatePages().size(), querier.urls().size()); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 0)); + } } TEST_F(TopSitesTest, PinnedURLsDeleted) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); GURL google1_url("http://google.com"); GURL google2_url("http://google.com/redirect"); GURL google3_url("http://www.google.com"); @@ -851,132 +873,130 @@ TEST_F(TopSitesTest, PinnedURLsDeleted) { GURL news_url("http://news.google.com"); string16 news_title(ASCIIToUTF16("Google News")); - MockHistoryServiceImpl hs; + AddPageToHistory(google1_url, google_title); + AddPageToHistory(news_url, news_title); + + RefreshTopSitesAndRecreate(); - top_sites().Init(file_name()); + { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); - hs.AppendMockPage(google1_url, google_title); - hs.AppendMockPage(news_url, news_title); - top_sites().SetMockHistoryService(&hs); + // 2 extra prepopulated URLs. + ASSERT_EQ(4u, querier.urls().size()); + } - top_sites().StartQueryForMostVisited(); - MessageLoop::current()->RunAllPending(); - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - MessageLoop::current()->RunAllPending(); - EXPECT_EQ(1u, number_of_callbacks()); - // 2 extra prepopulated URLs. - ASSERT_EQ(4u, urls().size()); - - top_sites().AddPinnedURL(news_url, 3); - EXPECT_TRUE(top_sites().IsURLPinned(news_url)); - - hs.RemoveMostVisitedURL(); - history::URLsDeletedDetails history_details; - history_details.all_history = false; - history_details.urls.insert(news_url); - Details<URLsDeletedDetails> details(&history_details); - top_sites().Observe(NotificationType::HISTORY_URLS_DELETED, - Source<Profile> (&profile()), - details); - MessageLoop::current()->RunAllPending(); - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - MessageLoop::current()->RunAllPending(); - EXPECT_EQ(2u, number_of_callbacks()); - ASSERT_EQ(3u, urls().size()); - EXPECT_FALSE(top_sites().IsURLPinned(news_url)); - - hs.RemoveMostVisitedURL(); - history_details.all_history = true; - details = Details<HistoryDetails>(&history_details); - top_sites().Observe(NotificationType::HISTORY_URLS_DELETED, - Source<Profile> (&profile()), - details); - MessageLoop::current()->RunAllPending(); - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - ASSERT_EQ(2u, urls().size()); - MessageLoop::current()->RunAllPending(); - - top_sites().StartQueryForMostVisited(); - MessageLoop::current()->RunAllPending(); - top_sites().GetMostVisitedURLs( - consumer(), - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - ASSERT_EQ(2u, urls().size()); - EXPECT_EQ(welcome_url(), urls()[0].url); - EXPECT_EQ(themes_url(), urls()[1].url); + top_sites()->AddPinnedURL(news_url, 3); + EXPECT_TRUE(top_sites()->IsURLPinned(news_url)); + + DeleteURL(news_url); + WaitForHistory(); + + { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + + // 2 extra prepopulated URLs. + ASSERT_EQ(3u, querier.urls().size()); + EXPECT_FALSE(top_sites()->IsURLPinned(news_url)); + } + + history_service()->ExpireHistoryBetween( + std::set<GURL>(), base::Time(), base::Time(), + consumer(), NewCallback(static_cast<TopSitesTest*>(this), + &TopSitesTest::EmptyCallback)), + WaitForHistory(); + + { + TopSitesQuerier querier; + querier.QueryTopSites(top_sites(), false); + + // 2 extra prepopulated URLs. + ASSERT_EQ(GetPrepopulatePages().size(), querier.urls().size()); + EXPECT_FALSE(top_sites()->IsURLPinned(google1_url)); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 0)); + } } +// Makes sure GetUpdateDelay is updated appropriately. TEST_F(TopSitesTest, GetUpdateDelay) { - top_sites().last_num_urls_changed_ = 0; - EXPECT_EQ(30, top_sites().GetUpdateDelay().InSeconds()); + SetLastNumUrlsChanged(0); + EXPECT_EQ(30, GetUpdateDelay().InSeconds()); - top_sites().top_sites_.resize(20); - top_sites().last_num_urls_changed_ = 0; - EXPECT_EQ(60, top_sites().GetUpdateDelay().InMinutes()); + MostVisitedURLList url_list; + url_list.resize(20); + GURL tmp_url(GURL("http://x")); + for (size_t i = 0; i < url_list.size(); ++i) { + url_list[i].url = tmp_url; + url_list[i].redirects.push_back(tmp_url); + } + SetTopSites(url_list); + EXPECT_EQ(20u, last_num_urls_changed()); + SetLastNumUrlsChanged(0); + EXPECT_EQ(60, GetUpdateDelay().InMinutes()); - top_sites().last_num_urls_changed_ = 3; - EXPECT_EQ(52, top_sites().GetUpdateDelay().InMinutes()); + SetLastNumUrlsChanged(3); + EXPECT_EQ(52, GetUpdateDelay().InMinutes()); - top_sites().last_num_urls_changed_ = 20; - EXPECT_EQ(1, top_sites().GetUpdateDelay().InMinutes()); + SetLastNumUrlsChanged(20); + EXPECT_EQ(1, GetUpdateDelay().InMinutes()); } -TEST_F(TopSitesTest, Migration) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); - GURL google1_url("http://google.com"); - string16 google_title(ASCIIToUTF16("Google")); - GURL news_url("http://news.google.com"); - string16 news_title(ASCIIToUTF16("Google News")); +TEST_F(TopSitesMigrationTest, Migrate) { + EXPECT_TRUE(IsTopSitesLoaded()); - MockHistoryServiceImpl hs; + // Make sure the data was migrated to top sites. + ASSERT_NO_FATAL_FAILURE(MigrationAssertions()); - top_sites().Init(file_name()); + // We need to wait for top sites and history to finish processing requests. + WaitForTopSites(); + WaitForHistory(); - hs.AppendMockPage(google1_url, google_title); - hs.AppendMockPage(news_url, news_title); - top_sites().SetMockHistoryService(&hs); - MessageLoop::current()->RunAllPending(); + // Make sure there is no longer a Thumbnails file on disk. + ASSERT_FALSE(file_util::PathExists( + profile()->GetPath().Append(chrome::kThumbnailsFilename))); - top_sites().StartMigration(); - EXPECT_TRUE(top_sites().migration_in_progress_); - MessageLoop::current()->RunAllPending(); - EXPECT_EQ(2, hs.GetNumberOfThumbnailRequests()); - EXPECT_FALSE(top_sites().migration_in_progress_); + // Recreate top sites and make sure everything is still there. + profile()->CreateHistoryService(false, false); + RecreateTopSitesAndBlock(); + + ASSERT_NO_FATAL_FAILURE(MigrationAssertions()); } -TEST_F(TopSitesTest, QueueingRequestsForTopSites) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); - CancelableRequestConsumer c1; - CancelableRequestConsumer c2; - CancelableRequestConsumer c3; - top_sites().GetMostVisitedURLs( - &c1, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - - top_sites().GetMostVisitedURLs( - &c2, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - - top_sites().GetMostVisitedURLs( - &c3, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - - EXPECT_EQ(0u, number_of_callbacks()); - EXPECT_EQ(0u, urls().size()); +// Verifies that callbacks are notified correctly if requested before top sites +// has loaded. +TEST_F(TopSitesTest, NotifyCallbacksWhenLoaded) { + // Recreate top sites. It won't be loaded now. + profile()->CreateTopSites(); + + EXPECT_FALSE(IsTopSitesLoaded()); + + TopSitesQuerier querier1; + TopSitesQuerier querier2; + TopSitesQuerier querier3; + + // Starts the queries. + querier1.QueryTopSites(top_sites(), false); + querier2.QueryTopSites(top_sites(), false); + querier3.QueryTopSites(top_sites(), false); + + // We shouldn't have gotten a callback. + EXPECT_EQ(0, querier1.number_of_callbacks()); + EXPECT_EQ(0, querier2.number_of_callbacks()); + EXPECT_EQ(0, querier3.number_of_callbacks()); + // Wait for loading to complete. + profile()->BlockUntilTopSitesLoaded(); + + // Now we should have gotten the callbacks. + EXPECT_EQ(1, querier1.number_of_callbacks()); + EXPECT_EQ(GetPrepopulatePages().size(), querier1.urls().size()); + EXPECT_EQ(1, querier2.number_of_callbacks()); + EXPECT_EQ(GetPrepopulatePages().size(), querier2.urls().size()); + EXPECT_EQ(1, querier3.number_of_callbacks()); + EXPECT_EQ(GetPrepopulatePages().size(), querier3.urls().size()); + + // Reset the top sites. MostVisitedURLList pages; MostVisitedURL url; url.url = GURL("http://1.com/"); @@ -985,109 +1005,107 @@ TEST_F(TopSitesTest, QueueingRequestsForTopSites) { url.url = GURL("http://2.com/"); url.redirects.push_back(url.url); pages.push_back(url); - top_sites().OnTopSitesAvailable(0, pages); - MessageLoop::current()->RunAllPending(); + SetTopSites(pages); + + // Recreate top sites. It won't be loaded now. + profile()->CreateTopSites(); + + EXPECT_FALSE(IsTopSitesLoaded()); - EXPECT_EQ(3u, number_of_callbacks()); + TopSitesQuerier querier4; - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ("http://1.com/", urls()[0].url.spec()); - EXPECT_EQ("http://2.com/", urls()[1].url.spec()); - EXPECT_EQ(welcome_url(), urls()[2].url); - EXPECT_EQ(themes_url(), urls()[3].url); + // Query again. + querier4.QueryTopSites(top_sites(), false); + // We shouldn't have gotten a callback. + EXPECT_EQ(0, querier4.number_of_callbacks()); + // Wait for loading to complete. + profile()->BlockUntilTopSitesLoaded(); + + // Now we should have gotten the callbacks. + EXPECT_EQ(1, querier4.number_of_callbacks()); + ASSERT_EQ(2u + GetPrepopulatePages().size(), querier4.urls().size()); + + EXPECT_EQ("http://1.com/", querier4.urls()[0].url.spec()); + EXPECT_EQ("http://2.com/", querier4.urls()[1].url.spec()); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier4, 2)); + + // Reset the top sites again, this time don't reload. url.url = GURL("http://3.com/"); url.redirects.push_back(url.url); pages.push_back(url); - top_sites().OnTopSitesAvailable(0, pages); - MessageLoop::current()->RunAllPending(); + SetTopSites(pages); - top_sites().GetMostVisitedURLs( - &c3, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); + // Query again. + TopSitesQuerier querier5; + querier5.QueryTopSites(top_sites(), true); - EXPECT_EQ(4u, number_of_callbacks()); - - ASSERT_EQ(5u, urls().size()); - EXPECT_EQ("http://1.com/", urls()[0].url.spec()); - EXPECT_EQ("http://2.com/", urls()[1].url.spec()); - EXPECT_EQ("http://3.com/", urls()[2].url.spec()); - EXPECT_EQ(welcome_url(), urls()[3].url); - EXPECT_EQ(themes_url(), urls()[4].url); + EXPECT_EQ(1, querier5.number_of_callbacks()); + ASSERT_EQ(3u + GetPrepopulatePages().size(), querier5.urls().size()); + EXPECT_EQ("http://1.com/", querier5.urls()[0].url.spec()); + EXPECT_EQ("http://2.com/", querier5.urls()[1].url.spec()); + EXPECT_EQ("http://3.com/", querier5.urls()[2].url.spec()); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier5, 3)); } +// Makes sure canceled requests are not notified. TEST_F(TopSitesTest, CancelingRequestsForTopSites) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); - CancelableRequestConsumer c1; - CancelableRequestConsumer c2; - top_sites().GetMostVisitedURLs( - &c1, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - - top_sites().GetMostVisitedURLs( - &c2, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); + // Recreate top sites. It won't be loaded now. + profile()->CreateTopSites(); - { - CancelableRequestConsumer c3; - top_sites().GetMostVisitedURLs( - &c3, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - // c3 is out of scope, and the request should be cancelled. - } + EXPECT_FALSE(IsTopSitesLoaded()); - // No requests until OnTopSitesAvailable is called. - EXPECT_EQ(0u, number_of_callbacks()); - EXPECT_EQ(0u, urls().size()); + TopSitesQuerier querier1; + TopSitesQuerier querier2; - MostVisitedURLList pages; - MostVisitedURL url; - url.url = GURL("http://1.com/"); - url.redirects.push_back(url.url); - pages.push_back(url); - url.url = GURL("http://2.com/"); - pages.push_back(url); + // Starts the queries. + querier1.QueryTopSites(top_sites(), false); + querier2.QueryTopSites(top_sites(), false); - top_sites().OnTopSitesAvailable(0, pages); - MessageLoop::current()->RunAllPending(); + // We shouldn't have gotten a callback. + EXPECT_EQ(0, querier1.number_of_callbacks()); + EXPECT_EQ(0, querier2.number_of_callbacks()); - // 1 request was canceled. - EXPECT_EQ(2u, number_of_callbacks()); + querier2.CancelRequest(); - // 2 extra prepopulated URLs. - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ("http://1.com/", urls()[0].url.spec()); - EXPECT_EQ("http://2.com/", urls()[1].url.spec()); + // Wait for loading to complete. + profile()->BlockUntilTopSitesLoaded(); + + // The first callback should succeed. + EXPECT_EQ(1, querier1.number_of_callbacks()); + EXPECT_EQ(GetPrepopulatePages().size(), querier1.urls().size()); + + // And the canceled callback should not be notified. + EXPECT_EQ(0, querier2.number_of_callbacks()); } +// Makes sure temporary thumbnails are copied over correctly. TEST_F(TopSitesTest, AddTemporaryThumbnail) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); GURL unknown_url("http://news.google.com/"); GURL invalid_url("chrome://thumb/http://google.com/"); GURL url1a("http://google.com/"); GURL url1b("http://www.google.com/"); // Create a dummy thumbnail. - SkBitmap thumbnail; - thumbnail.setConfig(SkBitmap::kARGB_8888_Config, 4, 4); - thumbnail.allocPixels(); - thumbnail.eraseRGB(0x00, 0x00, 0x00); + SkBitmap thumbnail(CreateBitmap(SK_ColorRED)); ThumbnailScore medium_score(0.5, true, true, base::Time::Now()); // Don't store thumbnails for Javascript URLs. - EXPECT_FALSE(top_sites().SetPageThumbnail(invalid_url, - thumbnail, medium_score)); + EXPECT_FALSE(top_sites()->SetPageThumbnail(invalid_url, + thumbnail, + medium_score)); // Store thumbnails for unknown (but valid) URLs temporarily - calls // AddTemporaryThumbnail. - EXPECT_TRUE(top_sites().SetPageThumbnail(unknown_url, - thumbnail, medium_score)); + EXPECT_TRUE(top_sites()->SetPageThumbnail(unknown_url, + thumbnail, + medium_score)); + + // We shouldn't get the thumnail back though (the url isn't in to sites yet). + scoped_refptr<RefCountedBytes> out; + EXPECT_FALSE(top_sites()->GetPageThumbnail(unknown_url, &out)); std::vector<MostVisitedURL> list; @@ -1098,19 +1116,15 @@ TEST_F(TopSitesTest, AddTemporaryThumbnail) { mv.redirects.push_back(url1b); list.push_back(mv); - // Update URLs - use temporary thumbnails. - top_sites().UpdateMostVisited(list); + // Update URLs. This should result in using thumbnail. + SetTopSites(list); - RefCountedBytes* out = NULL; - ASSERT_TRUE(top_sites().GetPageThumbnail(unknown_url, &out)); - scoped_ptr<SkBitmap> out_bitmap(gfx::JPEGCodec::Decode(out->front(), - out->size())); - EXPECT_EQ(0, memcmp(thumbnail.getPixels(), out_bitmap->getPixels(), - thumbnail.getSize())); + ASSERT_TRUE(top_sites()->GetPageThumbnail(unknown_url, &out)); + EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, out.get())); } +// Tests variations of blacklisting. TEST_F(TopSitesTest, Blacklisting) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); MostVisitedURLList pages; MostVisitedURL url, url1; url.url = GURL("http://bbc.com/"); @@ -1120,91 +1134,83 @@ TEST_F(TopSitesTest, Blacklisting) { url1.redirects.push_back(url1.url); pages.push_back(url1); - CancelableRequestConsumer c; - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - top_sites().OnTopSitesAvailable(0, pages); - MessageLoop::current()->RunAllPending(); + SetTopSites(pages); + EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://bbc.com/"))); + + // Blacklist google.com. + top_sites()->AddBlacklistedURL(GURL("http://google.com/")); + + GURL prepopulate_url = GetPrepopulatePages()[0].url; + + EXPECT_TRUE(top_sites()->HasBlacklistedItems()); + EXPECT_TRUE(top_sites()->IsBlacklisted(GURL("http://google.com/"))); + EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://bbc.com/"))); + EXPECT_FALSE(top_sites()->IsBlacklisted(prepopulate_url)); + + // Make sure the blacklisted site isn't returned in the results. { - Lock& l = lock(); - AutoLock lock(l); // IsBlacklisted must be in a lock. - EXPECT_FALSE(top_sites().IsBlacklisted(GURL("http://bbc.com/"))); + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + ASSERT_EQ(1u + GetPrepopulatePages().size(), q.urls().size()); + EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 1)); } - EXPECT_EQ(1u, number_of_callbacks()); + // Recreate top sites and make sure blacklisted url was correctly read. + RecreateTopSitesAndBlock(); + { + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + ASSERT_EQ(1u + GetPrepopulatePages().size(), q.urls().size()); + EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 1)); + } - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ("http://bbc.com/", urls()[0].url.spec()); - EXPECT_EQ("http://google.com/", urls()[1].url.spec()); - EXPECT_EQ(welcome_url(), urls()[2].url); - EXPECT_EQ(themes_url(), urls()[3].url); - EXPECT_FALSE(top_sites().HasBlacklistedItems()); + // Blacklist one of the prepopulate urls. + top_sites()->AddBlacklistedURL(prepopulate_url); + EXPECT_TRUE(top_sites()->HasBlacklistedItems()); - top_sites().AddBlacklistedURL(GURL("http://google.com/")); - EXPECT_TRUE(top_sites().HasBlacklistedItems()); + // Make sure the blacklisted prepopulate url isn't returned. { - Lock& l = lock(); - AutoLock lock(l); // IsBlacklisted must be in a lock. - EXPECT_TRUE(top_sites().IsBlacklisted(GURL("http://google.com/"))); - EXPECT_FALSE(top_sites().IsBlacklisted(GURL("http://bbc.com/"))); - EXPECT_FALSE(top_sites().IsBlacklisted(welcome_url())); + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + ASSERT_EQ(1u + GetPrepopulatePages().size() - 1, q.urls().size()); + EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); + for (size_t i = 1; i < q.urls().size(); ++i) + EXPECT_NE(prepopulate_url.spec(), q.urls()[i].url.spec()); } - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - MessageLoop::current()->RunAllPending(); - EXPECT_EQ(2u, number_of_callbacks()); - ASSERT_EQ(3u, urls().size()); - EXPECT_EQ("http://bbc.com/", urls()[0].url.spec()); - EXPECT_EQ(welcome_url(), urls()[1].url); - EXPECT_EQ(themes_url(), urls()[2].url); - - top_sites().AddBlacklistedURL(welcome_url()); - EXPECT_TRUE(top_sites().HasBlacklistedItems()); - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - ASSERT_EQ(2u, urls().size()); - EXPECT_EQ("http://bbc.com/", urls()[0].url.spec()); - EXPECT_EQ(themes_url(), urls()[1].url); - - top_sites().RemoveBlacklistedURL(GURL("http://google.com/")); - EXPECT_TRUE(top_sites().HasBlacklistedItems()); + // Mark google as no longer blacklisted. + top_sites()->RemoveBlacklistedURL(GURL("http://google.com/")); + EXPECT_TRUE(top_sites()->HasBlacklistedItems()); + EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://google.com/"))); + + // Make sure google is returned now. { - Lock& l = lock(); - AutoLock lock(l); // IsBlacklisted must be in a lock. - EXPECT_FALSE(top_sites().IsBlacklisted(GURL("http://google.com/"))); + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + ASSERT_EQ(2u + GetPrepopulatePages().size() - 1, q.urls().size()); + EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); + EXPECT_EQ("http://google.com/", q.urls()[1].url.spec()); + EXPECT_NE(prepopulate_url.spec(), q.urls()[2].url.spec()); } - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - ASSERT_EQ(3u, urls().size()); - EXPECT_EQ("http://bbc.com/", urls()[0].url.spec()); - EXPECT_EQ("http://google.com/", urls()[1].url.spec()); - EXPECT_EQ(themes_url(), urls()[2].url); - - top_sites().ClearBlacklistedURLs(); - EXPECT_FALSE(top_sites().HasBlacklistedItems()); - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ("http://bbc.com/", urls()[0].url.spec()); - EXPECT_EQ("http://google.com/", urls()[1].url.spec()); - EXPECT_EQ(welcome_url(), urls()[2].url); - EXPECT_EQ(themes_url(), urls()[3].url); + // Remove all blacklisted sites. + top_sites()->ClearBlacklistedURLs(); + EXPECT_FALSE(top_sites()->HasBlacklistedItems()); + + { + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + ASSERT_EQ(2u + GetPrepopulatePages().size(), q.urls().size()); + EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); + EXPECT_EQ("http://google.com/", q.urls()[1].url.spec()); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 2)); + } } +// Tests variations of pinning/unpinning urls. TEST_F(TopSitesTest, PinnedURLs) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); MostVisitedURLList pages; MostVisitedURL url, url1; url.url = GURL("http://bbc.com/"); @@ -1214,127 +1220,127 @@ TEST_F(TopSitesTest, PinnedURLs) { url1.redirects.push_back(url1.url); pages.push_back(url1); - CancelableRequestConsumer c; - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - top_sites().OnTopSitesAvailable(0, pages); - MessageLoop::current()->RunAllPending(); - EXPECT_FALSE(top_sites().IsURLPinned(GURL("http://bbc.com/"))); - - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ("http://bbc.com/", urls()[0].url.spec()); - EXPECT_EQ("http://google.com/", urls()[1].url.spec()); - EXPECT_EQ(welcome_url(), urls()[2].url); - EXPECT_EQ(themes_url(), urls()[3].url); - - top_sites().AddPinnedURL(GURL("http://google.com/"), 3); - EXPECT_FALSE(top_sites().IsURLPinned(GURL("http://bbc.com/"))); - EXPECT_FALSE(top_sites().IsURLPinned(welcome_url())); - - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - EXPECT_EQ(2u, number_of_callbacks()); - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ("http://bbc.com/", urls()[0].url.spec()); - EXPECT_EQ(welcome_url(), urls()[1].url); - EXPECT_EQ(themes_url(), urls()[2].url); - EXPECT_EQ("http://google.com/", urls()[3].url.spec()); - - top_sites().RemovePinnedURL(GURL("http://google.com/")); - EXPECT_FALSE(top_sites().IsURLPinned(GURL("http://google.com/"))); - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ("http://bbc.com/", urls()[0].url.spec()); - EXPECT_EQ("http://google.com/", urls()[1].url.spec()); - EXPECT_EQ(welcome_url(), urls()[2].url); - EXPECT_EQ(themes_url(), urls()[3].url); - - top_sites().AddPinnedURL(GURL("http://bbc.com"), 1); - top_sites().AddPinnedURL(themes_url(), 0); - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ(themes_url(), urls()[0].url); - EXPECT_EQ("http://bbc.com/", urls()[1].url.spec()); - EXPECT_EQ("http://google.com/", urls()[2].url.spec()); - EXPECT_EQ(welcome_url(), urls()[3].url); - - top_sites().RemovePinnedURL(GURL("http://bbc.com")); - top_sites().RemovePinnedURL(themes_url()); - - top_sites().AddPinnedURL(welcome_url(), 1); - top_sites().AddPinnedURL(GURL("http://bbc.com"), 3); - - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - - ASSERT_EQ(4u, urls().size()); - EXPECT_EQ("http://google.com/", urls()[0].url.spec()); - EXPECT_EQ(welcome_url(), urls()[1].url); - EXPECT_EQ(themes_url(), urls()[2].url); - EXPECT_EQ("http://bbc.com/", urls()[3].url.spec()); + SetTopSites(pages); + + EXPECT_FALSE(top_sites()->IsURLPinned(GURL("http://bbc.com/"))); + + { + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + ASSERT_EQ(2u + GetPrepopulatePages().size(), q.urls().size()); + EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); + EXPECT_EQ("http://google.com/", q.urls()[1].url.spec()); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 2)); + } + + top_sites()->AddPinnedURL(GURL("http://google.com/"), 3); + EXPECT_FALSE(top_sites()->IsURLPinned(GURL("http://bbc.com/"))); + EXPECT_FALSE(top_sites()->IsURLPinned(GetPrepopulatePages()[0].url)); + + { + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + ASSERT_EQ(4u, q.urls().size()); + EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 1)); + EXPECT_EQ("http://google.com/", q.urls()[3].url.spec()); + } + + top_sites()->RemovePinnedURL(GURL("http://google.com/")); + EXPECT_FALSE(top_sites()->IsURLPinned(GURL("http://google.com/"))); + { + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + + ASSERT_EQ(2u + GetPrepopulatePages().size(), q.urls().size()); + EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); + EXPECT_EQ("http://google.com/", q.urls()[1].url.spec()); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 2)); + } + + GURL prepopulate_url = GetPrepopulatePages()[0].url; + top_sites()->AddPinnedURL(GURL("http://bbc.com"), 1); + top_sites()->AddPinnedURL(prepopulate_url, 0); + { + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + + ASSERT_EQ(3u + GetPrepopulatePages().size() - 1, q.urls().size()); + EXPECT_EQ(prepopulate_url, q.urls()[0].url); + EXPECT_EQ("http://bbc.com/", q.urls()[1].url.spec()); + EXPECT_EQ("http://google.com/", q.urls()[2].url.spec()); + if (GetPrepopulatePages().size() > 1) + EXPECT_EQ(GetPrepopulatePages()[1].url, q.urls()[3].url); + } + + // Recreate and make sure state remains the same. + RecreateTopSitesAndBlock(); + { + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + + ASSERT_EQ(3u + GetPrepopulatePages().size() - 1, q.urls().size()); + EXPECT_EQ(prepopulate_url, q.urls()[0].url); + EXPECT_EQ("http://bbc.com/", q.urls()[1].url.spec()); + EXPECT_EQ("http://google.com/", q.urls()[2].url.spec()); + if (GetPrepopulatePages().size() > 1) + EXPECT_EQ(GetPrepopulatePages()[1].url, q.urls()[3].url); + } } +// Tests blacklisting and pinning. TEST_F(TopSitesTest, BlacklistingAndPinnedURLs) { - BrowserThread db_loop(BrowserThread::DB, MessageLoop::current()); - MostVisitedURLList pages; - CancelableRequestConsumer c; - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - top_sites().OnTopSitesAvailable(0, pages); - MessageLoop::current()->RunAllPending(); - - ASSERT_EQ(2u, urls().size()); - EXPECT_EQ(welcome_url(), urls()[0].url); - EXPECT_EQ(themes_url(), urls()[1].url); - - top_sites().AddPinnedURL(themes_url(), 1); - top_sites().AddBlacklistedURL(welcome_url()); - - top_sites().GetMostVisitedURLs( - &c, - NewCallback(static_cast<TopSitesTest*>(this), - &TopSitesTest::OnTopSitesAvailable)); - - ASSERT_EQ(2u, urls().size()); - EXPECT_EQ(GURL(), urls()[0].url); - EXPECT_EQ(themes_url(), urls()[1].url); + MostVisitedURLList prepopulate_urls = GetPrepopulatePages(); + if (prepopulate_urls.size() < 2) + return; + + top_sites()->AddPinnedURL(prepopulate_urls[0].url, 1); + top_sites()->AddBlacklistedURL(prepopulate_urls[1].url); + { + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + + ASSERT_LE(2u, q.urls().size()); + EXPECT_EQ(GURL(), q.urls()[0].url); + EXPECT_EQ(prepopulate_urls[0].url, q.urls()[1].url); + } } +// Makes sure prepopulated pages exist. TEST_F(TopSitesTest, AddPrepopulatedPages) { - MostVisitedURLList pages; - top_sites().AddPrepopulatedPages(&pages); - ASSERT_EQ(2u, pages.size()); - EXPECT_EQ(welcome_url(), pages[0].url); - EXPECT_EQ(themes_url(), pages[1].url); - - pages.clear(); + TopSitesQuerier q; + q.QueryTopSites(top_sites(), true); + EXPECT_EQ(GetPrepopulatePages().size(), q.urls().size()); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 0)); - MostVisitedURL url(themes_url(), GURL(), string16()); - pages.push_back(url); + MostVisitedURLList pages = q.urls(); + EXPECT_FALSE(AddPrepopulatedPages(&pages)); - top_sites().AddPrepopulatedPages(&pages); + EXPECT_EQ(GetPrepopulatePages().size(), pages.size()); + q.set_urls(pages); + ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 0)); +} - // Themes URL is already in pages; should not be added twice. - ASSERT_EQ(2u, pages.size()); - EXPECT_EQ(themes_url(), pages[0].url); - EXPECT_EQ(welcome_url(), pages[1].url); +// Makes sure creating top sites before history is created works. +TEST_F(TopSitesTest, CreateTopSitesThenHistory) { + profile()->DestroyTopSites(); + profile()->DestroyHistoryService(); + + // Remove the TopSites file. This forces TopSites to wait until history loads + // before TopSites is considered loaded. + file_util::Delete(profile()->GetPath().Append(chrome::kTopSitesFilename), + false); + + // Create TopSites, but not History. + profile()->CreateTopSites(); + WaitForTopSites(); + EXPECT_FALSE(IsTopSitesLoaded()); + + // Load history, which should make TopSites finish loading too. + profile()->CreateHistoryService(false, false); + profile()->BlockUntilTopSitesLoaded(); + EXPECT_TRUE(IsTopSitesLoaded()); } } // namespace history diff --git a/chrome/browser/history/url_database.cc b/chrome/browser/history/url_database.cc index b63d24a..3f297bd 100644 --- a/chrome/browser/history/url_database.cc +++ b/chrome/browser/history/url_database.cc @@ -247,16 +247,29 @@ bool URLDatabase::IsFavIconUsed(FavIconID favicon_id) { void URLDatabase::AutocompleteForPrefix(const string16& prefix, size_t max_results, + bool typed_only, std::vector<history::URLRow>* results) { // NOTE: this query originally sorted by starred as the second parameter. But // as bookmarks is no longer part of the db we no longer include the order // by clause. results->clear(); - sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, - "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls " - "WHERE url >= ? AND url < ? AND hidden = 0 " - "ORDER BY typed_count DESC, visit_count DESC, last_visit_time DESC " - "LIMIT ?")); + const char* sql; + int line; + if (typed_only) { + sql = "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls " + "WHERE url >= ? AND url < ? AND hidden = 0 AND typed_count > 0 " + "ORDER BY typed_count DESC, visit_count DESC, last_visit_time DESC " + "LIMIT ?"; + line = __LINE__; + } else { + sql = "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls " + "WHERE url >= ? AND url < ? AND hidden = 0 " + "ORDER BY typed_count DESC, visit_count DESC, last_visit_time DESC " + "LIMIT ?"; + line = __LINE__; + } + sql::Statement statement( + GetDB().GetCachedStatement(sql::StatementID(__FILE__, line), sql)); if (!statement) return; @@ -327,7 +340,10 @@ bool URLDatabase::InitKeywordSearchTermsTable() { "term LONGVARCHAR NOT NULL)")) // The actual search term. return false; } + return true; +} +void URLDatabase::CreateKeywordSearchTermsIndices() { // For searching. GetDB().Execute("CREATE INDEX keyword_search_terms_index1 ON " "keyword_search_terms (keyword_id, lower_term)"); @@ -335,8 +351,6 @@ bool URLDatabase::InitKeywordSearchTermsTable() { // For deletion. GetDB().Execute("CREATE INDEX keyword_search_terms_index2 ON " "keyword_search_terms (url_id)"); - - return true; } bool URLDatabase::DropKeywordSearchTermsTable() { diff --git a/chrome/browser/history/url_database.h b/chrome/browser/history/url_database.h index 36bfebb..49bf5f0 100644 --- a/chrome/browser/history/url_database.h +++ b/chrome/browser/history/url_database.h @@ -138,10 +138,12 @@ class URLDatabase { // Autocomplete -------------------------------------------------------------- // Fills the given array with URLs matching the given prefix. They will be - // sorted by typed count, then by visit count, then by visit date (most - // recent first) up to the given maximum number. Called by HistoryURLProvider. + // sorted by typed count, then by visit count, then by visit date (most recent + // first) up to the given maximum number. If |typed_only| is true, only urls + // that have been typed once are returned. Called by HistoryURLProvider. void AutocompleteForPrefix(const string16& prefix, size_t max_results, + bool typed_only, std::vector<URLRow>* results); // Tries to find the shortest URL beginning with |base| that strictly @@ -218,6 +220,9 @@ class URLDatabase { // Ensures the keyword search terms table exists. bool InitKeywordSearchTermsTable(); + // Creates the indices used for keyword search terms. + void CreateKeywordSearchTermsIndices(); + // Deletes the keyword search terms table. bool DropKeywordSearchTermsTable(); diff --git a/chrome/browser/history/url_database_unittest.cc b/chrome/browser/history/url_database_unittest.cc index f567844..c37323f 100644 --- a/chrome/browser/history/url_database_unittest.cc +++ b/chrome/browser/history/url_database_unittest.cc @@ -52,6 +52,7 @@ class URLDatabaseTest : public testing::Test, CreateMainURLIndex(); CreateSupplimentaryURLIndices(); InitKeywordSearchTermsTable(); + CreateKeywordSearchTermsIndices(); } void TearDown() { db_.Close(); diff --git a/chrome/browser/host_content_settings_map.cc b/chrome/browser/host_content_settings_map.cc index 96a9bc2..a7dda7e 100644 --- a/chrome/browser/host_content_settings_map.cc +++ b/chrome/browser/host_content_settings_map.cc @@ -93,7 +93,8 @@ const bool kRequiresResourceIdentifier[CONTENT_SETTINGS_NUM_TYPES] = { // true for various internal objects like chrome:// URLs, so UI and other // things users think of as "not webpages" don't break. static bool ShouldAllowAllContent(const GURL& url) { - return url.SchemeIs(chrome::kChromeInternalScheme) || + return url.SchemeIs(chrome::kChromeDevToolsScheme) || + url.SchemeIs(chrome::kChromeInternalScheme) || url.SchemeIs(chrome::kChromeUIScheme) || url.SchemeIs(chrome::kExtensionScheme) || url.SchemeIs(chrome::kGearsScheme) || @@ -327,12 +328,14 @@ ContentSettings HostContentSettingsMap::GetContentSettings( AutoLock auto_lock(lock_); - // Make the remaining defaults explicit. - for (int j = 0; j < CONTENT_SETTINGS_NUM_TYPES; ++j) - if (output.settings[j] == CONTENT_SETTING_DEFAULT || - RequiresResourceIdentifier(ContentSettingsType(j))) + // If we require a resource identifier, set the content settings to default, + // otherwise make the defaults explicit. + for (int j = 0; j < CONTENT_SETTINGS_NUM_TYPES; ++j) { + if (RequiresResourceIdentifier(ContentSettingsType(j))) + output.settings[j] = CONTENT_SETTING_DEFAULT; + else if (output.settings[j] == CONTENT_SETTING_DEFAULT) output.settings[j] = default_content_settings_.settings[j]; - + } return output; } @@ -657,12 +660,19 @@ void HostContentSettingsMap::SetBlockThirdPartyCookies(bool block) { return; } + PrefService* prefs = profile_->GetPrefs(); + // If the preference block-third-party-cookies is managed then do not allow to + // change it. + if (prefs->IsManagedPreference(prefs::kBlockThirdPartyCookies)) { + NOTREACHED(); + return; + } + { AutoLock auto_lock(lock_); block_third_party_cookies_ = block; } - PrefService* prefs = profile_->GetPrefs(); if (block) prefs->SetBoolean(prefs::kBlockThirdPartyCookies, true); else @@ -1003,4 +1013,3 @@ void HostContentSettingsMap::CanonicalizeContentSettingsExceptions( move_items[i].second, pattern_settings_dictionary); } } - diff --git a/chrome/browser/host_content_settings_map_unittest.cc b/chrome/browser/host_content_settings_map_unittest.cc index ea829d4..b5c74a0 100644 --- a/chrome/browser/host_content_settings_map_unittest.cc +++ b/chrome/browser/host_content_settings_map_unittest.cc @@ -527,8 +527,8 @@ TEST_F(HostContentSettingsMapTest, OffTheRecord) { HostContentSettingsMap* host_content_settings_map = profile.GetHostContentSettingsMap(); profile.set_off_the_record(true); - scoped_refptr<HostContentSettingsMap> otr_map = - new HostContentSettingsMap(&profile); + scoped_refptr<HostContentSettingsMap> otr_map( + new HostContentSettingsMap(&profile)); profile.set_off_the_record(false); GURL host("http://example.com/"); diff --git a/chrome/browser/host_zoom_map.cc b/chrome/browser/host_zoom_map.cc index b9d8d24..19e55bb 100644 --- a/chrome/browser/host_zoom_map.cc +++ b/chrome/browser/host_zoom_map.cc @@ -29,6 +29,8 @@ HostZoomMap::HostZoomMap(Profile* profile) : profile_(profile), updating_preferences_(false) { Load(); + default_zoom_level_ = + profile_->GetPrefs()->GetReal(prefs::kDefaultZoomLevel); registrar_.Add(this, NotificationType::PROFILE_DESTROYED, Source<Profile>(profile)); // Don't observe pref changes (e.g. from sync) in Incognito; once we create @@ -37,6 +39,7 @@ HostZoomMap::HostZoomMap(Profile* profile) if (!profile_->IsOffTheRecord()) { pref_change_registrar_.Init(profile_->GetPrefs()); pref_change_registrar_.Add(prefs::kPerHostZoomLevels, this); + pref_change_registrar_.Add(prefs::kDefaultZoomLevel, this); } registrar_.Add( @@ -94,7 +97,7 @@ double HostZoomMap::GetZoomLevel(const GURL& url) const { std::string host(net::GetHostOrSpecFromURL(url)); AutoLock auto_lock(lock_); HostZoomLevels::const_iterator i(host_zoom_levels_.find(host)); - return (i == host_zoom_levels_.end()) ? 0 : i->second; + return (i == host_zoom_levels_.end()) ? default_zoom_level_ : i->second; } void HostZoomMap::SetZoomLevel(const GURL& url, double level) { @@ -106,7 +109,7 @@ void HostZoomMap::SetZoomLevel(const GURL& url, double level) { { AutoLock auto_lock(lock_); - if (level == 0) + if (level == default_zoom_level_) host_zoom_levels_.erase(host); else host_zoom_levels_[host] = level; @@ -126,7 +129,7 @@ void HostZoomMap::SetZoomLevel(const GURL& url, double level) { ScopedPrefUpdate update(profile_->GetPrefs(), prefs::kPerHostZoomLevels); DictionaryValue* host_zoom_dictionary = profile_->GetPrefs()->GetMutableDictionary(prefs::kPerHostZoomLevels); - if (level == 0) { + if (level == default_zoom_level_) { host_zoom_dictionary->RemoveWithoutPathExpansion(host, NULL); } else { host_zoom_dictionary->SetWithoutPathExpansion( @@ -240,6 +243,10 @@ void HostZoomMap::Observe( std::string* name = Details<std::string>(details).ptr(); if (prefs::kPerHostZoomLevels == *name) Load(); + else if (prefs::kDefaultZoomLevel == *name) { + default_zoom_level_ = + profile_->GetPrefs()->GetReal(prefs::kDefaultZoomLevel); + } } break; } diff --git a/chrome/browser/host_zoom_map.h b/chrome/browser/host_zoom_map.h index b75dcea..10f512e 100644 --- a/chrome/browser/host_zoom_map.h +++ b/chrome/browser/host_zoom_map.h @@ -34,22 +34,22 @@ class HostZoomMap : public NotificationObserver, // Returns the zoom level for a given url. The zoom level is determined by // the host portion of the URL, or (in the absence of a host) the complete // spec of the URL. In most cases, there is no custom zoom level, and this - // returns 0. Otherwise, returns the saved zoom level, which may be positive - // (to zoom in) or negative (to zoom out). + // returns the user's default zoom level. Otherwise, returns the saved zoom + // level, which may be positive (to zoom in) or negative (to zoom out). // // This may be called on any thread. double GetZoomLevel(const GURL& url) const; - // Sets the zoom level for a given url to |level|. If the level is 0, - // the host is erased from the saved preferences; otherwise the new value is - // written out. + // Sets the zoom level for a given url to |level|. If the level matches the + // current default zoom level, the host is erased from the saved preferences; + // otherwise the new value is written out. // // This should only be called on the UI thread. void SetZoomLevel(const GURL& url, double level); // Returns the temporary zoom level that's only valid for the lifetime of // the given tab (i.e. isn't saved and doesn't affect other tabs) if it - // exists, 0 otherwise. + // exists, the default zoom level otherwise. // // This may be called on any thread. double GetTemporaryZoomLevel(int render_process_id, @@ -92,6 +92,7 @@ class HostZoomMap : public NotificationObserver, // Copy of the pref data, so that we can read it on the IO thread. HostZoomLevels host_zoom_levels_; + double default_zoom_level_; struct TemporaryZoomLevel { int render_process_id; @@ -103,8 +104,8 @@ class HostZoomMap : public NotificationObserver, // level, so vector is fine for now. std::vector<TemporaryZoomLevel> temporary_zoom_levels_; - // Used around accesses to |host_zoom_levels_| and |temporary_zoom_levels_| to - // guarantee thread safety. + // Used around accesses to |host_zoom_levels_|, |default_zoom_level_| and + // |temporary_zoom_levels_| to guarantee thread safety. mutable Lock lock_; // Whether we are currently updating preferences, this is used to ignore diff --git a/chrome/browser/host_zoom_map_unittest.cc b/chrome/browser/host_zoom_map_unittest.cc index ea23b33..f77f96b 100644 --- a/chrome/browser/host_zoom_map_unittest.cc +++ b/chrome/browser/host_zoom_map_unittest.cc @@ -27,6 +27,7 @@ using testing::Property; class HostZoomMapTest : public testing::Test { public: static const double kZoomLevel; + static const double kDefaultZoomLevel; HostZoomMapTest() : ui_thread_(BrowserThread::UI, &message_loop_), prefs_(profile_.GetPrefs()), @@ -54,6 +55,7 @@ class HostZoomMapTest : public testing::Test { NotificationObserverMock pref_observer_; }; const double HostZoomMapTest::kZoomLevel = 4; +const double HostZoomMapTest::kDefaultZoomLevel = -2; TEST_F(HostZoomMapTest, LoadNoPrefs) { scoped_refptr<HostZoomMap> map(new HostZoomMap(&profile_)); @@ -120,3 +122,11 @@ TEST_F(HostZoomMapTest, NoHost) { EXPECT_EQ(kZoomLevel, map->GetZoomLevel(file_url1_)); EXPECT_EQ(0, map->GetZoomLevel(file_url2_)); } + +TEST_F(HostZoomMapTest, ChangeDefaultZoomLevel) { + FundamentalValue zoom_level(kDefaultZoomLevel); + prefs_->Set(prefs::kDefaultZoomLevel, zoom_level); + scoped_refptr<HostZoomMap> map(new HostZoomMap(&profile_)); + EXPECT_EQ(kDefaultZoomLevel, map->GetZoomLevel(url_)); +} + diff --git a/chrome/browser/icon_loader.cc b/chrome/browser/icon_loader.cc index 2281b89..ea22b69 100644 --- a/chrome/browser/icon_loader.cc +++ b/chrome/browser/icon_loader.cc @@ -1,12 +1,10 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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/icon_loader.h" -#include "base/message_loop.h" -#include "base/thread.h" -#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_thread.h" #include "third_party/skia/include/core/SkBitmap.h" #if defined(TOOLKIT_GTK) @@ -23,22 +21,21 @@ IconLoader::IconLoader(const IconGroupID& group, IconSize size, } IconLoader::~IconLoader() { - delete bitmap_; } void IconLoader::Start() { - target_message_loop_ = MessageLoop::current(); + target_message_loop_ = base::MessageLoopProxy::CreateForCurrentThread(); #if defined(TOOLKIT_GTK) // This call must happen on the UI thread before we can start loading icons. mime_util::DetectGtkTheme(); #endif - g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, NewRunnableMethod(this, &IconLoader::ReadIcon)); } void IconLoader::NotifyDelegate() { - if (delegate_->OnBitmapLoaded(this, bitmap_)) - bitmap_ = NULL; + if (delegate_->OnBitmapLoaded(this, bitmap_.Get())) + bitmap_.Release(); } diff --git a/chrome/browser/icon_loader.h b/chrome/browser/icon_loader.h index 32b4048..8d055e3 100644 --- a/chrome/browser/icon_loader.h +++ b/chrome/browser/icon_loader.h @@ -11,10 +11,13 @@ #include <string> #include "base/basictypes.h" +#include "base/message_loop_proxy.h" +#include "base/ref_counted.h" +#include "gfx/scoped_image.h" + #if defined(TOOLKIT_USES_GTK) #include "base/file_path.h" #endif -#include "base/ref_counted.h" #if defined(OS_WIN) // On Windows, we group files by their extension, with several exceptions: @@ -68,13 +71,13 @@ class IconLoader : public base::RefCountedThreadSafe<IconLoader> { void NotifyDelegate(); // The message loop object of the thread in which we notify the delegate. - MessageLoop* target_message_loop_; + scoped_refptr<base::MessageLoopProxy> target_message_loop_; IconGroupID group_; IconSize icon_size_; - SkBitmap* bitmap_; + gfx::ScopedImage<SkBitmap> bitmap_; Delegate* delegate_; diff --git a/chrome/browser/icon_loader_linux.cc b/chrome/browser/icon_loader_linux.cc index de26857..2452b4a 100644 --- a/chrome/browser/icon_loader_linux.cc +++ b/chrome/browser/icon_loader_linux.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -10,12 +10,12 @@ #include "base/file_util.h" #include "base/logging.h" -#include "app/gtk_util.h" #include "base/message_loop.h" #include "base/mime_util.h" #include "base/thread.h" #include "base/string_util.h" #include "gfx/codec/png_codec.h" +#include "gfx/gtk_util.h" #include "third_party/skia/include/core/SkBitmap.h" static int SizeToInt(IconLoader::IconSize size) { @@ -61,13 +61,13 @@ void IconLoader::ParseIcon() { LOG(WARNING) << "Got an image with no alpha channel, aborting load."; } else { uint8_t* BGRA_pixels = - gtk_util::BGRAToRGBA(pixels, width, height, stride); + gfx::BGRAToRGBA(pixels, width, height, stride); std::vector<unsigned char> pixel_vector; pixel_vector.resize(height * stride); memcpy(const_cast<unsigned char*>(pixel_vector.data()), BGRA_pixels, height * stride); - bitmap_ = gfx::PNGCodec::CreateSkBitmapFromBGRAFormat(pixel_vector, - width, height); + bitmap_.Set(gfx::PNGCodec::CreateSkBitmapFromBGRAFormat(pixel_vector, + width, height)); free(BGRA_pixels); } } else { diff --git a/chrome/browser/icon_loader_mac.mm b/chrome/browser/icon_loader_mac.mm index 028edf7..441d12a 100644 --- a/chrome/browser/icon_loader_mac.mm +++ b/chrome/browser/icon_loader_mac.mm @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -25,7 +25,7 @@ void IconLoader::ReadIcon() { else return; - bitmap_ = new SkBitmap(gfx::NSImageToSkBitmap(icon, size, false)); + bitmap_.Set(new SkBitmap(gfx::NSImageToSkBitmap(icon, size, false))); target_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &IconLoader::NotifyDelegate)); diff --git a/chrome/browser/icon_loader_win.cc b/chrome/browser/icon_loader_win.cc index 6fb5824..dfa115c 100644 --- a/chrome/browser/icon_loader_win.cc +++ b/chrome/browser/icon_loader_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -42,7 +42,7 @@ void IconLoader::ReadIcon() { DCHECK(r); gfx::Size icon_size(bitmap_info.bmWidth, bitmap_info.bmHeight); - bitmap_ = IconUtil::CreateSkBitmapFromHICON(file_info.hIcon, icon_size); + bitmap_.Set(IconUtil::CreateSkBitmapFromHICON(file_info.hIcon, icon_size)); target_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &IconLoader::NotifyDelegate)); diff --git a/chrome/browser/icon_manager.cc b/chrome/browser/icon_manager.cc index 98ff322..8f74ce4 100644 --- a/chrome/browser/icon_manager.cc +++ b/chrome/browser/icon_manager.cc @@ -10,6 +10,12 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" +struct IconManager::ClientRequest { + scoped_refptr<IconRequest> request; + IconGroupID group; + IconLoader::IconSize size; +}; + IconManager::IconManager() { } diff --git a/chrome/browser/icon_manager.h b/chrome/browser/icon_manager.h index 7ccd55a..210fa28 100644 --- a/chrome/browser/icon_manager.h +++ b/chrome/browser/icon_manager.h @@ -106,13 +106,9 @@ class IconManager : public IconLoader::Delegate, IconMap icon_cache_; typedef CancelableRequest<IconRequestCallback> IconRequest; - typedef struct { - scoped_refptr<IconRequest> request; - IconGroupID group; - IconLoader::IconSize size; - } ClientRequest; // Asynchronous requests that have not yet been completed. + struct ClientRequest; typedef std::map<IconLoader*, ClientRequest> ClientRequests; ClientRequests requests_; diff --git a/chrome/browser/importer/firefox2_importer.cc b/chrome/browser/importer/firefox2_importer.cc index a6b6534..7783a7c 100644 --- a/chrome/browser/importer/firefox2_importer.cc +++ b/chrome/browser/importer/firefox2_importer.cc @@ -50,7 +50,7 @@ Firefox2Importer::Firefox2Importer() : parsing_bookmarks_html_file_(false) { Firefox2Importer::~Firefox2Importer() { } -void Firefox2Importer::StartImport(importer::ProfileInfo profile_info, +void Firefox2Importer::StartImport(const importer::ProfileInfo& profile_info, uint16 items, ImporterBridge* bridge) { bridge_ = bridge; diff --git a/chrome/browser/importer/firefox2_importer.h b/chrome/browser/importer/firefox2_importer.h index 07a5567..5264c55 100644 --- a/chrome/browser/importer/firefox2_importer.h +++ b/chrome/browser/importer/firefox2_importer.h @@ -21,7 +21,7 @@ class Firefox2Importer : public Importer { Firefox2Importer(); // Importer methods. - virtual void StartImport(importer::ProfileInfo profile_info, + virtual void StartImport(const importer::ProfileInfo& profile_info, uint16 items, ImporterBridge* bridge); diff --git a/chrome/browser/importer/firefox3_importer.cc b/chrome/browser/importer/firefox3_importer.cc index 92637d0..6442c87 100644 --- a/chrome/browser/importer/firefox3_importer.cc +++ b/chrome/browser/importer/firefox3_importer.cc @@ -56,7 +56,7 @@ Firefox3Importer::Firefox3Importer() { Firefox3Importer::~Firefox3Importer() { } -void Firefox3Importer::StartImport(importer::ProfileInfo profile_info, +void Firefox3Importer::StartImport(const importer::ProfileInfo& profile_info, uint16 items, ImporterBridge* bridge) { #if defined(OS_LINUX) diff --git a/chrome/browser/importer/firefox3_importer.h b/chrome/browser/importer/firefox3_importer.h index 362f710..fe3deb7 100644 --- a/chrome/browser/importer/firefox3_importer.h +++ b/chrome/browser/importer/firefox3_importer.h @@ -27,7 +27,7 @@ class Firefox3Importer : public Importer { Firefox3Importer(); // Importer methods. - virtual void StartImport(importer::ProfileInfo profile_info, + virtual void StartImport(const importer::ProfileInfo& profile_info, uint16 items, ImporterBridge* bridge); diff --git a/chrome/browser/importer/firefox_importer_unittest_utils_mac.cc b/chrome/browser/importer/firefox_importer_unittest_utils_mac.cc index e35f7a7..fbe3d3d 100644 --- a/chrome/browser/importer/firefox_importer_unittest_utils_mac.cc +++ b/chrome/browser/importer/firefox_importer_unittest_utils_mac.cc @@ -178,8 +178,8 @@ bool FFUnitTestDecryptorProxy::WaitForClientResponse() { // the future and cancel it if an RPC message comes back earlier. // This relies on the IPC listener class to quit the message loop itself when // a message comes in. - scoped_refptr<CancellableQuitMsgLoop> quit_task = - new CancellableQuitMsgLoop(); + scoped_refptr<CancellableQuitMsgLoop> quit_task( + new CancellableQuitMsgLoop()); MessageLoop::current()->PostDelayedTask( FROM_HERE, NewRunnableMethod(quit_task.get(), &CancellableQuitMsgLoop::QuitNow), diff --git a/chrome/browser/importer/firefox_importer_utils.cc b/chrome/browser/importer/firefox_importer_utils.cc index e223491..be606ff 100644 --- a/chrome/browser/importer/firefox_importer_utils.cc +++ b/chrome/browser/importer/firefox_importer_utils.cc @@ -18,6 +18,7 @@ #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/search_engines/template_url_parser.h" +#include "chrome/browser/search_engines/template_url_prepopulate_data.h" #include "googleurl/src/gurl.h" namespace { @@ -214,8 +215,11 @@ void ParseSearchEnginesFromXMLFiles(const std::vector<FilePath>& xml_files, search_engine_for_url.erase(iter); } // Give this a keyword to facilitate tab-to-search, if possible. + GURL gurl = GURL(url); template_url->set_keyword( - TemplateURLModel::GenerateKeyword(GURL(url), false)); + TemplateURLModel::GenerateKeyword(gurl, false)); + template_url->set_logo_id( + TemplateURLPrepopulateData::GetSearchEngineLogo(gurl)); template_url->set_show_in_default_list(true); search_engine_for_url[url] = template_url; if (!default_turl) diff --git a/chrome/browser/importer/ie_importer.cc b/chrome/browser/importer/ie_importer.cc index f2905dd..efa2a0a 100644 --- a/chrome/browser/importer/ie_importer.cc +++ b/chrome/browser/importer/ie_importer.cc @@ -33,6 +33,7 @@ #include "chrome/browser/password_manager/ie7_password.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" +#include "chrome/browser/search_engines/template_url_prepopulate_data.h" #include "chrome/common/time_format.h" #include "chrome/common/url_constants.h" #include "googleurl/src/gurl.h" @@ -40,8 +41,6 @@ #include "webkit/glue/password_form.h" using base::Time; -using base::win::RegKey; -using base::win::RegistryValueIterator; using webkit_glue::PasswordForm; namespace { @@ -70,7 +69,7 @@ const GUID IEImporter::kPStoreAutocompleteGUID = {0xe161255a, 0x37c3, 0x11d2, const GUID IEImporter::kUnittestGUID = { 0xa79029d6, 0x753e, 0x4e27, {0xb8, 0x7, 0x3d, 0x46, 0xab, 0x15, 0x45, 0xdf}}; -void IEImporter::StartImport(ProfileInfo profile_info, +void IEImporter::StartImport(const ProfileInfo& profile_info, uint16 items, ImporterBridge* bridge) { bridge_ = bridge; @@ -259,8 +258,9 @@ void IEImporter::ImportPasswordsIE7() { const wchar_t kStorage2Path[] = L"Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2"; - RegKey key(HKEY_CURRENT_USER, kStorage2Path, KEY_READ); - RegistryValueIterator reg_iterator(HKEY_CURRENT_USER, kStorage2Path); + base::win::RegKey key(HKEY_CURRENT_USER, kStorage2Path, KEY_READ); + base::win::RegistryValueIterator reg_iterator(HKEY_CURRENT_USER, + kStorage2Path); while (reg_iterator.Valid() && !cancelled()) { // Get the size of the encrypted data. DWORD value_len = 0; @@ -351,7 +351,7 @@ void IEImporter::ImportSearchEngines() { const wchar_t kSearchScopePath[] = L"Software\\Microsoft\\Internet Explorer\\SearchScopes"; - RegKey key(HKEY_CURRENT_USER, kSearchScopePath, KEY_READ); + base::win::RegKey key(HKEY_CURRENT_USER, kSearchScopePath, KEY_READ); std::wstring default_search_engine_name; const TemplateURL* default_search_engine = NULL; std::map<std::string, TemplateURL*> search_engines_map; @@ -361,7 +361,8 @@ void IEImporter::ImportSearchEngines() { while (key_iterator.Valid()) { std::wstring sub_key_name = kSearchScopePath; sub_key_name.append(L"\\").append(key_iterator.Name()); - RegKey sub_key(HKEY_CURRENT_USER, sub_key_name.c_str(), KEY_READ); + base::win::RegKey sub_key(HKEY_CURRENT_USER, sub_key_name.c_str(), + KEY_READ); std::wstring wide_url; if (!sub_key.ReadValue(L"URL", &wide_url) || wide_url.empty()) { VLOG(1) << "No URL for IE search engine at " << key_iterator.Name(); @@ -392,8 +393,11 @@ void IEImporter::ImportSearchEngines() { template_url->set_short_name(name); template_url->SetURL(url, 0, 0); // Give this a keyword to facilitate tab-to-search, if possible. - template_url->set_keyword(TemplateURLModel::GenerateKeyword(GURL(url), + GURL gurl = GURL(url); + template_url->set_keyword(TemplateURLModel::GenerateKeyword(gurl, false)); + template_url->set_logo_id( + TemplateURLPrepopulateData::GetSearchEngineLogo(gurl)); template_url->set_show_in_default_list(true); search_engines_map[url] = template_url; } @@ -425,7 +429,7 @@ void IEImporter::ImportHomepage() { const wchar_t kIEHomepage[] = L"Start Page"; const wchar_t kIEDefaultHomepage[] = L"Default_Page_URL"; - RegKey key(HKEY_CURRENT_USER, kIESettingsMain, KEY_READ); + base::win::RegKey key(HKEY_CURRENT_USER, kIESettingsMain, KEY_READ); std::wstring homepage_url; if (!key.ReadValue(kIEHomepage, &homepage_url) || homepage_url.empty()) return; @@ -435,7 +439,7 @@ void IEImporter::ImportHomepage() { return; // Check to see if this is the default website and skip import. - RegKey keyDefault(HKEY_LOCAL_MACHINE, kIESettingsMain, KEY_READ); + base::win::RegKey keyDefault(HKEY_LOCAL_MACHINE, kIESettingsMain, KEY_READ); std::wstring default_homepage_url; if (keyDefault.ReadValue(kIEDefaultHomepage, &default_homepage_url) && !default_homepage_url.empty()) { @@ -469,9 +473,8 @@ bool IEImporter::GetFavoritesInfo(IEImporter::FavoritesInfo *info) { if (base::win::GetVersion() < base::win::VERSION_VISTA) { // The Link folder name is stored in the registry. DWORD buffer_length = sizeof(buffer); - RegKey reg_key(HKEY_CURRENT_USER, - L"Software\\Microsoft\\Internet Explorer\\Toolbar", - KEY_READ); + base::win::RegKey reg_key(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Internet Explorer\\Toolbar", KEY_READ); if (!reg_key.ReadValue(L"LinksFolderName", buffer, &buffer_length, NULL)) return false; info->links_folder = buffer; @@ -579,8 +582,8 @@ int IEImporter::CurrentIEVersion() const { if (version < 0) { wchar_t buffer[128]; DWORD buffer_length = sizeof(buffer); - RegKey reg_key(HKEY_LOCAL_MACHINE, - L"Software\\Microsoft\\Internet Explorer", KEY_READ); + base::win::RegKey reg_key(HKEY_LOCAL_MACHINE, + L"Software\\Microsoft\\Internet Explorer", KEY_READ); bool result = reg_key.ReadValue(L"Version", buffer, &buffer_length, NULL); version = (result ? _wtoi(buffer) : 0); } diff --git a/chrome/browser/importer/ie_importer.h b/chrome/browser/importer/ie_importer.h index 1acd27b..68b7c23 100644 --- a/chrome/browser/importer/ie_importer.h +++ b/chrome/browser/importer/ie_importer.h @@ -14,7 +14,7 @@ class IEImporter : public Importer { IEImporter() {} // Importer methods. - virtual void StartImport(ProfileInfo browser_info, + virtual void StartImport(const ProfileInfo& browser_info, uint16 items, ImporterBridge* bridge); diff --git a/chrome/browser/importer/importer.cc b/chrome/browser/importer/importer.cc index 9a40471..32f48d3 100644 --- a/chrome/browser/importer/importer.cc +++ b/chrome/browser/importer/importer.cc @@ -9,6 +9,7 @@ #include "base/values.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/browsing_instance.h" @@ -200,15 +201,11 @@ void ImporterHost::StartImportSettings( MB_OK | MB_TOPMOST); GURL url("https://www.google.com/accounts/ServiceLogin"); - Browser* browser = BrowserList::GetLastActive(); - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - // BrowsingInstance is refcounted. - BrowsingInstance* instance = new BrowsingInstance(writer_->profile()); - params.instance = instance->GetSiteInstanceForURL(url); - browser->AddTabWithURL(¶ms); + BrowserList::GetLastActive()->AddSelectedTabWithURL( + url, PageTransition::TYPED); MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - this, &ImporterHost::OnLockViewEnd, false)); + this, &ImporterHost::OnLockViewEnd, false)); is_source_readable_ = false; } diff --git a/chrome/browser/importer/importer.h b/chrome/browser/importer/importer.h index 4887138..1cb9db7 100644 --- a/chrome/browser/importer/importer.h +++ b/chrome/browser/importer/importer.h @@ -442,7 +442,7 @@ class Importer : public base::RefCountedThreadSafe<Importer> { // Since we do async import, the importer should invoke // ImporterHost::Finished() to notify its host that import // stuff have been finished. - virtual void StartImport(importer::ProfileInfo profile_info, + virtual void StartImport(const importer::ProfileInfo& profile_info, uint16 items, ImporterBridge* bridge) = 0; @@ -514,7 +514,7 @@ class ImportObserver { // if there's nothing to parent to. first_run is true if it's invoked in the // first run UI. void StartImportingWithUI(gfx::NativeWindow parent_window, - int16 items, + uint16 items, ImporterHost* coordinator, const importer::ProfileInfo& source_profile, Profile* target_profile, diff --git a/chrome/browser/importer/importer_list.cc b/chrome/browser/importer/importer_list.cc index f529284..6ac6517 100644 --- a/chrome/browser/importer/importer_list.cc +++ b/chrome/browser/importer/importer_list.cc @@ -35,9 +35,9 @@ ImporterList::~ImporterList() { } void ImporterList::DetectSourceProfiles() { +// The first run import will automatically take settings from the first +// profile detected, which should be the user's current default. #if defined(OS_WIN) - // The order in which detect is called determines the order - // in which the options appear in the dropdown combo-box if (ShellIntegration::IsFirefoxDefaultBrowser()) { DetectFirefoxProfiles(); DetectIEProfiles(); @@ -47,10 +47,15 @@ void ImporterList::DetectSourceProfiles() { } // TODO(brg) : Current UI requires win_util. DetectGoogleToolbarProfiles(); +#elif defined(OS_MACOSX) + if (ShellIntegration::IsFirefoxDefaultBrowser()) { + DetectFirefoxProfiles(); + DetectSafariProfiles(); + } else { + DetectSafariProfiles(); + DetectFirefoxProfiles(); + } #else -#if defined(OS_MACOSX) - DetectSafariProfiles(); -#endif DetectFirefoxProfiles(); #endif } diff --git a/chrome/browser/importer/importer_unittest.cc b/chrome/browser/importer/importer_unittest.cc index 2bad8ee..214f056 100644 --- a/chrome/browser/importer/importer_unittest.cc +++ b/chrome/browser/importer/importer_unittest.cc @@ -115,14 +115,14 @@ class ImporterTest : public testing::Test { profile_info.browser_type = FIREFOX3; profile_info.app_path = app_path_; profile_info.source_path = profile_path_; - scoped_refptr<ImporterHost> host = new ImporterHost(); + scoped_refptr<ImporterHost> host(new ImporterHost()); host->SetObserver(observer); int items = HISTORY | PASSWORDS | FAVORITES; if (import_search_plugins) items = items | SEARCH_ENGINES; loop->PostTask(FROM_HERE, NewRunnableMethod(host.get(), &ImporterHost::StartImportSettings, profile_info, - static_cast<Profile*>(NULL), items, writer, true)); + static_cast<Profile*>(NULL), items, make_scoped_refptr(writer), true)); loop->Run(); } @@ -697,7 +697,7 @@ TEST_F(ImporterTest, MAYBE(Firefox2Importer)) { ASSERT_TRUE(file_util::CopyDirectory(data_path, search_engine_path, false)); MessageLoop* loop = MessageLoop::current(); - scoped_refptr<ImporterHost> host = new ImporterHost(); + scoped_refptr<ImporterHost> host(new ImporterHost()); FirefoxObserver* observer = new FirefoxObserver(); host->SetObserver(observer); ProfileInfo profile_info; @@ -705,10 +705,14 @@ TEST_F(ImporterTest, MAYBE(Firefox2Importer)) { profile_info.app_path = app_path_; profile_info.source_path = profile_path_; - loop->PostTask(FROM_HERE, NewRunnableMethod(host.get(), - &ImporterHost::StartImportSettings, profile_info, + loop->PostTask(FROM_HERE, NewRunnableMethod( + host.get(), + &ImporterHost::StartImportSettings, + profile_info, static_cast<Profile*>(NULL), - HISTORY | PASSWORDS | FAVORITES | SEARCH_ENGINES, observer, true)); + HISTORY | PASSWORDS | FAVORITES | SEARCH_ENGINES, + make_scoped_refptr(observer), + true)); loop->Run(); } @@ -880,15 +884,15 @@ class Firefox3Observer : public ProfileWriter, }; TEST_F(ImporterTest, MAYBE(Firefox30Importer)) { - scoped_refptr<Firefox3Observer> observer = new Firefox3Observer(); + scoped_refptr<Firefox3Observer> observer(new Firefox3Observer()); Firefox3xImporterTest("firefox3_profile", observer.get(), observer.get(), true); } TEST_F(ImporterTest, MAYBE(Firefox35Importer)) { bool import_search_engines = false; - scoped_refptr<Firefox3Observer> observer = - new Firefox3Observer(import_search_engines); + scoped_refptr<Firefox3Observer> observer( + new Firefox3Observer(import_search_engines)); Firefox3xImporterTest("firefox35_profile", observer.get(), observer.get(), import_search_engines); } diff --git a/chrome/browser/importer/nss_decryptor.h b/chrome/browser/importer/nss_decryptor.h index 8361d17..9896a14 100644 --- a/chrome/browser/importer/nss_decryptor.h +++ b/chrome/browser/importer/nss_decryptor.h @@ -12,6 +12,13 @@ #include "chrome/browser/importer/nss_decryptor_mac.h" #elif defined(OS_WIN) #include "chrome/browser/importer/nss_decryptor_win.h" +#elif defined(USE_OPENSSL) +// TODO(joth): It should be an error to include this file with USE_OPENSSL +// defined. (Unless there is a way to do nss decrypt with OpenSSL). Ideally +// we remove the importers that depend on NSS when doing USE_OPENSSL builds, but +// that is going to take some non-trivial refactoring so in the meantime we're +// just falling back to a no-op implementation. +#include "chrome/browser/importer/nss_decryptor_null.h" #elif defined(USE_NSS) #include "chrome/browser/importer/nss_decryptor_system_nss.h" #endif diff --git a/chrome/browser/importer/nss_decryptor_null.h b/chrome/browser/importer/nss_decryptor_null.h new file mode 100644 index 0000000..155f1e0 --- /dev/null +++ b/chrome/browser/importer/nss_decryptor_null.h @@ -0,0 +1,40 @@ +// 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_IMPORTER_NSS_DECRYPTOR_NULL_H_ +#define CHROME_BROWSER_IMPORTER_NSS_DECRYPTOR_NULL_H_ +#pragma once + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/string16.h" + +class FilePath; + +namespace webkit_glue { +struct PasswordForm; +} + +// A NULL wrapper for Firefox NSS decrypt component, for use in builds where +// we do not have the NSS library. +class NSSDecryptor { + public: + NSSDecryptor() {} + bool Init(const std::wstring& /* dll_path */, + const std::wstring& db_path) { return false; } + string16 Decrypt(const std::string& crypt) const { return string16(); } + void ParseSignons(const std::string& content, + std::vector<webkit_glue::PasswordForm>* forms) {} + bool ReadAndParseSignons(const FilePath& sqlite_file, + std::vector<webkit_glue::PasswordForm>* forms) { + return false; + } + + private: + DISALLOW_COPY_AND_ASSIGN(NSSDecryptor); +}; + +#endif // CHROME_BROWSER_IMPORTER_NSS_DECRYPTOR_NULL_H_ diff --git a/chrome/browser/importer/safari_importer.h b/chrome/browser/importer/safari_importer.h index e723146..7e234f5 100644 --- a/chrome/browser/importer/safari_importer.h +++ b/chrome/browser/importer/safari_importer.h @@ -33,7 +33,7 @@ class SafariImporter : public Importer { explicit SafariImporter(const FilePath& library_dir); // Importer methods. - virtual void StartImport(importer::ProfileInfo profile_info, + virtual void StartImport(const importer::ProfileInfo& profile_info, uint16 items, ImporterBridge* bridge); diff --git a/chrome/browser/importer/safari_importer.mm b/chrome/browser/importer/safari_importer.mm index 3ff4676..de40655 100644 --- a/chrome/browser/importer/safari_importer.mm +++ b/chrome/browser/importer/safari_importer.mm @@ -76,7 +76,7 @@ bool SafariImporter::CanImport(const FilePath& library_dir, return *services_supported != importer::NONE; } -void SafariImporter::StartImport(importer::ProfileInfo profile_info, +void SafariImporter::StartImport(const importer::ProfileInfo& profile_info, uint16 services_supported, ImporterBridge* bridge) { bridge_ = bridge; diff --git a/chrome/browser/importer/toolbar_importer.cc b/chrome/browser/importer/toolbar_importer.cc index 0aabcc6..da367f8 100644 --- a/chrome/browser/importer/toolbar_importer.cc +++ b/chrome/browser/importer/toolbar_importer.cc @@ -99,7 +99,7 @@ Toolbar5Importer::~Toolbar5Importer() { DCHECK(!data_fetcher_); } -void Toolbar5Importer::StartImport(importer::ProfileInfo profile_info, +void Toolbar5Importer::StartImport(const importer::ProfileInfo& profile_info, uint16 items, ImporterBridge* bridge) { DCHECK(bridge); diff --git a/chrome/browser/importer/toolbar_importer.h b/chrome/browser/importer/toolbar_importer.h index 1def25d..f83135a 100644 --- a/chrome/browser/importer/toolbar_importer.h +++ b/chrome/browser/importer/toolbar_importer.h @@ -42,7 +42,7 @@ class Toolbar5Importer : public URLFetcher::Delegate, public Importer { // should only either be NONE or FAVORITES, since as of right now these are // the only items this importer supports. This method provides implementation // of Importer::StartImport. - virtual void StartImport(importer::ProfileInfo profile_info, + virtual void StartImport(const importer::ProfileInfo& profile_info, uint16 items, ImporterBridge* bridge); diff --git a/chrome/browser/in_process_webkit/browser_webkitclient_impl.cc b/chrome/browser/in_process_webkit/browser_webkitclient_impl.cc index eb696ac..365c91c 100644 --- a/chrome/browser/in_process_webkit/browser_webkitclient_impl.cc +++ b/chrome/browser/in_process_webkit/browser_webkitclient_impl.cc @@ -1,6 +1,6 @@ -// Copyright (c) 2009 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. +// 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/in_process_webkit/browser_webkitclient_impl.h" @@ -150,8 +150,8 @@ void BrowserWebKitClientImpl::createIDBKeysFromSerializedValuesAndKeyPath( WebKit::WebVector<WebKit::WebIDBKey>& keys) { // TODO(bulach): we need to figure out a way to keep the utility process // running for longer, and shut it down when no longer used. - scoped_refptr<IndexedDBKeyUtilityClient> indexed_db_key_utility_client = - new IndexedDBKeyUtilityClient(); + scoped_refptr<IndexedDBKeyUtilityClient> indexed_db_key_utility_client( + new IndexedDBKeyUtilityClient()); indexed_db_key_utility_client->StartUtilityProcess(); std::vector<SerializedScriptValue> std_values; diff --git a/chrome/browser/in_process_webkit/indexed_db_browsertest.cc b/chrome/browser/in_process_webkit/indexed_db_browsertest.cc index daf5df0..9804be7 100644 --- a/chrome/browser/in_process_webkit/indexed_db_browsertest.cc +++ b/chrome/browser/in_process_webkit/indexed_db_browsertest.cc @@ -7,47 +7,10 @@ #include "base/ref_counted.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser.h" -#include "chrome/browser/in_process_webkit/indexed_db_context.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/in_process_browser_test.h" -#include "chrome/test/thread_test_helper.h" #include "chrome/test/ui_test_utils.h" -#include "third_party/WebKit/WebKit/chromium/public/WebCString.h" -#include "third_party/WebKit/WebKit/chromium/public/WebSecurityOrigin.h" -#include "third_party/WebKit/WebKit/chromium/public/WebString.h" - -namespace { - -const FilePath kTestIndexedDBFile( - FILE_PATH_LITERAL("http_host_1@database%20name.indexeddb")); - -class TestOnWebKitThread : public ThreadTestHelper { - public: - TestOnWebKitThread() - : ThreadTestHelper(BrowserThread::WEBKIT) { - } - - const std::string& database_name() const { return database_name_; } - const WebKit::WebSecurityOrigin& security_origin() const { - return security_origin_; - } - - private: - virtual ~TestOnWebKitThread() {} - - virtual void RunTest() { - set_test_result(IndexedDBContext::SplitIndexedDBFileName( - kTestIndexedDBFile, &database_name_, &security_origin_)); - } - - std::string database_name_; - WebKit::WebSecurityOrigin security_origin_; - - DISALLOW_COPY_AND_ASSIGN(TestOnWebKitThread); -}; - -} // namespace // This browser test is aimed towards exercising the IndexedDB bindings and // the actual implementation that lives in the browser side (in_process_webkit). @@ -63,8 +26,8 @@ class IndexedDBBrowserTest : public InProcessBrowserTest { } void SimpleTest(const GURL& test_url) { - // The test page will open a cursor on IndexedDB, then navigate to either a - // #pass or #fail ref. + // The test page will perform tests on IndexedDB, then navigate to either + // a #pass or #fail ref. ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( browser(), test_url, 2); std::string result = browser()->GetSelectedTabContents()->GetURL().ref(); @@ -78,17 +41,6 @@ class IndexedDBBrowserTest : public InProcessBrowserTest { } }; -IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, SplitFilename) { - scoped_refptr<TestOnWebKitThread> test_on_webkit_thread( - new TestOnWebKitThread); - ASSERT_TRUE(test_on_webkit_thread->Run()); - - EXPECT_EQ("database name", test_on_webkit_thread->database_name()); - std::string origin_str = - test_on_webkit_thread->security_origin().toString().utf8(); - EXPECT_EQ("http://host:1", origin_str); -} - IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTest) { SimpleTest(testUrl(FilePath(FILE_PATH_LITERAL("cursor_test.html")))); } @@ -104,3 +56,15 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyPathTest) { IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionGetTest) { SimpleTest(testUrl(FilePath(FILE_PATH_LITERAL("transaction_get_test.html")))); } + +IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ObjectStoreTest) { + SimpleTest(testUrl(FilePath(FILE_PATH_LITERAL("object_store_test.html")))); +} + +IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DatabaseTest) { + SimpleTest(testUrl(FilePath(FILE_PATH_LITERAL("database_test.html")))); +} + +IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionTest) { + SimpleTest(testUrl(FilePath(FILE_PATH_LITERAL("transaction_test.html")))); +} diff --git a/chrome/browser/in_process_webkit/indexed_db_callbacks.cc b/chrome/browser/in_process_webkit/indexed_db_callbacks.cc new file mode 100644 index 0000000..faaa933 --- /dev/null +++ b/chrome/browser/in_process_webkit/indexed_db_callbacks.cc @@ -0,0 +1,43 @@ +// 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/in_process_webkit/indexed_db_callbacks.h" + +IndexedDBCallbacksBase::IndexedDBCallbacksBase( + IndexedDBDispatcherHost* dispatcher_host, + int32 response_id) + : dispatcher_host_(dispatcher_host), + response_id_(response_id) { +} + +IndexedDBCallbacksBase::~IndexedDBCallbacksBase() {} + +IndexedDBTransactionCallbacks::IndexedDBTransactionCallbacks( + IndexedDBDispatcherHost* dispatcher_host, + int transaction_id) + : dispatcher_host_(dispatcher_host), + transaction_id_(transaction_id) { +} + +IndexedDBTransactionCallbacks::~IndexedDBTransactionCallbacks() {} + +void IndexedDBCallbacksBase::onError(const WebKit::WebIDBDatabaseError& error) { + dispatcher_host_->Send(new ViewMsg_IDBCallbacksError( + response_id_, error.code(), error.message())); +} + +void IndexedDBTransactionCallbacks::onAbort() { + dispatcher_host_->Send( + new ViewMsg_IDBTransactionCallbacksAbort(transaction_id_)); +} + +void IndexedDBTransactionCallbacks::onComplete() { + dispatcher_host_->Send( + new ViewMsg_IDBTransactionCallbacksComplete(transaction_id_)); +} + +void IndexedDBTransactionCallbacks::onTimeout() { + dispatcher_host_->Send( + new ViewMsg_IDBTransactionCallbacksTimeout(transaction_id_)); +} diff --git a/chrome/browser/in_process_webkit/indexed_db_callbacks.h b/chrome/browser/in_process_webkit/indexed_db_callbacks.h index 1c6ed37..653e211 100644 --- a/chrome/browser/in_process_webkit/indexed_db_callbacks.h +++ b/chrome/browser/in_process_webkit/indexed_db_callbacks.h @@ -37,14 +37,12 @@ template <> struct WebIDBToMsgHelper<WebKit::WebIDBTransaction> { // The code the following two classes share. class IndexedDBCallbacksBase : public WebKit::WebIDBCallbacks { public: - IndexedDBCallbacksBase( - IndexedDBDispatcherHost* dispatcher_host, int32 response_id) - : dispatcher_host_(dispatcher_host), response_id_(response_id) { } + IndexedDBCallbacksBase(IndexedDBDispatcherHost* dispatcher_host, + int32 response_id); - virtual void onError(const WebKit::WebIDBDatabaseError& error) { - dispatcher_host_->Send(new ViewMsg_IDBCallbacksError( - response_id_, error.code(), error.message())); - } + virtual ~IndexedDBCallbacksBase(); + + virtual void onError(const WebKit::WebIDBDatabaseError& error); protected: IndexedDBDispatcherHost* dispatcher_host() const { @@ -166,25 +164,15 @@ class IndexedDBTransactionCallbacks : public WebKit::WebIDBTransactionCallbacks { public: IndexedDBTransactionCallbacks(IndexedDBDispatcherHost* dispatcher_host, - int transaction_id) - : dispatcher_host_(dispatcher_host), - transaction_id_(transaction_id) { - } + int transaction_id); - virtual void onAbort() { - dispatcher_host_->Send( - new ViewMsg_IDBTransactionCallbacksAbort(transaction_id_)); - } + virtual ~IndexedDBTransactionCallbacks(); - virtual void onComplete() { - dispatcher_host_->Send( - new ViewMsg_IDBTransactionCallbacksComplete(transaction_id_)); - } + virtual void onAbort(); - virtual void onTimeout() { - dispatcher_host_->Send( - new ViewMsg_IDBTransactionCallbacksTimeout(transaction_id_)); - } + virtual void onComplete(); + + virtual void onTimeout(); private: scoped_refptr<IndexedDBDispatcherHost> dispatcher_host_; diff --git a/chrome/browser/in_process_webkit/indexed_db_context.cc b/chrome/browser/in_process_webkit/indexed_db_context.cc index b057861..50dcd35 100644 --- a/chrome/browser/in_process_webkit/indexed_db_context.cc +++ b/chrome/browser/in_process_webkit/indexed_db_context.cc @@ -7,17 +7,14 @@ #include "base/logging.h" #include "base/utf_string_conversions.h" #include "chrome/browser/in_process_webkit/webkit_context.h" -#include "googleurl/src/url_util.h" #include "third_party/WebKit/WebKit/chromium/public/WebCString.h" #include "third_party/WebKit/WebKit/chromium/public/WebIDBDatabase.h" #include "third_party/WebKit/WebKit/chromium/public/WebIDBFactory.h" -#include "third_party/WebKit/WebKit/chromium/public/WebSecurityOrigin.h" #include "third_party/WebKit/WebKit/chromium/public/WebString.h" #include "webkit/glue/webkit_glue.h" using WebKit::WebIDBDatabase; using WebKit::WebIDBFactory; -using WebKit::WebSecurityOrigin; const FilePath::CharType IndexedDBContext::kIndexedDBDirectory[] = FILE_PATH_LITERAL("IndexedDB"); @@ -40,39 +37,9 @@ WebIDBFactory* IndexedDBContext::GetIDBFactory() { } FilePath IndexedDBContext::GetIndexedDBFilePath( - const string16& database_name, - const WebSecurityOrigin& origin) const { + const string16& origin_id) const { FilePath storage_dir = webkit_context_->data_path().Append( kIndexedDBDirectory); - FilePath::StringType id = webkit_glue::WebStringToFilePathString( - WebIDBFactory::databaseFileName(database_name, origin)); - return storage_dir.Append(id); -} - -// static -bool IndexedDBContext::SplitIndexedDBFileName( - const FilePath& file_name, - std::string* database_name, - WebSecurityOrigin* security_origin) { - FilePath::StringType base_name = - file_name.BaseName().RemoveExtension().value(); - size_t db_name_separator = base_name.find(FILE_PATH_LITERAL("@")); - if (db_name_separator == FilePath::StringType::npos || - db_name_separator == 0) { - return false; - } - - *security_origin = - WebSecurityOrigin::createFromDatabaseIdentifier( - webkit_glue::FilePathStringToWebString( - base_name.substr(0, db_name_separator))); -#if defined(OS_POSIX) - std::string name = base_name.substr(db_name_separator + 1); -#elif defined(OS_WIN) - std::string name = WideToUTF8(base_name.substr(db_name_separator + 1)); -#endif - url_canon::RawCanonOutputT<char16> output; - url_util::DecodeURLEscapeSequences(name.c_str(), name.length(), &output); - *database_name = UTF16ToUTF8(string16(output.data(), output.length())); - return true; + FilePath::StringType id = webkit_glue::WebStringToFilePathString(origin_id); + return storage_dir.Append(id.append(kIndexedDBExtension)); } diff --git a/chrome/browser/in_process_webkit/indexed_db_context.h b/chrome/browser/in_process_webkit/indexed_db_context.h index a8519fb..6112d94 100644 --- a/chrome/browser/in_process_webkit/indexed_db_context.h +++ b/chrome/browser/in_process_webkit/indexed_db_context.h @@ -15,7 +15,6 @@ class WebKitContext; namespace WebKit { class WebIDBFactory; -class WebSecurityOrigin; } class IndexedDBContext { @@ -31,17 +30,8 @@ class IndexedDBContext { // The indexed db file extension. static const FilePath::CharType kIndexedDBExtension[]; - // Get the file name of the indexed db file for the given origin and database - // name. - FilePath GetIndexedDBFilePath(const string16& database_name, - const WebKit::WebSecurityOrigin& origin) const; - - // Splits an indexed database file name into a security origin and a - // database name. - static bool SplitIndexedDBFileName( - const FilePath& file_name, - std::string* database_name, - WebKit::WebSecurityOrigin* security_origin); + // Get the file name of the indexed db file for the given origin. + FilePath GetIndexedDBFilePath(const string16& origin_id) const; private: scoped_ptr<WebKit::WebIDBFactory> idb_factory_; diff --git a/chrome/browser/in_process_webkit/indexed_db_dispatcher_host.cc b/chrome/browser/in_process_webkit/indexed_db_dispatcher_host.cc index 9bc80e6..c0034eb 100644 --- a/chrome/browser/in_process_webkit/indexed_db_dispatcher_host.cc +++ b/chrome/browser/in_process_webkit/indexed_db_dispatcher_host.cc @@ -51,7 +51,7 @@ namespace { // FIXME: Replace this magic constant once we have a more sophisticated quota // system. -static const uint64 kDatabaseQuota = 5 * 1024 * 1024; +static const uint64 kDefaultQuota = 5 * 1024 * 1024; template <class T> void DeleteOnWebKitThread(T* obj) { @@ -275,8 +275,7 @@ void IndexedDBDispatcherHost::OnIDBFactoryOpen( CallRenderViewHostContentSettingsDelegate( process_id_, params.routing_id_, &RenderViewHostDelegate::ContentSettings::OnIndexedDBAccessed, - host, params.name_, params.description_, - content_setting == CONTENT_SETTING_BLOCK); + host, params.description_, content_setting == CONTENT_SETTING_BLOCK); if (content_setting == CONTENT_SETTING_BLOCK) { // TODO(jorlow): Change this to the proper error code once we figure out @@ -289,12 +288,19 @@ void IndexedDBDispatcherHost::OnIDBFactoryOpen( return; } - DCHECK(kDatabaseQuota == params.maximum_size_); + DCHECK(kDefaultQuota == params.maximum_size_); + + uint64 quota = kDefaultQuota; + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kUnlimitedQuotaForIndexedDB)) { + quota = 1024 * 1024 * 1024; // 1GB. More or less "unlimited". + } + Context()->GetIDBFactory()->open( params.name_, params.description_, new IndexedDBCallbacks<WebIDBDatabase>(this, params.response_id_), WebSecurityOrigin::createFromDatabaseIdentifier(params.origin_), NULL, - webkit_glue::FilePathToWebString(indexed_db_path), kDatabaseQuota); + webkit_glue::FilePathToWebString(indexed_db_path), quota); } ////////////////////////////////////////////////////////////////////// @@ -391,6 +397,7 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnName( &map_, object_id, reply_msg, &WebIDBDatabase::name); } +// TODO(hans): Delete this? void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDescription( int32 object_id, IPC::Message* reply_msg) { parent_->SyncGetter<string16, ViewHostMsg_IDBDatabaseDescription>( diff --git a/chrome/browser/in_process_webkit/webkit_context.cc b/chrome/browser/in_process_webkit/webkit_context.cc index 30bde33..32fccf7 100644 --- a/chrome/browser/in_process_webkit/webkit_context.cc +++ b/chrome/browser/in_process_webkit/webkit_context.cc @@ -39,10 +39,9 @@ WebKitContext::~WebKitContext() { void WebKitContext::PurgeMemory() { if (!BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)) { - bool result = BrowserThread::PostTask( + BrowserThread::PostTask( BrowserThread::WEBKIT, FROM_HERE, NewRunnableMethod(this, &WebKitContext::PurgeMemory)); - DCHECK(result); return; } @@ -54,11 +53,10 @@ void WebKitContext::DeleteDataModifiedSince( const char* url_scheme_to_be_skipped, const std::vector<string16>& protected_origins) { if (!BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)) { - bool result = BrowserThread::PostTask( + BrowserThread::PostTask( BrowserThread::WEBKIT, FROM_HERE, NewRunnableMethod(this, &WebKitContext::DeleteDataModifiedSince, cutoff, url_scheme_to_be_skipped, protected_origins)); - DCHECK(result); return; } diff --git a/chrome/browser/instant/instant_browsertest.cc b/chrome/browser/instant/instant_browsertest.cc new file mode 100644 index 0000000..ec4fc69 --- /dev/null +++ b/chrome/browser/instant/instant_browsertest.cc @@ -0,0 +1,248 @@ +// 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 "base/command_line.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/autocomplete/autocomplete_edit_view.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/instant/instant_controller.h" +#include "chrome/browser/location_bar.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#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/chrome_switches.h" +#include "chrome/test/in_process_browser_test.h" +#include "chrome/test/ui_test_utils.h" + + +class InstantTest : public InProcessBrowserTest { + public: + InstantTest() + : location_bar_(NULL), + preview_(NULL) { + EnableDOMAutomation(); + } + + void SetupInstantProvider(const std::string& page) { + TemplateURLModel* model = browser()->profile()->GetTemplateURLModel(); + ASSERT_TRUE(model); + + if (!model->loaded()) { + model->Load(); + ui_test_utils::WaitForNotification( + NotificationType::TEMPLATE_URL_MODEL_LOADED); + } + + ASSERT_TRUE(model->loaded()); + + // TemplateURLModel takes ownership of this. + TemplateURL* template_url = new TemplateURL(); + + std::string url = StringPrintf( + "http://%s:%d/files/instant/%s?q={searchTerms}", + test_server()->host_port_pair().host().c_str(), + test_server()->host_port_pair().port(), + page.c_str()); + template_url->SetURL(url, 0, 0); + template_url->SetInstantURL(url, 0, 0); + template_url->set_keyword(UTF8ToWide("foo")); + template_url->set_short_name(UTF8ToWide("foo")); + + model->Add(template_url); + model->SetDefaultSearchProvider(template_url); + } + + // Type a character to get instant to trigger. + void SetupLocationBar() { + location_bar_ = browser()->window()->GetLocationBar(); + ASSERT_TRUE(location_bar_); + location_bar_->location_entry()->SetUserText(L"a"); + } + + // Wait for instant to load and ensure it is in the state we expect. + void SetupPreview() { + preview_ = browser()->instant()->GetPreviewContents(); + ASSERT_TRUE(preview_); + ui_test_utils::WaitForNavigation(&preview_->controller()); + + // Verify the initial setup of the search box. + ASSERT_TRUE(browser()->instant()); + EXPECT_TRUE(browser()->instant()->IsShowingInstant()); + EXPECT_FALSE(browser()->instant()->is_active()); + + // When the page loads, the initial searchBox values are set and no events + // have been called. + EXPECT_NO_FATAL_FAILURE(CheckBoolValueFromJavascript( + true, "window.chrome.sv", preview_)); + EXPECT_NO_FATAL_FAILURE(CheckIntValueFromJavascript( + 0, "window.onsubmitcalls", preview_)); + EXPECT_NO_FATAL_FAILURE(CheckIntValueFromJavascript( + 0, "window.oncancelcalls", preview_)); + EXPECT_NO_FATAL_FAILURE(CheckIntValueFromJavascript( + 0, "window.onchangecalls", preview_)); + EXPECT_NO_FATAL_FAILURE(CheckIntValueFromJavascript( + 0, "window.onresizecalls", preview_)); + EXPECT_NO_FATAL_FAILURE(CheckStringValueFromJavascript( + "a", "window.chrome.searchBox.value", preview_)); + EXPECT_NO_FATAL_FAILURE(CheckBoolValueFromJavascript( + false, "window.chrome.searchBox.verbatim", preview_)); + } + + void SetLocationBarText(const std::wstring& text) { + ASSERT_TRUE(location_bar_); + location_bar_->location_entry()->SetUserText(text); + ui_test_utils::WaitForNotification( + NotificationType::INSTANT_CONTROLLER_SHOWN); + } + + void SendKey(app::KeyboardCode key) { + ASSERT_TRUE(ui_test_utils::SendKeyPressSync( + browser(), key, false, false, false, false)); + } + + void CheckStringValueFromJavascript( + const std::string& expected, + const std::string& function, + TabContents* tab_contents) { + std::string script = StringPrintf( + "window.domAutomationController.send(%s)", function.c_str()); + std::string result; + ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString( + tab_contents->render_view_host(), + std::wstring(), UTF8ToWide(script), &result)); + EXPECT_EQ(expected, result); + } + + void CheckBoolValueFromJavascript( + bool expected, + const std::string& function, + TabContents* tab_contents) { + std::string script = StringPrintf( + "window.domAutomationController.send(%s)", function.c_str()); + bool result; + ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool( + tab_contents->render_view_host(), + std::wstring(), UTF8ToWide(script), &result)); + EXPECT_EQ(expected, result); + } + + void CheckIntValueFromJavascript( + int expected, + const std::string& function, + TabContents* tab_contents) { + std::string script = StringPrintf( + "window.domAutomationController.send(%s)", function.c_str()); + int result; + ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractInt( + tab_contents->render_view_host(), + std::wstring(), UTF8ToWide(script), &result)); + EXPECT_EQ(expected, result); + } + + protected: + virtual void SetUpCommandLine(CommandLine* command_line) { + command_line->AppendSwitch(switches::kEnablePredictiveInstant); + } + + LocationBar* location_bar_; + TabContents* preview_; +}; + +// TODO(tonyg): Add the following tests: +// 1. Test that setSuggestions() works. +// 2. Test that the search box API is not populated for pages other than the +// default search provider. +// 3. Test resize events. + +#if defined(OS_WIN) +#define MAYBE_OnChangeEvent OnChangeEvent +#else +#define MAYBE_OnChangeEvent DISABLED_OnChangeEvent +#endif +// Verify that the onchange event is dispatched upon typing in the box. +IN_PROC_BROWSER_TEST_F(InstantTest, MAYBE_OnChangeEvent) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_NO_FATAL_FAILURE(SetupInstantProvider("search.html")); + ASSERT_NO_FATAL_FAILURE(SetupLocationBar()); + ASSERT_NO_FATAL_FAILURE(SetupPreview()); + + ASSERT_NO_FATAL_FAILURE(SetLocationBarText(L"abc")); + + // Check that the value is reflected and onchange is called. + EXPECT_NO_FATAL_FAILURE(CheckStringValueFromJavascript( + "abc", "window.chrome.searchBox.value", preview_)); + EXPECT_NO_FATAL_FAILURE(CheckBoolValueFromJavascript( + false, "window.chrome.searchBox.verbatim", preview_)); + EXPECT_NO_FATAL_FAILURE(CheckIntValueFromJavascript( + 1, "window.onchangecalls", preview_)); +} + +#if defined(OS_WIN) +#define MAYBE_OnSubmitEvent OnSubmitEvent +#else +#define MAYBE_OnSubmitEvent DISABLED_OnSubmitEvent +#endif +// Verify that the onsubmit event is dispatched upon pressing enter. +IN_PROC_BROWSER_TEST_F(InstantTest, MAYBE_OnSubmitEvent) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_NO_FATAL_FAILURE(SetupInstantProvider("search.html")); + ASSERT_NO_FATAL_FAILURE(SetupLocationBar()); + ASSERT_NO_FATAL_FAILURE(SetupPreview()); + + ASSERT_NO_FATAL_FAILURE(SetLocationBarText(L"abc")); + ASSERT_NO_FATAL_FAILURE(SendKey(app::VKEY_RETURN)); + + // Check that the preview contents have been committed. + ASSERT_FALSE(browser()->instant()->GetPreviewContents()); + TabContents* contents = browser()->GetSelectedTabContents(); + ASSERT_TRUE(contents); + + // Check that the value is reflected and onsubmit is called. + EXPECT_NO_FATAL_FAILURE(CheckBoolValueFromJavascript( + true, "window.chrome.sv", contents)); + EXPECT_NO_FATAL_FAILURE(CheckStringValueFromJavascript( + "abc", "window.chrome.searchBox.value", contents)); + EXPECT_NO_FATAL_FAILURE(CheckBoolValueFromJavascript( + true, "window.chrome.searchBox.verbatim", contents)); + EXPECT_NO_FATAL_FAILURE(CheckIntValueFromJavascript( + 1, "window.onsubmitcalls", contents)); +} + +#if defined(OS_WIN) +#define MAYBE_OnCancelEvent OnCancelEvent +#else +#define MAYBE_OnCancelEvent DISABLED_OnCancelEvent +#endif +// Verify that the oncancel event is dispatched upon losing focus. +IN_PROC_BROWSER_TEST_F(InstantTest, MAYBE_OnCancelEvent) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_NO_FATAL_FAILURE(SetupInstantProvider("search.html")); + ASSERT_NO_FATAL_FAILURE(SetupLocationBar()); + ASSERT_NO_FATAL_FAILURE(SetupPreview()); + + ASSERT_NO_FATAL_FAILURE(SetLocationBarText(L"abc")); + ASSERT_NO_FATAL_FAILURE(ui_test_utils::ClickOnView(browser(), + VIEW_ID_TAB_CONTAINER)); + + // Check that the preview contents have been committed. + ASSERT_FALSE(browser()->instant()->GetPreviewContents()); + TabContents* contents = browser()->GetSelectedTabContents(); + ASSERT_TRUE(contents); + + // Check that the value is reflected and oncancel is called. + EXPECT_NO_FATAL_FAILURE(CheckBoolValueFromJavascript( + true, "window.chrome.sv", contents)); + EXPECT_NO_FATAL_FAILURE(CheckStringValueFromJavascript( + "abc", "window.chrome.searchBox.value", contents)); + EXPECT_NO_FATAL_FAILURE(CheckBoolValueFromJavascript( + false, "window.chrome.searchBox.verbatim", contents)); + EXPECT_NO_FATAL_FAILURE(CheckIntValueFromJavascript( + 1, "window.oncancelcalls", contents)); +} diff --git a/chrome/browser/instant/instant_confirm_dialog.cc b/chrome/browser/instant/instant_confirm_dialog.cc index ad7a396..5c6c535 100644 --- a/chrome/browser/instant/instant_confirm_dialog.cc +++ b/chrome/browser/instant/instant_confirm_dialog.cc @@ -27,7 +27,7 @@ void ShowInstantConfirmDialogIfNecessary(gfx::NativeWindow parent, ShowInstantConfirmDialog(parent, profile); } -#if !defined(TOOLKIT_VIEWS) +#if !defined(TOOLKIT_VIEWS) && !defined(TOOLKIT_GTK) void ShowInstantConfirmDialog(gfx::NativeWindow parent, Profile* profile) { NOTIMPLEMENTED(); diff --git a/chrome/browser/instant/instant_controller.cc b/chrome/browser/instant/instant_controller.cc index d534cef..ab6617a 100644 --- a/chrome/browser/instant/instant_controller.cc +++ b/chrome/browser/instant/instant_controller.cc @@ -5,12 +5,14 @@ #include "chrome/browser/instant/instant_controller.h" #include "base/command_line.h" -#include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/instant/instant_delegate.h" #include "chrome/browser/instant/instant_loader.h" #include "chrome/browser/instant/instant_loader_manager.h" +#include "chrome/browser/platform_util.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" #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" @@ -30,23 +32,35 @@ void InstantController::RegisterUserPrefs(PrefService* prefs) { // static bool InstantController::IsEnabled(Profile* profile) { - static bool enabled = false; - static bool checked = false; - if (!checked) { - checked = true; - enabled = CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableMatchPreview); + return IsEnabled(profile, PREDICTIVE_TYPE) || + IsEnabled(profile, VERBATIM_TYPE); +} + +// static +bool InstantController::IsEnabled(Profile* profile, Type type) { + CommandLine* cl = CommandLine::ForCurrentProcess(); + if (type == PREDICTIVE_TYPE) { + return (cl->HasSwitch(switches::kEnablePredictiveInstant) || + (profile->GetPrefs() && + profile->GetPrefs()->GetBoolean(prefs::kInstantEnabled))); } - PrefService* prefs = profile->GetPrefs(); - return (enabled || (prefs && prefs->GetBoolean(prefs::kInstantEnabled))); + return cl->HasSwitch(switches::kEnableVerbatimInstant); } -InstantController::InstantController(InstantDelegate* delegate) +static InstantController::Type GetType(Profile* profile) { + return InstantController::IsEnabled(profile, + InstantController::PREDICTIVE_TYPE) ? + InstantController::PREDICTIVE_TYPE : InstantController::VERBATIM_TYPE; +} + +InstantController::InstantController(Profile* profile, + InstantDelegate* delegate) : delegate_(delegate), tab_contents_(NULL), is_active_(false), commit_on_mouse_up_(false), - last_transition_type_(PageTransition::LINK) { + last_transition_type_(PageTransition::LINK), + type_(GetType(profile)) { } InstantController::~InstantController() { @@ -138,6 +152,53 @@ bool InstantController::IsMouseDownFromActivate() { return loader_manager_->current_loader()->IsMouseDownFromActivate(); } +void InstantController::OnAutocompleteLostFocus( + gfx::NativeView view_gaining_focus) { + if (!is_active() || !GetPreviewContents()) + return; + + RenderWidgetHostView* rwhv = + GetPreviewContents()->GetRenderWidgetHostView(); + if (!view_gaining_focus || !rwhv) + return DestroyPreviewContents(); + + gfx::NativeView tab_view = GetPreviewContents()->GetNativeView(); + // Focus is going to the renderer. + if (rwhv->GetNativeView() == view_gaining_focus || + tab_view == view_gaining_focus) { + if (!IsMouseDownFromActivate()) { + // If the mouse is not down, focus is not going to the renderer. Someone + // else moved focus and we shouldn't commit. + return DestroyPreviewContents(); + } + + if (IsShowingInstant()) { + // We're showing instant results. As instant results may shift when + // committing we commit on the mouse up. This way a slow click still + // works fine. + return SetCommitOnMouseUp(); + } + + return CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST); + } + + // Walk up the view hierarchy. If the view gaining focus is a subview of the + // TabContents view (such as a windowed plugin or http auth dialog), we want + // to keep the preview contents. Otherwise, focus has gone somewhere else, + // such as the JS inspector, and we want to cancel the preview. + gfx::NativeView view_gaining_focus_ancestor = view_gaining_focus; + while (view_gaining_focus_ancestor && + view_gaining_focus_ancestor != tab_view) { + view_gaining_focus_ancestor = + platform_util::GetParent(view_gaining_focus_ancestor); + } + + if (view_gaining_focus_ancestor) + return CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST); + + return DestroyPreviewContents(); +} + TabContents* InstantController::ReleasePreviewContents(InstantCommitType type) { if (!loader_manager_.get()) return NULL; @@ -180,6 +241,11 @@ void InstantController::ShowInstantLoader(InstantLoader* loader) { } else { // The loader supports instant but isn't active yet. Nothing to do. } + + NotificationService::current()->Notify( + NotificationType::INSTANT_CONTROLLER_SHOWN, + Source<InstantController>(this), + NotificationService::NoDetails()); } void InstantController::SetSuggestedTextFor(InstantLoader* loader, @@ -318,6 +384,16 @@ void InstantController::ClearBlacklist() { const TemplateURL* InstantController::GetTemplateURL( const AutocompleteMatch& match) { + if (type_ == VERBATIM_TYPE) { + // When using VERBATIM_TYPE we don't want to attempt to use the instant + // JavaScript API, otherwise the page would show predictive results. By + // returning NULL here we ensure we don't attempt to use the instant API. + // + // TODO: when the full search box API is in place we can lift this + // restriction and force the page to show verbatim results always. + return NULL; + } + const TemplateURL* template_url = match.template_url; if (match.type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED || match.type == AutocompleteMatch::SEARCH_HISTORY || diff --git a/chrome/browser/instant/instant_controller.h b/chrome/browser/instant/instant_controller.h index 0430083..009340b 100644 --- a/chrome/browser/instant/instant_controller.h +++ b/chrome/browser/instant/instant_controller.h @@ -16,6 +16,7 @@ #include "chrome/browser/instant/instant_loader_delegate.h" #include "chrome/browser/search_engines/template_url_id.h" #include "chrome/common/page_transition_types.h" +#include "gfx/native_widget_types.h" #include "gfx/rect.h" #include "googleurl/src/gurl.h" @@ -37,15 +38,28 @@ class TemplateURL; // being invoked on the delegate. class InstantController : public InstantLoaderDelegate { public: - explicit InstantController(InstantDelegate* delegate); + // Variations of instant support. + enum Type { + // Search results are shown for the best guess of what we think the user was + // planning on typing. + PREDICTIVE_TYPE, + + // Search results are shown for exactly what was typed. + VERBATIM_TYPE, + }; + + InstantController(Profile* profile, InstantDelegate* delegate); ~InstantController(); // Registers instant related preferences. static void RegisterUserPrefs(PrefService* prefs); - // Is InstantController enabled? + // Returns true if either type of instant is enabled. static bool IsEnabled(Profile* profile); + // Returns true if the specified type of instant is enabled. + static bool IsEnabled(Profile* profile, Type type); + // Invoked as the user types in the omnibox with the url to navigate to. If // the url is empty and there is a preview TabContents it is destroyed. If url // is non-empty and the preview TabContents has not been created it is @@ -84,6 +98,10 @@ class InstantController : public InstantLoaderDelegate { // content. bool IsMouseDownFromActivate(); + // The autocomplete edit that was initiating the current instant session has + // lost focus. Commit or discard the preview accordingly. + void OnAutocompleteLostFocus(gfx::NativeView view_gaining_focus); + // Releases the preview TabContents passing ownership to the caller. This is // intended to be called when the preview TabContents is committed. This does // not notify the delegate. @@ -190,6 +208,8 @@ class InstantController : public InstantLoaderDelegate { // URL last pased to ScheduleUpdate. GURL scheduled_url_; + const Type type_; + DISALLOW_COPY_AND_ASSIGN(InstantController); }; diff --git a/chrome/browser/instant/instant_loader.cc b/chrome/browser/instant/instant_loader.cc index 3c561b3..7147b7c 100644 --- a/chrome/browser/instant/instant_loader.cc +++ b/chrome/browser/instant/instant_loader.cc @@ -7,8 +7,10 @@ #include <algorithm> #include <utility> +#include "app/l10n_util.h" #include "base/command_line.h" #include "base/string_number_conversions.h" +#include "base/timer.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/favicon_service.h" @@ -24,6 +26,7 @@ #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" #include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" @@ -34,104 +37,19 @@ #include "ipc/ipc_message.h" namespace { - -// Script sent as the user is typing and the provider supports instant. -// Params: -// . the text the user typed. -// TODO: add support for the 2nd and 3rd params. -const char kUserInputScript[] = - "if (window.chrome.userInput) window.chrome.userInput(\"$1\", 0, 0);"; - -// Script sent when the page is committed and the provider supports instant. -// Params: -// . the text the user typed. -// . boolean indicating if the user pressed enter to accept the text. -const char kUserDoneScript[] = - "if (window.chrome.userWantsQuery) " - "window.chrome.userWantsQuery(\"$1\", $2);"; - -// Script sent when the bounds of the omnibox changes and the provider supports -// instant. The params are the bounds relative to the origin of the preview -// (x, y, width, height). -const char kSetOmniboxBoundsScript[] = - "if (window.chrome.setDropdownDimensions) " - "window.chrome.setDropdownDimensions($1, $2, $3, $4);"; - -// Script sent to see if the page really supports instant. -const char kSupportsInstantScript[] = - "if (window.chrome.setDropdownDimensions) true; else false;"; - // Number of ms to delay before updating the omnibox bounds. This is a bit long // as updating the bounds ends up being quite expensive. const int kUpdateBoundsDelayMS = 500; - -// Escapes quotes in the |text| so that it be passed to JavaScript as a quoted -// string. -string16 EscapeUserText(const string16& text) { - string16 escaped_text(text); - ReplaceSubstringsAfterOffset(&escaped_text, 0L, ASCIIToUTF16("\""), - ASCIIToUTF16("\\\"")); - return escaped_text; -} - -// Sends the script for when the user commits the preview. |pressed_enter| is -// true if the user pressed enter to commit. -void SendDoneScript(TabContents* tab_contents, - const string16& text, - bool pressed_enter) { - std::vector<string16> params; - params.push_back(EscapeUserText(text)); - params.push_back(pressed_enter ? ASCIIToUTF16("true") : - ASCIIToUTF16("false")); - string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserDoneScript), - params, - NULL); - tab_contents->render_view_host()->ExecuteJavascriptInWebFrame( - std::wstring(), - UTF16ToWide(script)); -} - -// Sends the user input script to |tab_contents|. |text| is the text the user -// input into the omnibox. -void SendUserInputScript(TabContents* tab_contents, const string16& text) { - string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserInputScript), - EscapeUserText(text), - NULL); - tab_contents->render_view_host()->ExecuteJavascriptInWebFrame( - std::wstring(), - UTF16ToWide(script)); -} - -// Sends the script for setting the bounds of the omnibox to |tab_contents|. -void SendOmniboxBoundsScript(TabContents* tab_contents, - const gfx::Rect& bounds) { - std::vector<string16> bounds_vector; - bounds_vector.push_back(base::IntToString16(bounds.x())); - bounds_vector.push_back(base::IntToString16(bounds.y())); - bounds_vector.push_back(base::IntToString16(bounds.width())); - bounds_vector.push_back(base::IntToString16(bounds.height())); - string16 script = ReplaceStringPlaceholders( - ASCIIToUTF16(kSetOmniboxBoundsScript), - bounds_vector, - NULL); - tab_contents->render_view_host()->ExecuteJavascriptInWebFrame( - std::wstring(), - UTF16ToWide(script)); -} - } // namespace -// FrameLoadObserver is responsible for waiting for the TabContents to finish -// loading and when done sending the necessary script down to the page. +// FrameLoadObserver is responsible for determining if the page supports +// instant after it has loaded. class InstantLoader::FrameLoadObserver : public NotificationObserver { public: - FrameLoadObserver(InstantLoader* loader, const string16& text) - : loader_(loader), - tab_contents_(loader->preview_contents()), - unique_id_(tab_contents_->controller().pending_entry()->unique_id()), + FrameLoadObserver(TabContents* tab_contents, const string16& text) + : tab_contents_(tab_contents), text_(text), - initial_text_(text), - execute_js_id_(0) { + unique_id_(tab_contents_->controller().pending_entry()->unique_id()) { registrar_.Add(this, NotificationType::LOAD_COMPLETED_MAIN_FRAME, Source<TabContents>(tab_contents_)); } @@ -152,29 +70,10 @@ class InstantLoader::FrameLoadObserver : public NotificationObserver { active_entry->unique_id() != unique_id_) { return; } - - DetermineIfPageSupportsInstant(); + tab_contents_->render_view_host()->DetermineIfPageSupportsInstant( + text_); break; } - - case NotificationType::EXECUTE_JAVASCRIPT_RESULT: { - typedef std::pair<int, Value*> ExecuteDetailType; - ExecuteDetailType* result = - (static_cast<Details<ExecuteDetailType > >(details)).ptr(); - if (result->first != execute_js_id_) - return; - - DCHECK(loader_); - bool bool_result; - if (!result->second || !result->second->IsType(Value::TYPE_BOOLEAN) || - !result->second->GetAsBoolean(&bool_result) || !bool_result) { - DoesntSupportInstant(); - return; - } - SupportsInstant(); - return; - } - default: NOTREACHED(); break; @@ -182,66 +81,18 @@ class InstantLoader::FrameLoadObserver : public NotificationObserver { } private: - // Executes the javascript to determine if the page supports script. The - // results are passed back to us by way of NotificationObserver. - void DetermineIfPageSupportsInstant() { - DCHECK_EQ(0, execute_js_id_); - - RenderViewHost* rvh = tab_contents_->render_view_host(); - registrar_.Add(this, NotificationType::EXECUTE_JAVASCRIPT_RESULT, - Source<RenderViewHost>(rvh)); - execute_js_id_ = rvh->ExecuteJavascriptInWebFrameNotifyResult( - string16(), - ASCIIToUTF16(kSupportsInstantScript)); - } - - // Invoked when we determine the page doesn't really support instant. - void DoesntSupportInstant() { - DCHECK(loader_); - - loader_->PageDoesntSupportInstant(text_ != initial_text_); - - // WARNING: we've been deleted. - } - - // Invoked when we determine the page really supports instant. - void SupportsInstant() { - DCHECK(loader_); - - gfx::Rect bounds = loader_->GetOmniboxBoundsInTermsOfPreview(); - loader_->last_omnibox_bounds_ = loader_->omnibox_bounds_; - if (!bounds.IsEmpty()) - SendOmniboxBoundsScript(tab_contents_, bounds); - - SendUserInputScript(tab_contents_, text_); - - loader_->PageFinishedLoading(); - - // WARNING: we've been deleted. - } - - // InstantLoader that created us. - InstantLoader* loader_; - // The TabContents we're listening for changes on. TabContents* tab_contents_; - // unique_id of the NavigationEntry we're waiting on. - const int unique_id_; - // Text to send down to the page. string16 text_; - // Initial text supplied to constructor. - const string16 initial_text_; + // unique_id of the NavigationEntry we're waiting on. + const int unique_id_; // Registers and unregisters us for notifications. NotificationRegistrar registrar_; - // ID of the javascript that was executed to determine if the page supports - // instant. - int execute_js_id_; - DISALLOW_COPY_AND_ASSIGN(FrameLoadObserver); }; @@ -274,11 +125,13 @@ class InstantLoader::TabContentsDelegateImpl : public TabContentsDelegate { : loader_(loader), installed_paint_observer_(false), waiting_for_new_page_(true), - is_mouse_down_from_activate_(false) { + is_mouse_down_from_activate_(false), + user_typed_before_load_(false) { } // Invoked prior to loading a new URL. void PrepareForNewLoad() { + user_typed_before_load_ = false; waiting_for_new_page_ = true; add_page_vector_.clear(); } @@ -294,6 +147,8 @@ class InstantLoader::TabContentsDelegateImpl : public TabContentsDelegate { return is_mouse_down_from_activate_; } + void set_user_typed_before_load() { user_typed_before_load_ = true; } + // Commits the currently buffered history. void CommitHistory() { TabContents* tab = loader_->preview_contents(); @@ -353,7 +208,7 @@ class InstantLoader::TabContentsDelegateImpl : public TabContentsDelegate { virtual bool IsPopup(const TabContents* source) const { return false; } - virtual bool ShouldFocusConstrainedWindow(TabContents* source) { + virtual bool ShouldFocusConstrainedWindow() { // Return false so that constrained windows are not initially focused. If // we did otherwise the preview would prematurely get committed when focus // goes to the constrained window. @@ -460,12 +315,33 @@ class InstantLoader::TabContentsDelegateImpl : public TabContentsDelegate { virtual void UpdatePreferredSize(const gfx::Size& pref_size) {} virtual void ContentTypeChanged(TabContents* source) {} - virtual void OnSetSuggestResult(int32 page_id, const std::string& result) { + virtual void OnSetSuggestions(int32 page_id, + const std::vector<std::string>& suggestions) { TabContents* source = loader_->preview_contents(); + if (!source->controller().GetActiveEntry() || + page_id != source->controller().GetActiveEntry()->page_id()) + return; + // TODO: only allow for default search provider. - if (source->controller().GetActiveEntry() && - page_id == source->controller().GetActiveEntry()->page_id()) { - loader_->SetCompleteSuggestedText(UTF8ToUTF16(result)); + // TODO(sky): Handle multiple suggestions. + if (suggestions.empty()) + loader_->SetCompleteSuggestedText(string16()); + else + loader_->SetCompleteSuggestedText(UTF8ToUTF16(suggestions[0])); + } + + virtual void OnInstantSupportDetermined(int32 page_id, bool result) { + TabContents* source = loader_->preview_contents(); + if (!source->controller().GetActiveEntry() || + page_id != source->controller().GetActiveEntry()->page_id()) + return; + + if (result) { + gfx::Rect bounds = loader_->GetOmniboxBoundsInTermsOfPreview(); + loader_->last_omnibox_bounds_ = loader_->omnibox_bounds_; + loader_->PageFinishedLoading(); + } else { + loader_->PageDoesntSupportInstant(user_typed_before_load_); } } @@ -496,9 +372,12 @@ class InstantLoader::TabContentsDelegateImpl : public TabContentsDelegate { // NEW_PAGE navigation we don't add history items to add_page_vector_. bool waiting_for_new_page_; - // Returns true if the mouse is down from an activate. + // True if the mouse is down from an activate. bool is_mouse_down_from_activate_; + // True if the user typed in the search box before the page loaded. + bool user_typed_before_load_; + DISALLOW_COPY_AND_ASSIGN(TabContentsDelegateImpl); }; @@ -563,23 +442,40 @@ void InstantLoader::Update(TabContents* tab_contents, if (is_waiting_for_load()) { // The page hasn't loaded yet. We'll send the script down when it does. frame_load_observer_->set_text(user_text_); + preview_tab_contents_delegate_->set_user_typed_before_load(); return; } - SendUserInputScript(preview_contents_.get(), user_text_); - if (complete_suggested_text_.size() > user_text_.size() && - !complete_suggested_text_.compare(0, user_text_.size(), user_text_)) { + preview_contents_->render_view_host()->SearchBoxChange( + user_text_, false, 0, 0); + + string16 complete_suggested_text_lower = l10n_util::ToLower( + complete_suggested_text_); + string16 user_text_lower = l10n_util::ToLower(user_text_); + if (complete_suggested_text_lower.size() > user_text_lower.size() && + !complete_suggested_text_lower.compare(0, user_text_lower.size(), + user_text_lower)) { *suggested_text = complete_suggested_text_.substr(user_text_.size()); } } else { // Load the instant URL. We don't reflect the url we load in url() as // callers expect that we're loading the URL they tell us to. + // + // This uses an empty string for the replacement text as the url doesn't + // really have the search params, but we need to use the replace + // functionality so that embeded tags (like {google:baseURL}) are escaped + // correctly. + // TODO(sky): having to use a replaceable url is a bit of a hack here. GURL instant_url( template_url->instant_url()->ReplaceSearchTerms( - *template_url, UTF16ToWideHack(user_text), -1, std::wstring())); + *template_url, std::wstring(), -1, std::wstring())); + CommandLine* cl = CommandLine::ForCurrentProcess(); + if (cl->HasSwitch(switches::kInstantURL)) + instant_url = GURL(cl->GetSwitchValueASCII(switches::kInstantURL)); initial_instant_url_ = url; preview_contents_->controller().LoadURL( instant_url, GURL(), transition_type); - frame_load_observer_.reset(new FrameLoadObserver(this, user_text_)); + frame_load_observer_.reset( + new FrameLoadObserver(preview_contents(), user_text_)); } } else { DCHECK(template_url_id_ == 0); @@ -619,9 +515,11 @@ TabContents* InstantLoader::ReleasePreviewContents(InstantCommitType type) { DCHECK(type == INSTANT_COMMIT_DESTROY || !frame_load_observer_.get()); if (type != INSTANT_COMMIT_DESTROY && is_showing_instant()) { - SendDoneScript(preview_contents_.get(), - user_text_, - type == INSTANT_COMMIT_PRESSED_ENTER); + if (type == INSTANT_COMMIT_FOCUS_LOST) + preview_contents_->render_view_host()->SearchBoxCancel(); + else + preview_contents_->render_view_host()->SearchBoxSubmit( + user_text_, type == INSTANT_COMMIT_PRESSED_ENTER); } omnibox_bounds_ = gfx::Rect(); last_omnibox_bounds_ = gfx::Rect(); @@ -678,8 +576,12 @@ void InstantLoader::SetCompleteSuggestedText( if (complete_suggested_text == complete_suggested_text_) return; - if (user_text_.compare(0, user_text_.size(), complete_suggested_text, - 0, user_text_.size())) { + string16 user_text_lower = l10n_util::ToLower(user_text_); + string16 complete_suggested_text_lower = l10n_util::ToLower( + complete_suggested_text); + if (user_text_lower.compare(0, user_text_lower.size(), + complete_suggested_text_lower, + 0, user_text_lower.size())) { // The user text no longer contains the suggested text, ignore it. complete_suggested_text_.clear(); delegate_->SetSuggestedTextFor(this, string16()); @@ -718,15 +620,26 @@ void InstantLoader::PageFinishedLoading() { // date by the time we show it. } +// TODO(tonyg): This method only fires when the omnibox bounds change. It also +// needs to fire when the preview bounds change (e.g. open/close info bar). gfx::Rect InstantLoader::GetOmniboxBoundsInTermsOfPreview() { - if (omnibox_bounds_.IsEmpty()) - return omnibox_bounds_; - gfx::Rect preview_bounds(delegate_->GetInstantBounds()); - return gfx::Rect(omnibox_bounds_.x() - preview_bounds.x(), - omnibox_bounds_.y() - preview_bounds.y(), - omnibox_bounds_.width(), - omnibox_bounds_.height()); + gfx::Rect intersection(omnibox_bounds_.Intersect(preview_bounds)); + + // Translate into window's coordinates. + if (!intersection.IsEmpty()) { + intersection.Offset(-preview_bounds.origin().x(), + -preview_bounds.origin().y()); + } + + // In the current Chrome UI, these must always be true so they sanity check + // the above operations. In a future UI, these may be removed or adjusted. + DCHECK_EQ(0, intersection.y()); + DCHECK_LE(0, intersection.x()); + DCHECK_LE(0, intersection.width()); + DCHECK_LE(0, intersection.height()); + + return intersection; } void InstantLoader::PageDoesntSupportInstant(bool needs_reload) { @@ -749,7 +662,7 @@ void InstantLoader::ProcessBoundsChange() { last_omnibox_bounds_ = omnibox_bounds_; if (preview_contents_.get() && is_showing_instant() && !is_waiting_for_load()) { - SendOmniboxBoundsScript(preview_contents_.get(), - GetOmniboxBoundsInTermsOfPreview()); + preview_contents_->render_view_host()->SearchBoxResize( + GetOmniboxBoundsInTermsOfPreview()); } } diff --git a/chrome/browser/intranet_redirect_detector.cc b/chrome/browser/intranet_redirect_detector.cc index 481e8cf..46fd8be 100644 --- a/chrome/browser/intranet_redirect_detector.cc +++ b/chrome/browser/intranet_redirect_detector.cc @@ -80,8 +80,10 @@ void IntranetRedirectDetector::StartFetchesIfPossible() { if (in_sleep_ || !request_context_available_) return; - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableBackgroundNetworking)) + // The detector is not needed in Chrome Frame since we have no omnibox there. + const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + if (cmd_line->HasSwitch(switches::kDisableBackgroundNetworking) || + cmd_line->HasSwitch(switches::kChromeFrame)) return; DCHECK(fetchers_.empty() && resulting_origins_.empty()); diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc index 990f9a4..b12b7f0 100644 --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc @@ -5,21 +5,24 @@ #include "chrome/browser/io_thread.h" #include "base/command_line.h" -#include "base/leak_tracker.h" +#include "base/debug/leak_tracker.h" #include "base/logging.h" #include "base/metrics/field_trial.h" #include "base/stl_util-inl.h" #include "base/string_number_conversions.h" #include "base/string_split.h" #include "base/string_util.h" +#include "base/thread_restrictions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/gpu_process_host.h" #include "chrome/browser/net/chrome_net_log.h" +#include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/net/connect_interceptor.h" #include "chrome/browser/net/passive_log_collector.h" #include "chrome/browser/net/predictor_api.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/net/raw_host_resolver_proc.h" #include "chrome/common/net/url_fetcher.h" #include "net/base/dnsrr_resolver.h" #include "net/base/host_cache.h" @@ -60,13 +63,15 @@ net::HostResolver* CreateGlobalHostResolver(net::NetLog* net_log) { // For each option (i.e., non-default), we have a fixed probability. base::FieldTrial::Probability kProbabilityPerGroup = 100; // 10%. - scoped_refptr<base::FieldTrial> trial = - new base::FieldTrial("DnsParallelism", kDivisor); + scoped_refptr<base::FieldTrial> trial( + new base::FieldTrial("DnsParallelism", kDivisor)); // List options with different counts. // Firefox limits total to 8 in parallel, and default is currently 50. int parallel_6 = trial->AppendGroup("parallel_6", kProbabilityPerGroup); + int parallel_7 = trial->AppendGroup("parallel_7", kProbabilityPerGroup); int parallel_8 = trial->AppendGroup("parallel_8", kProbabilityPerGroup); + int parallel_9 = trial->AppendGroup("parallel_9", kProbabilityPerGroup); int parallel_10 = trial->AppendGroup("parallel_10", kProbabilityPerGroup); int parallel_14 = trial->AppendGroup("parallel_14", kProbabilityPerGroup); int parallel_20 = trial->AppendGroup("parallel_20", kProbabilityPerGroup); @@ -76,8 +81,12 @@ net::HostResolver* CreateGlobalHostResolver(net::NetLog* net_log) { if (trial->group() == parallel_6) parallelism = 6; + else if (trial->group() == parallel_7) + parallelism = 7; else if (trial->group() == parallel_8) parallelism = 8; + else if (trial->group() == parallel_9) + parallelism = 9; else if (trial->group() == parallel_10) parallelism = 10; else if (trial->group() == parallel_14) @@ -86,8 +95,24 @@ net::HostResolver* CreateGlobalHostResolver(net::NetLog* net_log) { parallelism = 20; } + // Use the specified DNS server for doing raw resolutions if requested + // from the command-line. + scoped_refptr<net::HostResolverProc> resolver_proc; + if (command_line.HasSwitch(switches::kDnsServer)) { + std::string dns_ip_string = + command_line.GetSwitchValueASCII(switches::kDnsServer); + net::IPAddressNumber dns_ip_number; + if (net::ParseIPLiteralToNumber(dns_ip_string, &dns_ip_number)) { + resolver_proc = + new chrome_common_net::RawHostResolverProc(dns_ip_number, NULL); + } else { + LOG(ERROR) << "Invalid IP address specified for --dns-server: " + << dns_ip_string; + } + } + net::HostResolver* global_host_resolver = - net::CreateSystemHostResolver(parallelism, net_log); + net::CreateSystemHostResolver(parallelism, resolver_proc.get(), net_log); // Determine if we should disable IPv6 support. if (!command_line.HasSwitch(switches::kEnableIPv6)) { @@ -203,7 +228,7 @@ IOThread::Globals* IOThread::globals() { void IOThread::InitNetworkPredictor( bool prefetching_enabled, base::TimeDelta max_dns_queue_delay, - size_t max_concurrent, + size_t max_speculative_parallel_resolves, const chrome_common_net::UrlList& startup_urls, ListValue* referral_list, bool preconnect_enabled) { @@ -213,10 +238,35 @@ void IOThread::InitNetworkPredictor( NewRunnableMethod( this, &IOThread::InitNetworkPredictorOnIOThread, - prefetching_enabled, max_dns_queue_delay, max_concurrent, + prefetching_enabled, max_dns_queue_delay, + max_speculative_parallel_resolves, startup_urls, referral_list, preconnect_enabled)); } +void IOThread::RegisterURLRequestContextGetter( + ChromeURLRequestContextGetter* url_request_context_getter) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + std::list<ChromeURLRequestContextGetter*>::const_iterator it = + std::find(url_request_context_getters_.begin(), + url_request_context_getters_.end(), + url_request_context_getter); + DCHECK(it == url_request_context_getters_.end()); + url_request_context_getters_.push_back(url_request_context_getter); +} + +void IOThread::UnregisterURLRequestContextGetter( + ChromeURLRequestContextGetter* url_request_context_getter) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + std::list<ChromeURLRequestContextGetter*>::iterator it = + std::find(url_request_context_getters_.begin(), + url_request_context_getters_.end(), + url_request_context_getter); + DCHECK(it != url_request_context_getters_.end()); + // This does not scale, but we shouldn't have many URLRequestContextGetters in + // the first place, so this should be fine. + url_request_context_getters_.erase(it); +} + void IOThread::ChangedToOnTheRecord() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); message_loop()->PostTask( @@ -232,6 +282,13 @@ net::ProxyScriptFetcher* IOThread::CreateAndRegisterProxyScriptFetcher( } void IOThread::Init() { +#if !defined(OS_CHROMEOS) + // TODO(evan): test and enable this on all platforms. + // Though this thread is called the "IO" thread, it actually just routes + // messages around; it shouldn't be allowed to perform any blocking disk I/O. + base::ThreadRestrictions::SetIOAllowed(false); +#endif + BrowserProcessSubThread::Init(); DCHECK_EQ(MessageLoop::TYPE_IO, message_loop()->type()); @@ -259,6 +316,9 @@ void IOThread::Init() { } void IOThread::CleanUp() { + // Step 1: Kill all things that might be holding onto + // URLRequest/URLRequestContexts. + #if defined(USE_NSS) net::ShutdownOCSP(); #endif // defined(USE_NSS) @@ -266,14 +326,32 @@ void IOThread::CleanUp() { // Destroy all URLRequests started by URLFetchers. URLFetcher::CancelAll(); - // This must be reset before the ChromeNetLog is destroyed. - network_change_observer_.reset(); + // Break any cycles between the ProxyScriptFetcher and URLRequestContext. + for (ProxyScriptFetchers::const_iterator it = fetchers_.begin(); + it != fetchers_.end(); ++it) { + (*it)->Cancel(); + } // If any child processes are still running, terminate them and // and delete the BrowserChildProcessHost instances to release whatever // IO thread only resources they are referencing. BrowserChildProcessHost::TerminateAll(); + std::list<ChromeURLRequestContextGetter*> url_request_context_getters; + url_request_context_getters.swap(url_request_context_getters_); + for (std::list<ChromeURLRequestContextGetter*>::iterator it = + url_request_context_getters.begin(); + it != url_request_context_getters.end(); ++it) { + ChromeURLRequestContextGetter* getter = *it; + getter->ReleaseURLRequestContext(); + } + + // Step 2: Release objects that the URLRequestContext could have been pointing + // to. + + // This must be reset before the ChromeNetLog is destroyed. + network_change_observer_.reset(); + // Not initialized in Init(). May not be initialized. if (predictor_) { predictor_->Shutdown(); @@ -294,12 +372,6 @@ void IOThread::CleanUp() { globals_->host_resolver.get()->GetAsHostResolverImpl()->Shutdown(); } - // Break any cycles between the ProxyScriptFetcher and URLRequestContext. - for (ProxyScriptFetchers::const_iterator it = fetchers_.begin(); - it != fetchers_.end(); ++it) { - (*it)->Cancel(); - } - // We will delete the NetLog as part of CleanUpAfterMessageLoopDestruction() // in case any of the message loop destruction observers try to access it. deferred_net_log_to_delete_.reset(globals_->net_log.release()); @@ -312,10 +384,13 @@ void IOThread::CleanUp() { void IOThread::CleanUpAfterMessageLoopDestruction() { // TODO(eroman): get rid of this special case for 39723. If we could instead - // have a method that runs after the message loop destruction obsevers have + // have a method that runs after the message loop destruction observers have // run, but before the message loop itself is destroyed, we could safely // combine the two cleanups. deferred_net_log_to_delete_.reset(); + + // This will delete the |notification_service_|. Make sure it's done after + // anything else can reference it. BrowserProcessSubThread::CleanUpAfterMessageLoopDestruction(); // URLRequest instances must NOT outlive the IO thread. @@ -323,7 +398,7 @@ void IOThread::CleanUpAfterMessageLoopDestruction() { // To allow for URLRequests to be deleted from // MessageLoop::DestructionObserver this check has to happen after CleanUp // (which runs before DestructionObservers). - base::LeakTracker<URLRequest>::CheckForLeaks(); + base::debug::LeakTracker<URLRequest>::CheckForLeaks(); } net::HttpAuthHandlerFactory* IOThread::CreateDefaultAuthHandlerFactory( @@ -366,7 +441,7 @@ net::HttpAuthHandlerFactory* IOThread::CreateDefaultAuthHandlerFactory( void IOThread::InitNetworkPredictorOnIOThread( bool prefetching_enabled, base::TimeDelta max_dns_queue_delay, - size_t max_concurrent, + size_t max_speculative_parallel_resolves, const chrome_common_net::UrlList& startup_urls, ListValue* referral_list, bool preconnect_enabled) { @@ -378,7 +453,7 @@ void IOThread::InitNetworkPredictorOnIOThread( predictor_ = new chrome_browser_net::Predictor( globals_->host_resolver.get(), max_dns_queue_delay, - max_concurrent, + max_speculative_parallel_resolves, preconnect_enabled); predictor_->AddRef(); diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h index 2baec87..99cb281 100644 --- a/chrome/browser/io_thread.h +++ b/chrome/browser/io_thread.h @@ -6,8 +6,8 @@ #define CHROME_BROWSER_IO_THREAD_H_ #pragma once +#include <list> #include <set> - #include "base/basictypes.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" @@ -17,6 +17,7 @@ #include "net/base/network_change_notifier.h" class ChromeNetLog; +class ChromeURLRequestContextGetter; class ListValue; class URLRequestContext; @@ -61,11 +62,26 @@ class IOThread : public BrowserProcessSubThread { // It will post a task to the IO thread to perform the actual initialization. void InitNetworkPredictor(bool prefetching_enabled, base::TimeDelta max_dns_queue_delay, - size_t max_concurrent, + size_t max_speculative_parallel_resolves, const chrome_common_net::UrlList& startup_urls, ListValue* referral_list, bool preconnect_enabled); + // Registers |url_request_context_getter| into the IO thread. During + // IOThread::CleanUp(), IOThread will iterate through known getters and + // release their URLRequestContexts. Only called on the IO thread. It does + // not acquire a refcount for |url_request_context_getter|. If + // |url_request_context_getter| is being deleted before IOThread::CleanUp() is + // invoked, then this needs to be balanced with a call to + // UnregisterURLRequestContextGetter(). + void RegisterURLRequestContextGetter( + ChromeURLRequestContextGetter* url_request_context_getter); + + // Unregisters |url_request_context_getter| from the IO thread. Only called + // on the IO thread. + void UnregisterURLRequestContextGetter( + ChromeURLRequestContextGetter* url_request_context_getter); + // Handles changing to On The Record mode. Posts a task for this onto the // IOThread's message loop. void ChangedToOnTheRecord(); @@ -92,9 +108,8 @@ class IOThread : public BrowserProcessSubThread { void InitNetworkPredictorOnIOThread( bool prefetching_enabled, base::TimeDelta max_dns_queue_delay, - size_t max_concurrent, - const chrome_common_net::UrlList& startup_urls, - + size_t max_speculative_parallel_resolves, + const chrome_common_net::UrlList& startup_urls, ListValue* referral_list, bool preconnect_enabled); @@ -132,6 +147,11 @@ class IOThread : public BrowserProcessSubThread { // List of live ProxyScriptFetchers. ProxyScriptFetchers fetchers_; + // Keeps track of all live ChromeURLRequestContextGetters, so the + // ChromeURLRequestContexts can be released during + // IOThread::CleanUpAfterMessageLoopDestruction(). + std::list<ChromeURLRequestContextGetter*> url_request_context_getters_; + DISALLOW_COPY_AND_ASSIGN(IOThread); }; diff --git a/chrome/browser/jankometer.cc b/chrome/browser/jankometer.cc index a9a99b0..ed4f9fd 100644 --- a/chrome/browser/jankometer.cc +++ b/chrome/browser/jankometer.cc @@ -91,9 +91,24 @@ class JankObserverHelper { void StartProcessingTimers(const TimeDelta& queueing_time); void EndProcessingTimers(); + // Indicate if we will bother to measuer this message. + bool MessageWillBeMeasured(); + + static void SetDefaultMessagesToSkip(int count) { discard_count_ = count; } + private: const TimeDelta max_message_delay_; + // Indicate if we'll bother measuring this message. + bool measure_current_message_; + + // Down counter which will periodically hit 0, and only then bother to measure + // the corresponding message. + int events_till_measurement_; + + // The value to re-initialize events_till_measurement_ after it reaches 0. + static int discard_count_; + // Time at which the current message processing began. TimeTicks begin_process_message_; @@ -116,6 +131,8 @@ JankObserverHelper::JankObserverHelper( const TimeDelta& excessive_duration, bool watchdog_enable) : max_message_delay_(excessive_duration), + measure_current_message_(true), + events_till_measurement_(0), slow_processing_counter_(std::string("Chrome.SlowMsg") + thread_name), queueing_delay_counter_(std::string("Chrome.DelayMsg") + thread_name), total_time_watchdog_(excessive_duration, thread_name, watchdog_enable) { @@ -125,6 +142,11 @@ JankObserverHelper::JankObserverHelper( total_times_ = base::Histogram::FactoryGet( std::string("Chrome.TotalMsgL ") + thread_name, 1, 3600000, 50, base::Histogram::kUmaTargetedHistogramFlag); + if (discard_count_ > 0) { + // Select a vaguely random sample-start-point. + events_till_measurement_ = static_cast<int>( + (TimeTicks::Now() - TimeTicks()).InSeconds() % (discard_count_ + 1)); + } } JankObserverHelper::~JankObserverHelper() {} @@ -132,6 +154,7 @@ JankObserverHelper::~JankObserverHelper() {} // Called when a message has just begun processing, initializes // per-message variables and timers. void JankObserverHelper::StartProcessingTimers(const TimeDelta& queueing_time) { + DCHECK(measure_current_message_); begin_process_message_ = TimeTicks::Now(); queueing_time_ = queueing_time; @@ -150,6 +173,8 @@ void JankObserverHelper::StartProcessingTimers(const TimeDelta& queueing_time) { // Called when a message has just finished processing, finalizes // per-message variables and timers. void JankObserverHelper::EndProcessingTimers() { + if (!measure_current_message_) + return; total_time_watchdog_.Disarm(); TimeTicks now = TimeTicks::Now(); if (begin_process_message_ != TimeTicks()) { @@ -172,6 +197,18 @@ void JankObserverHelper::EndProcessingTimers() { queueing_time_ = base::TimeDelta(); } +bool JankObserverHelper::MessageWillBeMeasured() { + measure_current_message_ = events_till_measurement_ <= 0; + if (!measure_current_message_) + --events_till_measurement_; + else + events_till_measurement_ = discard_count_; + return measure_current_message_; +} + +// static +int JankObserverHelper::discard_count_ = 99; // Measure only 1 in 100. + //------------------------------------------------------------------------------ class IOJankObserver : public base::RefCountedThreadSafe<IOJankObserver>, public MessageLoopForIO::IOObserver, @@ -199,6 +236,8 @@ class IOJankObserver : public base::RefCountedThreadSafe<IOJankObserver>, } virtual void WillProcessIOEvent() { + if (!helper_.MessageWillBeMeasured()) + return; helper_.StartProcessingTimers(base::TimeDelta()); } @@ -206,13 +245,15 @@ class IOJankObserver : public base::RefCountedThreadSafe<IOJankObserver>, helper_.EndProcessingTimers(); } - virtual void WillProcessTask(base::TimeTicks birth_time) { + virtual void WillProcessTask(const Task* task) { + if (!helper_.MessageWillBeMeasured()) + return; base::TimeTicks now = base::TimeTicks::Now(); - const base::TimeDelta queueing_time = now - birth_time; + const base::TimeDelta queueing_time = now - task->tracked_birth_time(); helper_.StartProcessingTimers(queueing_time); } - virtual void DidProcessTask() { + virtual void DidProcessTask(const Task* task) { helper_.EndProcessingTimers(); } @@ -250,18 +291,22 @@ class UIJankObserver : public base::RefCountedThreadSafe<UIJankObserver>, MessageLoopForUI::current()->RemoveObserver(this); } - virtual void WillProcessTask(base::TimeTicks birth_time) { + virtual void WillProcessTask(const Task* task) { + if (!helper_.MessageWillBeMeasured()) + return; base::TimeTicks now = base::TimeTicks::Now(); - const base::TimeDelta queueing_time = now - birth_time; + const base::TimeDelta queueing_time = now - task->tracked_birth_time(); helper_.StartProcessingTimers(queueing_time); } - virtual void DidProcessTask() { + virtual void DidProcessTask(const Task* task) { helper_.EndProcessingTimers(); } #if defined(OS_WIN) virtual void WillProcessMessage(const MSG& msg) { + if (!helper_.MessageWillBeMeasured()) + return; // GetMessageTime returns a LONG (signed 32-bit) and GetTickCount returns // a DWORD (unsigned 32-bit). They both wrap around when the time is longer // than they can hold. I'm not sure if GetMessageTime wraps around to 0, @@ -284,6 +329,8 @@ class UIJankObserver : public base::RefCountedThreadSafe<UIJankObserver>, } #elif defined(TOOLKIT_USES_GTK) virtual void WillProcessEvent(GdkEvent* event) { + if (!helper_.MessageWillBeMeasured()) + return; // TODO(evanm): we want to set queueing_time_ using // gdk_event_get_time, but how do you convert that info // into a delta? @@ -330,6 +377,9 @@ void InstallJankometer(const CommandLine& parsed_command_line) { io_watchdog_enabled = true; } + if (ui_watchdog_enabled || io_watchdog_enabled) + JankObserverHelper::SetDefaultMessagesToSkip(0); // Watch everything. + // Install on the UI thread. ui_observer = new scoped_refptr<UIJankObserver>( new UIJankObserver( diff --git a/chrome/browser/language_combobox_model.cc b/chrome/browser/language_combobox_model.cc index ceb8fb4..55b98d4 100644 --- a/chrome/browser/language_combobox_model.cc +++ b/chrome/browser/language_combobox_model.cc @@ -7,6 +7,7 @@ #include "app/l10n_util.h" #include "base/i18n/rtl.h" #include "base/string_split.h" +#include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/metrics/user_metrics.h" @@ -109,8 +110,8 @@ std::wstring LanguageList::GetLanguageNameAt(int index) const { // changed the format. We also want to switch the order of locale_name // and native_name without going back to translators. std::wstring formatted_item; - SStringPrintf(&formatted_item, L"%ls - %ls", locale_name.c_str(), - native_name.c_str()); + base::SStringPrintf(&formatted_item, L"%ls - %ls", locale_name.c_str(), + native_name.c_str()); if (base::i18n::IsRTL()) // Somehow combo box (even with LAYOUTRTL flag) doesn't get this // right so we add RTL BDO (U+202E) to set the direction diff --git a/chrome/browser/language_order_table_model.cc b/chrome/browser/language_order_table_model.cc index 0f3680b..c1a4103 100644 --- a/chrome/browser/language_order_table_model.cc +++ b/chrome/browser/language_order_table_model.cc @@ -16,6 +16,8 @@ LanguageOrderTableModel::LanguageOrderTableModel() : observer_(NULL) { } +LanguageOrderTableModel::~LanguageOrderTableModel() {} + void LanguageOrderTableModel::SetAcceptLanguagesString( const std::string& language_list) { std::vector<std::string> languages_vector; diff --git a/chrome/browser/language_order_table_model.h b/chrome/browser/language_order_table_model.h index 4b11748..9cfdf4c 100644 --- a/chrome/browser/language_order_table_model.h +++ b/chrome/browser/language_order_table_model.h @@ -18,6 +18,8 @@ class LanguageOrderTableModel : public TableModel { public: LanguageOrderTableModel(); + virtual ~LanguageOrderTableModel(); + // Set Language List. void SetAcceptLanguagesString(const std::string& language_list); diff --git a/chrome/browser/locale_tests_uitest.cc b/chrome/browser/locale_tests_uitest.cc index 85a9510..ea171a8 100644 --- a/chrome/browser/locale_tests_uitest.cc +++ b/chrome/browser/locale_tests_uitest.cc @@ -12,8 +12,7 @@ class LocaleTestsBase : public UITest { LocaleTestsBase() : UITest(), old_lc_all_(NULL) { } - protected: - void RestoreLcAllEnvironment() { + virtual void TearDown() { #if defined(OS_LINUX) scoped_ptr<base::Environment> env(base::Environment::Create()); if (old_lc_all_) { @@ -22,8 +21,10 @@ class LocaleTestsBase : public UITest { env->UnSetVar("LC_ALL"); } #endif - }; + UITest::TearDown(); + } + protected: const char* old_lc_all_; }; @@ -66,15 +67,12 @@ class LocaleTestsZhTw : public LocaleTestsBase { TEST_F(LocaleTestsDa, TestStart) { // Just making sure we can start/shutdown cleanly. - RestoreLcAllEnvironment(); } TEST_F(LocaleTestsHe, TestStart) { // Just making sure we can start/shutdown cleanly. - RestoreLcAllEnvironment(); } TEST_F(LocaleTestsZhTw, TestStart) { // Just making sure we can start/shutdown cleanly. - RestoreLcAllEnvironment(); } diff --git a/chrome/browser/login_prompt_gtk.cc b/chrome/browser/login_prompt_gtk.cc index 77dab23..6752925 100644 --- a/chrome/browser/login_prompt_gtk.cc +++ b/chrome/browser/login_prompt_gtk.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -17,6 +17,7 @@ #include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "chrome/browser/tab_contents/navigation_controller.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_delegate.h" #include "chrome/browser/tab_contents/tab_contents_view_gtk.h" #include "chrome/browser/tab_contents/tab_util.h" #include "chrome/common/notification_service.h" @@ -173,15 +174,19 @@ void LoginHandlerGtk::OnPromptHierarchyChanged(GtkWidget* sender, GTK_WIDGET_SET_FLAGS(ok_, GTK_CAN_DEFAULT); gtk_widget_grab_default(ok_); + TabContents* contents = GetTabContentsForLogin(); + // The user may have focused another tab. In this case do not grab focus // until this tab is refocused. - if (gtk_util::IsWidgetAncestryVisible(username_entry_)) { + if ((!contents->delegate() || + contents->delegate()->ShouldFocusConstrainedWindow()) && + gtk_util::IsWidgetAncestryVisible(username_entry_)) { gtk_widget_grab_focus(username_entry_); } else { // TODO(estade): this define should not need to be here because this class // should not be used on linux/views. #if defined(TOOLKIT_GTK) - static_cast<TabContentsViewGtk*>(GetTabContentsForLogin()->view())-> + static_cast<TabContentsViewGtk*>(contents->view())-> SetFocusedWidget(username_entry_); #endif } diff --git a/chrome/browser/login_prompt_uitest.cc b/chrome/browser/login_prompt_uitest.cc index 410db2b..bdda3fd 100644 --- a/chrome/browser/login_prompt_uitest.cc +++ b/chrome/browser/login_prompt_uitest.cc @@ -51,8 +51,15 @@ wstring ExpectedTitleFromAuth(const wstring& username, return username + L"/" + password; } +#if defined(OS_WIN) +// Probably related to test server flakiness in http://crbug.com/60937 +#define MAYBE_TestBasicAuth FLAKY_TestBasicAuth +#else +#define MAYBE_TestBasicAuth TestBasicAuth +#endif + // Test that "Basic" HTTP authentication works. -TEST_F(LoginPromptTest, TestBasicAuth) { +TEST_F(LoginPromptTest, MAYBE_TestBasicAuth) { ASSERT_TRUE(test_server_.Start()); scoped_refptr<TabProxy> tab(GetActiveTab()); @@ -75,8 +82,15 @@ TEST_F(LoginPromptTest, TestBasicAuth) { GetActiveTabTitle()); } +#if defined(OS_WIN) +// Probably related to test server flakiness in http://crbug.com/60937 +#define MAYBE_TestDigestAuth FLAKY_TestDigestAuth +#else +#define MAYBE_TestDigestAuth TestDigestAuth +#endif + // Test that "Digest" HTTP authentication works. -TEST_F(LoginPromptTest, TestDigestAuth) { +TEST_F(LoginPromptTest, MAYBE_TestDigestAuth) { ASSERT_TRUE(test_server_.Start()); scoped_refptr<TabProxy> tab(GetActiveTab()); @@ -98,8 +112,15 @@ TEST_F(LoginPromptTest, TestDigestAuth) { GetActiveTabTitle()); } +#if defined(OS_WIN) +// Probably related to test server flakiness in http://crbug.com/60937 +#define MAYBE_TestTwoAuths FLAKY_TestTwoAuths +#else +#define MAYBE_TestTwoAuths TestTwoAuths +#endif + // Test that logging in on 2 tabs at once works. -TEST_F(LoginPromptTest, TestTwoAuths) { +TEST_F(LoginPromptTest, MAYBE_TestTwoAuths) { ASSERT_TRUE(test_server_.Start()); scoped_refptr<TabProxy> basic_tab(GetActiveTab()); @@ -126,8 +147,15 @@ TEST_F(LoginPromptTest, TestTwoAuths) { EXPECT_EQ(ExpectedTitleFromAuth(username_digest_, password_), title); } +#if defined(OS_WIN) +// Probably related to test server flakiness in http://crbug.com/60937 +#define MAYBE_TestCancelAuth FLAKY_TestCancelAuth +#else +#define MAYBE_TestCancelAuth TestCancelAuth +#endif + // Test that cancelling authentication works. -TEST_F(LoginPromptTest, TestCancelAuth) { +TEST_F(LoginPromptTest, MAYBE_TestCancelAuth) { ASSERT_TRUE(test_server_.Start()); scoped_refptr<TabProxy> tab(GetActiveTab()); @@ -171,9 +199,16 @@ TEST_F(LoginPromptTest, TestCancelAuth) { EXPECT_EQ(L"Denied: no auth", GetActiveTabTitle()); } +#if defined(OS_WIN) +// Probably related to test server flakiness in http://crbug.com/60937 +#define MAYBE_SupplyRedundantAuths FLAKY_SupplyRedundantAuths +#else +#define MAYBE_SupplyRedundantAuths SupplyRedundantAuths +#endif + // If multiple tabs are looking for the same auth, the user should only have to // enter it once. -TEST_F(LoginPromptTest, SupplyRedundantAuths) { +TEST_F(LoginPromptTest, MAYBE_SupplyRedundantAuths) { ASSERT_TRUE(test_server_.Start()); scoped_refptr<TabProxy> basic_tab1(GetActiveTab()); @@ -204,9 +239,16 @@ TEST_F(LoginPromptTest, SupplyRedundantAuths) { EXPECT_EQ(ExpectedTitleFromAuth(username_basic_, password_), title2); } +#if defined(OS_WIN) +// Probably related to test server flakiness in http://crbug.com/60937 +#define MAYBE_CancelRedundantAuths FLAKY_CancelRedundantAuths +#else +#define MAYBE_CancelRedundantAuths CancelRedundantAuths +#endif + // If multiple tabs are looking for the same auth, and one is cancelled, the // other should be cancelled as well. -TEST_F(LoginPromptTest, CancelRedundantAuths) { +TEST_F(LoginPromptTest, MAYBE_CancelRedundantAuths) { ASSERT_TRUE(test_server_.Start()); scoped_refptr<TabProxy> basic_tab1(GetActiveTab()); diff --git a/chrome/browser/login_prompt_win.cc b/chrome/browser/login_prompt_win.cc index df887fd..703dbbb 100644 --- a/chrome/browser/login_prompt_win.cc +++ b/chrome/browser/login_prompt_win.cc @@ -107,7 +107,7 @@ class LoginHandlerWin : public LoginHandler, TabContents* tab_contents = GetTabContentsForLogin(); bool should_focus_view = !tab_contents->delegate() || - tab_contents->delegate()->ShouldFocusConstrainedWindow(tab_contents); + tab_contents->delegate()->ShouldFocusConstrainedWindow(); LoginView* view = new LoginView(explanation, should_focus_view); diff --git a/chrome/browser/memory_purger.cc b/chrome/browser/memory_purger.cc index d1431da..3543ee5 100644 --- a/chrome/browser/memory_purger.cc +++ b/chrome/browser/memory_purger.cc @@ -37,7 +37,8 @@ class PurgeMemoryIOHelper : safe_browsing_service_(safe_browsing_service) { } - void AddRequestContextGetter(URLRequestContextGetter* request_context_getter); + void AddRequestContextGetter( + scoped_refptr<URLRequestContextGetter> request_context_getter); void PurgeMemoryOnIOThread(); @@ -52,11 +53,8 @@ class PurgeMemoryIOHelper }; void PurgeMemoryIOHelper::AddRequestContextGetter( - URLRequestContextGetter* request_context_getter) { - if (!request_context_getters_.count(request_context_getter)) { - request_context_getters_.insert( - RequestContextGetter(request_context_getter)); - } + scoped_refptr<URLRequestContextGetter> request_context_getter) { + request_context_getters_.insert(request_context_getter); } void PurgeMemoryIOHelper::PurgeMemoryOnIOThread() { @@ -104,7 +102,7 @@ void MemoryPurger::PurgeBrowser() { i != profile_manager->end(); ++i) { Profile* profile = *i; purge_memory_io_helper->AddRequestContextGetter( - profile->GetRequestContext()); + make_scoped_refptr(profile->GetRequestContext())); // NOTE: Some objects below may be duplicates across profiles. We could // conceivably put all these in sets and then iterate over the sets. diff --git a/chrome/browser/message_box_handler.cc b/chrome/browser/message_box_handler.cc index 1f5831a..4150945 100644 --- a/chrome/browser/message_box_handler.cc +++ b/chrome/browser/message_box_handler.cc @@ -28,7 +28,8 @@ static std::wstring GetTitle(Profile* profile, const GURL& frame_url) { ExtensionsService* extensions_service = profile->GetExtensionsService(); if (extensions_service) { - Extension* extension = extensions_service->GetExtensionByURL(frame_url); + const Extension* extension = + extensions_service->GetExtensionByURL(frame_url); if (!extension) extension = extensions_service->GetExtensionByWebExtent(frame_url); @@ -46,10 +47,9 @@ static std::wstring GetTitle(Profile* profile, // TODO(brettw) it should be easier than this to do the correct language // handling without getting the accept language from the profile. - string16 base_address = WideToUTF16(gfx::ElideUrl(frame_url.GetOrigin(), + string16 base_address = gfx::ElideUrl(frame_url.GetOrigin(), gfx::Font(), 0, - UTF8ToWide( - profile->GetPrefs()->GetString(prefs::kAcceptLanguages)))); + UTF8ToWide(profile->GetPrefs()->GetString(prefs::kAcceptLanguages))); // Force URL to have LTR directionality. base_address = base::i18n::GetDisplayStringInLTRDirectionality( diff --git a/chrome/browser/metrics/histogram_synchronizer.cc b/chrome/browser/metrics/histogram_synchronizer.cc index 3311bb5..0fa0c82 100644 --- a/chrome/browser/metrics/histogram_synchronizer.cc +++ b/chrome/browser/metrics/histogram_synchronizer.cc @@ -18,15 +18,15 @@ using base::TimeTicks; HistogramSynchronizer::HistogramSynchronizer() : lock_(), - received_all_renderer_historgrams_(&lock_), + received_all_renderer_histograms_(&lock_), callback_task_(NULL), callback_thread_(NULL), io_message_loop_(NULL), - next_available_sequence_number_(0), - async_sequence_number_(0), + next_available_sequence_number_(-2), + async_sequence_number_(-2), async_renderers_pending_(0), async_callback_start_time_(TimeTicks::Now()), - synchronous_sequence_number_(0), + synchronous_sequence_number_(-2), synchronous_renderers_pending_(0) { DCHECK(histogram_synchronizer_ == NULL); histogram_synchronizer_ = this; @@ -50,23 +50,24 @@ void HistogramSynchronizer::FetchRendererHistogramsSynchronously( TimeDelta wait_time) { DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); - int sequence_number = GetNextAvaibleSequenceNumber(SYNCHRONOUS_HISTOGRAMS); + int sequence_number = GetNextAvailableSequenceNumber(SYNCHRONOUS_HISTOGRAMS); for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); !it.IsAtEnd(); it.Advance()) { + IncrementPendingRenderers(SYNCHRONOUS_HISTOGRAMS); it.GetCurrentValue()->Send( new ViewMsg_GetRendererHistograms(sequence_number)); - IncrementPendingRenderers(SYNCHRONOUS_HISTOGRAMS); } + // Send notification that we're done sending messages to renderers. + DecrementPendingRenderers(sequence_number); TimeTicks start = TimeTicks::Now(); TimeTicks end_time = start + wait_time; int unresponsive_renderer_count; { AutoLock auto_lock(lock_); - while (synchronous_renderers_pending_ > 0 && - TimeTicks::Now() < end_time) { + while (synchronous_renderers_pending_ > 0 && TimeTicks::Now() < end_time) { wait_time = end_time - TimeTicks::Now(); - received_all_renderer_historgrams_.TimedWait(wait_time); + received_all_renderer_histograms_.TimedWait(wait_time); } unresponsive_renderer_count = synchronous_renderers_pending_; synchronous_renderers_pending_ = 0; @@ -108,13 +109,15 @@ void HistogramSynchronizer::FetchRendererHistogramsAsynchronously( // Tell all renderer processes to send their histograms. int sequence_number = - current_synchronizer->GetNextAvaibleSequenceNumber(ASYNC_HISTOGRAMS); + current_synchronizer->GetNextAvailableSequenceNumber(ASYNC_HISTOGRAMS); for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); !it.IsAtEnd(); it.Advance()) { + current_synchronizer->IncrementPendingRenderers(ASYNC_HISTOGRAMS); it.GetCurrentValue()->Send( new ViewMsg_GetRendererHistograms(sequence_number)); - current_synchronizer->IncrementPendingRenderers(ASYNC_HISTOGRAMS); } + // Send notification that we're done sending messages to renderers. + current_synchronizer->DecrementPendingRenderers(sequence_number); // Post a task that would be called after waiting for wait_time. BrowserThread::PostDelayedTask( @@ -144,13 +147,12 @@ void HistogramSynchronizer::DeserializeHistogramList( } // Record that we have received a histogram from renderer process. - current_synchronizer->RecordRendererHistogram(sequence_number); + current_synchronizer->DecrementPendingRenderers(sequence_number); } -bool HistogramSynchronizer::RecordRendererHistogram(int sequence_number) { - DCHECK(IsOnIoThread()); - +bool HistogramSynchronizer::DecrementPendingRenderers(int sequence_number) { if (sequence_number == async_sequence_number_) { + DCHECK(IsOnIoThread()); if ((async_renderers_pending_ == 0) || (--async_renderers_pending_ > 0)) return false; @@ -166,14 +168,13 @@ bool HistogramSynchronizer::RecordRendererHistogram(int sequence_number) { // synchronous_sequence_number_ or async_sequence_number_. return true; } - if ((synchronous_renderers_pending_ == 0) || - (--synchronous_renderers_pending_ > 0)) + if (--synchronous_renderers_pending_ > 0) return false; DCHECK_EQ(synchronous_renderers_pending_, 0); } // We could call Signal() without holding the lock. - received_all_renderer_historgrams_.Signal(); + received_all_renderer_histograms_.Signal(); return true; } @@ -237,7 +238,7 @@ void HistogramSynchronizer::CallCallbackTaskAndResetData() { callback_thread_ = NULL; } -int HistogramSynchronizer::GetNextAvaibleSequenceNumber( +int HistogramSynchronizer::GetNextAvailableSequenceNumber( RendererHistogramRequester requester) { AutoLock auto_lock(lock_); ++next_available_sequence_number_; @@ -249,11 +250,12 @@ int HistogramSynchronizer::GetNextAvaibleSequenceNumber( DCHECK_NE(next_available_sequence_number_, chrome::kHistogramSynchronizerReservedSequenceNumber); if (requester == ASYNC_HISTOGRAMS) { + DCHECK(IsOnIoThread()); async_sequence_number_ = next_available_sequence_number_; - async_renderers_pending_ = 0; + async_renderers_pending_ = 1; } else if (requester == SYNCHRONOUS_HISTOGRAMS) { synchronous_sequence_number_ = next_available_sequence_number_; - synchronous_renderers_pending_ = 0; + synchronous_renderers_pending_ = 1; } return next_available_sequence_number_; } @@ -261,9 +263,13 @@ int HistogramSynchronizer::GetNextAvaibleSequenceNumber( void HistogramSynchronizer::IncrementPendingRenderers( RendererHistogramRequester requester) { if (requester == ASYNC_HISTOGRAMS) { - async_renderers_pending_++; + DCHECK(IsOnIoThread()); + DCHECK_GT(async_renderers_pending_, 0); + ++async_renderers_pending_; } else { - synchronous_renderers_pending_++; + AutoLock auto_lock(lock_); + DCHECK_GT(synchronous_renderers_pending_, 0); + ++synchronous_renderers_pending_; } } diff --git a/chrome/browser/metrics/histogram_synchronizer.h b/chrome/browser/metrics/histogram_synchronizer.h index 39e0e2a..9190b20 100644 --- a/chrome/browser/metrics/histogram_synchronizer.h +++ b/chrome/browser/metrics/histogram_synchronizer.h @@ -55,12 +55,12 @@ class HistogramSynchronizer : public int sequence_number, const std::vector<std::string>& histograms); private: - // Records that we have received the histograms from a renderer for the given - // sequence number. If we have received a response from all histograms, either - // signal the waiting process or call the callback function. Returns true when - // we receive histograms from the last of N renderers that were contacted for - // an update. This is called on IO Thread. - bool RecordRendererHistogram(int sequence_number); + // Records that we are waiting for one less histogram from a renderer for the + // given sequence number. If we have received a response from all histograms, + // either signal the waiting process or call the callback function. Returns + // true when we receive histograms from the last of N renderers that were + // contacted for an update. + bool DecrementPendingRenderers(int sequence_number); void SetCallbackTaskToCallAfterGettingHistograms( MessageLoop* callback_thread, Task* callback_task); @@ -71,8 +71,11 @@ class HistogramSynchronizer : public void CallCallbackTaskAndResetData(); // Gets a new sequence number to be sent to renderers from browser process. - // This will also reset the current pending renderers for the given type. - int GetNextAvaibleSequenceNumber(RendererHistogramRequester requster); + // This will also reset the count of pending renderers for the given type to + // 1. After all calls to renderers have been made, a call to + // DecrementPendingRenderers() must be mode to make it possible for the + // counter to go to zero (after all renderers have responded). + int GetNextAvailableSequenceNumber(RendererHistogramRequester requster); // Increments the count of the renderers we're waiting for for the request // of the given type. @@ -84,13 +87,13 @@ class HistogramSynchronizer : public // singular IO thread and we don't need to worry about locks. bool IsOnIoThread(); - // This lock_ protects access to sequence number and - // synchronous_renderers_pending_. + // This lock_ protects access to next_sequence_number_, + // synchronous_renderers_pending_, and synchronous_sequence_number_. Lock lock_; // This condition variable is used to block caller of the synchronous request // to update histograms, and to signal that thread when updates are completed. - ConditionVariable received_all_renderer_historgrams_; + ConditionVariable received_all_renderer_histograms_; // When a request is made to asynchronously update the histograms, we store // the task and thread we use to post a completion notification in @@ -110,27 +113,27 @@ class HistogramSynchronizer : public // sure a response from a renderer is associated with the current round of // requests (and not merely a VERY belated prior response). // next_available_sequence_number_ is the next available number (used to - // avoid reuse for a long time). + // avoid reuse for a long time). Access is protected by lock_. int next_available_sequence_number_; // The sequence number used by the most recent asynchronous update request to - // contact all renderers. + // contact all renderers. Access is only permitted on the IO thread. int async_sequence_number_; // The number of renderers that have not yet responded to requests (as part of - // an asynchronous update). + // an asynchronous update). Access is only permitted on the IO thread. int async_renderers_pending_; // The time when we were told to start the fetch histograms asynchronously - // from renderers. + // from renderers. Access is only permitted on the IO thread. base::TimeTicks async_callback_start_time_; // The sequence number used by the most recent synchronous update request to - // contact all renderers. + // contact all renderers. Protected by lock_. int synchronous_sequence_number_; // The number of renderers that have not yet responded to requests (as part of - // a synchronous update). + // a synchronous update). Protected by lock_. int synchronous_renderers_pending_; // This singleton instance should be started during the single threaded diff --git a/chrome/browser/metrics/metrics_log.cc b/chrome/browser/metrics/metrics_log.cc index 7d602e6..5a7db76 100644 --- a/chrome/browser/metrics/metrics_log.cc +++ b/chrome/browser/metrics/metrics_log.cc @@ -17,6 +17,7 @@ #include "base/time.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/gpu_process_host.h" #include "chrome/browser/prefs/pref_service.h" diff --git a/chrome/browser/metrics/metrics_service.cc b/chrome/browser/metrics/metrics_service.cc index 1ddc987..9cafefc 100644 --- a/chrome/browser/metrics/metrics_service.cc +++ b/chrome/browser/metrics/metrics_service.cc @@ -970,7 +970,7 @@ void MetricsService::LogTransmissionTimerDone() { Task* task = log_sender_factory_. NewRunnableMethod(&MetricsService::OnMemoryDetailCollectionDone); - scoped_refptr<MetricsMemoryDetails> details = new MetricsMemoryDetails(task); + scoped_refptr<MetricsMemoryDetails> details(new MetricsMemoryDetails(task)); details->StartFetch(); // Collect WebCore cache information to put into a histogram. diff --git a/chrome/browser/metrics/metrics_service_uitest.cc b/chrome/browser/metrics/metrics_service_uitest.cc index cc71c5a..93889c5 100644 --- a/chrome/browser/metrics/metrics_service_uitest.cc +++ b/chrome/browser/metrics/metrics_service_uitest.cc @@ -73,10 +73,6 @@ TEST_F(MetricsServiceTest, CloseRenderersNormally) { EXPECT_EQ(0, local_state->GetInteger(prefs::kStabilityRendererCrashCount)); } -#if defined(OS_WIN) -// http://crbug.com/32048 -#define CrashRenderers FLAKY_CrashRenders -#endif TEST_F(MetricsServiceTest, CrashRenderers) { // This doesn't make sense to test in single process mode. if (in_process_renderer_) diff --git a/chrome/browser/mock_browsing_data_indexed_db_helper.cc b/chrome/browser/mock_browsing_data_indexed_db_helper.cc index ed83fd3..06e4039 100644 --- a/chrome/browser/mock_browsing_data_indexed_db_helper.cc +++ b/chrome/browser/mock_browsing_data_indexed_db_helper.cc @@ -34,12 +34,12 @@ void MockBrowsingDataIndexedDBHelper::DeleteIndexedDBFile( void MockBrowsingDataIndexedDBHelper::AddIndexedDBSamples() { response_.push_back( BrowsingDataIndexedDBHelper::IndexedDBInfo( - "http", "idbhost1", 1, "idb1", "http://idbhost1:1/", "name", + "http", "idbhost1", 1, "idb1", "http://idbhost1:1/", FilePath(FILE_PATH_LITERAL("file1")), 1, base::Time())); files_[FILE_PATH_LITERAL("file1")] = true; response_.push_back( BrowsingDataIndexedDBHelper::IndexedDBInfo( - "http", "idbhost2", 2, "idb2", "http://idbhost2:2/", "name", + "http", "idbhost2", 2, "idb2", "http://idbhost2:2/", FilePath(FILE_PATH_LITERAL("file2")), 2, base::Time())); files_[FILE_PATH_LITERAL("file2")] = true; } diff --git a/chrome/browser/modal_html_dialog_delegate.h b/chrome/browser/modal_html_dialog_delegate.h index 54809ba..23bb0f7 100644 --- a/chrome/browser/modal_html_dialog_delegate.h +++ b/chrome/browser/modal_html_dialog_delegate.h @@ -47,6 +47,7 @@ class ModalHtmlDialogDelegate virtual std::string GetDialogArgs() const; virtual void OnDialogClosed(const std::string& json_retval); virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { } + virtual bool ShouldShowDialogTitle() const { return true; } private: NotificationRegistrar registrar_; diff --git a/chrome/browser/nacl_host/nacl_process_host.cc b/chrome/browser/nacl_host/nacl_process_host.cc index 1ee53b9..b5123ef 100644 --- a/chrome/browser/nacl_host/nacl_process_host.cc +++ b/chrome/browser/nacl_host/nacl_process_host.cc @@ -11,6 +11,7 @@ #endif #include "base/command_line.h" +#include "base/metrics/nacl_histogram.h" #include "base/utf_string_conversions.h" #include "chrome/browser/renderer_host/resource_message_filter.h" #include "chrome/common/chrome_switches.h" @@ -114,7 +115,7 @@ bool NaClProcessHost::Launch(ResourceMessageFilter* resource_message_filter, if (!LaunchSelLdr()) { return false; } - + UmaNaclHistogramEnumeration(NACL_STARTED); resource_message_filter_ = resource_message_filter; reply_msg_ = reply_msg; diff --git a/chrome/browser/net/chrome_cookie_notification_details.h b/chrome/browser/net/chrome_cookie_notification_details.h index 42542b0..57df427 100644 --- a/chrome/browser/net/chrome_cookie_notification_details.h +++ b/chrome/browser/net/chrome_cookie_notification_details.h @@ -10,13 +10,13 @@ struct ChromeCookieDetails { public: - ChromeCookieDetails(net::CookieMonster::CanonicalCookie* cookie_copy, + ChromeCookieDetails(const net::CookieMonster::CanonicalCookie* cookie_copy, bool is_removed) : cookie(cookie_copy), removed(is_removed) { } - net::CookieMonster::CanonicalCookie* cookie; + const net::CookieMonster::CanonicalCookie* cookie; bool removed; }; diff --git a/chrome/browser/net/chrome_url_request_context.cc b/chrome/browser/net/chrome_url_request_context.cc index cc1fe93..a40e548 100644 --- a/chrome/browser/net/chrome_url_request_context.cc +++ b/chrome/browser/net/chrome_url_request_context.cc @@ -208,7 +208,7 @@ class ChromeCookieMonsterDelegate : public net::CookieMonster::Delegate { virtual ~ChromeCookieMonsterDelegate() {} void OnCookieChangedAsyncHelper( - net::CookieMonster::CanonicalCookie cookie, + const net::CookieMonster::CanonicalCookie& cookie, bool removed) { if (profile_getter_->get()) { ChromeCookieDetails cookie_details(&cookie, removed); @@ -261,6 +261,7 @@ ChromeURLRequestContext* FactoryForOriginal::Create() { // Global host resolver for the context. context->set_host_resolver(io_thread_globals->host_resolver.get()); + context->set_dnsrr_resolver(io_thread_globals->dnsrr_resolver.get()); context->set_http_auth_handler_factory( io_thread_globals->http_auth_handler_factory.get()); @@ -286,9 +287,6 @@ ChromeURLRequestContext* FactoryForOriginal::Create() { io_thread_globals->net_log.get(), backend); - if (command_line.HasSwitch(switches::kDisableByteRangeSupport)) - cache->set_enable_range_support(false); - bool record_mode = chrome::kRecordModeEnabled && command_line.HasSwitch(switches::kRecordMode); bool playback_mode = command_line.HasSwitch(switches::kPlaybackMode); @@ -358,9 +356,10 @@ ChromeURLRequestContext* FactoryForExtensions::Create() { net::CookieMonster* cookie_monster = new net::CookieMonster(cookie_db.get(), NULL); - // Enable cookies for extension URLs only. - const char* schemes[] = {chrome::kExtensionScheme}; - cookie_monster->SetCookieableSchemes(schemes, 1); + // Enable cookies for devtools and extension URLs. + const char* schemes[] = {chrome::kChromeDevToolsScheme, + chrome::kExtensionScheme}; + cookie_monster->SetCookieableSchemes(schemes, 2); context->set_cookie_store(cookie_monster); // TODO(cbentzel): How should extensions handle HTTP Authentication? context->set_http_auth_handler_factory( @@ -419,10 +418,6 @@ ChromeURLRequestContext* FactoryForOffTheRecord::Create() { new ChromeCookiePolicy(host_content_settings_map_)); context->set_http_transaction_factory(cache); - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableByteRangeSupport)) - cache->set_enable_range_support(false); - context->set_ftp_transaction_factory( new net::FtpNetworkLayer(context->host_resolver())); @@ -511,10 +506,6 @@ ChromeURLRequestContext* FactoryForMedia::Create() { backend); } - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableByteRangeSupport)) - cache->set_enable_range_support(false); - context->set_http_transaction_factory(cache); context->set_net_log(io_thread_globals->net_log.get()); @@ -530,8 +521,9 @@ ChromeURLRequestContext* FactoryForMedia::Create() { ChromeURLRequestContextGetter::ChromeURLRequestContextGetter( Profile* profile, ChromeURLRequestContextFactory* factory) - : factory_(factory), - url_request_context_(NULL) { + : io_thread_(g_browser_process->io_thread()), + factory_(factory), + url_request_context_(NULL) { DCHECK(factory); // If a base profile was specified, listen for changes to the preferences. @@ -549,6 +541,9 @@ ChromeURLRequestContextGetter::~ChromeURLRequestContextGetter() { DCHECK((factory_.get() && !url_request_context_.get()) || (!factory_.get() && url_request_context_.get())); + if (url_request_context_) + io_thread_->UnregisterURLRequestContextGetter(this); + // The scoped_refptr / scoped_ptr destructors take care of releasing // |factory_| and |url_request_context_| now. } @@ -569,11 +564,17 @@ URLRequestContext* ChromeURLRequestContextGetter::GetURLRequestContext() { } factory_.reset(); + io_thread_->RegisterURLRequestContextGetter(this); } return url_request_context_; } +void ChromeURLRequestContextGetter::ReleaseURLRequestContext() { + DCHECK(url_request_context_); + url_request_context_ = NULL; +} + void ChromeURLRequestContextGetter::RegisterUserPrefs( PrefService* pref_service) { pref_service->RegisterBooleanPref(prefs::kNoProxyServer, false); @@ -608,7 +609,7 @@ net::CookieStore* ChromeURLRequestContextGetter::GetCookieStore() { } scoped_refptr<base::MessageLoopProxy> -ChromeURLRequestContextGetter::GetIOMessageLoopProxy() { +ChromeURLRequestContextGetter::GetIOMessageLoopProxy() const { return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); } @@ -766,9 +767,12 @@ ChromeURLRequestContext::~ChromeURLRequestContext() { #if defined(USE_NSS) if (is_main()) { - DCHECK_EQ(this, net::GetURLRequestContextForOCSP()); - // We are releasing the URLRequestContext used by OCSP handlers. - net::SetURLRequestContextForOCSP(NULL); + URLRequestContext* ocsp_context = net::GetURLRequestContextForOCSP(); + if (ocsp_context) { + DCHECK_EQ(this, ocsp_context); + // We are releasing the URLRequestContext used by OCSP handlers. + net::SetURLRequestContextForOCSP(NULL); + } } #endif @@ -939,9 +943,17 @@ net::ProxyConfig* CreateProxyConfig(const PrefService* pref_service) { prefs::kProxyAutoDetect }; + // Check whether the preference system holds a valid proxy configuration. Note + // that preferences coming from a lower-priority source than the user settings + // are ignored. That's because chrome treats the system settings as the + // default values, which should apply if there's no explicit value forced by + // policy or the user. bool found_enable_proxy_pref = false; for (size_t i = 0; i < arraysize(proxy_prefs); i++) { - if (pref_service->HasPrefPath(proxy_prefs[i])) { + const PrefService::Preference* pref = + pref_service->FindPreference(proxy_prefs[i]); + DCHECK(pref); + if (pref && (!pref->IsUserModifiable() || pref->HasUserSetting())) { found_enable_proxy_pref = true; break; } diff --git a/chrome/browser/net/chrome_url_request_context.h b/chrome/browser/net/chrome_url_request_context.h index a580282..51d9246 100644 --- a/chrome/browser/net/chrome_url_request_context.h +++ b/chrome/browser/net/chrome_url_request_context.h @@ -240,7 +240,11 @@ class ChromeURLRequestContextGetter : public URLRequestContextGetter, // URLRequestContextGetter implementation. virtual URLRequestContext* GetURLRequestContext(); virtual net::CookieStore* GetCookieStore(); - virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy(); + virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const; + + // Releases |url_request_context_|. It's invalid to call + // GetURLRequestContext() after this point. + void ReleaseURLRequestContext(); // Convenience overload of GetURLRequestContext() that returns a // ChromeURLRequestContext* rather than a URLRequestContext*. @@ -309,6 +313,10 @@ class ChromeURLRequestContextGetter : public URLRequestContextGetter, PrefChangeRegistrar registrar_; + // |io_thread_| is always valid during the lifetime of |this| since |this| is + // deleted on the IO thread. + IOThread* const io_thread_; + // Deferred logic for creating a ChromeURLRequestContext. // Access only from the IO thread. scoped_ptr<ChromeURLRequestContextFactory> factory_; diff --git a/chrome/browser/net/connection_tester.cc b/chrome/browser/net/connection_tester.cc index 889494d..8f762ef 100644 --- a/chrome/browser/net/connection_tester.cc +++ b/chrome/browser/net/connection_tester.cc @@ -277,8 +277,8 @@ void ConnectionTester::TestRunner::OnReadCompleted(URLRequest* request, void ConnectionTester::TestRunner::ReadBody(URLRequest* request) { // Read the response body |kReadBufferSize| bytes at a time. - scoped_refptr<net::IOBuffer> unused_buffer = - new net::IOBuffer(kReadBufferSize); + scoped_refptr<net::IOBuffer> unused_buffer( + new net::IOBuffer(kReadBufferSize)); int num_bytes; if (request->Read(unused_buffer, kReadBufferSize, &num_bytes)) { OnReadCompleted(request, num_bytes); @@ -299,8 +299,8 @@ void ConnectionTester::TestRunner::OnResponseCompleted(URLRequest* request) { void ConnectionTester::TestRunner::Run(const Experiment& experiment) { // Try to create a URLRequestContext for this experiment. - scoped_refptr<ExperimentURLRequestContext> context = - new ExperimentURLRequestContext(tester_->io_thread_); + scoped_refptr<ExperimentURLRequestContext> context( + new ExperimentURLRequestContext(tester_->io_thread_)); int rv = context->Init(experiment); if (rv != net::OK) { // Complete the experiment with a failure. diff --git a/chrome/browser/net/connection_tester_unittest.cc b/chrome/browser/net/connection_tester_unittest.cc index fe6e8cc..4191484 100644 --- a/chrome/browser/net/connection_tester_unittest.cc +++ b/chrome/browser/net/connection_tester_unittest.cc @@ -76,8 +76,8 @@ class ConnectionTesterTest : public PlatformTest { : test_server_(net::TestServer::TYPE_HTTP, FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest"))), message_loop_(MessageLoop::TYPE_IO) { - scoped_refptr<net::RuleBasedHostResolverProc> catchall_resolver = - new net::RuleBasedHostResolverProc(NULL); + scoped_refptr<net::RuleBasedHostResolverProc> catchall_resolver( + new net::RuleBasedHostResolverProc(NULL)); catchall_resolver->AddRule("*", "127.0.0.1"); diff --git a/chrome/browser/net/gaia/token_service.cc b/chrome/browser/net/gaia/token_service.cc index 963b546..a2c4cfe 100644 --- a/chrome/browser/net/gaia/token_service.cc +++ b/chrome/browser/net/gaia/token_service.cc @@ -17,8 +17,13 @@ // Unfortunately kNumServices must be defined in the .h. // TODO(chron): Sync doesn't use the TalkToken anymore so we can stop // requesting it. -const char* TokenService::kServices[] = {GaiaConstants::kSyncService, - GaiaConstants::kTalkService}; +const char* TokenService::kServices[] = { + GaiaConstants::kGaiaService, + GaiaConstants::kSyncService, + GaiaConstants::kTalkService, + GaiaConstants::kDeviceManagementService +}; + TokenService::TokenService() : token_loading_query_(0) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -184,7 +189,7 @@ void TokenService::IssueAuthTokenForTest(const std::string& service, void TokenService::OnIssueAuthTokenSuccess(const std::string& service, const std::string& auth_token) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - LOG(INFO) << "Got an authorization token for " << service; + VLOG(1) << "Got an authorization token for " << service; token_map_[service] = auth_token; FireTokenAvailableNotification(service, auth_token); SaveAuthTokenToDB(service, auth_token); @@ -237,8 +242,7 @@ void TokenService::LoadTokensIntoMemory( db_tokens.count(kServices[i])) { std::string db_token = db_tokens.find(kServices[i])->second; if (!db_token.empty()) { - LOG(INFO) << "Loading " << kServices[i] << "token from DB:" - << db_token; + VLOG(1) << "Loading " << kServices[i] << "token from DB: " << db_token; (*in_memory_tokens)[kServices[i]] = db_token; FireTokenAvailableNotification(kServices[i], db_token); // Failures are only for network errors. diff --git a/chrome/browser/net/gaia/token_service.h b/chrome/browser/net/gaia/token_service.h index b6f1b3c..bc17c2b 100644 --- a/chrome/browser/net/gaia/token_service.h +++ b/chrome/browser/net/gaia/token_service.h @@ -173,7 +173,7 @@ class TokenService : public GaiaAuthConsumer, GaiaAuthConsumer::ClientLoginResult credentials_; // Size of array of services (must be defined here). - static const int kNumServices = 2; + static const int kNumServices = 4; // List of services that we're performing operations for. static const char* kServices[kNumServices]; // A bunch of fetchers suitable for token issuing. We don't care about diff --git a/chrome/browser/net/net_log_logger.cc b/chrome/browser/net/net_log_logger.cc index 5db7657..b533e95 100644 --- a/chrome/browser/net/net_log_logger.cc +++ b/chrome/browser/net/net_log_logger.cc @@ -21,6 +21,6 @@ void NetLogLogger::OnAddEntry(net::NetLog::EventType type, params, true)); std::string json; base::JSONWriter::Write(value.get(), true, &json); - LOG(INFO) << json; + VLOG(1) << json; } diff --git a/chrome/browser/net/net_log_logger.h b/chrome/browser/net/net_log_logger.h index 98c989e..7f90d94 100644 --- a/chrome/browser/net/net_log_logger.h +++ b/chrome/browser/net/net_log_logger.h @@ -9,7 +9,7 @@ #include "chrome/browser/net/chrome_net_log.h" // NetLogLogger watches the NetLog event stream, and sends all entries to -// LOG(INFO). This is to debug errors that prevent getting to the +// VLOG(1). This is to debug errors that prevent getting to the // about:net-internals page. class NetLogLogger : public ChromeNetLog::Observer { public: diff --git a/chrome/browser/net/preconnect.cc b/chrome/browser/net/preconnect.cc index 8b7019a..307f20f 100644 --- a/chrome/browser/net/preconnect.cc +++ b/chrome/browser/net/preconnect.cc @@ -100,7 +100,7 @@ void Preconnect::Connect(const GURL& url) { if (session->http_stream_factory()->next_protos()) ssl_config_->next_protos = *session->http_stream_factory()->next_protos(); - // All preconnects should be for main pages. + // All preconnects should perform EV certificate verification. ssl_config_->verify_ev_cert = true; proxy_info_.reset(new net::ProxyInfo()); diff --git a/chrome/browser/net/predictor.h b/chrome/browser/net/predictor.h index e6363ad..f735cb4 100644 --- a/chrome/browser/net/predictor.h +++ b/chrome/browser/net/predictor.h @@ -263,7 +263,10 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { // When true, we don't make new lookup requests. bool shutdown_; - // The number of concurrent lookups currently allowed. + // The number of concurrent speculative lookups currently allowed to be sent + // to the resolver. Any additional lookups will be queued to avoid exceeding + // this value. The queue is a priority queue that will accelerate + // sub-resource speculation, and retard resolutions suggested by page scans. const size_t max_concurrent_dns_lookups_; // The maximum queueing delay that is acceptable before we enter congestion diff --git a/chrome/browser/net/predictor_api.cc b/chrome/browser/net/predictor_api.cc index 9149095..a0d885b 100644 --- a/chrome/browser/net/predictor_api.cc +++ b/chrome/browser/net/predictor_api.cc @@ -41,11 +41,35 @@ static void DnsPrefetchMotivatedList(const UrlList& urls, static UrlList GetPredictedUrlListAtStartup(PrefService* user_prefs, PrefService* local_state); +// Given that the underlying Chromium resolver defaults to a total maximum of +// 8 paralell resolutions, we will avoid any chance of starving navigational +// resolutions by limiting the number of paralell speculative resolutions. +// TODO(jar): Move this limitation into the resolver. // static -const size_t PredictorInit::kMaxPrefetchConcurrentLookups = 8; - +const size_t PredictorInit::kMaxSpeculativeParallelResolves = 3; + +// To control our congestion avoidance system, which discards a queue when +// resolutions are "taking too long," we need an expected resolution time. +// Common average is in the range of 300-500ms. +static const int kExpectedResolutionTimeMs = 500; + +// To control the congestion avoidance system, we need an estimate of how many +// speculative requests may arrive at once. Since we currently only keep 8 +// subresource names for each frame, we'll use that as our basis. Note that +// when scanning search results lists, we might actually get 10 at a time, and +// wikipedia can often supply (during a page scan) upwards of 50. In those odd +// cases, we may discard some of the later speculative requests mistakenly +// assuming that the resolutions took too long. +static const int kTypicalSpeculativeGroupSize = 8; + +// The next constant specifies an amount of queueing delay that is "too large," +// and indicative of problems with resolutions (perhaps due to an overloaded +// router, or such). When we exceed this delay, congestion avoidance will kick +// in and all speculations in the queue will be discarded. // static -const int PredictorInit::kMaxPrefetchQueueingDelayMs = 500; +const int PredictorInit::kMaxSpeculativeResolveQueueDelayMs = + (kExpectedResolutionTimeMs * kTypicalSpeculativeGroupSize) / + kMaxSpeculativeParallelResolves; // A version number for prefs that are saved. This should be incremented when // we change the format so that we discard old data. @@ -351,8 +375,10 @@ void PredictorGetHtmlInfo(std::string* output) { //------------------------------------------------------------------------------ static void InitNetworkPredictor(TimeDelta max_dns_queue_delay, - size_t max_concurrent, PrefService* user_prefs, PrefService* local_state, - bool preconnect_enabled) { + size_t max_parallel_resolves, + PrefService* user_prefs, + PrefService* local_state, + bool preconnect_enabled) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); bool prefetching_enabled = @@ -367,7 +393,7 @@ static void InitNetworkPredictor(TimeDelta max_dns_queue_delay, local_state->GetMutableList(prefs::kDnsHostReferralList)->DeepCopy()); g_browser_process->io_thread()->InitNetworkPredictor( - prefetching_enabled, max_dns_queue_delay, max_concurrent, urls, + prefetching_enabled, max_dns_queue_delay, max_parallel_resolves, urls, referral_list, preconnect_enabled); } @@ -554,33 +580,35 @@ PredictorInit::PredictorInit(PrefService* user_prefs, if (trial_->group() != disabled_prefetch) { // Initialize the DNS prefetch system. - size_t max_concurrent = kMaxPrefetchConcurrentLookups; - int max_queueing_delay_ms = kMaxPrefetchQueueingDelayMs; - - if (trial_->group() == max_250ms_prefetch) - max_queueing_delay_ms = 250; - else if (trial_->group() == max_500ms_prefetch) - max_queueing_delay_ms = 500; - else if (trial_->group() == max_750ms_prefetch) - max_queueing_delay_ms = 750; - else if (trial_->group() == max_2s_prefetch) - max_queueing_delay_ms = 2000; + size_t max_parallel_resolves = kMaxSpeculativeParallelResolves; + int max_queueing_delay_ms = kMaxSpeculativeResolveQueueDelayMs; + if (trial_->group() == max_2_concurrent_prefetch) - max_concurrent = 2; + max_parallel_resolves = 2; else if (trial_->group() == max_4_concurrent_prefetch) - max_concurrent = 4; + max_parallel_resolves = 4; else if (trial_->group() == max_6_concurrent_prefetch) - max_concurrent = 6; - // Scale acceptable delay so we don't cause congestion limits to fire as - // we modulate max_concurrent (*if* we are modulating it at all). - max_queueing_delay_ms = (kMaxPrefetchQueueingDelayMs * - kMaxPrefetchConcurrentLookups) / max_concurrent; + max_parallel_resolves = 6; + + if (trial_->group() == max_250ms_prefetch) { + max_queueing_delay_ms = + (250 * kTypicalSpeculativeGroupSize) / max_parallel_resolves; + } else if (trial_->group() == max_500ms_prefetch) { + max_queueing_delay_ms = + (500 * kTypicalSpeculativeGroupSize) / max_parallel_resolves; + } else if (trial_->group() == max_750ms_prefetch) { + max_queueing_delay_ms = + (750 * kTypicalSpeculativeGroupSize) / max_parallel_resolves; + } else if (trial_->group() == max_2s_prefetch) { + max_queueing_delay_ms = + (2000 * kTypicalSpeculativeGroupSize) / max_parallel_resolves; + } TimeDelta max_queueing_delay( TimeDelta::FromMilliseconds(max_queueing_delay_ms)); DCHECK(!g_predictor); - InitNetworkPredictor(max_queueing_delay, max_concurrent, user_prefs, + InitNetworkPredictor(max_queueing_delay, max_parallel_resolves, user_prefs, local_state, preconnect_enabled); } } diff --git a/chrome/browser/net/predictor_api.h b/chrome/browser/net/predictor_api.h index 3e04e11..a03f7c9 100644 --- a/chrome/browser/net/predictor_api.h +++ b/chrome/browser/net/predictor_api.h @@ -81,15 +81,14 @@ void SavePredictorStateForNextStartupAndTrim(PrefService* prefs); // Helper class to handle global init and shutdown. class PredictorInit { public: - // Too many concurrent lookups negate benefits of prefetching by trashing - // the OS cache before all resource loading is complete. - // This is the default. - static const size_t kMaxPrefetchConcurrentLookups; + // Too many concurrent lookups performed in parallel may overload a resolver, + // or may cause problems for a local router. The following limits that count. + static const size_t kMaxSpeculativeParallelResolves; // When prefetch requests are queued beyond some period of time, then the // system is congested, and we need to clear all queued requests to get out // of that state. The following is the suggested default time limit. - static const int kMaxPrefetchQueueingDelayMs; + static const int kMaxSpeculativeResolveQueueDelayMs; PredictorInit(PrefService* user_prefs, PrefService* local_state, bool preconnect_enabled); diff --git a/chrome/browser/net/predictor_unittest.cc b/chrome/browser/net/predictor_unittest.cc index a0a0113..bbc798e 100644 --- a/chrome/browser/net/predictor_unittest.cc +++ b/chrome/browser/net/predictor_unittest.cc @@ -65,7 +65,7 @@ class PredictorTest : public testing::Test { : io_thread_(BrowserThread::IO, &loop_), host_resolver_(new net::MockCachingHostResolver()), default_max_queueing_delay_(TimeDelta::FromMilliseconds( - PredictorInit::kMaxPrefetchQueueingDelayMs)) { + PredictorInit::kMaxSpeculativeResolveQueueDelayMs)) { } protected: @@ -109,25 +109,25 @@ class PredictorTest : public testing::Test { //------------------------------------------------------------------------------ TEST_F(PredictorTest, StartupShutdownTest) { - scoped_refptr<Predictor> testing_master = + scoped_refptr<Predictor> testing_master( new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, - false); + PredictorInit::kMaxSpeculativeParallelResolves, + false)); testing_master->Shutdown(); } TEST_F(PredictorTest, ShutdownWhenResolutionIsPendingTest) { - scoped_refptr<net::WaitingHostResolverProc> resolver_proc = - new net::WaitingHostResolverProc(NULL); + scoped_refptr<net::WaitingHostResolverProc> resolver_proc( + new net::WaitingHostResolverProc(NULL)); host_resolver_->Reset(resolver_proc); - scoped_refptr<Predictor> testing_master = + scoped_refptr<Predictor> testing_master( new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, - false); + PredictorInit::kMaxSpeculativeParallelResolves, + false)); GURL localhost("http://localhost:80"); UrlList names; @@ -149,11 +149,11 @@ TEST_F(PredictorTest, ShutdownWhenResolutionIsPendingTest) { } TEST_F(PredictorTest, SingleLookupTest) { - scoped_refptr<Predictor> testing_master = + scoped_refptr<Predictor> testing_master( new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, - false); + PredictorInit::kMaxSpeculativeParallelResolves, + false)); GURL goog("http://www.google.com:80"); @@ -181,11 +181,11 @@ TEST_F(PredictorTest, SingleLookupTest) { TEST_F(PredictorTest, ConcurrentLookupTest) { host_resolver_->rules()->AddSimulatedFailure("*.notfound"); - scoped_refptr<Predictor> testing_master = + scoped_refptr<Predictor> testing_master( new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, - false); + PredictorInit::kMaxSpeculativeParallelResolves, + false)); GURL goog("http://www.google.com:80"), goog2("http://gmail.google.com.com:80"), @@ -221,7 +221,6 @@ TEST_F(PredictorTest, ConcurrentLookupTest) { EXPECT_FALSE(testing_master->WasFound(bad1)); EXPECT_FALSE(testing_master->WasFound(bad2)); - EXPECT_GT(testing_master->peak_pending_lookups(), names.size() / 2); EXPECT_LE(testing_master->peak_pending_lookups(), names.size()); EXPECT_LE(testing_master->peak_pending_lookups(), testing_master->max_concurrent_dns_lookups()); @@ -232,11 +231,11 @@ TEST_F(PredictorTest, ConcurrentLookupTest) { TEST_F(PredictorTest, MassiveConcurrentLookupTest) { host_resolver_->rules()->AddSimulatedFailure("*.notfound"); - scoped_refptr<Predictor> testing_master = + scoped_refptr<Predictor> testing_master( new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, - false); + PredictorInit::kMaxSpeculativeParallelResolves, + false)); UrlList names; for (int i = 0; i < 100; i++) @@ -352,11 +351,11 @@ static bool GetDataFromSerialization(const GURL& motivation, // Make sure nil referral lists really have no entries, and no latency listed. TEST_F(PredictorTest, ReferrerSerializationNilTest) { - scoped_refptr<Predictor> predictor = + scoped_refptr<Predictor> predictor( new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, - false); + PredictorInit::kMaxSpeculativeParallelResolves, + false)); scoped_ptr<ListValue> referral_list(NewEmptySerializationList()); predictor->SerializeReferrers(referral_list.get()); EXPECT_EQ(1U, referral_list->GetSize()); @@ -371,11 +370,11 @@ TEST_F(PredictorTest, ReferrerSerializationNilTest) { // deserialized into the database, and can be extracted back out via // serialization without being changed. TEST_F(PredictorTest, ReferrerSerializationSingleReferrerTest) { - scoped_refptr<Predictor> predictor = + scoped_refptr<Predictor> predictor( new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, - false); + PredictorInit::kMaxSpeculativeParallelResolves, + false)); const GURL motivation_url("http://www.google.com:91"); const GURL subresource_url("http://icons.google.com:90"); const double kUseRate = 23.4; @@ -399,11 +398,11 @@ TEST_F(PredictorTest, ReferrerSerializationSingleReferrerTest) { // Make sure the Trim() functionality works as expected. TEST_F(PredictorTest, ReferrerSerializationTrimTest) { - scoped_refptr<Predictor> predictor = + scoped_refptr<Predictor> predictor( new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, - false); + PredictorInit::kMaxSpeculativeParallelResolves, + false)); GURL motivation_url("http://www.google.com:110"); GURL icon_subresource_url("http://icons.google.com:111"); diff --git a/chrome/browser/net/url_info.cc b/chrome/browser/net/url_info.cc index c383359..505425a 100644 --- a/chrome/browser/net/url_info.cc +++ b/chrome/browser/net/url_info.cc @@ -183,11 +183,9 @@ bool UrlInfo::IsStillCached() const { void UrlInfo::DLogResultsStats(const char* message) const { if (!detailed_logging_enabled) return; - DLOG(INFO) << "\t" << message << "\tq=" - << queue_duration().InMilliseconds() << "ms,\tr=" - << resolve_duration().InMilliseconds() << "ms\tp=" - << sequence_number_ - << "\t" << url_.spec(); + DVLOG(1) << "\t" << message << "\tq=" << queue_duration().InMilliseconds() + << "ms,\tr=" << resolve_duration().InMilliseconds() + << "ms,\tp=" << sequence_number_ << "\t" << url_.spec(); } //------------------------------------------------------------------------------ diff --git a/chrome/browser/net/url_request_mock_http_job.cc b/chrome/browser/net/url_request_mock_http_job.cc index a35cd1e..4029605 100644 --- a/chrome/browser/net/url_request_mock_http_job.cc +++ b/chrome/browser/net/url_request_mock_http_job.cc @@ -7,6 +7,7 @@ #include "base/file_util.h" #include "base/message_loop.h" #include "base/string_util.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "chrome/common/url_constants.h" #include "net/base/net_util.h" @@ -87,6 +88,10 @@ bool URLRequestMockHTTPJob::IsRedirectResponse(GURL* location, // Private const version. void URLRequestMockHTTPJob::GetResponseInfoConst( net::HttpResponseInfo* info) const { + // We have to load our headers from disk, but we only use this class + // from tests, so allow these IO operations to happen on any thread. + base::ThreadRestrictions::ScopedAllowIO allow_io; + FilePath header_file = FilePath(file_path_.value() + kMockHeaderFileSuffix); std::string raw_headers; if (!file_util::ReadFileToString(header_file, &raw_headers)) diff --git a/chrome/browser/net/url_request_mock_util.cc b/chrome/browser/net/url_request_mock_util.cc index e6531f8..b3ac398 100644 --- a/chrome/browser/net/url_request_mock_util.cc +++ b/chrome/browser/net/url_request_mock_util.cc @@ -7,6 +7,7 @@ #include <string> #include "base/path_service.h" +#include "base/thread_restrictions.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/net/url_request_failed_dns_job.h" #include "chrome/browser/net/url_request_mock_http_job.h" @@ -24,6 +25,11 @@ void SetUrlRequestMocksEnabled(bool enabled) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (enabled) { + // We have to look around for our helper files, but we only use + // this from tests, so allow these IO operations to happen + // anywhere. + base::ThreadRestrictions::ScopedAllowIO allow_io; + URLRequestFilter::GetInstance()->ClearHandlers(); URLRequestFailedDnsJob::AddUrlHandler(); diff --git a/chrome/browser/net/websocket_experiment/websocket_experiment_runner.cc b/chrome/browser/net/websocket_experiment/websocket_experiment_runner.cc index 18f31df..fd5088b 100644 --- a/chrome/browser/net/websocket_experiment/websocket_experiment_runner.cc +++ b/chrome/browser/net/websocket_experiment/websocket_experiment_runner.cc @@ -28,8 +28,8 @@ static scoped_refptr<WebSocketExperimentRunner> runner; void WebSocketExperimentRunner::Start() { DCHECK(!runner.get()); - scoped_refptr<base::FieldTrial> trial = - new base::FieldTrial("WebSocketExperiment", 1000); + scoped_refptr<base::FieldTrial> trial( + new base::FieldTrial("WebSocketExperiment", 1000)); trial->AppendGroup("active", 5); // 0.5% in active group. bool run_experiment = @@ -222,7 +222,7 @@ void WebSocketExperimentRunner::OnTaskCompleted(int result) { if (next_state_ == STATE_NONE) { task_.reset(); // Task is Canceled. - DLOG(INFO) << "WebSocketExperiment Task is canceled."; + DVLOG(1) << "WebSocketExperiment Task is canceled."; Release(); return; } diff --git a/chrome/browser/net/websocket_experiment/websocket_experiment_task.cc b/chrome/browser/net/websocket_experiment/websocket_experiment_task.cc index 6279615..05b2315 100644 --- a/chrome/browser/net/websocket_experiment/websocket_experiment_task.cc +++ b/chrome/browser/net/websocket_experiment/websocket_experiment_task.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -235,8 +235,8 @@ void WebSocketExperimentTask::ReleaseHistogram() { } void WebSocketExperimentTask::Run() { - DLOG(INFO) << "Run WebSocket experiment for " << config_.url - << " " << GetProtocolVersionName(config_.protocol_version); + DVLOG(1) << "Run WebSocket experiment for " << config_.url << " " + << GetProtocolVersionName(config_.protocol_version); next_state_ = STATE_URL_FETCH; DoLoop(net::OK); } @@ -247,8 +247,8 @@ void WebSocketExperimentTask::Cancel() { } void WebSocketExperimentTask::SaveResult() const { - DLOG(INFO) << "WebSocket experiment save result for " << config_.url - << " last_state=" << result_.last_state; + DVLOG(1) << "WebSocket experiment save result for " << config_.url + << " last_state=" << result_.last_state; UpdateHistogramEnums(config_, "LastState", result_.last_state, NUM_STATES); UpdateHistogramTimes(config_, "UrlFetch", result_.url_fetch, base::TimeDelta::FromMilliseconds(1), @@ -303,8 +303,8 @@ void WebSocketExperimentTask::OnURLFetchComplete( RevokeTimeoutTimer(); int result = net::ERR_FAILED; if (next_state_ != STATE_URL_FETCH_COMPLETE) { - DLOG(INFO) << "unexpected state=" << next_state_ - << " at OnURLFetchComplete for " << config_.http_url; + DVLOG(1) << "unexpected state=" << next_state_ + << " at OnURLFetchComplete for " << config_.http_url; result = net::ERR_UNEXPECTED; } else if (response_code == 200 || response_code == 304) { result = net::OK; @@ -321,9 +321,9 @@ void WebSocketExperimentTask::OnOpen(net::WebSocket* websocket) { if (next_state_ == STATE_WEBSOCKET_CONNECT_COMPLETE) result = net::OK; else - DLOG(INFO) << "unexpected state=" << next_state_ - << " at OnOpen for " << config_.url - << " " << GetProtocolVersionName(config_.protocol_version); + DVLOG(1) << "unexpected state=" << next_state_ + << " at OnOpen for " << config_.url + << " " << GetProtocolVersionName(config_.protocol_version); DoLoop(result); } @@ -346,9 +346,9 @@ void WebSocketExperimentTask::OnMessage( result = net::OK; break; default: - DLOG(INFO) << "unexpected state=" << next_state_ - << " at OnMessage for " << config_.url - << " " << GetProtocolVersionName(config_.protocol_version); + DVLOG(1) << "unexpected state=" << next_state_ + << " at OnMessage for " << config_.url + << " " << GetProtocolVersionName(config_.protocol_version); break; } DoLoop(result); @@ -367,9 +367,9 @@ void WebSocketExperimentTask::OnClose( int result = net::ERR_CONNECTION_CLOSED; if (last_websocket_error_ != net::OK) result = last_websocket_error_; - DLOG(INFO) << "WebSocket onclose was_clean=" << was_clean - << " next_state=" << next_state_ - << " last_error=" << net::ErrorToString(result); + DVLOG(1) << "WebSocket onclose was_clean=" << was_clean + << " next_state=" << next_state_ + << " last_error=" << net::ErrorToString(result); if (config_.protocol_version == net::WebSocket::DEFAULT_VERSION) { if (next_state_ == STATE_WEBSOCKET_CLOSE_COMPLETE && was_clean) result = net::OK; @@ -383,10 +383,10 @@ void WebSocketExperimentTask::OnClose( void WebSocketExperimentTask::OnSocketError( const net::WebSocket* websocket, int error) { - DLOG(INFO) << "WebSocket socket level error=" << net::ErrorToString(error) - << " next_state=" << next_state_ - << " for " << config_.url - << " " << GetProtocolVersionName(config_.protocol_version); + DVLOG(1) << "WebSocket socket level error=" << net::ErrorToString(error) + << " next_state=" << next_state_ + << " for " << config_.url + << " " << GetProtocolVersionName(config_.protocol_version); last_websocket_error_ = error; } @@ -395,9 +395,9 @@ void WebSocketExperimentTask::SetContext(Context* context) { } void WebSocketExperimentTask::OnTimedOut() { - DLOG(INFO) << "OnTimedOut next_state=" << next_state_ - << " for " << config_.url - << " " << GetProtocolVersionName(config_.protocol_version); + DVLOG(1) << "OnTimedOut next_state=" << next_state_ + << " for " << config_.url + << " " << GetProtocolVersionName(config_.protocol_version); RevokeTimeoutTimer(); DoLoop(net::ERR_TIMED_OUT); } @@ -644,10 +644,10 @@ void WebSocketExperimentTask::Finish(int result) { websocket_ = NULL; if (websocket) websocket->DetachDelegate(); - DLOG(INFO) << "Finish WebSocket experiment for " << config_.url - << " " << GetProtocolVersionName(config_.protocol_version) - << " next_state=" << next_state_ - << " result=" << net::ErrorToString(result); + DVLOG(1) << "Finish WebSocket experiment for " << config_.url + << " " << GetProtocolVersionName(config_.protocol_version) + << " next_state=" << next_state_ + << " result=" << net::ErrorToString(result); callback_->Run(result); // will release this. } diff --git a/chrome/browser/notifications/balloon_collection.cc b/chrome/browser/notifications/balloon_collection.cc index ebbe566..a125837 100644 --- a/chrome/browser/notifications/balloon_collection.cc +++ b/chrome/browser/notifications/balloon_collection.cc @@ -68,6 +68,10 @@ void BalloonCollectionImpl::Add(const Notification& notification, // There may be no listener in a unit test. if (space_change_listener_) space_change_listener_->OnBalloonSpaceChanged(); + + // This is used only for testing. + if (on_collection_changed_callback_.get()) + on_collection_changed_callback_->Run(); } bool BalloonCollectionImpl::Remove(const Notification& notification) { @@ -148,6 +152,10 @@ void BalloonCollectionImpl::OnBalloonClosed(Balloon* source) { // There may be no listener in a unit test. if (space_change_listener_) space_change_listener_->OnBalloonSpaceChanged(); + + // This is used only for testing. + if (on_collection_changed_callback_.get()) + on_collection_changed_callback_->Run(); } void BalloonCollectionImpl::PositionBalloonsInternal(bool reposition) { diff --git a/chrome/browser/notifications/balloon_collection.h b/chrome/browser/notifications/balloon_collection.h index 7a7b081..499b937 100644 --- a/chrome/browser/notifications/balloon_collection.h +++ b/chrome/browser/notifications/balloon_collection.h @@ -10,6 +10,9 @@ #include <deque> +#include "base/callback.h" +#include "base/scoped_ptr.h" + class Balloon; class Notification; class Profile; @@ -68,9 +71,17 @@ class BalloonCollection { space_change_listener_ = listener; } + void set_on_collection_changed_callback(Callback0::Type* callback) { + on_collection_changed_callback_.reset(callback); + } + protected: // Non-owned pointer to an object listening for space changes. BalloonSpaceChangeListener* space_change_listener_; + + // For use only with testing. This callback is invoked when a balloon + // is added or removed from the collection. + scoped_ptr<Callback0::Type> on_collection_changed_callback_; }; #endif // CHROME_BROWSER_NOTIFICATIONS_BALLOON_COLLECTION_H_ diff --git a/chrome/browser/notifications/balloon_collection_impl.h b/chrome/browser/notifications/balloon_collection_impl.h index dc9975e..a9c7afc 100644 --- a/chrome/browser/notifications/balloon_collection_impl.h +++ b/chrome/browser/notifications/balloon_collection_impl.h @@ -122,7 +122,7 @@ class BalloonCollectionImpl : public BalloonCollection static const int kBalloonMinWidth = 300; static const int kBalloonMaxWidth = 300; static const int kBalloonMinHeight = 24; - static const int kBalloonMaxHeight = 120; + static const int kBalloonMaxHeight = 160; static Placement placement_; gfx::Rect work_area_; diff --git a/chrome/browser/notifications/balloon_host.cc b/chrome/browser/notifications/balloon_host.cc index f47d395..04f0b89 100644 --- a/chrome/browser/notifications/balloon_host.cc +++ b/chrome/browser/notifications/balloon_host.cc @@ -7,6 +7,7 @@ #include "chrome/browser/browser_list.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/notifications/balloon.h" +#include "chrome/browser/notifications/notification.h" #include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/site_instance.h" @@ -17,6 +18,7 @@ #include "chrome/common/render_messages.h" #include "chrome/common/renderer_preferences.h" #include "chrome/common/url_constants.h" +#include "webkit/glue/webpreferences.h" BalloonHost::BalloonHost(Balloon* balloon) : render_view_host_(NULL), @@ -40,6 +42,7 @@ BalloonHost::BalloonHost(Balloon* balloon) } void BalloonHost::Shutdown() { + NotifyDisconnect(); if (render_view_host_) { render_view_host_->Shutdown(); render_view_host_ = NULL; @@ -58,6 +61,10 @@ gfx::NativeView BalloonHost::GetNativeViewOfHost() { TabContents* BalloonHost::associated_tab_contents() const { return NULL; } +const string16& BalloonHost::GetSource() const { + return balloon_->notification().display_source(); +} + WebPreferences BalloonHost::GetWebkitPrefs() { WebPreferences prefs; prefs.allow_scripts_to_close_windows = true; diff --git a/chrome/browser/notifications/balloon_host.h b/chrome/browser/notifications/balloon_host.h index 1c8b088..4e51eba 100644 --- a/chrome/browser/notifications/balloon_host.h +++ b/chrome/browser/notifications/balloon_host.h @@ -7,19 +7,18 @@ #pragma once #include <string> +#include <vector> #include "chrome/browser/extensions/extension_function_dispatcher.h" -#include "chrome/browser/notifications/balloon.h" -#include "chrome/browser/notifications/notification.h" #include "chrome/browser/renderer_host/render_view_host_delegate.h" -#include "chrome/browser/renderer_host/site_instance.h" #include "chrome/browser/tab_contents/render_view_host_delegate_helper.h" -#include "chrome/common/extensions/extension_constants.h" -#include "chrome/common/renderer_preferences.h" -#include "webkit/glue/webpreferences.h" +class Balloon; class Browser; class Profile; +class SiteInstance; +struct RendererPreferences; +struct WebPreferences; class BalloonHost : public RenderViewHostDelegate, public RenderViewHostDelegate::View, @@ -40,9 +39,7 @@ class BalloonHost : public RenderViewHostDelegate, RenderViewHost* render_view_host() const { return render_view_host_; } - const string16& GetSource() const { - return balloon_->notification().display_source(); - } + const string16& GetSource() const; // RenderViewHostDelegate overrides. virtual WebPreferences GetWebkitPrefs(); @@ -77,6 +74,12 @@ class BalloonHost : public RenderViewHostDelegate, const gfx::Rect& initial_pos) {} virtual void ShowCreatedFullscreenWidget(int route_id) {} virtual void ShowContextMenu(const ContextMenuParams& params) {} + virtual void ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned) {} virtual void StartDragging(const WebDropData& drop_data, WebKit::WebDragOperationsMask allowed_ops) {} virtual void StartDragging(const WebDropData&, diff --git a/chrome/browser/notifications/desktop_notification_service.cc b/chrome/browser/notifications/desktop_notification_service.cc index a4c45d5..e7530e4 100644 --- a/chrome/browser/notifications/desktop_notification_service.cc +++ b/chrome/browser/notifications/desktop_notification_service.cc @@ -600,7 +600,7 @@ string16 DesktopNotificationService::DisplayNameForOrigin( if (origin.SchemeIs(chrome::kExtensionScheme)) { ExtensionsService* ext_service = profile_->GetExtensionsService(); if (ext_service) { - Extension* extension = ext_service->GetExtensionByURL(origin); + const Extension* extension = ext_service->GetExtensionByURL(origin); if (extension) return UTF8ToUTF16(extension->name()); } diff --git a/chrome/browser/notifications/desktop_notification_service.h b/chrome/browser/notifications/desktop_notification_service.h index a6815da..d92d7e6 100644 --- a/chrome/browser/notifications/desktop_notification_service.h +++ b/chrome/browser/notifications/desktop_notification_service.h @@ -114,6 +114,8 @@ class DesktopNotificationService : public NotificationObserver { static void RegisterUserPrefs(PrefService* user_prefs); + ContentSetting GetContentSetting(const GURL& origin); + private: void InitPrefs(); void StartObserving(); @@ -130,8 +132,6 @@ class DesktopNotificationService : public NotificationObserver { // itself when dealing with extensions. string16 DisplayNameForOrigin(const GURL& origin); - ContentSetting GetContentSetting(const GURL& origin); - // The profile which owns this object. Profile* profile_; diff --git a/chrome/browser/notifications/desktop_notification_service_unittest.cc b/chrome/browser/notifications/desktop_notification_service_unittest.cc index 9f0067c..3c1c8ca 100644 --- a/chrome/browser/notifications/desktop_notification_service_unittest.cc +++ b/chrome/browser/notifications/desktop_notification_service_unittest.cc @@ -42,7 +42,7 @@ class ThreadProxy : public base::RefCountedThreadSafe<ThreadProxy> { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, NewRunnableMethod(this, &ThreadProxy::CacheHasPermissionIO, - cache, url)); + make_scoped_refptr(cache), url)); io_event_.Signal(); ui_event_.Wait(); // Wait for IO thread to be done. BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, diff --git a/chrome/browser/notifications/notification_options_menu_model.cc b/chrome/browser/notifications/notification_options_menu_model.cc index 209a578..97d269c 100644 --- a/chrome/browser/notifications/notification_options_menu_model.cc +++ b/chrome/browser/notifications/notification_options_menu_model.cc @@ -10,6 +10,7 @@ #include "chrome/browser/browser_list.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/notifications/desktop_notification_service.h" +#include "chrome/browser/notifications/notifications_prefs_cache.h" #include "chrome/browser/profile.h" #include "chrome/common/content_settings_types.h" #include "chrome/common/extensions/extension.h" @@ -17,8 +18,8 @@ #include "grit/generated_resources.h" // Menu commands -const int kRevokePermissionCommand = 0; -const int kDisableExtensionCommand = 1; +const int kTogglePermissionCommand = 0; +const int kToggleExtensionCommand = 1; const int kOpenContentSettingsCommand = 2; NotificationOptionsMenuModel::NotificationOptionsMenuModel(Balloon* balloon) @@ -31,12 +32,12 @@ NotificationOptionsMenuModel::NotificationOptionsMenuModel(Balloon* balloon) if (origin.SchemeIs(chrome::kExtensionScheme)) { const string16 disable_label = l10n_util::GetStringUTF16( IDS_EXTENSIONS_DISABLE); - AddItem(kDisableExtensionCommand, disable_label); + AddItem(kToggleExtensionCommand, disable_label); } else { const string16 disable_label = l10n_util::GetStringFUTF16( IDS_NOTIFICATION_BALLOON_REVOKE_MESSAGE, notification.display_source()); - AddItem(kRevokePermissionCommand, disable_label); + AddItem(kTogglePermissionCommand, disable_label); } const string16 settings_label = l10n_util::GetStringUTF16( @@ -47,6 +48,52 @@ NotificationOptionsMenuModel::NotificationOptionsMenuModel(Balloon* balloon) NotificationOptionsMenuModel::~NotificationOptionsMenuModel() { } +bool NotificationOptionsMenuModel::IsLabelForCommandIdDynamic(int command_id) + const { + return command_id == kTogglePermissionCommand || + command_id == kToggleExtensionCommand; +} + +string16 NotificationOptionsMenuModel::GetLabelForCommandId(int command_id) + const { + // TODO(tfarina,johnnyg): Removed this code if we decide to close + // notifications after permissions are revoked. + if (command_id == kTogglePermissionCommand || + command_id == kToggleExtensionCommand) { + const Notification& notification = balloon_->notification(); + const GURL& origin = notification.origin_url(); + + DesktopNotificationService* service = + balloon_->profile()->GetDesktopNotificationService(); + if (origin.SchemeIs(chrome::kExtensionScheme)) { + ExtensionsService* ext_service = + balloon_->profile()->GetExtensionsService(); + const Extension* extension = ext_service->GetExtensionByURL(origin); + if (extension) { + ExtensionPrefs* extension_prefs = ext_service->extension_prefs(); + const std::string& id = extension->id(); + if (extension_prefs->GetExtensionState(id) == Extension::ENABLED) + return l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLE); + else + return l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE); + } + } else { + if (service->GetContentSetting(origin) == CONTENT_SETTING_ALLOW) { + return l10n_util::GetStringFUTF16( + IDS_NOTIFICATION_BALLOON_REVOKE_MESSAGE, + notification.display_source()); + } else { + return l10n_util::GetStringFUTF16( + IDS_NOTIFICATION_BALLOON_ENABLE_MESSAGE, + notification.display_source()); + } + } + } else if (command_id == kOpenContentSettingsCommand) { + return l10n_util::GetStringUTF16(IDS_NOTIFICATIONS_SETTINGS_BUTTON); + } + return string16(); +} + bool NotificationOptionsMenuModel::IsCommandIdChecked(int /* command_id */) const { // Nothing in the menu is checked. @@ -72,13 +119,22 @@ void NotificationOptionsMenuModel::ExecuteCommand(int command_id) { balloon_->profile()->GetExtensionsService(); const GURL& origin = balloon_->notification().origin_url(); switch (command_id) { - case kRevokePermissionCommand: - service->DenyPermission(origin); + case kTogglePermissionCommand: + if (service->GetContentSetting(origin) == CONTENT_SETTING_ALLOW) + service->DenyPermission(origin); + else + service->GrantPermission(origin); break; - case kDisableExtensionCommand: { - Extension* extension = ext_service->GetExtensionByURL(origin); - if (extension) - ext_service->DisableExtension(extension->id()); + case kToggleExtensionCommand: { + const Extension* extension = ext_service->GetExtensionByURL(origin); + if (extension) { + ExtensionPrefs* extension_prefs = ext_service->extension_prefs(); + const std::string& id = extension->id(); + if (extension_prefs->GetExtensionState(id) == Extension::ENABLED) + ext_service->DisableExtension(id); + else + ext_service->EnableExtension(id); + } break; } case kOpenContentSettingsCommand: { diff --git a/chrome/browser/notifications/notification_options_menu_model.h b/chrome/browser/notifications/notification_options_menu_model.h index 2044da5..b276e11 100644 --- a/chrome/browser/notifications/notification_options_menu_model.h +++ b/chrome/browser/notifications/notification_options_menu_model.h @@ -15,6 +15,10 @@ class NotificationOptionsMenuModel : public menus::SimpleMenuModel, explicit NotificationOptionsMenuModel(Balloon* balloon); virtual ~NotificationOptionsMenuModel(); + // Overridden from menus::SimpleMenuModel: + virtual bool IsLabelForCommandIdDynamic(int command_id) const; + virtual string16 GetLabelForCommandId(int command_id) const; + // Overridden from menus::SimpleMenuModel::Delegate: virtual bool IsCommandIdChecked(int command_id) const; virtual bool IsCommandIdEnabled(int command_id) const; diff --git a/chrome/browser/notifications/notification_test_util.h b/chrome/browser/notifications/notification_test_util.h index b2f0c34..4cbf600 100644 --- a/chrome/browser/notifications/notification_test_util.h +++ b/chrome/browser/notifications/notification_test_util.h @@ -10,8 +10,30 @@ #include "chrome/browser/notifications/balloon.h" #include "gfx/size.h" +// NotificationDelegate which does nothing, useful for testing when +// the notification events are not important. +class MockNotificationDelegate : public NotificationDelegate { + public: + explicit MockNotificationDelegate(std::string id) : id_(id) {} + virtual ~MockNotificationDelegate() {} + + // NotificationDelegate interface. + virtual void Display() {} + virtual void Error() {} + virtual void Close(bool by_user) {} + virtual void Click() {} + virtual std::string id() const { return id_; } + + private: + std::string id_; +}; + // Mock implementation of Javascript object proxy which logs events that -// would have been fired on it. |Logger| class must static "log()" method. +// would have been fired on it. Useful for tests where the sequence of +// notification events needs to be verified. +// +// |Logger| class provided in template must implement method +// static void log(string); template<class Logger> class LoggingNotificationProxyBase : public NotificationObjectProxy { public: diff --git a/chrome/browser/notifications/notifications_interactive_uitest.cc b/chrome/browser/notifications/notifications_interactive_uitest.cc index afd44d7..43d905f 100644 --- a/chrome/browser/notifications/notifications_interactive_uitest.cc +++ b/chrome/browser/notifications/notifications_interactive_uitest.cc @@ -17,7 +17,8 @@ class NotificationsPermissionTest : public UITest { } }; -TEST_F(NotificationsPermissionTest, TestUserGestureInfobar) { +// Flaky, http://crbug.com/62311. +TEST_F(NotificationsPermissionTest, FLAKY_TestUserGestureInfobar) { net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(FILE_PATH_LITERAL("chrome/test/data"))); ASSERT_TRUE(test_server.Start()); @@ -43,7 +44,8 @@ TEST_F(NotificationsPermissionTest, TestUserGestureInfobar) { EXPECT_TRUE(tab->WaitForInfoBarCount(1)); } -TEST_F(NotificationsPermissionTest, TestNoUserGestureInfobar) { +// Flaky, http://crbug.com/62311. +TEST_F(NotificationsPermissionTest, FLAKY_TestNoUserGestureInfobar) { net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(FILE_PATH_LITERAL("chrome/test/data"))); ASSERT_TRUE(test_server.Start()); diff --git a/chrome/browser/omnibox_search_hint.cc b/chrome/browser/omnibox_search_hint.cc index e1ab84c..42cbe63 100644 --- a/chrome/browser/omnibox_search_hint.cc +++ b/chrome/browser/omnibox_search_hint.cc @@ -9,8 +9,10 @@ #include "base/command_line.h" #include "base/metrics/histogram.h" #include "base/task.h" +#include "chrome/browser/autocomplete/autocomplete.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/autocomplete/autocomplete_edit_view.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/location_bar.h" diff --git a/chrome/browser/options_util.cc b/chrome/browser/options_util.cc index caf0ee0..56d0c07 100644 --- a/chrome/browser/options_util.cc +++ b/chrome/browser/options_util.cc @@ -30,6 +30,7 @@ void OptionsUtil::ResetToDefaults(Profile* profile) { prefs::kClearSiteDataOnExit, prefs::kCookieBehavior, prefs::kDefaultCharset, + prefs::kDefaultZoomLevel, prefs::kDnsPrefetchingEnabled, #if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_OPENBSD) prefs::kCertRevocationCheckingEnabled, diff --git a/chrome/browser/page_info_model.cc b/chrome/browser/page_info_model.cc index 02b1fbc..9e821d4 100644 --- a/chrome/browser/page_info_model.cc +++ b/chrome/browser/page_info_model.cc @@ -150,7 +150,6 @@ PageInfoModel::PageInfoModel(Profile* profile, } sections_.push_back(SectionInfo( icon_id, - l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_IDENTITY_TITLE), headline, description, SECTION_INFO_IDENTITY)); @@ -196,6 +195,15 @@ PageInfoModel::PageInfoModel(Profile* profile, uint16 cipher_suite = net::SSLConnectionStatusToCipherSuite(ssl.connection_status()); if (ssl.security_bits() > 0 && cipher_suite) { + int ssl_version = + net::SSLConnectionStatusToVersion(ssl.connection_status()); + const char* ssl_version_str; + net::SSLVersionToString(&ssl_version_str, ssl_version); + description += ASCIIToUTF16("\n\n"); + description += l10n_util::GetStringFUTF16( + IDS_PAGE_INFO_SECURITY_TAB_SSL_VERSION, + ASCIIToUTF16(ssl_version_str)); + bool did_fallback = (ssl.connection_status() & net::SSL_CONNECTION_SSL3_FALLBACK) != 0; bool no_renegotiation = @@ -213,7 +221,7 @@ PageInfoModel::PageInfoModel(Profile* profile, uint8 compression_id = net::SSLConnectionStatusToCompression(ssl.connection_status()); if (compression_id) { - const char *compression; + const char* compression; net::SSLCompressionToString(&compression, compression_id); description += l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_COMPRESSION_DETAILS, @@ -240,7 +248,6 @@ PageInfoModel::PageInfoModel(Profile* profile, if (!description.empty()) { sections_.push_back(SectionInfo( icon_id, - l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_CONNECTION_TITLE), headline, description, SECTION_INFO_CONNECTION)); @@ -299,23 +306,19 @@ void PageInfoModel::OnGotVisitCountToHost(HistoryService::Handle handle, visited_before_today = (first_visit_midnight < today); } - string16 title = l10n_util::GetStringUTF16(IDS_PAGE_INFO_SITE_INFO_TITLE); + string16 headline = l10n_util::GetStringUTF16(IDS_PAGE_INFO_SITE_INFO_TITLE); if (!visited_before_today) { sections_.push_back(SectionInfo( ICON_STATE_WARNING_MAJOR, - l10n_util::GetStringUTF16( - IDS_PAGE_INFO_SECURITY_TAB_PERSONAL_HISTORY_TITLE), - title, + headline, l10n_util::GetStringUTF16( IDS_PAGE_INFO_SECURITY_TAB_FIRST_VISITED_TODAY), SECTION_INFO_FIRST_VISIT)); } else { sections_.push_back(SectionInfo( ICON_STATE_INFO, - l10n_util::GetStringUTF16( - IDS_PAGE_INFO_SECURITY_TAB_PERSONAL_HISTORY_TITLE), - title, + headline, l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_VISITED_BEFORE_TODAY, WideToUTF16(base::TimeFormatShortDate(first_visit))), @@ -324,11 +327,6 @@ void PageInfoModel::OnGotVisitCountToHost(HistoryService::Handle handle, observer_->ModelChanged(); } -// static -void PageInfoModel::RegisterPrefs(PrefService* prefs) { - prefs->RegisterDictionaryPref(prefs::kPageInfoWindowPlacement); -} - PageInfoModel::PageInfoModel() : observer_(NULL) { Init(); } diff --git a/chrome/browser/page_info_model.h b/chrome/browser/page_info_model.h index 3e6b962..f58556d 100644 --- a/chrome/browser/page_info_model.h +++ b/chrome/browser/page_info_model.h @@ -52,12 +52,10 @@ class PageInfoModel { struct SectionInfo { SectionInfo(SectionStateIcon icon_id, - const string16& title, const string16& headline, const string16& description, SectionInfoType type) : icon_id(icon_id), - title(title), headline(headline), description(description), type(type) { @@ -66,9 +64,6 @@ class PageInfoModel { // The overall state of the connection (error, warning, ok). SectionStateIcon icon_id; - // The title of the section. - string16 title; - // A single line describing the section, optional. string16 headline; @@ -99,8 +94,6 @@ class PageInfoModel { int count, base::Time first_visit); - static void RegisterPrefs(PrefService* prefs); - protected: // Testing constructor. DO NOT USE. PageInfoModel(); diff --git a/chrome/browser/parsers/metadata_parser_filebase.cc b/chrome/browser/parsers/metadata_parser_filebase.cc index 0146f3f..123cb6f 100644 --- a/chrome/browser/parsers/metadata_parser_filebase.cc +++ b/chrome/browser/parsers/metadata_parser_filebase.cc @@ -9,10 +9,12 @@ #include "base/utf_string_conversions.h" FileMetadataParser::FileMetadataParser(const FilePath& path) - : MetadataParser(path) { - path_ = path; + : MetadataParser(path), + path_(path) { } +FileMetadataParser::~FileMetadataParser() {} + bool FileMetadataParser::Parse() { std::string value; int64 size; @@ -48,6 +50,8 @@ FileMetadataPropertyIterator::FileMetadataPropertyIterator( it = properties_.begin(); } +FileMetadataPropertyIterator::~FileMetadataPropertyIterator() {} + bool FileMetadataPropertyIterator::GetNext(std::string* key, std::string* value) { if (it == properties_.end()) { diff --git a/chrome/browser/parsers/metadata_parser_filebase.h b/chrome/browser/parsers/metadata_parser_filebase.h index 8b232a2..9ed5775 100644 --- a/chrome/browser/parsers/metadata_parser_filebase.h +++ b/chrome/browser/parsers/metadata_parser_filebase.h @@ -21,6 +21,8 @@ class FileMetadataParser : public MetadataParser { public: explicit FileMetadataParser(const FilePath& path); + virtual ~FileMetadataParser(); + // Implementation of MetadataParser virtual bool Parse(); virtual bool GetProperty(const std::string& key, std::string* value); @@ -39,6 +41,8 @@ class FileMetadataPropertyIterator : public MetadataPropertyIterator { public: explicit FileMetadataPropertyIterator(PropertyMap& properties); + virtual ~FileMetadataPropertyIterator(); + // Implementation of MetadataPropertyIterator virtual bool GetNext(std::string* key, std::string* value); virtual int Length(); diff --git a/chrome/browser/platform_util.h b/chrome/browser/platform_util.h index fb82b19..3738aa8 100644 --- a/chrome/browser/platform_util.h +++ b/chrome/browser/platform_util.h @@ -27,6 +27,9 @@ void OpenExternal(const GURL& url); // Get the top level window for the native view. This can return NULL. gfx::NativeWindow GetTopLevel(gfx::NativeView view); +// Get the direct parent of |view|, may return NULL. +gfx::NativeView GetParent(gfx::NativeView view); + // Returns true if |window| is the foreground top level window. bool IsWindowActive(gfx::NativeWindow window); diff --git a/chrome/browser/platform_util_common_linux.cc b/chrome/browser/platform_util_common_linux.cc index 1f0ea42..062a2b8 100644 --- a/chrome/browser/platform_util_common_linux.cc +++ b/chrome/browser/platform_util_common_linux.cc @@ -6,7 +6,6 @@ #include <gtk/gtk.h> -#include "app/gtk_util.h" #include "base/file_util.h" #include "base/message_loop.h" #include "base/process_util.h" @@ -65,6 +64,10 @@ gfx::NativeWindow GetTopLevel(gfx::NativeView view) { return GTK_IS_WINDOW(toplevel) ? GTK_WINDOW(toplevel) : NULL; } +gfx::NativeView GetParent(gfx::NativeView view) { + return gtk_widget_get_parent(view); +} + bool IsWindowActive(gfx::NativeWindow window) { return gtk_window_is_active(window); } diff --git a/chrome/browser/platform_util_mac.mm b/chrome/browser/platform_util_mac.mm index d7da563..719183d 100644 --- a/chrome/browser/platform_util_mac.mm +++ b/chrome/browser/platform_util_mac.mm @@ -132,6 +132,10 @@ gfx::NativeWindow GetTopLevel(gfx::NativeView view) { return [view window]; } +gfx::NativeView GetParent(gfx::NativeView view) { + return nil; +} + bool IsWindowActive(gfx::NativeWindow window) { return [window isKeyWindow] || [window isMainWindow]; } diff --git a/chrome/browser/platform_util_win.cc b/chrome/browser/platform_util_win.cc index c20c925..f27e936 100644 --- a/chrome/browser/platform_util_win.cc +++ b/chrome/browser/platform_util_win.cc @@ -137,7 +137,11 @@ void OpenExternal(const GURL& url) { } gfx::NativeWindow GetTopLevel(gfx::NativeView view) { - return GetAncestor(view, GA_ROOT); + return ::GetAncestor(view, GA_ROOT); +} + +gfx::NativeView GetParent(gfx::NativeView view) { + return ::GetParent(view); } bool IsWindowActive(gfx::NativeWindow window) { diff --git a/chrome/browser/plugin_carbon_interpose_mac.cc b/chrome/browser/plugin_carbon_interpose_mac.cc index e0e304f..55e1405 100644 --- a/chrome/browser/plugin_carbon_interpose_mac.cc +++ b/chrome/browser/plugin_carbon_interpose_mac.cc @@ -20,7 +20,7 @@ static bool IsModalWindow(WindowRef window) { return (status == noErr) && (modality != kWindowModalityNone); } -static bool IsContainingWindowActive(const WebPluginDelegateImpl* delegate) { +static bool IsContainingWindowActive(const OpaquePluginRef delegate) { return mac_plugin_interposing::GetPluginWindowHasFocus(delegate); } @@ -60,7 +60,7 @@ static void OnPluginWindowSelected(WindowRef window) { #pragma mark - static Boolean ChromePluginIsWindowActive(WindowRef window) { - const WebPluginDelegateImpl* delegate = + const OpaquePluginRef delegate = CarbonPluginWindowTracker::SharedInstance()->GetDelegateForDummyWindow( window); return delegate ? IsContainingWindowActive(delegate) @@ -68,7 +68,7 @@ static Boolean ChromePluginIsWindowActive(WindowRef window) { } static Boolean ChromePluginIsWindowHilited(WindowRef window) { - const WebPluginDelegateImpl* delegate = + const OpaquePluginRef delegate = CarbonPluginWindowTracker::SharedInstance()->GetDelegateForDummyWindow( window); return delegate ? IsContainingWindowActive(delegate) @@ -125,7 +125,7 @@ static void ChromePluginDisposeDialog(DialogRef dialog) { } static WindowPartCode ChromePluginFindWindow(Point point, WindowRef* window) { - WebPluginDelegateImpl* delegate = mac_plugin_interposing::GetActiveDelegate(); + OpaquePluginRef delegate = mac_plugin_interposing::GetActiveDelegate(); CarbonPluginWindowTracker* tracker = CarbonPluginWindowTracker::SharedInstance(); WindowRef plugin_window = tracker->GetDummyWindowForDelegate(delegate); @@ -146,7 +146,7 @@ static WindowPartCode ChromePluginFindWindow(Point point, WindowRef* window) { } static OSStatus ChromePluginSetThemeCursor(ThemeCursor cursor) { - WebPluginDelegateImpl* delegate = mac_plugin_interposing::GetActiveDelegate(); + OpaquePluginRef delegate = mac_plugin_interposing::GetActiveDelegate(); if (delegate) { mac_plugin_interposing::NotifyPluginOfSetThemeCursor(delegate, cursor); return noErr; @@ -155,7 +155,7 @@ static OSStatus ChromePluginSetThemeCursor(ThemeCursor cursor) { } static void ChromePluginSetCursor(const Cursor* cursor) { - WebPluginDelegateImpl* delegate = mac_plugin_interposing::GetActiveDelegate(); + OpaquePluginRef delegate = mac_plugin_interposing::GetActiveDelegate(); if (delegate) { mac_plugin_interposing::NotifyPluginOfSetCursor(delegate, cursor); return; diff --git a/chrome/browser/plugin_installer.cc b/chrome/browser/plugin_installer.cc index 1b1d0c5..b6ceb3c 100644 --- a/chrome/browser/plugin_installer.cc +++ b/chrome/browser/plugin_installer.cc @@ -14,6 +14,10 @@ #include "grit/theme_resources.h" #include "webkit/glue/plugins/default_plugin_shared.h" +// The URL for the "Problems installing" page for the Plugins infobar. +static const char kLearnMorePluginInstallerUrl[] = + "http://www.google.com/support/chrome/bin/answer.py?answer=95697&topic=14687"; + PluginInstaller::PluginInstaller(TabContents* tab_contents) : ConfirmInfoBarDelegate(tab_contents), tab_contents_(tab_contents) { @@ -73,8 +77,7 @@ string16 PluginInstaller::GetLinkText() { bool PluginInstaller::LinkClicked(WindowOpenDisposition disposition) { // Ignore the click dispostion and always open in a new top level tab. - tab_contents_->OpenURL( - GURL(l10n_util::GetStringUTF8(IDS_LEARN_MORE_PLUGININSTALLER_URL)), - GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); + tab_contents_->OpenURL(GURL(kLearnMorePluginInstallerUrl), GURL(), + NEW_FOREGROUND_TAB, PageTransition::LINK); return false; // Do not dismiss the info bar. } diff --git a/chrome/browser/plugin_process_host.cc b/chrome/browser/plugin_process_host.cc index 4b5f5a1..8e459ee 100644 --- a/chrome/browser/plugin_process_host.cc +++ b/chrome/browser/plugin_process_host.cc @@ -144,6 +144,8 @@ PluginProcessHost::~PluginProcessHost() { } } #endif + // Cancel all pending and sent requests. + CancelRequests(); } bool PluginProcessHost::Init(const WebPluginInfo& info, @@ -307,49 +309,40 @@ void PluginProcessHost::OnMessageReceived(const IPC::Message& msg) { void PluginProcessHost::OnChannelConnected(int32 peer_pid) { for (size_t i = 0; i < pending_requests_.size(); ++i) { - RequestPluginChannel(pending_requests_[i].renderer_message_filter_.get(), - pending_requests_[i].mime_type, - pending_requests_[i].reply_msg); + RequestPluginChannel(pending_requests_[i]); } pending_requests_.clear(); } void PluginProcessHost::OnChannelError() { - for (size_t i = 0; i < pending_requests_.size(); ++i) { - ReplyToRenderer(pending_requests_[i].renderer_message_filter_.get(), - IPC::ChannelHandle(), - info_, - pending_requests_[i].reply_msg); - } + CancelRequests(); +} +void PluginProcessHost::CancelRequests() { + for (size_t i = 0; i < pending_requests_.size(); ++i) + pending_requests_[i]->OnError(); pending_requests_.clear(); while (!sent_requests_.empty()) { - ReplyToRenderer(sent_requests_.front().renderer_message_filter_.get(), - IPC::ChannelHandle(), - info_, - sent_requests_.front().reply_msg); + sent_requests_.front()->OnError(); sent_requests_.pop(); } } -void PluginProcessHost::OpenChannelToPlugin( - ResourceMessageFilter* renderer_message_filter, - const std::string& mime_type, - IPC::Message* reply_msg) { +void PluginProcessHost::OpenChannelToPlugin(Client* client) { InstanceCreated(); + client->SetPluginInfo(info_); if (opening_channel()) { // The channel is already in the process of being opened. Put // this "open channel" request into a queue of requests that will // be run once the channel is open. - pending_requests_.push_back( - ChannelRequest(renderer_message_filter, mime_type, reply_msg)); + pending_requests_.push_back(client); return; } // We already have an open channel, send a request right away to plugin. - RequestPluginChannel(renderer_message_filter, mime_type, reply_msg); + RequestPluginChannel(client); } void PluginProcessHost::OnGetCookies(uint32 request_context, @@ -402,52 +395,34 @@ void PluginProcessHost::OnResolveProxyCompleted(IPC::Message* reply_msg, Send(reply_msg); } -void PluginProcessHost::ReplyToRenderer( - ResourceMessageFilter* renderer_message_filter, - const IPC::ChannelHandle& channel, - const WebPluginInfo& info, - IPC::Message* reply_msg) { - ViewHostMsg_OpenChannelToPlugin::WriteReplyParams(reply_msg, channel, info); - renderer_message_filter->Send(reply_msg); -} - URLRequestContext* PluginProcessHost::GetRequestContext( uint32 request_id, const ViewHostMsg_Resource_Request& request_data) { return CPBrowsingContextManager::Instance()->ToURLRequestContext(request_id); } -void PluginProcessHost::RequestPluginChannel( - ResourceMessageFilter* renderer_message_filter, - const std::string& mime_type, IPC::Message* reply_msg) { +void PluginProcessHost::RequestPluginChannel(Client* client) { // We can't send any sync messages from the browser because it might lead to // a hang. However this async messages must be answered right away by the // plugin process (i.e. unblocks a Send() call like a sync message) otherwise // a deadlock can occur if the plugin creation request from the renderer is // a result of a sync message by the plugin process. - PluginProcessMsg_CreateChannel* msg = new PluginProcessMsg_CreateChannel( - renderer_message_filter->id(), - renderer_message_filter->off_the_record()); + PluginProcessMsg_CreateChannel* msg = + new PluginProcessMsg_CreateChannel(client->ID(), + client->OffTheRecord()); msg->set_unblock(true); if (Send(msg)) { - sent_requests_.push(ChannelRequest( - renderer_message_filter, mime_type, reply_msg)); + sent_requests_.push(client); } else { - ReplyToRenderer(renderer_message_filter, - IPC::ChannelHandle(), - info_, - reply_msg); + client->OnError(); } } void PluginProcessHost::OnChannelCreated( const IPC::ChannelHandle& channel_handle) { - const ChannelRequest& request = sent_requests_.front(); + Client* client = sent_requests_.front(); - ReplyToRenderer(request.renderer_message_filter_.get(), - channel_handle, - info_, - request.reply_msg); + client->OnChannelOpened(channel_handle); sent_requests_.pop(); } @@ -473,14 +448,3 @@ void PluginProcessHost::OnPluginMessage( chrome_plugin->functions().on_message(data_ptr, data_len); } } - -PluginProcessHost::ChannelRequest::ChannelRequest( - ResourceMessageFilter* renderer_message_filter, - const std::string& m, - IPC::Message* r) - : mime_type(m), - reply_msg(r), - renderer_message_filter_(renderer_message_filter) { -} - -PluginProcessHost::ChannelRequest::~ChannelRequest() {} diff --git a/chrome/browser/plugin_process_host.h b/chrome/browser/plugin_process_host.h index 79e34a1..3857c73 100644 --- a/chrome/browser/plugin_process_host.h +++ b/chrome/browser/plugin_process_host.h @@ -17,14 +17,17 @@ #include "base/ref_counted.h" #include "chrome/browser/browser_child_process_host.h" #include "chrome/browser/net/resolve_proxy_msg_helper.h" -#include "chrome/browser/renderer_host/resource_message_filter.h" -#include "ipc/ipc_channel_handle.h" +#include "gfx/native_widget_types.h" #include "webkit/glue/plugins/webplugininfo.h" namespace gfx { class Rect; } +namespace IPC { +struct ChannelHandle; +} + class URLRequestContext; struct ViewHostMsg_Resource_Request; class GURL; @@ -40,6 +43,21 @@ class GURL; class PluginProcessHost : public BrowserChildProcessHost, public ResolveProxyMsgHelper::Delegate { public: + class Client { + public: + // Returns a opaque unique identifier for the process requesting + // the channel. + virtual int ID() = 0; + virtual bool OffTheRecord() = 0; + virtual void SetPluginInfo(const WebPluginInfo& info) = 0; + // The client should delete itself when one of these methods is called. + virtual void OnChannelOpened(const IPC::ChannelHandle& handle) = 0; + virtual void OnError() = 0; + + protected: + virtual ~Client() {} + }; + PluginProcessHost(); virtual ~PluginProcessHost(); @@ -61,17 +79,8 @@ class PluginProcessHost : public BrowserChildProcessHost, // Tells the plugin process to create a new channel for communication with a // renderer. When the plugin process responds with the channel name, - // reply_msg is used to send the name to the renderer. - void OpenChannelToPlugin(ResourceMessageFilter* renderer_message_filter, - const std::string& mime_type, - IPC::Message* reply_msg); - - // Sends the reply to an open channel request to the renderer with the given - // channel name. - static void ReplyToRenderer(ResourceMessageFilter* renderer_message_filter, - const IPC::ChannelHandle& channel, - const WebPluginInfo& info, - IPC::Message* reply_msg); + // OnChannelOpened in the client is called. + void OpenChannelToPlugin(Client* client); // This function is called on the IO thread once we receive a reply from the // modal HTML dialog (in the form of a JSON string). This function forwards @@ -102,9 +111,7 @@ class PluginProcessHost : public BrowserChildProcessHost, // Sends a message to the plugin process to request creation of a new channel // for the given mime type. - void RequestPluginChannel(ResourceMessageFilter* renderer_message_filter, - const std::string& mime_type, - IPC::Message* reply_msg); + void RequestPluginChannel(Client* client); virtual void OnProcessLaunched(); @@ -139,23 +146,15 @@ class PluginProcessHost : public BrowserChildProcessHost, virtual bool CanShutdown() { return sent_requests_.empty(); } - struct ChannelRequest { - ChannelRequest(ResourceMessageFilter* renderer_message_filter, - const std::string& m, IPC::Message* r); - ~ChannelRequest(); - - std::string mime_type; - IPC::Message* reply_msg; - scoped_refptr<ResourceMessageFilter> renderer_message_filter_; - }; + void CancelRequests(); // These are channel requests that we are waiting to send to the // plugin process once the channel is opened. - std::vector<ChannelRequest> pending_requests_; + std::vector<Client*> pending_requests_; // These are the channel requests that we have already sent to // the plugin process, but haven't heard back about yet. - std::queue<ChannelRequest> sent_requests_; + std::queue<Client*> sent_requests_; // Information about the plugin. WebPluginInfo info_; diff --git a/chrome/browser/plugin_service.cc b/chrome/browser/plugin_service.cc index a3d636b..cc6408f 100644 --- a/chrome/browser/plugin_service.cc +++ b/chrome/browser/plugin_service.cc @@ -17,7 +17,6 @@ #include "chrome/browser/browser_thread.h" #include "chrome/browser/chrome_plugin_host.h" #include "chrome/browser/extensions/extensions_service.h" -#include "chrome/browser/plugin_process_host.h" #include "chrome/browser/plugin_updater.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" @@ -250,24 +249,22 @@ PluginProcessHost* PluginService::FindOrStartPluginProcess( } void PluginService::OpenChannelToPlugin( - ResourceMessageFilter* renderer_msg_filter, const GURL& url, const std::string& mime_type, - IPC::Message* reply_msg) { + PluginProcessHost::Client* client) { // The PluginList::GetFirstAllowedPluginInfo may need to load the // plugins. Don't do it on the IO thread. BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod( this, &PluginService::GetAllowedPluginForOpenChannelToPlugin, - make_scoped_refptr(renderer_msg_filter), url, mime_type, reply_msg)); + url, mime_type, client)); } void PluginService::GetAllowedPluginForOpenChannelToPlugin( - ResourceMessageFilter* renderer_msg_filter, const GURL& url, const std::string& mime_type, - IPC::Message* reply_msg) { + PluginProcessHost::Client* client) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); WebPluginInfo info; bool found = GetFirstAllowedPluginInfo(url, mime_type, &info, NULL); @@ -280,24 +277,19 @@ void PluginService::GetAllowedPluginForOpenChannelToPlugin( BrowserThread::IO, FROM_HERE, NewRunnableMethod( this, &PluginService::FinishOpenChannelToPlugin, - make_scoped_refptr(renderer_msg_filter), mime_type, plugin_path, - reply_msg)); + plugin_path, client)); } void PluginService::FinishOpenChannelToPlugin( - ResourceMessageFilter* renderer_msg_filter, - const std::string& mime_type, const FilePath& plugin_path, - IPC::Message* reply_msg) { + PluginProcessHost::Client* client) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); PluginProcessHost* plugin_host = FindOrStartPluginProcess(plugin_path); - if (plugin_host) { - plugin_host->OpenChannelToPlugin(renderer_msg_filter, mime_type, reply_msg); - } else { - PluginProcessHost::ReplyToRenderer( - renderer_msg_filter, IPC::ChannelHandle(), WebPluginInfo(), reply_msg); - } + if (plugin_host) + plugin_host->OpenChannelToPlugin(client); + else + client->OnError(); } bool PluginService::GetFirstAllowedPluginInfo( @@ -364,7 +356,7 @@ void PluginService::Observe(NotificationType type, const NotificationDetails& details) { switch (type.value) { case NotificationType::EXTENSION_LOADED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); bool plugins_changed = false; for (size_t i = 0; i < extension->plugins().size(); ++i) { const Extension::PluginInfo& plugin = extension->plugins()[i]; @@ -380,7 +372,7 @@ void PluginService::Observe(NotificationType type, } case NotificationType::EXTENSION_UNLOADED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); bool plugins_changed = false; for (size_t i = 0; i < extension->plugins().size(); ++i) { const Extension::PluginInfo& plugin = extension->plugins()[i]; diff --git a/chrome/browser/plugin_service.h b/chrome/browser/plugin_service.h index abb55a1..d6de864 100644 --- a/chrome/browser/plugin_service.h +++ b/chrome/browser/plugin_service.h @@ -16,9 +16,11 @@ #include "base/hash_tables.h" #include "base/singleton.h" #include "base/waitable_event_watcher.h" +#include "chrome/browser/plugin_process_host.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "googleurl/src/gurl.h" +#include "ipc/ipc_channel_handle.h" #if defined(OS_WIN) #include "base/scoped_ptr.h" @@ -36,7 +38,6 @@ class Message; } class MessageLoop; -class PluginProcessHost; class Profile; class ResourceDispatcherHost; class ResourceMessageFilter; @@ -82,10 +83,9 @@ class PluginService // Opens a channel to a plugin process for the given mime type, starting // a new plugin process if necessary. This must be called on the IO thread // or else a deadlock can occur. - void OpenChannelToPlugin(ResourceMessageFilter* renderer_msg_filter, - const GURL& url, + void OpenChannelToPlugin(const GURL& url, const std::string& mime_type, - IPC::Message* reply_msg); + PluginProcessHost::Client* client); // Gets the first allowed plugin in the list of plugins that matches // the given url and mime type. Must be called on the FILE thread. @@ -126,18 +126,15 @@ class PluginService // Helper so we can do the plugin lookup on the FILE thread. void GetAllowedPluginForOpenChannelToPlugin( - ResourceMessageFilter* renderer_msg_filter, const GURL& url, const std::string& mime_type, - IPC::Message* reply_msg); + PluginProcessHost::Client* client); // Helper so we can finish opening the channel after looking up the // plugin. void FinishOpenChannelToPlugin( - ResourceMessageFilter* renderer_msg_filter, - const std::string& mime_type, const FilePath& plugin_path, - IPC::Message* reply_msg); + PluginProcessHost::Client* client); // mapping between plugin path and PluginProcessHost typedef base::hash_map<FilePath, PluginProcessHost*> PluginMap; diff --git a/chrome/browser/plugin_updater.cc b/chrome/browser/plugin_updater.cc index ded1d61..8e8227c 100644 --- a/chrome/browser/plugin_updater.cc +++ b/chrome/browser/plugin_updater.cc @@ -24,6 +24,10 @@ #include "chrome/common/pref_names.h" #include "webkit/glue/plugins/webplugininfo.h" +// How long to wait to save the plugin enabled information, which might need to +// go to disk. +#define kPluginUpdateDelayMs (60 * 1000) + PluginUpdater::PluginUpdater() : enable_internal_pdf_(true), notify_pending_(false) { @@ -132,6 +136,8 @@ void PluginUpdater::DisablePluginGroupsFromPrefs(Profile* profile) { bool found_internal_pdf = false; bool force_enable_internal_pdf = false; string16 pdf_group_name = ASCIIToUTF16(PepperPluginRegistry::kPDFPluginName); + bool force_internal_pdf_for_this_run = CommandLine::ForCurrentProcess()-> + HasSwitch(switches::kForceInternalPDFPlugin); FilePath pdf_path; PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_path); FilePath::StringType pdf_path_str = pdf_path.value(); @@ -177,9 +183,13 @@ void PluginUpdater::DisablePluginGroupsFromPrefs(Profile* profile) { if (FilePath::CompareIgnoreCase(path, pdf_path_str) == 0) { found_internal_pdf = true; - if (!enabled && force_enable_internal_pdf) { - enabled = true; - plugin->SetBoolean("enabled", true); + if (!enabled) { + if (force_enable_internal_pdf) { + enabled = true; + plugin->SetBoolean("enabled", true); + } else if (force_internal_pdf_for_this_run) { + enabled = true; + } } } if (!enabled) @@ -202,7 +212,8 @@ void PluginUpdater::DisablePluginGroupsFromPrefs(Profile* profile) { profile->GetPrefs()->GetList(prefs::kPluginsPluginsBlacklist); DisablePluginsFromPolicy(plugin_blacklist); - if (!enable_internal_pdf_ && !found_internal_pdf) { + if ((!enable_internal_pdf_ && !found_internal_pdf) && + !force_internal_pdf_for_this_run) { // The internal PDF plugin is disabled by default, and the user hasn't // overridden the default. NPAPI::PluginList::Singleton()->DisablePlugin(pdf_path); @@ -213,15 +224,20 @@ void PluginUpdater::DisablePluginGroupsFromPrefs(Profile* profile) { // See http://crbug.com/50105 for background. EnablePluginGroup(false, ASCIIToUTF16(PluginGroup::kAdobeReader8GroupName)); EnablePluginGroup(false, ASCIIToUTF16(PluginGroup::kAdobeReader9GroupName)); + + // We want to save this, but doing so requires loading the list of plugins, + // so do it after a minute as to not impact startup performance. Note that + // plugins are loaded after 30s by the metrics service. + UpdatePreferences(profile, kPluginUpdateDelayMs); } } -void PluginUpdater::UpdatePreferences(Profile* profile) { - BrowserThread::PostTask( +void PluginUpdater::UpdatePreferences(Profile* profile, int delay_ms) { + BrowserThread::PostDelayedTask( BrowserThread::FILE, FROM_HERE, NewRunnableFunction( - &PluginUpdater::GetPreferencesDataOnFileThread, profile)); + &PluginUpdater::GetPreferencesDataOnFileThread, profile), delay_ms); } void PluginUpdater::GetPreferencesDataOnFileThread(void* profile) { diff --git a/chrome/browser/plugin_updater.h b/chrome/browser/plugin_updater.h index 2a8df1a..2b8cd10 100644 --- a/chrome/browser/plugin_updater.h +++ b/chrome/browser/plugin_updater.h @@ -36,7 +36,7 @@ class PluginUpdater : public NotificationObserver { void DisablePluginGroupsFromPrefs(Profile* profile); // Write the enable/disable status to the user's preference file. - void UpdatePreferences(Profile* profile); + void UpdatePreferences(Profile* profile, int delay_ms); // NotificationObserver method overrides void Observe(NotificationType type, diff --git a/chrome/browser/policy/config_dir_policy_provider.cc b/chrome/browser/policy/config_dir_policy_provider.cc index 6fe7630..f134aeb 100644 --- a/chrome/browser/policy/config_dir_policy_provider.cc +++ b/chrome/browser/policy/config_dir_policy_provider.cc @@ -7,114 +7,19 @@ #include <set> #include "base/file_util.h" -#include "base/logging.h" -#include "base/message_loop.h" -#include "base/task.h" -#include "base/utf_string_conversions.h" #include "base/values.h" -#include "chrome/browser/browser_thread.h" #include "chrome/common/json_value_serializer.h" namespace policy { -// Amount of time we wait for the files in the policy directory to settle before -// trying to load it. This alleviates the problem of reading partially written -// files and allows to batch quasi-simultaneous changes. -const int kSettleIntervalSeconds = 5; - -// The time interval for rechecking policy. This is our fallback in case the -// directory watch fails or doesn't report a change. -const int kReloadIntervalMinutes = 15; - -// PolicyDirLoader implementation: - -PolicyDirLoader::PolicyDirLoader( - base::WeakPtr<ConfigDirPolicyProvider> provider, - const FilePath& config_dir, - int settle_interval_seconds, - int reload_interval_minutes) - : provider_(provider), - origin_loop_(MessageLoop::current()), - config_dir_(config_dir), - reload_task_(NULL), - settle_interval_seconds_(settle_interval_seconds), - reload_interval_minutes_(reload_interval_minutes) { - // Force an initial load, so GetPolicy() works. - policy_.reset(Load()); - DCHECK(policy_.get()); -} - -void PolicyDirLoader::Stop() { - if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - NewRunnableMethod(this, &PolicyDirLoader::Stop)); - return; - } - - if (reload_task_) { - reload_task_->Cancel(); - reload_task_ = NULL; - } +ConfigDirPolicyLoader::ConfigDirPolicyLoader(const FilePath& config_dir) + : FileBasedPolicyProvider::Delegate(config_dir) { } -void PolicyDirLoader::Reload() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - - // Check the directory time in order to see whether a reload is required. - base::TimeDelta delay; - base::Time now = base::Time::Now(); - if (!IsSafeToReloadPolicy(now, &delay)) { - ScheduleReloadTask(delay); - return; - } - - // Load the policy definitions. - scoped_ptr<DictionaryValue> new_policy(Load()); - - // Check again in case the directory has changed while reading it. - if (!IsSafeToReloadPolicy(now, &delay)) { - ScheduleReloadTask(delay); - return; - } - - // Replace policy definition. - bool changed = false; - { - AutoLock lock(lock_); - changed = !policy_->Equals(new_policy.get()); - policy_.reset(new_policy.release()); - } - - // There's a change, report it! - if (changed) { - VLOG(1) << "Policy reload from " << config_dir_.value() << " succeeded."; - origin_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &PolicyDirLoader::NotifyPolicyChanged)); - } - - // As a safeguard in case the file watcher fails, schedule a reload task - // that'll make us recheck after a reasonable interval. - ScheduleReloadTask(base::TimeDelta::FromMinutes(reload_interval_minutes_)); -} - -DictionaryValue* PolicyDirLoader::GetPolicy() { - AutoLock lock(lock_); - return static_cast<DictionaryValue*>(policy_->DeepCopy()); -} - -void PolicyDirLoader::OnFilePathChanged(const FilePath& path) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - Reload(); -} - -void PolicyDirLoader::OnError() { - LOG(ERROR) << "FileWatcher on " << config_dir_.value() << " failed."; -} - -DictionaryValue* PolicyDirLoader::Load() { +DictionaryValue* ConfigDirPolicyLoader::Load() { // Enumerate the files and sort them lexicographically. std::set<FilePath> files; - file_util::FileEnumerator file_enumerator(config_dir_, false, + file_util::FileEnumerator file_enumerator(config_file_path(), false, file_util::FileEnumerator::FILES); for (FilePath config_file_path = file_enumerator.Next(); !config_file_path.empty(); config_file_path = file_enumerator.Next()) @@ -144,131 +49,37 @@ DictionaryValue* PolicyDirLoader::Load() { return policy; } -bool PolicyDirLoader::IsSafeToReloadPolicy(const base::Time& now, - base::TimeDelta* delay) { - DCHECK(delay); - base::PlatformFileInfo dir_info; +base::Time ConfigDirPolicyLoader::GetLastModification() { + base::Time last_modification = base::Time(); + base::PlatformFileInfo file_info; - // Reading an empty directory or a file is always safe. - if (!file_util::GetFileInfo(config_dir_, &dir_info) || - !dir_info.is_directory) { - last_modification_file_ = base::Time(); - return true; + // If the path does not exist or points to a directory, it's safe to load. + if (!file_util::GetFileInfo(config_file_path(), &file_info) || + !file_info.is_directory) { + return last_modification; } - // If there was a change since the last recorded modification, wait some more. - base::TimeDelta settleInterval( - base::TimeDelta::FromSeconds(settle_interval_seconds_)); - if (dir_info.last_modified != last_modification_file_) { - last_modification_file_ = dir_info.last_modified; - last_modification_clock_ = now; - *delay = settleInterval; - return false; - } - - // Check whether the settle interval has elapsed. - base::TimeDelta age = now - last_modification_clock_; - if (age < settleInterval) { - *delay = settleInterval - age; - return false; - } - - return true; -} - -void PolicyDirLoader::ScheduleReloadTask(const base::TimeDelta& delay) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - - if (reload_task_) - reload_task_->Cancel(); - - reload_task_ = NewRunnableMethod(this, &PolicyDirLoader::ReloadFromTask); - BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_, - delay.InMilliseconds()); -} - -void PolicyDirLoader::NotifyPolicyChanged() { - DCHECK_EQ(origin_loop_, MessageLoop::current()); - if (provider_) - provider_->NotifyStoreOfPolicyChange(); -} - -void PolicyDirLoader::ReloadFromTask() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - - // Drop the reference to the reload task, since the task might be the only - // referer that keeps us alive, so we should not Cancel() it. - reload_task_ = NULL; - - Reload(); -} - -// PolicyDirWatcher implementation: - -void PolicyDirWatcher::Init(PolicyDirLoader* loader) { - // Initialization can happen early when the file thread is not yet available. - // So post a task to ourselves on the UI thread which will run after threading - // is up and schedule watch initialization on the file thread. - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - NewRunnableMethod(this, - &PolicyDirWatcher::InitWatcher, - scoped_refptr<PolicyDirLoader>(loader))); -} - -void PolicyDirWatcher::InitWatcher( - const scoped_refptr<PolicyDirLoader>& loader) { - if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - NewRunnableMethod(this, &PolicyDirWatcher::InitWatcher, loader)); - return; + // Enumerate the files and find the most recent modification timestamp. + file_util::FileEnumerator file_enumerator(config_file_path(), + false, + file_util::FileEnumerator::FILES); + for (FilePath config_file = file_enumerator.Next(); + !config_file.empty(); + config_file = file_enumerator.Next()) { + if (file_util::GetFileInfo(config_file, &file_info) && + !file_info.is_directory) { + last_modification = std::min(last_modification, file_info.last_modified); + } } - if (!watcher_.Watch(loader->config_dir(), loader.get())) - loader->OnError(); - - // There might have been changes to the directory in the time between - // construction of the loader and initialization of the watcher. Call reload - // to detect if that is the case. - loader->Reload(); + return last_modification; } -// ConfigDirPolicyProvider implementation: - ConfigDirPolicyProvider::ConfigDirPolicyProvider( - const ConfigurationPolicyProvider::StaticPolicyValueMap& policy_map, + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, const FilePath& config_dir) - : ConfigurationPolicyProvider(policy_map) { - loader_ = new PolicyDirLoader(AsWeakPtr(), config_dir, kSettleIntervalSeconds, - kReloadIntervalMinutes); - watcher_ = new PolicyDirWatcher; - watcher_->Init(loader_.get()); -} - -ConfigDirPolicyProvider::~ConfigDirPolicyProvider() { - loader_->Stop(); -} - -bool ConfigDirPolicyProvider::Provide(ConfigurationPolicyStore* store) { - scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); - DCHECK(policy.get()); - DecodePolicyValueTree(policy.get(), store); - return true; -} - -void ConfigDirPolicyProvider::DecodePolicyValueTree( - DictionaryValue* policies, - ConfigurationPolicyStore* store) { - const PolicyValueMap& mapping(policy_value_map()); - for (PolicyValueMap::const_iterator i = mapping.begin(); - i != mapping.end(); ++i) { - const PolicyValueMapEntry& entry(*i); - Value* value; - if (policies->Get(entry.name, &value) && value->IsType(entry.value_type)) - store->Apply(entry.policy_type, value->DeepCopy()); - } - - // TODO(mnissler): Handle preference overrides once |ConfigurationPolicyStore| - // supports it. + : FileBasedPolicyProvider(policy_list, + new ConfigDirPolicyLoader(config_dir)) { } } // namespace policy diff --git a/chrome/browser/policy/config_dir_policy_provider.h b/chrome/browser/policy/config_dir_policy_provider.h index 3657944..6096513 100644 --- a/chrome/browser/policy/config_dir_policy_provider.h +++ b/chrome/browser/policy/config_dir_policy_provider.h @@ -6,167 +6,35 @@ #define CHROME_BROWSER_POLICY_CONFIG_DIR_POLICY_PROVIDER_H_ #pragma once -#include "base/basictypes.h" -#include "base/file_path.h" -#include "base/lock.h" -#include "base/ref_counted.h" -#include "base/scoped_ptr.h" -#include "base/time.h" -#include "base/weak_ptr.h" -#include "chrome/browser/file_path_watcher.h" -#include "chrome/browser/policy/configuration_policy_provider.h" - -class CancelableTask; -class DictionaryValue; -class MessageLoop; +#include "chrome/browser/policy/file_based_policy_provider.h" namespace policy { -class ConfigDirPolicyProvider; - -// FilePathWatcher delegate implementation that handles change notifications for -// the configuration directory. It keeps the authorative version of the -// currently effective policy dictionary and updates it as appropriate. -class PolicyDirLoader : public FilePathWatcher::Delegate { +// A policy loader implementation backed by a set of files in a given directory. +// The files should contain JSON-formatted policy settings. They are merged +// together and the result is returned via the PolicyLoader interface. The files +// are consulted in lexicographic file name order, so the last value read takes +// precedence in case of preference key collisions. +class ConfigDirPolicyLoader : public FileBasedPolicyProvider::Delegate { public: - // Creates a new loader that'll load its data from |config_dir|. The - // parameters |settle_interval_seconds| and |reload_interval_minutes| specify - // the time to wait before reading the directory contents after a change and - // the period for checking |config_dir| for changes, respectively. - PolicyDirLoader(base::WeakPtr<ConfigDirPolicyProvider> provider, - const FilePath& config_dir, - int settle_interval_seconds, - int reload_interval_minutes); - - // Stops any pending reload tasks. - void Stop(); - - // Reloads the policies and sends out a notification, if appropriate. Must be - // called on the file thread. - void Reload(); - - // Gets the current dictionary value object. Ownership of the returned value - // is transferred to the caller. - DictionaryValue* GetPolicy(); + explicit ConfigDirPolicyLoader(const FilePath& config_dir); - const FilePath& config_dir() { return config_dir_; } - - // FilePathWatcher::Delegate implementation: - void OnFilePathChanged(const FilePath& path); - void OnError(); + // FileBasedPolicyLoader::Delegate implementation. + virtual DictionaryValue* Load(); + virtual base::Time GetLastModification(); private: - // Loads the policy information. Ownership of the return value is transferred - // to the caller. - DictionaryValue* Load(); - - // Checks the directory modification time to see whether reading the - // configuration directory is safe. If not, returns false and the delay until - // it is considered safe to reload in |delay|. - bool IsSafeToReloadPolicy(const base::Time& now, base::TimeDelta* delay); - - // Schedules a reload task to run when |delay| expires. Must be called on the - // file thread. - void ScheduleReloadTask(const base::TimeDelta& delay); - - // Notifies the policy provider to send out a policy changed notification. - // Must be called on |origin_loop_|. - void NotifyPolicyChanged(); - - // Invoked from the reload task on the file thread. - void ReloadFromTask(); - - // The provider this loader is associated with. Access only on the thread that - // called the constructor. See |origin_loop_| below. - base::WeakPtr<ConfigDirPolicyProvider> provider_; - - // The message loop on which this object was constructed and |provider_| - // received on. Recorded so we can call back into the non thread safe provider - // to fire the notification. - MessageLoop* origin_loop_; - - // The directory in which we look for configuration files. - const FilePath config_dir_; - - // Records last known modification timestamp of |config_dir_|. - base::Time last_modification_file_; - - // The wall clock time at which the last modification timestamp was recorded. - // It's better to not assume the file notification time and the wall clock - // times come from the same source, just in case there is some non-local - // filesystem involved. - base::Time last_modification_clock_; - - // Protects |policy_|. - Lock lock_; - - // The current policy definition. - scoped_ptr<DictionaryValue> policy_; - - // The reload task. Access only on the file thread. Holds a reference to the - // currently posted task, so we can cancel and repost it if necessary. - CancelableTask* reload_task_; - - // Settle and reload intervals. - const int settle_interval_seconds_; - const int reload_interval_minutes_; - - DISALLOW_COPY_AND_ASSIGN(PolicyDirLoader); -}; - -// Wraps a FilePathWatcher for the configuration directory and takes care of -// initializing the watcher object on the file thread. -class PolicyDirWatcher : public base::RefCountedThreadSafe<PolicyDirWatcher> { - public: - PolicyDirWatcher() {} - - // Runs initialization. This is in a separate method since we need to post a - // task (which cannot be done from the constructor). - void Init(PolicyDirLoader* loader); - - private: - // PolicyDirWatcher objects should only be deleted by RefCountedThreadSafe. - friend class base::RefCountedThreadSafe<PolicyDirWatcher>; - ~PolicyDirWatcher() {} - - // Actually sets up the watch with the FilePathWatcher code. - void InitWatcher(const scoped_refptr<PolicyDirLoader>& loader); - - // Wrapped watcher that takes care of the actual watching. - FilePathWatcher watcher_; - - DISALLOW_COPY_AND_ASSIGN(PolicyDirWatcher); + DISALLOW_COPY_AND_ASSIGN(ConfigDirPolicyLoader); }; -// A policy provider implementation backed by a set of files in a given -// directory. The files should contain JSON-formatted policy settings. They are -// merged together and the result is returned via the -// ConfigurationPolicyProvider interface. The files are consulted in -// lexicographic file name order, so the last value read takes precedence in -// case of preference key collisions. -class ConfigDirPolicyProvider - : public ConfigurationPolicyProvider, - public base::SupportsWeakPtr<ConfigDirPolicyProvider> { +// Policy provider backed by JSON files in a configuration directory. +class ConfigDirPolicyProvider : public FileBasedPolicyProvider { public: - explicit ConfigDirPolicyProvider( - const ConfigurationPolicyProvider::StaticPolicyValueMap& policy_map, + ConfigDirPolicyProvider( + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, const FilePath& config_dir); - virtual ~ConfigDirPolicyProvider(); - - // ConfigurationPolicyProvider implementation. - virtual bool Provide(ConfigurationPolicyStore* store); private: - // Decodes the value tree and writes the configuration to the given |store|. - void DecodePolicyValueTree(DictionaryValue* policies, - ConfigurationPolicyStore* store); - - // Watches for changes to the configuration directory. - scoped_refptr<PolicyDirWatcher> watcher_; - - // The loader object we use internally. - scoped_refptr<PolicyDirLoader> loader_; - DISALLOW_COPY_AND_ASSIGN(ConfigDirPolicyProvider); }; diff --git a/chrome/browser/policy/config_dir_policy_provider_unittest.cc b/chrome/browser/policy/config_dir_policy_provider_unittest.cc index 2c755ab..25893da 100644 --- a/chrome/browser/policy/config_dir_policy_provider_unittest.cc +++ b/chrome/browser/policy/config_dir_policy_provider_unittest.cc @@ -6,47 +6,24 @@ #include "base/file_util.h" #include "base/path_service.h" +#include "base/scoped_temp_dir.h" #include "base/string_number_conversions.h" #include "chrome/browser/policy/config_dir_policy_provider.h" #include "chrome/browser/policy/configuration_policy_pref_store.h" #include "chrome/browser/policy/mock_configuration_policy_store.h" #include "chrome/common/json_value_serializer.h" #include "chrome/common/policy_constants.h" -#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -using testing::Mock; - namespace policy { -// Shorter reload intervals for testing PolicyDirWatcher. -const int kSettleIntervalSecondsForTesting = 0; -const int kReloadIntervalMinutesForTesting = 1; - template<typename BASE> class ConfigDirPolicyProviderTestBase : public BASE { protected: - ConfigDirPolicyProviderTestBase() - : ui_thread_(BrowserThread::UI, &loop_), - file_thread_(BrowserThread::FILE, &loop_) {} + ConfigDirPolicyProviderTestBase() {} virtual void SetUp() { - // Determine the directory to use for testing. - ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); - test_dir_ = - test_dir_.Append(FILE_PATH_LITERAL("ConfigDirPolicyProviderTest")); - - // Make sure the directory is fresh. - file_util::Delete(test_dir_, true); - file_util::CreateDirectory(test_dir_); - ASSERT_TRUE(file_util::DirectoryExists(test_dir_)); - } - - virtual void TearDown() { - loop_.RunAllPending(); - // Clean up test directory. - ASSERT_TRUE(file_util::Delete(test_dir_, true)); - ASSERT_FALSE(file_util::PathExists(test_dir_)); + ASSERT_TRUE(test_dir_.CreateUniqueTempDir()); } // JSON-encode a dictionary and write it to a file. @@ -55,155 +32,53 @@ class ConfigDirPolicyProviderTestBase : public BASE { std::string data; JSONStringValueSerializer serializer(&data); serializer.Serialize(dict); - FilePath file_path(test_dir_.AppendASCII(file_name)); + const FilePath file_path(test_dir().AppendASCII(file_name)); ASSERT_TRUE(file_util::WriteFile(file_path, data.c_str(), data.size())); } - FilePath test_dir_; - MessageLoop loop_; - BrowserThread ui_thread_; - BrowserThread file_thread_; -}; - -// A mock provider that allows us to capture reload notifications. -class MockConfigDirPolicyProvider : public ConfigDirPolicyProvider { - public: - explicit MockConfigDirPolicyProvider(const FilePath& config_dir_) - : ConfigDirPolicyProvider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), - config_dir_) { - } + const FilePath& test_dir() { return test_dir_.path(); } - MOCK_METHOD0(NotifyStoreOfPolicyChange, void()); + private: + ScopedTempDir test_dir_; }; -class PolicyDirLoaderTest +class ConfigDirPolicyLoaderTest : public ConfigDirPolicyProviderTestBase<testing::Test> { - protected: - PolicyDirLoaderTest() {} - - virtual void SetUp() { - ConfigDirPolicyProviderTestBase<testing::Test>::SetUp(); - provider_.reset(new MockConfigDirPolicyProvider(test_dir_)); - } - - virtual void TearDown() { - provider_.reset(NULL); - ConfigDirPolicyProviderTestBase<testing::Test>::TearDown(); - } - - scoped_ptr<MockConfigDirPolicyProvider> provider_; -}; - -TEST_F(PolicyDirLoaderTest, BasicLoad) { - DictionaryValue test_dict; - test_dict.SetString("HomepageLocation", "http://www.google.com"); - WriteConfigFile(test_dict, "config_file"); - - scoped_refptr<PolicyDirLoader> loader_( - new PolicyDirLoader(provider_->AsWeakPtr(), test_dir_, - kSettleIntervalSecondsForTesting, - kReloadIntervalMinutesForTesting)); - scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); - EXPECT_TRUE(policy.get()); - EXPECT_EQ(1U, policy->size()); - - std::string str_value; - EXPECT_TRUE(policy->GetString("HomepageLocation", &str_value)); - EXPECT_EQ("http://www.google.com", str_value); - - loader_->Stop(); -} - -TEST_F(PolicyDirLoaderTest, TestRefresh) { - scoped_refptr<PolicyDirLoader> loader_( - new PolicyDirLoader(provider_->AsWeakPtr(), test_dir_, - kSettleIntervalSecondsForTesting, - kReloadIntervalMinutesForTesting)); - scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); - EXPECT_TRUE(policy.get()); - EXPECT_EQ(0U, policy->size()); - - DictionaryValue test_dict; - test_dict.SetString("HomepageLocation", "http://www.google.com"); - WriteConfigFile(test_dict, "config_file"); - - EXPECT_CALL(*provider_, NotifyStoreOfPolicyChange()).Times(1); - loader_->OnFilePathChanged(test_dir_.AppendASCII("config_file")); - - // Run the loop. The refresh should be handled immediately since the settle - // interval has been disabled. - loop_.RunAllPending(); - Mock::VerifyAndClearExpectations(provider_.get()); - - policy.reset(loader_->GetPolicy()); - EXPECT_TRUE(policy.get()); - EXPECT_EQ(1U, policy->size()); - - std::string str_value; - EXPECT_TRUE(policy->GetString("HomepageLocation", &str_value)); - EXPECT_EQ("http://www.google.com", str_value); - - loader_->Stop(); -} - -template<typename BASE> -class ConfigDirPolicyProviderTestWithMockStore - : public ConfigDirPolicyProviderTestBase<BASE> { - protected: - virtual void SetUp() { - ConfigDirPolicyProviderTestBase<BASE>::SetUp(); - // Create a fresh policy store mock. - policy_store_.reset(new MockConfigurationPolicyStore()); - } - - scoped_ptr<MockConfigurationPolicyStore> policy_store_; -}; - -class ConfigDirPolicyProviderTest - : public ConfigDirPolicyProviderTestWithMockStore<testing::Test> { }; // The preferences dictionary is expected to be empty when there are no files to // load. -TEST_F(ConfigDirPolicyProviderTest, ReadPrefsEmpty) { - ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_TRUE(policy_store_->policy_map().empty()); +TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsEmpty) { + ConfigDirPolicyLoader loader(test_dir()); + scoped_ptr<DictionaryValue> policy(loader.Load()); + EXPECT_TRUE(policy.get()); + EXPECT_TRUE(policy->empty()); } // Reading from a non-existent directory should result in an empty preferences // dictionary. -TEST_F(ConfigDirPolicyProviderTest, ReadPrefsNonExistentDirectory) { - FilePath non_existent_dir(test_dir_.Append(FILE_PATH_LITERAL("not_there"))); - ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), - non_existent_dir); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_TRUE(policy_store_->policy_map().empty()); +TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsNonExistentDirectory) { + FilePath non_existent_dir(test_dir().Append(FILE_PATH_LITERAL("not_there"))); + ConfigDirPolicyLoader loader(non_existent_dir); + scoped_ptr<DictionaryValue> policy(loader.Load()); + EXPECT_TRUE(policy.get()); + EXPECT_TRUE(policy->empty()); } // Test reading back a single preference value. -TEST_F(ConfigDirPolicyProviderTest, ReadPrefsSinglePref) { +TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsSinglePref) { DictionaryValue test_dict; test_dict.SetString("HomepageLocation", "http://www.google.com"); WriteConfigFile(test_dict, "config_file"); - ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_EQ(1U, policy_store_->policy_map().size()); - const Value* value = - policy_store_->Get(ConfigurationPolicyStore::kPolicyHomePage); - ASSERT_TRUE(value); - std::string str_value; - EXPECT_TRUE(value->GetAsString(&str_value)); - EXPECT_EQ("http://www.google.com", str_value); + ConfigDirPolicyLoader loader(test_dir()); + scoped_ptr<DictionaryValue> policy(loader.Load()); + EXPECT_TRUE(policy.get()); + EXPECT_TRUE(policy->Equals(&test_dict)); } // Test merging values from different files. -TEST_F(ConfigDirPolicyProviderTest, ReadPrefsMergePrefs) { +TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsMergePrefs) { // Write a bunch of data files in order to increase the chance to detect the // provider not respecting lexicographic ordering when reading them. Since the // filesystem may return files in arbitrary order, there is no way to be sure, @@ -217,17 +92,11 @@ TEST_F(ConfigDirPolicyProviderTest, ReadPrefsMergePrefs) { WriteConfigFile(test_dict_foo, "9"); for (unsigned int i = 5; i <= 8; ++i) WriteConfigFile(test_dict_bar, base::IntToString(i)); - ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_EQ(1U, policy_store_->policy_map().size()); - const Value* value = - policy_store_->Get(ConfigurationPolicyStore::kPolicyHomePage); - ASSERT_TRUE(value); - std::string str_value; - EXPECT_TRUE(value->GetAsString(&str_value)); - EXPECT_EQ("http://foo.com", str_value); + ConfigDirPolicyLoader loader(test_dir()); + scoped_ptr<DictionaryValue> policy(loader.Load()); + EXPECT_TRUE(policy.get()); + EXPECT_TRUE(policy->Equals(&test_dict_foo)); } // Holds policy type, corresponding policy key string and a valid value for use @@ -235,7 +104,7 @@ TEST_F(ConfigDirPolicyProviderTest, ReadPrefsMergePrefs) { class ValueTestParams { public: // Assumes ownership of |test_value|. - ValueTestParams(ConfigurationPolicyStore::PolicyType type, + ValueTestParams(ConfigurationPolicyType type, const char* policy_key, Value* test_value) : type_(type), @@ -260,28 +129,28 @@ class ValueTestParams { test_value_.swap(other.test_value_); } - ConfigurationPolicyStore::PolicyType type() const { return type_; } + ConfigurationPolicyType type() const { return type_; } const char* policy_key() const { return policy_key_; } const Value* test_value() const { return test_value_.get(); } // Factory methods that create parameter objects for different value types. static ValueTestParams ForStringPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* policy_key) { return ValueTestParams(type, policy_key, Value::CreateStringValue("test")); } static ValueTestParams ForBooleanPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* policy_key) { return ValueTestParams(type, policy_key, Value::CreateBooleanValue(true)); } static ValueTestParams ForIntegerPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* policy_key) { return ValueTestParams(type, policy_key, Value::CreateIntegerValue(42)); } static ValueTestParams ForListPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* policy_key) { ListValue* value = new ListValue(); value->Set(0U, Value::CreateStringValue("first")); @@ -290,7 +159,7 @@ class ValueTestParams { } private: - ConfigurationPolicyStore::PolicyType type_; + ConfigurationPolicyType type_; const char* policy_key_; scoped_ptr<Value> test_value_; }; @@ -298,15 +167,31 @@ class ValueTestParams { // Tests whether the provider correctly reads a value from the file and forwards // it to the store. class ConfigDirPolicyProviderValueTest - : public ConfigDirPolicyProviderTestWithMockStore< + : public ConfigDirPolicyProviderTestBase< testing::TestWithParam<ValueTestParams> > { + protected: + ConfigDirPolicyProviderValueTest() + : ui_thread_(BrowserThread::UI, &loop_), + file_thread_(BrowserThread::FILE, &loop_) {} + + virtual void TearDown() { + loop_.RunAllPending(); + } + + MockConfigurationPolicyStore policy_store_; + + private: + MessageLoop loop_; + BrowserThread ui_thread_; + BrowserThread file_thread_; }; TEST_P(ConfigDirPolicyProviderValueTest, Default) { ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_TRUE(policy_store_->policy_map().empty()); + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), + test_dir()); + EXPECT_TRUE(provider.Provide(&policy_store_)); + EXPECT_TRUE(policy_store_.policy_map().empty()); } TEST_P(ConfigDirPolicyProviderValueTest, NullValue) { @@ -314,9 +199,10 @@ TEST_P(ConfigDirPolicyProviderValueTest, NullValue) { dict.Set(GetParam().policy_key(), Value::CreateNullValue()); WriteConfigFile(dict, "empty"); ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_TRUE(policy_store_->policy_map().empty()); + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), + test_dir()); + EXPECT_TRUE(provider.Provide(&policy_store_)); + EXPECT_TRUE(policy_store_.policy_map().empty()); } TEST_P(ConfigDirPolicyProviderValueTest, TestValue) { @@ -324,10 +210,11 @@ TEST_P(ConfigDirPolicyProviderValueTest, TestValue) { dict.Set(GetParam().policy_key(), GetParam().test_value()->DeepCopy()); WriteConfigFile(dict, "policy"); ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_EQ(1U, policy_store_->policy_map().size()); - const Value* value = policy_store_->Get(GetParam().type()); + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), + test_dir()); + EXPECT_TRUE(provider.Provide(&policy_store_)); + EXPECT_EQ(1U, policy_store_.policy_map().size()); + const Value* value = policy_store_.Get(GetParam().type()); ASSERT_TRUE(value); EXPECT_TRUE(GetParam().test_value()->Equals(value)); } @@ -338,94 +225,94 @@ INSTANTIATE_TEST_CASE_P( ConfigDirPolicyProviderValueTest, testing::Values( ValueTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyHomePage, + kPolicyHomePage, key::kHomepageLocation), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyHomepageIsNewTabPage, + kPolicyHomepageIsNewTabPage, key::kHomepageIsNewTabPage), ValueTestParams::ForIntegerPolicy( - ConfigurationPolicyStore::kPolicyRestoreOnStartup, + kPolicyRestoreOnStartup, key::kRestoreOnStartup), ValueTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyURLsToRestoreOnStartup, + kPolicyURLsToRestoreOnStartup, key::kURLsToRestoreOnStartup), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEnabled, + kPolicyDefaultSearchProviderEnabled, key::kDefaultSearchProviderEnabled), ValueTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderName, + kPolicyDefaultSearchProviderName, key::kDefaultSearchProviderName), ValueTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderKeyword, + kPolicyDefaultSearchProviderKeyword, key::kDefaultSearchProviderKeyword), ValueTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSearchURL, + kPolicyDefaultSearchProviderSearchURL, key::kDefaultSearchProviderSearchURL), ValueTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSuggestURL, + kPolicyDefaultSearchProviderSuggestURL, key::kDefaultSearchProviderSuggestURL), ValueTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderIconURL, + kPolicyDefaultSearchProviderIconURL, key::kDefaultSearchProviderIconURL), ValueTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEncodings, + kPolicyDefaultSearchProviderEncodings, key::kDefaultSearchProviderEncodings), ValueTestParams::ForIntegerPolicy( - ConfigurationPolicyStore::kPolicyProxyServerMode, + kPolicyProxyServerMode, key::kProxyServerMode), ValueTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyProxyServer, + kPolicyProxyServer, key::kProxyServer), ValueTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyProxyPacUrl, + kPolicyProxyPacUrl, key::kProxyPacUrl), ValueTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyProxyBypassList, + kPolicyProxyBypassList, key::kProxyBypassList), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyAlternateErrorPagesEnabled, + kPolicyAlternateErrorPagesEnabled, key::kAlternateErrorPagesEnabled), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicySearchSuggestEnabled, + kPolicySearchSuggestEnabled, key::kSearchSuggestEnabled), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyDnsPrefetchingEnabled, + kPolicyDnsPrefetchingEnabled, key::kDnsPrefetchingEnabled), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicySafeBrowsingEnabled, + kPolicySafeBrowsingEnabled, key::kSafeBrowsingEnabled), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyMetricsReportingEnabled, + kPolicyMetricsReportingEnabled, key::kMetricsReportingEnabled), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyPasswordManagerEnabled, + kPolicyPasswordManagerEnabled, key::kPasswordManagerEnabled), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyPasswordManagerAllowShowPasswords, + kPolicyPasswordManagerAllowShowPasswords, key::kPasswordManagerAllowShowPasswords), ValueTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyDisabledPlugins, + kPolicyDisabledPlugins, key::kDisabledPlugins), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyAutoFillEnabled, + kPolicyAutoFillEnabled, key::kAutoFillEnabled), ValueTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyApplicationLocale, + kPolicyApplicationLocale, key::kApplicationLocaleValue), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicySyncDisabled, + kPolicySyncDisabled, key::kSyncDisabled), ValueTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyExtensionInstallAllowList, + kPolicyExtensionInstallAllowList, key::kExtensionInstallAllowList), ValueTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyExtensionInstallDenyList, + kPolicyExtensionInstallDenyList, key::kExtensionInstallDenyList), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyShowHomeButton, + kPolicyShowHomeButton, key::kShowHomeButton), ValueTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyPrintingEnabled, + kPolicyPrintingEnabled, key::kPrintingEnabled))); } // namespace policy diff --git a/chrome/browser/policy/configuration_policy_pref_store.cc b/chrome/browser/policy/configuration_policy_pref_store.cc index c496348..72830cd 100644 --- a/chrome/browser/policy/configuration_policy_pref_store.cc +++ b/chrome/browser/policy/configuration_policy_pref_store.cc @@ -35,8 +35,7 @@ class ConfigurationPolicyProviderKeeper { public: ConfigurationPolicyProviderKeeper() : managed_provider_(CreateManagedProvider()), - recommended_provider_(CreateRecommendedProvider()) { - } + recommended_provider_(CreateRecommendedProvider()) {} virtual ~ConfigurationPolicyProviderKeeper() {} ConfigurationPolicyProvider* managed_provider() const { @@ -51,210 +50,207 @@ class ConfigurationPolicyProviderKeeper { scoped_ptr<ConfigurationPolicyProvider> managed_provider_; scoped_ptr<ConfigurationPolicyProvider> recommended_provider_; - static ConfigurationPolicyProvider* CreateManagedProvider() { - const ConfigurationPolicyProvider::StaticPolicyValueMap& policy_map = - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(); + static ConfigurationPolicyProvider* CreateManagedProvider(); + static ConfigurationPolicyProvider* CreateRecommendedProvider(); + + DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyProviderKeeper); +}; + + +ConfigurationPolicyProvider* + ConfigurationPolicyProviderKeeper::CreateManagedProvider() { + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list = + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(); #if defined(OS_WIN) - return new ConfigurationPolicyProviderWin(policy_map); + return new ConfigurationPolicyProviderWin(policy_list); #elif defined(OS_MACOSX) - return new ConfigurationPolicyProviderMac(policy_map); + return new ConfigurationPolicyProviderMac(policy_list); #elif defined(OS_POSIX) - FilePath config_dir_path; - if (PathService::Get(chrome::DIR_POLICY_FILES, &config_dir_path)) { - return new ConfigDirPolicyProvider(policy_map, - config_dir_path.Append(FILE_PATH_LITERAL("managed"))); - } else { - return new DummyConfigurationPolicyProvider(policy_map); - } + FilePath config_dir_path; + if (PathService::Get(chrome::DIR_POLICY_FILES, &config_dir_path)) { + return new ConfigDirPolicyProvider( + policy_list, + config_dir_path.Append(FILE_PATH_LITERAL("managed"))); + } else { + return new DummyConfigurationPolicyProvider(policy_list); + } #else - return new DummyConfigurationPolicyProvider(policy_map); + return new DummyConfigurationPolicyProvider(policy_list); #endif - } +} - static ConfigurationPolicyProvider* CreateRecommendedProvider() { - const ConfigurationPolicyProvider::StaticPolicyValueMap& policy_map = - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(); +ConfigurationPolicyProvider* + ConfigurationPolicyProviderKeeper::CreateRecommendedProvider() { + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list = + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(); #if defined(OS_POSIX) && !defined(OS_MACOSX) - FilePath config_dir_path; - if (PathService::Get(chrome::DIR_POLICY_FILES, &config_dir_path)) { - return new ConfigDirPolicyProvider(policy_map, - config_dir_path.Append(FILE_PATH_LITERAL("recommended"))); - } else { - return new DummyConfigurationPolicyProvider(policy_map); - } + FilePath config_dir_path; + if (PathService::Get(chrome::DIR_POLICY_FILES, &config_dir_path)) { + return new ConfigDirPolicyProvider( + policy_list, + config_dir_path.Append(FILE_PATH_LITERAL("recommended"))); + } else { + return new DummyConfigurationPolicyProvider(policy_list); + } #else - return new DummyConfigurationPolicyProvider(policy_map); + return new DummyConfigurationPolicyProvider(policy_list); #endif - } - - DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyProviderKeeper); -}; +} const ConfigurationPolicyPrefStore::PolicyToPreferenceMapEntry - ConfigurationPolicyPrefStore::simple_policy_map_[] = { + ConfigurationPolicyPrefStore::kSimplePolicyMap[] = { { Value::TYPE_STRING, kPolicyHomePage, prefs::kHomePage }, { Value::TYPE_BOOLEAN, kPolicyHomepageIsNewTabPage, - prefs::kHomePageIsNewTabPage }, + prefs::kHomePageIsNewTabPage }, { Value::TYPE_INTEGER, kPolicyRestoreOnStartup, - prefs::kRestoreOnStartup}, + prefs::kRestoreOnStartup}, { Value::TYPE_LIST, kPolicyURLsToRestoreOnStartup, - prefs::kURLsToRestoreOnStartup }, + prefs::kURLsToRestoreOnStartup }, { Value::TYPE_BOOLEAN, kPolicyAlternateErrorPagesEnabled, - prefs::kAlternateErrorPagesEnabled }, + prefs::kAlternateErrorPagesEnabled }, { Value::TYPE_BOOLEAN, kPolicySearchSuggestEnabled, - prefs::kSearchSuggestEnabled }, + prefs::kSearchSuggestEnabled }, { Value::TYPE_BOOLEAN, kPolicyDnsPrefetchingEnabled, - prefs::kDnsPrefetchingEnabled }, + prefs::kDnsPrefetchingEnabled }, { Value::TYPE_BOOLEAN, kPolicyDisableSpdy, - prefs::kDisableSpdy }, + prefs::kDisableSpdy }, { Value::TYPE_BOOLEAN, kPolicySafeBrowsingEnabled, - prefs::kSafeBrowsingEnabled }, + prefs::kSafeBrowsingEnabled }, { Value::TYPE_BOOLEAN, kPolicyPasswordManagerEnabled, - prefs::kPasswordManagerEnabled }, + prefs::kPasswordManagerEnabled }, { Value::TYPE_BOOLEAN, kPolicyPasswordManagerAllowShowPasswords, - prefs::kPasswordManagerAllowShowPasswords }, + prefs::kPasswordManagerAllowShowPasswords }, { Value::TYPE_BOOLEAN, kPolicyPrintingEnabled, - prefs::kPrintingEnabled }, + prefs::kPrintingEnabled }, { Value::TYPE_BOOLEAN, kPolicyMetricsReportingEnabled, - prefs::kMetricsReportingEnabled }, + prefs::kMetricsReportingEnabled }, { Value::TYPE_STRING, kPolicyApplicationLocale, - prefs::kApplicationLocale}, + prefs::kApplicationLocale}, { Value::TYPE_LIST, kPolicyExtensionInstallAllowList, - prefs::kExtensionInstallAllowList}, + prefs::kExtensionInstallAllowList}, { Value::TYPE_LIST, kPolicyExtensionInstallDenyList, - prefs::kExtensionInstallDenyList}, + prefs::kExtensionInstallDenyList}, + { Value::TYPE_LIST, kPolicyExtensionInstallForceList, + prefs::kExtensionInstallForceList}, { Value::TYPE_LIST, kPolicyDisabledPlugins, - prefs::kPluginsPluginsBlacklist}, + prefs::kPluginsPluginsBlacklist}, { Value::TYPE_BOOLEAN, kPolicyShowHomeButton, - prefs::kShowHomeButton }, + prefs::kShowHomeButton }, { Value::TYPE_BOOLEAN, kPolicyJavascriptEnabled, - prefs::kWebKitJavascriptEnabled }, + prefs::kWebKitJavascriptEnabled }, { Value::TYPE_BOOLEAN, kPolicySavingBrowserHistoryDisabled, - prefs::kSavingBrowserHistoryDisabled }, + prefs::kSavingBrowserHistoryDisabled }, + { Value::TYPE_BOOLEAN, kPolicyDeveloperToolsDisabled, + prefs::kDevToolsDisabled }, + { Value::TYPE_BOOLEAN, kPolicyBlockThirdPartyCookies, + prefs::kBlockThirdPartyCookies}, #if defined(OS_CHROMEOS) { Value::TYPE_BOOLEAN, kPolicyChromeOsLockOnIdleSuspend, - prefs::kEnableScreenLock }, + prefs::kEnableScreenLock }, #endif }; const ConfigurationPolicyPrefStore::PolicyToPreferenceMapEntry - ConfigurationPolicyPrefStore::default_search_policy_map_[] = { + ConfigurationPolicyPrefStore::kDefaultSearchPolicyMap[] = { { Value::TYPE_BOOLEAN, kPolicyDefaultSearchProviderEnabled, - prefs::kDefaultSearchProviderEnabled }, + prefs::kDefaultSearchProviderEnabled }, { Value::TYPE_STRING, kPolicyDefaultSearchProviderName, - prefs::kDefaultSearchProviderName }, + prefs::kDefaultSearchProviderName }, { Value::TYPE_STRING, kPolicyDefaultSearchProviderKeyword, - prefs::kDefaultSearchProviderKeyword }, + prefs::kDefaultSearchProviderKeyword }, { Value::TYPE_STRING, kPolicyDefaultSearchProviderSearchURL, - prefs::kDefaultSearchProviderSearchURL }, + prefs::kDefaultSearchProviderSearchURL }, { Value::TYPE_STRING, kPolicyDefaultSearchProviderSuggestURL, - prefs::kDefaultSearchProviderSuggestURL }, + prefs::kDefaultSearchProviderSuggestURL }, { Value::TYPE_STRING, kPolicyDefaultSearchProviderIconURL, - prefs::kDefaultSearchProviderIconURL }, + prefs::kDefaultSearchProviderIconURL }, { Value::TYPE_STRING, kPolicyDefaultSearchProviderEncodings, - prefs::kDefaultSearchProviderEncodings }, + prefs::kDefaultSearchProviderEncodings }, }; const ConfigurationPolicyPrefStore::PolicyToPreferenceMapEntry - ConfigurationPolicyPrefStore::proxy_policy_map_[] = { + ConfigurationPolicyPrefStore::kProxyPolicyMap[] = { { Value::TYPE_STRING, kPolicyProxyServer, prefs::kProxyServer }, { Value::TYPE_STRING, kPolicyProxyPacUrl, prefs::kProxyPacUrl }, { Value::TYPE_STRING, kPolicyProxyBypassList, prefs::kProxyBypassList } }; /* static */ -ConfigurationPolicyProvider::StaticPolicyValueMap -ConfigurationPolicyPrefStore::GetChromePolicyValueMap() { - static ConfigurationPolicyProvider::StaticPolicyValueMap::Entry entries[] = { - { ConfigurationPolicyStore::kPolicyHomePage, - Value::TYPE_STRING, key::kHomepageLocation }, - { ConfigurationPolicyStore::kPolicyHomepageIsNewTabPage, - Value::TYPE_BOOLEAN, key::kHomepageIsNewTabPage }, - { ConfigurationPolicyStore::kPolicyRestoreOnStartup, - Value::TYPE_INTEGER, key::kRestoreOnStartup }, - { ConfigurationPolicyStore::kPolicyURLsToRestoreOnStartup, - Value::TYPE_LIST, key::kURLsToRestoreOnStartup }, - { ConfigurationPolicyStore::kPolicyDefaultSearchProviderEnabled, - Value::TYPE_BOOLEAN, key::kDefaultSearchProviderEnabled }, - { ConfigurationPolicyStore::kPolicyDefaultSearchProviderName, - Value::TYPE_STRING, key::kDefaultSearchProviderName }, - { ConfigurationPolicyStore::kPolicyDefaultSearchProviderKeyword, - Value::TYPE_STRING, key::kDefaultSearchProviderKeyword }, - { ConfigurationPolicyStore::kPolicyDefaultSearchProviderSearchURL, - Value::TYPE_STRING, key::kDefaultSearchProviderSearchURL }, - { ConfigurationPolicyStore::kPolicyDefaultSearchProviderSuggestURL, - Value::TYPE_STRING, key::kDefaultSearchProviderSuggestURL }, - { ConfigurationPolicyStore::kPolicyDefaultSearchProviderIconURL, - Value::TYPE_STRING, key::kDefaultSearchProviderIconURL }, - { ConfigurationPolicyStore::kPolicyDefaultSearchProviderEncodings, - Value::TYPE_STRING, key::kDefaultSearchProviderEncodings }, - { ConfigurationPolicyStore::kPolicyProxyServerMode, - Value::TYPE_INTEGER, key::kProxyServerMode }, - { ConfigurationPolicyStore::kPolicyProxyServer, - Value::TYPE_STRING, key::kProxyServer }, - { ConfigurationPolicyStore::kPolicyProxyPacUrl, - Value::TYPE_STRING, key::kProxyPacUrl }, - { ConfigurationPolicyStore::kPolicyProxyBypassList, - Value::TYPE_STRING, key::kProxyBypassList }, - { ConfigurationPolicyStore::kPolicyAlternateErrorPagesEnabled, - Value::TYPE_BOOLEAN, key::kAlternateErrorPagesEnabled }, - { ConfigurationPolicyStore::kPolicySearchSuggestEnabled, - Value::TYPE_BOOLEAN, key::kSearchSuggestEnabled }, - { ConfigurationPolicyStore::kPolicyDnsPrefetchingEnabled, - Value::TYPE_BOOLEAN, key::kDnsPrefetchingEnabled }, - { ConfigurationPolicyStore::kPolicyDisableSpdy, - Value::TYPE_BOOLEAN, key::kDisableSpdy }, - { ConfigurationPolicyStore::kPolicySafeBrowsingEnabled, - Value::TYPE_BOOLEAN, key::kSafeBrowsingEnabled }, - { ConfigurationPolicyStore::kPolicyMetricsReportingEnabled, - Value::TYPE_BOOLEAN, key::kMetricsReportingEnabled }, - { ConfigurationPolicyStore::kPolicyPasswordManagerEnabled, - Value::TYPE_BOOLEAN, key::kPasswordManagerEnabled }, - { ConfigurationPolicyStore::kPolicyPasswordManagerAllowShowPasswords, - Value::TYPE_BOOLEAN, key::kPasswordManagerAllowShowPasswords }, - { ConfigurationPolicyStore::kPolicyAutoFillEnabled, - Value::TYPE_BOOLEAN, key::kAutoFillEnabled }, - { ConfigurationPolicyStore::kPolicyDisabledPlugins, - Value::TYPE_LIST, key::kDisabledPlugins }, - { ConfigurationPolicyStore::kPolicyApplicationLocale, - Value::TYPE_STRING, key::kApplicationLocaleValue }, - { ConfigurationPolicyStore::kPolicySyncDisabled, - Value::TYPE_BOOLEAN, key::kSyncDisabled }, - { ConfigurationPolicyStore::kPolicyExtensionInstallAllowList, - Value::TYPE_LIST, key::kExtensionInstallAllowList }, - { ConfigurationPolicyStore::kPolicyExtensionInstallDenyList, - Value::TYPE_LIST, key::kExtensionInstallDenyList }, - { ConfigurationPolicyStore::kPolicyShowHomeButton, - Value::TYPE_BOOLEAN, key::kShowHomeButton }, - { ConfigurationPolicyStore::kPolicyPrintingEnabled, - Value::TYPE_BOOLEAN, key::kPrintingEnabled }, - { ConfigurationPolicyStore::kPolicyJavascriptEnabled, - Value::TYPE_BOOLEAN, key::kJavascriptEnabled }, - { ConfigurationPolicyStore::kPolicySavingBrowserHistoryDisabled, - Value::TYPE_BOOLEAN, key::kSavingBrowserHistoryDisabled }, +ConfigurationPolicyProvider::PolicyDefinitionList* +ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList() { + static ConfigurationPolicyProvider::PolicyDefinitionList::Entry entries[] = { + { kPolicyHomePage, Value::TYPE_STRING, key::kHomepageLocation }, + { kPolicyHomepageIsNewTabPage, Value::TYPE_BOOLEAN, + key::kHomepageIsNewTabPage }, + { kPolicyRestoreOnStartup, Value::TYPE_INTEGER, key::kRestoreOnStartup }, + { kPolicyURLsToRestoreOnStartup, Value::TYPE_LIST, + key::kURLsToRestoreOnStartup }, + { kPolicyDefaultSearchProviderEnabled, Value::TYPE_BOOLEAN, + key::kDefaultSearchProviderEnabled }, + { kPolicyDefaultSearchProviderName, Value::TYPE_STRING, + key::kDefaultSearchProviderName }, + { kPolicyDefaultSearchProviderKeyword, Value::TYPE_STRING, + key::kDefaultSearchProviderKeyword }, + { kPolicyDefaultSearchProviderSearchURL, Value::TYPE_STRING, + key::kDefaultSearchProviderSearchURL }, + { kPolicyDefaultSearchProviderSuggestURL, Value::TYPE_STRING, + key::kDefaultSearchProviderSuggestURL }, + { kPolicyDefaultSearchProviderIconURL, Value::TYPE_STRING, + key::kDefaultSearchProviderIconURL }, + { kPolicyDefaultSearchProviderEncodings, Value::TYPE_STRING, + key::kDefaultSearchProviderEncodings }, + { kPolicyProxyServerMode, Value::TYPE_INTEGER, key::kProxyServerMode }, + { kPolicyProxyServer, Value::TYPE_STRING, key::kProxyServer }, + { kPolicyProxyPacUrl, Value::TYPE_STRING, key::kProxyPacUrl }, + { kPolicyProxyBypassList, Value::TYPE_STRING, key::kProxyBypassList }, + { kPolicyAlternateErrorPagesEnabled, Value::TYPE_BOOLEAN, + key::kAlternateErrorPagesEnabled }, + { kPolicySearchSuggestEnabled, Value::TYPE_BOOLEAN, + key::kSearchSuggestEnabled }, + { kPolicyDnsPrefetchingEnabled, Value::TYPE_BOOLEAN, + key::kDnsPrefetchingEnabled }, + { kPolicyDisableSpdy, Value::TYPE_BOOLEAN, key::kDisableSpdy }, + { kPolicySafeBrowsingEnabled, Value::TYPE_BOOLEAN, + key::kSafeBrowsingEnabled }, + { kPolicyMetricsReportingEnabled, Value::TYPE_BOOLEAN, + key::kMetricsReportingEnabled }, + { kPolicyPasswordManagerEnabled, Value::TYPE_BOOLEAN, + key::kPasswordManagerEnabled }, + { kPolicyPasswordManagerAllowShowPasswords, Value::TYPE_BOOLEAN, + key::kPasswordManagerAllowShowPasswords }, + { kPolicyAutoFillEnabled, Value::TYPE_BOOLEAN, key::kAutoFillEnabled }, + { kPolicyDisabledPlugins, Value::TYPE_LIST, key::kDisabledPlugins }, + { kPolicyApplicationLocale, Value::TYPE_STRING, + key::kApplicationLocaleValue }, + { kPolicySyncDisabled, Value::TYPE_BOOLEAN, key::kSyncDisabled }, + { kPolicyExtensionInstallAllowList, Value::TYPE_LIST, + key::kExtensionInstallAllowList }, + { kPolicyExtensionInstallDenyList, Value::TYPE_LIST, + key::kExtensionInstallDenyList }, + { kPolicyExtensionInstallForceList, Value::TYPE_LIST, + key::kExtensionInstallForceList }, + { kPolicyShowHomeButton, Value::TYPE_BOOLEAN, key::kShowHomeButton }, + { kPolicyPrintingEnabled, Value::TYPE_BOOLEAN, key::kPrintingEnabled }, + { kPolicyJavascriptEnabled, Value::TYPE_BOOLEAN, key::kJavascriptEnabled }, + { kPolicySavingBrowserHistoryDisabled, Value::TYPE_BOOLEAN, + key::kSavingBrowserHistoryDisabled }, + { kPolicyDeveloperToolsDisabled, Value::TYPE_BOOLEAN, + key::kDeveloperToolsDisabled }, + { kPolicyBlockThirdPartyCookies, Value::TYPE_BOOLEAN, + key::kBlockThirdPartyCookies }, #if defined(OS_CHROMEOS) - { ConfigurationPolicyStore::kPolicyChromeOsLockOnIdleSuspend, - Value::TYPE_BOOLEAN, key::kChromeOsLockOnIdleSuspend }, + { kPolicyChromeOsLockOnIdleSuspend, Value::TYPE_BOOLEAN, + key::kChromeOsLockOnIdleSuspend }, #endif }; - ConfigurationPolicyProvider::StaticPolicyValueMap map = { - arraysize(entries), - entries + static ConfigurationPolicyProvider::PolicyDefinitionList policy_list = { + entries, + entries + arraysize(entries), }; - return map; -} - -void ConfigurationPolicyPrefStore::GetProxyPreferenceSet( - ProxyPreferenceSet* proxy_pref_set) { - proxy_pref_set->clear(); - for (size_t current = 0; current < arraysize(proxy_policy_map_); ++current) { - proxy_pref_set->insert(proxy_policy_map_[current].preference_path); - } - proxy_pref_set->insert(prefs::kNoProxyServer); - proxy_pref_set->insert(prefs::kProxyAutoDetect); + return &policy_list; } ConfigurationPolicyPrefStore::ConfigurationPolicyPrefStore( @@ -272,13 +268,14 @@ PrefStore::PrefReadError ConfigurationPolicyPrefStore::ReadPrefs() { proxy_configuration_specified_ = false; lower_priority_proxy_settings_overridden_ = false; - bool success = (provider_ == NULL || provider_->Provide(this)); + const bool success = (provider_ == NULL || provider_->Provide(this)); FinalizeDefaultSearchPolicySettings(); return success ? PrefStore::PREF_READ_ERROR_NONE : - PrefStore::PREF_READ_ERROR_OTHER; + PrefStore::PREF_READ_ERROR_OTHER; } -void ConfigurationPolicyPrefStore::Apply(PolicyType policy, Value* value) { +void ConfigurationPolicyPrefStore::Apply(ConfigurationPolicyType policy, + Value* value) { if (ApplyProxyPolicy(policy, value)) return; @@ -288,12 +285,12 @@ void ConfigurationPolicyPrefStore::Apply(PolicyType policy, Value* value) { if (ApplyAutoFillPolicy(policy, value)) return; - if (ApplyPolicyFromMap(policy, value, default_search_policy_map_, - arraysize(default_search_policy_map_))) + if (ApplyPolicyFromMap(policy, value, kDefaultSearchPolicyMap, + arraysize(kDefaultSearchPolicyMap))) return; - if (ApplyPolicyFromMap(policy, value, simple_policy_map_, - arraysize(simple_policy_map_))) + if (ApplyPolicyFromMap(policy, value, kSimplePolicyMap, + arraysize(kSimplePolicyMap))) return; // Other policy implementations go here. @@ -317,9 +314,22 @@ ConfigurationPolicyPrefStore::CreateRecommendedPolicyPrefStore() { return new ConfigurationPolicyPrefStore(keeper->recommended_provider()); } +// static +void ConfigurationPolicyPrefStore::GetProxyPreferenceSet( + ProxyPreferenceSet* proxy_pref_set) { + proxy_pref_set->clear(); + for (size_t current = 0; current < arraysize(kProxyPolicyMap); ++current) { + proxy_pref_set->insert(kProxyPolicyMap[current].preference_path); + } + proxy_pref_set->insert(prefs::kNoProxyServer); + proxy_pref_set->insert(prefs::kProxyAutoDetect); +} + const ConfigurationPolicyPrefStore::PolicyToPreferenceMapEntry* -ConfigurationPolicyPrefStore::FindPolicyInMap(PolicyType policy, - const PolicyToPreferenceMapEntry* map, int table_size) { +ConfigurationPolicyPrefStore::FindPolicyInMap( + ConfigurationPolicyType policy, + const PolicyToPreferenceMapEntry* map, + int table_size) const { for (int i = 0; i < table_size; ++i) { if (map[i].policy_type == policy) return map + i; @@ -329,36 +339,41 @@ ConfigurationPolicyPrefStore::FindPolicyInMap(PolicyType policy, bool ConfigurationPolicyPrefStore::RemovePreferencesOfMap( const PolicyToPreferenceMapEntry* map, int table_size) { - bool found_one = false; + bool found_any = false; for (int i = 0; i < table_size; ++i) { if (prefs_->Remove(map[i].preference_path, NULL)) - found_one = true; + found_any = true; } - return found_one; + return found_any; } -bool ConfigurationPolicyPrefStore::ApplyPolicyFromMap(PolicyType policy, - Value* value, const PolicyToPreferenceMapEntry map[], int size) { - const PolicyToPreferenceMapEntry* end = map + size; - for (const PolicyToPreferenceMapEntry* current = map; - current != end; ++current) { - if (current->policy_type == policy) { - DCHECK(current->value_type == value->GetType()); - prefs_->Set(current->preference_path, value); +bool ConfigurationPolicyPrefStore::ApplyPolicyFromMap( + ConfigurationPolicyType policy, + Value* value, + const PolicyToPreferenceMapEntry* map, + int size) { + for (int current = 0; current < size; ++current) { + if (map[current].policy_type == policy) { + DCHECK_EQ(map[current].value_type, value->GetType()) + << "mismatch in provided and expected policy value for preferences" + << map[current].preference_path << ". expected = " + << map[current].value_type << ", actual = "<< value->GetType(); + prefs_->Set(map[current].preference_path, value); return true; } } return false; } -bool ConfigurationPolicyPrefStore::ApplyProxyPolicy(PolicyType policy, - Value* value) { +bool ConfigurationPolicyPrefStore::ApplyProxyPolicy( + ConfigurationPolicyType policy, + Value* value) { bool result = false; bool warn_about_proxy_disable_config = false; bool warn_about_proxy_system_config = false; const PolicyToPreferenceMapEntry* match_entry = - FindPolicyInMap(policy, proxy_policy_map_, arraysize(proxy_policy_map_)); + FindPolicyInMap(policy, kProxyPolicyMap, arraysize(kProxyPolicyMap)); // When the first proxy-related policy is applied, ALL proxy-related // preferences that have been set by command-line switches, extensions, @@ -369,7 +384,7 @@ bool ConfigurationPolicyPrefStore::ApplyProxyPolicy(PolicyType policy, // policy. if (!lower_priority_proxy_settings_overridden_ && (match_entry || - policy == ConfigurationPolicyPrefStore::kPolicyProxyServerMode)) { + policy == kPolicyProxyServerMode)) { ProxyPreferenceSet proxy_preference_set; GetProxyPreferenceSet(&proxy_preference_set); for (ProxyPreferenceSet::const_iterator i = proxy_preference_set.begin(); @@ -380,25 +395,25 @@ bool ConfigurationPolicyPrefStore::ApplyProxyPolicy(PolicyType policy, } // Translate the proxy policy into preferences. - if (policy == ConfigurationPolicyStore::kPolicyProxyServerMode) { + if (policy == kPolicyProxyServerMode) { int int_value; bool proxy_auto_detect = false; if (value->GetAsInteger(&int_value)) { result = true; switch (int_value) { - case ConfigurationPolicyStore::kPolicyNoProxyServerMode: + case kPolicyNoProxyServerMode: if (!proxy_disabled_) { if (proxy_configuration_specified_) warn_about_proxy_disable_config = true; proxy_disabled_ = true; } break; - case ConfigurationPolicyStore::kPolicyAutoDetectProxyMode: + case kPolicyAutoDetectProxyMode: proxy_auto_detect = true; break; - case ConfigurationPolicyStore::kPolicyManuallyConfiguredProxyMode: + case kPolicyManuallyConfiguredProxyMode: break; - case ConfigurationPolicyStore::kPolicyUseSystemProxyMode: + case kPolicyUseSystemProxyMode: if (!use_system_proxy_) { if (proxy_configuration_specified_) warn_about_proxy_system_config = true; @@ -455,9 +470,9 @@ bool ConfigurationPolicyPrefStore::ApplyProxyPolicy(PolicyType policy, return result; } -bool ConfigurationPolicyPrefStore::ApplySyncPolicy(PolicyType policy, - Value* value) { - if (policy == ConfigurationPolicyStore::kPolicySyncDisabled) { +bool ConfigurationPolicyPrefStore::ApplySyncPolicy( + ConfigurationPolicyType policy, Value* value) { + if (policy == kPolicySyncDisabled) { bool disable_sync; if (value->GetAsBoolean(&disable_sync) && disable_sync) prefs_->Set(prefs::kSyncManaged, value); @@ -468,9 +483,9 @@ bool ConfigurationPolicyPrefStore::ApplySyncPolicy(PolicyType policy, return false; } -bool ConfigurationPolicyPrefStore::ApplyAutoFillPolicy(PolicyType policy, - Value* value) { - if (policy == ConfigurationPolicyStore::kPolicyAutoFillEnabled) { +bool ConfigurationPolicyPrefStore::ApplyAutoFillPolicy( + ConfigurationPolicyType policy, Value* value) { + if (policy == kPolicyAutoFillEnabled) { bool auto_fill_enabled; if (value->GetAsBoolean(&auto_fill_enabled) && !auto_fill_enabled) prefs_->Set(prefs::kAutoFillEnabled, Value::CreateBooleanValue(false)); @@ -487,6 +502,8 @@ void ConfigurationPolicyPrefStore::EnsureStringPrefExists( prefs_->SetString(path, value); } +namespace { + // Implementation of SearchTermsData just for validation. class SearchTermsDataForValidation : public SearchTermsData { public: @@ -501,25 +518,26 @@ class SearchTermsDataForValidation : public SearchTermsData { } #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD) virtual std::wstring GetRlzParameterValue() const { - return L""; + return std::wstring(); } #endif private: DISALLOW_COPY_AND_ASSIGN(SearchTermsDataForValidation); }; +} // namepsace void ConfigurationPolicyPrefStore::FinalizeDefaultSearchPolicySettings() { bool enabled = true; if (prefs_->GetBoolean(prefs::kDefaultSearchProviderEnabled, &enabled) && !enabled) { // If default search is disabled, we ignore the other fields. - prefs_->SetString(prefs::kDefaultSearchProviderName, ""); - prefs_->SetString(prefs::kDefaultSearchProviderSearchURL, ""); - prefs_->SetString(prefs::kDefaultSearchProviderSuggestURL, ""); - prefs_->SetString(prefs::kDefaultSearchProviderIconURL, ""); - prefs_->SetString(prefs::kDefaultSearchProviderEncodings, ""); - prefs_->SetString(prefs::kDefaultSearchProviderKeyword, ""); + prefs_->SetString(prefs::kDefaultSearchProviderName, std::string()); + prefs_->SetString(prefs::kDefaultSearchProviderSearchURL, std::string()); + prefs_->SetString(prefs::kDefaultSearchProviderSuggestURL, std::string()); + prefs_->SetString(prefs::kDefaultSearchProviderIconURL, std::string()); + prefs_->SetString(prefs::kDefaultSearchProviderEncodings, std::string()); + prefs_->SetString(prefs::kDefaultSearchProviderKeyword, std::string()); return; } std::string search_url; @@ -527,7 +545,7 @@ void ConfigurationPolicyPrefStore::FinalizeDefaultSearchPolicySettings() { if (prefs_->GetString(prefs::kDefaultSearchProviderSearchURL, &search_url) && !search_url.empty()) { SearchTermsDataForValidation search_terms_data; - TemplateURLRef search_url_ref(search_url, 0, 0); + const TemplateURLRef search_url_ref(search_url, 0, 0); // It must support replacement (which implies it is valid). if (search_url_ref.SupportsReplacementUsingTermsData(search_terms_data)) { // The other entries are optional. Just make sure that they are all @@ -545,14 +563,15 @@ void ConfigurationPolicyPrefStore::FinalizeDefaultSearchPolicySettings() { GURL(search_url).host()); // And clear the IDs since these are not specified via policy. - prefs_->SetString(prefs::kDefaultSearchProviderID, ""); - prefs_->SetString(prefs::kDefaultSearchProviderPrepopulateID, ""); + prefs_->SetString(prefs::kDefaultSearchProviderID, std::string()); + prefs_->SetString(prefs::kDefaultSearchProviderPrepopulateID, + std::string()); return; } } // Required entries are not there. Remove any related entries. - RemovePreferencesOfMap(default_search_policy_map_, - arraysize(default_search_policy_map_)); + RemovePreferencesOfMap(kDefaultSearchPolicyMap, + arraysize(kDefaultSearchPolicyMap)); } } // namespace policy diff --git a/chrome/browser/policy/configuration_policy_pref_store.h b/chrome/browser/policy/configuration_policy_pref_store.h index 8f8167d..066952b 100644 --- a/chrome/browser/policy/configuration_policy_pref_store.h +++ b/chrome/browser/policy/configuration_policy_pref_store.h @@ -13,7 +13,7 @@ #include "base/scoped_ptr.h" #include "base/values.h" #include "chrome/browser/policy/configuration_policy_provider.h" -#include "chrome/browser/policy/configuration_policy_store.h" +#include "chrome/browser/policy/configuration_policy_store_interface.h" #include "chrome/common/pref_store.h" namespace policy { @@ -21,19 +21,21 @@ namespace policy { // An implementation of the |PrefStore| that holds a Dictionary // created through applied policy. class ConfigurationPolicyPrefStore : public PrefStore, - public ConfigurationPolicyStore { + public ConfigurationPolicyStoreInterface { public: + typedef std::set<const char*> ProxyPreferenceSet; + // The ConfigurationPolicyPrefStore does not take ownership of the // passed-in |provider|. explicit ConfigurationPolicyPrefStore(ConfigurationPolicyProvider* provider); - virtual ~ConfigurationPolicyPrefStore() { } + virtual ~ConfigurationPolicyPrefStore() {} // PrefStore methods: virtual PrefReadError ReadPrefs(); - virtual DictionaryValue* prefs() { return prefs_.get(); } + virtual DictionaryValue* prefs() const { return prefs_.get(); } // ConfigurationPolicyStore methods: - virtual void Apply(PolicyType setting, Value* value); + virtual void Apply(ConfigurationPolicyType setting, Value* value); // Creates a ConfigurationPolicyPrefStore that reads managed policy. static ConfigurationPolicyPrefStore* CreateManagedPolicyPrefStore(); @@ -41,11 +43,9 @@ class ConfigurationPolicyPrefStore : public PrefStore, // Creates a ConfigurationPolicyPrefStore that reads recommended policy. static ConfigurationPolicyPrefStore* CreateRecommendedPolicyPrefStore(); - // Returns the default policy value map for Chrome. - static ConfigurationPolicyProvider::StaticPolicyValueMap - GetChromePolicyValueMap(); - - typedef std::set<const char*> ProxyPreferenceSet; + // Returns the default policy definition list for Chrome. + static ConfigurationPolicyProvider::PolicyDefinitionList* + GetChromePolicyDefinitionList(); // Returns the set of preference paths that can be affected by a proxy // policy. @@ -57,15 +57,15 @@ class ConfigurationPolicyPrefStore : public PrefStore, // has an entry in |simple_policy_map_| with the following type. struct PolicyToPreferenceMapEntry { Value::ValueType value_type; - PolicyType policy_type; + ConfigurationPolicyType policy_type; const char* preference_path; // A DictionaryValue path, not a file path. }; - static const PolicyToPreferenceMapEntry simple_policy_map_[]; - static const PolicyToPreferenceMapEntry proxy_policy_map_[]; - static const PolicyToPreferenceMapEntry default_search_policy_map_[]; - static const ConfigurationPolicyProvider::StaticPolicyValueMap - policy_value_map_; + static const PolicyToPreferenceMapEntry kSimplePolicyMap[]; + static const PolicyToPreferenceMapEntry kProxyPolicyMap[]; + static const PolicyToPreferenceMapEntry kDefaultSearchPolicyMap[]; + static const ConfigurationPolicyProvider::PolicyDefinitionList + kPolicyDefinitionList; ConfigurationPolicyProvider* provider_; scoped_ptr<DictionaryValue> prefs_; @@ -89,29 +89,33 @@ class ConfigurationPolicyPrefStore : public PrefStore, bool use_system_proxy_; // Returns the map entry that corresponds to |policy| in the map. - const PolicyToPreferenceMapEntry* FindPolicyInMap(PolicyType policy, - const PolicyToPreferenceMapEntry* map, int size); + const PolicyToPreferenceMapEntry* FindPolicyInMap( + ConfigurationPolicyType policy, + const PolicyToPreferenceMapEntry* map, + int size) const; // Remove the preferences found in the map from |prefs_|. Returns true if // any such preferences were found and removed. bool RemovePreferencesOfMap(const PolicyToPreferenceMapEntry* map, int table_size); - bool ApplyPolicyFromMap(PolicyType policy, Value* value, - const PolicyToPreferenceMapEntry map[], int size); + bool ApplyPolicyFromMap(ConfigurationPolicyType policy, + Value* value, + const PolicyToPreferenceMapEntry* map, + int size); // Processes proxy-specific policies. Returns true if the specified policy // is a proxy-related policy. ApplyProxyPolicy assumes the ownership // of |value| in the case that the policy is proxy-specific. - bool ApplyProxyPolicy(PolicyType policy, Value* value); + bool ApplyProxyPolicy(ConfigurationPolicyType policy, Value* value); // Handles sync-related policies. Returns true if the policy was handled. // Assumes ownership of |value| in that case. - bool ApplySyncPolicy(PolicyType policy, Value* value); + bool ApplySyncPolicy(ConfigurationPolicyType policy, Value* value); // Handles policies that affect AutoFill. Returns true if the policy was // handled and assumes ownership of |value| in that case. - bool ApplyAutoFillPolicy(PolicyType policy, Value* value); + bool ApplyAutoFillPolicy(ConfigurationPolicyType policy, Value* value); // Make sure that the |path| if present in |prefs_|. If not, set it to // a blank string. diff --git a/chrome/browser/policy/configuration_policy_pref_store_unittest.cc b/chrome/browser/policy/configuration_policy_pref_store_unittest.cc index ca4d51e..0809c00 100644 --- a/chrome/browser/policy/configuration_policy_pref_store_unittest.cc +++ b/chrome/browser/policy/configuration_policy_pref_store_unittest.cc @@ -15,15 +15,15 @@ namespace policy { // Holds a set of test parameters, consisting of pref name and policy type. class TypeAndName { public: - TypeAndName(ConfigurationPolicyStore::PolicyType type, const char* pref_name) + TypeAndName(ConfigurationPolicyType type, const char* pref_name) : type_(type), pref_name_(pref_name) {} - ConfigurationPolicyStore::PolicyType type() const { return type_; } + ConfigurationPolicyType type() const { return type_; } const char* pref_name() const { return pref_name_; } private: - ConfigurationPolicyStore::PolicyType type_; + ConfigurationPolicyType type_; const char* pref_name_; }; @@ -47,7 +47,7 @@ TEST_P(ConfigurationPolicyPrefStoreListTest, SetValue) { ListValue* list = NULL; EXPECT_TRUE(store.prefs()->GetList(GetParam().pref_name(), &list)); ListValue::const_iterator current(list->begin()); - ListValue::const_iterator end(list->end()); + const ListValue::const_iterator end(list->end()); ASSERT_TRUE(current != end); std::string value; (*current)->GetAsString(&value); @@ -64,13 +64,13 @@ INSTANTIATE_TEST_CASE_P( ConfigurationPolicyPrefStoreListTestInstance, ConfigurationPolicyPrefStoreListTest, testing::Values( - TypeAndName(ConfigurationPolicyStore::kPolicyURLsToRestoreOnStartup, + TypeAndName(kPolicyURLsToRestoreOnStartup, prefs::kURLsToRestoreOnStartup), - TypeAndName(ConfigurationPolicyStore::kPolicyExtensionInstallAllowList, + TypeAndName(kPolicyExtensionInstallAllowList, prefs::kExtensionInstallAllowList), - TypeAndName(ConfigurationPolicyStore::kPolicyExtensionInstallDenyList, + TypeAndName(kPolicyExtensionInstallDenyList, prefs::kExtensionInstallDenyList), - TypeAndName(ConfigurationPolicyStore::kPolicyDisabledPlugins, + TypeAndName(kPolicyDisabledPlugins, prefs::kPluginsPluginsBlacklist))); // Test cases for string-valued policy settings. @@ -97,15 +97,15 @@ INSTANTIATE_TEST_CASE_P( ConfigurationPolicyPrefStoreStringTestInstance, ConfigurationPolicyPrefStoreStringTest, testing::Values( - TypeAndName(ConfigurationPolicyStore::kPolicyHomePage, + TypeAndName(kPolicyHomePage, prefs::kHomePage), - TypeAndName(ConfigurationPolicyStore::kPolicyProxyServer, + TypeAndName(kPolicyProxyServer, prefs::kProxyServer), - TypeAndName(ConfigurationPolicyStore::kPolicyProxyPacUrl, + TypeAndName(kPolicyProxyPacUrl, prefs::kProxyPacUrl), - TypeAndName(ConfigurationPolicyStore::kPolicyProxyBypassList, + TypeAndName(kPolicyProxyBypassList, prefs::kProxyBypassList), - TypeAndName(ConfigurationPolicyStore::kPolicyApplicationLocale, + TypeAndName(kPolicyApplicationLocale, prefs::kApplicationLocale))); // Test cases for boolean-valued policy settings. @@ -136,33 +136,31 @@ INSTANTIATE_TEST_CASE_P( ConfigurationPolicyPrefStoreBooleanTestInstance, ConfigurationPolicyPrefStoreBooleanTest, testing::Values( - TypeAndName(ConfigurationPolicyStore::kPolicyHomepageIsNewTabPage, + TypeAndName(kPolicyHomepageIsNewTabPage, prefs::kHomePageIsNewTabPage), - TypeAndName(ConfigurationPolicyStore::kPolicyAlternateErrorPagesEnabled, + TypeAndName(kPolicyAlternateErrorPagesEnabled, prefs::kAlternateErrorPagesEnabled), - TypeAndName(ConfigurationPolicyStore::kPolicySearchSuggestEnabled, + TypeAndName(kPolicySearchSuggestEnabled, prefs::kSearchSuggestEnabled), - TypeAndName(ConfigurationPolicyStore::kPolicyDnsPrefetchingEnabled, + TypeAndName(kPolicyDnsPrefetchingEnabled, prefs::kDnsPrefetchingEnabled), - TypeAndName(ConfigurationPolicyStore::kPolicyDisableSpdy, + TypeAndName(kPolicyDisableSpdy, prefs::kDisableSpdy), - TypeAndName(ConfigurationPolicyStore::kPolicySafeBrowsingEnabled, + TypeAndName(kPolicySafeBrowsingEnabled, prefs::kSafeBrowsingEnabled), - TypeAndName(ConfigurationPolicyStore::kPolicyMetricsReportingEnabled, + TypeAndName(kPolicyMetricsReportingEnabled, prefs::kMetricsReportingEnabled), - TypeAndName(ConfigurationPolicyStore::kPolicyPasswordManagerEnabled, + TypeAndName(kPolicyPasswordManagerEnabled, prefs::kPasswordManagerEnabled), - TypeAndName(ConfigurationPolicyStore:: - kPolicyPasswordManagerAllowShowPasswords, + TypeAndName(kPolicyPasswordManagerAllowShowPasswords, prefs::kPasswordManagerAllowShowPasswords), - TypeAndName(ConfigurationPolicyStore::kPolicyShowHomeButton, + TypeAndName(kPolicyShowHomeButton, prefs::kShowHomeButton), - TypeAndName(ConfigurationPolicyStore::kPolicyPrintingEnabled, + TypeAndName(kPolicyPrintingEnabled, prefs::kPrintingEnabled), - TypeAndName(ConfigurationPolicyStore::kPolicyJavascriptEnabled, + TypeAndName(kPolicyJavascriptEnabled, prefs::kWebKitJavascriptEnabled), - TypeAndName(ConfigurationPolicyStore:: - kPolicySavingBrowserHistoryDisabled, + TypeAndName(kPolicySavingBrowserHistoryDisabled, prefs::kSavingBrowserHistoryDisabled))); #if defined(OS_CHROMEOS) @@ -170,8 +168,7 @@ INSTANTIATE_TEST_CASE_P( CrosConfigurationPolicyPrefStoreBooleanTestInstance, ConfigurationPolicyPrefStoreBooleanTest, testing::Values( - TypeAndName(ConfigurationPolicyStore:: - kPolicyChromeOsLockOnIdleSuspend, + TypeAndName(kPolicyChromeOsLockOnIdleSuspend, prefs::kEnableScreenLock))); #endif // defined(OS_CHROMEOS) @@ -198,7 +195,7 @@ INSTANTIATE_TEST_CASE_P( ConfigurationPolicyPrefStoreIntegerTestInstance, ConfigurationPolicyPrefStoreIntegerTest, testing::Values( - TypeAndName(ConfigurationPolicyStore::kPolicyRestoreOnStartup, + TypeAndName(kPolicyRestoreOnStartup, prefs::kRestoreOnStartup))); // Test cases for the proxy policy settings. @@ -208,15 +205,15 @@ class ConfigurationPolicyPrefStoreProxyTest : public testing::Test { TEST_F(ConfigurationPolicyPrefStoreProxyTest, ManualOptions) { scoped_ptr<MockConfigurationPolicyProvider> provider( new MockConfigurationPolicyProvider()); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyBypassList, - Value::CreateStringValue("http://chromium.org/override")); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyPacUrl, - Value::CreateStringValue("http://chromium.org/proxy.pac")); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyServer, - Value::CreateStringValue("chromium.org")); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyServerMode, - Value::CreateIntegerValue( - ConfigurationPolicyStore::kPolicyManuallyConfiguredProxyMode)); + provider->AddPolicy(kPolicyProxyBypassList, + Value::CreateStringValue("http://chromium.org/override")); + provider->AddPolicy(kPolicyProxyPacUrl, + Value::CreateStringValue("http://short.org/proxy.pac")); + provider->AddPolicy(kPolicyProxyServer, + Value::CreateStringValue("chromium.org")); + provider->AddPolicy(kPolicyProxyServerMode, + Value::CreateIntegerValue( + kPolicyManuallyConfiguredProxyMode)); ConfigurationPolicyPrefStore store(provider.get()); EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE); @@ -226,7 +223,7 @@ TEST_F(ConfigurationPolicyPrefStoreProxyTest, ManualOptions) { &string_result)); EXPECT_EQ("http://chromium.org/override", string_result); EXPECT_TRUE(store.prefs()->GetString(prefs::kProxyPacUrl, &string_result)); - EXPECT_EQ("http://chromium.org/proxy.pac", string_result); + EXPECT_EQ("http://short.org/proxy.pac", string_result); EXPECT_TRUE(store.prefs()->GetString(prefs::kProxyServer, &string_result)); EXPECT_EQ("chromium.org", string_result); bool bool_result; @@ -239,11 +236,11 @@ TEST_F(ConfigurationPolicyPrefStoreProxyTest, ManualOptions) { TEST_F(ConfigurationPolicyPrefStoreProxyTest, NoProxy) { scoped_ptr<MockConfigurationPolicyProvider> provider( new MockConfigurationPolicyProvider()); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyBypassList, - Value::CreateStringValue("http://chromium.org/override")); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyServerMode, - Value::CreateIntegerValue( - ConfigurationPolicyStore::kPolicyNoProxyServerMode)); + provider->AddPolicy(kPolicyProxyBypassList, + Value::CreateStringValue("http://chromium.org/override")); + provider->AddPolicy(kPolicyProxyServerMode, + Value::CreateIntegerValue( + kPolicyNoProxyServerMode)); ConfigurationPolicyPrefStore store(provider.get()); EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE); @@ -263,11 +260,11 @@ TEST_F(ConfigurationPolicyPrefStoreProxyTest, NoProxy) { TEST_F(ConfigurationPolicyPrefStoreProxyTest, NoProxyReversedApplyOrder) { scoped_ptr<MockConfigurationPolicyProvider> provider( new MockConfigurationPolicyProvider()); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyServerMode, - Value::CreateIntegerValue( - ConfigurationPolicyStore::kPolicyNoProxyServerMode)); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyBypassList, - Value::CreateStringValue("http://chromium.org/override")); + provider->AddPolicy(kPolicyProxyServerMode, + Value::CreateIntegerValue( + kPolicyNoProxyServerMode)); + provider->AddPolicy(kPolicyProxyBypassList, + Value::CreateStringValue("http://chromium.org/override")); ConfigurationPolicyPrefStore store(provider.get()); EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE); @@ -287,9 +284,9 @@ TEST_F(ConfigurationPolicyPrefStoreProxyTest, NoProxyReversedApplyOrder) { TEST_F(ConfigurationPolicyPrefStoreProxyTest, AutoDetect) { scoped_ptr<MockConfigurationPolicyProvider> provider( new MockConfigurationPolicyProvider()); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyServerMode, - Value::CreateIntegerValue( - ConfigurationPolicyStore::kPolicyAutoDetectProxyMode)); + provider->AddPolicy(kPolicyProxyServerMode, + Value::CreateIntegerValue( + kPolicyAutoDetectProxyMode)); ConfigurationPolicyPrefStore store(provider.get()); EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE); @@ -309,11 +306,11 @@ TEST_F(ConfigurationPolicyPrefStoreProxyTest, AutoDetect) { TEST_F(ConfigurationPolicyPrefStoreProxyTest, UseSystem) { scoped_ptr<MockConfigurationPolicyProvider> provider( new MockConfigurationPolicyProvider()); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyBypassList, - Value::CreateStringValue("http://chromium.org/override")); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyServerMode, - Value::CreateIntegerValue( - ConfigurationPolicyStore::kPolicyUseSystemProxyMode)); + provider->AddPolicy(kPolicyProxyBypassList, + Value::CreateStringValue("http://chromium.org/override")); + provider->AddPolicy(kPolicyProxyServerMode, + Value::CreateIntegerValue( + kPolicyUseSystemProxyMode)); ConfigurationPolicyPrefStore store(provider.get()); EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE); @@ -332,11 +329,11 @@ TEST_F(ConfigurationPolicyPrefStoreProxyTest, UseSystem) { TEST_F(ConfigurationPolicyPrefStoreProxyTest, UseSystemReversedApplyOrder) { scoped_ptr<MockConfigurationPolicyProvider> provider( new MockConfigurationPolicyProvider()); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyServerMode, - Value::CreateIntegerValue( - ConfigurationPolicyStore::kPolicyUseSystemProxyMode)); - provider->AddPolicy(ConfigurationPolicyStore::kPolicyProxyBypassList, - Value::CreateStringValue("http://chromium.org/override")); + provider->AddPolicy(kPolicyProxyServerMode, + Value::CreateIntegerValue( + kPolicyUseSystemProxyMode)); + provider->AddPolicy(kPolicyProxyBypassList, + Value::CreateStringValue("http://chromium.org/override")); ConfigurationPolicyPrefStore store(provider.get()); EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE); @@ -358,20 +355,20 @@ class ConfigurationPolicyPrefStoreDefaultSearchTest : public testing::Test { // Checks that if the policy for default search is valid, i.e. there's a // search URL, that all the elements have been given proper defaults. TEST_F(ConfigurationPolicyPrefStoreDefaultSearchTest, MinimallyDefined) { - const char* search_url = "http://test.com/search?t={searchTerms}"; + const char* const search_url = "http://test.com/search?t={searchTerms}"; scoped_ptr<MockConfigurationPolicyProvider> provider( new MockConfigurationPolicyProvider()); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEnabled, + kPolicyDefaultSearchProviderEnabled, Value::CreateBooleanValue(true)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSearchURL, + kPolicyDefaultSearchProviderSearchURL, Value::CreateStringValue(search_url)); ConfigurationPolicyPrefStore store(provider.get()); EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE); - DictionaryValue* prefs = store.prefs(); + const DictionaryValue* prefs = store.prefs(); std::string string_result; EXPECT_TRUE(prefs->GetString(prefs::kDefaultSearchProviderSearchURL, @@ -384,57 +381,57 @@ TEST_F(ConfigurationPolicyPrefStoreDefaultSearchTest, MinimallyDefined) { EXPECT_TRUE(prefs->GetString(prefs::kDefaultSearchProviderKeyword, &string_result)); - EXPECT_EQ(string_result, ""); + EXPECT_EQ(string_result, std::string()); EXPECT_TRUE(prefs->GetString(prefs::kDefaultSearchProviderSuggestURL, &string_result)); - EXPECT_EQ(string_result, ""); + EXPECT_EQ(string_result, std::string()); EXPECT_TRUE(prefs->GetString(prefs::kDefaultSearchProviderIconURL, &string_result)); - EXPECT_EQ(string_result, ""); + EXPECT_EQ(string_result, std::string()); EXPECT_TRUE(prefs->GetString(prefs::kDefaultSearchProviderEncodings, &string_result)); - EXPECT_EQ(string_result, ""); + EXPECT_EQ(string_result, std::string()); } // Checks that for a fully defined search policy, all elements have been // read properly. TEST_F(ConfigurationPolicyPrefStoreDefaultSearchTest, FullyDefined) { - const char* search_url = "http://test.com/search?t={searchTerms}"; - const char* suggest_url = "http://test.com/sugg?={searchTerms}"; - const char* icon_url = "http://test.com/icon.jpg"; - const char* name = "MyName"; - const char* keyword = "MyKeyword"; - const char* encodings = "UTF-16;UTF-8"; + const char* const search_url = "http://test.com/search?t={searchTerms}"; + const char* const suggest_url = "http://test.com/sugg?={searchTerms}"; + const char* const icon_url = "http://test.com/icon.jpg"; + const char* const name = "MyName"; + const char* const keyword = "MyKeyword"; + const char* const encodings = "UTF-16;UTF-8"; scoped_ptr<MockConfigurationPolicyProvider> provider( new MockConfigurationPolicyProvider()); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEnabled, + kPolicyDefaultSearchProviderEnabled, Value::CreateBooleanValue(true)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSearchURL, + kPolicyDefaultSearchProviderSearchURL, Value::CreateStringValue(search_url)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderName, + kPolicyDefaultSearchProviderName, Value::CreateStringValue(name)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderKeyword, + kPolicyDefaultSearchProviderKeyword, Value::CreateStringValue(keyword)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSuggestURL, + kPolicyDefaultSearchProviderSuggestURL, Value::CreateStringValue(suggest_url)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderIconURL, + kPolicyDefaultSearchProviderIconURL, Value::CreateStringValue(icon_url)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEncodings, + kPolicyDefaultSearchProviderEncodings, Value::CreateStringValue(encodings)); ConfigurationPolicyPrefStore store(provider.get()); EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE); - DictionaryValue* prefs = store.prefs(); + const DictionaryValue* prefs = store.prefs(); std::string result_search_url; EXPECT_TRUE(prefs->GetString(prefs::kDefaultSearchProviderSearchURL, @@ -470,103 +467,103 @@ TEST_F(ConfigurationPolicyPrefStoreDefaultSearchTest, FullyDefined) { // Checks that if the default search policy is missing, that no elements of the // default search policy will be present. TEST_F(ConfigurationPolicyPrefStoreDefaultSearchTest, MissingUrl) { - const char* suggest_url = "http://test.com/sugg?t={searchTerms}"; - const char* icon_url = "http://test.com/icon.jpg"; - const char* name = "MyName"; - const char* keyword = "MyKeyword"; - const char* encodings = "UTF-16;UTF-8"; + const char* const suggest_url = "http://test.com/sugg?t={searchTerms}"; + const char* const icon_url = "http://test.com/icon.jpg"; + const char* const name = "MyName"; + const char* const keyword = "MyKeyword"; + const char* const encodings = "UTF-16;UTF-8"; scoped_ptr<MockConfigurationPolicyProvider> provider( new MockConfigurationPolicyProvider()); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEnabled, + kPolicyDefaultSearchProviderEnabled, Value::CreateBooleanValue(true)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderName, + kPolicyDefaultSearchProviderName, Value::CreateStringValue(name)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderKeyword, + kPolicyDefaultSearchProviderKeyword, Value::CreateStringValue(keyword)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSuggestURL, + kPolicyDefaultSearchProviderSuggestURL, Value::CreateStringValue(suggest_url)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderIconURL, + kPolicyDefaultSearchProviderIconURL, Value::CreateStringValue(icon_url)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEncodings, + kPolicyDefaultSearchProviderEncodings, Value::CreateStringValue(encodings)); ConfigurationPolicyPrefStore store(provider.get()); EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE); - DictionaryValue* prefs = store.prefs(); + const DictionaryValue* prefs = store.prefs(); std::string string_result; EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderSearchURL, - &string_result)); + &string_result)); EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderName, - &string_result)); + &string_result)); EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderKeyword, - &string_result)); + &string_result)); EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderSuggestURL, - &string_result)); + &string_result)); EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderIconURL, - &string_result)); + &string_result)); EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderEncodings, - &string_result)); + &string_result)); } // Checks that if the default search policy is invalid, that no elements of the // default search policy will be present. TEST_F(ConfigurationPolicyPrefStoreDefaultSearchTest, Invalid) { - const char* bad_search_url = "http://test.com/noSearchTerms"; - const char* suggest_url = "http://test.com/sugg?t={searchTerms}"; - const char* icon_url = "http://test.com/icon.jpg"; - const char* name = "MyName"; - const char* keyword = "MyKeyword"; - const char* encodings = "UTF-16;UTF-8"; + const char* const bad_search_url = "http://test.com/noSearchTerms"; + const char* const suggest_url = "http://test.com/sugg?t={searchTerms}"; + const char* const icon_url = "http://test.com/icon.jpg"; + const char* const name = "MyName"; + const char* const keyword = "MyKeyword"; + const char* const encodings = "UTF-16;UTF-8"; scoped_ptr<MockConfigurationPolicyProvider> provider( new MockConfigurationPolicyProvider()); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEnabled, + kPolicyDefaultSearchProviderEnabled, Value::CreateBooleanValue(true)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSearchURL, + kPolicyDefaultSearchProviderSearchURL, Value::CreateStringValue(bad_search_url)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderName, + kPolicyDefaultSearchProviderName, Value::CreateStringValue(name)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderKeyword, + kPolicyDefaultSearchProviderKeyword, Value::CreateStringValue(keyword)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSuggestURL, + kPolicyDefaultSearchProviderSuggestURL, Value::CreateStringValue(suggest_url)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderIconURL, + kPolicyDefaultSearchProviderIconURL, Value::CreateStringValue(icon_url)); provider->AddPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEncodings, + kPolicyDefaultSearchProviderEncodings, Value::CreateStringValue(encodings)); ConfigurationPolicyPrefStore store(provider.get()); EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE); - DictionaryValue* prefs = store.prefs(); + const DictionaryValue* const prefs = store.prefs(); std::string string_result; EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderEnabled, - &string_result)); + &string_result)); EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderSearchURL, - &string_result)); + &string_result)); EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderName, - &string_result)); + &string_result)); EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderKeyword, - &string_result)); + &string_result)); EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderSuggestURL, - &string_result)); + &string_result)); EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderIconURL, - &string_result)); + &string_result)); EXPECT_FALSE(prefs->GetString(prefs::kDefaultSearchProviderEncodings, - &string_result)); + &string_result)); } // Test cases for the Sync policy setting. @@ -581,8 +578,7 @@ TEST_F(ConfigurationPolicyPrefStoreSyncTest, Default) { TEST_F(ConfigurationPolicyPrefStoreSyncTest, Enabled) { ConfigurationPolicyPrefStore store(NULL); - store.Apply(ConfigurationPolicyPrefStore::kPolicySyncDisabled, - Value::CreateBooleanValue(false)); + store.Apply(kPolicySyncDisabled, Value::CreateBooleanValue(false)); // Enabling Sync should not set the pref. bool result = false; EXPECT_FALSE(store.prefs()->GetBoolean(prefs::kSyncManaged, &result)); @@ -590,8 +586,7 @@ TEST_F(ConfigurationPolicyPrefStoreSyncTest, Enabled) { TEST_F(ConfigurationPolicyPrefStoreSyncTest, Disabled) { ConfigurationPolicyPrefStore store(NULL); - store.Apply(ConfigurationPolicyPrefStore::kPolicySyncDisabled, - Value::CreateBooleanValue(true)); + store.Apply(kPolicySyncDisabled, Value::CreateBooleanValue(true)); // Sync should be flagged as managed. bool result = false; EXPECT_TRUE(store.prefs()->GetBoolean(prefs::kSyncManaged, &result)); @@ -610,8 +605,7 @@ TEST_F(ConfigurationPolicyPrefStoreAutoFillTest, Default) { TEST_F(ConfigurationPolicyPrefStoreAutoFillTest, Enabled) { ConfigurationPolicyPrefStore store(NULL); - store.Apply(ConfigurationPolicyPrefStore::kPolicyAutoFillEnabled, - Value::CreateBooleanValue(true)); + store.Apply(kPolicyAutoFillEnabled, Value::CreateBooleanValue(true)); // Enabling AutoFill should not set the pref. bool result = false; EXPECT_FALSE(store.prefs()->GetBoolean(prefs::kAutoFillEnabled, &result)); @@ -619,8 +613,7 @@ TEST_F(ConfigurationPolicyPrefStoreAutoFillTest, Enabled) { TEST_F(ConfigurationPolicyPrefStoreAutoFillTest, Disabled) { ConfigurationPolicyPrefStore store(NULL); - store.Apply(ConfigurationPolicyPrefStore::kPolicyAutoFillEnabled, - Value::CreateBooleanValue(false)); + store.Apply(kPolicyAutoFillEnabled, Value::CreateBooleanValue(false)); // Disabling AutoFill should switch the pref to managed. bool result = true; EXPECT_TRUE(store.prefs()->GetBoolean(prefs::kAutoFillEnabled, &result)); diff --git a/chrome/browser/policy/configuration_policy_provider.cc b/chrome/browser/policy/configuration_policy_provider.cc index 0c7f2fd..2b77db8 100644 --- a/chrome/browser/policy/configuration_policy_provider.cc +++ b/chrome/browser/policy/configuration_policy_provider.cc @@ -10,15 +10,8 @@ namespace policy { ConfigurationPolicyProvider::ConfigurationPolicyProvider( - const StaticPolicyValueMap& policy_map) { - for (size_t i = 0; i < policy_map.entry_count; ++i) { - PolicyValueMapEntry entry = { - policy_map.entries[i].policy_type, - policy_map.entries[i].value_type, - std::string(policy_map.entries[i].name) - }; - policy_value_map_.push_back(entry); - } + const PolicyDefinitionList* policy_list) + : policy_definition_list_(policy_list) { } ConfigurationPolicyProvider::~ConfigurationPolicyProvider() {} diff --git a/chrome/browser/policy/configuration_policy_provider.h b/chrome/browser/policy/configuration_policy_provider.h index 4115107..7969988 100644 --- a/chrome/browser/policy/configuration_policy_provider.h +++ b/chrome/browser/policy/configuration_policy_provider.h @@ -6,11 +6,13 @@ #define CHROME_BROWSER_POLICY_CONFIGURATION_POLICY_PROVIDER_H_ #pragma once +#include <string> #include <vector> #include "base/basictypes.h" +#include "base/scoped_ptr.h" #include "base/values.h" -#include "chrome/browser/policy/configuration_policy_store.h" +#include "chrome/browser/policy/configuration_policy_store_interface.h" namespace policy { @@ -21,18 +23,18 @@ class ConfigurationPolicyProvider { public: // Used for static arrays of policy values that is used to initialize an // instance of the ConfigurationPolicyProvider. - struct StaticPolicyValueMap { + struct PolicyDefinitionList { struct Entry { - ConfigurationPolicyStore::PolicyType policy_type; + ConfigurationPolicyType policy_type; Value::ValueType value_type; const char* name; }; - size_t entry_count; - const Entry* entries; + const Entry* begin; + const Entry* end; }; - explicit ConfigurationPolicyProvider(const StaticPolicyValueMap& policy_map); + explicit ConfigurationPolicyProvider(const PolicyDefinitionList* policy_list); virtual ~ConfigurationPolicyProvider(); @@ -43,7 +45,7 @@ class ConfigurationPolicyProvider { // the |ConfigurationPolicyProvider| must make calls to the // |Apply| method of |store| to apply specific policies. // Returns true if the policy could be provided, otherwise false. - virtual bool Provide(ConfigurationPolicyStore* store) = 0; + virtual bool Provide(ConfigurationPolicyStoreInterface* store) = 0; // Called by the subclass provider at any time to indicate that the currently // applied policy is not longer current. A policy refresh will be initiated as @@ -51,21 +53,14 @@ class ConfigurationPolicyProvider { virtual void NotifyStoreOfPolicyChange(); protected: - // A structure mapping policies to their implementations by providers. - struct PolicyValueMapEntry { - ConfigurationPolicyStore::PolicyType policy_type; - Value::ValueType value_type; - std::string name; - }; - typedef std::vector<PolicyValueMapEntry> PolicyValueMap; - - const PolicyValueMap& policy_value_map() const { - return policy_value_map_; + const PolicyDefinitionList* policy_definition_list() const { + return policy_definition_list_; } private: // Contains the default mapping from policy values to the actual names. - PolicyValueMap policy_value_map_; + const ConfigurationPolicyProvider::PolicyDefinitionList* + policy_definition_list_; private: DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyProvider); diff --git a/chrome/browser/policy/configuration_policy_provider_mac.cc b/chrome/browser/policy/configuration_policy_provider_mac.cc index 552e33c..62b7436 100644 --- a/chrome/browser/policy/configuration_policy_provider_mac.cc +++ b/chrome/browser/policy/configuration_policy_provider_mac.cc @@ -4,28 +4,50 @@ #include "chrome/browser/policy/configuration_policy_provider_mac.h" +#include "base/file_util.h" #include "base/logging.h" #include "base/mac/scoped_cftyperef.h" +#include "base/path_service.h" #include "base/sys_string_conversions.h" +#include "chrome/common/chrome_paths.h" namespace policy { -ConfigurationPolicyProviderMac::ConfigurationPolicyProviderMac( - const StaticPolicyValueMap& policy_map) - : ConfigurationPolicyProvider(policy_map), - preferences_(new MacPreferences()) { +static FilePath GetManagedPolicyPath() { + // This constructs the path to the plist file in which Mac OS X stores the + // managed preference for the application. This is undocumented and therefore + // fragile, but if it doesn't work out, FileBasedPolicyLoader has a task that + // polls periodically in order to reload managed preferences later even if we + // missed the change. + FilePath path; + if (!PathService::Get(chrome::DIR_MANAGED_PREFS, &path)) + return FilePath(); + + CFBundleRef bundle(CFBundleGetMainBundle()); + if (!bundle) + return FilePath(); + + CFStringRef bundle_id = CFBundleGetIdentifier(bundle); + if (!bundle_id) + return FilePath(); + + return path.Append(base::SysCFStringRefToUTF8(bundle_id) + ".plist"); } -ConfigurationPolicyProviderMac::ConfigurationPolicyProviderMac( - const StaticPolicyValueMap& policy_map, MacPreferences* preferences) - : ConfigurationPolicyProvider(policy_map), preferences_(preferences) { +MacPreferencesPolicyLoader::MacPreferencesPolicyLoader( + MacPreferences* preferences, + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list) + : FileBasedPolicyProvider::Delegate(GetManagedPolicyPath()), + policy_list_(policy_list), + preferences_(preferences) { } -bool ConfigurationPolicyProviderMac::Provide(ConfigurationPolicyStore* store) { - const PolicyValueMap& mapping = policy_value_map(); +DictionaryValue* MacPreferencesPolicyLoader::Load() { + preferences_->AppSynchronize(kCFPreferencesCurrentApplication); + DictionaryValue* policy = new DictionaryValue; - for (PolicyValueMap::const_iterator current = mapping.begin(); - current != mapping.end(); ++current) { + const ConfigurationPolicyProvider::PolicyDefinitionList::Entry* current; + for (current = policy_list_->begin; current != policy_list_->end; ++current) { base::mac::ScopedCFTypeRef<CFStringRef> name( base::SysUTF8ToCFStringRef(current->name)); base::mac::ScopedCFTypeRef<CFPropertyListRef> value( @@ -40,16 +62,13 @@ bool ConfigurationPolicyProviderMac::Provide(ConfigurationPolicyStore* store) { if (CFGetTypeID(value) == CFStringGetTypeID()) { std::string string_value = base::SysCFStringRefToUTF8((CFStringRef)value.get()); - store->Apply( - current->policy_type, - Value::CreateStringValue(string_value)); + policy->SetString(current->name, string_value); } break; case Value::TYPE_BOOLEAN: if (CFGetTypeID(value) == CFBooleanGetTypeID()) { bool bool_value = CFBooleanGetValue((CFBooleanRef)value.get()); - store->Apply(current->policy_type, - Value::CreateBooleanValue(bool_value)); + policy->SetBoolean(current->name, bool_value); } break; case Value::TYPE_INTEGER: @@ -59,8 +78,7 @@ bool ConfigurationPolicyProviderMac::Provide(ConfigurationPolicyStore* store) { kCFNumberIntType, &int_value); if (cast) - store->Apply(current->policy_type, - Value::CreateIntegerValue(int_value)); + policy->SetInteger(current->name, int_value); } break; case Value::TYPE_LIST: @@ -81,16 +99,38 @@ bool ConfigurationPolicyProviderMac::Provide(ConfigurationPolicyStore* store) { } } if (valid_array) - store->Apply(current->policy_type, list_value.release()); + policy->Set(current->name, list_value.release()); } break; default: NOTREACHED(); - return false; } } - return true; + return policy; +} + +base::Time MacPreferencesPolicyLoader::GetLastModification() { + base::PlatformFileInfo file_info; + if (!file_util::GetFileInfo(config_file_path(), &file_info) || + file_info.is_directory) { + return base::Time(); + } + + return file_info.last_modified; +} + +ConfigurationPolicyProviderMac::ConfigurationPolicyProviderMac( + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list) + : FileBasedPolicyProvider(policy_list, + new MacPreferencesPolicyLoader(new MacPreferences, policy_list)) { +} + +ConfigurationPolicyProviderMac::ConfigurationPolicyProviderMac( + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, + MacPreferences* preferences) + : FileBasedPolicyProvider(policy_list, + new MacPreferencesPolicyLoader(preferences, policy_list)) { } } // namespace policy diff --git a/chrome/browser/policy/configuration_policy_provider_mac.h b/chrome/browser/policy/configuration_policy_provider_mac.h index 4f0c4b1..d804500 100644 --- a/chrome/browser/policy/configuration_policy_provider_mac.h +++ b/chrome/browser/policy/configuration_policy_provider_mac.h @@ -7,28 +7,50 @@ #pragma once #include "base/scoped_ptr.h" -#include "chrome/browser/policy/configuration_policy_store.h" -#include "chrome/browser/policy/configuration_policy_provider.h" +#include "chrome/browser/policy/configuration_policy_store_interface.h" +#include "chrome/browser/policy/file_based_policy_provider.h" #include "chrome/browser/preferences_mac.h" namespace policy { +// A policy loader implementation that read Mac OS X's managed preferences. +class MacPreferencesPolicyLoader : public FileBasedPolicyProvider::Delegate { + public: + // Takes ownership of |preferences|. + MacPreferencesPolicyLoader( + MacPreferences* preferences, + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list); + + // FileBasedPolicyLoader::Delegate implementation. + virtual DictionaryValue* Load(); + virtual base::Time GetLastModification(); + + private: + // In order to access the application preferences API, the names and values of + // the policies that are recognized must be known to the loader. + // Unfortunately, we cannot get the policy list at load time from the + // provider, because the loader may outlive the provider, so we store our own + // pointer to the list. + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list_; + + scoped_ptr<MacPreferences> preferences_; + + DISALLOW_COPY_AND_ASSIGN(MacPreferencesPolicyLoader); +}; + // An implementation of |ConfigurationPolicyProvider| using the mechanism // provided by Mac OS X's managed preferences. -class ConfigurationPolicyProviderMac : public ConfigurationPolicyProvider { +class ConfigurationPolicyProviderMac + : public FileBasedPolicyProvider { public: explicit ConfigurationPolicyProviderMac( - const StaticPolicyValueMap& policy_map); + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list); // For testing; takes ownership of |preferences|. - ConfigurationPolicyProviderMac(const StaticPolicyValueMap& policy_map, - MacPreferences* preferences); - virtual ~ConfigurationPolicyProviderMac() { } + ConfigurationPolicyProviderMac( + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, + MacPreferences* preferences); - // ConfigurationPolicyProvider method overrides: - virtual bool Provide(ConfigurationPolicyStore* store); - - protected: - scoped_ptr<MacPreferences> preferences_; + DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyProviderMac); }; } // namespace policy diff --git a/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc b/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc index 86794a0..5690375 100644 --- a/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc +++ b/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc @@ -20,7 +20,7 @@ namespace policy { class PolicyTestParams { public: // Takes ownership of |test_value|. - PolicyTestParams(ConfigurationPolicyStore::PolicyType type, + PolicyTestParams(ConfigurationPolicyType type, const char* policy_name, Value* test_value) : type_(type), @@ -45,7 +45,7 @@ class PolicyTestParams { test_value_.swap(other.test_value_); } - ConfigurationPolicyStore::PolicyType type() const { return type_; } + ConfigurationPolicyType type() const { return type_; } const char* policy_name() const { return policy_name_; } const Value* test_value() const { return test_value_.get(); } @@ -96,22 +96,22 @@ class PolicyTestParams { // Factory methods that create parameter objects for different value types. static PolicyTestParams ForStringPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* name) { return PolicyTestParams(type, name, Value::CreateStringValue("test")); } static PolicyTestParams ForBooleanPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* name) { return PolicyTestParams(type, name, Value::CreateBooleanValue(true)); } static PolicyTestParams ForIntegerPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* name) { return PolicyTestParams(type, name, Value::CreateIntegerValue(42)); } static PolicyTestParams ForListPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* name) { ListValue* value = new ListValue; value->Set(0U, Value::CreateStringValue("first")); @@ -120,7 +120,7 @@ class PolicyTestParams { } private: - ConfigurationPolicyStore::PolicyType type_; + ConfigurationPolicyType type_; const char* policy_name_; scoped_ptr<Value> test_value_; }; @@ -133,20 +133,17 @@ class ConfigurationPolicyProviderMacTest virtual void SetUp() { prefs_ = new MockPreferences; store_.reset(new MockConfigurationPolicyStore); - provider_.reset( - new ConfigurationPolicyProviderMac( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), - prefs_)); } protected: MockPreferences* prefs_; scoped_ptr<MockConfigurationPolicyStore> store_; - scoped_ptr<ConfigurationPolicyProviderMac> provider_; }; TEST_P(ConfigurationPolicyProviderMacTest, Default) { - EXPECT_TRUE(provider_->Provide(store_.get())); + ConfigurationPolicyProviderMac provider( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), prefs_); + EXPECT_TRUE(provider.Provide(store_.get())); EXPECT_TRUE(store_->policy_map().empty()); } @@ -156,7 +153,11 @@ TEST_P(ConfigurationPolicyProviderMacTest, Invalid) { base::mac::ScopedCFTypeRef<CFDataRef> invalid_data( CFDataCreate(NULL, NULL, 0)); prefs_->AddTestItem(name, invalid_data.get(), true); - EXPECT_TRUE(provider_->Provide(store_.get())); + + // Create the provider and have it read |prefs_|. + ConfigurationPolicyProviderMac provider( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), prefs_); + EXPECT_TRUE(provider.Provide(store_.get())); EXPECT_TRUE(store_->policy_map().empty()); } @@ -167,7 +168,11 @@ TEST_P(ConfigurationPolicyProviderMacTest, TestNonForcedValue) { GetParam().GetPropertyListValue()); ASSERT_TRUE(test_value.get()); prefs_->AddTestItem(name, test_value.get(), false); - EXPECT_TRUE(provider_->Provide(store_.get())); + + // Create the provider and have it read |prefs_|. + ConfigurationPolicyProviderMac provider( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), prefs_); + EXPECT_TRUE(provider.Provide(store_.get())); EXPECT_TRUE(store_->policy_map().empty()); } @@ -178,7 +183,11 @@ TEST_P(ConfigurationPolicyProviderMacTest, TestValue) { GetParam().GetPropertyListValue()); ASSERT_TRUE(test_value.get()); prefs_->AddTestItem(name, test_value, true); - EXPECT_TRUE(provider_->Provide(store_.get())); + + // Create the provider and have it read |prefs_|. + ConfigurationPolicyProviderMac provider( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), prefs_); + EXPECT_TRUE(provider.Provide(store_.get())); ASSERT_EQ(1U, store_->policy_map().size()); const Value* value = store_->Get(GetParam().type()); ASSERT_TRUE(value); @@ -191,94 +200,94 @@ INSTANTIATE_TEST_CASE_P( ConfigurationPolicyProviderMacTest, testing::Values( PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyHomePage, + kPolicyHomePage, key::kHomepageLocation), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyHomepageIsNewTabPage, + kPolicyHomepageIsNewTabPage, key::kHomepageIsNewTabPage), PolicyTestParams::ForIntegerPolicy( - ConfigurationPolicyStore::kPolicyRestoreOnStartup, + kPolicyRestoreOnStartup, key::kRestoreOnStartup), PolicyTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyURLsToRestoreOnStartup, + kPolicyURLsToRestoreOnStartup, key::kURLsToRestoreOnStartup), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEnabled, + kPolicyDefaultSearchProviderEnabled, key::kDefaultSearchProviderEnabled), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderName, + kPolicyDefaultSearchProviderName, key::kDefaultSearchProviderName), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderKeyword, + kPolicyDefaultSearchProviderKeyword, key::kDefaultSearchProviderKeyword), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSearchURL, + kPolicyDefaultSearchProviderSearchURL, key::kDefaultSearchProviderSearchURL), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSuggestURL, + kPolicyDefaultSearchProviderSuggestURL, key::kDefaultSearchProviderSuggestURL), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderIconURL, + kPolicyDefaultSearchProviderIconURL, key::kDefaultSearchProviderIconURL), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEncodings, + kPolicyDefaultSearchProviderEncodings, key::kDefaultSearchProviderEncodings), PolicyTestParams::ForIntegerPolicy( - ConfigurationPolicyStore::kPolicyProxyServerMode, + kPolicyProxyServerMode, key::kProxyServerMode), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyProxyServer, + kPolicyProxyServer, key::kProxyServer), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyProxyPacUrl, + kPolicyProxyPacUrl, key::kProxyPacUrl), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyProxyBypassList, + kPolicyProxyBypassList, key::kProxyBypassList), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyAlternateErrorPagesEnabled, + kPolicyAlternateErrorPagesEnabled, key::kAlternateErrorPagesEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicySearchSuggestEnabled, + kPolicySearchSuggestEnabled, key::kSearchSuggestEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyDnsPrefetchingEnabled, + kPolicyDnsPrefetchingEnabled, key::kDnsPrefetchingEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicySafeBrowsingEnabled, + kPolicySafeBrowsingEnabled, key::kSafeBrowsingEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyMetricsReportingEnabled, + kPolicyMetricsReportingEnabled, key::kMetricsReportingEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyPasswordManagerEnabled, + kPolicyPasswordManagerEnabled, key::kPasswordManagerEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyPasswordManagerAllowShowPasswords, + kPolicyPasswordManagerAllowShowPasswords, key::kPasswordManagerAllowShowPasswords), PolicyTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyDisabledPlugins, + kPolicyDisabledPlugins, key::kDisabledPlugins), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyAutoFillEnabled, + kPolicyAutoFillEnabled, key::kAutoFillEnabled), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyApplicationLocale, + kPolicyApplicationLocale, key::kApplicationLocaleValue), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicySyncDisabled, + kPolicySyncDisabled, key::kSyncDisabled), PolicyTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyExtensionInstallAllowList, + kPolicyExtensionInstallAllowList, key::kExtensionInstallAllowList), PolicyTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyExtensionInstallDenyList, + kPolicyExtensionInstallDenyList, key::kExtensionInstallDenyList), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyShowHomeButton, + kPolicyShowHomeButton, key::kShowHomeButton), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyPrintingEnabled, + kPolicyPrintingEnabled, key::kPrintingEnabled))); } // namespace policy diff --git a/chrome/browser/policy/configuration_policy_provider_win.cc b/chrome/browser/policy/configuration_policy_provider_win.cc index c1ae370..8f86973 100644 --- a/chrome/browser/policy/configuration_policy_provider_win.cc +++ b/chrome/browser/policy/configuration_policy_provider_win.cc @@ -145,7 +145,9 @@ void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher:: void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher:: OnObjectSignaled(HANDLE object) { DCHECK(object == user_policy_changed_event_.handle() || - object == machine_policy_changed_event_.handle()); + object == machine_policy_changed_event_.handle()) + << "unexpected object signaled policy reload, obj = " + << std::showbase << std::hex << object; Reload(); } @@ -156,8 +158,8 @@ void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher:: } ConfigurationPolicyProviderWin::ConfigurationPolicyProviderWin( - const StaticPolicyValueMap& policy_map) - : ConfigurationPolicyProvider(policy_map) { + const PolicyDefinitionList* policy_list) + : ConfigurationPolicyProvider(policy_list) { watcher_ = new GroupPolicyChangeWatcher(this->AsWeakPtr(), kReloadIntervalMinutes); watcher_->Start(); @@ -168,7 +170,7 @@ ConfigurationPolicyProviderWin::~ConfigurationPolicyProviderWin() { } bool ConfigurationPolicyProviderWin::GetRegistryPolicyString( - const string16& name, string16* result) { + const string16& name, string16* result) const { string16 path = string16(kRegistrySubKey); RegKey policy_key; // First try the global policy. @@ -184,7 +186,7 @@ bool ConfigurationPolicyProviderWin::GetRegistryPolicyString( } bool ConfigurationPolicyProviderWin::GetRegistryPolicyStringList( - const string16& key, ListValue* result) { + const string16& key, ListValue* result) const { string16 path = string16(kRegistrySubKey); path += ASCIIToUTF16("\\") + key; RegKey policy_key; @@ -204,7 +206,7 @@ bool ConfigurationPolicyProviderWin::GetRegistryPolicyStringList( } bool ConfigurationPolicyProviderWin::GetRegistryPolicyBoolean( - const string16& value_name, bool* result) { + const string16& value_name, bool* result) const { DWORD value; RegKey hkcu_policy_key(HKEY_LOCAL_MACHINE, kRegistrySubKey, KEY_READ); if (hkcu_policy_key.ReadValueDW(value_name.c_str(), &value)) { @@ -221,7 +223,7 @@ bool ConfigurationPolicyProviderWin::GetRegistryPolicyBoolean( } bool ConfigurationPolicyProviderWin::GetRegistryPolicyInteger( - const string16& value_name, uint32* result) { + const string16& value_name, uint32* result) const { DWORD value; RegKey hkcu_policy_key(HKEY_LOCAL_MACHINE, kRegistrySubKey, KEY_READ); if (hkcu_policy_key.ReadValueDW(value_name.c_str(), &value)) { @@ -238,10 +240,10 @@ bool ConfigurationPolicyProviderWin::GetRegistryPolicyInteger( } bool ConfigurationPolicyProviderWin::Provide( - ConfigurationPolicyStore* store) { - const PolicyValueMap& mapping(policy_value_map()); - for (PolicyValueMap::const_iterator current = mapping.begin(); - current != mapping.end(); ++current) { + ConfigurationPolicyStoreInterface* store) { + const PolicyDefinitionList* policy_list(policy_definition_list()); + for (const PolicyDefinitionList::Entry* current = policy_list->begin; + current != policy_list->end; ++current) { std::wstring name = UTF8ToWide(current->name); switch (current->value_type) { case Value::TYPE_STRING: { diff --git a/chrome/browser/policy/configuration_policy_provider_win.h b/chrome/browser/policy/configuration_policy_provider_win.h index 26bc3e6..c2d5509 100644 --- a/chrome/browser/policy/configuration_policy_provider_win.h +++ b/chrome/browser/policy/configuration_policy_provider_win.h @@ -11,7 +11,7 @@ #include "base/scoped_ptr.h" #include "base/waitable_event.h" #include "base/weak_ptr.h" -#include "chrome/browser/policy/configuration_policy_store.h" +#include "chrome/browser/policy/configuration_policy_store_interface.h" #include "chrome/browser/policy/configuration_policy_provider.h" namespace base { @@ -84,11 +84,11 @@ class ConfigurationPolicyProviderWin }; explicit ConfigurationPolicyProviderWin( - const StaticPolicyValueMap& policy_map); + const PolicyDefinitionList* policy_list); virtual ~ConfigurationPolicyProviderWin(); // ConfigurationPolicyProvider method overrides: - virtual bool Provide(ConfigurationPolicyStore* store); + virtual bool Provide(ConfigurationPolicyStoreInterface* store); protected: // The sub key path for Chromium's Group Policy information in the @@ -103,12 +103,14 @@ class ConfigurationPolicyProviderWin // Reads a string registry value |name| at the specified |key| and puts the // resulting string in |result|. - - bool GetRegistryPolicyString(const string16& name, string16* result); + bool GetRegistryPolicyString(const string16& name, string16* result) const; // Gets a list value contained under |key| one level below the policy root. - bool GetRegistryPolicyStringList(const string16& key, ListValue* result); - bool GetRegistryPolicyBoolean(const string16& value_name, bool* result); - bool GetRegistryPolicyInteger(const string16& value_name, uint32* result); + bool GetRegistryPolicyStringList(const string16& key, + ListValue* result) const; + bool GetRegistryPolicyBoolean(const string16& value_name, + bool* result) const; + bool GetRegistryPolicyInteger(const string16& value_name, + uint32* result) const; }; } // namespace policy diff --git a/chrome/browser/policy/configuration_policy_provider_win_unittest.cc b/chrome/browser/policy/configuration_policy_provider_win_unittest.cc index f7a3524..39c4dce 100644 --- a/chrome/browser/policy/configuration_policy_provider_win_unittest.cc +++ b/chrome/browser/policy/configuration_policy_provider_win_unittest.cc @@ -29,15 +29,17 @@ const wchar_t kUnitTestMachineOverrideSubKey[] = const wchar_t kUnitTestUserOverrideSubKey[] = L"SOFTWARE\\Chromium Unit Tests\\HKCU Override"; +namespace { + // Holds policy type, corresponding policy name string and a valid value for use // in parametrized value tests. class PolicyTestParams { public: // Assumes ownership of |hklm_value| and |hkcu_value|. - PolicyTestParams(ConfigurationPolicyStore::PolicyType type, - const char* policy_name, - Value* hklm_value, - Value* hkcu_value) + PolicyTestParams(ConfigurationPolicyType type, + const char* policy_name, + Value* hklm_value, + Value* hkcu_value) : type_(type), policy_name_(policy_name), hklm_value_(hklm_value), @@ -63,14 +65,14 @@ class PolicyTestParams { hkcu_value_.swap(other.hkcu_value_); } - ConfigurationPolicyStore::PolicyType type() const { return type_; } + ConfigurationPolicyType type() const { return type_; } const char* policy_name() const { return policy_name_; } const Value* hklm_value() const { return hklm_value_.get(); } const Value* hkcu_value() const { return hkcu_value_.get(); } // Factory methods for different value types. static PolicyTestParams ForStringPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* policy_name) { return PolicyTestParams(type, policy_name, @@ -78,7 +80,7 @@ class PolicyTestParams { Value::CreateStringValue("string_b")); } static PolicyTestParams ForBooleanPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* policy_name) { return PolicyTestParams(type, policy_name, @@ -86,7 +88,7 @@ class PolicyTestParams { Value::CreateBooleanValue(false)); } static PolicyTestParams ForIntegerPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* policy_name) { return PolicyTestParams(type, policy_name, @@ -94,7 +96,7 @@ class PolicyTestParams { Value::CreateIntegerValue(17)); } static PolicyTestParams ForListPolicy( - ConfigurationPolicyStore::PolicyType type, + ConfigurationPolicyType type, const char* policy_name) { ListValue* hklm_value = new ListValue; hklm_value->Set(0U, Value::CreateStringValue("It's a plane!")); @@ -105,12 +107,14 @@ class PolicyTestParams { } private: - ConfigurationPolicyStore::PolicyType type_; + ConfigurationPolicyType type_; const char* policy_name_; scoped_ptr<Value> hklm_value_; scoped_ptr<Value> hkcu_value_; }; +} // namespace + // This test class provides sandboxing and mocking for the parts of the // Windows Registry implementing Group Policy. The |SetUp| method prepares // two temporary sandbox keys in |kUnitTestRegistrySubKey|, one for HKLM and one @@ -183,9 +187,8 @@ void ConfigurationPolicyProviderWinTest::SetUp() { ActivateOverrides(); store_.reset(new MockConfigurationPolicyStore); - provider_.reset( - new ConfigurationPolicyProviderWin( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap())); + provider_.reset(new ConfigurationPolicyProviderWin( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList())); } void ConfigurationPolicyProviderWinTest::TearDown() { @@ -256,7 +259,7 @@ void ConfigurationPolicyProviderWinTest::WriteValue(HKEY hive, const ListValue* list = static_cast<const ListValue*>(value); RegKey key(hive, (string16(policy::kRegistrySubKey) + ASCIIToUTF16("\\") + - UTF8ToUTF16(name)).c_str(), + UTF8ToUTF16(name)).c_str(), KEY_ALL_ACCESS); int index = 1; for (ListValue::const_iterator element(list->begin()); @@ -339,91 +342,91 @@ INSTANTIATE_TEST_CASE_P( ConfigurationPolicyProviderWinTest, testing::Values( PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyHomePage, + kPolicyHomePage, key::kHomepageLocation), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyHomepageIsNewTabPage, + kPolicyHomepageIsNewTabPage, key::kHomepageIsNewTabPage), PolicyTestParams::ForIntegerPolicy( - ConfigurationPolicyStore::kPolicyRestoreOnStartup, + kPolicyRestoreOnStartup, key::kRestoreOnStartup), PolicyTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyURLsToRestoreOnStartup, + kPolicyURLsToRestoreOnStartup, key::kURLsToRestoreOnStartup), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEnabled, + kPolicyDefaultSearchProviderEnabled, key::kDefaultSearchProviderEnabled), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderName, + kPolicyDefaultSearchProviderName, key::kDefaultSearchProviderName), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderKeyword, + kPolicyDefaultSearchProviderKeyword, key::kDefaultSearchProviderKeyword), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSearchURL, + kPolicyDefaultSearchProviderSearchURL, key::kDefaultSearchProviderSearchURL), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderSuggestURL, + kPolicyDefaultSearchProviderSuggestURL, key::kDefaultSearchProviderSuggestURL), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderIconURL, + kPolicyDefaultSearchProviderIconURL, key::kDefaultSearchProviderIconURL), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyDefaultSearchProviderEncodings, + kPolicyDefaultSearchProviderEncodings, key::kDefaultSearchProviderEncodings), PolicyTestParams::ForIntegerPolicy( - ConfigurationPolicyStore::kPolicyProxyServerMode, + kPolicyProxyServerMode, key::kProxyServerMode), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyProxyServer, + kPolicyProxyServer, key::kProxyServer), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyProxyPacUrl, + kPolicyProxyPacUrl, key::kProxyPacUrl), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyProxyBypassList, + kPolicyProxyBypassList, key::kProxyBypassList), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyAlternateErrorPagesEnabled, + kPolicyAlternateErrorPagesEnabled, key::kAlternateErrorPagesEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicySearchSuggestEnabled, + kPolicySearchSuggestEnabled, key::kSearchSuggestEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyDnsPrefetchingEnabled, + kPolicyDnsPrefetchingEnabled, key::kDnsPrefetchingEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicySafeBrowsingEnabled, + kPolicySafeBrowsingEnabled, key::kSafeBrowsingEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyMetricsReportingEnabled, + kPolicyMetricsReportingEnabled, key::kMetricsReportingEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyPasswordManagerEnabled, + kPolicyPasswordManagerEnabled, key::kPasswordManagerEnabled), PolicyTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyDisabledPlugins, + kPolicyDisabledPlugins, key::kDisabledPlugins), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyAutoFillEnabled, + kPolicyAutoFillEnabled, key::kAutoFillEnabled), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicySyncDisabled, + kPolicySyncDisabled, key::kSyncDisabled), PolicyTestParams::ForStringPolicy( - ConfigurationPolicyStore::kPolicyApplicationLocale, + kPolicyApplicationLocale, key::kApplicationLocaleValue), PolicyTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyExtensionInstallAllowList, + kPolicyExtensionInstallAllowList, key::kExtensionInstallAllowList), PolicyTestParams::ForListPolicy( - ConfigurationPolicyStore::kPolicyExtensionInstallDenyList, + kPolicyExtensionInstallDenyList, key::kExtensionInstallDenyList), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyShowHomeButton, + kPolicyShowHomeButton, key::kShowHomeButton), PolicyTestParams::ForBooleanPolicy( - ConfigurationPolicyStore::kPolicyPrintingEnabled, + kPolicyPrintingEnabled, key::kPrintingEnabled))); } // namespace policy diff --git a/chrome/browser/policy/configuration_policy_store.h b/chrome/browser/policy/configuration_policy_store.h deleted file mode 100644 index fb21d0e..0000000 --- a/chrome/browser/policy/configuration_policy_store.h +++ /dev/null @@ -1,78 +0,0 @@ -// 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_POLICY_CONFIGURATION_POLICY_STORE_H_ -#define CHROME_BROWSER_POLICY_CONFIGURATION_POLICY_STORE_H_ -#pragma once - -#include "base/basictypes.h" - -class Value; - -namespace policy { - -// An abstract super class for policy stores that provides a method that can be -// called by a |ConfigurationPolicyProvider| to specify a policy. -class ConfigurationPolicyStore { - public: - ConfigurationPolicyStore() {} - virtual ~ConfigurationPolicyStore() {} - - enum PolicyType { - kPolicyHomePage, - kPolicyHomepageIsNewTabPage, - kPolicyRestoreOnStartup, - kPolicyURLsToRestoreOnStartup, - kPolicyDefaultSearchProviderEnabled, - kPolicyDefaultSearchProviderName, - kPolicyDefaultSearchProviderKeyword, - kPolicyDefaultSearchProviderSearchURL, - kPolicyDefaultSearchProviderSuggestURL, - kPolicyDefaultSearchProviderIconURL, - kPolicyDefaultSearchProviderEncodings, - kPolicyDisableSpdy, - kPolicyProxyServerMode, - kPolicyProxyServer, - kPolicyProxyPacUrl, - kPolicyProxyBypassList, - kPolicyAlternateErrorPagesEnabled, - kPolicySearchSuggestEnabled, - kPolicyDnsPrefetchingEnabled, - kPolicySafeBrowsingEnabled, - kPolicyMetricsReportingEnabled, - kPolicyPasswordManagerEnabled, - kPolicyPasswordManagerAllowShowPasswords, - kPolicyAutoFillEnabled, - kPolicySyncDisabled, - kPolicyApplicationLocale, - kPolicyExtensionInstallAllowList, - kPolicyExtensionInstallDenyList, - kPolicyShowHomeButton, - kPolicyDisabledPlugins, - kPolicyPrintingEnabled, - kPolicyChromeFrameRendererSettings, - kPolicyRenderInChromeFrameList, - kPolicyRenderInHostList, - kPolicyJavascriptEnabled, - kPolicySavingBrowserHistoryDisabled, - kPolicyChromeOsLockOnIdleSuspend, - }; - - static const int kPolicyNoProxyServerMode = 0; - static const int kPolicyAutoDetectProxyMode = 1; - static const int kPolicyManuallyConfiguredProxyMode = 2; - static const int kPolicyUseSystemProxyMode = 3; - - // A |ConfigurationPolicyProvider| specifies the value of a policy setting - // through a call to |Apply|. - // The configuration policy pref store takes over the ownership of |value|. - virtual void Apply(PolicyType policy, Value* value) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyStore); -}; - -} // namespace policy - -#endif // CHROME_BROWSER_POLICY_CONFIGURATION_POLICY_STORE_H_ diff --git a/chrome/browser/policy/configuration_policy_store_interface.h b/chrome/browser/policy/configuration_policy_store_interface.h new file mode 100644 index 0000000..7bdcf74 --- /dev/null +++ b/chrome/browser/policy/configuration_policy_store_interface.h @@ -0,0 +1,83 @@ +// 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_POLICY_CONFIGURATION_POLICY_STORE_INTERFACE_H_ +#define CHROME_BROWSER_POLICY_CONFIGURATION_POLICY_STORE_INTERFACE_H_ +#pragma once + +#include "base/basictypes.h" + +class Value; + +namespace policy { + +enum ConfigurationPolicyType { + kPolicyHomePage, + kPolicyHomepageIsNewTabPage, + kPolicyRestoreOnStartup, + kPolicyURLsToRestoreOnStartup, + kPolicyDefaultSearchProviderEnabled, + kPolicyDefaultSearchProviderName, + kPolicyDefaultSearchProviderKeyword, + kPolicyDefaultSearchProviderSearchURL, + kPolicyDefaultSearchProviderSuggestURL, + kPolicyDefaultSearchProviderIconURL, + kPolicyDefaultSearchProviderEncodings, + kPolicyDisableSpdy, + kPolicyProxyServerMode, + kPolicyProxyServer, + kPolicyProxyPacUrl, + kPolicyProxyBypassList, + kPolicyAlternateErrorPagesEnabled, + kPolicySearchSuggestEnabled, + kPolicyDnsPrefetchingEnabled, + kPolicySafeBrowsingEnabled, + kPolicyMetricsReportingEnabled, + kPolicyPasswordManagerEnabled, + kPolicyPasswordManagerAllowShowPasswords, + kPolicyAutoFillEnabled, + kPolicySyncDisabled, + kPolicyApplicationLocale, + kPolicyExtensionInstallAllowList, + kPolicyExtensionInstallDenyList, + kPolicyShowHomeButton, + kPolicyDisabledPlugins, + kPolicyPrintingEnabled, + kPolicyChromeFrameRendererSettings, + kPolicyRenderInChromeFrameList, + kPolicyRenderInHostList, + kPolicyJavascriptEnabled, + kPolicySavingBrowserHistoryDisabled, + kPolicyDeveloperToolsDisabled, + kPolicyBlockThirdPartyCookies, + kPolicyExtensionInstallForceList, + kPolicyChromeOsLockOnIdleSuspend, +}; + +static const int kPolicyNoProxyServerMode = 0; +static const int kPolicyAutoDetectProxyMode = 1; +static const int kPolicyManuallyConfiguredProxyMode = 2; +static const int kPolicyUseSystemProxyMode = 3; + +// An abstract super class for policy stores that provides a method that can be +// called by a |ConfigurationPolicyProvider| to specify a policy. +class ConfigurationPolicyStoreInterface { + public: + virtual ~ConfigurationPolicyStoreInterface() {} + + // A |ConfigurationPolicyProvider| specifies the value of a policy + // setting through a call to |Apply|. The configuration policy pref + // store takes over the ownership of |value|. + virtual void Apply(ConfigurationPolicyType policy, Value* value) = 0; + + protected: + ConfigurationPolicyStoreInterface() {} + + private: + DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyStoreInterface); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_CONFIGURATION_POLICY_STORE_INTERFACE_H_ diff --git a/chrome/browser/policy/device_management_backend.h b/chrome/browser/policy/device_management_backend.h new file mode 100644 index 0000000..25c7b1b --- /dev/null +++ b/chrome/browser/policy/device_management_backend.h @@ -0,0 +1,114 @@ +// 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_POLICY_DEVICE_MANAGEMENT_BACKEND_H_ +#define CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_H_ +#pragma once + +#include <string> + +#include "base/basictypes.h" +#include "base/non_thread_safe.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" + +namespace policy { + +namespace em = enterprise_management; + +// Interface for clients that need to converse with the device management +// server, which provides services to register Chrome installations and CrOS +// devices for the purpose of fetching centrally-administered policy from the +// cloud. +class DeviceManagementBackend : NonThreadSafe { + public: + enum ErrorCode { + // Request payload invalid. + kErrorRequestInvalid, + // The HTTP request failed. + kErrorRequestFailed, + // The HTTP request returned a non-success code. + kErrorHttpStatus, + // Response could not be decoded. + kErrorResponseDecoding, + // Service error: Management not supported. + kErrorServiceManagementNotSupported, + // Service error: Device not found. + kErrorServiceDeviceNotFound, + // Service error: Device token invalid. + kErrorServiceManagementTokenInvalid, + // Service error: Activation pending. + kErrorServiceActivationPending, + }; + + class DeviceRegisterResponseDelegate { + public: + virtual ~DeviceRegisterResponseDelegate() {} + virtual void HandleRegisterResponse( + const em::DeviceRegisterResponse& response) = 0; + virtual void OnError(ErrorCode code) = 0; + + protected: + DeviceRegisterResponseDelegate() {} + + private: + DISALLOW_COPY_AND_ASSIGN(DeviceRegisterResponseDelegate); + }; + + class DeviceUnregisterResponseDelegate { + public: + virtual ~DeviceUnregisterResponseDelegate() {} + virtual void HandleUnregisterResponse( + const em::DeviceUnregisterResponse& response) = 0; + virtual void OnError(ErrorCode code) = 0; + + protected: + DeviceUnregisterResponseDelegate() {} + + private: + DISALLOW_COPY_AND_ASSIGN(DeviceUnregisterResponseDelegate); + }; + + class DevicePolicyResponseDelegate { + public: + virtual ~DevicePolicyResponseDelegate() {} + + virtual void HandlePolicyResponse( + const em::DevicePolicyResponse& response) = 0; + virtual void OnError(ErrorCode code) = 0; + + protected: + DevicePolicyResponseDelegate() {} + + private: + DISALLOW_COPY_AND_ASSIGN(DevicePolicyResponseDelegate); + }; + + virtual ~DeviceManagementBackend() {} + + virtual void ProcessRegisterRequest( + const std::string& auth_token, + const std::string& device_id, + const em::DeviceRegisterRequest& request, + DeviceRegisterResponseDelegate* delegate) = 0; + + virtual void ProcessUnregisterRequest( + const std::string& device_management_token, + const em::DeviceUnregisterRequest& request, + DeviceUnregisterResponseDelegate* delegate) = 0; + + virtual void ProcessPolicyRequest( + const std::string& device_management_token, + const em::DevicePolicyRequest& request, + DevicePolicyResponseDelegate* delegate) = 0; + + protected: + DeviceManagementBackend() {} + + private: + DISALLOW_COPY_AND_ASSIGN(DeviceManagementBackend); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_H_ diff --git a/chrome/browser/policy/device_management_backend_impl.cc b/chrome/browser/policy/device_management_backend_impl.cc new file mode 100644 index 0000000..c0e9b07 --- /dev/null +++ b/chrome/browser/policy/device_management_backend_impl.cc @@ -0,0 +1,415 @@ +// 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/policy/device_management_backend_impl.h" + +#include <utility> +#include <vector> + +#include "base/stl_util-inl.h" +#include "base/stringprintf.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/common/net/url_request_context_getter.h" +#include "net/base/cookie_monster.h" +#include "net/base/escape.h" +#include "net/base/host_resolver.h" +#include "net/base/ssl_config_service_defaults.h" +#include "net/http/http_auth_handler_factory.h" +#include "net/http/http_network_layer.h" +#include "net/proxy/proxy_service.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_status.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/io_thread.h" +#include "chrome/browser/net/chrome_net_log.h" +#include "chrome/common/chrome_version_info.h" + +namespace policy { + +namespace { + +// Name constants for URL query parameters. +const char kServiceParamRequest[] = "request"; +const char kServiceParamDeviceType[] = "devicetype"; +const char kServiceParamDeviceID[] = "deviceid"; +const char kServiceParamAgent[] = "agent"; + +// String constants for the device type and agent we report to the service. +const char kServiceValueDeviceType[] = "Chrome"; +const char kServiceValueAgent[] = + "%s enterprise management client version %s (%s)"; + +const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth="; +const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token="; + +} // namespace + +// Custom request context implementation that allows to override the user agent, +// amongst others. Using the default request context is not an option since this +// service may be constructed before the default request context is created +// (i.e. before the profile has been loaded). +class DeviceManagementBackendRequestContext : public URLRequestContext { + public: + explicit DeviceManagementBackendRequestContext(IOThread::Globals* io_globals); + virtual ~DeviceManagementBackendRequestContext(); + + private: + virtual const std::string& GetUserAgent(const GURL& url) const; + + std::string user_agent_; +}; + +DeviceManagementBackendRequestContext::DeviceManagementBackendRequestContext( + IOThread::Globals* io_globals) { + net_log_ = io_globals->net_log.get(); + host_resolver_ = io_globals->host_resolver.get(); + proxy_service_ = net::ProxyService::CreateDirect(); + ssl_config_service_ = net::SSLConfigService::CreateSystemSSLConfigService(); + http_auth_handler_factory_ = + net::HttpAuthHandlerFactory::CreateDefault(host_resolver_); + http_transaction_factory_ = + net::HttpNetworkLayer::CreateFactory(host_resolver_, + io_globals->dnsrr_resolver.get(), + NULL /* ssl_host_info_factory */, + proxy_service_, + ssl_config_service_, + http_auth_handler_factory_, + NULL /* network_delegate */, + net_log_); + cookie_store_ = new net::CookieMonster(NULL, NULL); + user_agent_ = DeviceManagementBackendImpl::GetAgentString(); + accept_language_ = "*"; + accept_charset_ = "*"; +} + +DeviceManagementBackendRequestContext + ::~DeviceManagementBackendRequestContext() { + delete http_transaction_factory_; + delete http_auth_handler_factory_; +} + +const std::string& +DeviceManagementBackendRequestContext::GetUserAgent(const GURL& url) const { + return user_agent_; +} + +// Request context holder. +class DeviceManagementBackendRequestContextGetter + : public URLRequestContextGetter { + public: + DeviceManagementBackendRequestContextGetter() + : io_thread_(g_browser_process->io_thread()) {} + + // URLRequestContextGetter overrides. + virtual URLRequestContext* GetURLRequestContext(); + virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const; + + private: + scoped_refptr<URLRequestContext> context_; + IOThread* io_thread_; +}; + + +URLRequestContext* +DeviceManagementBackendRequestContextGetter::GetURLRequestContext() { + if (!context_) + context_ = new DeviceManagementBackendRequestContext(io_thread_->globals()); + + return context_.get(); +} + +scoped_refptr<base::MessageLoopProxy> +DeviceManagementBackendRequestContextGetter::GetIOMessageLoopProxy() const { + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); +} + +// Helper class for URL query parameter encoding/decoding. +class URLQueryParameters { + public: + URLQueryParameters() {} + + // Add a query parameter. + void Put(const std::string& name, const std::string& value); + + // Produce the query string, taking care of properly encoding and assembling + // the names and values. + std::string Encode(); + + private: + typedef std::vector<std::pair<std::string, std::string> > ParameterMap; + ParameterMap params_; + + DISALLOW_COPY_AND_ASSIGN(URLQueryParameters); +}; + +void URLQueryParameters::Put(const std::string& name, + const std::string& value) { + params_.push_back(std::make_pair(name, value)); +} + +std::string URLQueryParameters::Encode() { + std::string result; + for (ParameterMap::const_iterator entry(params_.begin()); + entry != params_.end(); + ++entry) { + if (entry != params_.begin()) + result += '&'; + result += EscapeUrlEncodedData(entry->first); + result += '='; + result += EscapeUrlEncodedData(entry->second); + } + return result; +} + +// Wraps common response parsing and handling functionality. +class ResponseHandler { + public: + ResponseHandler() {} + virtual ~ResponseHandler() {} + + // Handles the URL request response. + void HandleResponse(const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + + // Forwards the given error to the delegate. + virtual void OnError(DeviceManagementBackend::ErrorCode error) = 0; + + private: + // Implemented by subclasses to handle the decoded response. + virtual void ProcessResponse( + const em::DeviceManagementResponse& response) = 0; +}; + +void ResponseHandler::HandleResponse(const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + if (status.status() != URLRequestStatus::SUCCESS) { + OnError(DeviceManagementBackend::kErrorRequestFailed); + return; + } + + if (response_code != 200) { + OnError(DeviceManagementBackend::kErrorHttpStatus); + return; + } + + em::DeviceManagementResponse response; + if (!response.ParseFromString(data)) { + OnError(DeviceManagementBackend::kErrorResponseDecoding); + return; + } + + // Check service error code. + switch (response.error()) { + case em::DeviceManagementResponse::SUCCESS: + break; + case em::DeviceManagementResponse::DEVICE_MANAGEMENT_NOT_SUPPORTED: + OnError(DeviceManagementBackend::kErrorServiceManagementNotSupported); + return; + case em::DeviceManagementResponse::DEVICE_NOT_FOUND: + OnError(DeviceManagementBackend::kErrorServiceDeviceNotFound); + return; + case em::DeviceManagementResponse::DEVICE_MANAGEMENT_TOKEN_INVALID: + OnError(DeviceManagementBackend::kErrorServiceManagementTokenInvalid); + return; + case em::DeviceManagementResponse::ACTIVATION_PENDING: + OnError(DeviceManagementBackend::kErrorServiceActivationPending); + return; + default: + // This should be caught by the protobuf decoder. + NOTREACHED(); + OnError(DeviceManagementBackend::kErrorResponseDecoding); + return; + } + + ProcessResponse(response); +} + +// Handles device registration responses. +class RegisterResponseHandler : public ResponseHandler { + public: + RegisterResponseHandler( + DeviceManagementBackend::DeviceRegisterResponseDelegate* delegate) + : delegate_(delegate) {} + + private: + // ResponseHandler overrides. + virtual void OnError(DeviceManagementBackend::ErrorCode error) { + delegate_->OnError(error); + } + virtual void ProcessResponse(const em::DeviceManagementResponse& response) { + delegate_->HandleRegisterResponse(response.register_response()); + } + + DeviceManagementBackend::DeviceRegisterResponseDelegate* delegate_; +}; + +// Handles device unregister responses. +class UnregisterResponseHandler : public ResponseHandler { + public: + UnregisterResponseHandler( + DeviceManagementBackend::DeviceUnregisterResponseDelegate* delegate) + : delegate_(delegate) {} + + private: + // ResponseHandler overrides. + virtual void OnError(DeviceManagementBackend::ErrorCode error) { + delegate_->OnError(error); + } + virtual void ProcessResponse(const em::DeviceManagementResponse& response) { + delegate_->HandleUnregisterResponse(response.unregister_response()); + } + + DeviceManagementBackend::DeviceUnregisterResponseDelegate* delegate_; +}; + +// Handles device policy responses. +class PolicyResponseHandler : public ResponseHandler { + public: + PolicyResponseHandler( + DeviceManagementBackend::DevicePolicyResponseDelegate* delegate) + : delegate_(delegate) {} + + private: + // ResponseHandler overrides. + virtual void OnError(DeviceManagementBackend::ErrorCode error) { + delegate_->OnError(error); + } + virtual void ProcessResponse(const em::DeviceManagementResponse& response) { + delegate_->HandlePolicyResponse(response.policy_response()); + } + + DeviceManagementBackend::DevicePolicyResponseDelegate* delegate_; +}; + +DeviceManagementBackendImpl::DeviceManagementBackendImpl( + const std::string& server_url) + : server_url_(server_url), + request_context_getter_( + new DeviceManagementBackendRequestContextGetter()) { +} + +DeviceManagementBackendImpl::~DeviceManagementBackendImpl() { + // Cancel all pending requests. + STLDeleteContainerPairPointers(response_handlers_.begin(), + response_handlers_.end()); +} + +void DeviceManagementBackendImpl::ProcessRegisterRequest( + const std::string& auth_token, + const std::string& device_id, + const em::DeviceRegisterRequest& request, + DeviceRegisterResponseDelegate* delegate) { + em::DeviceManagementRequest request_wrapper; + request_wrapper.mutable_register_request()->CopyFrom(request); + + URLQueryParameters params; + PutCommonQueryParameters(¶ms); + params.Put(kServiceParamRequest, "register"); + params.Put(kServiceParamDeviceID, device_id); + + CreateFetcher(request_wrapper, + new RegisterResponseHandler(delegate), + params.Encode(), + kServiceTokenAuthHeader + auth_token); +} + +void DeviceManagementBackendImpl::ProcessUnregisterRequest( + const std::string& device_management_token, + const em::DeviceUnregisterRequest& request, + DeviceUnregisterResponseDelegate* delegate) { + em::DeviceManagementRequest request_wrapper; + request_wrapper.mutable_unregister_request()->CopyFrom(request); + + URLQueryParameters params; + PutCommonQueryParameters(¶ms); + params.Put(kServiceParamRequest, "unregister"); + + CreateFetcher(request_wrapper, + new UnregisterResponseHandler(delegate), + params.Encode(), + kDMTokenAuthHeader + device_management_token); +} + +void DeviceManagementBackendImpl::ProcessPolicyRequest( + const std::string& device_management_token, + const em::DevicePolicyRequest& request, + DevicePolicyResponseDelegate* delegate) { + em::DeviceManagementRequest request_wrapper; + request_wrapper.mutable_policy_request()->CopyFrom(request); + + URLQueryParameters params; + PutCommonQueryParameters(¶ms); + params.Put(kServiceParamRequest, "policy"); + + CreateFetcher(request_wrapper, + new PolicyResponseHandler(delegate), + params.Encode(), + kDMTokenAuthHeader + device_management_token); +} + +// static +std::string DeviceManagementBackendImpl::GetAgentString() { + chrome::VersionInfo version_info; + return base::StringPrintf(kServiceValueAgent, + version_info.Name().c_str(), + version_info.Version().c_str(), + version_info.LastChange().c_str()); +} + +void DeviceManagementBackendImpl::OnURLFetchComplete( + const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + ResponseHandlerMap::iterator entry(response_handlers_.find(source)); + if (entry != response_handlers_.end()) { + ResponseHandler* handler = entry->second; + handler->HandleResponse(status, response_code, cookies, data); + response_handlers_.erase(entry); + delete handler; + } else { + NOTREACHED() << "Callback from foreign URL fetcher"; + } + delete source; +} + +void DeviceManagementBackendImpl::CreateFetcher( + const em::DeviceManagementRequest& request, + ResponseHandler* handler, + const std::string& query_params, + const std::string& extra_headers) { + scoped_ptr<ResponseHandler> handler_ptr(handler); + + // Construct the payload. + std::string payload; + if (!request.SerializeToString(&payload)) { + handler->OnError(DeviceManagementBackend::kErrorRequestInvalid); + return; + } + + // Instantiate the fetcher. + GURL url(server_url_ + '?' + query_params); + URLFetcher* fetcher = URLFetcher::Create(0, url, URLFetcher::POST, this); + fetcher->set_request_context(request_context_getter_.get()); + fetcher->set_upload_data("application/octet-stream", payload); + fetcher->set_extra_request_headers(extra_headers); + response_handlers_[fetcher] = handler_ptr.release(); + + // Start the request. The fetcher will call OnURLFetchComplete when done. + fetcher->Start(); +} + +void DeviceManagementBackendImpl::PutCommonQueryParameters( + URLQueryParameters* params) { + params->Put(kServiceParamDeviceType, kServiceValueDeviceType); + params->Put(kServiceParamAgent, GetAgentString()); +} + +} // namespace policy diff --git a/chrome/browser/policy/device_management_backend_impl.h b/chrome/browser/policy/device_management_backend_impl.h new file mode 100644 index 0000000..648ca55 --- /dev/null +++ b/chrome/browser/policy/device_management_backend_impl.h @@ -0,0 +1,84 @@ +// 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_POLICY_DEVICE_MANAGEMENT_BACKEND_IMPL_H_ +#define CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_IMPL_H_ +#pragma once + +#include <map> +#include <string> + +#include "chrome/browser/policy/device_management_backend.h" +#include "chrome/common/net/url_fetcher.h" +#include "googleurl/src/gurl.h" + +class URLRequestContextGetter; + +namespace policy { + +class ResponseHandler; +class URLQueryParameters; + +// Device management backend implementation. This implementation makes HTTP +// requests to the policy server through the net layer. +class DeviceManagementBackendImpl : public DeviceManagementBackend, + public URLFetcher::Delegate { + public: + explicit DeviceManagementBackendImpl(const std::string& server_url); + virtual ~DeviceManagementBackendImpl(); + + // GoogleAppsPolicyService overrides: + virtual void ProcessRegisterRequest( + const std::string& auth_token, + const std::string& device_id, + const em::DeviceRegisterRequest& request, + DeviceRegisterResponseDelegate* response_delegate); + virtual void ProcessUnregisterRequest( + const std::string& device_management_token, + const em::DeviceUnregisterRequest& request, + DeviceUnregisterResponseDelegate* response_delegate); + virtual void ProcessPolicyRequest( + const std::string& device_management_token, + const em::DevicePolicyRequest& request, + DevicePolicyResponseDelegate* response_delegate); + + // Get the agent string (used for HTTP user agent and as agent passed to the + // server). + static std::string GetAgentString(); + + private: + typedef std::map<const URLFetcher*, ResponseHandler*> ResponseHandlerMap; + + // URLFetcher::Delegate override. + virtual void OnURLFetchComplete(const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + + // Create a URLFetcher for a given request message and response handler. + void CreateFetcher(const em::DeviceManagementRequest& request, + ResponseHandler* handler, + const std::string& query_params, + const std::string& extra_headers); + + // Fill in the common query parameters. + void PutCommonQueryParameters(URLQueryParameters* params); + + // Server at which to contact the service. + const std::string server_url_; + + // The request context we use. + scoped_refptr<URLRequestContextGetter> request_context_getter_; + + // Keeps track of all in-flight requests an their response handlers. + ResponseHandlerMap response_handlers_; + + DISALLOW_COPY_AND_ASSIGN(DeviceManagementBackendImpl); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_IMPL_H_ diff --git a/chrome/browser/policy/device_management_backend_impl_browsertest.cc b/chrome/browser/policy/device_management_backend_impl_browsertest.cc new file mode 100644 index 0000000..43df7be --- /dev/null +++ b/chrome/browser/policy/device_management_backend_impl_browsertest.cc @@ -0,0 +1,141 @@ +// 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/policy/device_management_backend_impl.h" + +#include "base/message_loop.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/policy/device_management_backend_mock.h" +#include "chrome/test/in_process_browser_test.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_test_job.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::_; +using testing::DoAll; +using testing::Invoke; +using testing::InvokeWithoutArgs; + +namespace policy { + +namespace { + +const char kServiceUrl[] = "http://example.com/service"; + +// Binary representation of successful register response containing a token. +const char kServiceResponseRegister[] = + "\x08\x00\x1a\x22\x0a\x20\x64\x64\x32\x63\x38\x63\x33\x65\x64\x61" + "\x63\x63\x34\x61\x33\x32\x38\x31\x66\x33\x38\x62\x36\x35\x31\x31" + "\x36\x64\x61\x62\x66\x63"; +// Contains a single policy setting, namely HomepageIsNewTabPage: false. +const char kServiceResponsePolicy[] = + "\x08\x00\x2a\x2a\x0a\x28\x0a\x06\x70\x6f\x6c\x69\x63\x79\x12\x1e" + "\x0a\x1c\x0a\x14\x48\x6f\x6d\x65\x70\x61\x67\x65\x49\x73\x4e\x65" + "\x77\x54\x61\x62\x50\x61\x67\x65\x12\x04\x08\x01\x10\x00"; +// Successful unregister response. +const char kServiceResponseUnregister[] = + "\x08\x00\x22\x00"; + +#define PROTO_STRING(name) (std::string(name, arraysize(name) - 1)) + +} // namespace + +// Interceptor implementation that returns test data back to the service. +class CannedResponseInterceptor : public URLRequest::Interceptor { + public: + CannedResponseInterceptor(const GURL& service_url, + const std::string& response_data) + : service_url_(service_url), + response_data_(response_data) { + URLRequest::RegisterRequestInterceptor(this); + } + + virtual ~CannedResponseInterceptor() { + URLRequest::UnregisterRequestInterceptor(this); + } + + private: + // URLRequest::Interceptor overrides. + virtual URLRequestJob* MaybeIntercept(URLRequest* request) { + if (request->url().GetOrigin() == service_url_.GetOrigin() && + request->url().path() == service_url_.path()) { + return new URLRequestTestJob(request, + URLRequestTestJob::test_headers(), + response_data_, + true); + } + + return NULL; + } + + const GURL service_url_; + const std::string response_data_; +}; + +class DeviceManagementBackendImplIntegrationTest : public InProcessBrowserTest { + public: + void CaptureToken(const em::DeviceRegisterResponse& response) { + token_ = response.device_management_token(); + } + + protected: + DeviceManagementBackendImplIntegrationTest() { + URLFetcher::enable_interception_for_tests(true); + } + + std::string token_; +}; + +static void QuitMessageLoop() { + MessageLoop::current()->Quit(); +} + +IN_PROC_BROWSER_TEST_F(DeviceManagementBackendImplIntegrationTest, + RegisterAndFetchPolicy) { + DeviceManagementBackendImpl service(kServiceUrl); + + { + CannedResponseInterceptor interceptor( + GURL(kServiceUrl), PROTO_STRING(kServiceResponseRegister)); + DeviceRegisterResponseDelegateMock delegate; + EXPECT_CALL(delegate, HandleRegisterResponse(_)) + .WillOnce(DoAll(Invoke(this, &DeviceManagementBackendImplIntegrationTest + ::CaptureToken), + InvokeWithoutArgs(QuitMessageLoop))); + em::DeviceRegisterRequest request; + service.ProcessRegisterRequest("token", "device id", request, &delegate); + MessageLoop::current()->Run(); + } + + { + CannedResponseInterceptor interceptor( + GURL(kServiceUrl), PROTO_STRING(kServiceResponsePolicy)); + DevicePolicyResponseDelegateMock delegate; + EXPECT_CALL(delegate, HandlePolicyResponse(_)) + .WillOnce(InvokeWithoutArgs(QuitMessageLoop)); + em::DevicePolicyRequest request; + request.set_policy_scope("chrome"); + em::DevicePolicySettingRequest* setting_request = + request.add_setting_request(); + setting_request->set_key("policy"); + service.ProcessPolicyRequest(token_, request, &delegate); + + MessageLoop::current()->Run(); + } + + { + CannedResponseInterceptor interceptor( + GURL(kServiceUrl), PROTO_STRING(kServiceResponseUnregister)); + DeviceUnregisterResponseDelegateMock delegate; + EXPECT_CALL(delegate, HandleUnregisterResponse(_)) + .WillOnce(InvokeWithoutArgs(QuitMessageLoop)); + em::DeviceUnregisterRequest request; + service.ProcessUnregisterRequest(token_, request, &delegate); + + MessageLoop::current()->Run(); + } +} + +} // namespace policy diff --git a/chrome/browser/policy/device_management_backend_impl_unittest.cc b/chrome/browser/policy/device_management_backend_impl_unittest.cc new file mode 100644 index 0000000..1e93f3a --- /dev/null +++ b/chrome/browser/policy/device_management_backend_impl_unittest.cc @@ -0,0 +1,375 @@ +// 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/policy/device_management_backend_impl.h" + +#include "base/message_loop.h" +#include "base/string_split.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/policy/device_management_backend_mock.h" +#include "chrome/common/net/test_url_fetcher_factory.h" +#include "net/base/escape.h" +#include "net/url_request/url_request_status.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::_; + +namespace policy { + +namespace { + +const char kServiceURL[] = "https://example.com/management_service"; + +// Encoded error response messages for testing the error code paths. +const char kResponseEmpty[] = "\x08\x00"; +const char kResponseErrorManagementNotSupported[] = "\x08\x01"; +const char kResponseErrorDeviceNotFound[] = "\x08\x02"; +const char kResponseErrorManagementTokenInvalid[] = "\x08\x03"; +const char kResponseErrorActivationPending[] = "\x08\x04"; + +#define PROTO_STRING(name) (std::string(name, arraysize(name) - 1)) + +} // namespace + +// Unit tests for the google apps policy backend. The pattern here is each test +// case triggeres a request and installs a mock delegate. The test will run and +// the default action installed on the test delegate will quit the loop. +template<typename TESTBASE> +class DeviceManagementBackendImplTestBase : public TESTBASE { + protected: + DeviceManagementBackendImplTestBase() + : io_thread_(BrowserThread::IO, &loop_), + service_(kServiceURL) {} + + virtual void SetUp() { + URLFetcher::set_factory(&factory_); + } + + virtual void TearDown() { + URLFetcher::set_factory(NULL); + loop_.RunAllPending(); + } + + MessageLoopForUI loop_; + BrowserThread io_thread_; + TestURLFetcherFactory factory_; + DeviceManagementBackendImpl service_; +}; + +struct FailedRequestParams { + FailedRequestParams(DeviceManagementBackend::ErrorCode expected_error, + URLRequestStatus::Status request_status, + int http_status, + const std::string& response) + : expected_error_(expected_error), + request_status_(request_status, 0), + http_status_(http_status), + response_(response) {} + + DeviceManagementBackend::ErrorCode expected_error_; + URLRequestStatus request_status_; + int http_status_; + std::string response_; +}; + +// A parameterized test case for erroneous response situations, they're mostly +// the same for all kinds of requests. +class DeviceManagementBackendImplFailedRequestTest + : public DeviceManagementBackendImplTestBase< + testing::TestWithParam<FailedRequestParams> > { +}; + +TEST_P(DeviceManagementBackendImplFailedRequestTest, RegisterRequest) { + DeviceRegisterResponseDelegateMock mock; + EXPECT_CALL(mock, OnError(GetParam().expected_error_)); + em::DeviceRegisterRequest request; + service_.ProcessRegisterRequest("token", "device id", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + GetParam().request_status_, + GetParam().http_status_, + ResponseCookies(), + GetParam().response_); +} + +TEST_P(DeviceManagementBackendImplFailedRequestTest, UnregisterRequest) { + DeviceUnregisterResponseDelegateMock mock; + EXPECT_CALL(mock, OnError(GetParam().expected_error_)); + em::DeviceUnregisterRequest request; + service_.ProcessUnregisterRequest("token", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + GetParam().request_status_, + GetParam().http_status_, + ResponseCookies(), + GetParam().response_); +} + +TEST_P(DeviceManagementBackendImplFailedRequestTest, PolicyRequest) { + DevicePolicyResponseDelegateMock mock; + EXPECT_CALL(mock, OnError(GetParam().expected_error_)); + em::DevicePolicyRequest request; + request.set_policy_scope("Chrome"); + em::DevicePolicySettingRequest* setting_request = + request.add_setting_request(); + setting_request->set_key("policy"); + service_.ProcessPolicyRequest("token", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + GetParam().request_status_, + GetParam().http_status_, + ResponseCookies(), + GetParam().response_); +} + +INSTANTIATE_TEST_CASE_P( + DeviceManagementBackendImplFailedRequestTestInstance, + DeviceManagementBackendImplFailedRequestTest, + testing::Values( + FailedRequestParams( + DeviceManagementBackend::kErrorRequestFailed, + URLRequestStatus::FAILED, + 200, + PROTO_STRING(kResponseEmpty)), + FailedRequestParams( + DeviceManagementBackend::kErrorHttpStatus, + URLRequestStatus::SUCCESS, + 500, + PROTO_STRING(kResponseEmpty)), + FailedRequestParams( + DeviceManagementBackend::kErrorResponseDecoding, + URLRequestStatus::SUCCESS, + 200, + PROTO_STRING("Not a protobuf.")), + FailedRequestParams( + DeviceManagementBackend::kErrorServiceManagementNotSupported, + URLRequestStatus::SUCCESS, + 200, + PROTO_STRING(kResponseErrorManagementNotSupported)), + FailedRequestParams( + DeviceManagementBackend::kErrorServiceDeviceNotFound, + URLRequestStatus::SUCCESS, + 200, + PROTO_STRING(kResponseErrorDeviceNotFound)), + FailedRequestParams( + DeviceManagementBackend::kErrorServiceManagementTokenInvalid, + URLRequestStatus::SUCCESS, + 200, + PROTO_STRING(kResponseErrorManagementTokenInvalid)), + FailedRequestParams( + DeviceManagementBackend::kErrorServiceActivationPending, + URLRequestStatus::SUCCESS, + 200, + PROTO_STRING(kResponseErrorActivationPending)))); + +class DeviceManagementBackendImplTest + : public DeviceManagementBackendImplTestBase<testing::Test> { +}; + +MATCHER_P(MessageEquals, reference, "") { + std::string reference_data; + std::string arg_data; + return arg.SerializeToString(&arg_data) && + reference.SerializeToString(&reference_data) && + arg_data == reference_data; +} + +// Simple query parameter parser for testing. +class QueryParams { + public: + explicit QueryParams(const std::string& query) { + base::SplitStringIntoKeyValuePairs(query, '=', '&', ¶ms_); + } + + bool Check(const std::string& name, const std::string& expected_value) { + bool found = false; + for (ParamMap::const_iterator i(params_.begin()); i != params_.end(); ++i) { + std::string unescaped_name( + UnescapeURLComponent(i->first, + UnescapeRule::NORMAL | + UnescapeRule::SPACES | + UnescapeRule::URL_SPECIAL_CHARS | + UnescapeRule::CONTROL_CHARS | + UnescapeRule::REPLACE_PLUS_WITH_SPACE)); + if (unescaped_name == name) { + if (found) + return false; + found = true; + std::string unescaped_value( + UnescapeURLComponent(i->second, + UnescapeRule::NORMAL | + UnescapeRule::SPACES | + UnescapeRule::URL_SPECIAL_CHARS | + UnescapeRule::CONTROL_CHARS | + UnescapeRule::REPLACE_PLUS_WITH_SPACE)); + if (unescaped_value != expected_value) + return false; + } + } + return found; + } + + private: + typedef std::vector<std::pair<std::string, std::string> > ParamMap; + ParamMap params_; +}; + +TEST_F(DeviceManagementBackendImplTest, RegisterRequest) { + DeviceRegisterResponseDelegateMock mock; + em::DeviceRegisterResponse expected_response; + expected_response.set_device_management_token("mtoken"); + EXPECT_CALL(mock, HandleRegisterResponse(MessageEquals(expected_response))); + em::DeviceRegisterRequest request; + service_.ProcessRegisterRequest("token", "device id", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + // Check the data the fetcher received. + const GURL& request_url(fetcher->original_url()); + const GURL service_url(kServiceURL); + EXPECT_EQ(service_url.scheme(), request_url.scheme()); + EXPECT_EQ(service_url.host(), request_url.host()); + EXPECT_EQ(service_url.port(), request_url.port()); + EXPECT_EQ(service_url.path(), request_url.path()); + + QueryParams query_params(request_url.query()); + EXPECT_TRUE(query_params.Check("request", "register")); + + em::DeviceManagementRequest expected_request_wrapper; + expected_request_wrapper.mutable_register_request()->CopyFrom(request); + std::string expected_request_data; + ASSERT_TRUE(expected_request_wrapper.SerializeToString( + &expected_request_data)); + EXPECT_EQ(expected_request_data, fetcher->upload_data()); + + // Generate the response. + std::string response_data; + em::DeviceManagementResponse response_wrapper; + response_wrapper.set_error(em::DeviceManagementResponse::SUCCESS); + response_wrapper.mutable_register_response()->CopyFrom(expected_response); + ASSERT_TRUE(response_wrapper.SerializeToString(&response_data)); + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + status, + 200, + ResponseCookies(), + response_data); +} + +TEST_F(DeviceManagementBackendImplTest, UnregisterRequest) { + DeviceUnregisterResponseDelegateMock mock; + em::DeviceUnregisterResponse expected_response; + EXPECT_CALL(mock, HandleUnregisterResponse(MessageEquals(expected_response))); + em::DeviceUnregisterRequest request; + service_.ProcessUnregisterRequest("dmtokenvalue", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + // Check the data the fetcher received. + const GURL& request_url(fetcher->original_url()); + const GURL service_url(kServiceURL); + EXPECT_EQ(service_url.scheme(), request_url.scheme()); + EXPECT_EQ(service_url.host(), request_url.host()); + EXPECT_EQ(service_url.port(), request_url.port()); + EXPECT_EQ(service_url.path(), request_url.path()); + + QueryParams query_params(request_url.query()); + EXPECT_TRUE(query_params.Check("request", "unregister")); + + em::DeviceManagementRequest expected_request_wrapper; + expected_request_wrapper.mutable_unregister_request()->CopyFrom(request); + std::string expected_request_data; + ASSERT_TRUE(expected_request_wrapper.SerializeToString( + &expected_request_data)); + EXPECT_EQ(expected_request_data, fetcher->upload_data()); + + // Generate the response. + std::string response_data; + em::DeviceManagementResponse response_wrapper; + response_wrapper.set_error(em::DeviceManagementResponse::SUCCESS); + response_wrapper.mutable_unregister_response()->CopyFrom(expected_response); + ASSERT_TRUE(response_wrapper.SerializeToString(&response_data)); + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + status, + 200, + ResponseCookies(), + response_data); +} + +TEST_F(DeviceManagementBackendImplTest, PolicyRequest) { + DevicePolicyResponseDelegateMock mock; + em::DevicePolicyResponse expected_response; + em::DevicePolicySetting* policy_setting = expected_response.add_setting(); + policy_setting->set_policy_key("policy"); + policy_setting->set_watermark("fresh"); + em::GenericSetting* policy_value = policy_setting->mutable_policy_value(); + em::GenericNamedValue* named_value = policy_value->add_named_value(); + named_value->set_name("HomepageLocation"); + named_value->mutable_value()->set_value_type( + em::GenericValue::VALUE_TYPE_STRING); + named_value->mutable_value()->set_string_value("http://www.chromium.org"); + named_value = policy_value->add_named_value(); + named_value->set_name("HomepageIsNewTabPage"); + named_value->mutable_value()->set_value_type( + em::GenericValue::VALUE_TYPE_BOOL); + named_value->mutable_value()->set_bool_value(false); + EXPECT_CALL(mock, HandlePolicyResponse(MessageEquals(expected_response))); + + em::DevicePolicyRequest request; + request.set_policy_scope("chromium"); + em::DevicePolicySettingRequest* setting_request = + request.add_setting_request(); + setting_request->set_key("policy"); + setting_request->set_watermark("stale"); + service_.ProcessPolicyRequest("dmtokenvalue", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + // Check the data the fetcher received. + const GURL& request_url(fetcher->original_url()); + const GURL service_url(kServiceURL); + EXPECT_EQ(service_url.scheme(), request_url.scheme()); + EXPECT_EQ(service_url.host(), request_url.host()); + EXPECT_EQ(service_url.port(), request_url.port()); + EXPECT_EQ(service_url.path(), request_url.path()); + + QueryParams query_params(request_url.query()); + EXPECT_TRUE(query_params.Check("request", "policy")); + + em::DeviceManagementRequest expected_request_wrapper; + expected_request_wrapper.mutable_policy_request()->CopyFrom(request); + std::string expected_request_data; + ASSERT_TRUE(expected_request_wrapper.SerializeToString( + &expected_request_data)); + EXPECT_EQ(expected_request_data, fetcher->upload_data()); + + // Generate the response. + std::string response_data; + em::DeviceManagementResponse response_wrapper; + response_wrapper.set_error(em::DeviceManagementResponse::SUCCESS); + response_wrapper.mutable_policy_response()->CopyFrom(expected_response); + ASSERT_TRUE(response_wrapper.SerializeToString(&response_data)); + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + status, + 200, + ResponseCookies(), + response_data); +} + +} // namespace policy diff --git a/chrome/browser/policy/device_management_backend_mock.h b/chrome/browser/policy/device_management_backend_mock.h new file mode 100644 index 0000000..170fe3a --- /dev/null +++ b/chrome/browser/policy/device_management_backend_mock.h @@ -0,0 +1,38 @@ +// 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_POLICY_DEVICE_MANAGEMENT_BACKEND_MOCK_H_ +#define CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_MOCK_H_ + +#include "testing/gmock/include/gmock/gmock.h" + +namespace policy { + +// Mock classes for the various DeviceManagementBackend delegates that allow to +// capture callbacks using gmock. +class DeviceRegisterResponseDelegateMock + : public DeviceManagementBackend::DeviceRegisterResponseDelegate { + public: + MOCK_METHOD1(HandleRegisterResponse, void(const em::DeviceRegisterResponse&)); + MOCK_METHOD1(OnError, void(DeviceManagementBackend::ErrorCode error)); +}; + +class DeviceUnregisterResponseDelegateMock + : public DeviceManagementBackend::DeviceUnregisterResponseDelegate { + public: + MOCK_METHOD1(HandleUnregisterResponse, + void(const em::DeviceUnregisterResponse&)); + MOCK_METHOD1(OnError, void(DeviceManagementBackend::ErrorCode error)); +}; + +class DevicePolicyResponseDelegateMock + : public DeviceManagementBackend::DevicePolicyResponseDelegate { + public: + MOCK_METHOD1(HandlePolicyResponse, void(const em::DevicePolicyResponse&)); + MOCK_METHOD1(OnError, void(DeviceManagementBackend::ErrorCode error)); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_MOCK_H_ diff --git a/chrome/browser/policy/device_management_policy_cache.cc b/chrome/browser/policy/device_management_policy_cache.cc new file mode 100644 index 0000000..b70ced4 --- /dev/null +++ b/chrome/browser/policy/device_management_policy_cache.cc @@ -0,0 +1,222 @@ +// 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/policy/device_management_policy_cache.h" + +#include <limits> +#include <string> + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/values.h" +#include "chrome/browser/browser_thread.h" + +using google::protobuf::RepeatedField; +using google::protobuf::RepeatedPtrField; + +namespace policy { + +// A task implementation that saves policy information to a file. +class PersistPolicyTask : public Task { + public: + PersistPolicyTask(const FilePath& path, + const em::DevicePolicyResponse* policy); + + private: + // Task override. + virtual void Run(); + + const FilePath path_; + scoped_ptr<const em::DevicePolicyResponse> policy_; +}; + +PersistPolicyTask::PersistPolicyTask(const FilePath& path, + const em::DevicePolicyResponse* policy) + : path_(path), + policy_(policy) { +} + +void PersistPolicyTask::Run() { + std::string data; + if (!policy_->SerializeToString(&data)) { + LOG(WARNING) << "Failed to serialize policy data"; + return; + } + + int size = data.size(); + if (file_util::WriteFile(path_, data.c_str(), size) != size) { + LOG(WARNING) << "Failed to write " << path_.value(); + return; + } +} + +DeviceManagementPolicyCache::DeviceManagementPolicyCache( + const FilePath& backing_file_path) + : backing_file_path_(backing_file_path), + policy_(new DictionaryValue), + fresh_policy_(false) { +} + +void DeviceManagementPolicyCache::LoadPolicyFromFile() { + if (!file_util::PathExists(backing_file_path_) || fresh_policy_) + return; + + // Read the protobuf from the file. + std::string data; + if (!file_util::ReadFileToString(backing_file_path_, &data)) { + LOG(WARNING) << "Failed to read policy data from " + << backing_file_path_.value(); + return; + } + + em::DevicePolicyResponse policy; + if (!policy.ParseFromArray(data.c_str(), data.size())) { + LOG(WARNING) << "Failed to parse policy data read from " + << backing_file_path_.value(); + return; + } + + // Decode and swap in the new policy information. + scoped_ptr<DictionaryValue> value(DecodePolicy(policy)); + { + AutoLock lock(lock_); + if (!fresh_policy_) + policy_.reset(value.release()); + } +} + +void DeviceManagementPolicyCache::SetPolicy( + const em::DevicePolicyResponse& policy) { + DictionaryValue* value = DeviceManagementPolicyCache::DecodePolicy(policy); + { + AutoLock lock(lock_); + policy_.reset(value); + fresh_policy_ = true; + } + + em::DevicePolicyResponse* policy_copy = new em::DevicePolicyResponse; + policy_copy->CopyFrom(policy); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + new PersistPolicyTask(backing_file_path_, policy_copy)); +} + +DictionaryValue* DeviceManagementPolicyCache::GetPolicy() { + AutoLock lock(lock_); + return static_cast<DictionaryValue*>(policy_->DeepCopy()); +} + +// static +Value* DeviceManagementPolicyCache::DecodeIntegerValue( + google::protobuf::int64 value) { + if (value < std::numeric_limits<int>::min() || + value > std::numeric_limits<int>::max()) { + LOG(WARNING) << "Integer value " << value + << " out of numeric limits, ignoring."; + return NULL; + } + + return Value::CreateIntegerValue(static_cast<int>(value)); +} + +// static +Value* DeviceManagementPolicyCache::DecodeValue(const em::GenericValue& value) { + if (!value.has_value_type()) + return NULL; + + switch (value.value_type()) { + case em::GenericValue::VALUE_TYPE_BOOL: + if (value.has_bool_value()) + return Value::CreateBooleanValue(value.bool_value()); + return NULL; + case em::GenericValue::VALUE_TYPE_INT64: + if (value.has_int64_value()) + return DecodeIntegerValue(value.int64_value()); + return NULL; + case em::GenericValue::VALUE_TYPE_STRING: + if (value.has_string_value()) + return Value::CreateStringValue(value.string_value()); + return NULL; + case em::GenericValue::VALUE_TYPE_DOUBLE: + if (value.has_double_value()) + return Value::CreateRealValue(value.double_value()); + return NULL; + case em::GenericValue::VALUE_TYPE_BYTES: + if (value.has_bytes_value()) { + std::string bytes = value.bytes_value(); + return BinaryValue::CreateWithCopiedBuffer(bytes.c_str(), bytes.size()); + } + return NULL; + case em::GenericValue::VALUE_TYPE_BOOL_ARRAY: { + ListValue* list = new ListValue; + RepeatedField<bool>::const_iterator i; + for (i = value.bool_array().begin(); i != value.bool_array().end(); ++i) + list->Append(Value::CreateBooleanValue(*i)); + return list; + } + case em::GenericValue::VALUE_TYPE_INT64_ARRAY_: { + ListValue* list = new ListValue; + RepeatedField<google::protobuf::int64>::const_iterator i; + for (i = value.int64_array().begin(); + i != value.int64_array().end(); ++i) { + Value* int_value = DecodeIntegerValue(*i); + if (int_value) + list->Append(int_value); + } + return list; + } + case em::GenericValue::VALUE_TYPE_STRING_ARRAY: { + ListValue* list = new ListValue; + RepeatedPtrField<std::string>::const_iterator i; + for (i = value.string_array().begin(); + i != value.string_array().end(); ++i) + list->Append(Value::CreateStringValue(*i)); + return list; + } + case em::GenericValue::VALUE_TYPE_DOUBLE_ARRAY: { + ListValue* list = new ListValue; + RepeatedField<double>::const_iterator i; + for (i = value.double_array().begin(); + i != value.double_array().end(); ++i) + list->Append(Value::CreateRealValue(*i)); + return list; + } + default: + NOTREACHED() << "Unhandled value type"; + } + + return NULL; +} + +// static +DictionaryValue* DeviceManagementPolicyCache::DecodePolicy( + const em::DevicePolicyResponse& policy) { + DictionaryValue* result = new DictionaryValue; + RepeatedPtrField<em::DevicePolicySetting>::const_iterator setting; + for (setting = policy.setting().begin(); + setting != policy.setting().end(); + ++setting) { + // No policy value? Skip. + if (!setting->has_policy_value()) + continue; + + // Iterate through all the name-value pairs wrapped in |setting|. + const em::GenericSetting& policy_value(setting->policy_value()); + RepeatedPtrField<em::GenericNamedValue>::const_iterator named_value; + for (named_value = policy_value.named_value().begin(); + named_value != policy_value.named_value().end(); + ++named_value) { + if (named_value->has_value()) { + Value* decoded_value = + DeviceManagementPolicyCache::DecodeValue(named_value->value()); + if (decoded_value) + result->Set(named_value->name(), decoded_value); + } + } + } + return result; +} + +} // namespace policy diff --git a/chrome/browser/policy/device_management_policy_cache.h b/chrome/browser/policy/device_management_policy_cache.h new file mode 100644 index 0000000..a50402f --- /dev/null +++ b/chrome/browser/policy/device_management_policy_cache.h @@ -0,0 +1,76 @@ +// 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_POLICY_DEVICE_MANAGEMENT_POLICY_CACHE_H_ +#define CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_POLICY_CACHE_H_ + +#include "base/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/lock.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" + +class DictionaryValue; +class Value; + +namespace policy { + +namespace em = enterprise_management; + +// Keeps the authoritative copy of cloud policy information as read from the +// persistence file or determined by the policy backend. The cache doesn't talk +// to the service directly, but receives updated policy information through +// SetPolicy() calls, which is then persisted and decoded into the internal +// Value representation chrome uses. +class DeviceManagementPolicyCache { + public: + explicit DeviceManagementPolicyCache(const FilePath& backing_file_path); + + // Loads policy information from the backing file. Non-existing or erroneous + // cache files are ignored. + void LoadPolicyFromFile(); + + // Resets the policy information. + void SetPolicy(const em::DevicePolicyResponse& policy); + + // Gets the policy information. Ownership of the return value is transferred + // to the caller. + DictionaryValue* GetPolicy(); + + private: + friend class DeviceManagementPolicyCacheDecodeTest; + FRIEND_TEST_ALL_PREFIXES(DeviceManagementPolicyCacheDecodeTest, DecodePolicy); + + // Decodes an int64 value. Checks whether the passed value fits the numeric + // limits of the value representation. Returns a value (ownership is + // transferred to the caller) on success, NULL on failure. + static Value* DecodeIntegerValue(google::protobuf::int64 value); + + // Decode a GenericValue message to the Value representation used internally. + // Returns NULL if |value| is invalid (i.e. contains no actual value). + static Value* DecodeValue(const em::GenericValue& value); + + // Decodes a policy message and returns it in Value representation. Ownership + // of the returned dictionary is transferred to the caller. + static DictionaryValue* DecodePolicy( + const em::DevicePolicyResponse& response); + + // The file in which we store a cached version of the policy information. + const FilePath backing_file_path_; + + // Protects |policy_|. + Lock lock_; + + // Policy key-value information. + scoped_ptr<DictionaryValue> policy_; + + // Tracks whether the store received a SetPolicy() call, which overrides any + // information loaded from the file. + bool fresh_policy_; +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_POLICY_CACHE_H_ diff --git a/chrome/browser/policy/device_management_policy_cache_unittest.cc b/chrome/browser/policy/device_management_policy_cache_unittest.cc new file mode 100644 index 0000000..3f9ba42 --- /dev/null +++ b/chrome/browser/policy/device_management_policy_cache_unittest.cc @@ -0,0 +1,294 @@ +// 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/policy/device_management_policy_cache.h" + +#include <limits> + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/scoped_temp_dir.h" +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace policy { + +// Wraps base functionaly for the test cases. +class DeviceManagementPolicyCacheTestBase : public testing::Test { + protected: + // Add a string policy setting to a policy response message. + void AddStringPolicy(em::DevicePolicyResponse* policy, + const std::string& name, + const std::string& value) { + em::DevicePolicySetting* setting = policy->add_setting(); + setting->set_policy_key("test"); + em::GenericSetting* policy_value = setting->mutable_policy_value(); + em::GenericNamedValue* named_value = policy_value->add_named_value(); + named_value->set_name(name); + em::GenericValue* value_container = named_value->mutable_value(); + value_container->set_value_type(em::GenericValue::VALUE_TYPE_STRING); + value_container->set_string_value(value); + } +}; + +// Tests the device management policy cache. +class DeviceManagementPolicyCacheTest + : public DeviceManagementPolicyCacheTestBase { + protected: + DeviceManagementPolicyCacheTest() + : loop_(MessageLoop::TYPE_UI), + ui_thread_(BrowserThread::UI, &loop_), + file_thread_(BrowserThread::FILE, &loop_) {} + + void SetUp() { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + } + + void TearDown() { + loop_.RunAllPending(); + } + + void WritePolicy(const em::DevicePolicyResponse& policy) { + std::string data; + EXPECT_TRUE(policy.SerializeToString(&data)); + EXPECT_EQ(static_cast<int>(data.size()), + file_util::WriteFile(test_file(), data.c_str(), data.size())); + } + + FilePath test_file() { + return temp_dir_.path().AppendASCII("DeviceManagementPolicyCacheTest"); + } + + protected: + MessageLoop loop_; + + private: + ScopedTempDir temp_dir_; + BrowserThread ui_thread_; + BrowserThread file_thread_; +}; + +TEST_F(DeviceManagementPolicyCacheTest, Empty) { + DeviceManagementPolicyCache cache(test_file()); + DictionaryValue empty; + scoped_ptr<Value> policy(cache.GetPolicy()); + EXPECT_TRUE(empty.Equals(policy.get())); +} + +TEST_F(DeviceManagementPolicyCacheTest, LoadNoFile) { + DeviceManagementPolicyCache cache(test_file()); + cache.LoadPolicyFromFile(); + DictionaryValue empty; + scoped_ptr<Value> policy(cache.GetPolicy()); + EXPECT_TRUE(empty.Equals(policy.get())); +} + +TEST_F(DeviceManagementPolicyCacheTest, LoadWithFile) { + WritePolicy(em::DevicePolicyResponse()); + DeviceManagementPolicyCache cache(test_file()); + cache.LoadPolicyFromFile(); + DictionaryValue empty; + scoped_ptr<Value> policy(cache.GetPolicy()); + EXPECT_TRUE(empty.Equals(policy.get())); +} + +TEST_F(DeviceManagementPolicyCacheTest, LoadWithData) { + em::DevicePolicyResponse policy; + AddStringPolicy(&policy, "HomepageLocation", "http://www.example.com"); + WritePolicy(policy); + DeviceManagementPolicyCache cache(test_file()); + cache.LoadPolicyFromFile(); + DictionaryValue expected; + expected.Set("HomepageLocation", + Value::CreateStringValue("http://www.example.com")); + scoped_ptr<Value> policy_value(cache.GetPolicy()); + EXPECT_TRUE(expected.Equals(policy_value.get())); +} + +TEST_F(DeviceManagementPolicyCacheTest, SetPolicy) { + DeviceManagementPolicyCache cache(test_file()); + em::DevicePolicyResponse policy; + AddStringPolicy(&policy, "HomepageLocation", "http://www.example.com"); + cache.SetPolicy(policy); + DictionaryValue expected; + expected.Set("HomepageLocation", + Value::CreateStringValue("http://www.example.com")); + scoped_ptr<Value> policy_value(cache.GetPolicy()); + EXPECT_TRUE(expected.Equals(policy_value.get())); +} + +TEST_F(DeviceManagementPolicyCacheTest, ResetPolicy) { + DeviceManagementPolicyCache cache(test_file()); + + em::DevicePolicyResponse policy; + AddStringPolicy(&policy, "HomepageLocation", "http://www.example.com"); + cache.SetPolicy(policy); + DictionaryValue expected; + expected.Set("HomepageLocation", + Value::CreateStringValue("http://www.example.com")); + scoped_ptr<Value> policy_value(cache.GetPolicy()); + EXPECT_TRUE(expected.Equals(policy_value.get())); + + cache.SetPolicy(em::DevicePolicyResponse()); + policy_value.reset(cache.GetPolicy()); + DictionaryValue empty; + EXPECT_TRUE(empty.Equals(policy_value.get())); +} + +TEST_F(DeviceManagementPolicyCacheTest, PersistPolicy) { + { + DeviceManagementPolicyCache cache(test_file()); + em::DevicePolicyResponse policy; + AddStringPolicy(&policy, "HomepageLocation", "http://www.example.com"); + cache.SetPolicy(policy); + } + + loop_.RunAllPending(); + + EXPECT_TRUE(file_util::PathExists(test_file())); + DeviceManagementPolicyCache cache(test_file()); + cache.LoadPolicyFromFile(); + DictionaryValue expected; + expected.Set("HomepageLocation", + Value::CreateStringValue("http://www.example.com")); + scoped_ptr<Value> policy_value(cache.GetPolicy()); + EXPECT_TRUE(expected.Equals(policy_value.get())); +} + +TEST_F(DeviceManagementPolicyCacheTest, FreshPolicyOverride) { + em::DevicePolicyResponse policy; + AddStringPolicy(&policy, "HomepageLocation", "http://www.example.com"); + WritePolicy(policy); + + DeviceManagementPolicyCache cache(test_file()); + em::DevicePolicyResponse updated_policy; + AddStringPolicy(&updated_policy, "HomepageLocation", + "http://www.chromium.org"); + cache.SetPolicy(updated_policy); + + cache.LoadPolicyFromFile(); + DictionaryValue expected; + expected.Set("HomepageLocation", + Value::CreateStringValue("http://www.chromium.org")); + scoped_ptr<Value> policy_value(cache.GetPolicy()); + EXPECT_TRUE(expected.Equals(policy_value.get())); +} + +// Tests proper decoding of policy values. +class DeviceManagementPolicyCacheDecodeTest + : public DeviceManagementPolicyCacheTestBase { + protected: + void DecodeAndCheck(Value* expected_value_ptr) { + scoped_ptr<Value> expected_value(expected_value_ptr); + scoped_ptr<Value> decoded_value( + DeviceManagementPolicyCache::DecodeValue(value_)); + if (expected_value_ptr) { + ASSERT_TRUE(decoded_value.get()); + EXPECT_TRUE(decoded_value->Equals(expected_value.get())); + } else { + ASSERT_FALSE(decoded_value.get()); + } + } + + em::GenericValue value_; +}; + +TEST_F(DeviceManagementPolicyCacheDecodeTest, Bool) { + value_.set_value_type(em::GenericValue::VALUE_TYPE_BOOL); + value_.set_bool_value(true); + DecodeAndCheck(Value::CreateBooleanValue(true)); +} + +TEST_F(DeviceManagementPolicyCacheDecodeTest, Int64) { + value_.set_value_type(em::GenericValue::VALUE_TYPE_INT64); + value_.set_int64_value(42); + DecodeAndCheck(Value::CreateIntegerValue(42)); +} + +TEST_F(DeviceManagementPolicyCacheDecodeTest, Int64Overflow) { + const int min = std::numeric_limits<int>::min(); + const int max = std::numeric_limits<int>::max(); + value_.set_value_type(em::GenericValue::VALUE_TYPE_INT64); + value_.set_int64_value(min - 1LL); + DecodeAndCheck(NULL); + value_.set_int64_value(max + 1LL); + DecodeAndCheck(NULL); + value_.set_int64_value(min); + DecodeAndCheck(Value::CreateIntegerValue(min)); + value_.set_int64_value(max); + DecodeAndCheck(Value::CreateIntegerValue(max)); +} + +TEST_F(DeviceManagementPolicyCacheDecodeTest, String) { + value_.set_value_type(em::GenericValue::VALUE_TYPE_STRING); + value_.set_string_value("ponies!"); + DecodeAndCheck(Value::CreateStringValue("ponies!")); +} + +TEST_F(DeviceManagementPolicyCacheDecodeTest, Double) { + value_.set_value_type(em::GenericValue::VALUE_TYPE_DOUBLE); + value_.set_double_value(0.42L); + DecodeAndCheck(Value::CreateRealValue(0.42L)); +} + +TEST_F(DeviceManagementPolicyCacheDecodeTest, Bytes) { + std::string data("binary ponies."); + value_.set_value_type(em::GenericValue::VALUE_TYPE_BYTES); + value_.set_bytes_value(data); + DecodeAndCheck( + BinaryValue::CreateWithCopiedBuffer(data.c_str(), data.size())); +} + +TEST_F(DeviceManagementPolicyCacheDecodeTest, BoolArray) { + value_.set_value_type(em::GenericValue::VALUE_TYPE_BOOL_ARRAY); + value_.add_bool_array(false); + value_.add_bool_array(true); + ListValue* list = new ListValue; + list->Append(Value::CreateBooleanValue(false)); + list->Append(Value::CreateBooleanValue(true)); + DecodeAndCheck(list); +} + +TEST_F(DeviceManagementPolicyCacheDecodeTest, Int64Array) { + value_.set_value_type(em::GenericValue::VALUE_TYPE_INT64_ARRAY_); + value_.add_int64_array(42); + value_.add_int64_array(17); + ListValue* list = new ListValue; + list->Append(Value::CreateIntegerValue(42)); + list->Append(Value::CreateIntegerValue(17)); + DecodeAndCheck(list); +} + +TEST_F(DeviceManagementPolicyCacheDecodeTest, StringArray) { + value_.set_value_type(em::GenericValue::VALUE_TYPE_STRING_ARRAY); + value_.add_string_array("ponies"); + value_.add_string_array("more ponies"); + ListValue* list = new ListValue; + list->Append(Value::CreateStringValue("ponies")); + list->Append(Value::CreateStringValue("more ponies")); + DecodeAndCheck(list); +} + +TEST_F(DeviceManagementPolicyCacheDecodeTest, DoubleArray) { + value_.set_value_type(em::GenericValue::VALUE_TYPE_DOUBLE_ARRAY); + value_.add_double_array(0.42L); + value_.add_double_array(0.17L); + ListValue* list = new ListValue; + list->Append(Value::CreateRealValue(0.42L)); + list->Append(Value::CreateRealValue(0.17L)); + DecodeAndCheck(list); +} + +TEST_F(DeviceManagementPolicyCacheDecodeTest, DecodePolicy) { + em::DevicePolicyResponse policy; + AddStringPolicy(&policy, "HomepageLocation", "http://www.example.com"); + scoped_ptr<Value> decoded(DeviceManagementPolicyCache::DecodePolicy(policy)); + DictionaryValue expected; + expected.Set("HomepageLocation", + Value::CreateStringValue("http://www.example.com")); + EXPECT_TRUE(expected.Equals(decoded.get())); +} + +} // namespace policy diff --git a/chrome/browser/policy/device_token_fetcher.cc b/chrome/browser/policy/device_token_fetcher.cc new file mode 100644 index 0000000..9362747 --- /dev/null +++ b/chrome/browser/policy/device_token_fetcher.cc @@ -0,0 +1,165 @@ +// 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/policy/device_token_fetcher.h" + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/singleton.h" +#include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/policy/mock_device_management_backend.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/net/gaia/gaia_constants.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.h" + +namespace { + +static const char kPlaceholderDeviceID[] = "placeholder_device_id"; + +} // namespace + +namespace policy { + +bool UserDirDeviceTokenPathProvider::GetPath(FilePath* path) const { + FilePath dir_path; + if ( PathService::Get(chrome::DIR_USER_DATA, &dir_path)) + return false; + *path = dir_path.Append(FILE_PATH_LITERAL("DeviceManagementToken")); + return true; +} + +DeviceTokenFetcher::DeviceTokenFetcher( + DeviceManagementBackend* backend, + StoredDeviceTokenPathProvider* path_provider) + : backend_(backend), + path_provider_(path_provider), + state_(kStateLoadDeviceTokenFromDisk), + device_token_load_complete_event_(true, false) { +} + +void DeviceTokenFetcher::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(CalledOnValidThread()); + if (type == NotificationType::TOKEN_AVAILABLE) { + const Source<TokenService> token_service(source); + const TokenService::TokenAvailableDetails* token_details = + Details<const TokenService::TokenAvailableDetails>(details).ptr(); + if (token_details->service() == GaiaConstants::kDeviceManagementService && + state_ < kStateHasAuthToken) { + DCHECK_EQ(kStateFetchingAuthToken, state_); + SetState(kStateHasAuthToken); + em::DeviceRegisterRequest register_request; + backend_->ProcessRegisterRequest(token_details->token(), + GetDeviceID(), + register_request, + this); + } + } else { + NOTREACHED(); + } +} + +void DeviceTokenFetcher::HandleRegisterResponse( + const em::DeviceRegisterResponse& response) { + DCHECK(CalledOnValidThread()); + DCHECK_EQ(kStateHasAuthToken, state_); + if (response.has_device_management_token()) { + device_token_ = response.device_management_token(); + FilePath device_token_path; + if (path_provider_->GetPath(&device_token_path)) { + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + NewRunnableFunction(&WriteDeviceTokenToDisk, + device_token_path, + device_token_)); + } + SetState(kStateHasDeviceToken); + } else { + NOTREACHED(); + SetState(kStateFailure); + } +} + +void DeviceTokenFetcher::OnError(DeviceManagementBackend::ErrorCode code) { + DCHECK(CalledOnValidThread()); + SetState(kStateFailure); +} + +void DeviceTokenFetcher::StartFetching() { + DCHECK(CalledOnValidThread()); + if (state_ < kStateHasDeviceToken) { + FilePath device_token_path; + FetcherState new_state = kStateFailure; + if (path_provider_->GetPath(&device_token_path)) { + if (file_util::PathExists(device_token_path)) { + std::string device_token; + if (file_util::ReadFileToString(device_token_path, &device_token_)) { + new_state = kStateHasDeviceToken; + } + } + if (new_state != kStateHasDeviceToken) { + new_state = kStateFetchingAuthToken; + // The policy provider gets initialized with the PrefService and Profile + // before ServiceTokens are available. Install a notification observer + // to ensure that the device management token gets fetched after the + // AuthTokens are available if it's needed. + registrar_.Add(this, + NotificationType::TOKEN_AVAILABLE, + NotificationService::AllSources()); + } + } + SetState(new_state); + } +} + +bool DeviceTokenFetcher::IsTokenPending() { + DCHECK(CalledOnValidThread()); + return !device_token_load_complete_event_.IsSignaled(); +} + +std::string DeviceTokenFetcher::GetDeviceToken() { + DCHECK(CalledOnValidThread()); + device_token_load_complete_event_.Wait(); + return device_token_; +} + +void DeviceTokenFetcher::SetState(FetcherState state) { + DCHECK(CalledOnValidThread()); + state_ = state; + if (state == kStateFailure) { + device_token_load_complete_event_.Signal(); + } else if (state == kStateHasDeviceToken) { + device_token_load_complete_event_.Signal(); + NotificationService::current()->Notify( + NotificationType::DEVICE_TOKEN_AVAILABLE, + Source<DeviceTokenFetcher>(this), + NotificationService::NoDetails()); + } +} + +bool DeviceTokenFetcher::IsTokenValid() const { + return state_ == kStateHasDeviceToken; +} + +// static +void DeviceTokenFetcher::WriteDeviceTokenToDisk( + const FilePath& path, + const std::string& device_token) { + file_util::WriteFile(path, + device_token.c_str(), + device_token.length()); +} + +// static +std::string DeviceTokenFetcher::GetDeviceID() { + // TODO(danno): fetch a real device_id + return kPlaceholderDeviceID; +} + +} diff --git a/chrome/browser/policy/device_token_fetcher.h b/chrome/browser/policy/device_token_fetcher.h new file mode 100644 index 0000000..f5a0770 --- /dev/null +++ b/chrome/browser/policy/device_token_fetcher.h @@ -0,0 +1,134 @@ +// 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_POLICY_DEVICE_TOKEN_FETCHER_H_ +#define CHROME_BROWSER_POLICY_DEVICE_TOKEN_FETCHER_H_ +#pragma once + +#include <string> + +#include "base/file_path.h" +#include "base/non_thread_safe.h" +#include "base/waitable_event.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/browser/policy/device_management_backend.h" + +namespace policy { + +namespace em = enterprise_management; + +// Abstracts how the path is determined where the DeviceTokenFetcher stores the +// device token once it has been returned from the server. Tests provide a mock +// implementation to the DeviceTokenFetcher that doesn't write to DIR_USER_DATA. +class StoredDeviceTokenPathProvider { + public: + virtual ~StoredDeviceTokenPathProvider() {} + + // Sets |path| to contain the path at which to use to store the device + // management token file. Returns true if successful, otherwise false. + virtual bool GetPath(FilePath* path) const = 0; + protected: + StoredDeviceTokenPathProvider() {} + private: + DISALLOW_COPY_AND_ASSIGN(StoredDeviceTokenPathProvider); +}; + +// Provides a path to the device token that's inside DIR_USER_DATA. +class UserDirDeviceTokenPathProvider : public StoredDeviceTokenPathProvider { + public: + UserDirDeviceTokenPathProvider() {} + virtual ~UserDirDeviceTokenPathProvider() {} + virtual bool GetPath(FilePath* path) const; + private: + DISALLOW_COPY_AND_ASSIGN(UserDirDeviceTokenPathProvider); +}; + +// Fetches the device token that can be used for policy requests with the device +// management server, either from disk if it already has been successfully +// requested, otherwise from the device management server. An instance of the +// fetcher is shared as a singleton by all users of the device management token +// to ensure they all get the same token. +class DeviceTokenFetcher + : public NonThreadSafe, + public NotificationObserver, + public DeviceManagementBackend::DeviceRegisterResponseDelegate { + public: + // Requests to the device management server are sent through |backend|. The + // DeviceTokenFetcher assumes ownership of |backend|, which is passed in + // explicitly to simplify mocking of the backend for unit testing. The + // fetcher uses |path_provider| to determine the directory in which the device + // token is stored once it's retrieved from the server. The fetcher assumes + // ownership of |path_provider|. + DeviceTokenFetcher(DeviceManagementBackend* backend, + StoredDeviceTokenPathProvider* path_provider); + virtual ~DeviceTokenFetcher() {} + + // NotificationObserver method overrides: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // DeviceManagementBackend::DeviceRegisterResponseDelegate method overrides: + virtual void HandleRegisterResponse( + const em::DeviceRegisterResponse& response); + virtual void OnError(DeviceManagementBackend::ErrorCode code); + + // Called by subscribers of the device management token to indicate that they + // will need the token in the future. + void StartFetching(); + + // Returns true if there is a pending token request to the device management + // server. + bool IsTokenPending(); + + // Returns the device management token for this device, blocking until + // outstanding requests to the device management server are satisfied. In the + // case that the token could not be fetched, an empty string is returned. + std::string GetDeviceToken(); + + // True if the device token has been fetched and is valid. + bool IsTokenValid() const; + + private: + // The different states that the fetcher can be in during the process of + // getting the device token. + enum FetcherState { + kStateLoadDeviceTokenFromDisk, + kStateFetchingAuthToken, + kStateHasAuthToken, + kStateHasDeviceToken, + kStateFailure + }; + + // Moves the fetcher into a new state. If the fetcher has the device token + // or is moving into the failure state, callers waiting on WaitForToken + // are unblocked. + void SetState(FetcherState state); + + // Saves the device management token to disk once it has been retrieved from + // the server. Must be called on the FILE thread. + static void WriteDeviceTokenToDisk(const FilePath& path, + const std::string& token); + + // Returns the device ID used to register the device with the device + // management server and generate the device token. + static std::string GetDeviceID(); + + scoped_ptr<DeviceManagementBackend> backend_; + scoped_ptr<StoredDeviceTokenPathProvider> path_provider_; + FetcherState state_; + std::string device_token_; + + // An event that is signaled only once the device token has been fetched + // or it has been determined that there was an error during fetching. + base::WaitableEvent device_token_load_complete_event_; + + // Registers the fetcher for notification of successful Gaia logins. + NotificationRegistrar registrar_; +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_DEVICE_TOKEN_FETCHER_H_ diff --git a/chrome/browser/policy/device_token_fetcher_unittest.cc b/chrome/browser/policy/device_token_fetcher_unittest.cc new file mode 100644 index 0000000..3d15d1b --- /dev/null +++ b/chrome/browser/policy/device_token_fetcher_unittest.cc @@ -0,0 +1,178 @@ +// 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 "base/file_util.h" +#include "base/message_loop.h" +#include "base/scoped_temp_dir.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/policy/device_token_fetcher.h" +#include "chrome/browser/policy/mock_device_management_backend.h" +#include "chrome/common/net/gaia/gaia_constants.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace policy { + +using testing::_; +using testing::Mock; + +class MockDeviceTokenPathProvider + : public StoredDeviceTokenPathProvider { + public: + MockDeviceTokenPathProvider() { + EXPECT_TRUE(temp_user_data_dir_.CreateUniqueTempDir()); + } + + virtual ~MockDeviceTokenPathProvider() {} + + virtual bool GetPath(FilePath* path) const { + if (!file_util::PathExists(temp_user_data_dir_.path())) + return false; + *path = temp_user_data_dir_.path().Append( + FILE_PATH_LITERAL("temp_token_file")); + return true; + } + + private: + ScopedTempDir temp_user_data_dir_; + + DISALLOW_COPY_AND_ASSIGN(MockDeviceTokenPathProvider); +}; + +class MockTokenAvailableObserver : public NotificationObserver { + public: + MockTokenAvailableObserver() {} + virtual ~MockTokenAvailableObserver() {} + + MOCK_METHOD3(Observe, void( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockTokenAvailableObserver); +}; + +class DeviceTokenFetcherTest : public testing::Test { + protected: + DeviceTokenFetcherTest() { + backend_ = new MockDeviceManagementBackend(); + path_provider_ = new MockDeviceTokenPathProvider(); + fetcher_.reset(new DeviceTokenFetcher(backend_, path_provider_)); + fetcher_->StartFetching(); + } + + virtual void SetUp() { + ui_thread_.reset(new BrowserThread(BrowserThread::UI, &loop_)); + file_thread_.reset(new BrowserThread(BrowserThread::FILE, &loop_)); + } + + virtual void TearDown() { + loop_.RunAllPending(); + } + + void SimulateSuccessfulLogin() { + const std::string service(GaiaConstants::kDeviceManagementService); + const std::string auth_token(kFakeGaiaAuthToken); + const Source<TokenService> source(NULL); + TokenService::TokenAvailableDetails details(service, auth_token); + NotificationService::current()->Notify( + NotificationType::TOKEN_AVAILABLE, + source, + Details<const TokenService::TokenAvailableDetails>(&details)); + loop_.RunAllPending(); + } + + MockDeviceManagementBackend* backend_; // weak + MockDeviceTokenPathProvider* path_provider_; // weak + scoped_ptr<DeviceTokenFetcher> fetcher_; + + private: + MessageLoop loop_; + scoped_ptr<BrowserThread> ui_thread_; + scoped_ptr<BrowserThread> file_thread_; + + static const char kFakeGaiaAuthToken[]; +}; + +const char DeviceTokenFetcherTest::kFakeGaiaAuthToken[] = "0123456789abcdef"; + +TEST_F(DeviceTokenFetcherTest, IsPending) { + ASSERT_TRUE(fetcher_->IsTokenPending()); + SimulateSuccessfulLogin(); + ASSERT_FALSE(fetcher_->IsTokenPending()); +} + +TEST_F(DeviceTokenFetcherTest, SimpleFetchSingleLogin) { + SimulateSuccessfulLogin(); + ASSERT_FALSE(fetcher_->IsTokenPending()); + ASSERT_TRUE(fetcher_->IsTokenValid()); + const std::string token(fetcher_->GetDeviceToken()); + EXPECT_NE("", token); +} + +TEST_F(DeviceTokenFetcherTest, SimpleFetchDoubleLogin) { + SimulateSuccessfulLogin(); + ASSERT_FALSE(fetcher_->IsTokenPending()); + const std::string token(fetcher_->GetDeviceToken()); + EXPECT_NE("", token); + + SimulateSuccessfulLogin(); + ASSERT_FALSE(fetcher_->IsTokenPending()); + const std::string token2(fetcher_->GetDeviceToken()); + EXPECT_NE("", token2); + EXPECT_EQ(token, token2); +} + +TEST_F(DeviceTokenFetcherTest, FetchBetweenBrowserLaunchAndNotify) { + NotificationRegistrar registrar; + MockTokenAvailableObserver observer; + registrar.Add(&observer, + NotificationType::DEVICE_TOKEN_AVAILABLE, + NotificationService::AllSources()); + EXPECT_CALL(observer, Observe(_, _, _)).Times(1); + + SimulateSuccessfulLogin(); + ASSERT_FALSE(fetcher_->IsTokenPending()); + const std::string token(fetcher_->GetDeviceToken()); + EXPECT_NE("", token); + + Mock::VerifyAndClearExpectations(&observer); + EXPECT_CALL(observer, Observe(_, _, _)).Times(1); + + // Swap out the fetchers, including copying the device management token on + // disk to where the new fetcher expects it. + backend_ = new MockDeviceManagementBackend(); + FilePath old_path; + ASSERT_TRUE(path_provider_->GetPath(&old_path)); + MockDeviceTokenPathProvider* new_provider = + new MockDeviceTokenPathProvider(); + FilePath new_path; + ASSERT_TRUE(new_provider->GetPath(&new_path)); + ASSERT_TRUE(file_util::Move(old_path, new_path)); + path_provider_ = new_provider; + fetcher_.reset(new DeviceTokenFetcher(backend_, path_provider_)); + + fetcher_->StartFetching(); + ASSERT_FALSE(fetcher_->IsTokenPending()); + const std::string token2(fetcher_->GetDeviceToken()); + EXPECT_NE("", token2); + EXPECT_EQ(token, token2); +} + +TEST_F(DeviceTokenFetcherTest, FailedServerRequest) { + backend_->SetFailure(true); + SimulateSuccessfulLogin(); + ASSERT_FALSE(fetcher_->IsTokenPending()); + const std::string token(fetcher_->GetDeviceToken()); + EXPECT_EQ("", token); +} + +} // namespace policy diff --git a/chrome/browser/policy/dummy_configuration_policy_provider.h b/chrome/browser/policy/dummy_configuration_policy_provider.h index fde6c40..2c519cb 100644 --- a/chrome/browser/policy/dummy_configuration_policy_provider.h +++ b/chrome/browser/policy/dummy_configuration_policy_provider.h @@ -6,7 +6,7 @@ #define CHROME_BROWSER_POLICY_DUMMY_CONFIGURATION_POLICY_PROVIDER_H_ #pragma once -#include "chrome/browser/policy/configuration_policy_store.h" +#include "chrome/browser/policy/configuration_policy_store_interface.h" #include "chrome/browser/policy/configuration_policy_provider.h" namespace policy { @@ -14,12 +14,12 @@ namespace policy { class DummyConfigurationPolicyProvider : public ConfigurationPolicyProvider { public: explicit DummyConfigurationPolicyProvider( - const StaticPolicyValueMap& policy_map) - : ConfigurationPolicyProvider(policy_map) { + const PolicyDefinitionList* policy_list) + : ConfigurationPolicyProvider(policy_list) { } virtual ~DummyConfigurationPolicyProvider() {} - virtual bool Provide(ConfigurationPolicyStore* store) { + virtual bool Provide(ConfigurationPolicyStoreInterface* store) { return true; } diff --git a/chrome/browser/policy/file_based_policy_provider.cc b/chrome/browser/policy/file_based_policy_provider.cc new file mode 100644 index 0000000..5a92105 --- /dev/null +++ b/chrome/browser/policy/file_based_policy_provider.cc @@ -0,0 +1,259 @@ +// 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/policy/file_based_policy_provider.h" + +#include <set> + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/task.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/common/json_value_serializer.h" + +namespace policy { + +// Amount of time we wait for the files on disk to settle before trying to load +// it. This alleviates the problem of reading partially written files and allows +// to batch quasi-simultaneous changes. +const int kSettleIntervalSeconds = 5; + +// The time interval for rechecking policy. This is our fallback in case the +// file path watch fails or doesn't report a change. +const int kReloadIntervalMinutes = 15; + +// FileBasedPolicyProvider implementation: + +FileBasedPolicyProvider::Delegate::~Delegate() { +} + +FileBasedPolicyProvider::Delegate::Delegate(const FilePath& config_file_path) + : config_file_path_(config_file_path) { +} + +FileBasedPolicyProvider::FileBasedPolicyProvider( + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, + FileBasedPolicyProvider::Delegate* delegate) + : ConfigurationPolicyProvider(policy_list) { + loader_ = new FileBasedPolicyLoader(AsWeakPtr(), + delegate, + kSettleIntervalSeconds, + kReloadIntervalMinutes); + watcher_ = new FileBasedPolicyWatcher; + watcher_->Init(loader_.get()); +} + +FileBasedPolicyProvider::~FileBasedPolicyProvider() { + loader_->Stop(); +} + +bool FileBasedPolicyProvider::Provide( + ConfigurationPolicyStoreInterface* store) { + scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); + DCHECK(policy.get()); + DecodePolicyValueTree(policy.get(), store); + return true; +} + +void FileBasedPolicyProvider::DecodePolicyValueTree( + DictionaryValue* policies, + ConfigurationPolicyStoreInterface* store) { + const PolicyDefinitionList* policy_list(policy_definition_list()); + for (const PolicyDefinitionList::Entry* i = policy_list->begin; + i != policy_list->end; ++i) { + Value* value; + if (policies->Get(i->name, &value) && value->IsType(i->value_type)) + store->Apply(i->policy_type, value->DeepCopy()); + } + + // TODO(mnissler): Handle preference overrides once |ConfigurationPolicyStore| + // supports it. +} + +// FileBasedPolicyLoader implementation: + +FileBasedPolicyLoader::FileBasedPolicyLoader( + base::WeakPtr<ConfigurationPolicyProvider> provider, + FileBasedPolicyProvider::Delegate* delegate, + int settle_interval_seconds, + int reload_interval_minutes) + : delegate_(delegate), + provider_(provider), + origin_loop_(MessageLoop::current()), + reload_task_(NULL), + settle_interval_seconds_(settle_interval_seconds), + reload_interval_minutes_(reload_interval_minutes) { + // Force an initial load, so GetPolicy() works. + policy_.reset(delegate_->Load()); + DCHECK(policy_.get()); +} + +void FileBasedPolicyLoader::Stop() { + if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, &FileBasedPolicyLoader::Stop)); + return; + } + + if (reload_task_) { + reload_task_->Cancel(); + reload_task_ = NULL; + } +} + +void FileBasedPolicyLoader::Reload() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + // Check the directory time in order to see whether a reload is required. + base::TimeDelta delay; + base::Time now = base::Time::Now(); + if (!IsSafeToReloadPolicy(now, &delay)) { + ScheduleReloadTask(delay); + return; + } + + // Load the policy definitions. + scoped_ptr<DictionaryValue> new_policy(delegate_->Load()); + + // Check again in case the directory has changed while reading it. + if (!IsSafeToReloadPolicy(now, &delay)) { + ScheduleReloadTask(delay); + return; + } + + // Replace policy definition. + bool changed = false; + { + AutoLock lock(lock_); + changed = !policy_->Equals(new_policy.get()); + policy_.reset(new_policy.release()); + } + + // There's a change, report it! + if (changed) { + VLOG(0) << "Policy reload from " << config_file_path().value() + << " succeeded."; + origin_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &FileBasedPolicyLoader::NotifyPolicyChanged)); + } + + // As a safeguard in case the file watcher fails, schedule a reload task + // that'll make us recheck after a reasonable interval. + ScheduleReloadTask(base::TimeDelta::FromMinutes(reload_interval_minutes_)); +} + +DictionaryValue* FileBasedPolicyLoader::GetPolicy() { + AutoLock lock(lock_); + return static_cast<DictionaryValue*>(policy_->DeepCopy()); +} + +void FileBasedPolicyLoader::OnFilePathChanged(const FilePath& path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + Reload(); +} + +void FileBasedPolicyLoader::OnError() { + LOG(ERROR) << "FilePathWatcher on " << config_file_path().value() + << " failed."; +} + +FileBasedPolicyLoader::~FileBasedPolicyLoader() { +} + +bool FileBasedPolicyLoader::IsSafeToReloadPolicy(const base::Time& now, + base::TimeDelta* delay) { + DCHECK(delay); + + // A null modification time indicates there's no data. + base::Time last_modification(delegate_->GetLastModification()); + if (last_modification.is_null()) + return true; + + // If there was a change since the last recorded modification, wait some more. + base::TimeDelta settleInterval( + base::TimeDelta::FromSeconds(settle_interval_seconds_)); + if (last_modification != last_modification_file_) { + last_modification_file_ = last_modification; + last_modification_clock_ = now; + *delay = settleInterval; + return false; + } + + // Check whether the settle interval has elapsed. + base::TimeDelta age = now - last_modification_clock_; + if (age < settleInterval) { + *delay = settleInterval - age; + return false; + } + + return true; +} + +void FileBasedPolicyLoader::ScheduleReloadTask(const base::TimeDelta& delay) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + if (reload_task_) + reload_task_->Cancel(); + + reload_task_ = + NewRunnableMethod(this, &FileBasedPolicyLoader::ReloadFromTask); + BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_, + delay.InMilliseconds()); +} + +void FileBasedPolicyLoader::NotifyPolicyChanged() { + DCHECK_EQ(origin_loop_, MessageLoop::current()); + if (provider_) + provider_->NotifyStoreOfPolicyChange(); +} + +void FileBasedPolicyLoader::ReloadFromTask() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + // Drop the reference to the reload task, since the task might be the only + // referer that keeps us alive, so we should not Cancel() it. + reload_task_ = NULL; + + Reload(); +} + +// FileBasedPolicyWatcher implementation: + +FileBasedPolicyWatcher::FileBasedPolicyWatcher() { +} + +void FileBasedPolicyWatcher::Init(FileBasedPolicyLoader* loader) { + // Initialization can happen early when the file thread is not yet available. + // So post a task to ourselves on the UI thread which will run after threading + // is up and schedule watch initialization on the file thread. + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, + &FileBasedPolicyWatcher::InitWatcher, + scoped_refptr<FileBasedPolicyLoader>(loader))); +} + +FileBasedPolicyWatcher::~FileBasedPolicyWatcher() { +} + +void FileBasedPolicyWatcher::InitWatcher( + const scoped_refptr<FileBasedPolicyLoader>& loader) { + if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, &FileBasedPolicyWatcher::InitWatcher, loader)); + return; + } + + if (!loader->config_file_path().empty() && + !watcher_.Watch(loader->config_file_path(), loader.get())) + loader->OnError(); + + // There might have been changes to the directory in the time between + // construction of the loader and initialization of the watcher. Call reload + // to detect if that is the case. + loader->Reload(); +} + +} // namespace policy diff --git a/chrome/browser/policy/file_based_policy_provider.h b/chrome/browser/policy/file_based_policy_provider.h new file mode 100644 index 0000000..49af11f --- /dev/null +++ b/chrome/browser/policy/file_based_policy_provider.h @@ -0,0 +1,203 @@ +// 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_POLICY_FILE_BASED_POLICY_PROVIDER_H_ +#define CHROME_BROWSER_POLICY_FILE_BASED_POLICY_PROVIDER_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/lock.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "base/weak_ptr.h" +#include "chrome/browser/file_path_watcher.h" +#include "chrome/browser/policy/configuration_policy_provider.h" + +class CancelableTask; +class DictionaryValue; +class MessageLoop; + +namespace policy { + +class FileBasedPolicyLoader; +class FileBasedPolicyWatcher; + +// File based policy provider that coordinates watching and reloading policy +// information from the configuration path. Actual logic for loading policy +// information is handled by a delegate passed at construction time. +class FileBasedPolicyProvider + : public ConfigurationPolicyProvider, + public base::SupportsWeakPtr<FileBasedPolicyProvider> { + public: + // Delegate interface for actual policy loading from the system. + class Delegate { + public: + virtual ~Delegate(); + + // Loads the policy information. Ownership of the return value is + // transferred to the caller. + virtual DictionaryValue* Load() = 0; + + // Gets the last modification timestamp for the policy information from the + // filesystem. Returns base::Time() if the information is not present, in + // which case Load() should return an empty dictionary. + virtual base::Time GetLastModification() = 0; + + const FilePath& config_file_path() { return config_file_path_; } + + protected: + explicit Delegate(const FilePath& config_file_path); + + private: + // The path at which we look for configuration files. + const FilePath config_file_path_; + + DISALLOW_COPY_AND_ASSIGN(Delegate); + }; + + // Assumes ownership of |delegate|. + FileBasedPolicyProvider(const PolicyDefinitionList* policy_list, + Delegate* delegate); + virtual ~FileBasedPolicyProvider(); + + // ConfigurationPolicyProvider implementation. + virtual bool Provide(ConfigurationPolicyStoreInterface* store); + + private: + // Decodes the value tree and writes the configuration to the given |store|. + void DecodePolicyValueTree(DictionaryValue* policies, + ConfigurationPolicyStoreInterface* store); + + // Watches for changes to the configuration directory. + scoped_refptr<FileBasedPolicyWatcher> watcher_; + + // The loader object we use internally. + scoped_refptr<FileBasedPolicyLoader> loader_; + + DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyProvider); +}; + +// FilePathWatcher delegate implementation that handles change notifications for +// the configuration file or directory. It keeps the authorative version of the +// currently effective policy dictionary and updates it as appropriate. The +// actual loading logic is handled by a delegate. +class FileBasedPolicyLoader : public FilePathWatcher::Delegate { + public: + // Creates a new loader that'll load its data from |config_file_path|. + // Assumes ownership of |delegate|, which provides the actual loading logic. + // The parameters |settle_interval_seconds| and |reload_interval_minutes| + // specify the time to wait before reading the file contents after a change + // and the period for checking |config_file_path| for changes, respectively. + FileBasedPolicyLoader(base::WeakPtr<ConfigurationPolicyProvider> provider, + FileBasedPolicyProvider::Delegate* delegate, + int settle_interval_seconds, + int reload_interval_minutes); + + // Stops any pending reload tasks. + void Stop(); + + // Reloads the policies and sends out a notification, if appropriate. Must be + // called on the file thread. + void Reload(); + + // Gets the current dictionary value object. Ownership of the returned value + // is transferred to the caller. + DictionaryValue* GetPolicy(); + + const FilePath& config_file_path() { return delegate_->config_file_path(); } + + // FilePathWatcher::Delegate implementation: + void OnFilePathChanged(const FilePath& path); + void OnError(); + + private: + // FileBasedPolicyLoader objects should only be deleted by + // RefCountedThreadSafe. + friend class base::RefCountedThreadSafe<FileBasedPolicyLoader>; + virtual ~FileBasedPolicyLoader(); + + // Checks whether reading policy information is safe to do. If not, returns + // false and the delay until it is considered safe to reload in |delay|. + bool IsSafeToReloadPolicy(const base::Time& now, base::TimeDelta* delay); + + // Schedules a reload task to run when |delay| expires. Must be called on the + // file thread. + void ScheduleReloadTask(const base::TimeDelta& delay); + + // Notifies the policy provider to send out a policy changed notification. + // Must be called on |origin_loop_|. + void NotifyPolicyChanged(); + + // Invoked from the reload task on the file thread. + void ReloadFromTask(); + + // The delegate. + scoped_ptr<FileBasedPolicyProvider::Delegate> delegate_; + + // The provider this loader is associated with. Access only on the thread that + // called the constructor. See |origin_loop_| below. + base::WeakPtr<ConfigurationPolicyProvider> provider_; + + // The message loop on which this object was constructed and |provider_| + // received on. Recorded so we can call back into the non thread safe provider + // to fire the notification. + MessageLoop* origin_loop_; + + // Records last known modification timestamp of |config_file_path_|. + base::Time last_modification_file_; + + // The wall clock time at which the last modification timestamp was recorded. + // It's better to not assume the file notification time and the wall clock + // times come from the same source, just in case there is some non-local + // filesystem involved. + base::Time last_modification_clock_; + + // Protects |policy_|. + Lock lock_; + + // The current policy definition. + scoped_ptr<DictionaryValue> policy_; + + // The reload task. Access only on the file thread. Holds a reference to the + // currently posted task, so we can cancel and repost it if necessary. + CancelableTask* reload_task_; + + // Settle and reload intervals. + const int settle_interval_seconds_; + const int reload_interval_minutes_; + + DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyLoader); +}; + +// Wraps a FilePathWatcher for the configuration path and takes care of +// initializing the watcher object on the file thread. +class FileBasedPolicyWatcher + : public base::RefCountedThreadSafe<FileBasedPolicyWatcher> { + public: + FileBasedPolicyWatcher(); + + // Runs initialization. This is in a separate method since we need to post a + // task (which cannot be done from the constructor). + void Init(FileBasedPolicyLoader* loader); + + private: + // FileBasedPolicyWatcher objects should only be deleted by + // RefCountedThreadSafe. + friend class base::RefCountedThreadSafe<FileBasedPolicyWatcher>; + virtual ~FileBasedPolicyWatcher(); + + // Actually sets up the watch with the FilePathWatcher code. + void InitWatcher(const scoped_refptr<FileBasedPolicyLoader>& loader); + + // Wrapped watcher that takes care of the actual watching. + FilePathWatcher watcher_; + + DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyWatcher); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_FILE_BASED_POLICY_PROVIDER_H_ diff --git a/chrome/browser/policy/file_based_policy_provider_unittest.cc b/chrome/browser/policy/file_based_policy_provider_unittest.cc new file mode 100644 index 0000000..c78c586 --- /dev/null +++ b/chrome/browser/policy/file_based_policy_provider_unittest.cc @@ -0,0 +1,131 @@ +// 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/policy/configuration_policy_pref_store.h" +#include "chrome/browser/policy/file_based_policy_provider.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Mock; + +namespace policy { + +// Shorter reload intervals for testing FileBasedPolicyLoader. +const int kSettleIntervalSecondsForTesting = 0; +const int kReloadIntervalMinutesForTesting = 1; + +// A delegate for testing that can feed arbitrary information to the loader. +class TestDelegate : public FileBasedPolicyProvider::Delegate { + public: + TestDelegate() + : FileBasedPolicyProvider::Delegate(FilePath(FILE_PATH_LITERAL("fake"))) { + } + + // FileBasedPolicyProvider::Delegate implementation: + virtual DictionaryValue* Load() { + return static_cast<DictionaryValue*>(dict_.DeepCopy()); + } + + virtual base::Time GetLastModification() { + return last_modification_; + } + + DictionaryValue* dict() { return &dict_; } + void set_last_modification(const base::Time& last_modification) { + last_modification_ = last_modification; + } + + private: + DictionaryValue dict_; + base::Time last_modification_; +}; + +// A mock provider that allows us to capture reload notifications. +class MockPolicyProvider : public ConfigurationPolicyProvider, + public base::SupportsWeakPtr<MockPolicyProvider> { + public: + explicit MockPolicyProvider() + : ConfigurationPolicyProvider( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList()) { + } + + virtual bool Provide(ConfigurationPolicyStoreInterface* store) { + return true; + } + + MOCK_METHOD0(NotifyStoreOfPolicyChange, void()); +}; + +class FileBasedPolicyLoaderTest : public testing::Test { + protected: + FileBasedPolicyLoaderTest() + : ui_thread_(BrowserThread::UI, &loop_), + file_thread_(BrowserThread::FILE, &loop_) {} + + virtual void TearDown() { + loop_.RunAllPending(); + } + + MessageLoop loop_; + + private: + BrowserThread ui_thread_; + BrowserThread file_thread_; +}; + +TEST_F(FileBasedPolicyLoaderTest, BasicLoad) { + TestDelegate* test_delegate = new TestDelegate; + test_delegate->dict()->SetString("HomepageLocation", "http://www.google.com"); + + scoped_refptr<FileBasedPolicyLoader> loader( + new FileBasedPolicyLoader(base::WeakPtr<FileBasedPolicyProvider>(), + test_delegate, + kSettleIntervalSecondsForTesting, + kReloadIntervalMinutesForTesting)); + scoped_ptr<DictionaryValue> policy(loader->GetPolicy()); + EXPECT_TRUE(policy.get()); + EXPECT_EQ(1U, policy->size()); + + std::string str_value; + EXPECT_TRUE(policy->GetString("HomepageLocation", &str_value)); + EXPECT_EQ("http://www.google.com", str_value); + + loader->Stop(); +} + +TEST_F(FileBasedPolicyLoaderTest, TestRefresh) { + MockPolicyProvider provider; + TestDelegate* test_delegate = new TestDelegate; + + scoped_refptr<FileBasedPolicyLoader> loader( + new FileBasedPolicyLoader(provider.AsWeakPtr(), + test_delegate, + kSettleIntervalSecondsForTesting, + kReloadIntervalMinutesForTesting)); + scoped_ptr<DictionaryValue> policy(loader->GetPolicy()); + EXPECT_TRUE(policy.get()); + EXPECT_EQ(0U, policy->size()); + + test_delegate->dict()->SetString("HomepageLocation", "http://www.google.com"); + + EXPECT_CALL(provider, NotifyStoreOfPolicyChange()).Times(1); + loader->OnFilePathChanged(FilePath(FILE_PATH_LITERAL("fake"))); + + // Run the loop. The refresh should be handled immediately since the settle + // interval has been disabled. + loop_.RunAllPending(); + Mock::VerifyAndClearExpectations(&provider); + + policy.reset(loader->GetPolicy()); + EXPECT_TRUE(policy.get()); + EXPECT_EQ(1U, policy->size()); + + std::string str_value; + EXPECT_TRUE(policy->GetString("HomepageLocation", &str_value)); + EXPECT_EQ("http://www.google.com", str_value); + + loader->Stop(); +} + +} // namespace policy diff --git a/chrome/browser/policy/mock_configuration_policy_provider.cc b/chrome/browser/policy/mock_configuration_policy_provider.cc new file mode 100644 index 0000000..4f11497 --- /dev/null +++ b/chrome/browser/policy/mock_configuration_policy_provider.cc @@ -0,0 +1,35 @@ +// 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/policy/mock_configuration_policy_provider.h" + +#include "chrome/browser/policy/configuration_policy_pref_store.h" + +namespace policy { + +MockConfigurationPolicyProvider::MockConfigurationPolicyProvider() + : ConfigurationPolicyProvider( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList()) { +} + +MockConfigurationPolicyProvider::~MockConfigurationPolicyProvider() { + STLDeleteValues(&policy_map_); +} + +void MockConfigurationPolicyProvider::AddPolicy(ConfigurationPolicyType policy, + Value* value) { + std::swap(policy_map_[policy], value); + delete value; +} + +bool MockConfigurationPolicyProvider::Provide( + ConfigurationPolicyStoreInterface* store) { + for (PolicyMap::const_iterator current = policy_map_.begin(); + current != policy_map_.end(); ++current) { + store->Apply(current->first, current->second->DeepCopy()); + } + return true; +} + +} diff --git a/chrome/browser/policy/mock_configuration_policy_provider.h b/chrome/browser/policy/mock_configuration_policy_provider.h index 6b23613..8ba8a88 100644 --- a/chrome/browser/policy/mock_configuration_policy_provider.h +++ b/chrome/browser/policy/mock_configuration_policy_provider.h @@ -18,31 +18,17 @@ namespace policy { // values for polices. class MockConfigurationPolicyProvider : public ConfigurationPolicyProvider { public: - MockConfigurationPolicyProvider() - : ConfigurationPolicyProvider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap()) { - } - ~MockConfigurationPolicyProvider() { - STLDeleteValues(&policy_map_); - } + MockConfigurationPolicyProvider(); + virtual ~MockConfigurationPolicyProvider(); - typedef std::map<ConfigurationPolicyStore::PolicyType, Value*> PolicyMap; - - void AddPolicy(ConfigurationPolicyStore::PolicyType policy, Value* value) { - std::swap(policy_map_[policy], value); - delete value; - } + void AddPolicy(ConfigurationPolicyType policy, Value* value); // ConfigurationPolicyProvider method overrides. - virtual bool Provide(ConfigurationPolicyStore* store) { - for (PolicyMap::const_iterator current = policy_map_.begin(); - current != policy_map_.end(); ++current) { - store->Apply(current->first, current->second->DeepCopy()); - } - return true; - } + virtual bool Provide(ConfigurationPolicyStoreInterface* store); private: + typedef std::map<ConfigurationPolicyType, Value*> PolicyMap; + PolicyMap policy_map_; }; diff --git a/chrome/browser/policy/mock_configuration_policy_store.h b/chrome/browser/policy/mock_configuration_policy_store.h index 29c4c5a..87dc646 100644 --- a/chrome/browser/policy/mock_configuration_policy_store.h +++ b/chrome/browser/policy/mock_configuration_policy_store.h @@ -10,30 +10,31 @@ #include <utility> #include "base/stl_util-inl.h" -#include "chrome/browser/policy/configuration_policy_store.h" +#include "chrome/browser/policy/configuration_policy_store_interface.h" namespace policy { // Mock ConfigurationPolicyStore implementation that records values for policy // settings as they get set. -class MockConfigurationPolicyStore : public ConfigurationPolicyStore { +class MockConfigurationPolicyStore : public ConfigurationPolicyStoreInterface { public: + typedef std::map<ConfigurationPolicyType, Value*> PolicyMap; + MockConfigurationPolicyStore() {} ~MockConfigurationPolicyStore() { STLDeleteValues(&policy_map_); } - typedef std::map<ConfigurationPolicyStore::PolicyType, Value*> PolicyMap; - const PolicyMap& policy_map() { return policy_map_; } + const PolicyMap& policy_map() const { return policy_map_; } // Get a value for the given policy. Returns NULL if that key doesn't exist. - const Value* Get(ConfigurationPolicyStore::PolicyType type) const { + const Value* Get(ConfigurationPolicyType type) const { PolicyMap::const_iterator entry(policy_map_.find(type)); return entry == policy_map_.end() ? NULL : entry->second; } // ConfigurationPolicyStore implementation. - virtual void Apply(PolicyType policy, Value* value) { + virtual void Apply(ConfigurationPolicyType policy, Value* value) { std::swap(policy_map_[policy], value); delete value; } diff --git a/chrome/browser/policy/mock_device_management_backend.cc b/chrome/browser/policy/mock_device_management_backend.cc new file mode 100644 index 0000000..61a20e9 --- /dev/null +++ b/chrome/browser/policy/mock_device_management_backend.cc @@ -0,0 +1,61 @@ +// 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 "base/logging.h" +#include "chrome/browser/policy/mock_device_management_backend.h" + +namespace { + +static const char kFakeDeviceManagementToken[] = "FAKE_DEVICE_TOKEN_"; +static char next_token_suffix_ = '0'; + +} // namespace + +namespace policy { + +using enterprise_management::DeviceRegisterRequest; +using enterprise_management::DeviceUnregisterRequest; +using enterprise_management::DevicePolicyRequest; +using enterprise_management::DeviceRegisterResponse; +using enterprise_management::DeviceUnregisterResponse; +using enterprise_management::DevicePolicyResponse; + +MockDeviceManagementBackend::MockDeviceManagementBackend() + : DeviceManagementBackend(), + failure_(false) { +} + +void MockDeviceManagementBackend::ProcessRegisterRequest( + const std::string& auth_token, + const std::string& device_id, + const DeviceRegisterRequest& request, + DeviceRegisterResponseDelegate* delegate) { + if (failure_) { + delegate->OnError(kErrorRequestInvalid); + } else { + DeviceRegisterResponse response; + std::string token(kFakeDeviceManagementToken); + token += next_token_suffix_++; + response.set_device_management_token(token); + delegate->HandleRegisterResponse(response); + } +} + +void MockDeviceManagementBackend::ProcessUnregisterRequest( + const std::string& device_management_token, + const DeviceUnregisterRequest& request, + DeviceUnregisterResponseDelegate* delegate) { + // TODO(danno): need a mock implementation for the backend here. + NOTREACHED(); +} + +void MockDeviceManagementBackend::ProcessPolicyRequest( + const std::string& device_management_token, + const DevicePolicyRequest& request, + DevicePolicyResponseDelegate* delegate) { + // TODO(danno): need a mock implementation for the backend here. + NOTREACHED(); +} + +} // namespace diff --git a/chrome/browser/policy/mock_device_management_backend.h b/chrome/browser/policy/mock_device_management_backend.h new file mode 100644 index 0000000..290e615 --- /dev/null +++ b/chrome/browser/policy/mock_device_management_backend.h @@ -0,0 +1,52 @@ +// 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_POLICY_MOCK_DEVICE_MANAGEMENT_BACKEND_H_ +#define CHROME_BROWSER_POLICY_MOCK_DEVICE_MANAGEMENT_BACKEND_H_ +#pragma once + +#include <string> + +#include "chrome/browser/policy/device_management_backend.h" + +namespace policy { + +namespace em = enterprise_management; + +// Useful for unit testing when a server-based backend isn't +// available. Simulates both successful and failed requests to the device +// management server. +class MockDeviceManagementBackend + : public DeviceManagementBackend { + public: + MockDeviceManagementBackend(); + virtual ~MockDeviceManagementBackend() {} + + void SetFailure(bool failure) { failure_ = failure; } + + // DeviceManagementBackend method overrides: + virtual void ProcessRegisterRequest( + const std::string& auth_token, + const std::string& device_id, + const em::DeviceRegisterRequest& request, + DeviceRegisterResponseDelegate* delegate); + + virtual void ProcessUnregisterRequest( + const std::string& device_management_token, + const em::DeviceUnregisterRequest& request, + DeviceUnregisterResponseDelegate* delegate); + + virtual void ProcessPolicyRequest( + const std::string& device_management_token, + const em::DevicePolicyRequest& request, + DevicePolicyResponseDelegate* delegate); + + private: + bool failure_; + DISALLOW_COPY_AND_ASSIGN(MockDeviceManagementBackend); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_MOCK_DEVICE_MANAGEMENT_BACKEND_H_ diff --git a/chrome/browser/policy/proto/device_management_backend.proto b/chrome/browser/policy/proto/device_management_backend.proto new file mode 100644 index 0000000..6b0cc8d --- /dev/null +++ b/chrome/browser/policy/proto/device_management_backend.proto @@ -0,0 +1,165 @@ +// 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. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package enterprise_management; + +// Generic value container. +message GenericValue { + enum ValueType { + VALUE_TYPE_BOOL = 1; + VALUE_TYPE_INT64 = 2; + VALUE_TYPE_STRING = 3; + VALUE_TYPE_DOUBLE = 4; + VALUE_TYPE_BYTES = 5; + VALUE_TYPE_BOOL_ARRAY = 6; + VALUE_TYPE_INT64_ARRAY_ = 7; + VALUE_TYPE_STRING_ARRAY = 8; + VALUE_TYPE_DOUBLE_ARRAY = 9; + } + + optional ValueType value_type = 1 [default = VALUE_TYPE_STRING]; + + // basic value types + optional bool bool_value = 2; + optional int64 int64_value = 3; + optional string string_value = 4; + optional double double_value = 5; + optional bytes bytes_value = 6; + repeated bool bool_array = 7; + repeated int64 int64_array = 8; + repeated string string_array = 9; + repeated double double_array = 10; +} + +// Generic name value pair container. +message GenericNamedValue { + required string name = 1; + optional GenericValue value = 2; +} + +// A setting is a set of generic name value pairs. +message GenericSetting { + repeated GenericNamedValue named_value = 1; +} + +// Identify a single device policy setting key/value pair. +message DevicePolicySetting { + // key of the policy setting + required string policy_key = 1; + // value of the setting + optional GenericSetting policy_value = 2; + // watermark for setting value. + optional string watermark = 3; +} + +// Request from device to server to register device. +message DeviceRegisterRequest { + // reregister device without erasing server state. + // it can be used to refresh dmtoken etc. + optional bool reregister = 1; +} + +// Response from server to device register request. +message DeviceRegisterResponse { + // device mangement toke for this registration. + required string device_management_token = 1; +} + +// Request from device to server to unregister device. +message DeviceUnregisterRequest { +} + +// Response from server to device unregister request. +message DeviceUnregisterResponse { +} + +// Request for a setting or with optional watermark on client side. +message DevicePolicySettingRequest { + // setting key + required string key = 1; + // watermark last read from server if available. + optional string watermark = 2; +} + +// Request from device to server to read device policies. +message DevicePolicyRequest { + // identify request scope: CrOS settings or other type of settings. + optional string policy_scope = 1; + // identify key to the settings: proxy etc. + repeated DevicePolicySettingRequest setting_request = 2; +} + +// Response from server to agent for reading policies. +message DevicePolicyResponse { + // the result of the settings. + repeated DevicePolicySetting setting = 1; +} + +// Request from the DMAgent on the device to the DMServer. +// This is container for all requests from client. +// +// Authorization: +// 1. If request is register_request, client must pass in GoogleLogin auth +// cookie in Authorization header: +// Authorization: GoogleLogin auth=<auth cookie> +// The response will contain an unique DMToken for future requests. +// Depending on domain policy, the request may need admin approval before +// DMToken is issued. +// 2. For other requests, client must pass in DMToken in Authorization header: +// Authorization: GoogleDMToken token=<google dm token> +// +// Http Query parameters: +// Query parameters contain the following information in each request: +// request: register/unregister/policy etc. +// devicetype: CrOS/Android/Iphone etc. +// apptype: CrOS/AndroidDM etc. +// deviceid: unique id that identify the device. +// agent: identify agent on device. +message DeviceManagementRequest { + // Register request. + optional DeviceRegisterRequest register_request = 1; + + // Unregister request. + optional DeviceUnregisterRequest unregister_request = 2; + + // Data request. + optional DevicePolicyRequest policy_request = 3; +} + +// Response from server to device. +message DeviceManagementResponse { + // Error code to client. + enum ErrorCode { + SUCCESS = 0; + // Returned for register request when device management is not supported + // for the domain. + DEVICE_MANAGEMENT_NOT_SUPPORTED = 1; + // Returned when the device is not found. + DEVICE_NOT_FOUND = 2; + // Returned when passed in device management token doesn't match the token + // on server side. + DEVICE_MANAGEMENT_TOKEN_INVALID = 3; + // Returned when device registration is pending approval (if required). + ACTIVATION_PENDING = 4; + } + + // Error code for this request. + required ErrorCode error = 1; + + // Error message. + optional string error_message = 2; + + // Register response + optional DeviceRegisterResponse register_response = 3; + + // Unregister response + optional DeviceUnregisterResponse unregister_response = 4; + + // Policy response. + optional DevicePolicyResponse policy_response = 5; +} diff --git a/chrome/browser/preferences_mac.cc b/chrome/browser/preferences_mac.cc index f1430f7..89f4aad 100644 --- a/chrome/browser/preferences_mac.cc +++ b/chrome/browser/preferences_mac.cc @@ -4,6 +4,10 @@ #include "chrome/browser/preferences_mac.h" +Boolean MacPreferences::AppSynchronize(CFStringRef applicationID) { + return CFPreferencesAppSynchronize(applicationID); +} + CFPropertyListRef MacPreferences::CopyAppValue(CFStringRef key, CFStringRef applicationID) { return CFPreferencesCopyAppValue(key, applicationID); diff --git a/chrome/browser/preferences_mac.h b/chrome/browser/preferences_mac.h index 34675f0..6b447ad 100644 --- a/chrome/browser/preferences_mac.h +++ b/chrome/browser/preferences_mac.h @@ -21,6 +21,8 @@ class MacPreferences { MacPreferences() {} virtual ~MacPreferences() {} + virtual Boolean AppSynchronize(CFStringRef applicationID); + virtual CFPropertyListRef CopyAppValue(CFStringRef key, CFStringRef applicationID); diff --git a/chrome/browser/preferences_mock_mac.cc b/chrome/browser/preferences_mock_mac.cc index 6d0f0a0..3dca588 100644 --- a/chrome/browser/preferences_mock_mac.cc +++ b/chrome/browser/preferences_mock_mac.cc @@ -17,6 +17,9 @@ MockPreferences::MockPreferences() { MockPreferences::~MockPreferences() { } +Boolean MockPreferences::AppSynchronize(CFStringRef applicationID) { + return true; +} CFPropertyListRef MockPreferences::CopyAppValue(CFStringRef key, CFStringRef applicationID) { diff --git a/chrome/browser/preferences_mock_mac.h b/chrome/browser/preferences_mock_mac.h index 6c1ea3d..85e0e30 100644 --- a/chrome/browser/preferences_mock_mac.h +++ b/chrome/browser/preferences_mock_mac.h @@ -15,6 +15,8 @@ class MockPreferences : public MacPreferences { MockPreferences(); virtual ~MockPreferences(); + virtual Boolean AppSynchronize(CFStringRef applicationID); + virtual CFPropertyListRef CopyAppValue(CFStringRef key, CFStringRef applicationID); diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 0b65bf0..9f23e54 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -63,6 +63,7 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/cros_settings_provider_user.h" #include "chrome/browser/chromeos/login/apply_services_customization.h" +#include "chrome/browser/chromeos/login/signed_settings_temp_storage.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/preferences.h" @@ -89,7 +90,6 @@ void RegisterLocalState(PrefService* local_state) { SafeBrowsingService::RegisterPrefs(local_state); browser_shutdown::RegisterPrefs(local_state); chrome_browser_net::RegisterPrefs(local_state); - PageInfoModel::RegisterPrefs(local_state); #if defined(TOOLKIT_VIEWS) BrowserView::RegisterBrowserViewPrefs(local_state); #endif @@ -103,6 +103,7 @@ void RegisterLocalState(PrefService* local_state) { WizardController::RegisterPrefs(local_state); chromeos::InputMethodMenuButton::RegisterPrefs(local_state); chromeos::ApplyServicesCustomization::RegisterPrefs(local_state); + chromeos::SignedSettingsTempStorage::RegisterPrefs(local_state); #endif } diff --git a/chrome/browser/prefs/command_line_pref_store.cc b/chrome/browser/prefs/command_line_pref_store.cc index 8d398b6..5a9a7eb 100644 --- a/chrome/browser/prefs/command_line_pref_store.cc +++ b/chrome/browser/prefs/command_line_pref_store.cc @@ -36,8 +36,6 @@ PrefStore::PrefReadError CommandLinePrefStore::ReadPrefs() { return PrefStore::PREF_READ_ERROR_NONE; } -DictionaryValue* CommandLinePrefStore::prefs() { return prefs_.get(); } - void CommandLinePrefStore::ApplySimpleSwitches() { // Look for each switch we know about and set its preference accordingly. for (size_t i = 0; i < arraysize(string_switch_map_); ++i) { diff --git a/chrome/browser/prefs/command_line_pref_store.h b/chrome/browser/prefs/command_line_pref_store.h index 8dd7cd5..2ea29ae 100644 --- a/chrome/browser/prefs/command_line_pref_store.h +++ b/chrome/browser/prefs/command_line_pref_store.h @@ -22,7 +22,7 @@ class CommandLinePrefStore : public PrefStore { // PrefStore methods: virtual PrefReadError ReadPrefs(); - virtual DictionaryValue* prefs(); + virtual DictionaryValue* prefs() const { return prefs_.get(); } protected: // Logs a message and returns false if the proxy switches are @@ -30,16 +30,10 @@ class CommandLinePrefStore : public PrefStore { bool ValidateProxySwitches(); private: - // Weak reference. - const CommandLine* command_line_; - - scoped_ptr<DictionaryValue> prefs_; - struct StringSwitchToPreferenceMapEntry { const char* switch_name; const char* preference_path; }; - static const StringSwitchToPreferenceMapEntry string_switch_map_[]; // |set_value| indicates what the preference should be set to if the switch // is present. @@ -54,6 +48,13 @@ class CommandLinePrefStore : public PrefStore { // corresponding preferences in this pref store. void ApplySimpleSwitches(); + // Weak reference. + const CommandLine* command_line_; + + scoped_ptr<DictionaryValue> prefs_; + + static const StringSwitchToPreferenceMapEntry string_switch_map_[]; + DISALLOW_COPY_AND_ASSIGN(CommandLinePrefStore); }; diff --git a/chrome/browser/prefs/default_pref_store.cc b/chrome/browser/prefs/default_pref_store.cc index 6b0aa71..a9e6861 100644 --- a/chrome/browser/prefs/default_pref_store.cc +++ b/chrome/browser/prefs/default_pref_store.cc @@ -12,7 +12,7 @@ DefaultPrefStore::DefaultPrefStore() : prefs_(new DictionaryValue()) { DefaultPrefStore::~DefaultPrefStore() { } -DictionaryValue* DefaultPrefStore::prefs() { +DictionaryValue* DefaultPrefStore::prefs() const { return prefs_.get(); } diff --git a/chrome/browser/prefs/default_pref_store.h b/chrome/browser/prefs/default_pref_store.h index b137936..6378aca 100644 --- a/chrome/browser/prefs/default_pref_store.h +++ b/chrome/browser/prefs/default_pref_store.h @@ -18,7 +18,7 @@ class DefaultPrefStore : public PrefStore { virtual ~DefaultPrefStore(); // PrefStore methods: - virtual DictionaryValue* prefs(); + virtual DictionaryValue* prefs() const; virtual PrefStore::PrefReadError ReadPrefs(); private: diff --git a/chrome/browser/prefs/dummy_pref_store.h b/chrome/browser/prefs/dummy_pref_store.h index ebf92fc..4a4e6ba 100644 --- a/chrome/browser/prefs/dummy_pref_store.h +++ b/chrome/browser/prefs/dummy_pref_store.h @@ -17,10 +17,9 @@ class DictionaryValue; class DummyPrefStore : public PrefStore { public: DummyPrefStore(); - virtual ~DummyPrefStore() {} - virtual DictionaryValue* prefs() { return prefs_.get(); } + virtual DictionaryValue* prefs() const { return prefs_.get(); } virtual PrefStore::PrefReadError ReadPrefs(); diff --git a/chrome/browser/prefs/pref_service_unittest.cc b/chrome/browser/prefs/pref_service_unittest.cc index ce2f91c..54cc3dd 100644 --- a/chrome/browser/prefs/pref_service_unittest.cc +++ b/chrome/browser/prefs/pref_service_unittest.cc @@ -31,6 +31,8 @@ using testing::Mock; using testing::Pointee; using testing::Property; +namespace { + class TestPrefObserver : public NotificationObserver { public: TestPrefObserver(const PrefService* prefs, @@ -39,17 +41,16 @@ class TestPrefObserver : public NotificationObserver { : observer_fired_(false), prefs_(prefs), pref_name_(pref_name), - new_pref_value_(new_pref_value) { - } + new_pref_value_(new_pref_value) {} virtual ~TestPrefObserver() {} virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { EXPECT_EQ(type.value, NotificationType::PREF_CHANGED); - PrefService* prefs_in = Source<PrefService>(source).ptr(); + const PrefService* prefs_in = Source<PrefService>(source).ptr(); EXPECT_EQ(prefs_in, prefs_); - std::string* pref_name_in = Details<std::string>(details).ptr(); + const std::string* pref_name_in = Details<std::string>(details).ptr(); EXPECT_EQ(*pref_name_in, pref_name_); EXPECT_EQ(new_pref_value_, prefs_in->GetString("homepage")); observer_fired_ = true; @@ -69,6 +70,8 @@ class TestPrefObserver : public NotificationObserver { std::string new_pref_value_; }; +} // namespace + // TODO(port): port this test to POSIX. #if defined(OS_WIN) TEST(PrefServiceTest, LocalizedPrefs) { @@ -98,7 +101,7 @@ TEST(PrefServiceTest, NoObserverFire) { TestingPrefService prefs; const char pref_name[] = "homepage"; - prefs.RegisterStringPref(pref_name, ""); + prefs.RegisterStringPref(pref_name, std::string()); const std::string new_pref_value("http://www.google.com/"); TestPrefObserver obs(&prefs, pref_name, new_pref_value); @@ -119,12 +122,12 @@ TEST(PrefServiceTest, NoObserverFire) { EXPECT_FALSE(obs.observer_fired()); // Clearing the pref should cause the pref to fire. - obs.Reset(""); + obs.Reset(std::string()); prefs.ClearPref(pref_name); EXPECT_TRUE(obs.observer_fired()); // Clearing the pref again should not cause the pref to fire. - obs.Reset(""); + obs.Reset(std::string()); prefs.ClearPref(pref_name); EXPECT_FALSE(obs.observer_fired()); } @@ -152,7 +155,7 @@ TEST(PrefServiceTest, Observers) { TestingPrefService prefs; prefs.SetUserPref(pref_name, Value::CreateStringValue("http://www.cnn.com")); - prefs.RegisterStringPref(pref_name, ""); + prefs.RegisterStringPref(pref_name, std::string()); const std::string new_pref_value("http://www.google.com/"); TestPrefObserver obs(&prefs, pref_name, new_pref_value); @@ -177,7 +180,7 @@ TEST(PrefServiceTest, Observers) { // Make sure obs2 still works after removing obs. registrar.Remove(pref_name, &obs); - obs.Reset(""); + obs.Reset(std::string()); obs2.Reset(new_pref_value); // This should only fire the observer in obs2. prefs.SetString(pref_name, new_pref_value); @@ -205,19 +208,14 @@ TEST(PrefServiceTest, ProxyPolicyOverridesCommandLineOptions) { scoped_ptr<policy::MockConfigurationPolicyProvider> provider( new policy::MockConfigurationPolicyProvider()); Value* mode_value = Value::CreateIntegerValue( - policy::ConfigurationPolicyStore::kPolicyManuallyConfiguredProxyMode); - provider->AddPolicy( - policy::ConfigurationPolicyStore::kPolicyProxyServerMode, - mode_value); - provider->AddPolicy( - policy::ConfigurationPolicyStore::kPolicyProxyBypassList, - Value::CreateStringValue("abc")); - provider->AddPolicy( - policy::ConfigurationPolicyStore::kPolicyProxyPacUrl, - Value::CreateStringValue("def")); - provider->AddPolicy( - policy::ConfigurationPolicyStore::kPolicyProxyServer, - Value::CreateStringValue("ghi")); + policy::kPolicyManuallyConfiguredProxyMode); + provider->AddPolicy(policy::kPolicyProxyServerMode, mode_value); + provider->AddPolicy(policy::kPolicyProxyBypassList, + Value::CreateStringValue("abc")); + provider->AddPolicy(policy::kPolicyProxyPacUrl, + Value::CreateStringValue("def")); + provider->AddPolicy(policy::kPolicyProxyServer, + Value::CreateStringValue("ghi")); // First verify that command-line options are set correctly when // there is no policy in effect. @@ -249,10 +247,8 @@ TEST(PrefServiceTest, ProxyPolicyOverridesUnrelatedCommandLineOptions) { scoped_ptr<policy::MockConfigurationPolicyProvider> provider( new policy::MockConfigurationPolicyProvider()); Value* mode_value = Value::CreateIntegerValue( - policy::ConfigurationPolicyStore::kPolicyUseSystemProxyMode); - provider->AddPolicy( - policy::ConfigurationPolicyStore::kPolicyProxyServerMode, - mode_value); + policy::kPolicyUseSystemProxyMode); + provider->AddPolicy(policy::kPolicyProxyServerMode, mode_value); // First verify that command-line options are set correctly when // there is no policy in effect. @@ -272,9 +268,9 @@ TEST(PrefServiceTest, ProxyPolicyOverridesUnrelatedCommandLineOptions) { browser::RegisterUserPrefs(&prefs2); EXPECT_FALSE(prefs2.GetBoolean(prefs::kProxyAutoDetect)); EXPECT_FALSE(prefs2.GetBoolean(prefs::kNoProxyServer)); - EXPECT_EQ("", prefs2.GetString(prefs::kProxyServer)); - EXPECT_EQ("", prefs2.GetString(prefs::kProxyPacUrl)); - EXPECT_EQ("", prefs2.GetString(prefs::kProxyBypassList)); + EXPECT_EQ(std::string(), prefs2.GetString(prefs::kProxyServer)); + EXPECT_EQ(std::string(), prefs2.GetString(prefs::kProxyPacUrl)); + EXPECT_EQ(std::string(), prefs2.GetString(prefs::kProxyBypassList)); } TEST(PrefServiceTest, ProxyPolicyOverridesCommandLineNoProxy) { @@ -283,10 +279,8 @@ TEST(PrefServiceTest, ProxyPolicyOverridesCommandLineNoProxy) { scoped_ptr<policy::MockConfigurationPolicyProvider> provider( new policy::MockConfigurationPolicyProvider()); Value* mode_value = Value::CreateIntegerValue( - policy::ConfigurationPolicyStore::kPolicyAutoDetectProxyMode); - provider->AddPolicy( - policy::ConfigurationPolicyStore::kPolicyProxyServerMode, - mode_value); + policy::kPolicyAutoDetectProxyMode); + provider->AddPolicy(policy::kPolicyProxyServerMode, mode_value); // First verify that command-line options are set correctly when // there is no policy in effect. @@ -294,9 +288,9 @@ TEST(PrefServiceTest, ProxyPolicyOverridesCommandLineNoProxy) { browser::RegisterUserPrefs(&prefs); EXPECT_FALSE(prefs.GetBoolean(prefs::kProxyAutoDetect)); EXPECT_TRUE(prefs.GetBoolean(prefs::kNoProxyServer)); - EXPECT_EQ("", prefs.GetString(prefs::kProxyServer)); - EXPECT_EQ("", prefs.GetString(prefs::kProxyPacUrl)); - EXPECT_EQ("", prefs.GetString(prefs::kProxyBypassList)); + EXPECT_EQ(std::string(), prefs.GetString(prefs::kProxyServer)); + EXPECT_EQ(std::string(), prefs.GetString(prefs::kProxyPacUrl)); + EXPECT_EQ(std::string(), prefs.GetString(prefs::kProxyBypassList)); // Try a second time time with the managed PrefStore in place, the // auto-detect should be overridden. The default pref store must be @@ -305,9 +299,9 @@ TEST(PrefServiceTest, ProxyPolicyOverridesCommandLineNoProxy) { browser::RegisterUserPrefs(&prefs2); EXPECT_TRUE(prefs2.GetBoolean(prefs::kProxyAutoDetect)); EXPECT_FALSE(prefs2.GetBoolean(prefs::kNoProxyServer)); - EXPECT_EQ("", prefs2.GetString(prefs::kProxyServer)); - EXPECT_EQ("", prefs2.GetString(prefs::kProxyPacUrl)); - EXPECT_EQ("", prefs2.GetString(prefs::kProxyBypassList)); + EXPECT_EQ(std::string(), prefs2.GetString(prefs::kProxyServer)); + EXPECT_EQ(std::string(), prefs2.GetString(prefs::kProxyPacUrl)); + EXPECT_EQ(std::string(), prefs2.GetString(prefs::kProxyBypassList)); } TEST(PrefServiceTest, ProxyPolicyOverridesCommandLineAutoDetect) { @@ -316,10 +310,8 @@ TEST(PrefServiceTest, ProxyPolicyOverridesCommandLineAutoDetect) { scoped_ptr<policy::MockConfigurationPolicyProvider> provider( new policy::MockConfigurationPolicyProvider()); Value* mode_value = Value::CreateIntegerValue( - policy::ConfigurationPolicyStore::kPolicyNoProxyServerMode); - provider->AddPolicy( - policy::ConfigurationPolicyStore::kPolicyProxyServerMode, - mode_value); + policy::kPolicyNoProxyServerMode); + provider->AddPolicy(policy::kPolicyProxyServerMode, mode_value); // First verify that the auto-detect is set if there is no managed // PrefStore. @@ -327,9 +319,9 @@ TEST(PrefServiceTest, ProxyPolicyOverridesCommandLineAutoDetect) { browser::RegisterUserPrefs(&prefs); EXPECT_TRUE(prefs.GetBoolean(prefs::kProxyAutoDetect)); EXPECT_FALSE(prefs.GetBoolean(prefs::kNoProxyServer)); - EXPECT_EQ("", prefs.GetString(prefs::kProxyServer)); - EXPECT_EQ("", prefs.GetString(prefs::kProxyPacUrl)); - EXPECT_EQ("", prefs.GetString(prefs::kProxyBypassList)); + EXPECT_EQ(std::string(), prefs.GetString(prefs::kProxyServer)); + EXPECT_EQ(std::string(), prefs.GetString(prefs::kProxyPacUrl)); + EXPECT_EQ(std::string(), prefs.GetString(prefs::kProxyBypassList)); // Try a second time time with the managed PrefStore in place, the // auto-detect should be overridden. The default pref store must be @@ -338,18 +330,18 @@ TEST(PrefServiceTest, ProxyPolicyOverridesCommandLineAutoDetect) { browser::RegisterUserPrefs(&prefs2); EXPECT_FALSE(prefs2.GetBoolean(prefs::kProxyAutoDetect)); EXPECT_TRUE(prefs2.GetBoolean(prefs::kNoProxyServer)); - EXPECT_EQ("", prefs2.GetString(prefs::kProxyServer)); - EXPECT_EQ("", prefs2.GetString(prefs::kProxyPacUrl)); - EXPECT_EQ("", prefs2.GetString(prefs::kProxyBypassList)); + EXPECT_EQ(std::string(), prefs2.GetString(prefs::kProxyServer)); + EXPECT_EQ(std::string(), prefs2.GetString(prefs::kProxyPacUrl)); + EXPECT_EQ(std::string(), prefs2.GetString(prefs::kProxyBypassList)); } class PrefServiceSetValueTest : public testing::Test { protected: - static const char name_[]; - static const char value_[]; + static const char kName[]; + static const char kValue[]; PrefServiceSetValueTest() - : name_string_(name_), + : name_string_(kName), null_value_(Value::CreateNullValue()) {} void SetExpectNoNotification() { @@ -369,97 +361,98 @@ class PrefServiceSetValueTest : public testing::Test { NotificationObserverMock observer_; }; -const char PrefServiceSetValueTest::name_[] = "name"; -const char PrefServiceSetValueTest::value_[] = "value"; +const char PrefServiceSetValueTest::kName[] = "name"; +const char PrefServiceSetValueTest::kValue[] = "value"; TEST_F(PrefServiceSetValueTest, SetStringValue) { const char default_string[] = "default"; - scoped_ptr<Value> default_value(Value::CreateStringValue(default_string)); - prefs_.RegisterStringPref(name_, default_string); + const scoped_ptr<Value> default_value( + Value::CreateStringValue(default_string)); + prefs_.RegisterStringPref(kName, default_string); PrefChangeRegistrar registrar; registrar.Init(&prefs_); - registrar.Add(name_, &observer_); + registrar.Add(kName, &observer_); // Changing the controlling store from default to user triggers notification. SetExpectPrefChanged(); - prefs_.Set(name_, *default_value); + prefs_.Set(kName, *default_value); Mock::VerifyAndClearExpectations(&observer_); SetExpectNoNotification(); - prefs_.Set(name_, *default_value); + prefs_.Set(kName, *default_value); Mock::VerifyAndClearExpectations(&observer_); - scoped_ptr<Value> new_value(Value::CreateStringValue(value_)); + const scoped_ptr<Value> new_value(Value::CreateStringValue(kValue)); SetExpectPrefChanged(); - prefs_.Set(name_, *new_value); - EXPECT_EQ(value_, prefs_.GetString(name_)); + prefs_.Set(kName, *new_value); + EXPECT_EQ(kValue, prefs_.GetString(kName)); } TEST_F(PrefServiceSetValueTest, SetDictionaryValue) { - prefs_.RegisterDictionaryPref(name_); + prefs_.RegisterDictionaryPref(kName); PrefChangeRegistrar registrar; registrar.Init(&prefs_); - registrar.Add(name_, &observer_); + registrar.Add(kName, &observer_); // Dictionary values are special: setting one to NULL is the same as clearing // the user value, allowing the NULL default to take (or keep) control. SetExpectNoNotification(); - prefs_.Set(name_, *null_value_); + prefs_.Set(kName, *null_value_); Mock::VerifyAndClearExpectations(&observer_); DictionaryValue new_value; - new_value.SetString(name_, value_); + new_value.SetString(kName, kValue); SetExpectPrefChanged(); - prefs_.Set(name_, new_value); + prefs_.Set(kName, new_value); Mock::VerifyAndClearExpectations(&observer_); - DictionaryValue* dict = prefs_.GetMutableDictionary(name_); + DictionaryValue* dict = prefs_.GetMutableDictionary(kName); EXPECT_EQ(1U, dict->size()); std::string out_value; - dict->GetString(name_, &out_value); - EXPECT_EQ(value_, out_value); + dict->GetString(kName, &out_value); + EXPECT_EQ(kValue, out_value); SetExpectNoNotification(); - prefs_.Set(name_, new_value); + prefs_.Set(kName, new_value); Mock::VerifyAndClearExpectations(&observer_); SetExpectPrefChanged(); - prefs_.Set(name_, *null_value_); + prefs_.Set(kName, *null_value_); Mock::VerifyAndClearExpectations(&observer_); - dict = prefs_.GetMutableDictionary(name_); + dict = prefs_.GetMutableDictionary(kName); EXPECT_EQ(0U, dict->size()); } TEST_F(PrefServiceSetValueTest, SetListValue) { - prefs_.RegisterListPref(name_); + prefs_.RegisterListPref(kName); PrefChangeRegistrar registrar; registrar.Init(&prefs_); - registrar.Add(name_, &observer_); + registrar.Add(kName, &observer_); // List values are special: setting one to NULL is the same as clearing the // user value, allowing the NULL default to take (or keep) control. SetExpectNoNotification(); - prefs_.Set(name_, *null_value_); + prefs_.Set(kName, *null_value_); Mock::VerifyAndClearExpectations(&observer_); ListValue new_value; - new_value.Append(Value::CreateStringValue(value_)); + new_value.Append(Value::CreateStringValue(kValue)); SetExpectPrefChanged(); - prefs_.Set(name_, new_value); + prefs_.Set(kName, new_value); Mock::VerifyAndClearExpectations(&observer_); - ListValue* list = prefs_.GetMutableList(name_); + const ListValue* list = prefs_.GetMutableList(kName); ASSERT_EQ(1U, list->GetSize()); std::string out_value; list->GetString(0, &out_value); - EXPECT_EQ(value_, out_value); + EXPECT_EQ(kValue, out_value); SetExpectNoNotification(); - prefs_.Set(name_, new_value); + prefs_.Set(kName, new_value); Mock::VerifyAndClearExpectations(&observer_); SetExpectPrefChanged(); - prefs_.Set(name_, *null_value_); + prefs_.Set(kName, *null_value_); Mock::VerifyAndClearExpectations(&observer_); - list = prefs_.GetMutableList(name_); + list = prefs_.GetMutableList(kName); EXPECT_EQ(0U, list->GetSize()); } diff --git a/chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc b/chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc index 3c63d7d..9607e3f 100644 --- a/chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc +++ b/chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc @@ -181,4 +181,3 @@ bool CloudPrintProxyService::InvokeServiceTask(Task* task) { process_control->Launch(task, NULL); return !!process_control; } - diff --git a/chrome/browser/printing/cloud_print/cloud_print_setup_flow.h b/chrome/browser/printing/cloud_print/cloud_print_setup_flow.h index d79e578..c6f71ea 100644 --- a/chrome/browser/printing/cloud_print/cloud_print_setup_flow.h +++ b/chrome/browser/printing/cloud_print/cloud_print_setup_flow.h @@ -70,6 +70,7 @@ class CloudPrintSetupFlow : public HtmlDialogUIDelegate, virtual void OnCloseContents(TabContents* source, bool* out_close_dialog); virtual std::wstring GetDialogTitle() const; virtual bool IsDialogModal() const; + virtual bool ShouldShowDialogTitle() const { return true; } // GaiaAuthConsumer implementation. virtual void OnClientLoginFailure( diff --git a/chrome/browser/printing/print_dialog_cloud.cc b/chrome/browser/printing/print_dialog_cloud.cc index 6fd43f4..1af016e 100644 --- a/chrome/browser/printing/print_dialog_cloud.cc +++ b/chrome/browser/printing/print_dialog_cloud.cc @@ -15,14 +15,17 @@ #include "chrome/browser/debugger/devtools_manager.h" #include "chrome/browser/dom_ui/dom_ui.h" #include "chrome/browser/dom_ui/dom_ui_util.h" -#include "chrome/browser/dom_ui/html_dialog_ui.h" +#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/printing/cloud_print/cloud_print_url.h" +#include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" #include "chrome/common/render_messages_params.h" #include "chrome/common/url_constants.h" #include "webkit/glue/webpreferences.h" @@ -92,7 +95,6 @@ // high-level flow (where the PDF data is generated before even // bringing up the dialog) isn't what we want. - namespace internal_cloud_print_helpers { bool GetRealOrInt(const DictionaryValue& dictionary, @@ -152,6 +154,14 @@ void CloudPrintDataSender::CancelPrintDataFile() { helper_ = NULL; } +CloudPrintDataSender::CloudPrintDataSender(CloudPrintDataSenderHelper* helper, + const string16& print_job_title) + : helper_(helper), + print_job_title_(print_job_title) { +} + +CloudPrintDataSender::~CloudPrintDataSender() {} + // Grab the raw PDF file contents and massage them into shape for // sending to the dialog contents (and up to the cloud print server) // by encoding it and prefixing it with the appropriate mime type. @@ -205,6 +215,18 @@ void CloudPrintDataSender::SendPrintDataFile() { } +CloudPrintFlowHandler::CloudPrintFlowHandler(const FilePath& path_to_pdf, + const string16& print_job_title) + : path_to_pdf_(path_to_pdf), + print_job_title_(print_job_title) { +} + +CloudPrintFlowHandler::~CloudPrintFlowHandler() { + // This will also cancel any task in flight. + CancelAnyRunningTask(); +} + + void CloudPrintFlowHandler::SetDialogDelegate( CloudPrintHtmlDialogDelegate* delegate) { // Even if setting a new dom_ui, it means any previous task needs @@ -225,7 +247,6 @@ void CloudPrintFlowHandler::CancelAnyRunningTask() { } } - void CloudPrintFlowHandler::RegisterMessages() { if (!dom_ui_) return; @@ -358,6 +379,16 @@ void CloudPrintFlowHandler::HandleSetPageParameters(const ListValue* args) { // that point. } +void CloudPrintFlowHandler::StoreDialogClientSize() const { + if (dom_ui_ && dom_ui_->tab_contents() && dom_ui_->tab_contents()->view()) { + gfx::Size size = dom_ui_->tab_contents()->view()->GetContainerSize(); + dom_ui_->GetProfile()->GetPrefs()->SetInteger( + prefs::kCloudPrintDialogWidth, size.width()); + dom_ui_->GetProfile()->GetPrefs()->SetInteger( + prefs::kCloudPrintDialogHeight, size.height()); + } +} + CloudPrintHtmlDialogDelegate::CloudPrintHtmlDialogDelegate( const FilePath& path_to_pdf, int width, int height, @@ -405,7 +436,7 @@ bool CloudPrintHtmlDialogDelegate::IsDialogModal() const { } std::wstring CloudPrintHtmlDialogDelegate::GetDialogTitle() const { - return l10n_util::GetString(IDS_CLOUD_PRINT_TITLE); + return std::wstring(); } GURL CloudPrintHtmlDialogDelegate::GetDialogContentURL() const { @@ -432,6 +463,8 @@ std::string CloudPrintHtmlDialogDelegate::GetDialogArgs() const { void CloudPrintHtmlDialogDelegate::OnDialogClosed( const std::string& json_retval) { + // Get the final dialog size and store it. + flow_handler_->StoreDialogClientSize(); delete this; } @@ -441,6 +474,10 @@ void CloudPrintHtmlDialogDelegate::OnCloseContents(TabContents* source, *out_close_dialog = true; } +bool CloudPrintHtmlDialogDelegate::ShouldShowDialogTitle() const { + return false; +} + } // end of namespace internal_cloud_print_helpers // static, called on the IO thread. This is the main entry point into @@ -473,11 +510,25 @@ PrintDialogCloud::PrintDialogCloud(const FilePath& path_to_pdf) if (browser_ && browser_->GetSelectedTabContents()) print_job_title = browser_->GetSelectedTabContents()->GetTitle(); - // TODO(scottbyer): Get the dialog width, height from the dialog - // contents, and take the screen size into account. + const int kDefaultWidth = 497; + const int kDefaultHeight = 332; + + PrefService* pref_service = browser_->GetProfile()->GetPrefs(); + DCHECK(pref_service); + if (!pref_service->FindPreference(prefs::kCloudPrintDialogWidth)) { + pref_service->RegisterIntegerPref(prefs::kCloudPrintDialogWidth, + kDefaultWidth); + } + if (!pref_service->FindPreference(prefs::kCloudPrintDialogHeight)) { + pref_service->RegisterIntegerPref(prefs::kCloudPrintDialogHeight, + kDefaultHeight); + } + + int width = pref_service->GetInteger(prefs::kCloudPrintDialogWidth); + int height = pref_service->GetInteger(prefs::kCloudPrintDialogHeight); HtmlDialogUIDelegate* dialog_delegate = new internal_cloud_print_helpers::CloudPrintHtmlDialogDelegate( - path_to_pdf, 500, 400, std::string(), print_job_title); + path_to_pdf, width, height, std::string(), print_job_title); browser_->BrowserShowHtmlDialog(dialog_delegate, NULL); } diff --git a/chrome/browser/printing/print_dialog_cloud_internal.h b/chrome/browser/printing/print_dialog_cloud_internal.h index c40f301..9c83154 100644 --- a/chrome/browser/printing/print_dialog_cloud_internal.h +++ b/chrome/browser/printing/print_dialog_cloud_internal.h @@ -53,8 +53,7 @@ class CloudPrintDataSender // The owner of this object is also expected to own and control the // lifetime of the helper. CloudPrintDataSender(CloudPrintDataSenderHelper* helper, - const string16& print_job_title) - : helper_(helper), print_job_title_(print_job_title) {} + const string16& print_job_title); // Calls to read in the PDF file (on the FILE thread) then send that // information to the dialog renderer (on the IO thread). We know @@ -69,7 +68,7 @@ class CloudPrintDataSender private: friend class base::RefCountedThreadSafe<CloudPrintDataSender>; - ~CloudPrintDataSender() {} + virtual ~CloudPrintDataSender(); Lock lock_; CloudPrintDataSenderHelper* volatile helper_; @@ -92,13 +91,8 @@ class CloudPrintFlowHandler : public DOMMessageHandler, public NotificationObserver { public: explicit CloudPrintFlowHandler(const FilePath& path_to_pdf, - const string16& print_job_title) - : path_to_pdf_(path_to_pdf), - print_job_title_(print_job_title) {} - virtual ~CloudPrintFlowHandler() { - // This will also cancel any task in flight. - CancelAnyRunningTask(); - } + const string16& print_job_title); + virtual ~CloudPrintFlowHandler(); // DOMMessageHandler implementation. virtual void RegisterMessages(); @@ -120,6 +114,7 @@ class CloudPrintFlowHandler : public DOMMessageHandler, virtual void SetDialogDelegate(CloudPrintHtmlDialogDelegate *delegate); void CancelAnyRunningTask(); + void StoreDialogClientSize() const; private: // For unit testing. @@ -156,6 +151,7 @@ class CloudPrintHtmlDialogDelegate : public HtmlDialogUIDelegate { virtual std::string GetDialogArgs() const; virtual void OnDialogClosed(const std::string& json_retval); virtual void OnCloseContents(TabContents* source, bool* out_close_dialog); + virtual bool ShouldShowDialogTitle() const; private: friend class ::CloudPrintHtmlDialogDelegateTest; diff --git a/chrome/browser/printing/print_dialog_cloud_uitest.cc b/chrome/browser/printing/print_dialog_cloud_uitest.cc index 1cc446d..3b57961 100644 --- a/chrome/browser/printing/print_dialog_cloud_uitest.cc +++ b/chrome/browser/printing/print_dialog_cloud_uitest.cc @@ -11,6 +11,7 @@ #include "base/file_util.h" #include "base/path_service.h" #include "base/singleton.h" +#include "base/thread_restrictions.h" #include "base/values.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_thread.h" @@ -32,7 +33,11 @@ class TestData { public: TestData() {} - char* GetTestData() { + const char* GetTestData() { + // Fetching this data blocks the IO thread, but we don't really care because + // this is a test. + base::ThreadRestrictions::ScopedAllowIO allow_io; + if (test_data_.empty()) { FilePath test_data_directory; PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory); @@ -40,7 +45,7 @@ class TestData { test_data_directory.AppendASCII("printing/cloud_print_uitest.html"); file_util::ReadFileToString(test_file, &test_data_); } - return &test_data_[0]; + return test_data_.c_str(); } private: std::string test_data_; diff --git a/chrome/browser/printing/print_dialog_cloud_unittest.cc b/chrome/browser/printing/print_dialog_cloud_unittest.cc index a03942b..da51656 100644 --- a/chrome/browser/printing/print_dialog_cloud_unittest.cc +++ b/chrome/browser/printing/print_dialog_cloud_unittest.cc @@ -300,7 +300,7 @@ TEST_F(CloudPrintHtmlDialogDelegateTest, BasicChecks) { EXPECT_TRUE(delegate_->IsDialogModal()); EXPECT_THAT(delegate_->GetDialogContentURL().spec(), StrEq(chrome::kCloudPrintResourcesURL)); - EXPECT_THAT(delegate_->GetDialogTitle(), HasSubstr(L"Print")); + EXPECT_TRUE(delegate_->GetDialogTitle().empty()); bool close_dialog = false; delegate_->OnCloseContents(NULL, &close_dialog); diff --git a/chrome/browser/printing/print_dialog_gtk.cc b/chrome/browser/printing/print_dialog_gtk.cc index 1a4db92..585daf4 100644 --- a/chrome/browser/printing/print_dialog_gtk.cc +++ b/chrome/browser/printing/print_dialog_gtk.cc @@ -9,9 +9,11 @@ #include <gtk/gtkpagesetupunixdialog.h> #include "base/file_util.h" +#include "base/file_util_proxy.h" #include "base/lazy_instance.h" #include "base/lock.h" #include "base/logging.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_thread.h" @@ -169,7 +171,11 @@ void PrintDialogGtk::OnJobCompleted(GtkPrintJob* job, GError* error) { if (job) g_object_unref(job); - file_util::Delete(path_to_pdf_, false); + base::FileUtilProxy::Delete( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), + path_to_pdf_, + false, + NULL); delete this; } diff --git a/chrome/browser/printing/print_job_manager.cc b/chrome/browser/printing/print_job_manager.cc index 91b27ee..25f4791 100644 --- a/chrome/browser/printing/print_job_manager.cc +++ b/chrome/browser/printing/print_job_manager.cc @@ -61,7 +61,7 @@ void PrintJobManager::StopJobs(bool wait_for_finish) { void PrintJobManager::QueuePrinterQuery(PrinterQuery* job) { AutoLock lock(lock_); DCHECK(job); - queued_queries_.push_back(job); + queued_queries_.push_back(make_scoped_refptr(job)); DCHECK(job->is_valid()); } @@ -116,7 +116,7 @@ void PrintJobManager::OnPrintJobEvent( current_jobs_.end(), print_job)); // Causes a AddRef(). - current_jobs_.push_back(print_job); + current_jobs_.push_back(make_scoped_refptr(print_job)); break; } case JobEventDetails::JOB_DONE: { diff --git a/chrome/browser/printing/print_job_unittest.cc b/chrome/browser/printing/print_job_unittest.cc index 757d6c8..1b62016 100644 --- a/chrome/browser/printing/print_job_unittest.cc +++ b/chrome/browser/printing/print_job_unittest.cc @@ -92,8 +92,8 @@ class TestPrintNotifObserv : public NotificationObserver { #define MAYBE_SimplePrint SimplePrint #endif TEST(PrintJobTest, MAYBE_SimplePrint) { - // Test the multithreaded nature of PrintJob to make sure we can use it with - // known livetime. + // Test the multi-threaded nature of PrintJob to make sure we can use it with + // known lifetime. // This message loop is actually never run. MessageLoop current; diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index 9ac5a72..bc5ba44 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -335,7 +335,7 @@ class OffTheRecordProfileImpl : public Profile, profile_->InitThemes(); } - virtual void SetTheme(Extension* extension) { + virtual void SetTheme(const Extension* extension) { profile_->SetTheme(extension); } @@ -347,7 +347,7 @@ class OffTheRecordProfileImpl : public Profile, profile_->ClearTheme(); } - virtual Extension* GetTheme() { + virtual const Extension* GetTheme() { return profile_->GetTheme(); } @@ -424,6 +424,11 @@ class OffTheRecordProfileImpl : public Profile, return false; } + virtual bool HasProfileSyncService() const { + // We never have a profile sync service. + return false; + } + virtual bool DidLastSessionExitCleanly() { return profile_->DidLastSessionExitCleanly(); } @@ -453,6 +458,10 @@ class OffTheRecordProfileImpl : public Profile, return NULL; } + virtual BrowserSignin* GetBrowserSignin() { + return profile_->GetBrowserSignin(); + } + virtual CloudPrintProxyService* GetCloudPrintProxyService() { return NULL; } @@ -487,6 +496,10 @@ class OffTheRecordProfileImpl : public Profile, return webkit_context_.get(); } + virtual history::TopSites* GetTopSitesWithoutCreating() { + return NULL; + } + virtual history::TopSites* GetTopSites() { return NULL; } diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h index c99981b..aac3f02 100644 --- a/chrome/browser/profile.h +++ b/chrome/browser/profile.h @@ -38,6 +38,7 @@ class AutocompleteClassifier; class BackgroundContentsService; class BackgroundModeManager; class BookmarkModel; +class BrowserSignin; class BrowserThemeProvider; class ChromeAppCacheService; class ChromeBlobStorageContext; @@ -173,6 +174,9 @@ class Profile { // for this profile. virtual history::TopSites* GetTopSites() = 0; + // Variant of GetTopSites that doesn't force creation. + virtual history::TopSites* GetTopSitesWithoutCreating() = 0; + // Retrieves a pointer to the VisitedLinkMaster associated with this // profile. The VisitedLinkMaster is lazily created the first time // that this method is called. @@ -286,11 +290,14 @@ class Profile { // Returns the HTML5 FileSystemHostContext assigned to this profile. virtual FileSystemHostContext* GetFileSystemHostContext() = 0; + // Returns the BrowserSignin object assigned to this profile. + virtual BrowserSignin* GetBrowserSignin() = 0; + // Init our themes system. virtual void InitThemes() = 0; // Set the theme to the specified extension. - virtual void SetTheme(Extension* extension) = 0; + virtual void SetTheme(const Extension* extension) = 0; // Set the theme to the machine's native theme. virtual void SetNativeTheme() = 0; @@ -300,7 +307,7 @@ class Profile { // Gets the theme that was last set. Returns NULL if the theme is no longer // installed, if there is no installed theme, or the theme was cleared. - virtual Extension* GetTheme() = 0; + virtual const Extension* GetTheme() = 0; // Returns or creates the ThemeProvider associated with this profile virtual BrowserThemeProvider* GetThemeProvider() = 0; @@ -323,12 +330,14 @@ class Profile { // notification has fired. The purpose for handling this event first is to // avoid race conditions by making sure URLRequestContexts learn about new // extensions before anything else needs them to know. - virtual void RegisterExtensionWithRequestContexts(Extension* extension) {} + virtual void RegisterExtensionWithRequestContexts( + const Extension* extension) {} // Called by the ExtensionsService that lives in this profile. Lets the // profile clean up its RequestContexts once all the listeners to the // EXTENSION_UNLOADED notification have finished running. - virtual void UnregisterExtensionWithRequestContexts(Extension* extension) {} + virtual void UnregisterExtensionWithRequestContexts( + const Extension* extension) {} // Returns the SSLConfigService for this profile. virtual net::SSLConfigService* GetSSLConfigService() = 0; @@ -370,6 +379,9 @@ class Profile { // Returns true if this profile has a session service. virtual bool HasSessionService() const = 0; + // Returns true if this profile has a profile sync service. + virtual bool HasProfileSyncService() const = 0; + // Returns true if the last time this profile was open it was exited cleanly. virtual bool DidLastSessionExitCleanly() = 0; diff --git a/chrome/browser/profile_impl.cc b/chrome/browser/profile_impl.cc index 8304c8d..649ef1c 100644 --- a/chrome/browser/profile_impl.cc +++ b/chrome/browser/profile_impl.cc @@ -23,6 +23,7 @@ #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_signin.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/chrome_blob_storage_context.h" #include "chrome/browser/dom_ui/ntp_resource_cache.h" @@ -305,9 +306,9 @@ ProfileImpl::ProfileImpl(const FilePath& path) if (base_cache_path_.empty()) base_cache_path_ = path_; - // Listen for theme installation. + // Listen for theme installations from our original profile. registrar_.Add(this, NotificationType::THEME_INSTALLED, - NotificationService::AllSources()); + Source<Profile>(GetOriginalProfile())); // Listen for bookmark model load, to bootstrap the sync service. registrar_.Add(this, NotificationType::BOOKMARK_MODEL_LOADED, @@ -317,7 +318,7 @@ ProfileImpl::ProfileImpl(const FilePath& path) SSLConfigServiceManager::CreateDefaultManager(this)); #if defined(OS_CHROMEOS) - chromeos_preferences_.reset(new chromeos::Preferences(this)); + chromeos_preferences_.reset(new chromeos::Preferences()); chromeos_preferences_->Init(prefs); #endif @@ -399,11 +400,6 @@ void ProfileImpl::RegisterComponentExtensions() { component_extensions.push_back( std::make_pair("web_store", IDR_WEBSTORE_MANIFEST)); -#if defined(OS_CHROMEOS) && defined(GOOGLE_CHROME_BUILD) - component_extensions.push_back( - std::make_pair("chat_manager", IDR_TALK_APP_MANIFEST)); -#endif - for (ComponentExtensionList::iterator iter = component_extensions.begin(); iter != component_extensions.end(); ++iter) { FilePath path; @@ -513,7 +509,7 @@ ProfileImpl::~ProfileImpl() { web_data_service_->Shutdown(); if (top_sites_.get()) - top_sites_->ClearProfile(); + top_sites_->Shutdown(); if (history_service_.get()) history_service_->Cleanup(); @@ -773,18 +769,20 @@ URLRequestContextGetter* ProfileImpl::GetRequestContextForExtensions() { return extensions_request_context_; } -void ProfileImpl::RegisterExtensionWithRequestContexts(Extension* extension) { +void ProfileImpl::RegisterExtensionWithRequestContexts( + const Extension* extension) { // AddRef to ensure the data lives until the other thread gets it. Balanced in // OnNewExtensions. - extension->static_data()->AddRef(); + extension->AddRef(); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableMethod(extension_info_map_.get(), &ExtensionInfoMap::AddExtension, - extension->static_data())); + extension)); } -void ProfileImpl::UnregisterExtensionWithRequestContexts(Extension* extension) { +void ProfileImpl::UnregisterExtensionWithRequestContexts( + const Extension* extension) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableMethod(extension_info_map_.get(), @@ -1032,7 +1030,7 @@ void ProfileImpl::InitThemes() { } } -void ProfileImpl::SetTheme(Extension* extension) { +void ProfileImpl::SetTheme(const Extension* extension) { InitThemes(); theme_provider_.get()->SetTheme(extension); } @@ -1047,7 +1045,7 @@ void ProfileImpl::ClearTheme() { theme_provider_.get()->UseDefaultTheme(); } -Extension* ProfileImpl::GetTheme() { +const Extension* ProfileImpl::GetTheme() { InitThemes(); std::string id = theme_provider_.get()->GetThemeID(); @@ -1086,6 +1084,10 @@ bool ProfileImpl::HasSessionService() const { return (session_service_.get() != NULL); } +bool ProfileImpl::HasProfileSyncService() const { + return (sync_service_.get() != NULL); +} + bool ProfileImpl::DidLastSessionExitCleanly() { // last_session_exited_cleanly_ is set when the preferences are loaded. Force // it to be set by asking for the prefs. @@ -1126,6 +1128,10 @@ history::TopSites* ProfileImpl::GetTopSites() { return top_sites_; } +history::TopSites* ProfileImpl::GetTopSitesWithoutCreating() { + return top_sites_; +} + void ProfileImpl::ResetTabRestoreService() { tab_restore_service_ = NULL; } @@ -1215,7 +1221,8 @@ void ProfileImpl::Observe(NotificationType type, Source<Profile>(this), NotificationService::NoDetails()); } } else if (NotificationType::THEME_INSTALLED == type) { - Extension* extension = Details<Extension>(details).ptr(); + DCHECK_EQ(Source<Profile>(source).ptr(), GetOriginalProfile()); + const Extension* extension = Details<const Extension>(details).ptr(); SetTheme(extension); } else if (NotificationType::BOOKMARK_MODEL_LOADED == type) { GetProfileSyncService(); // Causes lazy-load if sync is enabled. @@ -1249,6 +1256,13 @@ ProfileSyncService* ProfileImpl::GetProfileSyncService( return sync_service_.get(); } +BrowserSignin* ProfileImpl::GetBrowserSignin() { + if (!browser_signin_.get()) { + browser_signin_.reset(new BrowserSignin(this)); + } + return browser_signin_.get(); +} + CloudPrintProxyService* ProfileImpl::GetCloudPrintProxyService() { if (!cloud_print_proxy_service_.get()) InitCloudPrintProxyService(); diff --git a/chrome/browser/profile_impl.h b/chrome/browser/profile_impl.h index 1abb00d..9834fc1 100644 --- a/chrome/browser/profile_impl.h +++ b/chrome/browser/profile_impl.h @@ -48,6 +48,7 @@ class ProfileImpl : public Profile, virtual ChromeAppCacheService* GetAppCacheService(); virtual webkit_database::DatabaseTracker* GetDatabaseTracker(); virtual history::TopSites* GetTopSites(); + virtual history::TopSites* GetTopSitesWithoutCreating(); virtual VisitedLinkMaster* GetVisitedLinkMaster(); virtual UserScriptMaster* GetUserScriptMaster(); virtual SSLHostState* GetSSLHostState(); @@ -71,17 +72,18 @@ class ProfileImpl : public Profile, virtual PersonalDataManager* GetPersonalDataManager(); virtual FileSystemHostContext* GetFileSystemHostContext(); virtual void InitThemes(); - virtual void SetTheme(Extension* extension); + virtual void SetTheme(const Extension* extension); virtual void SetNativeTheme(); virtual void ClearTheme(); - virtual Extension* GetTheme(); + virtual const Extension* GetTheme(); virtual BrowserThemeProvider* GetThemeProvider(); virtual bool HasCreatedDownloadManager() const; virtual URLRequestContextGetter* GetRequestContext(); virtual URLRequestContextGetter* GetRequestContextForMedia(); virtual URLRequestContextGetter* GetRequestContextForExtensions(); - virtual void RegisterExtensionWithRequestContexts(Extension* extension); - virtual void UnregisterExtensionWithRequestContexts(Extension* extension); + virtual void RegisterExtensionWithRequestContexts(const Extension* extension); + virtual void UnregisterExtensionWithRequestContexts( + const Extension* extension); virtual net::SSLConfigService* GetSSLConfigService(); virtual HostContentSettingsMap* GetHostContentSettingsMap(); virtual HostZoomMap* GetHostZoomMap(); @@ -92,6 +94,7 @@ class ProfileImpl : public Profile, virtual SessionService* GetSessionService(); virtual void ShutdownSessionService(); virtual bool HasSessionService() const; + virtual bool HasProfileSyncService() const; virtual bool DidLastSessionExitCleanly(); virtual BookmarkModel* GetBookmarkModel(); virtual bool IsSameProfile(Profile* profile); @@ -119,6 +122,7 @@ class ProfileImpl : public Profile, void InitCloudPrintProxyService(); virtual ChromeBlobStorageContext* GetBlobStorageContext(); virtual ExtensionInfoMap* GetExtensionInfoMap(); + virtual BrowserSignin* GetBrowserSignin(); #if defined(OS_CHROMEOS) virtual chromeos::ProxyConfigServiceImpl* GetChromeOSProxyConfigServiceImpl(); @@ -218,6 +222,7 @@ class ProfileImpl : public Profile, scoped_refptr<PersonalDataManager> personal_data_manager_; scoped_ptr<PinnedTabService> pinned_tab_service_; scoped_refptr<FileSystemHostContext> file_system_host_context_; + scoped_ptr<BrowserSignin> browser_signin_; bool history_service_created_; bool favicon_service_created_; bool created_web_data_service_; diff --git a/chrome/browser/profile_manager_unittest.cc b/chrome/browser/profile_manager_unittest.cc index 24c3f0f..b3e7aad 100644 --- a/chrome/browser/profile_manager_unittest.cc +++ b/chrome/browser/profile_manager_unittest.cc @@ -143,4 +143,6 @@ TEST_F(ProfileManagerTest, CreateAndUseTwoProfiles) { EXPECT_TRUE(profile2->GetHistoryService(Profile::EXPLICIT_ACCESS)); profile1.reset(); profile2.reset(); + // Make sure history cleans up correctly. + message_loop_.RunAllPending(); } diff --git a/chrome/browser/remoting/remoting_setup_flow.cc b/chrome/browser/remoting/remoting_setup_flow.cc index f5b5937..fc50e8d 100644 --- a/chrome/browser/remoting/remoting_setup_flow.cc +++ b/chrome/browser/remoting/remoting_setup_flow.cc @@ -172,6 +172,10 @@ bool RemotingSetupFlow::IsDialogModal() const { return true; } +bool RemotingSetupFlow::ShouldShowDialogTitle() const { + return true; +} + /////////////////////////////////////////////////////////////////////////////// // GaiaAuthConsumer implementation. void RemotingSetupFlow::OnClientLoginFailure( diff --git a/chrome/browser/remoting/remoting_setup_flow.h b/chrome/browser/remoting/remoting_setup_flow.h index 05ec5da..c2e88f8 100644 --- a/chrome/browser/remoting/remoting_setup_flow.h +++ b/chrome/browser/remoting/remoting_setup_flow.h @@ -62,6 +62,7 @@ class RemotingSetupFlow : public HtmlDialogUIDelegate, virtual void OnCloseContents(TabContents* source, bool* out_close_dialog); virtual std::wstring GetDialogTitle() const; virtual bool IsDialogModal() const; + virtual bool ShouldShowDialogTitle() const; // GaiaAuthConsumer implementation. virtual void OnClientLoginFailure( diff --git a/chrome/browser/renderer_host/accelerated_surface_container_mac.cc b/chrome/browser/renderer_host/accelerated_surface_container_mac.cc index fdba6ea..7fb2e07 100644 --- a/chrome/browser/renderer_host/accelerated_surface_container_mac.cc +++ b/chrome/browser/renderer_host/accelerated_surface_container_mac.cc @@ -14,7 +14,7 @@ AcceleratedSurfaceContainerMac::AcceleratedSurfaceContainerMac( bool opaque) : manager_(manager), opaque_(opaque), - surface_(NULL), + surface_id_(0), width_(0), height_(0), texture_(0), @@ -25,29 +25,17 @@ AcceleratedSurfaceContainerMac::AcceleratedSurfaceContainerMac( } AcceleratedSurfaceContainerMac::~AcceleratedSurfaceContainerMac() { - ReleaseIOSurface(); -} - -void AcceleratedSurfaceContainerMac::ReleaseIOSurface() { - if (surface_) { - CFRelease(surface_); - surface_ = NULL; - } } void AcceleratedSurfaceContainerMac::SetSizeAndIOSurface( int32 width, int32 height, uint64 io_surface_identifier) { - ReleaseIOSurface(); - IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); - if (io_surface_support) { - surface_ = io_surface_support->IOSurfaceLookup( - static_cast<uint32>(io_surface_identifier)); - EnqueueTextureForDeletion(); - width_ = width; - height_ = height; - } + // Ignore |io_surface_identifier|: The surface hasn't been painted to and + // only contains garbage data. Update the surface in |set_was_painted_to()| + // instead. + width_ = width; + height_ = height; } void AcceleratedSurfaceContainerMac::SetSizeAndTransportDIB( @@ -85,7 +73,7 @@ void AcceleratedSurfaceContainerMac::Draw(CGLContextObj context) { texture_pending_deletion_ = 0; } if (!texture_) { - if ((io_surface_support && !surface_) || + if ((io_surface_support && !surface_.get()) || (!io_surface_support && !transport_dib_.get())) return; glGenTextures(1, &texture_); @@ -112,18 +100,18 @@ void AcceleratedSurfaceContainerMac::Draw(CGLContextObj context) { // When using an IOSurface, the texture does not need to be repeatedly // uploaded, just when we've been told we have to. if (io_surface_support && texture_needs_upload_) { - DCHECK(surface_); + DCHECK(surface_.get()); glBindTexture(target, texture_); // Don't think we need to identify a plane. GLuint plane = 0; io_surface_support->CGLTexImageIOSurface2D(context, target, GL_RGBA, - width_, - height_, + surface_width_, + surface_height_, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, - surface_, + surface_.get(), plane); texture_needs_upload_ = false; } @@ -146,6 +134,9 @@ void AcceleratedSurfaceContainerMac::Draw(CGLContextObj context) { } if (texture_) { + int texture_width = io_surface_support ? surface_width_ : width_; + int texture_height = io_surface_support ? surface_height_ : height_; + // TODO(kbr): convert this to use only OpenGL ES 2.0 functionality. // TODO(kbr): may need to pay attention to cutout rects. @@ -154,6 +145,11 @@ void AcceleratedSurfaceContainerMac::Draw(CGLContextObj context) { int clipWidth = clipRect_.width(); int clipHeight = clipRect_.height(); + if (clipX + clipWidth > texture_width) + clipWidth = texture_width - clipX; + if (clipY + clipHeight > texture_height) + clipHeight = texture_height - clipY; + if (opaque_) { // Pepper 3D's output is currently considered opaque even if the // program draws pixels with alpha less than 1. In order to have @@ -184,16 +180,16 @@ void AcceleratedSurfaceContainerMac::Draw(CGLContextObj context) { glEnable(target); glBegin(GL_TRIANGLE_STRIP); - glTexCoord2f(clipX, height_ - clipY); + glTexCoord2f(clipX, texture_height - clipY); glVertex3f(0, 0, 0); - glTexCoord2f(clipX + clipWidth, height_ - clipY); + glTexCoord2f(clipX + clipWidth, texture_height - clipY); glVertex3f(clipWidth, 0, 0); - glTexCoord2f(clipX, height_ - clipY - clipHeight); + glTexCoord2f(clipX, texture_height - clipY - clipHeight); glVertex3f(0, clipHeight, 0); - glTexCoord2f(clipX + clipWidth, height_ - clipY - clipHeight); + glTexCoord2f(clipX + clipWidth, texture_height - clipY - clipHeight); glVertex3f(clipWidth, clipHeight, 0); glEnd(); @@ -201,6 +197,27 @@ void AcceleratedSurfaceContainerMac::Draw(CGLContextObj context) { } } +void AcceleratedSurfaceContainerMac::set_was_painted_to(uint64 surface_id) { + if (surface_id && (!surface_ || surface_id != surface_id_)) { + // Keep the surface that was most recently painted to around. + if (IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize()) { + CFTypeRef surface = io_surface_support->IOSurfaceLookup( + static_cast<uint32>(surface_id)); + // Can fail if IOSurface with that ID was already released by the + // gpu process or the plugin process. We will get a |set_was_painted_to()| + // message with a new surface soon in that case. + if (surface) { + surface_.reset(surface); + surface_id_ = surface_id; + surface_width_ = io_surface_support->IOSurfaceGetWidth(surface_); + surface_height_ = io_surface_support->IOSurfaceGetHeight(surface_); + EnqueueTextureForDeletion(); + } + } + } + was_painted_to_ = true; +} + void AcceleratedSurfaceContainerMac::EnqueueTextureForDeletion() { if (texture_) { DCHECK(texture_pending_deletion_ == 0); diff --git a/chrome/browser/renderer_host/accelerated_surface_container_mac.h b/chrome/browser/renderer_host/accelerated_surface_container_mac.h index b0b6051..0c343e4 100644 --- a/chrome/browser/renderer_host/accelerated_surface_container_mac.h +++ b/chrome/browser/renderer_host/accelerated_surface_container_mac.h @@ -31,6 +31,7 @@ #include "app/surface/transport_dib.h" #include "base/basictypes.h" +#include "base/mac/scoped_cftyperef.h" #include "base/scoped_ptr.h" #include "gfx/native_widget_types.h" #include "gfx/rect.h" @@ -75,8 +76,11 @@ class AcceleratedSurfaceContainerMac { // time the drawing context has changed. void ForceTextureReload() { texture_needs_upload_ = true; } - // Notifies the surface that it was painted to. - void set_was_painted_to() { was_painted_to_ = true; } + // Notifies the the container that its surface was painted to. + void set_was_painted_to(uint64 surface_id); + + // Notifies the container that its surface is invalid. + void set_surface_invalid() { was_painted_to_ = false; } // Returns if the surface should be shown. bool should_be_visible() const { return visible_ && was_painted_to_; } @@ -91,14 +95,22 @@ class AcceleratedSurfaceContainerMac { // plugin process back to the browser process for drawing. // This is held as a CFTypeRef because we can't refer to the // IOSurfaceRef type when building on 10.5. - CFTypeRef surface_; + base::mac::ScopedCFTypeRef<CFTypeRef> surface_; + + // The id of |surface_|, or 0 if |surface_| is NULL. + uint64 surface_id_; + + // The width and height of the io surface. During resizing, this is different + // from |width_| and |height_|. + int32 surface_width_; + int32 surface_height_; // The TransportDIB which is used in pre-10.6 systems where the IOSurface // API is not supported. This is a weak reference to the actual TransportDIB // whic is owned by the GPU process. scoped_ptr<TransportDIB> transport_dib_; - // The width and height of the surface. + // The width and height of the container. int32 width_; int32 height_; diff --git a/chrome/browser/renderer_host/accelerated_surface_container_manager_mac.cc b/chrome/browser/renderer_host/accelerated_surface_container_manager_mac.cc index 8e8a17e..b123f76 100644 --- a/chrome/browser/renderer_host/accelerated_surface_container_manager_mac.cc +++ b/chrome/browser/renderer_host/accelerated_surface_container_manager_mac.cc @@ -53,6 +53,13 @@ bool AcceleratedSurfaceContainerManagerMac::IsRootContainer( root_container_handle_ == id; } +void AcceleratedSurfaceContainerManagerMac:: + set_gpu_rendering_active(bool active) { + if (gpu_rendering_active_ && !active) + SetRootSurfaceInvalid(); + gpu_rendering_active_ = active; +} + void AcceleratedSurfaceContainerManagerMac::SetSizeAndIOSurface( gfx::PluginWindowHandle id, int32 width, @@ -92,7 +99,10 @@ void AcceleratedSurfaceContainerManagerMac::Draw(CGLContextObj context, AutoLock lock(lock_); glColorMask(true, true, true, true); - glClearColor(0, 0, 0, 0); + // Should match the clear color of RenderWidgetHostViewMac. + glClearColor(255, 255, 255, 255); + // TODO(thakis): Clearing the whole color buffer is wasteful, since most of + // it is overwritten by the surface. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); @@ -124,12 +134,18 @@ void AcceleratedSurfaceContainerManagerMac::ForceTextureReload() { } void AcceleratedSurfaceContainerManagerMac::SetSurfaceWasPaintedTo( - gfx::PluginWindowHandle id) { + gfx::PluginWindowHandle id, uint64 surface_id) { AutoLock lock(lock_); AcceleratedSurfaceContainerMac* container = MapIDToContainer(id); if (container) - container->set_was_painted_to(); + container->set_was_painted_to(surface_id); +} + +void AcceleratedSurfaceContainerManagerMac::SetRootSurfaceInvalid() { + AutoLock lock(lock_); + if (root_container_) + root_container_->set_surface_invalid(); } bool AcceleratedSurfaceContainerManagerMac::SurfaceShouldBeVisible( diff --git a/chrome/browser/renderer_host/accelerated_surface_container_manager_mac.h b/chrome/browser/renderer_host/accelerated_surface_container_manager_mac.h index 0e38c11..a03a5c4 100644 --- a/chrome/browser/renderer_host/accelerated_surface_container_manager_mac.h +++ b/chrome/browser/renderer_host/accelerated_surface_container_manager_mac.h @@ -46,7 +46,7 @@ class AcceleratedSurfaceContainerManagerMac { } // Informs the manager if gpu rendering is active. - void set_gpu_rendering_active(bool active) { gpu_rendering_active_ = active; } + void set_gpu_rendering_active(bool active); // Sets the size and backing store of the plugin instance. There are two // versions: the IOSurface version is used on systems where the IOSurface @@ -74,7 +74,10 @@ class AcceleratedSurfaceContainerManagerMac { void ForceTextureReload(); // Notifies a surface that it has been painted to. - void SetSurfaceWasPaintedTo(gfx::PluginWindowHandle id); + void SetSurfaceWasPaintedTo(gfx::PluginWindowHandle id, uint64 surface_id); + + // Notifies the root container that its surface is invalid. + void SetRootSurfaceInvalid(); // Returns if a given surface should be shown. bool SurfaceShouldBeVisible(gfx::PluginWindowHandle id) const; diff --git a/chrome/browser/renderer_host/async_resource_handler.cc b/chrome/browser/renderer_host/async_resource_handler.cc index 92202fa..70dfc74 100644 --- a/chrome/browser/renderer_host/async_resource_handler.cc +++ b/chrome/browser/renderer_host/async_resource_handler.cc @@ -48,8 +48,7 @@ class SharedIOBuffer : public net::IOBuffer { buffer_size_(buffer_size) {} bool Init() { - if (shared_memory_.Create(std::string(), false, false, buffer_size_) && - shared_memory_.Map(buffer_size_)) { + if (shared_memory_.CreateAndMapAnonymous(buffer_size_)) { data_ = reinterpret_cast<char*>(shared_memory_.memory()); DCHECK(data_); ok_ = true; diff --git a/chrome/browser/renderer_host/audio_renderer_host.cc b/chrome/browser/renderer_host/audio_renderer_host.cc index 0b594a0..4c83384 100644 --- a/chrome/browser/renderer_host/audio_renderer_host.cc +++ b/chrome/browser/renderer_host/audio_renderer_host.cc @@ -67,6 +67,15 @@ static uint32 SelectHardwarePacketSize(AudioParameters params) { return params.channels * samples * params.bits_per_sample / 8; } +AudioRendererHost::AudioEntry::AudioEntry() + : render_view_id(0), + stream_id(0), + pending_buffer_request(false), + pending_close(false) { +} + +AudioRendererHost::AudioEntry::~AudioEntry() {} + /////////////////////////////////////////////////////////////////////////////// // AudioRendererHost implementations. AudioRendererHost::AudioRendererHost() @@ -216,7 +225,7 @@ void AudioRendererHost::DoCompleteCreation( SendMessage(new ViewMsg_NotifyLowLatencyAudioStreamCreated( entry->render_view_id, entry->stream_id, foreign_memory_handle, - foreign_socket_handle, entry->shared_memory.max_size())); + foreign_socket_handle, entry->shared_memory.created_size())); return; } @@ -224,7 +233,7 @@ void AudioRendererHost::DoCompleteCreation( // process. SendMessage(new ViewMsg_NotifyAudioStreamCreated( entry->render_view_id, entry->stream_id, foreign_memory_handle, - entry->shared_memory.max_size())); + entry->shared_memory.created_size())); } void AudioRendererHost::DoSendPlayingMessage( @@ -346,8 +355,7 @@ void AudioRendererHost::OnCreateStream( scoped_ptr<AudioEntry> entry(new AudioEntry()); // Create the shared memory and share with the renderer process. - if (!entry->shared_memory.Create("", false, false, hardware_packet_size) || - !entry->shared_memory.Map(entry->shared_memory.max_size())) { + if (!entry->shared_memory.CreateAndMapAnonymous(hardware_packet_size)) { // If creation of shared memory failed then send an error message. SendErrorMessage(msg.routing_id(), stream_id); return; @@ -471,7 +479,7 @@ void AudioRendererHost::OnNotifyPacketReady( } DCHECK(!entry->controller->LowLatencyMode()); - CHECK(packet_size <= entry->shared_memory.max_size()); + CHECK(packet_size <= entry->shared_memory.created_size()); if (!entry->pending_buffer_request) { NOTREACHED() << "Buffer received but no such pending request"; diff --git a/chrome/browser/renderer_host/audio_renderer_host.h b/chrome/browser/renderer_host/audio_renderer_host.h index 8ec3cd1..d2dfd59 100644 --- a/chrome/browser/renderer_host/audio_renderer_host.h +++ b/chrome/browser/renderer_host/audio_renderer_host.h @@ -83,12 +83,8 @@ class AudioRendererHost : public base::RefCountedThreadSafe< typedef std::pair<int32, int> AudioEntryId; struct AudioEntry { - AudioEntry() - : render_view_id(0), - stream_id(0), - pending_buffer_request(false), - pending_close(false) { - } + AudioEntry(); + ~AudioEntry(); // The AudioOutputController that manages the audio stream. scoped_refptr<media::AudioOutputController> controller; diff --git a/chrome/browser/renderer_host/audio_sync_reader.cc b/chrome/browser/renderer_host/audio_sync_reader.cc index f664d19..ce989e5 100644 --- a/chrome/browser/renderer_host/audio_sync_reader.cc +++ b/chrome/browser/renderer_host/audio_sync_reader.cc @@ -20,9 +20,9 @@ void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { } uint32 AudioSyncReader::Read(void* data, uint32 size) { - int read_size = std::min(size, shared_memory_->max_size()); + uint32 read_size = std::min(size, shared_memory_->created_size()); memcpy(data, shared_memory_->memory(), read_size); - memset(shared_memory_->memory(), 0, shared_memory_->max_size()); + memset(shared_memory_->memory(), 0, shared_memory_->created_size()); return read_size; } diff --git a/chrome/browser/renderer_host/backing_store.h b/chrome/browser/renderer_host/backing_store.h index 53d0288..57d5078 100644 --- a/chrome/browser/renderer_host/backing_store.h +++ b/chrome/browser/renderer_host/backing_store.h @@ -40,18 +40,11 @@ class BackingStore { // Paints the bitmap from the renderer onto the backing store. bitmap_rect // gives the location of bitmap, and copy_rects specifies the subregion(s) of // the backingstore to be painted from the bitmap. - // - // The value placed into |*painted_synchronously| indicates if the paint was - // completed synchronously and the TransportDIB can be freed. False means that - // the backing store may still be using the transport DIB and it will manage - // notifying the RenderWidgetHost that it's done with it via - // DonePaintingToBackingStore(). virtual void PaintToBackingStore( RenderProcessHost* process, TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, - const std::vector<gfx::Rect>& copy_rects, - bool* painted_synchronously) = 0; + const std::vector<gfx::Rect>& copy_rects) = 0; // Extracts the gives subset of the backing store and copies it to the given // PlatformCanvas. The PlatformCanvas should not be initialized. This function diff --git a/chrome/browser/renderer_host/backing_store_mac.h b/chrome/browser/renderer_host/backing_store_mac.h index 2c75400..9c4cc6d 100644 --- a/chrome/browser/renderer_host/backing_store_mac.h +++ b/chrome/browser/renderer_host/backing_store_mac.h @@ -28,8 +28,7 @@ class BackingStoreMac : public BackingStore { RenderProcessHost* process, TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, - const std::vector<gfx::Rect>& copy_rects, - bool* painted_synchronously); + const std::vector<gfx::Rect>& copy_rects); virtual bool CopyFromBackingStore(const gfx::Rect& rect, skia::PlatformCanvas* output); virtual void ScrollBackingStore(int dx, int dy, diff --git a/chrome/browser/renderer_host/backing_store_mac.mm b/chrome/browser/renderer_host/backing_store_mac.mm index bea378f..5754839 100644 --- a/chrome/browser/renderer_host/backing_store_mac.mm +++ b/chrome/browser/renderer_host/backing_store_mac.mm @@ -56,12 +56,7 @@ void BackingStoreMac::PaintToBackingStore( RenderProcessHost* process, TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, - const std::vector<gfx::Rect>& copy_rects, - bool* painted_synchronously) { - // Our paints are always synchronous and the caller can free the TransportDIB, - // even on failure. - *painted_synchronously = true; - + const std::vector<gfx::Rect>& copy_rects) { DCHECK_NE(static_cast<bool>(cg_layer()), static_cast<bool>(cg_bitmap())); TransportDIB* dib = process->GetTransportDIB(bitmap); diff --git a/chrome/browser/renderer_host/backing_store_manager.cc b/chrome/browser/renderer_host/backing_store_manager.cc index 4325f31..755a772 100644 --- a/chrome/browser/renderer_host/backing_store_manager.cc +++ b/chrome/browser/renderer_host/backing_store_manager.cc @@ -196,11 +196,7 @@ void BackingStoreManager::PrepareBackingStore( TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, const std::vector<gfx::Rect>& copy_rects, - bool* needs_full_paint, - bool* painted_synchronously) { - // Default to declaring we're done using the transport DIB so it can be freed. - *painted_synchronously = true; - + bool* needs_full_paint) { BackingStore* backing_store = GetBackingStore(host, backing_store_size); if (!backing_store) { // We need to get Webkit to generate a new paint here, as we @@ -218,8 +214,7 @@ void BackingStoreManager::PrepareBackingStore( } backing_store->PaintToBackingStore(host->process(), bitmap, - bitmap_rect, copy_rects, - painted_synchronously); + bitmap_rect, copy_rects); } // static diff --git a/chrome/browser/renderer_host/backing_store_manager.h b/chrome/browser/renderer_host/backing_store_manager.h index 1ab78cc..3db86b0 100644 --- a/chrome/browser/renderer_host/backing_store_manager.h +++ b/chrome/browser/renderer_host/backing_store_manager.h @@ -43,19 +43,13 @@ class BackingStoreManager { // needs_full_paint // Set if we need to send out a request to paint the view // to the renderer. - // painted_synchronously - // Will be set by the function if the request was processed synchronously, - // and the bitmap is done being used. False means that the backing store - // will paint the bitmap at a later time and that the TransportDIB can't be - // freed (it will be the backing store's job to free it later). static void PrepareBackingStore( RenderWidgetHost* host, const gfx::Size& backing_store_size, TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, const std::vector<gfx::Rect>& copy_rects, - bool* needs_full_paint, - bool* painted_synchronously); + bool* needs_full_paint); // Returns a matching backing store for the host. // Returns NULL if we fail to find one. diff --git a/chrome/browser/renderer_host/backing_store_proxy.cc b/chrome/browser/renderer_host/backing_store_proxy.cc deleted file mode 100644 index 3d2fd8e..0000000 --- a/chrome/browser/renderer_host/backing_store_proxy.cc +++ /dev/null @@ -1,101 +0,0 @@ -// 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/renderer_host/backing_store_proxy.h" - -#include "build/build_config.h" -#include "chrome/browser/gpu_process_host_ui_shim.h" -#include "chrome/browser/renderer_host/render_process_host.h" -#include "chrome/browser/renderer_host/render_widget_host.h" -#include "chrome/common/gpu_messages.h" -#include "chrome/common/render_messages.h" -#include "gfx/rect.h" - -#if defined(OS_WIN) -#include <windows.h> -#endif - -BackingStoreProxy::BackingStoreProxy(RenderWidgetHost* widget, - const gfx::Size& size, - GpuProcessHostUIShim* process_shim, - int32 routing_id) - : BackingStore(widget, size), - process_shim_(process_shim), - routing_id_(routing_id), - waiting_for_paint_ack_(false) { - process_shim_->AddRoute(routing_id_, this); -} - -BackingStoreProxy::~BackingStoreProxy() { - process_shim_->RemoveRoute(routing_id_); -} - -void BackingStoreProxy::PaintToBackingStore( - RenderProcessHost* process, - TransportDIB::Id bitmap, - const gfx::Rect& bitmap_rect, - const std::vector<gfx::Rect>& copy_rects, - bool* painted_synchronously) { - DCHECK(!waiting_for_paint_ack_); - - base::ProcessId process_id; -#if defined(OS_WIN) - process_id = ::GetProcessId(process->GetHandle()); -#elif defined(OS_POSIX) - process_id = process->GetHandle(); -#endif - - if (process_shim_->Send(new GpuMsg_PaintToBackingStore( - routing_id_, process_id, bitmap, bitmap_rect, copy_rects))) { - // Message sent successfully, so the caller can not destroy the - // TransportDIB. OnDonePaintingToBackingStore will free it later. - *painted_synchronously = false; - waiting_for_paint_ack_ = true; - } else { - // On error, we're done with the TransportDIB and the caller can free it. - *painted_synchronously = true; - } -} - -bool BackingStoreProxy::CopyFromBackingStore(const gfx::Rect& rect, - skia::PlatformCanvas* output) { - NOTIMPLEMENTED(); - return false; -} - -void BackingStoreProxy::ScrollBackingStore(int dx, int dy, - const gfx::Rect& clip_rect, - const gfx::Size& view_size) { - process_shim_->Send(new GpuMsg_ScrollBackingStore(routing_id_, dx, dy, - clip_rect, view_size)); -} - -void BackingStoreProxy::OnMessageReceived(const IPC::Message& msg) { - IPC_BEGIN_MESSAGE_MAP(BackingStoreProxy, msg) - IPC_MESSAGE_HANDLER(GpuHostMsg_PaintToBackingStore_ACK, - OnPaintToBackingStoreACK) - IPC_END_MESSAGE_MAP_EX() -} - -void BackingStoreProxy::OnChannelConnected(int32 peer_pid) { -} - -void BackingStoreProxy::OnChannelError() { - if (waiting_for_paint_ack_) { - // If the GPU process dies while painting, the renderer will be waiting for - // the paint ACK before painting any more. Since no ack is coming, we - // manually declare that we're done with the transport DIB here so it can - // continue. - OnPaintToBackingStoreACK(); - } - - // TODO(brettw): does this mean we aren't getting any more messages and we - // should delete outselves? -} - -void BackingStoreProxy::OnPaintToBackingStoreACK() { - DCHECK(waiting_for_paint_ack_); - render_widget_host()->DonePaintingToBackingStore(); - waiting_for_paint_ack_ = false; -} diff --git a/chrome/browser/renderer_host/backing_store_proxy.h b/chrome/browser/renderer_host/backing_store_proxy.h deleted file mode 100644 index 610f7ef..0000000 --- a/chrome/browser/renderer_host/backing_store_proxy.h +++ /dev/null @@ -1,54 +0,0 @@ -// 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_RENDERER_HOST_BACKING_STORE_PROXY_H_ -#define CHROME_BROWSER_RENDERER_HOST_BACKING_STORE_PROXY_H_ -#pragma once - -#include "base/basictypes.h" -#include "chrome/browser/renderer_host/backing_store.h" -#include "ipc/ipc_channel.h" - -class GpuProcessHostUIShim; - -class BackingStoreProxy : public BackingStore, - public IPC::Channel::Listener { - public: - BackingStoreProxy(RenderWidgetHost* widget, const gfx::Size& size, - GpuProcessHostUIShim* process_shim, int32 routing_id); - virtual ~BackingStoreProxy(); - - // BackingStore implementation. - virtual void PaintToBackingStore(RenderProcessHost* process, - TransportDIB::Id bitmap, - const gfx::Rect& bitmap_rect, - const std::vector<gfx::Rect>& copy_rects, - bool* painted_synchronously); - virtual bool CopyFromBackingStore(const gfx::Rect& rect, - skia::PlatformCanvas* output); - virtual void ScrollBackingStore(int dx, int dy, - const gfx::Rect& clip_rect, - const gfx::Size& view_size); - - // IPC::Channel::Listener implementation. - virtual void OnMessageReceived(const IPC::Message& message); - virtual void OnChannelConnected(int32 peer_pid); - virtual void OnChannelError(); - - private: - // Message handlers. - void OnPaintToBackingStoreACK(); - - GpuProcessHostUIShim* process_shim_; - int32 routing_id_; - - // Set to true when we're waiting for the GPU process to do a paint and send - // back a "done" message. In this case, the renderer will be waiting for our - // message that we're done using the backing store. - bool waiting_for_paint_ack_; - - DISALLOW_COPY_AND_ASSIGN(BackingStoreProxy); -}; - -#endif // CHROME_BROWSER_RENDERER_HOST_BACKING_STORE_PROXY_H_ diff --git a/chrome/browser/renderer_host/backing_store_win.cc b/chrome/browser/renderer_host/backing_store_win.cc index 94d9cbf..2013104 100644 --- a/chrome/browser/renderer_host/backing_store_win.cc +++ b/chrome/browser/renderer_host/backing_store_win.cc @@ -116,12 +116,7 @@ void BackingStoreWin::PaintToBackingStore( RenderProcessHost* process, TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, - const std::vector<gfx::Rect>& copy_rects, - bool* painted_synchronously) { - // Our paints are always synchronous and the TransportDIB can be freed when - // we're done (even on error). - *painted_synchronously = true; - + const std::vector<gfx::Rect>& copy_rects) { if (!backing_store_dib_) { backing_store_dib_ = CreateDIB(hdc_, size().width(), size().height(), color_depth_); diff --git a/chrome/browser/renderer_host/backing_store_win.h b/chrome/browser/renderer_host/backing_store_win.h index 221e0aa..ce4c419 100644 --- a/chrome/browser/renderer_host/backing_store_win.h +++ b/chrome/browser/renderer_host/backing_store_win.h @@ -26,8 +26,7 @@ class BackingStoreWin : public BackingStore { virtual void PaintToBackingStore(RenderProcessHost* process, TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, - const std::vector<gfx::Rect>& copy_rects, - bool* painted_synchronously); + const std::vector<gfx::Rect>& copy_rects); virtual bool CopyFromBackingStore(const gfx::Rect& rect, skia::PlatformCanvas* output); virtual void ScrollBackingStore(int dx, int dy, diff --git a/chrome/browser/renderer_host/backing_store_x.cc b/chrome/browser/renderer_host/backing_store_x.cc index a79a8fc..9fc7e5c 100644 --- a/chrome/browser/renderer_host/backing_store_x.cc +++ b/chrome/browser/renderer_host/backing_store_x.cc @@ -160,12 +160,7 @@ void BackingStoreX::PaintToBackingStore( RenderProcessHost* process, TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, - const std::vector<gfx::Rect>& copy_rects, - bool* painted_synchronously) { - // Our paints are always synchronous and the caller can free the TransportDIB - // when we're done, even on error. - *painted_synchronously = true; - + const std::vector<gfx::Rect>& copy_rects) { if (!display_) return; diff --git a/chrome/browser/renderer_host/backing_store_x.h b/chrome/browser/renderer_host/backing_store_x.h index 283c19f..e6a73a7 100644 --- a/chrome/browser/renderer_host/backing_store_x.h +++ b/chrome/browser/renderer_host/backing_store_x.h @@ -53,8 +53,7 @@ class BackingStoreX : public BackingStore { RenderProcessHost* process, TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, - const std::vector<gfx::Rect>& copy_rects, - bool* painted_synchronously); + const std::vector<gfx::Rect>& copy_rects); virtual bool CopyFromBackingStore(const gfx::Rect& rect, skia::PlatformCanvas* output); virtual void ScrollBackingStore(int dx, int dy, diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index b7af07f..af37c6b 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -47,6 +47,7 @@ #include "chrome/browser/renderer_host/resource_message_filter.h" #include "chrome/browser/renderer_host/web_cache_manager.h" #include "chrome/browser/spellcheck_host.h" +#include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/visitedlink_master.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" @@ -258,7 +259,7 @@ BrowserRenderProcessHost::BrowserRenderProcessHost(Profile* profile) } BrowserRenderProcessHost::~BrowserRenderProcessHost() { - LOG_IF(INFO, g_log_bug53991) << "~BrowserRenderProcessHost: " << this; + VLOG_IF(1, g_log_bug53991) << "~BrowserRenderProcessHost: " << this; WebCacheManager::GetInstance()->Remove(id()); ChildProcessSecurityPolicy::GetInstance()->Remove(id()); @@ -296,14 +297,14 @@ bool BrowserRenderProcessHost::Init( // Construct the AudioRendererHost with the IO thread. audio_renderer_host_ = new AudioRendererHost(); - scoped_refptr<ResourceMessageFilter> resource_message_filter = + scoped_refptr<ResourceMessageFilter> resource_message_filter( new ResourceMessageFilter(g_browser_process->resource_dispatcher_host(), id(), audio_renderer_host_.get(), PluginService::GetInstance(), g_browser_process->print_job_manager(), profile(), - widget_helper_); + widget_helper_)); CommandLine::StringType renderer_prefix; #if defined(OS_POSIX) @@ -334,8 +335,8 @@ bool BrowserRenderProcessHost::Init( // be doing. channel_->set_sync_messages_with_no_timeout_allowed(false); - scoped_refptr<PepperFileMessageFilter> pepper_file_message_filter = - new PepperFileMessageFilter(id(), profile()); + scoped_refptr<PepperFileMessageFilter> pepper_file_message_filter( + new PepperFileMessageFilter(id(), profile())); channel_->AddFilter(pepper_file_message_filter); if (run_renderer_in_process()) { @@ -578,7 +579,6 @@ void BrowserRenderProcessHost::PropagateBrowserCommandLineToRenderer( switches::kInternalNaCl, switches::kInternalPepper, switches::kRegisterPepperPlugins, - switches::kDisableByteRangeSupport, switches::kDisableDatabases, switches::kDisableDesktopNotifications, switches::kDisableWebSockets, @@ -594,7 +594,6 @@ void BrowserRenderProcessHost::PropagateBrowserCommandLineToRenderer( switches::kEnableOpenMax, switches::kVideoThreads, switches::kEnableVideoFullscreen, - switches::kEnableVideoLayering, switches::kEnableVideoLogging, switches::kEnableTouch, // We propagate the Chrome Frame command line here as well in case the @@ -607,6 +606,9 @@ void BrowserRenderProcessHost::PropagateBrowserCommandLineToRenderer( switches::kDisableExperimentalWebGL, switches::kDisableGLSLTranslator, switches::kInProcessWebGL, + // This flag needs to be propagated to the renderer process for + // --in-process-webgl. + switches::kUseGL, switches::kDisableAcceleratedCompositing, #if defined(OS_MACOSX) // Allow this to be set when invoking the browser and relayed along. @@ -708,7 +710,7 @@ void BrowserRenderProcessHost::SendExtensionInfo() { return; ViewMsg_ExtensionsUpdated_Params params; for (size_t i = 0; i < service->extensions()->size(); ++i) { - Extension* extension = service->extensions()->at(i); + const Extension* extension = service->extensions()->at(i); ViewMsg_ExtensionRendererInfo info; info.id = extension->id(); info.web_extent = extension->web_extent(); @@ -869,6 +871,8 @@ void BrowserRenderProcessHost::OnMessageReceived(const IPC::Message& msg) { OnExtensionRemoveListener) IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionCloseChannel, OnExtensionCloseChannel) + IPC_MESSAGE_HANDLER(ViewHostMsg_UserMetricsRecordAction, + OnUserMetricsRecordAction) IPC_MESSAGE_HANDLER(ViewHostMsg_SpellChecker_RequestDictionary, OnSpellCheckerRequestDictionary) IPC_MESSAGE_UNHANDLED_ERROR() @@ -1089,6 +1093,11 @@ void BrowserRenderProcessHost::OnExtensionCloseChannel(int port_id) { } } +void BrowserRenderProcessHost::OnUserMetricsRecordAction( + const std::string& action) { + UserMetrics::RecordComputedAction(action, profile()); +} + void BrowserRenderProcessHost::OnSpellCheckerRequestDictionary() { if (profile()->GetSpellCheckHost()) { // The spellchecker initialization already started and finished; just send diff --git a/chrome/browser/renderer_host/browser_render_process_host.h b/chrome/browser/renderer_host/browser_render_process_host.h index 639ba32..841b6ae 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.h +++ b/chrome/browser/renderer_host/browser_render_process_host.h @@ -113,6 +113,7 @@ class BrowserRenderProcessHost : public RenderProcessHost, void OnExtensionRemoveListener(const std::string& extension_id, const std::string& event_name); void OnExtensionCloseChannel(int port_id); + void OnUserMetricsRecordAction(const std::string& action); // Initialize support for visited links. Send the renderer process its initial // set of visited links. diff --git a/chrome/browser/renderer_host/buffered_resource_handler.cc b/chrome/browser/renderer_host/buffered_resource_handler.cc index a19d3c3..0a141b0 100644 --- a/chrome/browser/renderer_host/buffered_resource_handler.cc +++ b/chrome/browser/renderer_host/buffered_resource_handler.cc @@ -149,6 +149,8 @@ bool BufferedResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { return real_handler_->OnReadCompleted(request_id, bytes_read); } +BufferedResourceHandler::~BufferedResourceHandler() {} + bool BufferedResourceHandler::DelayResponse() { std::string mime_type; request_->GetMimeType(&mime_type); @@ -172,7 +174,7 @@ bool BufferedResourceHandler::DelayResponse() { // is. That means we need to delay sending the ResponseStarted message // over the IPC channel. sniff_content_ = true; - LOG(INFO) << "To buffer: " << request_->url().spec(); + VLOG(1) << "To buffer: " << request_->url().spec(); return true; } diff --git a/chrome/browser/renderer_host/buffered_resource_handler.h b/chrome/browser/renderer_host/buffered_resource_handler.h index 26bd7f8..69e64c5 100644 --- a/chrome/browser/renderer_host/buffered_resource_handler.h +++ b/chrome/browser/renderer_host/buffered_resource_handler.h @@ -22,21 +22,21 @@ class BufferedResourceHandler : public ResourceHandler { URLRequest* request); // ResourceHandler implementation: - bool OnUploadProgress(int request_id, uint64 position, uint64 size); - bool OnRequestRedirected(int request_id, const GURL& new_url, - ResourceResponse* response, bool* defer); - bool OnResponseStarted(int request_id, ResourceResponse* response); - bool OnWillStart(int request_id, const GURL& url, bool* defer); - bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, - int min_size); - bool OnReadCompleted(int request_id, int* bytes_read); - bool OnResponseCompleted(int request_id, - const URLRequestStatus& status, - const std::string& security_info); - void OnRequestClosed(); + virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size); + virtual bool OnRequestRedirected(int request_id, const GURL& new_url, + ResourceResponse* response, bool* defer); + virtual bool OnResponseStarted(int request_id, ResourceResponse* response); + virtual bool OnWillStart(int request_id, const GURL& url, bool* defer); + virtual bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, + int min_size); + virtual bool OnReadCompleted(int request_id, int* bytes_read); + virtual bool OnResponseCompleted(int request_id, + const URLRequestStatus& status, + const std::string& security_info); + virtual void OnRequestClosed(); private: - ~BufferedResourceHandler() {} + virtual ~BufferedResourceHandler(); // Returns true if we should delay OnResponseStarted forwarding. bool DelayResponse(); diff --git a/chrome/browser/renderer_host/cross_site_resource_handler.cc b/chrome/browser/renderer_host/cross_site_resource_handler.cc index 3198e76..ddab0e7 100644 --- a/chrome/browser/renderer_host/cross_site_resource_handler.cc +++ b/chrome/browser/renderer_host/cross_site_resource_handler.cc @@ -175,6 +175,8 @@ void CrossSiteResourceHandler::ResumeResponse() { } } +CrossSiteResourceHandler::~CrossSiteResourceHandler() {} + // Prepare to render the cross-site response in a new RenderViewHost, by // telling the old RenderViewHost to run its onunload handler. void CrossSiteResourceHandler::StartCrossSiteTransition( diff --git a/chrome/browser/renderer_host/cross_site_resource_handler.h b/chrome/browser/renderer_host/cross_site_resource_handler.h index aa731ca..33c1109 100644 --- a/chrome/browser/renderer_host/cross_site_resource_handler.h +++ b/chrome/browser/renderer_host/cross_site_resource_handler.h @@ -25,26 +25,26 @@ class CrossSiteResourceHandler : public ResourceHandler { ResourceDispatcherHost* resource_dispatcher_host); // ResourceHandler implementation: - bool OnUploadProgress(int request_id, uint64 position, uint64 size); - bool OnRequestRedirected(int request_id, const GURL& new_url, - ResourceResponse* response, bool* defer); - bool OnResponseStarted(int request_id, - ResourceResponse* response); - bool OnWillStart(int request_id, const GURL& url, bool* defer); - bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, - int min_size); - bool OnReadCompleted(int request_id, int* bytes_read); - bool OnResponseCompleted(int request_id, - const URLRequestStatus& status, - const std::string& security_info); - void OnRequestClosed(); + virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size); + virtual bool OnRequestRedirected(int request_id, const GURL& new_url, + ResourceResponse* response, bool* defer); + virtual bool OnResponseStarted(int request_id, + ResourceResponse* response); + virtual bool OnWillStart(int request_id, const GURL& url, bool* defer); + virtual bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, + int min_size); + virtual bool OnReadCompleted(int request_id, int* bytes_read); + virtual bool OnResponseCompleted(int request_id, + const URLRequestStatus& status, + const std::string& security_info); + virtual void OnRequestClosed(); // We can now send the response to the new renderer, which will cause // TabContents to swap in the new renderer and destroy the old one. void ResumeResponse(); private: - ~CrossSiteResourceHandler() {} + virtual ~CrossSiteResourceHandler(); // Prepare to render the cross-site response in a new RenderViewHost, by // telling the old RenderViewHost to run its onunload handler. diff --git a/chrome/browser/renderer_host/database_dispatcher_host.cc b/chrome/browser/renderer_host/database_dispatcher_host.cc index 276ba75..11c5125 100644 --- a/chrome/browser/renderer_host/database_dispatcher_host.cc +++ b/chrome/browser/renderer_host/database_dispatcher_host.cc @@ -124,6 +124,8 @@ void DatabaseDispatcherHost::Send(IPC::Message* message) { delete message; } +DatabaseDispatcherHost::~DatabaseDispatcherHost() {} + void DatabaseDispatcherHost::OnDatabaseOpenFile(const string16& vfs_file_name, int desired_flags, IPC::Message* reply_msg) { diff --git a/chrome/browser/renderer_host/database_dispatcher_host.h b/chrome/browser/renderer_host/database_dispatcher_host.h index 58b9b6e..000c063 100644 --- a/chrome/browser/renderer_host/database_dispatcher_host.h +++ b/chrome/browser/renderer_host/database_dispatcher_host.h @@ -74,6 +74,9 @@ class DatabaseDispatcherHost void Send(IPC::Message* message); private: + friend class base::RefCountedThreadSafe<DatabaseDispatcherHost>; + virtual ~DatabaseDispatcherHost(); + class PromptDelegate; void AddObserver(); diff --git a/chrome/browser/renderer_host/gpu_view_host.cc b/chrome/browser/renderer_host/gpu_view_host.cc deleted file mode 100644 index fa8c19c..0000000 --- a/chrome/browser/renderer_host/gpu_view_host.cc +++ /dev/null @@ -1,50 +0,0 @@ -// 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/renderer_host/gpu_view_host.h" - -#include "chrome/browser/gpu_process_host_ui_shim.h" -#include "chrome/browser/renderer_host/backing_store_proxy.h" -#include "chrome/browser/renderer_host/video_layer_proxy.h" -#include "chrome/common/gpu_messages.h" - -GpuViewHost::GpuViewHost(RenderWidgetHost* widget, GpuNativeWindowHandle parent) - : widget_(widget), - process_shim_(GpuProcessHostUIShim::Get()), - routing_id_(0) { - if (!process_shim_) { - // TODO(brettw) handle error. - return; - } - routing_id_ = process_shim_->NewRenderWidgetHostView(parent); -} - -GpuViewHost::~GpuViewHost() { -} - -BackingStore* GpuViewHost::CreateBackingStore(const gfx::Size& size) { - int32 backing_store_routing_id = process_shim_->GetNextRoutingId(); - BackingStoreProxy* result = - new BackingStoreProxy(widget_, size, - process_shim_, backing_store_routing_id); - process_shim_->Send(new GpuMsg_NewBackingStore(routing_id_, - backing_store_routing_id, - size)); - return result; -} - -VideoLayer* GpuViewHost::CreateVideoLayer(const gfx::Size& size) { - int32 video_layer_routing_id = process_shim_->GetNextRoutingId(); - VideoLayerProxy* result = - new VideoLayerProxy(widget_, size, - process_shim_, video_layer_routing_id); - process_shim_->Send(new GpuMsg_NewVideoLayer(routing_id_, - video_layer_routing_id, - size)); - return result; -} - -void GpuViewHost::OnWindowPainted() { - process_shim_->Send(new GpuMsg_WindowPainted(routing_id_)); -} diff --git a/chrome/browser/renderer_host/gpu_view_host.h b/chrome/browser/renderer_host/gpu_view_host.h deleted file mode 100644 index c37409c..0000000 --- a/chrome/browser/renderer_host/gpu_view_host.h +++ /dev/null @@ -1,50 +0,0 @@ -// 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_RENDERER_HOST_GPU_VIEW_HOST_H_ -#define CHROME_BROWSER_RENDERER_HOST_GPU_VIEW_HOST_H_ -#pragma once - -#include "base/basictypes.h" -#include "chrome/common/gpu_native_window_handle.h" - -class BackingStore; -class GpuProcessHostUIShim; -class RenderWidgetHost; -class VideoLayer; - -namespace gfx { -class Size; -} - -// A proxy for the GPU process' window for rendering pages. -class GpuViewHost { - public: - GpuViewHost(RenderWidgetHost* widget, GpuNativeWindowHandle parent); - ~GpuViewHost(); - - // Creates a new backing store in the GPU process and returns ownership of - // the new pointer to the caller. - BackingStore* CreateBackingStore(const gfx::Size& size); - - // Creates a new video layer in the GPU process and returns ownership of the - // new pointer to the caller. - VideoLayer* CreateVideoLayer(const gfx::Size& size); - - // Notification that the RenderWidgetHost has been asked to paint the window. - // Depending on the backing store, the GPU backing store may have to repaint - // at this time. On Linux this is needed because the GPU process paints - // directly into the RWH's X window. - void OnWindowPainted(); - - private: - RenderWidgetHost* widget_; - - GpuProcessHostUIShim* process_shim_; - int32 routing_id_; - - DISALLOW_COPY_AND_ASSIGN(GpuViewHost); -}; - -#endif // CHROME_BROWSER_RENDERER_HOST_GPU_VIEW_HOST_H_ diff --git a/chrome/browser/renderer_host/gtk_im_context_wrapper.cc b/chrome/browser/renderer_host/gtk_im_context_wrapper.cc index 1c07795..a766f3c 100644 --- a/chrome/browser/renderer_host/gtk_im_context_wrapper.cc +++ b/chrome/browser/renderer_host/gtk_im_context_wrapper.cc @@ -10,12 +10,11 @@ #include <algorithm> #include "app/l10n_util.h" -#include "base/gtk_util.h" #include "base/logging.h" #include "base/string_util.h" #include "base/third_party/icu/icu_utf.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/gtk/gtk_util.h" #if !defined(TOOLKIT_VIEWS) #include "chrome/browser/gtk/menu_gtk.h" @@ -24,6 +23,7 @@ #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" #include "chrome/common/native_web_keyboard_event.h" #include "chrome/common/render_messages.h" +#include "gfx/gtk_util.h" #include "gfx/rect.h" #include "grit/generated_resources.h" #include "third_party/skia/include/core/SkColor.h" @@ -266,7 +266,7 @@ void GtkIMContextWrapper::AppendInputMethodsContextMenu(MenuGtk* menu) { if (!show_input_method_menu) return; - std::string label = gtk_util::ConvertAcceleratorsFromWindowsStyle( + std::string label = gfx::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8(IDS_CONTENT_CONTEXT_INPUT_METHODS_MENU)); GtkWidget* menuitem = gtk_menu_item_new_with_mnemonic(label.c_str()); GtkWidget* submenu = gtk_menu_new(); diff --git a/chrome/browser/renderer_host/gtk_key_bindings_handler_unittest.cc b/chrome/browser/renderer_host/gtk_key_bindings_handler_unittest.cc index 19f0806..1ac020c 100644 --- a/chrome/browser/renderer_host/gtk_key_bindings_handler_unittest.cc +++ b/chrome/browser/renderer_host/gtk_key_bindings_handler_unittest.cc @@ -85,7 +85,8 @@ class GtkKeyBindingsHandlerTest : public testing::Test { GtkKeyBindingsHandler* handler_; }; -TEST_F(GtkKeyBindingsHandlerTest, MoveCursor) { +// Does not work in a chroot. See bug 60363. +TEST_F(GtkKeyBindingsHandlerTest, FLAKY_MoveCursor) { static const EditCommand kEditCommands[] = { // "move-cursor" (logical-positions, -2, 0) { "MoveBackward", "" }, @@ -127,7 +128,8 @@ TEST_F(GtkKeyBindingsHandlerTest, MoveCursor) { kEditCommands, arraysize(kEditCommands)); } -TEST_F(GtkKeyBindingsHandlerTest, DeleteFromCursor) { +// Does not work in a chroot. See bug 60363. +TEST_F(GtkKeyBindingsHandlerTest, FLAKY_DeleteFromCursor) { static const EditCommand kEditCommands[] = { // "delete-from-cursor" (chars, -2) { "DeleteBackward", "" }, @@ -171,7 +173,8 @@ TEST_F(GtkKeyBindingsHandlerTest, DeleteFromCursor) { kEditCommands, arraysize(kEditCommands)); } -TEST_F(GtkKeyBindingsHandlerTest, OtherActions) { +// Does not work in a chroot. See bug 60363. +TEST_F(GtkKeyBindingsHandlerTest, FLAKY_OtherActions) { static const EditCommand kBackspace[] = { { "DeleteBackward", "" } }; diff --git a/chrome/browser/renderer_host/offline_resource_handler.cc b/chrome/browser/renderer_host/offline_resource_handler.cc index 68a036d..ba94bee 100644 --- a/chrome/browser/renderer_host/offline_resource_handler.cc +++ b/chrome/browser/renderer_host/offline_resource_handler.cc @@ -68,7 +68,7 @@ bool OfflineResourceHandler::OnWillStart(int request_id, if (ShouldShowOfflinePage(url)) { deferred_request_id_ = request_id; deferred_url_ = url; - DLOG(INFO) << "WillStart: this=" << this << ", request id=" << request_id; + DVLOG(1) << "WillStart: this=" << this << ", request id=" << request_id; AddRef(); // Balanced with OnBlockingPageComplete BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -148,8 +148,7 @@ void OfflineResourceHandler::Resume() { DCHECK_NE(request_id, -1); bool defer = false; - DLOG(INFO) << "Resume load: this=" << this - << ", request id=" << request_id; + DVLOG(1) << "Resume load: this=" << this << ", request id=" << request_id; next_handler_->OnWillStart(request_id, url, &defer); if (!defer) rdh_->StartDeferredRequest(process_host_id_, request_id); diff --git a/chrome/browser/renderer_host/pepper_file_message_filter.cc b/chrome/browser/renderer_host/pepper_file_message_filter.cc index e7b331b..cbd09af 100644 --- a/chrome/browser/renderer_host/pepper_file_message_filter.cc +++ b/chrome/browser/renderer_host/pepper_file_message_filter.cc @@ -104,7 +104,7 @@ void PepperFileMessageFilter::OnMessageReceivedFileThread( } } -void PepperFileMessageFilter::OnDestruct() { +void PepperFileMessageFilter::OnDestruct() const { BrowserThread::DeleteOnIOThread::Destruct(this); } diff --git a/chrome/browser/renderer_host/pepper_file_message_filter.h b/chrome/browser/renderer_host/pepper_file_message_filter.h index 37e57f6..003d258 100644 --- a/chrome/browser/renderer_host/pepper_file_message_filter.h +++ b/chrome/browser/renderer_host/pepper_file_message_filter.h @@ -37,7 +37,7 @@ class PepperFileMessageFilter : public IPC::ChannelProxy::MessageFilter { virtual void OnChannelError(); virtual void OnChannelClosing(); virtual bool OnMessageReceived(const IPC::Message& message); - virtual void OnDestruct(); + virtual void OnDestruct() const; // Called from the FILE thread. void Send(IPC::Message* message); diff --git a/chrome/browser/renderer_host/render_process_host.cc b/chrome/browser/renderer_host/render_process_host.cc index b224b1f..a2c328c 100644 --- a/chrome/browser/renderer_host/render_process_host.cc +++ b/chrome/browser/renderer_host/render_process_host.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -111,14 +111,13 @@ RenderProcessHost::~RenderProcessHost() { void RenderProcessHost::Attach(IPC::Channel::Listener* listener, int routing_id) { - LOG_IF(INFO, g_log_bug53991) << - "AddListener: (" << this << "): " << routing_id; + VLOG_IF(1, g_log_bug53991) << "AddListener: (" << this << "): " << routing_id; listeners_.AddWithID(listener, routing_id); } void RenderProcessHost::Release(int listener_id) { - LOG_IF(INFO, g_log_bug53991) << - "RemListener: (" << this << "): " << listener_id; + VLOG_IF(1, g_log_bug53991) << "RemListener: (" << this << "): " + << listener_id; DCHECK(listeners_.Lookup(listener_id) != NULL); listeners_.Remove(listener_id); diff --git a/chrome/browser/renderer_host/render_sandbox_host_linux.cc b/chrome/browser/renderer_host/render_sandbox_host_linux.cc index 5292d95..0253f9e 100644 --- a/chrome/browser/renderer_host/render_sandbox_host_linux.cc +++ b/chrome/browser/renderer_host/render_sandbox_host_linux.cc @@ -360,7 +360,7 @@ class SandboxIPCProcess { return; int shm_fd = -1; base::SharedMemory shm; - if (shm.Create("", false, false, shm_size)) + if (shm.CreateAnonymous(shm_size)) shm_fd = shm.handle().fd; Pickle reply; SendRendererReply(fds, reply, shm_fd); @@ -540,6 +540,9 @@ class SandboxIPCProcess { FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lv")); FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lt")); break; + // TODO(jungshik): Would we be better off mapping Big5 to zh-tw + // and GB2312 to zh-cn? Fontconfig has 4 separate orthography + // files (zh-{cn,tw,hk,mo}. case NPCharsetChineseBIG5: case NPCharsetGB2312: FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh")); @@ -565,7 +568,7 @@ class SandboxIPCProcess { break; case NPCharsetShiftJIS: // Japanese - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("jp")); + FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ja")); break; case NPCharsetTurkish: FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("tr")); diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc index 3781bed..134274e 100644 --- a/chrome/browser/renderer_host/render_view_host.cc +++ b/chrome/browser/renderer_host/render_view_host.cc @@ -23,7 +23,6 @@ #include "chrome/browser/dom_operation_notification_details.h" #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/in_process_webkit/session_storage_namespace.h" -#include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/net/predictor_api.h" #include "chrome/browser/notifications/desktop_notification_service.h" #include "chrome/browser/profile.h" @@ -89,7 +88,7 @@ void FilterURL(ChildProcessSecurityPolicy* policy, int renderer_id, GURL* url) { // If this renderer is not permitted to request this URL, we invalidate the // URL. This prevents us from storing the blocked URL and becoming confused // later. - LOG(INFO) << "Blocked URL " << url->spec(); + VLOG(1) << "Blocked URL " << url->spec(); *url = GURL(); } } @@ -386,6 +385,10 @@ bool RenderViewHost::PrintPages() { return Send(new ViewMsg_PrintPages(routing_id())); } +bool RenderViewHost::PrintPreview() { + return Send(new ViewMsg_PrintPreview(routing_id())); +} + void RenderViewHost::PrintingDone(int document_cookie, bool success) { Send(new ViewMsg_PrintingDone(routing_id(), document_cookie, success)); } @@ -433,6 +436,10 @@ void RenderViewHost::Zoom(PageZoom::Function function) { Send(new ViewMsg_Zoom(routing_id(), function)); } +void RenderViewHost::SetZoomLevel(double zoom_level) { + Send(new ViewMsg_SetZoomLevel(routing_id(), zoom_level)); +} + void RenderViewHost::SetPageEncoding(const std::string& encoding_name) { Send(new ViewMsg_SetPageEncoding(routing_id(), encoding_name)); } @@ -796,6 +803,8 @@ void RenderViewHost::OnMessageReceived(const IPC::Message& msg) { OnMsgForwardMessageToExternalHost) IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentLoadedInFrame, OnMsgDocumentLoadedInFrame) + IPC_MESSAGE_HANDLER(ViewHostMsg_DidFinishLoad, + OnMsgDidFinishLoad) IPC_MESSAGE_HANDLER(ViewHostMsg_GoToEntryAtOffset, OnMsgGoToEntryAtOffset) IPC_MESSAGE_HANDLER(ViewHostMsg_SetTooltipText, OnMsgSetTooltipText) @@ -833,8 +842,6 @@ void RenderViewHost::OnMessageReceived(const IPC::Message& msg) { OnRequestUndockDevToolsWindow); IPC_MESSAGE_HANDLER(ViewHostMsg_DevToolsRuntimePropertyChanged, OnDevToolsRuntimePropertyChanged); - IPC_MESSAGE_HANDLER(ViewHostMsg_UserMetricsRecordAction, - OnUserMetricsRecordAction) IPC_MESSAGE_HANDLER(ViewHostMsg_MissingPluginStatus, OnMissingPluginStatus); IPC_MESSAGE_HANDLER(ViewHostMsg_NonSandboxedPluginBlocked, OnNonSandboxedPluginBlocked); @@ -884,12 +891,24 @@ void RenderViewHost::OnMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_HANDLER(ViewHostMsg_WebDatabaseAccessed, OnWebDatabaseAccessed) IPC_MESSAGE_HANDLER(ViewHostMsg_FocusedNodeChanged, OnMsgFocusedNodeChanged) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateZoomLimits, OnUpdateZoomLimits) - IPC_MESSAGE_HANDLER(ViewHostMsg_SetSuggestResult, OnSetSuggestResult) + IPC_MESSAGE_HANDLER(ViewHostMsg_SetSuggestions, OnSetSuggestions) + IPC_MESSAGE_HANDLER(ViewHostMsg_InstantSupportDetermined, + OnInstantSupportDetermined) IPC_MESSAGE_HANDLER(ViewHostMsg_DetectedPhishingSite, OnDetectedPhishingSite) IPC_MESSAGE_HANDLER(ViewHostMsg_ScriptEvalResponse, OnScriptEvalResponse) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateContentRestrictions, OnUpdateContentRestrictions) +#if defined(OS_MACOSX) + IPC_MESSAGE_HANDLER(ViewHostMsg_ShowPopup, OnMsgShowPopup) +#endif +#if defined(OS_MACOSX) || defined(OS_WIN) + IPC_MESSAGE_HANDLER(ViewHostMsg_PageReadyForPreview, + OnPageReadyForPreview) +#else + IPC_MESSAGE_HANDLER(ViewHostMsg_PagesReadyForPreview, + OnPagesReadyForPreview) +#endif // Have the super handle all other messages. IPC_MESSAGE_UNHANDLED(RenderWidgetHost::OnMessageReceived(msg)) IPC_END_MESSAGE_MAP_EX() @@ -1192,11 +1211,11 @@ void RenderViewHost::OnMsgDidFailProvisionalLoadWithError( int error_code, const GURL& url, bool showing_repost_interstitial) { - LOG(INFO) << "Failed Provisional Load: " << url.possibly_invalid_spec() - << ", error_code: " << error_code - << " is_main_frame: " << is_main_frame - << " showing_repost_interstitial: " << showing_repost_interstitial - << " frame_id: " << frame_id; + VLOG(1) << "Failed Provisional Load: " << url.possibly_invalid_spec() + << ", error_code: " << error_code + << " is_main_frame: " << is_main_frame + << " showing_repost_interstitial: " << showing_repost_interstitial + << " frame_id: " << frame_id; GURL validated_url(url); FilterURL(ChildProcessSecurityPolicy::GetInstance(), process()->id(), &validated_url); @@ -1349,11 +1368,18 @@ void RenderViewHost::OnMsgForwardMessageToExternalHost( delegate_->ProcessExternalHostMessage(message, origin, target); } -void RenderViewHost::OnMsgDocumentLoadedInFrame() { +void RenderViewHost::OnMsgDocumentLoadedInFrame(long long frame_id) { RenderViewHostDelegate::Resource* resource_delegate = delegate_->GetResourceDelegate(); if (resource_delegate) - resource_delegate->DocumentLoadedInFrame(); + resource_delegate->DocumentLoadedInFrame(frame_id); +} + +void RenderViewHost::OnMsgDidFinishLoad(long long frame_id) { + RenderViewHostDelegate::Resource* resource_delegate = + delegate_->GetResourceDelegate(); + if (resource_delegate) + resource_delegate->DidFinishLoad(frame_id); } void RenderViewHost::DisassociateFromPopupCount() { @@ -1567,10 +1593,6 @@ void RenderViewHost::OnDevToolsRuntimePropertyChanged( RuntimePropertyChanged(this, name, value); } -void RenderViewHost::OnUserMetricsRecordAction(const std::string& action) { - UserMetrics::RecordComputedAction(action, process()->profile()); -} - bool RenderViewHost::PreHandleKeyboardEvent( const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) { RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); @@ -1831,8 +1853,22 @@ void RenderViewHost::NotifyRendererResponsive() { delegate_->RendererResponsive(this); } -void RenderViewHost::OnMsgFocusedNodeChanged() { +void RenderViewHost::OnMsgFocusedNodeChanged(bool is_editable_node) { delegate_->FocusedNodeChanged(); + +#if defined(TOUCH_UI) + if (is_editable_node) { + // Need to summon on-screen keyboard + // TODO(bryeung): implement this + + // The currently focused element can be placed out of the view as the screen + // is now shared by the keyboard. Hence, we tell the renderer to scroll + // until the focused element comes in view. + Send(new ViewMsg_ScrollFocusedEditableNodeIntoView(routing_id())); + } else { + // TODO(bryeung): implement this. Should hide the on-screen keyboard. + } +#endif } void RenderViewHost::OnMsgFocus() { @@ -2009,6 +2045,41 @@ void RenderViewHost::EnablePreferredSizeChangedMode(int flags) { Send(new ViewMsg_EnablePreferredSizeChangedMode(routing_id(), flags)); } +#if defined(OS_MACOSX) +void RenderViewHost::DidSelectPopupMenuItem(int selected_index) { + Send(new ViewMsg_SelectPopupMenuItem(routing_id(), selected_index)); +} + +void RenderViewHost::DidCancelPopupMenu() { + Send(new ViewMsg_SelectPopupMenuItem(routing_id(), -1)); +} +#endif + +void RenderViewHost::SearchBoxChange(const string16& value, + bool verbatim, + int selection_start, + int selection_end) { + Send(new ViewMsg_SearchBoxChange( + routing_id(), value, verbatim, selection_start, selection_end)); +} + +void RenderViewHost::SearchBoxSubmit(const string16& value, + bool verbatim) { + Send(new ViewMsg_SearchBoxSubmit(routing_id(), value, verbatim)); +} + +void RenderViewHost::SearchBoxCancel() { + Send(new ViewMsg_SearchBoxCancel(routing_id())); +} + +void RenderViewHost::SearchBoxResize(const gfx::Rect& search_box_bounds) { + Send(new ViewMsg_SearchBoxResize(routing_id(), search_box_bounds)); +} + +void RenderViewHost::DetermineIfPageSupportsInstant(const string16& value) { + Send(new ViewMsg_DetermineIfPageSupportsInstant(routing_id(), value)); +} + void RenderViewHost::OnExtensionPostMessage( int port_id, const std::string& message) { if (process()->profile()->GetExtensionMessageService()) { @@ -2111,13 +2182,22 @@ void RenderViewHost::OnUpdateZoomLimits(int minimum_percent, delegate_->UpdateZoomLimits(minimum_percent, maximum_percent, remember); } -void RenderViewHost::OnSetSuggestResult(int32 page_id, - const std::string& result) { +void RenderViewHost::OnSetSuggestions( + int32 page_id, + const std::vector<std::string>& suggestions) { + RenderViewHostDelegate::BrowserIntegration* integration_delegate = + delegate_->GetBrowserIntegrationDelegate(); + if (!integration_delegate) + return; + integration_delegate->OnSetSuggestions(page_id, suggestions); +} + +void RenderViewHost::OnInstantSupportDetermined(int32 page_id, bool result) { RenderViewHostDelegate::BrowserIntegration* integration_delegate = delegate_->GetBrowserIntegrationDelegate(); if (!integration_delegate) return; - integration_delegate->OnSetSuggestResult(page_id, result); + integration_delegate->OnInstantSupportDetermined(page_id, result); } void RenderViewHost::OnDetectedPhishingSite(const GURL& phishing_url, @@ -2139,3 +2219,37 @@ void RenderViewHost::OnScriptEvalResponse(int id, bool result) { void RenderViewHost::OnUpdateContentRestrictions(int restrictions) { delegate_->UpdateContentRestrictions(restrictions); } + +#if defined(OS_MACOSX) +void RenderViewHost::OnMsgShowPopup( + const ViewHostMsg_ShowPopup_Params& params) { + RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); + if (view) { + view->ShowPopupMenu(params.bounds, + params.item_height, + params.item_font_size, + params.selected_item, + params.popup_items, + params.right_aligned); + } +} +#endif + +#if defined(OS_MACOSX) || defined(OS_WIN) +void RenderViewHost::OnPageReadyForPreview( + const ViewHostMsg_DidPrintPage_Params& params) { + // TODO(kmadhusu): Function definition needs to be changed. + // 'params' contains the metafile handle for preview. + + // Send the printingDone msg for now. + Send(new ViewMsg_PrintingDone(routing_id(), -1, true)); +} +#else +void RenderViewHost::OnPagesReadyForPreview(int fd_in_browser) { + // TODO(kmadhusu): Function definition needs to be changed. + // fd_in_browser should be the file descriptor of the metafile. + + // Send the printingDone msg for now. + Send(new ViewMsg_PrintingDone(routing_id(), -1, true)); +} +#endif diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h index 76f1f01..efcf56c 100644 --- a/chrome/browser/renderer_host/render_view_host.h +++ b/chrome/browser/renderer_host/render_view_host.h @@ -202,6 +202,9 @@ class RenderViewHost : public RenderWidgetHost { // behalf. bool PrintPages(); + // Asks the renderer to render pages for print preview. + bool PrintPreview(); + // Notify renderer of success/failure of print job. void PrintingDone(int document_cookie, bool success); @@ -216,9 +219,12 @@ class RenderViewHost : public RenderWidgetHost { // Cancel a pending find operation. void StopFinding(FindBarController::SelectionAction selection_action); - // Change the zoom level of a page. + // Increment, decrement, or reset the zoom level of a page. void Zoom(PageZoom::Function function); + // Change the zoom level of a page to a specific value. + void SetZoomLevel(double zoom_level); + // Change the encoding of the page. void SetPageEncoding(const std::string& encoding); @@ -490,6 +496,23 @@ class RenderViewHost : public RenderWidgetHost { // in render_messages.h. void EnablePreferredSizeChangedMode(int flags); +#if defined(OS_MACOSX) + // Select popup menu related methods (for external popup menus). + void DidSelectPopupMenuItem(int selected_index); + void DidCancelPopupMenu(); +#endif + + // SearchBox notifications. + void SearchBoxChange(const string16& value, + bool verbatim, + int selection_start, + int selection_end); + void SearchBoxSubmit(const string16& value, + bool verbatim); + void SearchBoxCancel(); + void SearchBoxResize(const gfx::Rect& search_box_bounds); + void DetermineIfPageSupportsInstant(const string16& value); + #if defined(UNIT_TEST) // These functions shouldn't be necessary outside of testing. @@ -512,7 +535,7 @@ class RenderViewHost : public RenderWidgetHost { virtual void OnUserGesture(); virtual void NotifyRendererUnresponsive(); virtual void NotifyRendererResponsive(); - virtual void OnMsgFocusedNodeChanged(); + virtual void OnMsgFocusedNodeChanged(bool is_editable_node); virtual void OnMsgFocus(); virtual void OnMsgBlur(); @@ -582,7 +605,8 @@ class RenderViewHost : public RenderWidgetHost { void OnMsgForwardMessageToExternalHost(const std::string& message, const std::string& origin, const std::string& target); - void OnMsgDocumentLoadedInFrame(); + void OnMsgDocumentLoadedInFrame(long long frame_id); + void OnMsgDidFinishLoad(long long frame_id); void OnMsgGoToEntryAtOffset(int offset); void OnMsgSetTooltipText(const std::wstring& tooltip_text, WebKit::WebTextDirection text_direction_hint); @@ -631,8 +655,6 @@ class RenderViewHost : public RenderWidgetHost { void OnRequestUndockDevToolsWindow(); void OnDevToolsRuntimePropertyChanged(const std::string& name, const std::string& value); - - void OnUserMetricsRecordAction(const std::string& action); void OnMissingPluginStatus(int status); void OnNonSandboxedPluginBlocked(const std::string& plugin, const string16& name); @@ -694,12 +716,23 @@ class RenderViewHost : public RenderWidgetHost { void OnUpdateZoomLimits(int minimum_percent, int maximum_percent, bool remember); - void OnSetSuggestResult(int32 page_id, const std::string& result); + void OnSetSuggestions(int32 page_id, + const std::vector<std::string>& suggestions); + void OnInstantSupportDetermined(int32 page_id, bool result); void OnDetectedPhishingSite(const GURL& phishing_url, double phishing_score, const SkBitmap& thumbnail); void OnScriptEvalResponse(int id, bool result); void OnUpdateContentRestrictions(int restrictions); +#if defined(OS_MACOSX) || defined(OS_WIN) + void OnPageReadyForPreview(const ViewHostMsg_DidPrintPage_Params& params); +#else + void OnPagesReadyForPreview(int fd_in_browser); +#endif + +#if defined(OS_MACOSX) + void OnMsgShowPopup(const ViewHostMsg_ShowPopup_Params& params); +#endif private: friend class TestRenderViewHost; diff --git a/chrome/browser/renderer_host/render_view_host_delegate.cc b/chrome/browser/renderer_host/render_view_host_delegate.cc index 19722a1..523fd8f 100644 --- a/chrome/browser/renderer_host/render_view_host_delegate.cc +++ b/chrome/browser/renderer_host/render_view_host_delegate.cc @@ -112,7 +112,3 @@ gfx::Rect RenderViewHostDelegate::GetRootWindowResizerRect() const { bool RenderViewHostDelegate::IsExternalTabContainer() const { return false; } - -bool RenderViewHostDelegate::View::ShouldDrawDropShadow() { - return false; -} diff --git a/chrome/browser/renderer_host/render_view_host_delegate.h b/chrome/browser/renderer_host/render_view_host_delegate.h index d0c88d6..a4e88a3 100644 --- a/chrome/browser/renderer_host/render_view_host_delegate.h +++ b/chrome/browser/renderer_host/render_view_host_delegate.h @@ -50,6 +50,7 @@ struct ViewHostMsg_FrameNavigate_Params; struct ViewHostMsg_PageHasOSDD_Type; struct ViewHostMsg_RunFileChooser_Params; struct WebDropData; +struct WebMenuItem; class WebKeyboardEvent; struct WebPreferences; @@ -146,6 +147,16 @@ class RenderViewHostDelegate { // provided in the supplied params. virtual void ShowContextMenu(const ContextMenuParams& params) = 0; + // Shows a popup menu with the specified items. + // This method should call RenderViewHost::DidSelectPopupMenuItemAt() or + // RenderViewHost::DidCancelPopupMenu() ased on the user action. + virtual void ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned) = 0; + // The user started dragging content of the specified type within the // RenderView. Contextual information about the dragged content is supplied // by WebDropData. @@ -198,10 +209,6 @@ class RenderViewHostDelegate { // The contents' preferred size changed. virtual void UpdatePreferredSize(const gfx::Size& pref_size) = 0; - // Called to determine whether the render view needs to draw a drop shadow - // at the top (currently used for infobars). - virtual bool ShouldDrawDropShadow(); - protected: virtual ~View() {} }; @@ -292,8 +299,12 @@ class RenderViewHostDelegate { TranslateErrors::Type error_type) = 0; // Notification that the page has a suggest result. - virtual void OnSetSuggestResult(int32 page_id, - const std::string& result) = 0; + virtual void OnSetSuggestions( + int32 page_id, + const std::vector<std::string>& result) = 0; + + // Notification of whether the page supports instant-style interaction. + virtual void OnInstantSupportDetermined(int32 page_id, bool result) = 0; protected: virtual ~BrowserIntegration() {} @@ -354,7 +365,10 @@ class RenderViewHostDelegate { bool showing_repost_interstitial) = 0; // Notification that a document has been loaded in a frame. - virtual void DocumentLoadedInFrame() = 0; + virtual void DocumentLoadedInFrame(long long frame_id) = 0; + + // Notification that a frame finished loading. + virtual void DidFinishLoad(long long frame_id) = 0; protected: virtual ~Resource() {} @@ -383,7 +397,6 @@ class RenderViewHostDelegate { // |blocked_by_policy| should be true, and this function should invoke // OnContentBlocked. virtual void OnIndexedDBAccessed(const GURL& url, - const string16& name, const string16& description, bool blocked_by_policy) = 0; diff --git a/chrome/browser/renderer_host/render_widget_helper.cc b/chrome/browser/renderer_host/render_widget_helper.cc index 0124117..c3abe80 100644 --- a/chrome/browser/renderer_host/render_widget_helper.cc +++ b/chrome/browser/renderer_host/render_widget_helper.cc @@ -195,7 +195,7 @@ void RenderWidgetHelper::OnCancelResourceRequests( } void RenderWidgetHelper::OnCrossSiteClosePageACK( - ViewMsg_ClosePage_Params params) { + const ViewMsg_ClosePage_Params& params) { resource_dispatcher_host_->OnClosePageACK(params); } @@ -290,8 +290,7 @@ TransportDIB* RenderWidgetHelper::MapTransportDIB(TransportDIB::Id dib_id) { void RenderWidgetHelper::AllocTransportDIB( size_t size, bool cache_in_browser, TransportDIB::Handle* result) { scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); - if (!shared_memory->Create("", false /* read write */, - false /* do not open existing */, size)) { + if (!shared_memory->CreateAnonymous(size)) { result->fd = -1; result->auto_close = false; return; diff --git a/chrome/browser/renderer_host/render_widget_helper.h b/chrome/browser/renderer_host/render_widget_helper.h index 6cad1dc..6d3dcfa 100644 --- a/chrome/browser/renderer_host/render_widget_helper.h +++ b/chrome/browser/renderer_host/render_widget_helper.h @@ -189,7 +189,7 @@ class RenderWidgetHelper void OnCancelResourceRequests(int render_widget_id); // Called on the IO thread to resume a cross-site response. - void OnCrossSiteClosePageACK(ViewMsg_ClosePage_Params params); + void OnCrossSiteClosePageACK(const ViewMsg_ClosePage_Params& params); #if defined(OS_MACOSX) // Called on destruction to release all allocated transport DIBs diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc index a6efd58..6f586ee 100644 --- a/chrome/browser/renderer_host/render_widget_host.cc +++ b/chrome/browser/renderer_host/render_widget_host.cc @@ -17,7 +17,6 @@ #include "chrome/browser/renderer_host/render_widget_helper.h" #include "chrome/browser/renderer_host/render_widget_host_painting_observer.h" #include "chrome/browser/renderer_host/render_widget_host_view.h" -#include "chrome/browser/renderer_host/video_layer.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/native_web_keyboard_event.h" #include "chrome/common/notification_service.h" @@ -150,9 +149,6 @@ void RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) { IPC_MESSAGE_HANDLER(ViewHostMsg_RequestMove, OnMsgRequestMove) IPC_MESSAGE_HANDLER(ViewHostMsg_PaintAtSize_ACK, OnMsgPaintAtSizeAck) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnMsgUpdateRect) - IPC_MESSAGE_HANDLER(ViewHostMsg_CreateVideo, OnMsgCreateVideo) - IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateVideo, OnMsgUpdateVideo) - IPC_MESSAGE_HANDLER(ViewHostMsg_DestroyVideo, OnMsgDestroyVideo) IPC_MESSAGE_HANDLER(ViewHostMsg_HandleInputEvent_ACK, OnMsgInputEventAck) IPC_MESSAGE_HANDLER(ViewHostMsg_Focus, OnMsgFocus) IPC_MESSAGE_HANDLER(ViewHostMsg_Blur, OnMsgBlur) @@ -164,7 +160,6 @@ void RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) { IPC_MESSAGE_HANDLER(ViewHostMsg_GpuRenderingActivated, OnMsgGpuRenderingActivated) #if defined(OS_MACOSX) - IPC_MESSAGE_HANDLER(ViewHostMsg_ShowPopup, OnMsgShowPopup) IPC_MESSAGE_HANDLER(ViewHostMsg_GetScreenInfo, OnMsgGetScreenInfo) IPC_MESSAGE_HANDLER(ViewHostMsg_GetWindowRect, OnMsgGetWindowRect) IPC_MESSAGE_HANDLER(ViewHostMsg_GetRootWindowRect, OnMsgGetRootWindowRect) @@ -793,7 +788,6 @@ void RenderWidgetHost::OnMsgUpdateRect( DCHECK(!params.bitmap_rect.IsEmpty()); DCHECK(!params.view_size.IsEmpty()); - bool painted_synchronously = true; // Default to sending a paint ACK below. if (!is_gpu_rendering_active_) { const size_t size = params.bitmap_rect.height() * params.bitmap_rect.width() * 4; @@ -817,8 +811,7 @@ void RenderWidgetHost::OnMsgUpdateRect( // renderer-supplied bits. The view will read out of the backing store // later to actually draw to the screen. PaintBackingStoreRect(params.bitmap, params.bitmap_rect, - params.copy_rects, params.view_size, - &painted_synchronously); + params.copy_rects, params.view_size); } } } @@ -826,10 +819,8 @@ void RenderWidgetHost::OnMsgUpdateRect( // ACK early so we can prefetch the next PaintRect if there is a next one. // This must be done AFTER we're done painting with the bitmap supplied by the // renderer. This ACK is a signal to the renderer that the backing store can - // be re-used, so the bitmap may be invalid after this call. If the backing - // store is painting asynchronously, it will manage issuing this IPC. - if (painted_synchronously) - Send(new ViewMsg_UpdateRect_ACK(routing_id_)); + // be re-used, so the bitmap may be invalid after this call. + Send(new ViewMsg_UpdateRect_ACK(routing_id_)); // We don't need to update the view if the view is hidden. We must do this // early return after the ACK is sent, however, or the renderer will not send @@ -872,27 +863,6 @@ void RenderWidgetHost::OnMsgUpdateRect( UMA_HISTOGRAM_TIMES("MPArch.RWH_OnMsgUpdateRect", delta); } -void RenderWidgetHost::OnMsgCreateVideo(const gfx::Size& size) { - DCHECK(!video_layer_.get()); - - video_layer_.reset(view_->AllocVideoLayer(size)); - - // TODO(scherkus): support actual video ids! - Send(new ViewMsg_CreateVideo_ACK(routing_id_, -1)); -} - -void RenderWidgetHost::OnMsgUpdateVideo(TransportDIB::Id bitmap, - const gfx::Rect& bitmap_rect) { - PaintVideoLayer(bitmap, bitmap_rect); - - // TODO(scherkus): support actual video ids! - Send(new ViewMsg_UpdateVideo_ACK(routing_id_, -1)); -} - -void RenderWidgetHost::OnMsgDestroyVideo() { - video_layer_.reset(); -} - void RenderWidgetHost::OnMsgInputEventAck(const IPC::Message& message) { // Log the time delta for processing an input event. TimeDelta delta = TimeTicks::Now() - input_event_start_time_; @@ -978,16 +948,6 @@ void RenderWidgetHost::OnMsgGpuRenderingActivated(bool activated) { #if defined(OS_MACOSX) -void RenderWidgetHost::OnMsgShowPopup( - const ViewHostMsg_ShowPopup_Params& params) { - view_->ShowPopupWithItems(params.bounds, - params.item_height, - params.item_font_size, - params.selected_item, - params.popup_items, - params.right_aligned); -} - void RenderWidgetHost::OnMsgGetScreenInfo(gfx::NativeViewId view, WebScreenInfo* results) { gfx::NativeView native_view = view_ ? view_->GetNativeView() : NULL; @@ -1056,9 +1016,9 @@ void RenderWidgetHost::OnAcceleratedSurfaceSetTransportDIB( } void RenderWidgetHost::OnAcceleratedSurfaceBuffersSwapped( - gfx::PluginWindowHandle window) { + gfx::PluginWindowHandle window, uint64 surface_id) { if (view_) { - view_->AcceleratedSurfaceBuffersSwapped(window); + view_->AcceleratedSurfaceBuffersSwapped(window, surface_id); } } #elif defined(OS_POSIX) @@ -1088,12 +1048,7 @@ void RenderWidgetHost::PaintBackingStoreRect( TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, const std::vector<gfx::Rect>& copy_rects, - const gfx::Size& view_size, - bool* painted_synchronously) { - // On failure, we need to be sure our caller knows we're done with the - // backing store. - *painted_synchronously = true; - + const gfx::Size& view_size) { // The view may be destroyed already. if (!view_) return; @@ -1108,8 +1063,7 @@ void RenderWidgetHost::PaintBackingStoreRect( bool needs_full_paint = false; BackingStoreManager::PrepareBackingStore(this, view_size, bitmap, bitmap_rect, - copy_rects, &needs_full_paint, - painted_synchronously); + copy_rects, &needs_full_paint); if (needs_full_paint) { repaint_start_time_ = TimeTicks::Now(); repaint_ack_pending_ = true; @@ -1137,26 +1091,6 @@ void RenderWidgetHost::ScrollBackingStoreRect(int dx, int dy, backing_store->ScrollBackingStore(dx, dy, clip_rect, view_size); } -void RenderWidgetHost::PaintVideoLayer(TransportDIB::Id bitmap, - const gfx::Rect& bitmap_rect) { - if (is_hidden_ || !video_layer_.get()) - return; - - video_layer_->CopyTransportDIB(process(), bitmap, bitmap_rect); - - // Don't update the view if we're hidden or if the view has been destroyed. - if (is_hidden_ || !view_) - return; - - // Trigger a paint for the updated video layer bitmap. - std::vector<gfx::Rect> copy_rects; - copy_rects.push_back(bitmap_rect); - - view_being_painted_ = true; - view_->DidUpdateBackingStore(gfx::Rect(), 0, 0, copy_rects); - view_being_painted_ = false; -} - void RenderWidgetHost::ToggleSpellPanel(bool is_currently_visible) { Send(new ViewMsg_ToggleSpellPanel(routing_id(), is_currently_visible)); } diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h index 6c3fed7..42bdbde 100644 --- a/chrome/browser/renderer_host/render_widget_host.h +++ b/chrome/browser/renderer_host/render_widget_host.h @@ -42,7 +42,6 @@ class RenderProcessHost; class RenderWidgetHostView; class RenderWidgetHostPaintingObserver; class TransportDIB; -class VideoLayer; class WebCursor; struct ViewHostMsg_ShowPopup_Params; struct ViewHostMsg_UpdateRect_Params; @@ -248,9 +247,6 @@ class RenderWidgetHost : public IPC::Channel::Listener, // block briefly waiting for an ack from the renderer. void ScheduleComposite(); - // Returns the video layer if it exists, NULL otherwise. - VideoLayer* video_layer() const { return video_layer_.get(); } - // Starts a hang monitor timeout. If there's already a hang monitor timeout // the new one will only fire if it has a shorter delay than the time // left on the existing timeouts. @@ -479,9 +475,6 @@ class RenderWidgetHost : public IPC::Channel::Listener, void OnMsgRequestMove(const gfx::Rect& pos); void OnMsgPaintAtSizeAck(int tag, const gfx::Size& size); void OnMsgUpdateRect(const ViewHostMsg_UpdateRect_Params& params); - void OnMsgCreateVideo(const gfx::Size& size); - void OnMsgUpdateVideo(TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect); - void OnMsgDestroyVideo(); void OnMsgInputEventAck(const IPC::Message& message); virtual void OnMsgFocus(); virtual void OnMsgBlur(); @@ -494,7 +487,6 @@ class RenderWidgetHost : public IPC::Channel::Listener, void OnMsgGpuRenderingActivated(bool activated); #if defined(OS_MACOSX) - void OnMsgShowPopup(const ViewHostMsg_ShowPopup_Params& params); void OnMsgGetScreenInfo(gfx::NativeViewId view, WebKit::WebScreenInfo* results); void OnMsgGetWindowRect(gfx::NativeViewId window_id, gfx::Rect* results); @@ -512,22 +504,18 @@ class RenderWidgetHost : public IPC::Channel::Listener, int32 width, int32 height, TransportDIB::Handle transport_dib); - void OnAcceleratedSurfaceBuffersSwapped(gfx::PluginWindowHandle window); + void OnAcceleratedSurfaceBuffersSwapped(gfx::PluginWindowHandle window, + uint64 surface_id); #elif defined(OS_POSIX) void OnMsgCreatePluginContainer(gfx::PluginWindowHandle id); void OnMsgDestroyPluginContainer(gfx::PluginWindowHandle id); #endif // Paints the given bitmap to the current backing store at the given location. - // |*painted_synchronously| will be true if the message was processed - // synchronously, and the bitmap is done being used. False means that the - // backing store will paint the bitmap at a later time and that the DIB can't - // be freed (it will be the backing store's job to free it later). void PaintBackingStoreRect(TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, const std::vector<gfx::Rect>& copy_rects, - const gfx::Size& view_size, - bool* painted_synchronously); + const gfx::Size& view_size); // Scrolls the given |clip_rect| in the backing by the given dx/dy amount. The // |dib| and its corresponding location |bitmap_rect| in the backing store @@ -535,12 +523,6 @@ class RenderWidgetHost : public IPC::Channel::Listener, void ScrollBackingStoreRect(int dx, int dy, const gfx::Rect& clip_rect, const gfx::Size& view_size); - // Paints the entire given bitmap into the current video layer, if it exists. - // |bitmap_rect| specifies the destination size and absolute location of the - // bitmap on the backing store. - void PaintVideoLayer(TransportDIB::Id bitmap, - const gfx::Rect& bitmap_rect); - // Called by OnMsgInputEventAck() to process a keyboard event ack message. void ProcessKeyboardEventAck(int type, bool processed); @@ -690,9 +672,6 @@ class RenderWidgetHost : public IPC::Channel::Listener, // changed. bool suppress_next_char_events_; - // Optional video YUV layer for used for out-of-process compositing. - scoped_ptr<VideoLayer> video_layer_; - DISALLOW_COPY_AND_ASSIGN(RenderWidgetHost); }; diff --git a/chrome/browser/renderer_host/render_widget_host_view.h b/chrome/browser/renderer_host/render_widget_host_view.h index 46d6bda..d7fa9c3 100644 --- a/chrome/browser/renderer_host/render_widget_host_view.h +++ b/chrome/browser/renderer_host/render_widget_host_view.h @@ -30,11 +30,9 @@ class Message; class BackingStore; class RenderProcessHost; class RenderWidgetHost; -class VideoLayer; class WebCursor; struct NativeWebKeyboardEvent; struct ViewHostMsg_AccessibilityNotification_Params; -struct WebMenuItem; namespace webkit_glue { struct WebAccessibility; @@ -174,9 +172,6 @@ class RenderWidgetHostView { // Allocate a backing store for this view virtual BackingStore* AllocBackingStore(const gfx::Size& size) = 0; - // Allocate a video layer for this view. - virtual VideoLayer* AllocVideoLayer(const gfx::Size& size) = 0; - #if defined(OS_MACOSX) // Tells the view whether or not to accept first responder status. If |flag| // is true, the view does not accept first responder status and instead @@ -184,14 +179,6 @@ class RenderWidgetHostView { // |flag| is false, the view participates in the key-view chain as normal. virtual void SetTakesFocusOnlyOnMouseDown(bool flag) = 0; - // Display a native control popup menu for WebKit. - virtual void ShowPopupWithItems(gfx::Rect bounds, - int item_height, - double item_font_size, - int selected_item, - const std::vector<WebMenuItem>& items, - bool right_aligned) = 0; - // Get the view's position on the screen. virtual gfx::Rect GetWindowRect() = 0; @@ -238,7 +225,7 @@ class RenderWidgetHostView { int32 height, TransportDIB::Handle transport_dib) = 0; virtual void AcceleratedSurfaceBuffersSwapped( - gfx::PluginWindowHandle window) = 0; + gfx::PluginWindowHandle window, uint64 surface_id) = 0; virtual void GpuRenderingStateDidChange() = 0; #endif diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc index 5f4a843..bbbe456 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc @@ -29,16 +29,14 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/renderer_host/backing_store_x.h" -#include "chrome/browser/renderer_host/gpu_view_host.h" #include "chrome/browser/renderer_host/gtk_im_context_wrapper.h" #include "chrome/browser/renderer_host/gtk_key_bindings_handler.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/render_view_host_delegate.h" #include "chrome/browser/renderer_host/render_widget_host.h" -#include "chrome/browser/renderer_host/video_layer_x.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/native_web_keyboard_event.h" -#include "gfx/gtk_util.h" +#include "gfx/gtk_preserve_window.h" #include "third_party/WebKit/WebKit/chromium/public/gtk/WebInputEventFactory.h" #include "webkit/glue/plugins/webplugin.h" #include "webkit/glue/webaccessibility.h" @@ -71,17 +69,16 @@ using WebKit::WebMouseWheelEvent; class RenderWidgetHostViewGtkWidget { public: static GtkWidget* CreateNewWidget(RenderWidgetHostViewGtk* host_view) { - GtkWidget* widget = gtk_fixed_new(); + GtkWidget* widget = gtk_preserve_window_new(); gtk_widget_set_name(widget, "chrome-render-widget-host-view"); - gtk_fixed_set_has_window(GTK_FIXED(widget), TRUE); // We manually double-buffer in Paint() because Paint() may or may not be // called in repsonse to an "expose-event" signal. gtk_widget_set_double_buffered(widget, FALSE); gtk_widget_set_redraw_on_allocate(widget, FALSE); #if defined(NDEBUG) - gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &gfx::kGdkWhite); + gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, >k_util::kGdkWhite); #else - gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &gfx::kGdkGreen); + gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, >k_util::kGdkGreen); #endif // Allow the browser window to be resized freely. gtk_widget_set_size_request(widget, 0, 0); @@ -266,6 +263,10 @@ class RenderWidgetHostViewGtkWidget { if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) return FALSE; + // If we don't have focus already, this mouse click will focus us. + if (!gtk_widget_is_focus(widget)) + host_view->host_->OnMouseActivate(); + // Confirm existing composition text on mouse click events, to make sure // the input caret won't be moved with an ongoing composition session. host_view->im_context_->ConfirmComposition(); @@ -353,9 +354,9 @@ class RenderWidgetHostViewGtkWidget { static gboolean ClientEvent(GtkWidget* widget, GdkEventClient* event, RenderWidgetHostViewGtk* host_view) { - LOG(INFO) << "client event type: " << event->message_type - << " data_format: " << event->data_format - << " data: " << event->data.l; + VLOG(1) << "client event type: " << event->message_type + << " data_format: " << event->data_format + << " data: " << event->data.l; return true; } @@ -471,7 +472,6 @@ RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host) : host_(widget_host), - enable_gpu_rendering_(false), about_to_validate_and_paint_(false), is_hidden_(false), is_loading_(false), @@ -485,11 +485,6 @@ RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host) dragged_at_horizontal_edge_(0), dragged_at_vertical_edge_(0) { host_->set_view(this); - - // Enable experimental out-of-process GPU rendering. - CommandLine* command_line = CommandLine::ForCurrentProcess(); - enable_gpu_rendering_ = - command_line->HasSwitch(switches::kEnableGPURendering); } RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() { @@ -749,38 +744,11 @@ bool RenderWidgetHostViewGtk::IsPopup() { BackingStore* RenderWidgetHostViewGtk::AllocBackingStore( const gfx::Size& size) { - if (enable_gpu_rendering_) { - // Use a special GPU accelerated backing store. - if (!gpu_view_host_.get()) { - // Here we lazily make the GpuViewHost. This must be allocated when we - // have a native view realized, which happens sometime after creation - // when our owner puts us in the parent window. - DCHECK(GetNativeView()); - XID window_xid = x11_util::GetX11WindowFromGtkWidget(GetNativeView()); - gpu_view_host_.reset(new GpuViewHost(host_, window_xid)); - } - return gpu_view_host_->CreateBackingStore(size); - } - return new BackingStoreX(host_, size, x11_util::GetVisualFromGtkWidget(view_.get()), gtk_widget_get_visual(view_.get())->depth); } -VideoLayer* RenderWidgetHostViewGtk::AllocVideoLayer(const gfx::Size& size) { - if (enable_gpu_rendering_) { - // TODO(scherkus): is it possible for a video layer to be allocated before a - // backing store? - DCHECK(gpu_view_host_.get()) - << "AllocVideoLayer() called before AllocBackingStore()"; - return gpu_view_host_->CreateVideoLayer(size); - } - - return new VideoLayerX(host_, size, - x11_util::GetVisualFromGtkWidget(view_.get()), - gtk_widget_get_visual(view_.get())->depth); -} - void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) { RenderWidgetHostView::SetBackground(background); host_->Send(new ViewMsg_SetBackground(host_->routing_id(), background)); @@ -839,17 +807,6 @@ void RenderWidgetHostViewGtk::ModifyEventForEdgeDragging( } void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) { - if (enable_gpu_rendering_) { - // When we're proxying painting, we don't actually display the web page - // ourselves. - if (gpu_view_host_.get()) - gpu_view_host_->OnWindowPainted(); - - // Erase the background. This will prevent a flash of black when resizing - // or exposing the window. White is usually better than black. - return; - } - // If the GPU process is rendering directly into the View, // call the compositor directly. RenderWidgetHost* render_widget_host = GetRenderWidgetHost(); @@ -876,26 +833,11 @@ void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) { // period where this object isn't attached to a window but hasn't been // Destroy()ed yet and it receives paint messages... if (window) { - gfx::Rect drop_shadow_area(0, 0, kMaxWindowWidth, - gtk_util::kInfoBarDropShadowHeight); - bool drop_shadow = host_->IsRenderView() && - static_cast<RenderViewHost*>(host_)->delegate()->GetViewDelegate()-> - ShouldDrawDropShadow() && - drop_shadow_area.Intersects(paint_rect); - - if (!visually_deemphasized_ && !drop_shadow) { + if (!visually_deemphasized_) { // In the common case, use XCopyArea. We don't draw more than once, so // we don't need to double buffer. backing_store->XShowRect( paint_rect, x11_util::GetX11WindowFromGtkWidget(view_.get())); - - // Paint the video layer using XCopyArea. - // TODO(scherkus): implement VideoLayerX::CairoShow() for grey - // blending. - VideoLayerX* video_layer = static_cast<VideoLayerX*>( - host_->video_layer()); - if (video_layer) - video_layer->XShow(x11_util::GetX11WindowFromGtkWidget(view_.get())); } else { // If the grey blend is showing, we make two drawing calls. Use double // buffering to prevent flicker. Use CairoShowRect because XShowRect @@ -915,15 +857,9 @@ void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) { backing_store->CairoShowRect(damage_rect, GDK_DRAWABLE(window)); cairo_t* cr = gdk_cairo_create(window); - if (visually_deemphasized_) { - gdk_cairo_rectangle(cr, &rect); - cairo_set_source_rgba(cr, 0, 0, 0, 0.7); - cairo_fill(cr); - } - if (drop_shadow) { - gtk_util::DrawTopDropShadowForRenderView( - cr, gfx::Point(), damage_rect); - } + gdk_cairo_rectangle(cr, &rect); + cairo_set_source_rgba(cr, 0, 0, 0, 0.7); + cairo_fill(cr); cairo_destroy(cr); gdk_window_end_paint(window); diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.h b/chrome/browser/renderer_host/render_widget_host_view_gtk.h index 099da46..1817569 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_gtk.h +++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.h @@ -21,7 +21,6 @@ #include "webkit/glue/webcursor.h" class RenderWidgetHost; -class GpuViewHost; class GtkIMContextWrapper; class GtkKeyBindingsHandler; #if !defined(TOOLKIT_VIEWS) @@ -82,7 +81,6 @@ class RenderWidgetHostViewGtk : public RenderWidgetHostView { virtual void SelectionChanged(const std::string& text); virtual void ShowingContextMenu(bool showing); virtual BackingStore* AllocBackingStore(const gfx::Size& size); - virtual VideoLayer* AllocVideoLayer(const gfx::Size& size); virtual void SetBackground(const SkBitmap& background); virtual void CreatePluginContainer(gfx::PluginWindowHandle id); virtual void DestroyPluginContainer(gfx::PluginWindowHandle id); @@ -138,12 +136,6 @@ class RenderWidgetHostViewGtk : public RenderWidgetHostView { // The native UI widget. OwnedWidgetGtk view_; - // Cached value of --enable-gpu-rendering for out-of-process painting. - bool enable_gpu_rendering_; - - // Non-NULL when we're doing out-of-process painting. - scoped_ptr<GpuViewHost> gpu_view_host_; - // This is true when we are currently painting and thus should handle extra // paint requests by expanding the invalid rect rather than actually // painting. diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.h b/chrome/browser/renderer_host/render_widget_host_view_mac.h index 45af5c0..154c0c5 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_mac.h +++ b/chrome/browser/renderer_host/render_widget_host_view_mac.h @@ -21,7 +21,6 @@ #include "chrome/common/edit_command.h" #include "third_party/WebKit/WebKit/chromium/public/WebCompositionUnderline.h" #include "webkit/glue/webcursor.h" -#include "webkit/glue/webmenuitem.h" @class AcceleratedPluginView; class RenderWidgetHostViewMac; @@ -207,14 +206,7 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView { virtual void SetTooltipText(const std::wstring& tooltip_text); virtual void SelectionChanged(const std::string& text); virtual BackingStore* AllocBackingStore(const gfx::Size& size); - virtual VideoLayer* AllocVideoLayer(const gfx::Size& size); virtual void SetTakesFocusOnlyOnMouseDown(bool flag); - virtual void ShowPopupWithItems(gfx::Rect bounds, - int item_height, - double item_font_size, - int selected_item, - const std::vector<WebMenuItem>& items, - bool right_aligned); virtual gfx::Rect GetWindowRect(); virtual gfx::Rect GetRootWindowRect(); virtual void SetActive(bool active); @@ -251,7 +243,8 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView { int32 width, int32 height, TransportDIB::Handle transport_dib); - virtual void AcceleratedSurfaceBuffersSwapped(gfx::PluginWindowHandle window); + virtual void AcceleratedSurfaceBuffersSwapped(gfx::PluginWindowHandle window, + uint64 surface_id); virtual void GpuRenderingStateDidChange(); void DrawAcceleratedSurfaceInstance( CGLContextObj context, @@ -265,8 +258,6 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView { void KillSelf(); - void set_parent_view(NSView* parent_view) { parent_view_ = parent_view; } - void SetTextInputActive(bool active); // Sends confirmed plugin IME text back to the renderer. @@ -274,6 +265,14 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView { const std::string& selected_text() const { return selected_text_; } + void UpdateRootGpuViewVisibility(bool show_gpu_widget); + + // When rendering transitions from gpu to software, the gpu widget can't be + // hidden until the software backing store has been updated. This method + // checks if the GPU view needs to be hidden and hides it if necessary. It + // should be called after the software backing store has been painted to. + void HandleDelayedGpuViewHiding(); + // These member variables should be private, but the associated ObjC class // needs access to them and can't be made a friend. @@ -328,11 +327,7 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView { bool IsVoiceOverRunning(); // The associated view. This is weak and is inserted into the view hierarchy - // to own this RenderWidgetHostViewMac object unless is_popup_menu_ is true. - // In that case, cocoa_view_ is never inserted into the view hierarchy, so - // the RenderWidgetHostViewMac will treat it as a strong reference and will - // release it when told to destroy (for example, because a pop-up menu has - // closed). + // to own this RenderWidgetHostViewMac object. RenderWidgetHostViewCocoa* cocoa_view_; // The cursor for the page. This is passed up from the renderer. @@ -344,22 +339,20 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView { // true if the View is not visible. bool is_hidden_; - // True if the widget is a native popup menu. The renderer code calls this - // an "external popup." - bool is_popup_menu_; - // The text to be shown in the tooltip, supplied by the renderer. std::wstring tooltip_text_; // Factory used to safely scope delayed calls to ShutdownHost(). ScopedRunnableMethodFactory<RenderWidgetHostViewMac> shutdown_factory_; - // Used for positioning a popup menu. - NSView* parent_view_; - // selected text on the renderer. std::string selected_text_; + // When rendering transitions from gpu to software, the gpu widget can't be + // hidden until the software backing store has been updated. This variable is + // set when the gpu widget needs to be hidden once a paint is completed. + bool needs_gpu_visibility_update_after_repaint_; + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewMac); }; diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm index ffc4a92..d8cd696 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm +++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm @@ -41,7 +41,6 @@ #include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h" #include "webkit/glue/plugins/webplugin.h" #include "webkit/glue/webaccessibility.h" -#include "webkit/glue/webmenurunner_mac.h" #import "third_party/mozilla/ComplexTextInputPanel.h" using WebKit::WebInputEvent; @@ -284,9 +283,11 @@ static CVReturn DrawOneAcceleratedPluginCallback( // Called on a background thread. Synchronized via the CGL context lock. CGLLockContext(cglContext_); - // TODO(thakis): Pixel or view coordinates for size? - renderWidgetHostView_->DrawAcceleratedSurfaceInstance( - cglContext_, pluginHandle_, [self cachedSize]); + if (renderWidgetHostView_) { + // TODO(thakis): Pixel or view coordinates for size? + renderWidgetHostView_->DrawAcceleratedSurfaceInstance( + cglContext_, pluginHandle_, [self cachedSize]); + } CGLFlushDrawable(cglContext_); CGLUnlockContext(cglContext_); @@ -408,6 +409,17 @@ static CVReturn DrawOneAcceleratedPluginCallback( [self setCachedSize:newSize]; [super setFrameSize:newSize]; } + +- (BOOL)acceptsFirstResponder { + // Accept first responder if the first responder isn't the RWHVMac. + return [[self window] firstResponder] != [self superview]; +} + +- (BOOL)becomeFirstResponder { + // Delegate first responder to the RWHVMac. + [[self window] makeFirstResponder:[self superview]]; + return YES; +} @end // RenderWidgetHostView -------------------------------------------------------- @@ -438,9 +450,8 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget) text_input_type_(WebKit::WebTextInputTypeNone), is_loading_(false), is_hidden_(false), - is_popup_menu_(false), shutdown_factory_(this), - parent_view_(NULL) { + needs_gpu_visibility_update_after_repaint_(false) { // |cocoa_view_| owns us and we will be deleted when |cocoa_view_| goes away. // Since we autorelease it, our caller must put |native_view()| into the view // hierarchy right after calling us. @@ -676,45 +687,53 @@ void RenderWidgetHostViewMac::ImeCancelComposition() { void RenderWidgetHostViewMac::DidUpdateBackingStore( const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy, const std::vector<gfx::Rect>& copy_rects) { - if (is_hidden_) - return; - - std::vector<gfx::Rect> rects(copy_rects); - - // Because the findbar might be open, we cannot use scrollRect:by: here. For - // now, simply mark all of scroll rect as dirty. - if (!scroll_rect.IsEmpty()) - rects.push_back(scroll_rect); - - for (size_t i = 0; i < rects.size(); ++i) { - NSRect ns_rect = [cocoa_view_ flipRectToNSRect:rects[i]]; - - if (about_to_validate_and_paint_) { - // As much as we'd like to use -setNeedsDisplayInRect: here, we can't. - // We're in the middle of executing a -drawRect:, and as soon as it - // returns Cocoa will clear its record of what needs display. We instead - // use |performSelector:| to call |setNeedsDisplayInRect:| after returning - // to the main loop, at which point |drawRect:| is no longer on the stack. - DCHECK([NSThread isMainThread]); - if (!call_set_needs_display_in_rect_pending_) { - [cocoa_view_ performSelector:@selector(callSetNeedsDisplayInRect) - withObject:nil - afterDelay:0]; - call_set_needs_display_in_rect_pending_ = true; - invalid_rect_ = ns_rect; + if (!is_hidden_) { + std::vector<gfx::Rect> rects(copy_rects); + + // Because the findbar might be open, we cannot use scrollRect:by: here. For + // now, simply mark all of scroll rect as dirty. + if (!scroll_rect.IsEmpty()) + rects.push_back(scroll_rect); + + for (size_t i = 0; i < rects.size(); ++i) { + NSRect ns_rect = [cocoa_view_ flipRectToNSRect:rects[i]]; + + if (about_to_validate_and_paint_) { + // As much as we'd like to use -setNeedsDisplayInRect: here, we can't. + // We're in the middle of executing a -drawRect:, and as soon as it + // returns Cocoa will clear its record of what needs display. We + // instead use |performSelector:| to call |setNeedsDisplayInRect:| + // after returning to the main loop, at which point |drawRect:| is no + // longer on the stack. + DCHECK([NSThread isMainThread]); + if (!call_set_needs_display_in_rect_pending_) { + [cocoa_view_ performSelector:@selector(callSetNeedsDisplayInRect) + withObject:nil + afterDelay:0]; + call_set_needs_display_in_rect_pending_ = true; + invalid_rect_ = ns_rect; + } else { + // The old invalid rect is probably invalid now, since the view has + // most likely been resized, but there's no harm in dirtying the + // union. In the limit, this becomes equivalent to dirtying the + // whole view. + invalid_rect_ = NSUnionRect(invalid_rect_, ns_rect); + } } else { - // The old invalid rect is probably invalid now, since the view has most - // likely been resized, but there's no harm in dirtying the union. In - // the limit, this becomes equivalent to dirtying the whole view. - invalid_rect_ = NSUnionRect(invalid_rect_, ns_rect); + [cocoa_view_ setNeedsDisplayInRect:ns_rect]; } - } else { - [cocoa_view_ setNeedsDisplayInRect:ns_rect]; } + + if (!about_to_validate_and_paint_) + [cocoa_view_ displayIfNeeded]; } + // If |about_to_validate_and_paint_| is set, then -drawRect: is on the stack + // and it's not allowed to call -setHidden on the accelerated view. In that + // case, -callSetNeedsDisplayInRect: will hide it later. + // If |about_to_validate_and_paint_| is not set, do it now. if (!about_to_validate_and_paint_) - [cocoa_view_ displayIfNeeded]; + HandleDelayedGpuViewHiding(); } void RenderWidgetHostViewMac::RenderViewGone() { @@ -732,37 +751,23 @@ void RenderWidgetHostViewMac::Destroy() { // time Destroy() was called. On the Mac we have to destroy all the popups // ourselves. - if (!is_popup_menu_) { - // Depth-first destroy all popups. Use ShutdownHost() to enforce - // deepest-first ordering. - for (NSView* subview in [cocoa_view_ subviews]) { - if ([subview isKindOfClass:[RenderWidgetHostViewCocoa class]]) { - [static_cast<RenderWidgetHostViewCocoa*>(subview) - renderWidgetHostViewMac]->ShutdownHost(); - } else if ([subview isKindOfClass:[AcceleratedPluginView class]]) { - [static_cast<AcceleratedPluginView*>(subview) - onRenderWidgetHostViewGone]; - } + // Depth-first destroy all popups. Use ShutdownHost() to enforce + // deepest-first ordering. + for (NSView* subview in [cocoa_view_ subviews]) { + if ([subview isKindOfClass:[RenderWidgetHostViewCocoa class]]) { + [static_cast<RenderWidgetHostViewCocoa*>(subview) + renderWidgetHostViewMac]->ShutdownHost(); + } else if ([subview isKindOfClass:[AcceleratedPluginView class]]) { + [static_cast<AcceleratedPluginView*>(subview) + onRenderWidgetHostViewGone]; } - - // We've been told to destroy. - [cocoa_view_ retain]; - [cocoa_view_ removeFromSuperview]; - [cocoa_view_ autorelease]; - } else { - // From the renderer's perspective, the pop-up menu is represented by a - // RenderWidget. The actual Mac implementation uses a native pop-up menu - // and doesn't actually make use of the RenderWidgetHostViewCocoa that - // was allocated to own it in its constructor. When the pop-up menu goes - // away, free the RenderWidgetHostViewCocoa. Its deallocation will result - // in this object's destruction. - - DCHECK([[cocoa_view_ subviews] count] == 0); - DCHECK([cocoa_view_ superview] == nil); - - [cocoa_view_ autorelease]; } + // We've been told to destroy. + [cocoa_view_ retain]; + [cocoa_view_ removeFromSuperview]; + [cocoa_view_ autorelease]; + // We get this call just before |render_widget_host_| deletes // itself. But we are owned by |cocoa_view_|, which may be retained // by some other code. Examples are TabContentsViewMac's @@ -804,89 +809,11 @@ BackingStore* RenderWidgetHostViewMac::AllocBackingStore( return new BackingStoreMac(render_widget_host_, size); } -VideoLayer* RenderWidgetHostViewMac::AllocVideoLayer( - const gfx::Size& size) { - NOTIMPLEMENTED(); - return NULL; -} - // Sets whether or not to accept first responder status. void RenderWidgetHostViewMac::SetTakesFocusOnlyOnMouseDown(bool flag) { [cocoa_view_ setTakesFocusOnlyOnMouseDown:flag]; } -// Display a popup menu for WebKit using Cocoa widgets. -void RenderWidgetHostViewMac::ShowPopupWithItems( - gfx::Rect bounds, - int item_height, - double item_font_size, - int selected_item, - const std::vector<WebMenuItem>& items, - bool right_aligned) { - is_popup_menu_ = true; - - // Retain the Cocoa view for the duration of the pop-up so that it can't - // be dealloced if my Destroy() method is called while the pop-up's up - // (which would in turn delete me, causing a crash once the -runMenuInView - // call returns. That's what was happening in <http://crbug.com/33250>). - scoped_nsobject<RenderWidgetHostViewCocoa> retainedCocoaView - ([cocoa_view_ retain]); - - NSRect view_rect = [cocoa_view_ bounds]; - NSRect parent_rect = [parent_view_ bounds]; - int y_offset = bounds.y() + bounds.height(); - NSRect position = NSMakeRect(bounds.x(), parent_rect.size.height - y_offset, - bounds.width(), bounds.height()); - - // Display the menu. - scoped_nsobject<WebMenuRunner> menu_runner; - menu_runner.reset([[WebMenuRunner alloc] initWithItems:items - fontSize:item_font_size - rightAligned:right_aligned]); - - { - // Make sure events can be pumped while the menu is up. - MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); - - // One of the events that could be pumped is |window.close()|. - // User-initiated event-tracking loops protect against this by - // setting flags in -[CrApplication sendEvent:], but since - // web-content menus are initiated by IPC message the setup has to - // be done manually. - chrome_application_mac::ScopedSendingEvent sendingEventScoper; - - // Now run a SYNCHRONOUS NESTED EVENT LOOP until the pop-up is finished. - [menu_runner runMenuInView:parent_view_ - withBounds:position - initialIndex:selected_item]; - } - - if (!render_widget_host_) { - // Bad news -- my Destroy() was called while I was off running the menu. - // Return ASAP, and the release of retainedCocoaView will dealloc my NSView, - // which will delete me (whew). - return; - } - - int window_num = [[parent_view_ window] windowNumber]; - NSEvent* event = - webkit_glue::EventWithMenuAction([menu_runner menuItemWasChosen], - window_num, item_height, - [menu_runner indexOfSelectedItem], - position, view_rect); - - if ([menu_runner menuItemWasChosen]) { - // Simulate a menu selection event. - const WebMouseEvent& mouse_event = - WebInputEventFactory::mouseEvent(event, cocoa_view_); - render_widget_host_->ForwardMouseEvent(mouse_event); - } else { - // Simulate a menu dismiss event. - NativeWebKeyboardEvent keyboard_event(event); - render_widget_host_->ForwardKeyboardEvent(keyboard_event); - } -} - void RenderWidgetHostViewMac::KillSelf() { if (shutdown_factory_.empty()) { [cocoa_view_ setHidden:YES]; @@ -1000,7 +927,7 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceSetTransportDIB( } void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped( - gfx::PluginWindowHandle window) { + gfx::PluginWindowHandle window, uint64 surface_id) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); PluginViewMap::iterator it = plugin_views_.find(window); DCHECK(plugin_views_.end() != it); @@ -1009,7 +936,7 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped( } DCHECK([it->second isKindOfClass:[AcceleratedPluginView class]]); - plugin_container_manager_.SetSurfaceWasPaintedTo(window); + plugin_container_manager_.SetSurfaceWasPaintedTo(window, surface_id); AcceleratedPluginView* view = static_cast<AcceleratedPluginView*>(it->second); // The surface is hidden until its first paint, to not show gargabe. @@ -1018,16 +945,15 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped( [view setSurfaceWasSwapped:YES]; } -void RenderWidgetHostViewMac::GpuRenderingStateDidChange() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +void RenderWidgetHostViewMac::UpdateRootGpuViewVisibility( + bool show_gpu_widget) { // Plugins are destroyed on page navigate. The compositor layer on the other // hand is created on demand and then stays alive until its renderer process // dies (usually on cross-domain navigation). Instead, only a flag // |is_gpu_rendering_active()| is flipped when the compositor output should be // shown/hidden. // Show/hide the view belonging to the compositor here. - plugin_container_manager_.set_gpu_rendering_active( - GetRenderWidgetHost()->is_gpu_rendering_active()); + plugin_container_manager_.set_gpu_rendering_active(show_gpu_widget); gfx::PluginWindowHandle root_handle = plugin_container_manager_.root_container_handle(); @@ -1039,10 +965,28 @@ void RenderWidgetHostViewMac::GpuRenderingStateDidChange() { } bool visible = plugin_container_manager_.SurfaceShouldBeVisible(root_handle); + [[it->second window] disableScreenUpdatesUntilFlush]; [it->second setHidden:!visible]; } } +void RenderWidgetHostViewMac::HandleDelayedGpuViewHiding() { + if (needs_gpu_visibility_update_after_repaint_) { + UpdateRootGpuViewVisibility(false); + needs_gpu_visibility_update_after_repaint_ = false; + } +} + +void RenderWidgetHostViewMac::GpuRenderingStateDidChange() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (GetRenderWidgetHost()->is_gpu_rendering_active()) { + UpdateRootGpuViewVisibility( + GetRenderWidgetHost()->is_gpu_rendering_active()); + } else { + needs_gpu_visibility_update_after_repaint_ = true; + } +} + void RenderWidgetHostViewMac::DrawAcceleratedSurfaceInstance( CGLContextObj context, gfx::PluginWindowHandle plugin_handle, @@ -1488,6 +1432,7 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) { return; } + const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask; // Only send a corresponding key press event if there is no marked text. if (!hasMarkedText_) { if (!textInserted && textToBeInserted_.length() == 1) { @@ -1499,7 +1444,9 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) { event.skip_in_browser = true; widgetHost->ForwardKeyboardEvent(event); } else if ((!textInserted || delayEventUntilAfterImeCompostion) && - editCommands_.empty() && [[theEvent characters] length] > 0) { + [[theEvent characters] length] > 0 && + (([theEvent modifierFlags] & kCtrlCmdKeyMask) || + (hasEditCommands_ && editCommands_.empty()))) { // We don't get insertText: calls if ctrl or cmd is down, or the key event // generates an insert command. So synthesize a keypress event for these // cases, unless the key event generated any other command. @@ -1570,6 +1517,8 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) { [self setNeedsDisplayInRect:renderWidgetHostView_->invalid_rect_]; renderWidgetHostView_->call_set_needs_display_in_rect_pending_ = false; renderWidgetHostView_->invalid_rect_ = NSZeroRect; + + renderWidgetHostView_->HandleDelayedGpuViewHiding(); } // Fills with white the parts of the area to the right and bottom for |rect| @@ -1751,6 +1700,16 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) { // See http://crbug.com/47209 [self cancelComposition]; + NSNumber* direction = [NSNumber numberWithUnsignedInteger: + [[self window] keyViewSelectionDirection]]; + NSDictionary* userInfo = + [NSDictionary dictionaryWithObject:direction + forKey:kSelectionDirection]; + [[NSNotificationCenter defaultCenter] + postNotificationName:kViewDidBecomeFirstResponder + object:self + userInfo:userInfo]; + return YES; } diff --git a/chrome/browser/renderer_host/render_widget_host_view_views.cc b/chrome/browser/renderer_host/render_widget_host_view_views.cc index da4e99c..014c7fd 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_views.cc +++ b/chrome/browser/renderer_host/render_widget_host_view_views.cc @@ -18,9 +18,7 @@ #include "base/task.h" #include "base/time.h" #include "chrome/browser/renderer_host/backing_store_x.h" -#include "chrome/browser/renderer_host/gpu_view_host.h" #include "chrome/browser/renderer_host/render_widget_host.h" -#include "chrome/browser/renderer_host/video_layer_x.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/native_web_keyboard_event.h" #include "chrome/common/render_messages.h" @@ -61,7 +59,6 @@ RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( RenderWidgetHostViewViews::RenderWidgetHostViewViews(RenderWidgetHost* host) : host_(host), - enable_gpu_rendering_(false), about_to_validate_and_paint_(false), is_hidden_(false), is_loading_(false), @@ -69,14 +66,10 @@ RenderWidgetHostViewViews::RenderWidgetHostViewViews(RenderWidgetHost* host) visually_deemphasized_(false) { SetFocusable(true); host_->set_view(this); - - // Enable experimental out-of-process GPU rendering. - CommandLine* command_line = CommandLine::ForCurrentProcess(); - enable_gpu_rendering_ = - command_line->HasSwitch(switches::kEnableGPURendering); } RenderWidgetHostViewViews::~RenderWidgetHostViewViews() { + RenderViewGone(); } void RenderWidgetHostViewViews::InitAsChild() { @@ -130,7 +123,7 @@ void RenderWidgetHostViewViews::SetSize(const gfx::Size& size) { if (requested_size_.width() != width || requested_size_.height() != height) { requested_size_ = gfx::Size(width, height); - SetBounds(GetViewBounds()); + SetBounds(x(), y(), requested_size_.width(), requested_size_.height()); host_->WasResized(); } } @@ -163,7 +156,6 @@ void RenderWidgetHostViewViews::Blur() { host_->Blur(); } - bool RenderWidgetHostViewViews::IsShowing() { return IsVisible(); } @@ -233,6 +225,7 @@ void RenderWidgetHostViewViews::DidUpdateBackingStore( } void RenderWidgetHostViewViews::RenderViewGone() { + GetRenderWidgetHost()->ViewDestroyed(); Destroy(); } @@ -266,19 +259,6 @@ bool RenderWidgetHostViewViews::IsPopup() { BackingStore* RenderWidgetHostViewViews::AllocBackingStore( const gfx::Size& size) { - if (enable_gpu_rendering_) { - // Use a special GPU accelerated backing store. - if (!gpu_view_host_.get()) { - // Here we lazily make the GpuViewHost. This must be allocated when we - // have a native view realized, which happens sometime after creation - // when our owner puts us in the parent window. - DCHECK(GetNativeView()); - XID window_xid = x11_util::GetX11WindowFromGtkWidget(GetNativeView()); - gpu_view_host_.reset(new GpuViewHost(host_, window_xid)); - } - return gpu_view_host_->CreateBackingStore(size); - } - return new BackingStoreX(host_, size, x11_util::GetVisualFromGtkWidget(native_view()), gtk_widget_get_visual(native_view())->depth); @@ -288,37 +268,12 @@ gfx::NativeView RenderWidgetHostViewViews::native_view() const { return GetWidget()->GetNativeView(); } -VideoLayer* RenderWidgetHostViewViews::AllocVideoLayer(const gfx::Size& size) { - if (enable_gpu_rendering_) { - // TODO(scherkus): is it possible for a video layer to be allocated before a - // backing store? - DCHECK(gpu_view_host_.get()) - << "AllocVideoLayer() called before AllocBackingStore()"; - return gpu_view_host_->CreateVideoLayer(size); - } - - return new VideoLayerX(host_, size, - x11_util::GetVisualFromGtkWidget(native_view()), - gtk_widget_get_visual(native_view())->depth); -} - void RenderWidgetHostViewViews::SetBackground(const SkBitmap& background) { RenderWidgetHostView::SetBackground(background); host_->Send(new ViewMsg_SetBackground(host_->routing_id(), background)); } void RenderWidgetHostViewViews::Paint(gfx::Canvas* canvas) { - if (enable_gpu_rendering_) { - // When we're proxying painting, we don't actually display the web page - // ourselves. - if (gpu_view_host_.get()) - gpu_view_host_->OnWindowPainted(); - - // Erase the background. This will prevent a flash of black when resizing - // or exposing the window. White is usually better than black. - return; - } - // Don't do any painting if the GPU process is rendering directly // into the View. RenderWidgetHost* render_widget_host = GetRenderWidgetHost(); @@ -330,7 +285,7 @@ void RenderWidgetHostViewViews::Paint(gfx::Canvas* canvas) { DCHECK(!about_to_validate_and_paint_); // TODO(anicolao): get the damage somehow - //invalid_rect_ = damage_rect; + // invalid_rect_ = damage_rect; invalid_rect_ = bounds(); about_to_validate_and_paint_ = true; BackingStoreX* backing_store = static_cast<BackingStoreX*>( @@ -351,15 +306,6 @@ void RenderWidgetHostViewViews::Paint(gfx::Canvas* canvas) { // we don't need to double buffer. backing_store->XShowRect( paint_rect, x11_util::GetX11WindowFromGtkWidget(native_view())); - - // Paint the video layer using XCopyArea. - // TODO(scherkus): implement VideoLayerX::CairoShow() for grey - // blending. - VideoLayerX* video_layer = static_cast<VideoLayerX*>( - host_->video_layer()); - if (video_layer) - video_layer->XShow( - x11_util::GetX11WindowFromGtkWidget(native_view())); } else { // If the grey blend is showing, we make two drawing calls. Use double // buffering to prevent flicker. Use CairoShowRect because XShowRect diff --git a/chrome/browser/renderer_host/render_widget_host_view_views.h b/chrome/browser/renderer_host/render_widget_host_view_views.h index 6ba5d4a..0ef343e 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_views.h +++ b/chrome/browser/renderer_host/render_widget_host_view_views.h @@ -20,7 +20,6 @@ #include "webkit/glue/webcursor.h" class RenderWidgetHost; -class GpuViewHost; struct NativeWebKeyboardEvent; namespace WebKit { @@ -71,7 +70,6 @@ class RenderWidgetHostViewViews : public RenderWidgetHostView, virtual void SelectionChanged(const std::string& text); virtual void ShowingContextMenu(bool showing); virtual BackingStore* AllocBackingStore(const gfx::Size& size); - virtual VideoLayer* AllocVideoLayer(const gfx::Size& size); virtual void SetBackground(const SkBitmap& background); virtual void CreatePluginContainer(gfx::PluginWindowHandle id); virtual void DestroyPluginContainer(gfx::PluginWindowHandle id); @@ -123,12 +121,6 @@ class RenderWidgetHostViewViews : public RenderWidgetHostView, // The model object. RenderWidgetHost* host_; - // Cached value of --enable-gpu-rendering for out-of-process painting. - bool enable_gpu_rendering_; - - // Non-NULL when we're doing out-of-process painting. - scoped_ptr<GpuViewHost> gpu_view_host_; - // This is true when we are currently painting and thus should handle extra // paint requests by expanding the invalid rect rather than actually // painting. diff --git a/chrome/browser/renderer_host/render_widget_host_view_win.cc b/chrome/browser/renderer_host/render_widget_host_view_win.cc index 0b66f0c..38bbee2 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_win.cc +++ b/chrome/browser/renderer_host/render_widget_host_view_win.cc @@ -7,6 +7,7 @@ #include "app/l10n_util.h" #include "app/l10n_util_win.h" #include "app/resource_bundle.h" +#include "app/win/scoped_prop.h" #include "base/command_line.h" #include "base/i18n/rtl.h" #include "base/metrics/histogram.h" @@ -23,7 +24,6 @@ #include "chrome/browser/plugin_process_host.h" #include "chrome/browser/renderer_host/backing_store.h" #include "chrome/browser/renderer_host/backing_store_win.h" -#include "chrome/browser/renderer_host/gpu_view_host.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/common/chrome_constants.h" @@ -310,13 +310,10 @@ void RenderWidgetHostViewWin::CreateWnd(HWND parent) { // this window. Used by the GPU process to validate window handles it // receives from renderer processes. int renderer_id = render_widget_host_->process()->id(); - SetProp(m_hWnd, - chrome::kChromiumRendererIdProperty, - reinterpret_cast<HANDLE>(renderer_id)); - - // Uncommenting this will enable experimental out-of-process painting. - // Contact brettw for more, - // gpu_view_host_.reset(new GpuViewHost(render_widget_host_, m_hWnd)); + props_.push_back( + new app::win::ScopedProp(m_hWnd, + chrome::kChromiumRendererIdProperty, + reinterpret_cast<HANDLE>(renderer_id))); } /////////////////////////////////////////////////////////////////////////////// @@ -789,17 +786,9 @@ void RenderWidgetHostViewWin::SetTooltipText(const std::wstring& tooltip_text) { BackingStore* RenderWidgetHostViewWin::AllocBackingStore( const gfx::Size& size) { - if (gpu_view_host_.get()) - return gpu_view_host_->CreateBackingStore(size); return new BackingStoreWin(render_widget_host_, size); } -VideoLayer* RenderWidgetHostViewWin::AllocVideoLayer( - const gfx::Size& size) { - NOTIMPLEMENTED(); - return NULL; -} - void RenderWidgetHostViewWin::SetBackground(const SkBitmap& background) { RenderWidgetHostView::SetBackground(background); Send(new ViewMsg_SetBackground(render_widget_host_->routing_id(), @@ -840,12 +829,15 @@ LRESULT RenderWidgetHostViewWin::OnCreate(CREATESTRUCT* create_struct) { OnInputLangChange(0, 0); // Marks that window as supporting mouse-wheel messages rerouting so it is // scrolled when under the mouse pointer even if inactive. - views::SetWindowSupportsRerouteMouseWheel(m_hWnd); + props_.push_back(views::SetWindowSupportsRerouteMouseWheel(m_hWnd)); // Save away our HWND in the parent window as a property so that the // accessibility code can find it. - ::SetProp(GetParent(), kViewsNativeHostPropForAccessibility, m_hWnd); - ::SetProp(m_hWnd, kRenderWidgetHostViewKey, - static_cast<RenderWidgetHostView*>(this)); + props_.push_back(new app::win::ScopedProp( + GetParent(), kViewsNativeHostPropForAccessibility, + m_hWnd)); + props_.push_back(new app::win::ScopedProp( + m_hWnd, kRenderWidgetHostViewKey, + static_cast<RenderWidgetHostView*>(this))); return 0; } @@ -876,7 +868,7 @@ void RenderWidgetHostViewWin::OnDestroy() { // sequence as part of the usual cleanup when the plugin instance goes away. EnumChildWindows(m_hWnd, DetachPluginWindowsCallback, NULL); - ::RemoveProp(m_hWnd, kRenderWidgetHostViewKey); + props_.reset(); ResetTooltip(); TrackMouseLeave(false); @@ -885,16 +877,6 @@ void RenderWidgetHostViewWin::OnDestroy() { void RenderWidgetHostViewWin::OnPaint(HDC unused_dc) { DCHECK(render_widget_host_->process()->HasConnection()); - if (gpu_view_host_.get()) { - // When we're proxying painting, we don't actually display the web page - // ourselves. We clear it white in case the proxy window isn't visible - // yet we won't show gibberish. - CPaintDC paint_dc(m_hWnd); - FillRect(paint_dc.m_hDC, &paint_dc.m_ps.rcPaint, - static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH))); - return; - } - // If the GPU process is rendering directly into the View, // call the compositor directly. RenderWidgetHost* render_widget_host = GetRenderWidgetHost(); diff --git a/chrome/browser/renderer_host/render_widget_host_view_win.h b/chrome/browser/renderer_host/render_widget_host_view_win.h index 9fbaf11..5d8c680 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_win.h +++ b/chrome/browser/renderer_host/render_widget_host_view_win.h @@ -15,6 +15,7 @@ #include "base/scoped_comptr_win.h" #include "base/scoped_ptr.h" +#include "base/scoped_vector.h" #include "base/task.h" #include "chrome/browser/accessibility/browser_accessibility_manager.h" #include "chrome/browser/ime_input.h" @@ -23,6 +24,12 @@ #include "chrome/common/notification_registrar.h" #include "webkit/glue/webcursor.h" +namespace app { +namespace win { +class ScopedProp; +} +} + namespace gfx { class Size; class Rect; @@ -34,7 +41,6 @@ class Message; class BackingStore; class RenderWidgetHost; -class GpuViewHost; typedef CWinTraits<WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0> RenderWidgetHostHWNDTraits; @@ -149,7 +155,6 @@ class RenderWidgetHostViewWin virtual void Destroy(); virtual void SetTooltipText(const std::wstring& tooltip_text); virtual BackingStore* AllocBackingStore(const gfx::Size& size); - virtual VideoLayer* AllocVideoLayer(const gfx::Size& size); virtual void SetBackground(const SkBitmap& background); virtual bool ContainsNativeView(gfx::NativeView native_view) const; virtual void SetVisuallyDeemphasized(bool deemphasized); @@ -261,10 +266,6 @@ class RenderWidgetHostViewWin // The associated Model. RenderWidgetHost* render_widget_host_; - // If we're doing out-of-process painting, this member will be non-NULL, - // indicating the gpu view we're using for the painting. - scoped_ptr<GpuViewHost> gpu_view_host_; - // The cursor for the page. This is passed up from the renderer. WebCursor current_cursor_; @@ -343,6 +344,8 @@ class RenderWidgetHostViewWin // method. WebKit::WebTextInputType text_input_type_; + ScopedVector<app::win::ScopedProp> props_; + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewWin); }; diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index a17b12b..6fd669e 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -78,15 +78,6 @@ #include "chrome/browser/renderer_host/offline_resource_handler.h" #endif -// Uncomment to enable logging of request traffic. -// #define LOG_RESOURCE_DISPATCHER_REQUESTS - -#ifdef LOG_RESOURCE_DISPATCHER_REQUESTS -# define RESOURCE_LOG(stuff) LOG(INFO) << stuff -#else -# define RESOURCE_LOG(stuff) -#endif - using base::Time; using base::TimeDelta; using base::TimeTicks; @@ -143,8 +134,8 @@ bool ShouldServiceRequest(ChildProcessInfo::ProcessType process_type, // Check if the renderer is permitted to request the requested URL. if (!policy->CanRequestURL(child_id, request_data.url)) { - LOG(INFO) << "Denied unauthorized request for " << - request_data.url.possibly_invalid_spec(); + VLOG(1) << "Denied unauthorized request for " + << request_data.url.possibly_invalid_spec(); return false; } @@ -449,11 +440,13 @@ void ResourceDispatcherHost::BeginRequest( request->SetExtraRequestHeaders(headers); int load_flags = request_data.load_flags; - // EV certificate verification could be expensive. We don't want to spend - // time performing EV certificate verification on all resources because - // EV status is irrelevant to sub-frames and sub-resources. + // Although EV status is irrelevant to sub-frames and sub-resources, we have + // to perform EV certificate verification on all resources because an HTTP + // keep-alive connection created to load a sub-frame or a sub-resource could + // be reused to load a main frame. + load_flags |= net::LOAD_VERIFY_EV_CERT; if (request_data.resource_type == ResourceType::MAIN_FRAME) { - load_flags |= net::LOAD_VERIFY_EV_CERT | net::LOAD_MAIN_FRAME; + load_flags |= net::LOAD_MAIN_FRAME; } else if (request_data.resource_type == ResourceType::SUB_FRAME) { load_flags |= net::LOAD_SUB_FRAME; } @@ -462,7 +455,7 @@ void ResourceDispatcherHost::BeginRequest( if ((load_flags & net::LOAD_REPORT_RAW_HEADERS) && !ChildProcessSecurityPolicy::GetInstance()-> CanReadRawCookies(child_id)) { - LOG(INFO) << "Denied unathorized request for raw headers"; + VLOG(1) << "Denied unathorized request for raw headers"; load_flags &= ~net::LOAD_REPORT_RAW_HEADERS; } @@ -704,8 +697,8 @@ void ResourceDispatcherHost::BeginDownload( // Check if the renderer is permitted to request the requested URL. if (!ChildProcessSecurityPolicy::GetInstance()-> CanRequestURL(child_id, url)) { - LOG(INFO) << "Denied unauthorized download request for " << - url.possibly_invalid_spec(); + VLOG(1) << "Denied unauthorized download request for " + << url.possibly_invalid_spec(); return; } @@ -716,7 +709,7 @@ void ResourceDispatcherHost::BeginDownload( request_id_--; - scoped_refptr<ResourceHandler> handler = + scoped_refptr<ResourceHandler> handler( new DownloadResourceHandler(this, child_id, route_id, @@ -725,7 +718,7 @@ void ResourceDispatcherHost::BeginDownload( download_file_manager_.get(), request, prompt_for_save_location, - save_info); + save_info)); if (safe_browsing_->enabled()) { handler = CreateSafeBrowsingResourceHandler(handler, child_id, route_id, @@ -733,8 +726,8 @@ void ResourceDispatcherHost::BeginDownload( } if (!URLRequest::IsHandledURL(url)) { - LOG(INFO) << "Download request for unsupported protocol: " << - url.possibly_invalid_spec(); + VLOG(1) << "Download request for unsupported protocol: " + << url.possibly_invalid_spec(); return; } @@ -766,11 +759,11 @@ void ResourceDispatcherHost::BeginSaveFile(const GURL& url, // requests. Does nothing if they are already loaded. PluginService::GetInstance()->LoadChromePlugins(this); - scoped_refptr<ResourceHandler> handler = + scoped_refptr<ResourceHandler> handler( new SaveFileResourceHandler(child_id, route_id, url, - save_file_manager_.get()); + save_file_manager_.get())); request_id_--; bool known_proto = URLRequest::IsHandledURL(url); @@ -876,7 +869,7 @@ void ResourceDispatcherHost::PauseRequest(int child_id, } info->set_pause_count(pause_count); - RESOURCE_LOG("To pause (" << pause << "): " << i->second->url().spec()); + VLOG(1) << "To pause (" << pause << "): " << i->second->url().spec(); // If we're resuming, kick the request to start reading again. Run the read // asynchronously to avoid recursion problems. @@ -1006,7 +999,7 @@ void ResourceDispatcherHost::RemovePendingRequest( void ResourceDispatcherHost::OnReceivedRedirect(URLRequest* request, const GURL& new_url, bool* defer_redirect) { - RESOURCE_LOG("OnReceivedRedirect: " << request->url().spec()); + VLOG(1) << "OnReceivedRedirect: " << request->url().spec(); ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); DCHECK(request->status().is_success()); @@ -1014,8 +1007,8 @@ void ResourceDispatcherHost::OnReceivedRedirect(URLRequest* request, if (info->process_type() != ChildProcessInfo::PLUGIN_PROCESS && !ChildProcessSecurityPolicy::GetInstance()-> CanRequestURL(info->child_id(), new_url)) { - LOG(INFO) << "Denied unauthorized request for " << - new_url.possibly_invalid_spec(); + VLOG(1) << "Denied unauthorized request for " + << new_url.possibly_invalid_spec(); // Tell the renderer that this request was disallowed. CancelRequestInternal(request, false); @@ -1032,7 +1025,7 @@ void ResourceDispatcherHost::OnReceivedRedirect(URLRequest* request, return; } - scoped_refptr<ResourceResponse> response = new ResourceResponse; + scoped_refptr<ResourceResponse> response(new ResourceResponse); PopulateResourceResponse(request, info->replace_extension_localization_templates(), response); if (!info->resource_handler()->OnRequestRedirected(info->request_id(), @@ -1086,7 +1079,7 @@ void ResourceDispatcherHost::OnSSLCertificateError( void ResourceDispatcherHost::OnSetCookie(URLRequest* request, const std::string& cookie_line, bool blocked_by_policy) { - RESOURCE_LOG("OnSetCookie: " << request->url().spec()); + VLOG(1) << "OnSetCookie: " << request->url().spec(); int render_process_id, render_view_id; if (!RenderViewForRequest(request, &render_process_id, &render_view_id)) @@ -1099,10 +1092,10 @@ void ResourceDispatcherHost::OnSetCookie(URLRequest* request, } void ResourceDispatcherHost::OnResponseStarted(URLRequest* request) { - RESOURCE_LOG("OnResponseStarted: " << request->url().spec()); + VLOG(1) << "OnResponseStarted: " << request->url().spec(); ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); if (PauseRequestIfNeeded(info)) { - RESOURCE_LOG("OnResponseStarted pausing: " << request->url().spec()); + VLOG(1) << "OnResponseStarted pausing: " << request->url().spec(); return; } @@ -1118,7 +1111,7 @@ void ResourceDispatcherHost::OnResponseStarted(URLRequest* request) { } else { // Check if the handler paused the request in their OnResponseStarted. if (PauseRequestIfNeeded(info)) { - RESOURCE_LOG("OnResponseStarted pausing2: " << request->url().spec()); + VLOG(1) << "OnResponseStarted pausing2: " << request->url().spec(); return; } @@ -1132,7 +1125,7 @@ void ResourceDispatcherHost::OnResponseStarted(URLRequest* request) { bool ResourceDispatcherHost::CompleteResponseStarted(URLRequest* request) { ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); - scoped_refptr<ResourceResponse> response = new ResourceResponse; + scoped_refptr<ResourceResponse> response(new ResourceResponse); PopulateResourceResponse(request, info->replace_extension_localization_templates(), response); @@ -1176,7 +1169,7 @@ void ResourceDispatcherHost::CancelRequest(int child_id, void ResourceDispatcherHost::CancelRequestInternal(URLRequest* request, bool from_renderer) { - RESOURCE_LOG("CancelRequest: " << request->url().spec()); + VLOG(1) << "CancelRequest: " << request->url().spec(); // WebKit will send us a cancel for downloads since it no longer handles them. // In this case, ignore the cancel since we handle downloads in the browser. @@ -1347,7 +1340,7 @@ void ResourceDispatcherHost::ResumeRequest(const GlobalRequestID& request_id) { if (!info->is_paused()) return; - RESOURCE_LOG("Resuming: " << i->second->url().spec()); + VLOG(1) << "Resuming: " << i->second->url().spec(); info->set_is_paused(false); @@ -1395,7 +1388,7 @@ bool ResourceDispatcherHost::Read(URLRequest* request, int* bytes_read) { void ResourceDispatcherHost::OnReadCompleted(URLRequest* request, int bytes_read) { DCHECK(request); - RESOURCE_LOG("OnReadCompleted: " << request->url().spec()); + VLOG(1) << "OnReadCompleted: " << request->url().spec(); ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); // OnReadCompleted can be called without Read (e.g., for chrome:// URLs). @@ -1404,7 +1397,7 @@ void ResourceDispatcherHost::OnReadCompleted(URLRequest* request, if (PauseRequestIfNeeded(info)) { info->set_paused_read_bytes(bytes_read); - RESOURCE_LOG("OnReadCompleted pausing: " << request->url().spec()); + VLOG(1) << "OnReadCompleted pausing: " << request->url().spec(); return; } @@ -1434,8 +1427,8 @@ void ResourceDispatcherHost::OnReadCompleted(URLRequest* request, if (PauseRequestIfNeeded(info)) { info->set_paused_read_bytes(bytes_read); - RESOURCE_LOG("OnReadCompleted (CompleteRead) pausing: " << - request->url().spec()); + VLOG(1) << "OnReadCompleted (CompleteRead) pausing: " + << request->url().spec(); return; } @@ -1463,7 +1456,7 @@ bool ResourceDispatcherHost::CompleteRead(URLRequest* request, } void ResourceDispatcherHost::OnResponseCompleted(URLRequest* request) { - RESOURCE_LOG("OnResponseCompleted: " << request->url().spec()); + VLOG(1) << "OnResponseCompleted: " << request->url().spec(); ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); // If the load for a main frame has failed, track it in a histogram, diff --git a/chrome/browser/renderer_host/resource_dispatcher_host_uitest.cc b/chrome/browser/renderer_host/resource_dispatcher_host_uitest.cc index bc4ac2b..67cbe92 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host_uitest.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host_uitest.cc @@ -191,10 +191,6 @@ TEST_F(ResourceDispatcherTest, CrossSiteOnunloadCookie) { } #if !defined(OS_MACOSX) -#if defined(OS_WIN) -// http://crbug.com/32048 -#define CrossSiteAfterCrash FLAKY_CrossSiteAfterCrash -#endif // Tests that the onbeforeunload and onunload logic is shortcutted if the old // renderer is gone. In that case, we don't want to wait for the old renderer // to run the handlers. diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index cea0c94..fc8cc12 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -4,13 +4,7 @@ #include "chrome/browser/renderer_host/resource_message_filter.h" -#include "app/clipboard/clipboard.h" -#include "base/callback.h" #include "base/command_line.h" -#if defined(OS_POSIX) -#include "base/file_descriptor_posix.h" -#endif -#include "base/file_path.h" #include "base/file_util.h" #include "base/metrics/histogram.h" #include "base/process_util.h" @@ -21,18 +15,17 @@ #include "base/worker_pool.h" #include "chrome/browser/appcache/appcache_dispatcher_host.h" #include "chrome/browser/automation/automation_resource_message_filter.h" -#include "chrome/browser/browser_about_handler.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/child_process_security_policy.h" #include "chrome/browser/chrome_plugin_browsing_context.h" #include "chrome/browser/clipboard_dispatcher.h" #include "chrome/browser/device_orientation/dispatcher_host.h" -#include "chrome/browser/download/download_file.h" +#include "chrome/browser/download/download_types.h" #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/file_system/file_system_dispatcher_host.h" #include "chrome/browser/file_system/file_system_host_context.h" -#include "chrome/browser/geolocation/geolocation_dispatcher_host.h" +#include "chrome/browser/geolocation/geolocation_dispatcher_host_old.h" #include "chrome/browser/geolocation/geolocation_permission_context.h" #include "chrome/browser/gpu_process_host.h" #include "chrome/browser/host_zoom_map.h" @@ -44,20 +37,18 @@ #include "chrome/browser/net/predictor_api.h" #include "chrome/browser/notifications/desktop_notification_service.h" #include "chrome/browser/notifications/notifications_prefs_cache.h" -#if defined(OS_CHROMEOS) -#include "chrome/browser/chromeos/plugin_selection_policy.h" -#endif +#include "chrome/browser/platform_util.h" #include "chrome/browser/plugin_service.h" -#include "chrome/browser/plugin_updater.h" -#include "chrome/browser/prefs/pref_service.h" -#include "chrome/browser/printing/print_job_manager.h" +#include "chrome/browser/plugin_process_host.h" #include "chrome/browser/printing/printer_query.h" +#include "chrome/browser/printing/print_job_manager.h" #include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/audio_renderer_host.h" #include "chrome/browser/renderer_host/blob_dispatcher_host.h" #include "chrome/browser/renderer_host/browser_render_process_host.h" #include "chrome/browser/renderer_host/database_dispatcher_host.h" #include "chrome/browser/renderer_host/file_utilities_dispatcher_host.h" +#include "chrome/browser/renderer_host/render_view_host_delegate.h" #include "chrome/browser/renderer_host/render_view_host_notification_task.h" #include "chrome/browser/renderer_host/render_widget_helper.h" #include "chrome/browser/search_engines/search_provider_install_state_dispatcher_host.h" @@ -67,35 +58,22 @@ #include "chrome/browser/ui_thread_helpers.h" #include "chrome/browser/worker_host/message_port_dispatcher.h" #include "chrome/browser/worker_host/worker_service.h" -#include "chrome/common/child_process_host.h" -#include "chrome/common/chrome_plugin_lib.h" -#include "chrome/common/chrome_plugin_util.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_file_util.h" #include "chrome/common/extensions/extension_message_bundle.h" -#if defined(OS_MACOSX) -#include "chrome/common/font_descriptor_mac.h" -#include "chrome/common/font_loader_mac.h" -#endif #include "chrome/common/notification_service.h" -#include "chrome/common/pref_names.h" #include "chrome/common/render_messages.h" #include "chrome/common/render_messages_params.h" #include "chrome/common/url_constants.h" -#include "chrome/common/worker_messages.h" -#include "gfx/native_widget_types.h" +#include "ipc/ipc_channel_handle.h" #include "net/base/cookie_monster.h" -#include "net/base/file_stream.h" #include "net/base/io_buffer.h" #include "net/base/keygen_handler.h" -#include "net/base/load_flags.h" #include "net/base/mime_util.h" #include "net/base/net_errors.h" #include "net/disk_cache/disk_cache.h" #include "net/http/http_cache.h" #include "net/http/http_network_layer.h" -#include "net/http/http_transaction_factory.h" #include "net/url_request/url_request_context.h" #include "third_party/WebKit/WebKit/chromium/public/WebNotificationPresenter.h" #include "webkit/glue/context_menu.h" @@ -105,6 +83,23 @@ #include "webkit/glue/webcookie.h" #include "webkit/glue/webkit_glue.h" +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/plugin_selection_policy.h" +#endif +#if defined(OS_MACOSX) +#include "chrome/common/font_descriptor_mac.h" +#include "chrome/common/font_loader_mac.h" +#endif +#if defined(OS_POSIX) +#include "base/file_descriptor_posix.h" +#endif +#if defined(OS_WIN) +#include "chrome/common/child_process_host.h" +#endif +#if defined(USE_TCMALLOC) +#include "chrome/browser/browser_about_handler.h" +#endif + using net::CookieStore; using WebKit::WebCache; @@ -200,6 +195,48 @@ class ClearCacheCompletion : public net::CompletionCallback { scoped_refptr<ResourceMessageFilter> filter_; }; +class OpenChannelToPluginCallback : public PluginProcessHost::Client { + public: + OpenChannelToPluginCallback(ResourceMessageFilter* filter, + IPC::Message* reply_msg) + : filter_(filter), + reply_msg_(reply_msg) { + } + + virtual int ID() { + return filter_->id(); + } + + virtual bool OffTheRecord() { + return filter_->off_the_record(); + } + + virtual void SetPluginInfo(const WebPluginInfo& info) { + info_ = info; + } + + virtual void OnChannelOpened(const IPC::ChannelHandle& handle) { + WriteReply(handle); + } + + virtual void OnError() { + WriteReply(IPC::ChannelHandle()); + } + + private: + void WriteReply(const IPC::ChannelHandle& handle) { + ViewHostMsg_OpenChannelToPlugin::WriteReplyParams(reply_msg_, + handle, + info_); + filter_->Send(reply_msg_); + delete this; + } + + scoped_refptr<ResourceMessageFilter> filter_; + IPC::Message* reply_msg_; + WebPluginInfo info_; +}; + } // namespace ResourceMessageFilter::ResourceMessageFilter( @@ -239,7 +276,7 @@ ResourceMessageFilter::ResourceMessageFilter( ALLOW_THIS_IN_INITIALIZER_LIST(speech_input_dispatcher_host_( new speech_input::SpeechInputDispatcherHost(this->id()))), ALLOW_THIS_IN_INITIALIZER_LIST(geolocation_dispatcher_host_( - GeolocationDispatcherHost::New( + GeolocationDispatcherHostOld::New( this->id(), profile->GetGeolocationPermissionContext()))), ALLOW_THIS_IN_INITIALIZER_LIST( search_provider_install_state_dispatcher_host_( @@ -463,6 +500,7 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& msg) { OnCheckNotificationPermission) IPC_MESSAGE_HANDLER(ViewHostMsg_GetMimeTypeFromExtension, OnGetMimeTypeFromExtension) + IPC_MESSAGE_HANDLER(ViewHostMsg_RevealFolderInOS, OnRevealFolderInOS) IPC_MESSAGE_HANDLER(ViewHostMsg_GetMimeTypeFromFile, OnGetMimeTypeFromFile) IPC_MESSAGE_HANDLER(ViewHostMsg_GetPreferredExtensionForMimeType, @@ -532,7 +570,27 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& msg) { return handled; } -void ResourceMessageFilter::OnDestruct() { +void ResourceMessageFilter::OnRevealFolderInOS(const FilePath& path) { +#if defined(OS_MACOSX) + const BrowserThread::ID kThreadID = BrowserThread::UI; +#else + const BrowserThread::ID kThreadID = BrowserThread::FILE; +#endif + if (!BrowserThread::CurrentlyOn(kThreadID)) { + // Only honor the request if appropriate persmissions are granted. + if (ChildProcessSecurityPolicy::GetInstance()->CanReadFile(id(), path)) + BrowserThread::PostTask( + kThreadID, FROM_HERE, + NewRunnableMethod( + this, &ResourceMessageFilter::OnRevealFolderInOS, path)); + return; + } + + DCHECK(BrowserThread::CurrentlyOn(kThreadID)); + platform_util::OpenItem(path); +} + +void ResourceMessageFilter::OnDestruct() const { BrowserThread::DeleteOnIOThread::Destruct(this); } @@ -787,16 +845,18 @@ void ResourceMessageFilter::OnGetPluginInfoOnFileThread( } void ResourceMessageFilter::OnGotPluginInfo(bool found, - WebPluginInfo info, + const WebPluginInfo& info, const std::string& actual_mime_type, const GURL& policy_url, IPC::Message* reply_msg) { ContentSetting setting = CONTENT_SETTING_DEFAULT; if (found) { - info.enabled = info.enabled && - plugin_service_->PrivatePluginAllowedForURL(info.path, policy_url); + WebPluginInfo info_copy = info; + info_copy.enabled = info_copy.enabled && + plugin_service_->PrivatePluginAllowedForURL(info_copy.path, policy_url); HostContentSettingsMap* map = profile_->GetHostContentSettingsMap(); - scoped_ptr<PluginGroup> group(PluginGroup::CopyOrCreatePluginGroup(info)); + scoped_ptr<PluginGroup> group( + PluginGroup::CopyOrCreatePluginGroup(info_copy)); std::string resource = group->identifier(); setting = map->GetContentSetting(policy_url, CONTENT_SETTINGS_TYPE_PLUGINS, @@ -811,7 +871,10 @@ void ResourceMessageFilter::OnGotPluginInfo(bool found, void ResourceMessageFilter::OnOpenChannelToPlugin(const GURL& url, const std::string& mime_type, IPC::Message* reply_msg) { - plugin_service_->OpenChannelToPlugin(this, url, mime_type, reply_msg); + plugin_service_->OpenChannelToPlugin( + url, + mime_type, + new OpenChannelToPluginCallback(this, reply_msg)); } void ResourceMessageFilter::OnLaunchNaCl( @@ -1050,8 +1113,7 @@ void ResourceMessageFilter::OnAllocateSharedMemoryBuffer( uint32 buffer_size, base::SharedMemoryHandle* handle) { base::SharedMemory shared_buf; - shared_buf.Create("", false, false, buffer_size); - if (!shared_buf.Map(buffer_size)) { + if (!shared_buf.CreateAndMapAnonymous(buffer_size)) { *handle = base::SharedMemory::NULLHandle(); NOTREACHED() << "Cannot map shared memory buffer"; return; @@ -1083,7 +1145,7 @@ void ResourceMessageFilter::OnResourceTypeStats( } void ResourceMessageFilter::OnResourceTypeStatsOnUIThread( - WebCache::ResourceTypeStats stats, base::ProcessId renderer_id) { + const WebCache::ResourceTypeStats& stats, base::ProcessId renderer_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); TaskManager::GetInstance()->model()->NotifyResourceTypeStats( renderer_id, stats); @@ -1463,7 +1525,7 @@ void ResourceMessageFilter::OnCacheableMetadataAvailable( http_transaction_factory()->GetCache(); DCHECK(cache); - scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(data.size()); + scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(data.size())); memcpy(buf->data(), &data.front(), data.size()); cache->WriteMetadata( url, base::Time::FromDoubleT(expected_response_time), buf, data.size()); @@ -1500,7 +1562,7 @@ void ResourceMessageFilter::OnKeygen(uint32 key_size_index, return; } - LOG(INFO) << "Dispatching keygen task to worker pool."; + VLOG(1) << "Dispatching keygen task to worker pool."; // Dispatch to worker pool, so we do not block the IO thread. if (!WorkerPool::PostTask( FROM_HERE, diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index 97d3992..850306e 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -39,7 +39,7 @@ class DOMStorageDispatcherHost; class FileSystemDispatcherHost; class FileUtilitiesDispatcherHost; struct FontDescriptor; -class GeolocationDispatcherHost; +class GeolocationDispatcherHostOld; class HostZoomMap; class IndexedDBDispatcherHost; class NotificationsPrefsCache; @@ -107,7 +107,7 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, virtual void OnChannelError(); virtual void OnChannelClosing(); virtual bool OnMessageReceived(const IPC::Message& message); - virtual void OnDestruct(); + virtual void OnDestruct() const; // ResourceDispatcherHost::Receiver methods: virtual bool Send(IPC::Message* message); @@ -188,7 +188,7 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, const std::string& mime_type, IPC::Message* reply_msg); void OnGotPluginInfo(bool found, - WebPluginInfo info, + const WebPluginInfo& info, const std::string& actual_mime_type, const GURL& policy_url, IPC::Message* reply_msg); @@ -252,6 +252,8 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, void OnGetWindowRect(gfx::NativeViewId window, IPC::Message* reply); void OnGetRootWindowRect(gfx::NativeViewId window, IPC::Message* reply); #endif + + void OnRevealFolderInOS(const FilePath& path); void OnGetMimeTypeFromExtension(const FilePath::StringType& ext, std::string* mime_type); void OnGetMimeTypeFromFile(const FilePath& file_path, @@ -282,8 +284,9 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, #endif void OnResourceTypeStats(const WebKit::WebCache::ResourceTypeStats& stats); - static void OnResourceTypeStatsOnUIThread(WebKit::WebCache::ResourceTypeStats, - base::ProcessId renderer_id); + static void OnResourceTypeStatsOnUIThread( + const WebKit::WebCache::ResourceTypeStats&, + base::ProcessId renderer_id); void OnV8HeapStats(int v8_memory_allocated, int v8_memory_used); static void OnV8HeapStatsOnUIThread(int v8_memory_allocated, @@ -479,7 +482,7 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, speech_input_dispatcher_host_; // Used to handle geolocation-related messages. - scoped_refptr<GeolocationDispatcherHost> geolocation_dispatcher_host_; + scoped_refptr<GeolocationDispatcherHostOld> geolocation_dispatcher_host_; // Used to handle search provider related messages. scoped_ptr<SearchProviderInstallStateDispatcherHost> diff --git a/chrome/browser/renderer_host/resource_request_details.cc b/chrome/browser/renderer_host/resource_request_details.cc new file mode 100644 index 0000000..20a255d --- /dev/null +++ b/chrome/browser/renderer_host/resource_request_details.cc @@ -0,0 +1,54 @@ +// 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/renderer_host/resource_request_details.h" + + +ResourceRequestDetails::ResourceRequestDetails(const URLRequest* request, + int cert_id) + : url_(request->url()), + original_url_(request->original_url()), + method_(request->method()), + referrer_(request->referrer()), + has_upload_(request->has_upload()), + load_flags_(request->load_flags()), + status_(request->status()), + ssl_cert_id_(cert_id), + ssl_cert_status_(request->ssl_info().cert_status) { + const ResourceDispatcherHostRequestInfo* info = + ResourceDispatcherHost::InfoForRequest(request); + DCHECK(info); + resource_type_ = info->resource_type(); + frame_origin_ = info->frame_origin(); + main_frame_origin_ = info->main_frame_origin(); + + // If request is from the worker process on behalf of a renderer, use + // the renderer process id, since it consumes the notification response + // such as ssl state etc. + const WorkerProcessHost::WorkerInstance* worker_instance = + WorkerService::GetInstance()->FindWorkerInstance(info->child_id()); + if (worker_instance) { + DCHECK(!worker_instance->worker_document_set()->IsEmpty()); + const WorkerDocumentSet::DocumentInfoSet& parents = + worker_instance->worker_document_set()->documents(); + // TODO(atwilson): need to notify all associated renderers in the case + // of ssl state change (http://crbug.com/25357). For now, just notify + // the first one (works for dedicated workers and shared workers with + // a single process). + origin_child_id_ = parents.begin()->renderer_id(); + } else { + origin_child_id_ = info->child_id(); + } +} + +ResourceRequestDetails::~ResourceRequestDetails() {} + +ResourceRedirectDetails::ResourceRedirectDetails(const URLRequest* request, + int cert_id, + const GURL& new_url) + : ResourceRequestDetails(request, cert_id), + new_url_(new_url) { +} + +ResourceRedirectDetails::~ResourceRedirectDetails() {} diff --git a/chrome/browser/renderer_host/resource_request_details.h b/chrome/browser/renderer_host/resource_request_details.h index 6af9f85..36f3456 100644 --- a/chrome/browser/renderer_host/resource_request_details.h +++ b/chrome/browser/renderer_host/resource_request_details.h @@ -24,43 +24,9 @@ class URLRequest; // Details about a resource request notification. class ResourceRequestDetails { public: - ResourceRequestDetails(const URLRequest* request, int cert_id) - : url_(request->url()), - original_url_(request->original_url()), - method_(request->method()), - referrer_(request->referrer()), - has_upload_(request->has_upload()), - load_flags_(request->load_flags()), - status_(request->status()), - ssl_cert_id_(cert_id), - ssl_cert_status_(request->ssl_info().cert_status) { - const ResourceDispatcherHostRequestInfo* info = - ResourceDispatcherHost::InfoForRequest(request); - DCHECK(info); - resource_type_ = info->resource_type(); - frame_origin_ = info->frame_origin(); - main_frame_origin_ = info->main_frame_origin(); + ResourceRequestDetails(const URLRequest* request, int cert_id); - // If request is from the worker process on behalf of a renderer, use - // the renderer process id, since it consumes the notification response - // such as ssl state etc. - const WorkerProcessHost::WorkerInstance* worker_instance = - WorkerService::GetInstance()->FindWorkerInstance(info->child_id()); - if (worker_instance) { - DCHECK(!worker_instance->worker_document_set()->IsEmpty()); - const WorkerDocumentSet::DocumentInfoSet& parents = - worker_instance->worker_document_set()->documents(); - // TODO(atwilson): need to notify all associated renderers in the case - // of ssl state change (http://crbug.com/25357). For now, just notify - // the first one (works for dedicated workers and shared workers with - // a single process). - origin_child_id_ = parents.begin()->renderer_id(); - } else { - origin_child_id_ = info->child_id(); - } - } - - virtual ~ResourceRequestDetails() {} + virtual ~ResourceRequestDetails(); const GURL& url() const { return url_; } const GURL& original_url() const { return original_url_; } @@ -97,9 +63,8 @@ class ResourceRedirectDetails : public ResourceRequestDetails { public: ResourceRedirectDetails(const URLRequest* request, int cert_id, - const GURL& new_url) - : ResourceRequestDetails(request, cert_id), - new_url_(new_url) {} + const GURL& new_url); + virtual ~ResourceRedirectDetails(); // The URL to which we are being redirected. const GURL& new_url() const { return new_url_; } diff --git a/chrome/browser/renderer_host/save_file_resource_handler.cc b/chrome/browser/renderer_host/save_file_resource_handler.cc index 38f83bd..d33e205 100644 --- a/chrome/browser/renderer_host/save_file_resource_handler.cc +++ b/chrome/browser/renderer_host/save_file_resource_handler.cc @@ -80,8 +80,8 @@ bool SaveFileResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, bool SaveFileResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { DCHECK(read_buffer_); // We are passing ownership of this buffer to the save file manager. - net::IOBuffer* buffer = NULL; - read_buffer_.swap(&buffer); + scoped_refptr<net::IOBuffer> buffer; + read_buffer_.swap(buffer); BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod(save_manager_, @@ -115,3 +115,5 @@ void SaveFileResourceHandler::set_content_length( const std::string& content_length) { base::StringToInt64(content_length, &content_length_); } + +SaveFileResourceHandler::~SaveFileResourceHandler() {} diff --git a/chrome/browser/renderer_host/save_file_resource_handler.h b/chrome/browser/renderer_host/save_file_resource_handler.h index ecd8a93..7475bef 100644 --- a/chrome/browser/renderer_host/save_file_resource_handler.h +++ b/chrome/browser/renderer_host/save_file_resource_handler.h @@ -21,32 +21,33 @@ class SaveFileResourceHandler : public ResourceHandler { const GURL& url, SaveFileManager* manager); - bool OnUploadProgress(int request_id, uint64 position, uint64 size); + // ResourceHandler Implementation: + virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size); // Saves the redirected URL to final_url_, we need to use the original // URL to match original request. - bool OnRequestRedirected(int request_id, const GURL& url, - ResourceResponse* response, bool* defer); + virtual bool OnRequestRedirected(int request_id, const GURL& url, + ResourceResponse* response, bool* defer); // Sends the download creation information to the download thread. - bool OnResponseStarted(int request_id, ResourceResponse* response); + virtual bool OnResponseStarted(int request_id, ResourceResponse* response); // Pass-through implementation. - bool OnWillStart(int request_id, const GURL& url, bool* defer); + virtual bool OnWillStart(int request_id, const GURL& url, bool* defer); // Creates a new buffer, which will be handed to the download thread for file // writing and deletion. - bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, - int min_size); + virtual bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, + int min_size); // Passes the buffer to the download file writer. - bool OnReadCompleted(int request_id, int* bytes_read); + virtual bool OnReadCompleted(int request_id, int* bytes_read); - bool OnResponseCompleted(int request_id, - const URLRequestStatus& status, - const std::string& security_info); + virtual bool OnResponseCompleted(int request_id, + const URLRequestStatus& status, + const std::string& security_info); - void OnRequestClosed(); + virtual void OnRequestClosed(); // If the content-length header is not present (or contains something other // than numbers), StringToInt64 returns 0, which indicates 'unknown size' and @@ -58,7 +59,7 @@ class SaveFileResourceHandler : public ResourceHandler { } private: - ~SaveFileResourceHandler() {} + virtual ~SaveFileResourceHandler(); int save_id_; int render_process_id_; diff --git a/chrome/browser/renderer_host/site_instance.cc b/chrome/browser/renderer_host/site_instance.cc index b0f4386..c6a660d 100644 --- a/chrome/browser/renderer_host/site_instance.cc +++ b/chrome/browser/renderer_host/site_instance.cc @@ -190,7 +190,7 @@ GURL SiteInstance::GetEffectiveURL(Profile* profile, const GURL& url) { if (!profile || !profile->GetExtensionsService()) return url; - Extension* extension = + const Extension* extension = profile->GetExtensionsService()->GetExtensionByWebExtent(url); if (extension) { // If the URL is part of an extension's web extent, convert it to an diff --git a/chrome/browser/renderer_host/socket_stream_dispatcher_host.cc b/chrome/browser/renderer_host/socket_stream_dispatcher_host.cc index b99e70d..6fa3fb8 100644 --- a/chrome/browser/renderer_host/socket_stream_dispatcher_host.cc +++ b/chrome/browser/renderer_host/socket_stream_dispatcher_host.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -69,9 +69,8 @@ void SocketStreamDispatcherHost::OnConnected(net::SocketStream* socket, SocketStreamHost::GetSocketStreamHost(socket); DCHECK(socket_stream_host); int socket_id = socket_stream_host->socket_id(); - DLOG(INFO) << "SocketStreamDispatcherHost::OnConnected socket_id=" - << socket_id - << " max_pending_send_allowed=" << max_pending_send_allowed; + DVLOG(1) << "SocketStreamDispatcherHost::OnConnected socket_id=" << socket_id + << " max_pending_send_allowed=" << max_pending_send_allowed; if (socket_id == chrome_common_net::kNoSocketId) { LOG(ERROR) << "NoSocketId in OnConnected"; return; @@ -88,9 +87,8 @@ void SocketStreamDispatcherHost::OnSentData(net::SocketStream* socket, SocketStreamHost::GetSocketStreamHost(socket); DCHECK(socket_stream_host); int socket_id = socket_stream_host->socket_id(); - DLOG(INFO) << "SocketStreamDispatcherHost::OnSentData socket_id=" - << socket_id - << " amount_sent=" << amount_sent; + DVLOG(1) << "SocketStreamDispatcherHost::OnSentData socket_id=" << socket_id + << " amount_sent=" << amount_sent; if (socket_id == chrome_common_net::kNoSocketId) { LOG(ERROR) << "NoSocketId in OnReceivedData"; return; @@ -107,8 +105,8 @@ void SocketStreamDispatcherHost::OnReceivedData( SocketStreamHost::GetSocketStreamHost(socket); DCHECK(socket_stream_host); int socket_id = socket_stream_host->socket_id(); - DLOG(INFO) << "SocketStreamDispatcherHost::OnReceiveData socket_id=" - << socket_id; + DVLOG(1) << "SocketStreamDispatcherHost::OnReceiveData socket_id=" + << socket_id; if (socket_id == chrome_common_net::kNoSocketId) { LOG(ERROR) << "NoSocketId in OnReceivedData"; return; @@ -124,8 +122,7 @@ void SocketStreamDispatcherHost::OnClose(net::SocketStream* socket) { SocketStreamHost::GetSocketStreamHost(socket); DCHECK(socket_stream_host); int socket_id = socket_stream_host->socket_id(); - DLOG(INFO) << "SocketStreamDispatcherHost::OnClosed socket_id=" - << socket_id; + DVLOG(1) << "SocketStreamDispatcherHost::OnClosed socket_id=" << socket_id; if (socket_id == chrome_common_net::kNoSocketId) { LOG(ERROR) << "NoSocketId in OnClose"; return; @@ -135,8 +132,8 @@ void SocketStreamDispatcherHost::OnClose(net::SocketStream* socket) { // Message handlers called by OnMessageReceived. void SocketStreamDispatcherHost::OnConnect(const GURL& url, int socket_id) { - DLOG(INFO) << "SocketStreamDispatcherHost::OnConnect url=" << url - << " socket_id=" << socket_id; + DVLOG(1) << "SocketStreamDispatcherHost::OnConnect url=" << url + << " socket_id=" << socket_id; DCHECK_NE(chrome_common_net::kNoSocketId, socket_id); DCHECK(receiver_); if (LookupHostMap(receiver_->id(), socket_id)) { @@ -148,13 +145,12 @@ void SocketStreamDispatcherHost::OnConnect(const GURL& url, int socket_id) { new SocketStreamHost(this, receiver_, socket_id); AddHostMap(receiver_->id(), socket_id, socket_stream_host); socket_stream_host->Connect(url); - DLOG(INFO) << "SocketStreamDispatcherHost::OnConnect -> " << socket_id; + DVLOG(1) << "SocketStreamDispatcherHost::OnConnect -> " << socket_id; } void SocketStreamDispatcherHost::OnSendData( int socket_id, const std::vector<char>& data) { - DLOG(INFO) << "SocketStreamDispatcherHost::OnSendData socket_id=" - << socket_id; + DVLOG(1) << "SocketStreamDispatcherHost::OnSendData socket_id=" << socket_id; DCHECK(receiver_); SocketStreamHost* socket_stream_host = LookupHostMap(receiver_->id(), socket_id); @@ -170,8 +166,7 @@ void SocketStreamDispatcherHost::OnSendData( } void SocketStreamDispatcherHost::OnCloseReq(int socket_id) { - DLOG(INFO) << "SocketStreamDispatcherHost::OnCloseReq socket_id=" - << socket_id; + DVLOG(1) << "SocketStreamDispatcherHost::OnCloseReq socket_id=" << socket_id; DCHECK(receiver_); SocketStreamHost* socket_stream_host = LookupHostMap(receiver_->id(), socket_id); diff --git a/chrome/browser/renderer_host/socket_stream_host.cc b/chrome/browser/renderer_host/socket_stream_host.cc index 51f7ec3..a6f8ddd 100644 --- a/chrome/browser/renderer_host/socket_stream_host.cc +++ b/chrome/browser/renderer_host/socket_stream_host.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -31,7 +31,7 @@ SocketStreamHost::SocketStreamHost( receiver_(receiver), socket_id_(socket_id) { DCHECK_NE(socket_id_, chrome_common_net::kNoSocketId); - LOG(INFO) << "SocketStreamHost: socket_id=" << socket_id_; + VLOG(1) << "SocketStreamHost: socket_id=" << socket_id_; } /* static */ @@ -46,15 +46,14 @@ SocketStreamHost::GetSocketStreamHost(net::SocketStream* socket) { } SocketStreamHost::~SocketStreamHost() { - LOG(INFO) << "SocketStreamHost destructed socket_id=" << socket_id_; - if (!receiver_->Send(new ViewMsg_SocketStream_Closed(socket_id_))) { + VLOG(1) << "SocketStreamHost destructed socket_id=" << socket_id_; + if (!receiver_->Send(new ViewMsg_SocketStream_Closed(socket_id_))) LOG(ERROR) << "ViewMsg_SocketStream_Closed failed."; - } socket_->DetachDelegate(); } void SocketStreamHost::Connect(const GURL& url) { - LOG(INFO) << "SocketStreamHost::Connect url=" << url; + VLOG(1) << "SocketStreamHost::Connect url=" << url; socket_ = net::SocketStreamJob::CreateSocketStreamJob(url, delegate_); URLRequestContextGetter* context_getter = Profile::GetDefaultRequestContext(); if (context_getter) @@ -64,17 +63,15 @@ void SocketStreamHost::Connect(const GURL& url) { } bool SocketStreamHost::SendData(const std::vector<char>& data) { - LOG(INFO) << "SocketStreamHost::SendData"; - if (!socket_) - return false; - return socket_->SendData(&data[0], data.size()); + VLOG(1) << "SocketStreamHost::SendData"; + return socket_ && socket_->SendData(&data[0], data.size()); } void SocketStreamHost::Close() { - LOG(INFO) << "SocketStreamHost::Close"; + VLOG(1) << "SocketStreamHost::Close"; if (!socket_) return; - return socket_->Close(); + socket_->Close(); } bool SocketStreamHost::Connected(int max_pending_send_allowed) { diff --git a/chrome/browser/renderer_host/test/render_view_host_manager_browsertest.cc b/chrome/browser/renderer_host/test/render_view_host_manager_browsertest.cc index 8fa9b11..2e7c56b 100644 --- a/chrome/browser/renderer_host/test/render_view_host_manager_browsertest.cc +++ b/chrome/browser/renderer_host/test/render_view_host_manager_browsertest.cc @@ -5,6 +5,7 @@ #include "base/file_util.h" #include "base/path_service.h" #include "base/ref_counted.h" +#include "base/stringprintf.h" #include "chrome/browser/browser.h" #include "chrome/browser/download/download_manager.h" #include "chrome/browser/extensions/extension_error_reporter.h" @@ -28,13 +29,28 @@ class RenderViewHostManagerTest : public InProcessBrowserTest { RenderViewHostManagerTest() { EnableDOMAutomation(); } + + std::string GetFileWithHostAndPortReplacement( + const std::string& original_path, + const net::HostPortPair& host_port_pair) const { + return StringPrintf("%s?replace_orig=%s&replace_new=%s", + original_path.c_str(), + kReplaceText_, + host_port_pair.ToString().c_str()); + } + + private: + static const char* const kReplaceText_; }; +// static +const char* const RenderViewHostManagerTest::kReplaceText_ = + "REPLACE_WITH_HOST_AND_PORT"; + // Test for crbug.com/24447. Following a cross-site link with rel=noreferrer // and target=_blank should create a new SiteInstance. -// Disabled, http://crbug.com/60079. IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, - DISABLED_SwapProcessWithRelNoreferrerAndTargetBlank) { + SwapProcessWithRelNoreferrerAndTargetBlank) { // Start two servers with different sites. ASSERT_TRUE(test_server()->Start()); net::TestServer https_server_( @@ -43,8 +59,11 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, ASSERT_TRUE(https_server_.Start()); // Load a page with links that open in a new window. - ui_test_utils::NavigateToURL(browser(), test_server()->GetURL( - "files/click-noreferrer-links.html")); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/click-noreferrer-links.html", + https_server_.host_port_pair()); + ui_test_utils::NavigateToURL(browser(), + test_server()->GetURL(replacement_path)); // Get the original SiteInstance for later comparison. scoped_refptr<SiteInstance> orig_site_instance( @@ -76,9 +95,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Test for crbug.com/24447. Following a cross-site link with just // target=_blank should not create a new SiteInstance. -// Disabled, http://crbug.com/60078. IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, - DISABLED_DontSwapProcessWithOnlyTargetBlank) { + DontSwapProcessWithOnlyTargetBlank) { // Start two servers with different sites. ASSERT_TRUE(test_server()->Start()); net::TestServer https_server_( @@ -87,8 +105,11 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, ASSERT_TRUE(https_server_.Start()); // Load a page with links that open in a new window. - ui_test_utils::NavigateToURL(browser(), test_server()->GetURL( - "files/click-noreferrer-links.html")); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/click-noreferrer-links.html", + https_server_.host_port_pair()); + ui_test_utils::NavigateToURL(browser(), + test_server()->GetURL(replacement_path)); // Get the original SiteInstance for later comparison. scoped_refptr<SiteInstance> orig_site_instance( @@ -120,9 +141,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Test for crbug.com/24447. Following a cross-site link with rel=noreferrer // and no target=_blank should not create a new SiteInstance. -// Disabled, http://crbug.com/60077. IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, - DISABLED_DontSwapProcessWithOnlyRelNoreferrer) { + DontSwapProcessWithOnlyRelNoreferrer) { // Start two servers with different sites. ASSERT_TRUE(test_server()->Start()); net::TestServer https_server_( @@ -131,8 +151,11 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, ASSERT_TRUE(https_server_.Start()); // Load a page with links that open in a new window. - ui_test_utils::NavigateToURL(browser(), test_server()->GetURL( - "files/click-noreferrer-links.html")); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/click-noreferrer-links.html", + https_server_.host_port_pair()); + ui_test_utils::NavigateToURL(browser(), + test_server()->GetURL(replacement_path)); // Get the original SiteInstance for later comparison. scoped_refptr<SiteInstance> orig_site_instance( diff --git a/chrome/browser/renderer_host/test/test_backing_store.cc b/chrome/browser/renderer_host/test/test_backing_store.cc index d90c7fd..cf7e7fa 100644 --- a/chrome/browser/renderer_host/test/test_backing_store.cc +++ b/chrome/browser/renderer_host/test/test_backing_store.cc @@ -16,8 +16,7 @@ void TestBackingStore::PaintToBackingStore( RenderProcessHost* process, TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, - const std::vector<gfx::Rect>& copy_rects, - bool* painted_synchronously) { + const std::vector<gfx::Rect>& copy_rects) { } bool TestBackingStore::CopyFromBackingStore(const gfx::Rect& rect, diff --git a/chrome/browser/renderer_host/test/test_backing_store.h b/chrome/browser/renderer_host/test/test_backing_store.h index f9db76e..d8f257b 100644 --- a/chrome/browser/renderer_host/test/test_backing_store.h +++ b/chrome/browser/renderer_host/test/test_backing_store.h @@ -18,8 +18,7 @@ class TestBackingStore : public BackingStore { virtual void PaintToBackingStore(RenderProcessHost* process, TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect, - const std::vector<gfx::Rect>& copy_rects, - bool* painted_synchronously); + const std::vector<gfx::Rect>& copy_rects); virtual bool CopyFromBackingStore(const gfx::Rect& rect, skia::PlatformCanvas* output); virtual void ScrollBackingStore(int dx, int dy, diff --git a/chrome/browser/renderer_host/test/test_render_view_host.cc b/chrome/browser/renderer_host/test/test_render_view_host.cc index ecc2426..f3988e1 100644 --- a/chrome/browser/renderer_host/test/test_render_view_host.cc +++ b/chrome/browser/renderer_host/test/test_render_view_host.cc @@ -114,12 +114,6 @@ BackingStore* TestRenderWidgetHostView::AllocBackingStore( return new TestBackingStore(rwh_, size); } -VideoLayer* TestRenderWidgetHostView::AllocVideoLayer( - const gfx::Size& size) { - NOTIMPLEMENTED(); - return NULL; -} - #if defined(OS_MACOSX) void TestRenderWidgetHostView::ShowPopupWithItems( @@ -167,7 +161,7 @@ void TestRenderWidgetHostView::AcceleratedSurfaceSetIOSurface( gfx::PluginWindowHandle window, int32 width, int32 height, - uint64 io_surface_identifier) { + uint64 surface_id) { } void TestRenderWidgetHostView::AcceleratedSurfaceSetTransportDIB( @@ -178,7 +172,7 @@ void TestRenderWidgetHostView::AcceleratedSurfaceSetTransportDIB( } void TestRenderWidgetHostView::AcceleratedSurfaceBuffersSwapped( - gfx::PluginWindowHandle window) { + gfx::PluginWindowHandle window, uint64 surface_id) { } void TestRenderWidgetHostView::GpuRenderingStateDidChange() { diff --git a/chrome/browser/renderer_host/test/test_render_view_host.h b/chrome/browser/renderer_host/test/test_render_view_host.h index e3c9f39..fe58b68 100644 --- a/chrome/browser/renderer_host/test/test_render_view_host.h +++ b/chrome/browser/renderer_host/test/test_render_view_host.h @@ -25,6 +25,7 @@ class NavigationController; class SiteInstance; class TestingProfile; class TestTabContents; +struct WebMenuItem; struct ViewHostMsg_FrameNavigate_Params; // Utility function to initialize ViewHostMsg_NavigateParams_Params @@ -87,7 +88,6 @@ class TestRenderWidgetHostView : public RenderWidgetHostView { virtual void PrepareToDestroy() {} virtual void SetTooltipText(const std::wstring& tooltip_text) {} virtual BackingStore* AllocBackingStore(const gfx::Size& size); - virtual VideoLayer* AllocVideoLayer(const gfx::Size& size); #if defined(OS_MACOSX) virtual void SetTakesFocusOnlyOnMouseDown(bool flag) {} virtual void ShowPopupWithItems(gfx::Rect bounds, @@ -111,13 +111,14 @@ class TestRenderWidgetHostView : public RenderWidgetHostView { virtual void AcceleratedSurfaceSetIOSurface(gfx::PluginWindowHandle window, int32 width, int32 height, - uint64 io_surface_identifier); + uint64 surface_id); virtual void AcceleratedSurfaceSetTransportDIB( gfx::PluginWindowHandle window, int32 width, int32 height, TransportDIB::Handle transport_dib); - virtual void AcceleratedSurfaceBuffersSwapped(gfx::PluginWindowHandle window); + virtual void AcceleratedSurfaceBuffersSwapped(gfx::PluginWindowHandle window, + uint64 surface_id); virtual void GpuRenderingStateDidChange(); #endif virtual void SetVisuallyDeemphasized(bool deemphasized) { } diff --git a/chrome/browser/renderer_host/video_layer.cc b/chrome/browser/renderer_host/video_layer.cc deleted file mode 100644 index 1555060..0000000 --- a/chrome/browser/renderer_host/video_layer.cc +++ /dev/null @@ -1,13 +0,0 @@ -// 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/renderer_host/video_layer.h" - -VideoLayer::VideoLayer(RenderWidgetHost* widget, const gfx::Size& size) - : render_widget_host_(widget), - size_(size) { -} - -VideoLayer::~VideoLayer() { -} diff --git a/chrome/browser/renderer_host/video_layer.h b/chrome/browser/renderer_host/video_layer.h deleted file mode 100644 index 0d7d4f8..0000000 --- a/chrome/browser/renderer_host/video_layer.h +++ /dev/null @@ -1,51 +0,0 @@ -// 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_RENDERER_HOST_VIDEO_LAYER_H_ -#define CHROME_BROWSER_RENDERER_HOST_VIDEO_LAYER_H_ -#pragma once - -#include "app/surface/transport_dib.h" -#include "gfx/size.h" - -class RenderProcessHost; -class RenderWidgetHost; - -namespace gfx { -class Rect; -} - -// Represents a layer of YUV data owned by RenderWidgetHost and composited with -// the backing store. VideoLayer is responsible for converting to RGB as -// needed. -class VideoLayer { - public: - virtual ~VideoLayer(); - - RenderWidgetHost* render_widget_host() const { return render_widget_host_; } - const gfx::Size& size() { return size_; } - - // Copy the incoming bitmap into this video layer. |bitmap| contains YUV - // pixel data in YV12 format and must be the same dimensions as this video - // layer. |bitmap_rect| specifies the absolute position and destination size - // of the bitmap on the backing store. - virtual void CopyTransportDIB(RenderProcessHost* process, - TransportDIB::Id bitmap, - const gfx::Rect& bitmap_rect) = 0; - - protected: - // Can only be constructed via subclasses. - VideoLayer(RenderWidgetHost* widget, const gfx::Size& size); - - private: - // The owner of this video layer. - RenderWidgetHost* render_widget_host_; - - // The size of the video layer. - gfx::Size size_; - - DISALLOW_COPY_AND_ASSIGN(VideoLayer); -}; - -#endif // CHROME_BROWSER_RENDERER_HOST_VIDEO_LAYER_H_ diff --git a/chrome/browser/renderer_host/video_layer_proxy.cc b/chrome/browser/renderer_host/video_layer_proxy.cc deleted file mode 100644 index 3df1d25..0000000 --- a/chrome/browser/renderer_host/video_layer_proxy.cc +++ /dev/null @@ -1,59 +0,0 @@ -// 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/renderer_host/video_layer_proxy.h" - -#include "chrome/browser/gpu_process_host_ui_shim.h" -#include "chrome/browser/renderer_host/render_process_host.h" -#include "chrome/common/gpu_messages.h" -#include "gfx/rect.h" - -VideoLayerProxy::VideoLayerProxy(RenderWidgetHost* widget, - const gfx::Size& size, - GpuProcessHostUIShim* process_shim, - int32 routing_id) - : VideoLayer(widget, size), - process_shim_(process_shim), - routing_id_(routing_id) { - process_shim_->AddRoute(routing_id_, this); -} - -VideoLayerProxy::~VideoLayerProxy() { - process_shim_->RemoveRoute(routing_id_); -} - -void VideoLayerProxy::CopyTransportDIB(RenderProcessHost* process, - TransportDIB::Id bitmap, - const gfx::Rect& bitmap_rect) { - base::ProcessId process_id; -#if defined(OS_WIN) - process_id = ::GetProcessId(process->GetHandle()); -#elif defined(OS_POSIX) - process_id = process->GetHandle(); -#endif - - if (process_shim_->Send(new GpuMsg_PaintToVideoLayer( - routing_id_, process_id, bitmap, bitmap_rect))) { - } else { - // TODO(scherkus): what to do ?!?! - } -} - -void VideoLayerProxy::OnMessageReceived(const IPC::Message& msg) { - IPC_BEGIN_MESSAGE_MAP(VideoLayerProxy, msg) - IPC_MESSAGE_HANDLER(GpuHostMsg_PaintToVideoLayer_ACK, - OnPaintToVideoLayerACK) - IPC_END_MESSAGE_MAP_EX() -} - -void VideoLayerProxy::OnChannelConnected(int32 peer_pid) { -} - -void VideoLayerProxy::OnChannelError() { -} - -void VideoLayerProxy::OnPaintToVideoLayerACK() { - // TODO(scherkus): we may not need to ACK video layer updates at all. - NOTIMPLEMENTED(); -} diff --git a/chrome/browser/renderer_host/video_layer_proxy.h b/chrome/browser/renderer_host/video_layer_proxy.h deleted file mode 100644 index 4a4cc71..0000000 --- a/chrome/browser/renderer_host/video_layer_proxy.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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_RENDERER_HOST_VIDEO_LAYER_PROXY_H_ -#define CHROME_BROWSER_RENDERER_HOST_VIDEO_LAYER_PROXY_H_ -#pragma once - -#include "chrome/browser/renderer_host/video_layer.h" -#include "ipc/ipc_channel.h" - -class GpuProcessHostUIShim; - -// Proxies YUV video layer data to the GPU process for rendering. -class VideoLayerProxy : public VideoLayer, public IPC::Channel::Listener { - public: - VideoLayerProxy(RenderWidgetHost* widget, const gfx::Size& size, - GpuProcessHostUIShim* process_shim, int32 routing_id); - virtual ~VideoLayerProxy(); - - // VideoLayer implementation. - virtual void CopyTransportDIB(RenderProcessHost* process, - TransportDIB::Id bitmap, - const gfx::Rect& bitmap_rect); - - // IPC::Channel::Listener implementation. - virtual void OnMessageReceived(const IPC::Message& message); - virtual void OnChannelConnected(int32 peer_pid); - virtual void OnChannelError(); - - private: - // Called when GPU process has finished painting the video layer. - void OnPaintToVideoLayerACK(); - - // GPU process receiving our proxied requests. - GpuProcessHostUIShim* process_shim_; - - // IPC routing ID to use when communicating with the GPU process. - int32 routing_id_; - - DISALLOW_COPY_AND_ASSIGN(VideoLayerProxy); -}; - -#endif // CHROME_BROWSER_RENDERER_HOST_VIDEO_LAYER_PROXY_H_ diff --git a/chrome/browser/renderer_host/video_layer_x.cc b/chrome/browser/renderer_host/video_layer_x.cc deleted file mode 100644 index 09891b3..0000000 --- a/chrome/browser/renderer_host/video_layer_x.cc +++ /dev/null @@ -1,110 +0,0 @@ -// 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/renderer_host/video_layer_x.h" - -#include "app/x11_util_internal.h" -#include "chrome/browser/renderer_host/render_process_host.h" -#include "media/base/yuv_convert.h" - - -// Assume that somewhere along the line, someone will do width * height * 4 -// with signed numbers. If the maximum value is 2**31, then 2**31 / 4 = -// 2**29 and floor(sqrt(2**29)) = 23170. - -// Max height and width for layers -static const int kMaxVideoLayerSize = 23170; - -VideoLayerX::VideoLayerX(RenderWidgetHost* widget, - const gfx::Size& size, - void* visual, - int depth) - : VideoLayer(widget, size), - visual_(visual), - depth_(depth), - display_(x11_util::GetXDisplay()), - rgb_frame_size_(0) { - DCHECK(!size.IsEmpty()); - - // Create our pixmap + GC representing an RGB version of a video frame. - pixmap_ = XCreatePixmap(display_, x11_util::GetX11RootWindow(), - size.width(), size.height(), depth_); - pixmap_gc_ = XCreateGC(display_, pixmap_, 0, NULL); - pixmap_bpp_ = x11_util::BitsPerPixelForPixmapDepth(display_, depth_); -} - -VideoLayerX::~VideoLayerX() { - // In unit tests, |display_| may be NULL. - if (!display_) - return; - - XFreePixmap(display_, pixmap_); - XFreeGC(display_, static_cast<GC>(pixmap_gc_)); -} - -void VideoLayerX::CopyTransportDIB(RenderProcessHost* process, - TransportDIB::Id bitmap, - const gfx::Rect& bitmap_rect) { - if (!display_) - return; - - if (bitmap_rect.IsEmpty()) - return; - - if (bitmap_rect.size() != size()) { - LOG(ERROR) << "Scaled video layer not supported."; - return; - } - - // Save location and size of destination bitmap. - rgb_rect_ = bitmap_rect; - - const int width = bitmap_rect.width(); - const int height = bitmap_rect.height(); - const size_t new_rgb_frame_size = static_cast<size_t>(width * height * 4); - - if (width <= 0 || width > kMaxVideoLayerSize || - height <= 0 || height > kMaxVideoLayerSize) - return; - - // Lazy allocate |rgb_frame_|. - if (!rgb_frame_.get() || rgb_frame_size_ < new_rgb_frame_size) { - // TODO(scherkus): handle changing dimensions and re-allocating. - CHECK(size() == rgb_rect_.size()); - rgb_frame_.reset(new uint8[new_rgb_frame_size]); - rgb_frame_size_ = new_rgb_frame_size; - } - - TransportDIB* dib = process->GetTransportDIB(bitmap); - if (!dib) - return; - - // Perform colour space conversion. - const uint8* y_plane = reinterpret_cast<uint8*>(dib->memory()); - const uint8* u_plane = y_plane + width * height; - const uint8* v_plane = u_plane + ((width * height) >> 2); - media::ConvertYUVToRGB32(y_plane, - u_plane, - v_plane, - rgb_frame_.get(), - width, - height, - width, - width / 2, - width * 4, - media::YV12); - - // Draw ARGB frame onto our pixmap. - x11_util::PutARGBImage(display_, visual_, depth_, pixmap_, pixmap_gc_, - rgb_frame_.get(), width, height); -} - -void VideoLayerX::XShow(XID target) { - if (rgb_rect_.IsEmpty()) - return; - - XCopyArea(display_, pixmap_, target, static_cast<GC>(pixmap_gc_), - 0, 0, rgb_rect_.width(), rgb_rect_.height(), - rgb_rect_.x(), rgb_rect_.y()); -} diff --git a/chrome/browser/renderer_host/video_layer_x.h b/chrome/browser/renderer_host/video_layer_x.h deleted file mode 100644 index a12c7b4..0000000 --- a/chrome/browser/renderer_host/video_layer_x.h +++ /dev/null @@ -1,56 +0,0 @@ -// 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_RENDERER_HOST_VIDEO_LAYER_X_H_ -#define CHROME_BROWSER_RENDERER_HOST_VIDEO_LAYER_X_H_ -#pragma once - -#include "app/x11_util.h" -#include "base/scoped_ptr.h" -#include "chrome/browser/renderer_host/video_layer.h" -#include "gfx/rect.h" - -// Implements a YUV data layer using X to hold the RGB data. -class VideoLayerX : public VideoLayer { - public: - VideoLayerX(RenderWidgetHost* widget, const gfx::Size& size, void* visual, - int depth); - virtual ~VideoLayerX(); - - // VideoLayer implementation. - virtual void CopyTransportDIB(RenderProcessHost* process, - TransportDIB::Id bitmap, - const gfx::Rect& bitmap_rect); - - // Copy from the server-side video layer to the target window. - // Unlike BackingStore, we maintain the absolute position and destination - // size so passing in a rect is not required. - void XShow(XID target); - - private: - // X Visual to get RGB mask information. - void* const visual_; - // Depth of the target window. - int depth_; - // Connection to the X server where this video layer will be displayed. - Display* const display_; - - // Handle to the server side pixmap which is our video layer. - XID pixmap_; - // Graphics context for painting our video layer. - void* pixmap_gc_; - // Server side bits-per-pixel for |pixmap_|. - int pixmap_bpp_; - - // Most recently converted frame stored as 32-bit ARGB. - scoped_array<uint8> rgb_frame_; - size_t rgb_frame_size_; - - // Destination size and absolution position of the converted frame. - gfx::Rect rgb_rect_; - - DISALLOW_COPY_AND_ASSIGN(VideoLayerX); -}; - -#endif // CHROME_BROWSER_RENDERER_HOST_VIDEO_LAYER_X_H_ diff --git a/chrome/browser/renderer_host/web_cache_manager.cc b/chrome/browser/renderer_host/web_cache_manager.cc index 88baa7b..a9262ed 100644 --- a/chrome/browser/renderer_host/web_cache_manager.cc +++ b/chrome/browser/renderer_host/web_cache_manager.cc @@ -248,7 +248,7 @@ bool WebCacheManager::AttemptTactic( return true; } -void WebCacheManager::AddToStrategy(std::set<int> renderers, +void WebCacheManager::AddToStrategy(const std::set<int>& renderers, AllocationTactic tactic, size_t extra_bytes_to_allocate, AllocationStrategy* strategy) { @@ -304,7 +304,7 @@ void WebCacheManager::EnactStrategy(const AllocationStrategy& strategy) { } } -void WebCacheManager::ClearRendederCache(std::set<int> renderers) { +void WebCacheManager::ClearRendederCache(const std::set<int>& renderers) { std::set<int>::const_iterator iter = renderers.begin(); for (; iter != renderers.end(); ++iter) { RenderProcessHost* host = RenderProcessHost::FromID(*iter); diff --git a/chrome/browser/renderer_host/web_cache_manager.h b/chrome/browser/renderer_host/web_cache_manager.h index 248363e..1bd266b 100644 --- a/chrome/browser/renderer_host/web_cache_manager.h +++ b/chrome/browser/renderer_host/web_cache_manager.h @@ -162,7 +162,7 @@ class WebCacheManager { // For each renderer in |renderers|, computes its allocation according to // |tactic| and add the result to |strategy|. Any |extra_bytes_to_allocate| // is divided evenly among the renderers. - void AddToStrategy(std::set<int> renderers, + void AddToStrategy(const std::set<int>& renderers, AllocationTactic tactic, size_t extra_bytes_to_allocate, AllocationStrategy* strategy); @@ -172,7 +172,7 @@ class WebCacheManager { void EnactStrategy(const AllocationStrategy& strategy); // Inform all |renderers| to clear their cache. - void ClearRendederCache(std::set<int> renderers); + void ClearRendederCache(const std::set<int>& renderers); // Check to see if any active renderers have fallen inactive. void FindInactiveRenderers(); diff --git a/chrome/browser/renderer_host/x509_user_cert_resource_handler.cc b/chrome/browser/renderer_host/x509_user_cert_resource_handler.cc index 1c61bea..9e1cccc 100644 --- a/chrome/browser/renderer_host/x509_user_cert_resource_handler.cc +++ b/chrome/browser/renderer_host/x509_user_cert_resource_handler.cc @@ -103,9 +103,9 @@ bool X509UserCertResourceHandler::OnResponseCompleted( // TODO(gauravsh): Verify that 'request_id' was actually a keygen form post // and only then import the certificate. AssembleResource(); - scoped_refptr<net::X509Certificate> cert = + scoped_refptr<net::X509Certificate> cert( net::X509Certificate::CreateFromBytes(resource_buffer_->data(), - content_length_); + content_length_)); // The handler will run the UI and delete itself when it's finished. new SSLAddCertHandler(request_, cert, render_process_host_id_, render_view_id_); diff --git a/chrome/browser/repost_form_warning_uitest.cc b/chrome/browser/repost_form_warning_uitest.cc index c6e9514..a80ac97 100644 --- a/chrome/browser/repost_form_warning_uitest.cc +++ b/chrome/browser/repost_form_warning_uitest.cc @@ -4,7 +4,7 @@ #include <string> -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/url_constants.h" diff --git a/chrome/browser/resources/about_conflicts.html b/chrome/browser/resources/about_conflicts.html new file mode 100644 index 0000000..2bf6a28 --- /dev/null +++ b/chrome/browser/resources/about_conflicts.html @@ -0,0 +1,292 @@ +<!DOCTYPE HTML> +<html i18n-values="dir:textdirection;"> +<head> +<meta charset="utf-8"> +<style> +body { + margin: 10px; + min-width: 47em; +} + +a { + color: blue; + font-size: 103%; +} + +div#header { + margin-bottom: 1.05em; + /* 67px is the height of the header's background image. */ + min-height: 67px; + overflow: hidden; + padding-bottom: 20px; + padding-left: 0; + padding-top: 20px; + position: relative; + -webkit-box-sizing: border-box; +} + +html[dir=rtl] #header { + padding-right: 0; +} + +#header h1 { + background: url('../../app/theme/conflicts_section.png') 0px 20px no-repeat; + display: inline; + margin: 0; + padding-bottom: 43px; + padding-left: 75px; + padding-top: 40px; +} + +html[dir=rtl] #header h1 { + background: url('../../app/theme/conflicts_section.png') right no-repeat; + padding-right: 95px; + padding-left: 0; +} + +h1 { + font-size: 156%; + font-weight: bold; + padding: 0; + margin: 0; +} + +#blurb-container { + padding-bottom: 1.5em; + font-size: 120%; +} + +div.content { + font-size: 88%; + margin-top: 5px; +} + +.section-header { + background: #ebeff9; + border-top: 1px solid #b5c7de; + font-size: 99%; + padding-bottom: 2px; + padding-left: 5px; + padding-top: 3px; + width: 100%; +} + +html[dir=rtl] .section-header { + padding-right: 5px; + padding-left: 0; +} + +.section-header > table > tr > td:first-child { + width: 100%; +} + +.section-header > table { + width: 100%; +} + +.section-header-title { + font-weight: bold; +} + +.vbox-container { + display: -webkit-box; + -webkit-box-orient: vertical; +} + +.wbox { + display: -webkit-box; + -webkit-box-align: stretch; + -webkit-box-flex: 1; +} + +#top { + padding-right: 5px; +} + +html[dir=rtl] #top { + padding-left: 5px; + padding-right: 0; +} + +.module-loaded > td { + padding-bottom: 4px; + padding-top: 5px; +} + +.module { + border-bottom: 1px solid #cdcdcd; +} + +.module-name { + font-weight: bold; +} + +.no-modules { + margin: 6em 0 0; + text-align: center; + font-size: 1.2em; +} + +.suspected-bad { + color: orange; +} + +.confirmed-bad { + color: red; +} +</style> +<script>
+
+ /** + * This variable structure is here to document the structure that the template + * expects to correctly populate the page. + */
+ var moduleListDataFormat = {
+ 'moduleList': [
+ {
+ 'type': 'The type of module found',
+ 'type_description':
+ 'The type of module (string), defaults to blank for regular modules',
+ 'status': 'The module status',
+ 'location': 'The module path, not including filename',
+ 'name': 'The name of the module', + 'product_name': 'The name of the product the module belongs to', + 'description': 'The module description', + 'version': 'The module version', + 'digital_signer': 'The signer of the digital certificate for the module',
+ 'recommended_action': 'The help tips bitmask',
+ 'possible_resolution': 'The help tips in string form',
+ 'help_url': 'The link to the Help Center article' + }
+ ]
+ };
+
+ /** + * Takes the |moduleListData| input argument which represents data about + * the currently available modules and populates the html jstemplate + * with that data. It expects an object structure like the above. + * @param {Object} moduleListData Information about available modules + */
+ function renderTemplate(moduleListData) {
+ // This is the javascript code that processes the template:
+ var input = new JsEvalContext(moduleListData);
+ var output = document.getElementById('modulesTemplate');
+ jstProcess(input, output);
+ }
+
+ /** + * Asks the C++ ConflictsDOMHandler to get details about the available modules + * and return detailed data about the configuration. The ConflictsDOMHandler + * should reply to returnModuleList() (below). + */
+ function requestModuleListData() {
+ chrome.send('requestModuleList', []);
+ }
+
+ /** + * Called by the dom_ui to re-populate the page with data representing the + * current state of installed modules. + */
+ function returnModuleList(moduleListData) {
+ var bodyContainer = document.getElementById('body-container');
+ renderTemplate(moduleListData);
+ bodyContainer.style.visibility = 'visible';
+ }
+
+ // Get data and have it displayed upon loading.
+ document.addEventListener('DOMContentLoaded', requestModuleListData); + +</script> +</head> +<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize"> +<div id="body-container" style="visibility:hidden"> + + <div id="header"><h1 i18n-content="modulesLongTitle">TITLE</h1></div> + + <div id="blurb-container"> + <span i18n-content="modulesBlurb">MODULES BLURB</span> + </div> + + <div id="modulesTemplate"> + + <div id="container" class="vbox-container"> + <div id="top" class="wbox"> + + <div class="section-header"> + <table cellpadding="0" cellspacing="0"><tr valign="center"> + <td> + <span class="section-header-title" + jscontent="modulesTableTitle">TITLE</span> + </td> + </tr></table> + </div> + + </div> + </div> + + <div class="content"> + <div class="module-name no-modules" + jsdisplay="moduleList.length === 0"> + <div i18n-content="modulesNoneLoaded">NO_MODULES_ARE_AVAILABLE</div> + </div> + + <div jsdisplay="moduleList.length > 0"> + <div class="module" jsselect="moduleList"> + <table width="100%" cellpadding="0" cellspacing="0"> + <tr class="module-loaded"> + <td valign="top"> + <table cellpadding="2" cellspacing="0"> + <tr> + <td colspan="2"><span class="module-name" dir="ltr" + jscontent="name">NAME</span> + </td> + </tr> + <tr> + <td width="75"><span i18n-content="headerSoftware" /></td> + <td><span dir="ltr" jsdisplay="type_description.length > 0"> + <span dir="ltr" + jscontent="type_description">MODULE_TYPE</span>: </span> + <span dir="ltr" jsvalues=".innerHTML:description"></span> + <span dir="ltr" jsdisplay="version.length > 0"> - </span> + <span dir="ltr" jscontent="version">VERSION</span></td> + </tr> + <tr jsdisplay="digital_signer.length > 0"> + <td><span i18n-content="headerSignedBy" /></td> + <td><span dir="ltr" jscontent="digital_signer">SIGNER</span></td> + </tr> + <tr> + <td><span i18n-content="headerLocation" /></td> + <td><span dir="ltr" + jscontent="location">LOCATION</span><span + dir="ltr" jscontent="name">NAME</span></td> + </tr> + <tr jsdisplay="status == 2 || status == 3"> + <td><span i18n-content="headerWarning" /></td> + <td><span jsdisplay="status == 2" + i18n-content="moduleSuspectedBad" + class="suspected-bad">SUSPECTED_BAD</span> + <span jsdisplay="status == 3" + i18n-content="moduleConfirmedBad" + class="confirmed-bad">CONFIRMED_BAD</span> + <a jsdisplay="help_url.length > 0" + jsvalues=".href:help_url"><span + i18n-content="helpCenterLink">HELP_CENTER</span></a> + </td> + </tr> + <tr jsdisplay="possibleResolution.length > 0"> + <td><span i18n-content="headerHelpTip" /></td> + <td><span + jscontent="possibleResolution">POSSIBLE_RESOLUTION</span></td> + </tr> + </table> + + </td> + </tr> + </table> + </div> + </div> + </div> + </div> +</div> +</body> +</html> diff --git a/chrome/browser/resources/browser_signin.html b/chrome/browser/resources/browser_signin.html new file mode 100644 index 0000000..17ead4e --- /dev/null +++ b/chrome/browser/resources/browser_signin.html @@ -0,0 +1,32 @@ +<html> +<style> +body { + margin: 0; + font-family: sans-serif; +} +#message { + position: absolute; + left: 10px; + width: 45%; +} +#login { + position: absolute; + left: 50%; +} +</style> +<script> +function initmessage() { + document.getElementById('message').innerHTML = chrome.dialogArguments; +} + +function init() { + chrome.send('SigninInit'); + initmessage(); +} +</script> +<body onload="init();"> +<div id="message"></div> +<iframe id="login" frameborder="0" width="50%" scrolling="no" height="100%" + src="chrome://syncresources/gaialogin" tabindex="-1"></iframe> +</body> +</html> diff --git a/chrome/browser/resources/chat_manager/16.png b/chrome/browser/resources/chat_manager/16.png Binary files differnew file mode 100644 index 0000000..acd4e72 --- /dev/null +++ b/chrome/browser/resources/chat_manager/16.png diff --git a/chrome/browser/resources/chat_manager/_locales/en/messages.json b/chrome/browser/resources/chat_manager/_locales/en/messages.json new file mode 100644 index 0000000..d4788ae --- /dev/null +++ b/chrome/browser/resources/chat_manager/_locales/en/messages.json @@ -0,0 +1 @@ +{"chat_manager_name":{"message":"Google Talk"},"chat_manager_description":{"message":"Use Google Talk extension to chat with friends."},"chat_manager_could_not_connect":{"message":"Could not connect to Google Talk."},"chat_manager_retry_now":{"message":"Retry Now"},"chat_manager_retrying_in":{"message":"Retrying in $1:$2","placeholders":{"1":{"content":"$1"},"2":{"content":"$2"}}}} diff --git a/chrome/browser/resources/chat_manager/background.html b/chrome/browser/resources/chat_manager/background.html index 964db68..b776cf2 100644 --- a/chrome/browser/resources/chat_manager/background.html +++ b/chrome/browser/resources/chat_manager/background.html @@ -14,7 +14,7 @@ to route both incoming and outgoing chats through this central roster. <body> <iframe id='centralRoster' - src='central_roster.html'>Central Roster</iframe> + src='central_roster.html'></iframe> <script src="js/chatbridgeeventtypes.js"></script> <script> var centralRosterJid; @@ -44,12 +44,37 @@ to route both incoming and outgoing chats through this central roster. } } + // Focus a chat popup. + function focusMole(hostId) { + findMole(hostId, function(win) { + chrome.windows.update(win.id, {focused: true}); + }); + } + + // Find a chat popup from a chat's hostId and executes callback with it. + function findMole(hostId, callback) { + var matchUrlIdRegExp = new RegExp('[&?]id=' + hostId + '(&|$)', 'i'); + chrome.windows.getAll({populate: true}, function(wins) { + for (var winIdx = 0, win = wins[winIdx]; win; win = wins[++winIdx]) { + var tabs = win.tabs; + for (var tabIdx = 0, tab = tabs[tabIdx]; tab; tab = tabs[++tabIdx]) { + if ((tab.url).match(matchUrlIdRegExp)) { + callback(win); + return; + } + } + } + }); + } + // Listen for content script connections. chrome.extension.onConnect.addListener(function(port) { // New central jid listener. // Update with current central roster jid, and add to tracking array. if (port.name == 'centralJidListener') { centralJidListenerPorts.push(port); + port.postMessage({eventType: ChatBridgeEventTypes.CENTRAL_USER_UPDATE, + jid: centralRosterJid}); // Clear tracking array entry when content script closes. port.onDisconnect.addListener(function(port) { @@ -111,6 +136,9 @@ to route both incoming and outgoing chats through this central roster. forwardEventToPortListeners(ChatBridgeEventTypes.CLOSED_MOLE_OUTGOING, request.jid); break; + case ChatBridgeEventTypes.MOLE_FOCUSED: + focusMole(request.jid); + break; } }); </script> diff --git a/chrome/browser/resources/chat_manager/central_roster.html b/chrome/browser/resources/chat_manager/central_roster.html index aa68798..3dfe173 100644 --- a/chrome/browser/resources/chat_manager/central_roster.html +++ b/chrome/browser/resources/chat_manager/central_roster.html @@ -27,9 +27,8 @@ Central roster: hosting all Google Talk chats in ChromeOS. } </style> <script> - var MIN_RETRY_MILLISECONDS = 15 * 1000; + var MIN_RETRY_MILLISECONDS = 1 * 1000; var MAX_RETRY_MILLISECONDS = 4 * 60 * 1000; - var retryStartTime = 0; var retryTime; var retryTimer; var chatClient = null; @@ -38,8 +37,9 @@ Central roster: hosting all Google Talk chats in ChromeOS. 'protocol': 'https', 'host': 'talkgadget.google.com', 'jsmode': 'pre', - 'hl': 'en', + 'hl': chrome.i18n.getMessage('@@ui_locale'), }; + // Read args. var urlParts = window.location.href.split(/[?&#]/); for (var i = 1; i < urlParts.length; i++) { @@ -63,19 +63,17 @@ Central roster: hosting all Google Talk chats in ChromeOS. document.body.appendChild(script); } function retryConnection() { - if (retryTimer) { - clearTimeout(retryTimer); - retryTimer = null; - } - runGTalkScript(); + location.reload(); } function retryConnectionCountdown() { var seconds = retryTime / 1000; var minutes = Math.floor(seconds / 60); seconds -= minutes * 60; - document.getElementById('retryStatus').textContent = - 'Retrying in ' + minutes + ':' + (seconds < 10 ? '0' : '') + seconds; + document.getElementById('retryStatus').textContent = + chrome.i18n.getMessage('CHAT_MANAGER_RETRYING_IN', + [minutes, (seconds < 10 ? '0' : '') + seconds]); + if (retryTime <= 0) { retryConnection(); } else { @@ -86,39 +84,58 @@ Central roster: hosting all Google Talk chats in ChromeOS. function loadGTalk() { if (window.GTalkNotifier) { document.getElementById('retryInfo').style.display = 'none'; + var baseUrl = args['protocol'] + '://' + args['host'] + '/talkgadget/'; chatClient = new window.GTalkNotifier( { - 'clientBaseUrl': - args['protocol'] + '://' + args['host'] + '/talkgadget/', + 'clientBaseUrl': baseUrl, 'clientUrl': 'notifierclient' + (args['jsmode'] != '' ? ('?jsmode=' + args['jsmode']) : ''), 'propertyName': 'ChromeOS', - 'xpcRelay': 'xpc_relay', - 'xpcBlank': 'xpc_blank', + 'xpcRelay': baseUrl + 'xpc_relay', + 'xpcBlank': baseUrl + 'xpc_blank', 'locale': args['hl'], 'isCentralRoster': true, 'hideProfileCard': true, 'isFullFrame': true } ); + delete localStorage.retryStartTime; } else { - if (retryStartTime == 0) { - retryStartTime = MIN_RETRY_MILLISECONDS; - } else if (retryStartTime < MAX_RETRY_MILLISECONDS) { - retryStartTime = Math.min(retryStartTime * 2, MAX_RETRY_MILLISECONDS); + if (!localStorage.retryStartTime) { + localStorage.retryStartTime = MIN_RETRY_MILLISECONDS; + } else if (localStorage.retryStartTime < MAX_RETRY_MILLISECONDS) { + localStorage.retryStartTime = Math.min(localStorage.retryStartTime * 2, + MAX_RETRY_MILLISECONDS); } - retryTime = retryStartTime; + retryTime = localStorage.retryStartTime; document.getElementById('retryInfo').style.display = 'inline'; retryConnectionCountdown(); } } + function onPageLoaded() { + // localize the page + document.getElementById('retryMessage').textContent = + chrome.i18n.getMessage('CHAT_MANAGER_COULD_NOT_CONNECT'); + document.getElementById('retryButton').value = + chrome.i18n.getMessage('CHAT_MANAGER_RETRY_NOW'); + + if (localStorage.hasOpenedInViewer) { + runGTalkScript(); + } + } + function onOpenedInViewer() { + if (!localStorage.hasOpenedInViewer) { + localStorage.hasOpenedInViewer = true; + runGTalkScript(); + } + } </script> </head> -<body onload='runGTalkScript()'> +<body onload='onPageLoaded()'> <div id='retryInfo' style='display:none'> - <p>Could not connect to Google Talk</p> + <p id='retryMessage'></p> <p id='retryStatus'></p> - <input type='button' value='Retry Now' onclick='retryConnection()'/> + <input id='retryButton' type='button' value='' onclick='retryConnection()'/> </div> </body> </html> diff --git a/chrome/browser/resources/chat_manager/central_roster_viewer.html b/chrome/browser/resources/chat_manager/central_roster_viewer.html index bab1cdf..fb62dbf 100644 --- a/chrome/browser/resources/chat_manager/central_roster_viewer.html +++ b/chrome/browser/resources/chat_manager/central_roster_viewer.html @@ -8,7 +8,7 @@ found in the LICENSE file. --> <head> - <title>Google Talk</title> + <title></title> <!-- Set height to 98% to get around bug in css calculation that triggers a vertical scroll bar. --> <style> @@ -21,6 +21,8 @@ found in the LICENSE file. } </style> <script> + document.title = chrome.i18n.getMessage('CHAT_MANAGER_NAME'); + function reparentCentralRosterToWindow() { var backgroundWindow = chrome.extension.getBackgroundPage(); if (backgroundWindow) { @@ -33,7 +35,13 @@ found in the LICENSE file. document.adoptNode(iframe); } document.body.appendChild(iframe); + iframe.contentWindow.onOpenedInViewer(); } + } else { + // Could not locate central roster. Close this blank window. + // Workaround since Chrome extensions cannot be singletons. + // Reference: http://crbug.com/45816 + window.close(); } } } diff --git a/chrome/browser/resources/chat_manager/js/chatbridgeeventtypes.js b/chrome/browser/resources/chat_manager/js/chatbridgeeventtypes.js index 8607fe8..3397115 100644 --- a/chrome/browser/resources/chat_manager/js/chatbridgeeventtypes.js +++ b/chrome/browser/resources/chat_manager/js/chatbridgeeventtypes.js @@ -18,5 +18,6 @@ var ChatBridgeEventTypes = { OPENED_MOLE_INCOMING: 'onMoleOpened', OPENED_MOLE_OUTGOING: 'onCentralMoleOpened', CLOSED_MOLE_INCOMING: 'onMoleClosed', - CLOSED_MOLE_OUTGOING: 'onCentralMoleClosed' + CLOSED_MOLE_OUTGOING: 'onCentralMoleClosed', + MOLE_FOCUSED: '*mfo' }; diff --git a/chrome/browser/resources/chat_manager/js/chatbridgehook.js b/chrome/browser/resources/chat_manager/js/chatbridgehook.js index afbe0be..9549a83 100644 --- a/chrome/browser/resources/chat_manager/js/chatbridgehook.js +++ b/chrome/browser/resources/chat_manager/js/chatbridgehook.js @@ -43,10 +43,11 @@ function forwardChatEvent(event) { } /** - * Triggered on opening/closing a central roster chat. Forward to extension. + * Forward mole events to extension. + * Triggered on opening/closing/focus a central roster chat. * @param {MessageEvent} event the opened/closed event. */ -function moleOpenedClosed(event) { +function forwardMoleEvent(event) { var eventType = event.type; var chatJid = event.data; chrome.extension.sendRequest({msg: eventType, jid: chatJid}); @@ -132,10 +133,13 @@ function onPageLoaded() { setupCentralRosterJidListener, false); divRosterHandler.addEventListener( ChatBridgeEventTypes.OPENED_MOLE_INCOMING, - moleOpenedClosed, false); + forwardMoleEvent, false); divRosterHandler.addEventListener( ChatBridgeEventTypes.CLOSED_MOLE_INCOMING, - moleOpenedClosed, false); + forwardMoleEvent, false); + divRosterHandler.addEventListener( + ChatBridgeEventTypes.MOLE_FOCUSED, + forwardMoleEvent, false); } } diff --git a/chrome/browser/resources/chat_manager/manifest.json b/chrome/browser/resources/chat_manager/manifest.json index df65666..982dcb6 100644 --- a/chrome/browser/resources/chat_manager/manifest.json +++ b/chrome/browser/resources/chat_manager/manifest.json @@ -1,14 +1,16 @@ { "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDrlwvcbUtVrbQvI7EPV1BTa63N8YkbBToHzxlMl0IzSBwOV+TUOsHE8vRq0HZWuwMAGeH8WdWVC3HRNdES8lScjlzxb1TsTQJAsF+hLXgcjgCUSSSGCfFzypvuvKsRQTx0d02yfWKJa47o0Ws5wL72NVtc7c51HujwWYg+Mz01wIDAQAB", - "name": "Google Talk", - "version": "1.0.13", + "name": "__MSG_CHAT_MANAGER_NAME__", + "version": "1.0.17", "icons": { "128": "128.png", + "16": "16.png", "24": "24.png", "32": "32.png", "48": "48.png" }, - "description": "Google Talk", + "description": "__MSG_CHAT_MANAGER_DESCRIPTION__", + "default_locale": "en", "background_page": "background.html", "options_page": "options.html", "content_scripts": [ { diff --git a/chrome/browser/resources/dom_ui.css b/chrome/browser/resources/dom_ui.css index 13e3756..9eba4bc 100644 --- a/chrome/browser/resources/dom_ui.css +++ b/chrome/browser/resources/dom_ui.css @@ -3,30 +3,37 @@ body { color:black;
margin:10px;
}
+
.header {
overflow:auto;
clear:both;
}
+
.header .logo {
float:left;
}
+
.header .form {
float:left;
margin-top:22px;
margin-left:12px;
}
+
html[dir='rtl'] .logo {
float:right;
}
+
html[dir='rtl'] .form {
float:right;
margin-right:12px;
}
+
.page-navigation {
padding:8px;
background-color:#ebeff9;
margin-right:4px;
}
+
.footer {
height:24px;
-}
\ No newline at end of file +}
diff --git a/chrome/browser/resources/extensions_ui.html b/chrome/browser/resources/extensions_ui.html index 4a7c035..cf469bb 100644 --- a/chrome/browser/resources/extensions_ui.html +++ b/chrome/browser/resources/extensions_ui.html @@ -838,12 +838,12 @@ document.addEventListener('DOMContentLoaded', requestExtensionsData); </td> <td valign="top"> <div> - <a jsdisplay="galleryUrl.length > 0" - jsvalues=".href:galleryUrl"> + <a jsdisplay="homepageUrl.length > 0" + jsvalues=".href:homepageUrl"> <span class="extension-name" jscontent="name">EXTENSION NAME</span></a> <span class="extension-name" - jsdisplay="galleryUrl.length == 0" + jsdisplay="homepageUrl.length == 0" jscontent="name">EXTENSION NAME</span> - <span i18n-content="extensionVersion">VERSION</span> <span jscontent="version">x.x.x.x</span> diff --git a/chrome/browser/resources/filebrowse.html b/chrome/browser/resources/filebrowse.html index 510f068..5c33205 100644 --- a/chrome/browser/resources/filebrowse.html +++ b/chrome/browser/resources/filebrowse.html @@ -598,6 +598,10 @@ div.fullcontainer { background: url('../../app/theme/alert_small.png'); } +input.error { + background-color: #ff6666; +} + </style> <script src="shared/js/local_strings.js"></script> <script src="shared/js/media_common.js"></script> @@ -888,10 +892,10 @@ function browseFileResult(info, results) { var dest = currentSavedPath + '/' + path[2]; var dirId = $('list/dir/' + currentSavedPath); if (dirId) { - + var element = $(dest); if (!element) { - // TODO(dhg): We probably should do some checking for + // TODO(dhg): We probably should do some checking for // existance. element = createNewFakeItem(path[2], dest, false, true); } @@ -998,9 +1002,25 @@ function dialogSaveClick() { } currentPath += '/'; currentPath += filename; - chrome.send('DialogClose', [JSON.stringify({'path' : currentPath})]); + chrome.send('validateSavePath', [currentPath]); + + filenameInput.disabled = true; + $('savebutton').disabled = true; }; +function onValidatedSavePath(valid, savePath) { + var filenameInput = $('filename'); + + filenameInput.disabled = false; + $('savebutton').disabled = false; + + if (valid) { + filenameInput.classList.remove('error'); + chrome.send('DialogClose', [JSON.stringify({'path' : savePath})]); + } else { + filenameInput.classList.add('error'); + } +} function createNewFormItem(initialName, isDirectory, id, blurcallback, keypresscallback) { var element = document.createElement('li'); @@ -1134,10 +1154,6 @@ function pauseToggleDownload(id) { chrome.send('pauseToggleDownload', ['' + id]); } -function downloadsList(results) { - downloadUpdated(results); -} - function allowDownload(id, path) { var element = $(path); element.removeAttribute('dangerous'); @@ -1213,7 +1229,7 @@ function removeDownload(element, dirId) { } } -function downloadUpdated(results) { +function downloadsList(results) { var removeDownloads = []; removeDownloads.pushUnique = function (element) { if (this.indexOf(element) === -1) { @@ -1234,94 +1250,115 @@ function downloadUpdated(results) { } } - for (var x = 0; x < results.length; x++) { - var element = $(results[x].file_path); - // extracted and dirId should be computed outside this loop. - var extracted = getPathAndFilenameForPath(results[x].file_path); + for (var i = 0; i< results.length; ++i) { + downloadUpdated(results[i]); + } + + for (var x = 0; x < removeDownloads.length; x++) { + var element = removeDownloads[x]; + var extracted = getPathAndFilenameForPath(element.id); var dirId = $('list/dir/' + extracted[1]); - if (!element && results[x].state != 'CANCELLED') { - if (dirId) { - element = createNewItem(results[x].file_name, results[x].file_path, false); - if (dirId.firstChild) { - dirId.insertBefore(element, dirId.firstChild); - } else { - dirId.appendChild(element); - } - element.scrollIntoView(); + removeDownload(element, dirId); + } +} + +function downloadUpdated(result) { + var element = $(result.file_path); + var extracted = getPathAndFilenameForPath(result.file_path); + var dirId = $('list/dir/' + extracted[1]); + if (!element && result.state != 'CANCELLED') { + if (dirId) { + element = createNewItem(result.file_name, result.file_path, false); + if (dirId.firstChild) { + dirId.insertBefore(element, dirId.firstChild); + } else { + dirId.appendChild(element); } + element.scrollIntoView(); } - if (element) { - if (results[x].state == 'CANCELLED') { - element.parentNode.removeChild(element); - continue; + } + if (element) { + if (result.state == 'CANCELLED') { + element.parentNode.removeChild(element); + continue; + } + if (result.percent < 100 || result.state == 'DANGEROUS') { + var progressDiv = null; + for (var y = 0; y < element.children.length; y++) { + if (element.children[y].className == 'downloadstatus') { + progressDiv = element.children[y]; + } } - if (results[x].percent < 100 || results[x].state == 'DANGEROUS') { - var progressDiv = null; - for (var y = 0; y < element.children.length; y++) { - if (element.children[y].className == 'downloadstatus') { - progressDiv = element.children[y]; - break; - } - } - if (progressDiv == null) { - downloadList.push(element); - element.className = 'filebrowserow downloading'; - fixTitleForItem(element, results[x]); - var progressDiv = document.createElement('div'); - progressDiv.className = 'downloadstatus'; - element.appendChild(progressDiv); - var pauseDiv = document.createElement('div'); - pauseDiv.onclick = partial(pauseToggleDownload, results[x].id); - pauseDiv.className = 'downloadpause'; - if (results[x].state == "DANGEROUS") { - element.setAttribute('dangerous', 'true'); - pauseDiv.onClick = undefined; - var prompt = document.createElement('div'); - prompt.textContent = localStrings.getString('allowdownload'); - prompt.className = 'prompt'; - pauseDiv.appendChild(prompt); - var yes = document.createElement('div'); - yes.className = 'link'; - yes.textContent = localStrings.getString('confirmyes'); - yes.onclick = partial(allowDownload, - results[x].id, - results[x].file_path); - var no = document.createElement('div'); - no.onclick = partial(cancelDownload, - results[x].id, - results[x].file_path); - no.textContent = localStrings.getString('confirmcancel'); - no.className = 'link'; - pauseDiv.onclick = undefined; - pauseDiv.appendChild(yes); - pauseDiv.appendChild(no); - progressDiv.textContent = ''; - } - pauseDiv.id = 'downloaditem' + results[x].id; - element.appendChild(pauseDiv); - } - var pauseDiv = $('downloaditem' + results[x].id); - if (pauseDiv && - results[x].state == "PAUSED" && - pauseDiv.textContent != kResumeDownload) { - pauseDiv.textContent = kResumeDownload; - } else if (pauseDiv && - results[x].state == "IN_PROGRESS" && - pauseDiv.textContent != kPauseDownload) { - pauseDiv.textContent = kPauseDownload; - } - if (results[x].percent < 100 && - results[x].state != 'DANGEROUS') { - progressDiv.textContent = results[x].progress_status_text; + if (progressDiv == null) { + downloadList.push(element); + element.className = 'filebrowserow downloading'; + fixTitleForItem(element, result); + var progressDiv = document.createElement('div'); + progressDiv.className = 'downloadstatus'; + element.appendChild(progressDiv); + var pauseDiv = document.createElement('div'); + pauseDiv.onclick = partial(pauseToggleDownload, result.id); + pauseDiv.className = 'downloadpause'; + if (result.state == "DANGEROUS") { + element.setAttribute('dangerous', 'true'); + pauseDiv.onClick = undefined; + var prompt = document.createElement('div'); + prompt.textContent = localStrings.getString('allowdownload'); + prompt.className = 'prompt'; + pauseDiv.appendChild(prompt); + var yes = document.createElement('div'); + yes.className = 'link'; + yes.textContent = localStrings.getString('confirmyes'); + yes.onclick = partial(allowDownload, + result.id, + result.file_path); + var no = document.createElement('div'); + no.onclick = partial(cancelDownload, + result.id, + result.file_path); + no.textContent = localStrings.getString('confirmcancel'); + no.className = 'link'; + pauseDiv.onclick = undefined; + pauseDiv.appendChild(yes); + pauseDiv.appendChild(no); + progressDiv.textContent = ''; } - - } else if (downloadList.indexOf(element) !== -1) { - removeDownloads.pushUnique(element); + pauseDiv.id = 'downloaditem' + result.id; + element.appendChild(pauseDiv); + } + var pauseDiv = $('downloaditem' + result.id); + if (pauseDiv && + result.state == "PAUSED" && + pauseDiv.textContent != kResumeDownload) { + pauseDiv.textContent = kResumeDownload; + } else if (pauseDiv && + result.state == "IN_PROGRESS" && + pauseDiv.textContent != kPauseDownload) { + pauseDiv.textContent = kPauseDownload; + } + if (result.percent < 100 && + result.state != 'DANGEROUS') { + progressDiv.textContent = result.progress_status_text; } + + } else { + removeDownloadIfDone(element, dirId); } } - for (x = 0; x < removeDownloads.length; x++) { - removeDownload(removeDownloads[x], dirId); +} + +function removeDownloadIfDone(element, dirId) { + var len = downloadList.length; + while (len--) { + if (element.title === downloadList[len].title) { + if (element.id !== downloadList[len].id) { + // Dangerous download. + removeDownload(downloadList[len], dirId); + } else { + removeDownload(downloadList[len]); + } + break; + } } } @@ -1636,7 +1673,7 @@ function createNewFakeItem(title, path, isDirectory, hasspinner) { function createNewItem(title, path, isDirectory) { var element = document.createElement('li'); element.setAttribute('draggable', 'true'); - + element.addEventListener('dragstart', function (e) { e.dataTransfer.effectAllowed = 'copy'; e.dataTransfer.setData('Text', this.id); diff --git a/chrome/browser/resources/keyboard_overlay.css b/chrome/browser/resources/keyboard_overlay.css new file mode 100644 index 0000000..880cbd7 --- /dev/null +++ b/chrome/browser/resources/keyboard_overlay.css @@ -0,0 +1,158 @@ +body { + background: #fff; + margin: 0; + padding: 0; + overflow: hidden; +} + +.keyboard-overlay-keyboard { + -webkit-border-radius: 6px; + background: -webkit-gradient(linear, left top, left bottom, + from(#484848), to(#252525)) no-repeat; + font-family: 'Droid Sans', Arial; +} + +.keyboard-overlay-instructions { + -webkit-border-radius: 5px; + background: -webkit-gradient(linear, left top, left bottom, + from(#334c7e), to(#0d172b)); + border: 2px solid #576ccf; + color: #fff; + display: table; + position: absolute; + vertical-align: middle; + z-index: 100; +} + +.keyboard-overlay-instructions-text { + display: table-cell; + font-weight: bold; + text-align: center; + vertical-align: middle; +} + +.keyboard-overlay-key { + -webkit-border-radius: 4px; + -webkit-box-orient: vertical; + background-color: rgba(24, 24, 24, 0.9); + border: 2px solid #7f7f7f; + color: #979796; + display: -webkit-box; + font-size: 75%; + font-weight: bold; + position: absolute; +} + +.keyboard-overlay-key.is-shortcut { + background: -webkit-gradient(linear, left top, left bottom, + from(rgba(61, 61, 61, 0.8)), + to(rgba(27, 27, 27, 0.8))); + color: #9e9e9e; +} + +.keyboard-overlay-key.is-shortcut.modifier-shift { + border-color: #61ba64; +} + +.keyboard-overlay-key.is-shortcut.modifier-ctrl { + border-color: #5d80c7; +} + +.keyboard-overlay-key.is-shortcut.modifier-alt { + border-color: #b85454; +} + +.keyboard-overlay-key.is-shortcut.modifier-shift.modifier-ctrl { + border-color: #79ac8f; +} + +.keyboard-overlay-key.is-shortcut.modifier-shift.modifier-alt { + border-color: #bfbd79; +} + +.keyboard-overlay-key.is-shortcut.modifier-ctrl.modifier-alt { + border-color: #9e54ce; +} + +.keyboard-overlay-key.is-shortcut.modifier-shift.modifier-ctrl.modifier-alt { + border-color: #7f7f7f; +} + +.keyboard-overlay-key.pressed { + border-color: #fff; + color: #fff; + font-size: 85%; +} + +.keyboard-overlay-key.pressed.is-shift { + background: -webkit-gradient(linear, left top, left bottom, + from(#44a142), to(#3e5f37)); +} + +.keyboard-overlay-key.pressed.is-shift.modifier-ctrl { + background: -webkit-gradient(linear, left top, left bottom, + from(#42a143), to(#2e5c53)); +} + +.keyboard-overlay-key.pressed.is-shift.modifier-alt { + background: -webkit-gradient(linear, left top, right bottom, + from(#45a343), to(#515134)); +} + +.keyboard-overlay-key.pressed.is-shift.modifier-ctrl.modifier-alt { + background: -webkit-gradient(linear, left top, right bottom, + from(#52a12a), to(#4f4d2e)); +} + +.keyboard-overlay-key.pressed.is-ctrl { + background: -webkit-gradient(linear, left top, left bottom, + from(#1f37a2), to(#19265a)); +} + +.keyboard-overlay-key.pressed.is-ctrl.modifier-shift { + background: -webkit-gradient(linear, left top, left bottom, + from(#439fa5), to(#1e3760)); +} + +.keyboard-overlay-key.pressed.is-ctrl.modifier-alt { + background: -webkit-gradient(linear, left top, left bottom, + from(#733690), to(#22255e)); +} + +.keyboard-overlay-key.pressed.is-ctrl.modifier-shift.modifier-alt { + background: -webkit-gradient(linear, left top, left bottom, + from(#733690), to(#21255d)); +} + +.keyboard-overlay-key.pressed.is-alt { + background: -webkit-gradient(linear, left top, left bottom, + from(#842c2a), to(#541e1c)); +} + +.keyboard-overlay-key.pressed.is-alt.modifier-shift { + background: -webkit-gradient(linear, left top, right bottom, + from(#745e31), to(#55241e)); +} + +.keyboard-overlay-key.pressed.is-alt.modifier-ctrl { + background: -webkit-gradient(linear, left top, left bottom, + from(#76368f), to(#522128)); +} + +.keyboard-overlay-key.pressed.is-alt.modifier-shift.modifier-ctrl { + background: -webkit-gradient(linear, left top, right bottom, + from(#735f29), to(#50241b)); +} + +.keyboard-overlay-shortcut-text { + -webkit-box-flex: 1; + -webkit-box-ordinal-group: 1; + color: #fff; + text-align: center; +} + +.keyboard-overlay-key-text { + -webkit-box-ordinal-group: 2; + padding-bottom: 1px; + text-align: center; +} diff --git a/chrome/browser/resources/keyboard_overlay.html b/chrome/browser/resources/keyboard_overlay.html new file mode 100644 index 0000000..9724f6a --- /dev/null +++ b/chrome/browser/resources/keyboard_overlay.html @@ -0,0 +1,10 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8" content="text/html"> +<title i18n-content="keyboardOverlayTitle"></title> +<link rel="stylesheet" href="keyboard_overlay.css"> +<script src="keyboard_overlay_data.js"></script> +<script src="keyboard_overlay.js"></script> +<body class="keyboard-overlay-keyboard"></body> +</html> diff --git a/chrome/browser/resources/keyboard_overlay.js b/chrome/browser/resources/keyboard_overlay.js new file mode 100644 index 0000000..f562dd0 --- /dev/null +++ b/chrome/browser/resources/keyboard_overlay.js @@ -0,0 +1,406 @@ +// 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. + +var BASE_KEYBOARD = { + top: 0, + left: 0, + width: 1237, + height: 468 +}; + +var BASE_INSTRUCTIONS = { + top: 174, + left: 370, + width: 498, + height: 81 +}; + +var LABEL_TO_KEY_TEXT = { + alt: 'alt', + backspace: 'backspace', + ctrl: 'ctrl', + enter: 'enter', + esc: 'esc', + glyph_arrow_down: 'down', + glyph_arrow_left: 'left', + glyph_arrow_right: 'right', + glyph_arrow_up: 'up', + glyph_back: 'back', + glyph_backspace: 'backspace', + glyph_brightness_down: 'bright down', + glyph_brightness_up: 'bright up', + glyph_enter: 'enter', + glyph_forward: 'forward', + glyph_fullscreen: 'fullscreen', + glyph_overview: 'windows', + glyph_power: 'power', + glyph_reload: 'reload', + glyph_reload: 'reload', + glyph_search: 'search', + glyph_tab: 'tab', + glyph_tools: 'tools', + glyph_volume_down: 'vol. down', + glyph_volume_mute: 'mute', + glyph_volume_up: 'vol. up', + shift: 'shift', + tab: 'tab' +}; + +var MODIFIER_TO_CLASS = { + 'SHIFT': 'modifier-shift', + 'CTRL': 'modifier-ctrl', + 'ALT': 'modifier-alt' +}; + +var IDENTIFIER_TO_CLASS = { + '2A': 'is-shift', + '1D': 'is-ctrl', + '38': 'is-alt' +}; + +var languageCode = 'en_US'; + +/** + * Returns layouts data. + */ +function getLayouts() { + return keyboardOverlayData['layouts']; +} + +/** + * Returns shortcut data. + */ +function getShortcutData() { + return keyboardOverlayData['shortcut']; +} + +/** + * Returns the language code of the system. + */ +function getLanguageCode() { + // TODO(mazda): Retrieve the language code from the system. + return languageCode; +} + +/** + * Returns keyboard glyph data. + */ +function getKeyboardGlyphData() { + return keyboardOverlayData['keyboardGlyph'][getLanguageCode()]; +} + +/** + * Converts a single hex number to a character. + */ +function hex2char(hex) { + if (!hex) { + return ''; + } + var result = ''; + var n = parseInt(hex, 16); + if (n <= 0xFFFF) { + result += String.fromCharCode(n); + } else if (n <= 0x10FFFF) { + n -= 0x10000; + result += (String.fromCharCode(0xD800 | (n >> 10)) + + String.fromCharCode(0xDC00 | (n & 0x3FF))); + } else { + console.error('hex2Char error: Code point out of range :' + hex); + } + return result; +} + +/** + * Returns a list of modifiers from the key event. + */ +function getModifiers(e) { + if (!e) { + return []; + } + var modifiers = []; + if (e.keyCode == 16 || e.shiftKey) { + modifiers.push('SHIFT'); + } + if (e.keyCode == 17 || e.ctrlKey) { + modifiers.push('CTRL'); + } + if (e.keyCode == 18 || e.altKey) { + modifiers.push('ALT'); + } + return modifiers.sort(); +} + +/** + * Returns an ID of the key. + */ +function keyId(identifier, i) { + return identifier + '-key-' + i; +} + +/** + * Returns an ID of the text on the key. + */ +function keyTextId(identifier, i) { + return identifier + '-key-text-' + i; +} + +/** + * Returns an ID of the shortcut text. + */ +function shortcutTextId(identifier, i) { + return identifier + '-shortcut-text-' + i; +} + +/** + * Returns true if |list| contains |e|. + */ +function contains(list, e) { + return list.indexOf(e) != -1; +} + +/** + * Returns a list of the class names corresponding to the identifier and + * modifiers. + */ +function getKeyClasses(identifier, modifiers) { + var classes = ['keyboard-overlay-key']; + for (var i = 0; i < modifiers.length; ++i) { + classes.push(MODIFIER_TO_CLASS[modifiers[i]]); + } + + if ((identifier == '2A' && contains(modifiers, 'SHIFT')) || + (identifier == '1D' && contains(modifiers, 'CTRL')) || + (identifier == '38' && contains(modifiers, 'ALT'))) { + classes.push('pressed'); + classes.push(IDENTIFIER_TO_CLASS[identifier]); + } + return classes; +} + +/** + * Returns true if a character is a ASCII character. + */ +function isAscii(c) { + var charCode = c.charCodeAt(0); + return 0x00 <= charCode && charCode <= 0x7F; +} + +/** + * Returns a label of the key. + */ +function getKeyLabel(keyData, modifiers) { + if (!keyData) { + return ''; + } + if (keyData.label in LABEL_TO_KEY_TEXT) { + return LABEL_TO_KEY_TEXT[keyData.label]; + } + var keyLabel = ''; + for (var j = 1; j <= 9; j++) { + var pos = keyData['p' + j]; + if (!pos) { + continue; + } + if (LABEL_TO_KEY_TEXT[pos]) { + return LABEL_TO_KEY_TEXT[pos]; + } + keyLabel = hex2char(pos); + if (!keyLabel) { + continue; + } + if (isAscii(keyLabel) && + getShortcutData()[getAction(keyLabel, modifiers)]) { + break; + } + } + return keyLabel; +} + +/** + * Returns a normalized string used for a key of shortcutData. + */ +function getAction(keycode, modifiers) { + return [keycode].concat(modifiers).join(' '); +} + +/** + * Returns a text which displayed on a key. + */ +function getKeyTextValue(keyData) { + if (LABEL_TO_KEY_TEXT[keyData.label]) { + return LABEL_TO_KEY_TEXT[keyData.label]; + } + + var chars = []; + for (var j = 1; j <= 9; ++j) { + var pos = keyData['p' + j]; + if (LABEL_TO_KEY_TEXT[pos]) { + return LABEL_TO_KEY_TEXT[pos]; + } + if (pos && pos.length > 0) { + chars.push(hex2char(pos)); + } + } + return chars.join(' '); +} + +/** + * Updates the whole keyboard. + */ +function update(e) { + var modifiers = getModifiers(e); + var instructions = document.getElementById('instructions'); + if (modifiers.length == 0) { + instructions.style.visibility = 'visible'; + } else { + instructions.style.visibility = 'hidden'; + } + + var keyboardGlyphData = getKeyboardGlyphData(); + var shortcutData = getShortcutData(); + var layout = getLayouts()[keyboardGlyphData.layoutName]; + for (var i = 0; i < layout.length; ++i) { + var identifier = layout[i][0]; + var keyData = keyboardGlyphData.keys[identifier]; + var classes = getKeyClasses(identifier, modifiers, keyData); + var keyLabel = getKeyLabel(keyData, modifiers); + var shortcutId = shortcutData[getAction(keyLabel, modifiers)]; + if (shortcutId) { + classes.push('is-shortcut'); + } + + var key = document.getElementById(keyId(identifier, i)); + key.className = classes.join(' '); + + if (!keyData) { + continue; + } + + var keyText = document.getElementById(keyTextId(identifier, i)); + var keyTextValue = getKeyTextValue(keyData); + if (keyTextValue) { + keyText.style.visibility = 'visible'; + } else { + keyText.style.visibility = 'hidden'; + } + keyText.textContent = keyTextValue; + + var shortcutText = document.getElementById(shortcutTextId(identifier, i)); + if (shortcutId) { + shortcutText.style.visibility = 'visible'; + shortcutText.textContent = templateData[shortcutId]; + } else { + shortcutText.style.visibility = 'hidden'; + } + + if (keyData.format) { + var format = keyData.format; + if (format == 'left' || format == 'right') { + shortcutText.style.textAlign = format; + keyText.style.textAlign = format; + } + } + } +} + +/** + * A callback furnction for the onkeydown event. + */ +function keydown(e) { + update(e); +} + +/** + * A callback furnction for the onkeyup event. + */ +function keyup(e) { + update(); +} + +/** + * Initializes the layout of the keys. + */ +function initLayout() { + var layout = getLayouts()[getKeyboardGlyphData().layoutName]; + var keyboard = document.body; + var minX = window.innerWidth; + var maxX = 0; + var minY = window.innerHeight; + var maxY = 0; + var multiplier = 1.38 * window.innerWidth / BASE_KEYBOARD.width; + var keyMargin = 7; + var offsetX = 10; + var offsetY = 7; + for (var i = 0; i < layout.length; i++) { + var array = layout[i]; + var identifier = array[0]; + var x = Math.round((array[1] + offsetX) * multiplier); + var y = Math.round((array[2] + offsetY) * multiplier); + var w = Math.round((array[3] - keyMargin) * multiplier); + var h = Math.round((array[4] - keyMargin) * multiplier); + + var key = document.createElement('div'); + key.id = keyId(identifier, i); + key.className = 'keyboard-overlay-key'; + key.style.left = x + 'px'; + key.style.top = y + 'px'; + key.style.width = w + 'px'; + key.style.height = h + 'px'; + + var keyText = document.createElement('div'); + keyText.id = keyTextId(identifier, i); + keyText.className = 'keyboard-overlay-key-text'; + keyText.style.visibility = 'hidden'; + key.appendChild(keyText); + + var shortcutText = document.createElement('div'); + shortcutText.id = shortcutTextId(identifier, i); + shortcutText.className = 'keyboard-overlay-shortcut-text'; + shortcutText.style.visilibity = 'hidden'; + key.appendChild(shortcutText); + keyboard.appendChild(key); + + minX = Math.min(minX, x); + maxX = Math.max(maxX, x + w); + minY = Math.min(minY, y); + maxY = Math.max(maxY, y + h); + } + + var width = maxX - minX + 1; + var height = maxY - minY + 1; + keyboard.style.width = (width + 2 * (minX + 1)) + 'px'; + keyboard.style.height = (height + 2 * (minY + 1)) + 'px'; + + var instructions = document.createElement('instructions'); + instructions = document.createElement('div'); + instructions.id = 'instructions'; + instructions.className = 'keyboard-overlay-instructions'; + instructions.style.left = ((BASE_INSTRUCTIONS.left - BASE_KEYBOARD.left) * + width / BASE_KEYBOARD.width + minX) + 'px'; + instructions.style.top = ((BASE_INSTRUCTIONS.top - BASE_KEYBOARD.top) * + height / BASE_KEYBOARD.height + minY) + 'px'; + instructions.style.width = (width * BASE_INSTRUCTIONS.width / + BASE_KEYBOARD.width) + 'px'; + instructions.style.height = (height * BASE_INSTRUCTIONS.height / + BASE_KEYBOARD.height) + 'px'; + + var instructionsText = document.createElement('span'); + instructionsText.id = 'instructions-text'; + instructionsText.className = 'keyboard-overlay-instructions-text'; + instructionsText.innerHTML = templateData.keyboardOverlayInstructions; + instructions.appendChild(instructionsText); + keyboard.appendChild(instructions); +} + +/** + * A callback function for the onload event of the body element. + */ +function init() { + document.addEventListener('keydown', keydown); + document.addEventListener('keyup', keyup); + initLayout(); + update(); +} + +document.addEventListener('DOMContentLoaded', init); diff --git a/chrome/browser/resources/keyboard_overlay_data.js b/chrome/browser/resources/keyboard_overlay_data.js new file mode 100644 index 0000000..9d590af --- /dev/null +++ b/chrome/browser/resources/keyboard_overlay_data.js @@ -0,0 +1,22941 @@ +var keyboardOverlayData = { + "keyboardGlyph": { + "ar": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! \n1 \u0661", + "p1": "21", + "p7": "31", + "p9": "661", + "position": "110" + }, + "03": { + "key": "@ \n2 \u0662", + "p1": "40", + "p7": "32", + "p9": "662", + "position": "111" + }, + "04": { + "key": "# \n3 \u0663", + "p1": "23", + "p7": "33", + "p9": "663", + "position": "112" + }, + "05": { + "key": "$ \n4 \u0664", + "p1": "24", + "p7": "34", + "p9": "664", + "position": "113" + }, + "06": { + "key": "\u066a \n5 \u0665", + "p1": "66A", + "p7": "35", + "p9": "665", + "position": "114" + }, + "07": { + "key": "^ \n6 \u0666", + "p1": "5E", + "p7": "36", + "p9": "666", + "position": "115" + }, + "08": { + "key": "& \n7 \u0667", + "p1": "26", + "p7": "37", + "p9": "667", + "position": "116" + }, + "09": { + "key": "\u066d \n8 \u0668", + "p1": "66D", + "p7": "38", + "p9": "668", + "position": "117" + }, + "0A": { + "key": "( \n9 \u0669", + "p1": "28", + "p7": "39", + "p9": "669", + "position": "118" + }, + "0B": { + "key": ") \n0 \u0660", + "p1": "29", + "p7": "30", + "p9": "660", + "position": "119" + }, + "0C": { + "key": "_ \n- ", + "p1": "5F", + "p7": "2D", + "position": "120" + }, + "0D": { + "key": "+ \n= ", + "p1": "2B", + "p7": "3D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " \u064e\nq \ufebf", + "p3": "064E", + "p7": "71", + "p9": "FEBF", + "position": "210" + }, + "11": { + "key": " \u064b\nw \ufebb", + "p3": "064B", + "p7": "77", + "p9": "FEBB", + "position": "211" + }, + "12": { + "key": " \u064f\ne \ufe9b", + "p3": "064F", + "p7": "65", + "p9": "FE9B", + "position": "212" + }, + "13": { + "key": " \u064c\nr \ufed7", + "p3": "064C", + "p7": "72", + "p9": "FED7", + "position": "213" + }, + "14": { + "key": " \ufef9\nt \ufed3", + "p3": "FEF9", + "p7": "74", + "p9": "FED3", + "position": "214" + }, + "15": { + "key": " \ufe87\ny \ufecf", + "p3": "FE87", + "p7": "79", + "p9": "FECF", + "position": "215" + }, + "16": { + "key": " `\nu \ufecb", + "p3": "60", + "p7": "75", + "p9": "FECB", + "position": "216" + }, + "17": { + "key": " \u00f7\ni \ufeeb", + "p3": "F7", + "p7": "69", + "p9": "FEEB", + "position": "217" + }, + "18": { + "key": " \u00d7\no \ufea7", + "p3": "D7", + "p7": "6F", + "p9": "FEA7", + "position": "218" + }, + "19": { + "key": " \u061b\np \ufea3", + "p3": "61B", + "p7": "70", + "p9": "FEA3", + "position": "219" + }, + "1A": { + "key": " <\n \ufe9f", + "p3": "3C", + "p9": "FE9F", + "position": "220" + }, + "1B": { + "key": " >\n \ufea9", + "p3": "3E", + "p9": "FEA9", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": " \u0650\na \ufeb7", + "p3": "650", + "p7": "61", + "p9": "FEB7", + "position": "310" + }, + "1F": { + "key": " \u064d\ns \ufeb3", + "p3": "064D", + "p7": "73", + "p9": "FEB3", + "position": "311" + }, + "20": { + "key": " [\nd \ufef3", + "p3": "005B", + "p7": "64", + "p9": "FEF3", + "position": "312" + }, + "21": { + "key": " ]\nf \ufe91", + "p3": "005D", + "p7": "66", + "p9": "FE91", + "position": "313" + }, + "22": { + "key": " \ufef7\ng \ufedf", + "p3": "FEF7", + "p7": "67", + "p9": "FEDF", + "position": "314" + }, + "23": { + "key": " \ufe83\nh \ufe8d", + "p3": "FE83", + "p7": "68", + "p9": "FE8D", + "position": "315" + }, + "24": { + "key": " \u0640\nj \ufe97", + "p3": "640", + "p7": "6A", + "p9": "FE97", + "position": "316" + }, + "25": { + "key": " \u060c\nk \ufee7", + "p3": "060C", + "p7": "6B", + "p9": "FEE7", + "position": "317" + }, + "26": { + "key": " /\nl \ufee3", + "p3": "002F", + "p7": "6C", + "p9": "FEE3", + "position": "318" + }, + "27": { + "key": " :\n \ufedb", + "p3": "003A", + "p9": "FEDB", + "position": "319" + }, + "28": { + "key": " \"\n \ufec3", + "p3": "22", + "p9": "FEC3", + "position": "320" + }, + "29": { + "key": "~ \ufe7c\n` \ufeab", + "p1": "007E", + "p3": "FE7C", + "p7": "60", + "p9": "FEAB", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "| \n\\ ", + "p1": "007c", + "p7": "005c", + "position": "290" + }, + "2C": { + "key": " ~\nz \ufe8b", + "p3": "007E", + "p7": "7A", + "p9": "FE8B", + "position": "410" + }, + "2D": { + "key": " \ufe7e\nx \ufe80", + "p3": "FE7E", + "p7": "78", + "p9": "FE80", + "position": "411" + }, + "2E": { + "key": " {\nc \ufe85", + "p3": "007B", + "p7": "63", + "p9": "FE85", + "position": "412" + }, + "2F": { + "key": " }\nv \ufead", + "p3": "007D", + "p7": "76", + "p9": "FEAD", + "position": "413" + }, + "30": { + "key": " \ufef5\nb \ufefb", + "p3": "FEF5", + "p7": "62", + "p9": "FEFB", + "position": "414" + }, + "31": { + "key": " \ufe81\nn \ufeef", + "p3": "FE81", + "p7": "6E", + "p9": "FEEF", + "position": "415" + }, + "32": { + "key": " '\nm \ufe93", + "p3": "27", + "p7": "6D", + "p9": "FE93", + "position": "416" + }, + "33": { + "key": " ,\n \ufeed", + "p3": "002C", + "p9": "FEED", + "position": "417" + }, + "34": { + "key": " .\n \ufeaf", + "p3": "002E", + "p9": "FEAF", + "position": "418" + }, + "35": { + "key": " \u061f\n \ufec7", + "p3": "61F", + "p9": "FEC7", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "ar_fr": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! \n1 ", + "p1": "21", + "p7": "31", + "position": "110" + }, + "03": { + "key": "@ \n2 ", + "p1": "40", + "p7": "32", + "position": "111" + }, + "04": { + "key": "# \n3 ", + "p1": "23", + "p7": "33", + "position": "112" + }, + "05": { + "key": "$ \n4 ", + "p1": "24", + "p7": "34", + "position": "113" + }, + "06": { + "key": "\u066a \n5 ", + "p1": "66A", + "p7": "35", + "position": "114" + }, + "07": { + "key": "^ \n6 ", + "p1": "5E", + "p7": "36", + "position": "115" + }, + "08": { + "key": "& \n7 ", + "p1": "26", + "p7": "37", + "position": "116" + }, + "09": { + "key": "\u066d \n8 ", + "p1": "66D", + "p7": "38", + "position": "117" + }, + "0A": { + "key": "( \n9 ", + "p1": "28", + "p7": "39", + "position": "118" + }, + "0B": { + "key": ") \n0 ", + "p1": "29", + "p7": "30", + "position": "119" + }, + "0C": { + "key": "_ \n- ", + "p1": "5F", + "p7": "2D", + "position": "120" + }, + "0D": { + "key": "+ \n= ", + "p1": "2B", + "p7": "3D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "\u064e q\n\ufebf ", + "p1": "064E", + "p5": "71", + "p7": "FEBF", + "position": "210" + }, + "11": { + "key": "\u064b w\n\ufebb ", + "p1": "064B", + "p5": "77", + "p7": "FEBB", + "position": "211" + }, + "12": { + "key": "\u064f e\n\ufe9b ", + "p1": "064F", + "p5": "65", + "p7": "FE9B", + "position": "212" + }, + "13": { + "key": "\u064c r\n\ufed7 ", + "p1": "064C", + "p5": "72", + "p7": "FED7", + "position": "213" + }, + "14": { + "key": "\ufef9 t\n\ufed3 ", + "p1": "FEF9", + "p5": "74", + "p7": "FED3", + "position": "214" + }, + "15": { + "key": "\ufe87 y\n\ufecf ", + "p1": "FE87", + "p5": "79", + "p7": "FECF", + "position": "215" + }, + "16": { + "key": "` u\n\ufecb ", + "p1": "60", + "p5": "75", + "p7": "FECB", + "position": "216" + }, + "17": { + "key": "\u00f7 i\n\ufeeb ", + "p1": "F7", + "p5": "69", + "p7": "FEEB", + "position": "217" + }, + "18": { + "key": "\u00d7 o\n\ufea7 ", + "p1": "D7", + "p5": "6F", + "p7": "FEA7", + "position": "218" + }, + "19": { + "key": "\u061b p\n\ufea3 ", + "p1": "61B", + "p5": "70", + "p7": "FEA3", + "position": "219" + }, + "1A": { + "key": "< \n\ufe9f ", + "p1": "3C", + "p7": "FE9F", + "position": "220" + }, + "1B": { + "key": "> \n\ufea9 ", + "p1": "3E", + "p7": "FEA9", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "\u0650 a\n\ufeb7 ", + "p1": "650", + "p5": "61", + "p7": "FEB7", + "position": "310" + }, + "1F": { + "key": "\u064d s\n\ufeb3 ", + "p1": "064D", + "p5": "73", + "p7": "FEB3", + "position": "311" + }, + "20": { + "key": "[ d\n\ufef3 ", + "p1": "005B", + "p5": "64", + "p7": "FEF3", + "position": "312" + }, + "21": { + "key": "] f\n\ufe91 ", + "p1": "005D", + "p5": "66", + "p7": "FE91", + "position": "313" + }, + "22": { + "key": "\ufef7 g\n\ufedf ", + "p1": "FEF7", + "p5": "67", + "p7": "FEDF", + "position": "314" + }, + "23": { + "key": "\ufe83 h\n\ufe8d ", + "p1": "FE83", + "p5": "68", + "p7": "FE8D", + "position": "315" + }, + "24": { + "key": "\u0640 j\n\ufe97 ", + "p1": "640", + "p5": "6A", + "p7": "FE97", + "position": "316" + }, + "25": { + "key": "\u060c k\n\ufee7 ", + "p1": "060C", + "p5": "6B", + "p7": "FEE7", + "position": "317" + }, + "26": { + "key": "/ l\n\ufee3 ", + "p1": "002F", + "p5": "6C", + "p7": "FEE3", + "position": "318" + }, + "27": { + "key": ": \n\ufedb ", + "p1": "003A", + "p7": "FEDB", + "position": "319" + }, + "28": { + "key": "\" \n\ufec3 ", + "p1": "22", + "p7": "FEC3", + "position": "320" + }, + "29": { + "key": "\ufe7c \n\ufeab ", + "p1": "FE7C", + "p7": "FEAB", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "| \n\\ ", + "p1": "007c", + "p7": "005c", + "position": "290" + }, + "2C": { + "key": "~ z\n\ufe8b ", + "p1": "007E", + "p5": "7A", + "p7": "FE8B", + "position": "410" + }, + "2D": { + "key": "\ufe7e x\n\ufe80 ", + "p1": "FE7E", + "p5": "78", + "p7": "FE80", + "position": "411" + }, + "2E": { + "key": "{ c\n\ufe85 ", + "p1": "007B", + "p5": "63", + "p7": "FE85", + "position": "412" + }, + "2F": { + "key": "} v\n\ufead ", + "p1": "007D", + "p5": "76", + "p7": "FEAD", + "position": "413" + }, + "30": { + "key": "\ufef5 b\n\ufefb ", + "p1": "FEF5", + "p5": "62", + "p7": "FEFB", + "position": "414" + }, + "31": { + "key": "\ufe81 n\n\ufeef ", + "p1": "FE81", + "p5": "6E", + "p7": "FEEF", + "position": "415" + }, + "32": { + "key": "' m\n\ufe93 ", + "p1": "27", + "p5": "6D", + "p7": "FE93", + "position": "416" + }, + "33": { + "key": ", \n\ufeed ", + "p1": "002C", + "p7": "FEED", + "position": "417" + }, + "34": { + "key": ". \n\ufeaf ", + "p1": "002E", + "p7": "FEAF", + "position": "418" + }, + "35": { + "key": "\u061f \n\ufec7 ", + "p1": "61F", + "p7": "FEC7", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "bg": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! \n1 ", + "p1": "21", + "p7": "31", + "position": "110" + }, + "03": { + "key": "@ ?\n2 ", + "p1": "40", + "p3": "003f", + "p7": "32", + "position": "111" + }, + "04": { + "key": "# +\n3 ", + "p1": "23", + "p3": "002b", + "p7": "33", + "position": "112" + }, + "05": { + "key": "$ \"\n4 ", + "p1": "24", + "p3": "22", + "p7": "34", + "position": "113" + }, + "06": { + "key": "% \n5 ", + "p1": "25", + "p7": "35", + "position": "114" + }, + "07": { + "key": "^ =\n6 ", + "p1": "5e", + "p3": "003d", + "p7": "36", + "position": "115" + }, + "08": { + "key": "& :\n7 ", + "p1": "26", + "p3": "003a", + "p7": "37", + "position": "116" + }, + "09": { + "key": "* /\n8 ", + "p1": "2a", + "p3": "002f", + "p7": "38", + "position": "117" + }, + "0A": { + "key": "( _\n9 ", + "p1": "28", + "p3": "005f", + "p7": "39", + "position": "118" + }, + "0B": { + "key": ") \u2116\n0 ", + "p1": "29", + "p3": "2116", + "p7": "30", + "position": "119" + }, + "0C": { + "key": "_ I\n- ", + "p1": "5f", + "p3": "49", + "p7": "002d", + "position": "120" + }, + "0D": { + "key": "+ V\n= .", + "p1": "2b", + "p3": "56", + "p7": "3d", + "p9": "002e", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q \u044b\n ,", + "p1": "71", + "p3": "44b", + "p9": "2c", + "position": "210" + }, + "11": { + "key": "w \n \u0443", + "p1": "77", + "p9": "443", + "position": "211" + }, + "12": { + "key": "e \n \u0435", + "p1": "65", + "p9": "435", + "position": "212" + }, + "13": { + "key": "r \n \u0438", + "p1": "72", + "p9": "438", + "position": "213" + }, + "14": { + "key": "t \n \u0448", + "p1": "74", + "p9": "448", + "position": "214" + }, + "15": { + "key": "y \n \u0449", + "p1": "79", + "p9": "449", + "position": "215" + }, + "16": { + "key": "u \n \u043a", + "p1": "75", + "p9": "43a", + "position": "216" + }, + "17": { + "key": "i \n \u0441", + "p1": "69", + "p9": "441", + "position": "217" + }, + "18": { + "key": "o \n \u0434", + "p1": "6f", + "p9": "434", + "position": "218" + }, + "19": { + "key": "p \n \u0437", + "p1": "70", + "p9": "437", + "position": "219" + }, + "1A": { + "key": "{ \n[ \u0446", + "p1": "7b", + "p7": "5b", + "p9": "446", + "position": "220" + }, + "1B": { + "key": "} \u00a7\n] ;", + "p1": "7d", + "p3": "a7", + "p7": "5d", + "p9": "3b", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a \n \u044c", + "p1": "61", + "p9": "44c", + "position": "310" + }, + "1F": { + "key": "s \n \u044f", + "p1": "73", + "p9": "44f", + "position": "311" + }, + "20": { + "key": "d \n \u0430", + "p1": "64", + "p9": "430", + "position": "312" + }, + "21": { + "key": "f \n \u043e", + "p1": "66", + "p9": "43e", + "position": "313" + }, + "22": { + "key": "g \n \u0436", + "p1": "67", + "p9": "436", + "position": "314" + }, + "23": { + "key": "h \n \u0433", + "p1": "68", + "p9": "433", + "position": "315" + }, + "24": { + "key": "j \n \u0442", + "p1": "6a", + "p9": "442", + "position": "316" + }, + "25": { + "key": "k \n \u043d", + "p1": "6b", + "p9": "43d", + "position": "317" + }, + "26": { + "key": "l \n \u0432", + "p1": "6c", + "p9": "432", + "position": "318" + }, + "27": { + "key": ": \n; \u043c", + "p1": "3a", + "p7": "3b", + "p9": "43c", + "position": "319" + }, + "28": { + "key": "\u201e \n' \u0447", + "p1": "201E", + "p7": "27", + "p9": "447", + "position": "320" + }, + "29": { + "key": "~ \n` ", + "p1": "007e", + "p7": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "| )\n\\ (", + "p1": "7c", + "p3": "29", + "p7": "5c", + "p9": "28", + "position": "290" + }, + "2C": { + "key": "z \n \u044e", + "p1": "7a", + "p9": "44e", + "position": "410" + }, + "2D": { + "key": "x \n \u0439", + "p1": "78", + "p9": "439", + "position": "411" + }, + "2E": { + "key": "c \n \u044a", + "p1": "63", + "p9": "44a", + "position": "412" + }, + "2F": { + "key": "v \n \u044d", + "p1": "76", + "p9": "44d", + "position": "413" + }, + "30": { + "key": "b \n \u0444", + "p1": "62", + "p9": "444", + "position": "414" + }, + "31": { + "key": "n \n \u0445", + "p1": "6e", + "p9": "445", + "position": "415" + }, + "32": { + "key": "m \n \u043f", + "p1": "6d", + "p9": "43f", + "position": "416" + }, + "33": { + "key": "< \n, \u0440", + "p1": "3c", + "p7": "2c", + "p9": "440", + "position": "417" + }, + "34": { + "key": "> \n. \u043b", + "p1": "3e", + "p7": "2e", + "p9": "43b", + "position": "418" + }, + "35": { + "key": "? \n/ \u0431", + "p1": "3f", + "p7": "2f", + "p9": "431", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "notes": " ", + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "notes": "does not exist in the layout U for Bulgarian", + "position": "420" + }, + "79": { + "format": "smaller", + "notes": "does not exist in the layout U for Bulgarian", + "position": "551" + }, + "7B": { + "format": "smaller", + "notes": "does not exist in the layout U for Bulgarian", + "position": "550" + }, + "7D": { + "notes": "does not exist in U layout for Bulgarian", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "ca": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " !\n 1 |", + "p2": "21", + "p8": "31", + "p9": "007C", + "position": "110" + }, + "03": { + "key": " \"\n 2 @", + "p2": "22", + "p8": "32", + "p9": "40", + "position": "111" + }, + "04": { + "key": " \u00b7\n 3 #", + "p2": "00B7", + "p8": "33", + "p9": "23", + "position": "112" + }, + "05": { + "key": " $\n 4 ~", + "p2": "24", + "p8": "34", + "p9": "007E", + "position": "113" + }, + "06": { + "key": " %\n 5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": " &\n 6 \u00ac", + "p2": "26", + "p8": "36", + "p9": "00AC", + "position": "115" + }, + "08": { + "key": " /\n 7", + "p2": "002F", + "p8": "37", + "position": "116" + }, + "09": { + "key": " (\n 8", + "p2": "28", + "p8": "38", + "position": "117" + }, + "0A": { + "key": " )\n 9", + "p2": "29", + "p8": "39", + "position": "118" + }, + "0B": { + "key": " =\n 0", + "p2": "003D", + "p8": "30", + "position": "119" + }, + "0C": { + "key": " ?\n '", + "p2": "003F", + "p8": "27", + "position": "120" + }, + "0D": { + "key": "\u00bf\n\u00a1", + "p2": "00BF", + "p8": "00A1", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "^\n` [", + "p2": "005E", + "p8": "60", + "p9": "005B", + "position": "220" + }, + "1B": { + "key": "*\n+ ]", + "p2": "002A", + "p8": "002B", + "p9": "005D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "006B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "006C", + "position": "318" + }, + "27": { + "key": "\u00f1", + "p5": "00F1", + "position": "319" + }, + "28": { + "key": "\u00a8\n\u00b4 {", + "p2": "00A8", + "p8": "00B4", + "p9": "007B", + "position": "320" + }, + "29": { + "key": " \u00aa\n \u00ba \\", + "p2": "00AA", + "p8": "00BA", + "p9": "005C", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u00c7\n\u00e7 }", + "p2": "00C7", + "p8": "E7", + "p9": "007D", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "007A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "006E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "006D", + "position": "416" + }, + "33": { + "key": ";\n,", + "p2": "003B", + "p8": "002C", + "position": "417" + }, + "34": { + "key": ":\n.", + "p2": "003A", + "p8": "002E", + "position": "418" + }, + "35": { + "key": "_\n-", + "p2": "005F", + "p8": "002D", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": ">\n<", + "p2": "003E", + "p8": "003C", + "position": "409" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "cs": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! 1\n1 +", + "p1": "21", + "p3": "31", + "p7": "31", + "p9": "002b", + "position": "110" + }, + "03": { + "key": "@ 2\n2 \u011b", + "p1": "40", + "p3": "32", + "p7": "32", + "p9": "011B", + "position": "111" + }, + "04": { + "key": "# 3\n3 \u0161", + "p1": "23", + "p3": "33", + "p7": "33", + "p9": "161", + "position": "112" + }, + "05": { + "key": "$ 4\n4 \u010d", + "p1": "24", + "p3": "34", + "p7": "34", + "p9": "010d", + "position": "113" + }, + "06": { + "key": "% 5\n5 \u0159", + "p1": "25", + "p3": "35", + "p7": "35", + "p9": "159", + "position": "114" + }, + "07": { + "key": "^ 6\n6 \u017e", + "p1": "5e", + "p3": "36", + "p7": "36", + "p9": "017e", + "position": "115" + }, + "08": { + "key": "& 7\n7 \u00fd", + "p1": "26", + "p3": "37", + "p7": "37", + "p9": "fd", + "position": "116" + }, + "09": { + "key": "* 8\n8 \u00e1", + "p1": "2a", + "p3": "38", + "p7": "38", + "p9": "e1", + "position": "117" + }, + "0A": { + "key": "( 9\n9 \u00ed", + "p1": "28", + "p3": "39", + "p7": "39", + "p9": "ed", + "position": "118" + }, + "0B": { + "key": ") 0\n0 \u00e9", + "p1": "29", + "p3": "30", + "p7": "30", + "p9": "e9", + "position": "119" + }, + "0C": { + "key": "_ %\n- =", + "p1": "5f", + "p3": "25", + "p7": "2d", + "p9": "3d", + "position": "120" + }, + "0D": { + "key": "+ \u02c7\n= \u00b4", + "p1": "2b", + "p3": "2c7", + "p7": "3d", + "p9": "b4", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e \u20ac", + "p5": "65", + "p9": "20ac", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "z y", + "p5": "7A", + "p9": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "6F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "{ /\n[ \u00fa", + "p1": "7b", + "p3": "002f", + "p7": "5b", + "p9": "fa", + "position": "220" + }, + "1B": { + "key": "} (\n] )", + "p1": "7d", + "p3": "28", + "p7": "5d", + "p9": "29", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": ": \"\n; \u016f", + "p1": "3a", + "p3": "22", + "p7": "3b", + "p9": "16f", + "position": "319" + }, + "28": { + "key": "\" !\n' \u00a7", + "p1": "22", + "p3": "21", + "p7": "27", + "p9": "a7", + "position": "320" + }, + "29": { + "key": "~ \u00b0\n` ;", + "p1": "7e", + "p3": "b0", + "p7": "60", + "p9": "3b", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "| '\n\\ \u00a8", + "p1": "007c", + "p3": "27", + "p7": "5c", + "p9": "a8", + "position": "290" + }, + "2C": { + "key": "y z", + "p5": "79", + "p9": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": "< ?\n, ,", + "p1": "3c", + "p3": "3f", + "p7": "2c", + "p9": "2c", + "position": "417" + }, + "34": { + "key": "> :\n. .", + "p1": "3e", + "p3": "3a", + "p7": "2e", + "p9": "2e", + "position": "418" + }, + "35": { + "key": "? _\n/ -", + "p1": "3f", + "p3": "5f", + "p7": "2f", + "p9": "2d", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": "| *\n\\ &", + "p1": "7c", + "p3": "2a", + "p7": "5c", + "p9": "26", + "position": "409" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "da": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " ! \n 1 ", + "p1": "21", + "p7": "31", + "position": "110" + }, + "03": { + "key": " \" \n 2 @", + "p1": "22", + "p7": "32", + "p9": "40", + "position": "111" + }, + "04": { + "key": " # \n 3 \u00a3", + "p1": "23", + "p7": "33", + "p9": "00A3", + "position": "112" + }, + "05": { + "key": " \u00a4 \n 4 $", + "p1": "00A4", + "p7": "34", + "p9": "24", + "position": "113" + }, + "06": { + "key": " % \n 5 ", + "p1": "25", + "p7": "35", + "position": "114" + }, + "07": { + "key": " & \n 6 ", + "p1": "26", + "p7": "36", + "position": "115" + }, + "08": { + "key": " / \n 7 {", + "p1": "002F", + "p7": "37", + "p9": "007B", + "position": "116" + }, + "09": { + "key": " ( \n 8 [", + "p1": "28", + "p7": "38", + "p9": "005B", + "position": "117" + }, + "0A": { + "key": " ) \n 9 ]", + "p1": "29", + "p7": "39", + "p9": "005D", + "position": "118" + }, + "0B": { + "key": " = \n 0 }", + "p1": "003D", + "p7": "30", + "p9": "007D", + "position": "119" + }, + "0C": { + "key": " ? \n + ", + "p1": "003F", + "p7": "002B", + "position": "120" + }, + "0D": { + "key": " ` \n \u00b4 |", + "p1": "60", + "p7": "00B4", + "p9": "007C", + "position": "121" + }, + "0E": { + "format": "right", + "key": " backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": " tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " qq ", + "p5": "71", + "position": "210" + }, + "11": { + "key": " ww ", + "p5": "77", + "position": "211" + }, + "12": { + "key": " ee \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": "rr ", + "p5": "72", + "position": "213" + }, + "14": { + "key": "tt ", + "p5": "74", + "position": "214" + }, + "15": { + "key": "yy ", + "p5": "79", + "position": "215" + }, + "16": { + "key": "uu ", + "p5": "75", + "position": "216" + }, + "17": { + "key": "ii ", + "p5": "69", + "position": "217" + }, + "18": { + "key": "oo ", + "p5": "006F", + "position": "218" + }, + "19": { + "key": "pp ", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "\u00e5\u00e5 ", + "p5": "E5", + "position": "220" + }, + "1B": { + "key": "^ \n\u00a8 ~", + "p1": "005E", + "p7": "00A8", + "p9": "007E", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "006B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "006C", + "position": "318" + }, + "27": { + "key": "\u00e6", + "p5": "E6", + "position": "319" + }, + "28": { + "key": "\u00f8", + "p5": "00F8", + "position": "320" + }, + "29": { + "key": " \u00a7 \n \u00bd ", + "p1": "00A7", + "p7": "00BD", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "* \n' ", + "p1": "002A", + "p7": "27", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "007A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "006E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "006D", + "position": "416" + }, + "33": { + "key": "; \n, ", + "p1": "003B", + "p7": "002C", + "position": "417" + }, + "34": { + "key": ": \n. ", + "p1": "003A", + "p7": "002E", + "position": "418" + }, + "35": { + "key": "_ \n- ", + "p1": "005F", + "p7": "002D", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": "> \n< \\", + "p1": "003E", + "p7": "003C", + "p9": "005C", + "position": "409" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "key": " ", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "de": { + "keys": { + "00": { + "key": " glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " !\n 1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": " \"\n 2 \u00b2", + "p2": "22", + "p8": "32", + "p9": "00B2", + "position": "111" + }, + "04": { + "key": " \u00a7\n 3 \u00b3", + "p2": "00A7", + "p8": "33", + "p9": "00B3", + "position": "112" + }, + "05": { + "key": " $\n 4", + "p2": "24", + "p8": "34", + "position": "113" + }, + "06": { + "key": " %\n 5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": " &\n 6", + "p2": "26", + "p8": "36", + "position": "115" + }, + "08": { + "key": " /\n 7 {", + "p2": "002F", + "p8": "37", + "p9": "007B", + "position": "116" + }, + "09": { + "key": " (\n 8 [", + "p2": "28", + "p8": "38", + "p9": "005B", + "position": "117" + }, + "0A": { + "key": " )\n 9 ]", + "p2": "29", + "p8": "39", + "p9": "005D", + "position": "118" + }, + "0B": { + "key": " =\n 0 }", + "p2": "3D", + "p8": "30", + "p9": "7D", + "position": "119" + }, + "0C": { + "key": " ?\n \u00df \\", + "p2": "3F", + "p8": "DF", + "p9": "5C", + "position": "120" + }, + "0D": { + "key": " `\n \u00b4", + "p2": "60", + "p8": "B4", + "position": "121" + }, + "0E": { + "format": "right", + "key": " glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": " glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " q @", + "p5": "71", + "p9": "40", + "position": "210" + }, + "11": { + "key": " w", + "p5": "77", + "position": "211" + }, + "12": { + "key": " e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": " r", + "p5": "72", + "position": "213" + }, + "14": { + "key": " t", + "p5": "74", + "position": "214" + }, + "15": { + "key": " z", + "p5": "007A", + "position": "215" + }, + "16": { + "key": " u", + "p5": "75", + "position": "216" + }, + "17": { + "key": " i", + "p5": "69", + "position": "217" + }, + "18": { + "key": " o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": " p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": " \u00fc", + "p5": "00FC", + "position": "220" + }, + "1B": { + "key": " *\n + ~", + "p2": "2A", + "p8": "2B", + "p9": "7E", + "position": "221" + }, + "1C": { + "format": "right", + "key": " glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": " strg", + "label": "strg", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": " a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": " s", + "p5": "73", + "position": "311" + }, + "20": { + "key": " d", + "p5": "64", + "position": "312" + }, + "21": { + "key": " f", + "p5": "66", + "position": "313" + }, + "22": { + "key": " g", + "p5": "67", + "position": "314" + }, + "23": { + "key": " h", + "p5": "68", + "position": "315" + }, + "24": { + "key": " j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": " k", + "p5": "006B", + "position": "317" + }, + "26": { + "key": " l", + "p5": "006C", + "position": "318" + }, + "27": { + "key": " \u00f6", + "p5": "00F6", + "position": "319" + }, + "28": { + "key": " \u00e4", + "p5": "E4", + "position": "320" + }, + "29": { + "key": " \u00b0\n ^", + "p2": "00B0", + "p8": "005E", + "position": "100" + }, + "2A": { + "format": "left", + "key": " glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": " '\n #", + "p2": "27", + "p8": "23", + "position": "290" + }, + "2C": { + "key": " y", + "p5": "79", + "position": "410" + }, + "2D": { + "key": " x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": " c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": " v", + "p5": "76", + "position": "413" + }, + "30": { + "key": " b", + "p5": "62", + "position": "414" + }, + "31": { + "key": " n", + "p5": "006E", + "position": "415" + }, + "32": { + "key": " m \u00b5", + "p5": "006D", + "p9": "B5", + "position": "416" + }, + "33": { + "key": " ;\n ,", + "p2": "3B", + "p8": "2C", + "position": "417" + }, + "34": { + "key": " :\n .", + "p2": "3A", + "p8": "2E", + "position": "418" + }, + "35": { + "key": " _\n -", + "p2": "5F", + "p8": "2D", + "position": "419" + }, + "36": { + "format": "right", + "key": " glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": " alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": " >\n < |", + "p2": "3E", + "p8": "3C", + "p9": "7C", + "position": "409" + }, + "E0 1D": { + "format": "smaller", + "key": " strg", + "label": "strg", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": " alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": " glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": " glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": " glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": " glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": " glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "el": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "Esc", + "label": "Esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! \n1 ", + "p1": "21", + "p7": "31", + "position": "110" + }, + "03": { + "key": "@ \n2 ", + "p1": "40", + "p7": "32", + "position": "111" + }, + "04": { + "key": "# \n3 ", + "p1": "23", + "p7": "33", + "position": "112" + }, + "05": { + "key": "$ \n4 ", + "p1": "24", + "p7": "34", + "position": "113" + }, + "06": { + "key": "% \n5 ", + "p1": "25", + "p7": "35", + "position": "114" + }, + "07": { + "key": "^ \n6 ", + "p1": "005E", + "p7": "36", + "position": "115" + }, + "08": { + "key": "& \n7 ", + "p1": "26", + "p7": "37", + "position": "116" + }, + "09": { + "key": "* \n8 ", + "p1": "002A", + "p7": "38", + "position": "117" + }, + "0A": { + "key": "( \n9 ", + "p1": "28", + "p7": "39", + "position": "118" + }, + "0B": { + "key": ") \n0 ", + "p1": "29", + "p7": "30", + "position": "119" + }, + "0C": { + "key": "_ \n- ", + "p1": "005F", + "p7": "002D", + "position": "120" + }, + "0D": { + "key": "+ \n= ", + "p1": "002B", + "p7": "003D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "Q :\n ;", + "p1": "51", + "p3": "3A", + "p9": "3B", + "position": "210" + }, + "11": { + "key": "W \n \u03c2", + "p1": "57", + "p9": "3C2", + "position": "211" + }, + "12": { + "key": "E \n \u20ac", + "p1": "45", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": "R \n \u03a1", + "p1": "52", + "p9": "03A1", + "position": "213" + }, + "14": { + "key": "T ", + "p1": "54", + "position": "214" + }, + "15": { + "key": "\u03a5 ", + "p1": "03A5", + "position": "215" + }, + "16": { + "key": "U \n \u0398", + "p1": "55", + "p9": "398", + "position": "216" + }, + "17": { + "key": "\u0399 ", + "p1": "399", + "position": "217" + }, + "18": { + "key": "\u039f ", + "p1": "039F", + "position": "218" + }, + "19": { + "key": "P \n \u03a0", + "p1": "50", + "p9": "03A0", + "position": "219" + }, + "1A": { + "key": "{ \n[ ", + "p1": "007B", + "p7": "005B", + "position": "220" + }, + "1B": { + "key": "} \n] ", + "p1": "007D", + "p7": "005D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "Ctrl", + "label": "Ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "\u0391 ", + "p1": "391", + "position": "310" + }, + "1F": { + "key": "S \n \u03a3", + "p1": "53", + "p9": "03A3", + "position": "311" + }, + "20": { + "key": "D \n \u0394", + "p1": "44", + "p9": "394", + "position": "312" + }, + "21": { + "key": "F \n \u03a6", + "p1": "46", + "p9": "3A6", + "position": "313" + }, + "22": { + "key": "G \n \u0393", + "p1": "47", + "p9": "393", + "position": "314" + }, + "23": { + "key": "\u0397 ", + "p1": "397", + "position": "315" + }, + "24": { + "key": "J \n \u039e", + "p1": "004A", + "p9": "039E", + "position": "316" + }, + "25": { + "key": "\u039a ", + "p1": "039A", + "position": "317" + }, + "26": { + "key": "L \n \u039b", + "p1": "004C", + "p9": "039B", + "position": "318" + }, + "27": { + "key": ": \u00a8\n; \u00b4", + "p1": "003A", + "p3": "00A8", + "p7": "003B", + "p9": "00B4", + "position": "319" + }, + "28": { + "key": "\" \n' ", + "p1": "22", + "p7": "27", + "position": "320" + }, + "29": { + "key": "~ \n` ", + "p1": "007E", + "p7": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "| \n\\ ", + "p1": "007C", + "p7": "005C", + "position": "290" + }, + "2C": { + "key": "\u0396 ", + "p1": "396", + "position": "410" + }, + "2D": { + "key": "\u03a7 ", + "p1": "03A7", + "position": "411" + }, + "2E": { + "key": "C \n \u03a8", + "p1": "43", + "p9": "03A8", + "position": "412" + }, + "2F": { + "key": "V \n \u03a9", + "p1": "56", + "p9": "03A9", + "position": "413" + }, + "30": { + "key": "\u0392 ", + "p1": "392", + "position": "414" + }, + "31": { + "key": "\u039d ", + "p1": "039D", + "position": "415" + }, + "32": { + "key": "\u039c ", + "p1": "039C", + "position": "416" + }, + "33": { + "key": "< \n, ", + "p1": "003C", + "p7": "002C", + "position": "417" + }, + "34": { + "key": "> \n. ", + "p1": "003E", + "p7": "002E", + "position": "418" + }, + "35": { + "key": "? \n/ ", + "p1": "003F", + "p7": "002F", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "Alt", + "label": "Alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "position": "409" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "Ctrl", + "label": "Ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "Alt Gr", + "label": "Alt Gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "en_GB": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "!\n1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": "\"\n2", + "p2": "22", + "p8": "32", + "position": "111" + }, + "04": { + "key": "\u00a3\n3", + "p2": "A3", + "p8": "33", + "position": "112" + }, + "05": { + "key": "$\n4 \u20ac", + "p2": "24", + "p8": "34", + "p9": "20AC", + "position": "113" + }, + "06": { + "key": "%\n5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": "^\n6", + "p2": "5E", + "p8": "36", + "position": "115" + }, + "08": { + "key": "&\n7", + "p2": "26", + "p8": "37", + "position": "116" + }, + "09": { + "key": "*\n8", + "p2": "2A", + "p8": "38", + "position": "117" + }, + "0A": { + "key": "(\n9", + "p2": "28", + "p8": "39", + "position": "118" + }, + "0B": { + "key": ")\n0", + "p2": "29", + "p8": "30", + "position": "119" + }, + "0C": { + "key": "_\n-", + "p2": "5F", + "p8": "2D", + "position": "120" + }, + "0D": { + "key": "+\n=", + "p2": "2B", + "p8": "3D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "6F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "{\n[", + "p2": "7B", + "p8": "5B", + "position": "220" + }, + "1B": { + "key": "}\n]", + "p2": "7D", + "p8": "5D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": ":\n;", + "p2": "3A", + "p8": "3B", + "position": "319" + }, + "28": { + "key": "@\n'", + "p2": "40", + "p8": "27", + "position": "320" + }, + "29": { + "key": "\u00ac\n` \u00a6", + "p2": "AC", + "p8": "60", + "p9": "00A6", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "~\n#", + "p2": "007E", + "p8": "23", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": "<\n,", + "p2": "3C", + "p8": "2C", + "position": "417" + }, + "34": { + "key": ">\n.", + "p2": "3E", + "p8": "2E", + "position": "418" + }, + "35": { + "key": "?\n/", + "p2": "3F", + "p8": "2F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": "|\n\\", + "p2": "007C", + "p8": "005C", + "position": "409" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "en_US": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "!\n1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": "@\n2", + "p2": "40", + "p8": "32", + "position": "111" + }, + "04": { + "key": "#\n3", + "p2": "23", + "p8": "33", + "position": "112" + }, + "05": { + "key": "$\n4", + "p2": "24", + "p8": "34", + "position": "113" + }, + "06": { + "key": "%\n5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": "^\n6", + "p2": "5E", + "p8": "36", + "position": "115" + }, + "08": { + "key": "&\n7", + "p2": "26", + "p8": "37", + "position": "116" + }, + "09": { + "key": "*\n8", + "p2": "2A", + "p8": "38", + "position": "117" + }, + "0A": { + "key": "(\n9", + "p2": "28", + "p8": "39", + "position": "118" + }, + "0B": { + "key": ")\n0", + "p2": "29", + "p8": "30", + "position": "119" + }, + "0C": { + "key": "_\n-", + "p2": "5F", + "p8": "2D", + "position": "120" + }, + "0D": { + "key": "+\n=", + "p2": "2B", + "p8": "3D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "6F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "{\n[", + "p2": "7B", + "p8": "5B", + "position": "220" + }, + "1B": { + "key": "}\n]", + "p2": "7D", + "p8": "5D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": ":\n;", + "p2": "3A", + "p8": "3B", + "position": "319" + }, + "28": { + "key": "\"\n'", + "p2": "22", + "p8": "27", + "position": "320" + }, + "29": { + "key": "~\n`", + "p2": "7E", + "p8": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "|\n\\", + "p2": "7C", + "p8": "5C", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": "<\n,", + "p2": "3C", + "p8": "2C", + "position": "417" + }, + "34": { + "key": ">\n.", + "p2": "3E", + "p8": "2E", + "position": "418" + }, + "35": { + "key": "?\n/", + "p2": "3F", + "p8": "2F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_reload", + "label": "glyph_reload", + "position": "12" + }, + "3E": { + "key": "glyph_overview", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_fullscreen", + "label": "glyph_overview", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "en_US_dvorak": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape" + }, + "02": { + "key": "!\n1", + "p2": "21", + "p8": "31" + }, + "03": { + "key": "@\n2", + "p2": "40", + "p8": "32" + }, + "04": { + "key": "#\n3", + "p2": "23", + "p8": "33" + }, + "05": { + "key": "$\n4", + "p2": "24", + "p8": "34" + }, + "06": { + "key": "%\n5", + "p2": "25", + "p8": "35" + }, + "07": { + "key": "^\n6", + "p2": "5E", + "p8": "36" + }, + "08": { + "key": "&\n7", + "p2": "26", + "p8": "37" + }, + "09": { + "key": "*\n8", + "p2": "2A", + "p8": "38" + }, + "0A": { + "key": "(\n9", + "p2": "28", + "p8": "39" + }, + "0B": { + "key": ")\n0", + "p2": "29", + "p8": "30" + }, + "0C": { + "key": "{\n[", + "p2": "7B", + "p8": "5B" + }, + "0D": { + "key": "}\n]", + "p2": "7D", + "p8": "5D" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab" + }, + "10": { + "key": "\"\n'", + "p2": "22", + "p8": "27" + }, + "11": { + "key": "<\n,", + "p2": "3C", + "p8": "2C" + }, + "12": { + "key": ">\n.", + "p2": "3E", + "p8": "2E" + }, + "13": { + "key": "p", + "p5": "70" + }, + "14": { + "key": "y", + "p5": "79" + }, + "15": { + "key": "f", + "p5": "66" + }, + "16": { + "key": "g", + "p5": "67" + }, + "17": { + "key": "c", + "p5": "63" + }, + "18": { + "key": "r", + "p5": "72" + }, + "19": { + "key": "l", + "p5": "6C" + }, + "1A": { + "key": "?\n/", + "p2": "3F", + "p8": "2F" + }, + "1B": { + "key": "+\n=", + "p2": "2B", + "p8": "3D" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl" + }, + "1E": { + "key": "a", + "p5": "61" + }, + "1F": { + "key": "o", + "p5": "6F" + }, + "20": { + "key": "e", + "p5": "65" + }, + "21": { + "key": "u", + "p5": "75" + }, + "22": { + "key": "i", + "p5": "69" + }, + "23": { + "key": "d", + "p5": "64" + }, + "24": { + "key": "h", + "p5": "68" + }, + "25": { + "key": "t", + "p5": "74" + }, + "26": { + "key": "n", + "p5": "6E" + }, + "27": { + "key": "s", + "p5": "73" + }, + "28": { + "key": "_\n-", + "p2": "5F", + "p8": "2D" + }, + "29": { + "key": "~\n`", + "p2": "7E", + "p8": "60" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift" + }, + "2B": { + "key": "|\n\\", + "p2": "7C", + "p8": "5C" + }, + "2C": { + "key": ":\n;", + "p2": "3A", + "p8": "3B" + }, + "2D": { + "key": "q", + "p5": "71" + }, + "2E": { + "key": "j", + "p5": "6A" + }, + "2F": { + "key": "k", + "p5": "6B" + }, + "30": { + "key": "x", + "p5": "78" + }, + "31": { + "key": "b", + "p5": "62" + }, + "32": { + "key": "m", + "p5": "6D" + }, + "33": { + "key": "w", + "p5": "77" + }, + "34": { + "key": "v", + "p5": "76" + }, + "35": { + "key": "z", + "p5": "7A" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift" + }, + "37": {}, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward" + }, + "3D": { + "key": "glyph_reload", + "label": "glyph_reload" + }, + "3E": { + "key": "glyph_overview", + "label": "glyph_overview" + }, + "3F": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up" + }, + "73": {}, + "79": { + "format": "smaller" + }, + "7B": { + "format": "smaller" + }, + "7D": {}, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search" + } + }, + "layoutName": "U" + }, + "en_fr_hybrid_CA": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! 1\n \u00b1", + "p1": "21", + "p5": "31", + "p9": "B1", + "position": "110" + }, + "03": { + "key": "@ \"2\n @", + "p1": "40", + "p3": "22", + "p5": "32", + "p9": "40", + "position": "111" + }, + "04": { + "key": "# /3\n \u00a3", + "p1": "23", + "p3": "2F", + "p5": "33", + "p9": "A3", + "position": "112" + }, + "05": { + "key": "$ 4\n \u00a2", + "p1": "24", + "p5": "34", + "p9": "A2", + "position": "113" + }, + "06": { + "key": "% 5\n \u00a4", + "p1": "25", + "p5": "35", + "p9": "A4", + "position": "114" + }, + "07": { + "key": "^ ?6\n \u00ac", + "p1": "005E", + "p3": "3F", + "p5": "36", + "p9": "AC", + "position": "115" + }, + "08": { + "key": "& 7\n \u00a6", + "p1": "26", + "p5": "37", + "p9": "A6", + "position": "116" + }, + "09": { + "key": "* 8\n \u00b2", + "p1": "2A", + "p5": "38", + "p9": "B2", + "position": "117" + }, + "0A": { + "key": "( 9\n \u00b3", + "p1": "28", + "p5": "39", + "p9": "B3", + "position": "118" + }, + "0B": { + "key": ") 0\n \u00bc", + "p1": "29", + "p5": "30", + "p9": "BC", + "position": "119" + }, + "0C": { + "key": "_ -\n \u00bd", + "p1": "5F", + "p5": "2D", + "p9": "BD", + "position": "120" + }, + "0D": { + "key": "+ =\n \u00be", + "p1": "2B", + "p5": "3D", + "p9": "BE", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o \u00a7", + "p5": "006F", + "p9": "00A7", + "position": "218" + }, + "19": { + "key": "p \u00b6", + "p5": "70", + "p9": "00B6", + "position": "219" + }, + "1A": { + "key": "{ ^\n[[ ^", + "p1": "7B", + "p3": "5E", + "p7": "5B", + "p8": "5B", + "p9": "5E", + "position": "220" + }, + "1B": { + "key": "} \u00a8\n]] \u00b8", + "p1": "7D", + "p3": "A8", + "p7": "5D", + "p8": "5D", + "p9": "B8", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": ": ;\n ~", + "p1": "3A", + "p5": "3B", + "p9": "7E", + "position": "319" + }, + "28": { + "key": "\" `\n'{ `", + "p1": "22", + "p3": "60", + "p7": "27", + "p8": "7B", + "p9": "60", + "position": "320" + }, + "29": { + "key": "~ |\n`\\ #", + "p1": "7E", + "p3": "7C", + "p7": "60", + "p8": "5C", + "p9": "23", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u00a6 >\n\\} <", + "p1": "00A6", + "p3": "3E", + "p7": "005C", + "p8": "7D", + "p9": "3C", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m \u00b5", + "p5": "6D", + "p9": "00B5", + "position": "416" + }, + "33": { + "key": "< '\n,\u00af ,", + "p1": "3C", + "p3": "27", + "p7": "2C", + "p8": "AF", + "p9": "2C", + "position": "417" + }, + "34": { + "key": "> .\n. .", + "p1": "3E", + "p3": "2E", + "p7": "2E", + "p9": "2E", + "position": "418" + }, + "35": { + "key": "? \u00c9\n/ \u00b4", + "p1": "3F", + "p3": "C9", + "p7": "2F", + "p9": "B4", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": "| \u00bb\n\\\u00b0 \u00ab", + "p1": "7C", + "p3": "BB", + "p7": "005C", + "p8": "B0", + "p9": "AB", + "position": "409" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt car", + "label": "alt car", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "es": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " !\n 1 |", + "p2": "21", + "p8": "31", + "p9": "007C", + "position": "110" + }, + "03": { + "key": " \"\n 2 @", + "p2": "22", + "p8": "32", + "p9": "40", + "position": "111" + }, + "04": { + "key": " \u00b7\n 3 #", + "p2": "00B7", + "p8": "33", + "p9": "23", + "position": "112" + }, + "05": { + "key": " $\n 4 ~", + "p2": "24", + "p8": "34", + "p9": "007E", + "position": "113" + }, + "06": { + "key": " %\n 5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": " &\n 6 \u00ac", + "p2": "26", + "p8": "36", + "p9": "00AC", + "position": "115" + }, + "08": { + "key": " /\n 7", + "p2": "002F", + "p8": "37", + "position": "116" + }, + "09": { + "key": " (\n 8", + "p2": "28", + "p8": "38", + "position": "117" + }, + "0A": { + "key": " )\n 9", + "p2": "29", + "p8": "39", + "position": "118" + }, + "0B": { + "key": " =\n 0", + "p2": "003D", + "p8": "30", + "position": "119" + }, + "0C": { + "key": " ?\n '", + "p2": "003F", + "p8": "27", + "position": "120" + }, + "0D": { + "key": "\u00bf\n\u00a1", + "p2": "00BF", + "p8": "00A1", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "^\n` [", + "p2": "005E", + "p8": "60", + "p9": "005B", + "position": "220" + }, + "1B": { + "key": "*\n+ ]", + "p2": "002A", + "p8": "002B", + "p9": "005D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "006B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "006C", + "position": "318" + }, + "27": { + "key": "\u00f1", + "p5": "00F1", + "position": "319" + }, + "28": { + "key": "\u00a8\n\u00b4 {", + "p2": "00A8", + "p8": "00B4", + "p9": "007B", + "position": "320" + }, + "29": { + "key": " \u00aa\n \u00ba \\", + "p2": "00AA", + "p8": "00BA", + "p9": "005C", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u00c7\n\u00e7 }", + "p2": "00C7", + "p8": "E7", + "p9": "007D", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "007A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "006E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "006D", + "position": "416" + }, + "33": { + "key": ";\n,", + "p2": "003B", + "p8": "002C", + "position": "417" + }, + "34": { + "key": ":\n.", + "p2": "003A", + "p8": "002E", + "position": "418" + }, + "35": { + "key": "_\n-", + "p2": "005F", + "p8": "002D", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": ">\n<", + "p2": "003E", + "p8": "003C", + "position": "409" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "es_419": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " !\n 1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": " \"\n 2", + "p2": "22", + "p8": "32", + "position": "111" + }, + "04": { + "key": " #\n 3", + "p2": "23", + "p8": "33", + "position": "112" + }, + "05": { + "key": " $\n 4", + "p2": "24", + "p8": "34", + "position": "113" + }, + "06": { + "key": " %\n 5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": " &\n 6", + "p2": "26", + "p8": "36", + "position": "115" + }, + "08": { + "key": " /\n 7", + "p2": "002F", + "p8": "37", + "position": "116" + }, + "09": { + "key": " (\n 8", + "p2": "28", + "p8": "38", + "position": "117" + }, + "0A": { + "key": " )\n 9", + "p2": "29", + "p8": "39", + "position": "118" + }, + "0B": { + "key": " =\n 0", + "p2": "3d", + "p8": "30", + "position": "119" + }, + "0C": { + "key": " ?\n ' \\", + "p2": "3f", + "p8": "27", + "p9": "005C", + "position": "120" + }, + "0D": { + "key": "\u00a1\n\u00bf", + "p2": "a1", + "p8": "bf", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q @", + "p5": "71", + "p9": "40", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "6f", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "\u00a8\n\u00b4", + "p2": "A8", + "p8": "B4", + "position": "220" + }, + "1B": { + "key": "*\n+ ~", + "p2": "2a", + "p8": "2b", + "p9": "7e", + "position": "221" + }, + "1C": { + "format": "right", + "key": "intro", + "label": "intro", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6a", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6b", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6c", + "position": "318" + }, + "27": { + "key": "\u00f1", + "p5": "f1", + "position": "319" + }, + "28": { + "key": "[\n{ ^", + "p2": "5B", + "p8": "7B", + "p9": "5E", + "position": "320" + }, + "29": { + "key": " \u00b0\n | \u00ac", + "p2": "00B0", + "p8": "007C", + "p9": "00AC", + "position": "100" + }, + "2A": { + "format": "left", + "key": "may\u00fas", + "label": "may\u00fas", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "]\n} `", + "p2": "5D", + "p8": "7D", + "p9": "60", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7a", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6e", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6d", + "position": "416" + }, + "33": { + "key": ";\n,", + "p2": "3b", + "p8": "2C", + "position": "417" + }, + "34": { + "key": ":\n.", + "p2": "3a", + "p8": "2e", + "position": "418" + }, + "35": { + "key": "_\n-", + "p2": "5f", + "p8": "2d", + "position": "419" + }, + "36": { + "format": "right", + "key": "may\u00fas", + "label": "may\u00fas", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": ">\n<", + "p2": "3e", + "p8": "3c", + "position": "409" + }, + "E0 1D": { + "format": "smaller", + "key": "crtl", + "label": "crtl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "et": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " !\n 1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": " \"\n 2 @", + "p2": "22", + "p8": "32", + "p9": "40", + "position": "111" + }, + "04": { + "key": " #\n 3 \u00a3", + "p2": "23", + "p8": "33", + "p9": "A3", + "position": "112" + }, + "05": { + "key": " \u00a4\n 4 $", + "p2": "A4", + "p8": "34", + "p9": "24", + "position": "113" + }, + "06": { + "key": " %\n 5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": " &\n 6", + "p2": "26", + "p8": "36", + "position": "115" + }, + "08": { + "key": " /\n 7 {", + "p2": "2F", + "p8": "37", + "p9": "7B", + "position": "116" + }, + "09": { + "key": " (\n 8 [", + "p2": "28", + "p8": "38", + "p9": "5B", + "position": "117" + }, + "0A": { + "key": " )\n 9 ]", + "p2": "29", + "p8": "39", + "p9": "5D", + "position": "118" + }, + "0B": { + "key": " =\n 0 }", + "p2": "3D", + "p8": "30", + "p9": "7D", + "position": "119" + }, + "0C": { + "key": " ?\n + \\", + "p2": "3F", + "p8": "2B", + "p9": "5C", + "position": "120" + }, + "0D": { + "key": " `\n \u00b4", + "p2": "60", + "p8": "B4", + "position": "121" + }, + "0E": { + "format": "right", + "key": " glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": " glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " q", + "p5": "71", + "position": "210" + }, + "11": { + "key": " w", + "p5": "77", + "position": "211" + }, + "12": { + "key": " e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": " r", + "p5": "72", + "position": "213" + }, + "14": { + "key": " t", + "p5": "74", + "position": "214" + }, + "15": { + "key": " y", + "p5": "79", + "position": "215" + }, + "16": { + "key": " u", + "p5": "75", + "position": "216" + }, + "17": { + "key": " i", + "p5": "69", + "position": "217" + }, + "18": { + "key": " o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": " p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": " \u00fc", + "p5": "FC", + "position": "220" + }, + "1B": { + "key": " \u00f5 \u00a7", + "p5": "F5", + "p9": "A7", + "position": "221" + }, + "1C": { + "format": "right", + "key": " enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": " ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": " a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": " s", + "p5": "73", + "position": "311" + }, + "20": { + "key": " d", + "p5": "64", + "position": "312" + }, + "21": { + "key": " f", + "p5": "66", + "position": "313" + }, + "22": { + "key": " g", + "p5": "67", + "position": "314" + }, + "23": { + "key": " h", + "p5": "68", + "position": "315" + }, + "24": { + "key": " j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": " k", + "p5": "006B", + "position": "317" + }, + "26": { + "key": " l", + "p5": "006C", + "position": "318" + }, + "27": { + "key": " \u00f6", + "p5": "00F6", + "position": "319" + }, + "28": { + "key": " \u00e4 ^", + "p5": "E4", + "p9": "5E", + "position": "320" + }, + "29": { + "key": " ~\n \u02c7", + "p2": "7E", + "p8": "2C7", + "position": "100" + }, + "2A": { + "format": "left", + "key": " glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": " *\n ' \u00bd", + "p2": "2A", + "p8": "27", + "p9": "BD", + "position": "290" + }, + "2C": { + "key": " z", + "p5": "007A", + "position": "410" + }, + "2D": { + "key": " x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": " c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": " v", + "p5": "76", + "position": "413" + }, + "30": { + "key": " b", + "p5": "62", + "position": "414" + }, + "31": { + "key": " n", + "p5": "006E", + "position": "415" + }, + "32": { + "key": " m", + "p5": "006D", + "position": "416" + }, + "33": { + "key": " ;\n ,", + "p2": "3B", + "p8": "2C", + "position": "417" + }, + "34": { + "key": " :\n .", + "p2": "3A", + "p8": "2E", + "position": "418" + }, + "35": { + "key": " _\n -", + "p2": "5F", + "p8": "2D", + "position": "419" + }, + "36": { + "format": "right", + "key": " glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": " alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": " >\n < |", + "p2": "3E", + "p8": "3C", + "p9": "7C", + "position": "409" + }, + "73": { + "key": " ", + "position": "420" + }, + "79": { + "format": "smaller", + "key": " ", + "position": "551" + }, + "7B": { + "format": "smaller", + "key": " ", + "position": "550" + }, + "7D": { + "key": " ", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": " ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": " alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": " glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "fi": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " !\n 1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": " \"\n 2 @", + "p2": "22", + "p8": "32", + "p9": "40", + "position": "111" + }, + "04": { + "key": " #\n 3 \u00a3", + "p2": "23", + "p8": "33", + "p9": "00A3", + "position": "112" + }, + "05": { + "key": " \u00a4\n 4 $", + "p2": "00A4", + "p8": "34", + "p9": "24", + "position": "113" + }, + "06": { + "key": " %\n 5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": " &\n 6", + "p2": "26", + "p8": "36", + "position": "115" + }, + "08": { + "key": " /\n 7 {", + "p2": "002F", + "p8": "37", + "p9": "007B", + "position": "116" + }, + "09": { + "key": " (\n 8 [", + "p2": "28", + "p8": "38", + "p9": "005B", + "position": "117" + }, + "0A": { + "key": " )\n 9 ]", + "p2": "29", + "p8": "39", + "p9": "005D", + "position": "118" + }, + "0B": { + "key": " =\n 0 }", + "p2": "003D", + "p8": "30", + "p9": "007D", + "position": "119" + }, + "0C": { + "key": " ?\n + \\", + "p2": "003F", + "p8": "002B", + "p9": "005C", + "position": "120" + }, + "0D": { + "key": " `\n \u00b4", + "p2": "60", + "p8": "00B4", + "position": "121" + }, + "0E": { + "format": "right", + "key": " glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": " glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " q", + "p5": "71", + "position": "210" + }, + "11": { + "key": " w", + "p5": "77", + "position": "211" + }, + "12": { + "key": " e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": " r", + "p5": "72", + "position": "213" + }, + "14": { + "key": " t", + "p5": "74", + "position": "214" + }, + "15": { + "key": " y", + "p5": "79", + "position": "215" + }, + "16": { + "key": " u", + "p5": "75", + "position": "216" + }, + "17": { + "key": " i", + "p5": "69", + "position": "217" + }, + "18": { + "key": " o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": " p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": " \u00e5", + "p5": "E5", + "position": "220" + }, + "1B": { + "key": " ^\n \u00a8 ~", + "p2": "005E", + "p8": "00A8", + "p9": "007E", + "position": "221" + }, + "1C": { + "format": "right", + "key": " glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": " ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": " a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": " s", + "p5": "73", + "position": "311" + }, + "20": { + "key": " d", + "p5": "64", + "position": "312" + }, + "21": { + "key": " f", + "p5": "66", + "position": "313" + }, + "22": { + "key": " g", + "p5": "67", + "position": "314" + }, + "23": { + "key": " h", + "p5": "68", + "position": "315" + }, + "24": { + "key": " j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": " k", + "p5": "006B", + "position": "317" + }, + "26": { + "key": " l", + "p5": "006C", + "position": "318" + }, + "27": { + "key": " \u00f6", + "p5": "00F6", + "position": "319" + }, + "28": { + "key": " \u00e4", + "p5": "E4", + "position": "320" + }, + "29": { + "key": " \u00bd\n \u00a7", + "p2": "00BD", + "p8": "00A7", + "position": "100" + }, + "2A": { + "format": "left", + "key": " glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": " *\n '", + "p2": "002A", + "p8": "27", + "position": "290" + }, + "2C": { + "key": " z", + "p5": "007A", + "position": "410" + }, + "2D": { + "key": " x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": " c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": " v", + "p5": "76", + "position": "413" + }, + "30": { + "key": " b", + "p5": "62", + "position": "414" + }, + "31": { + "key": " n", + "p5": "006E", + "position": "415" + }, + "32": { + "key": " m", + "p5": "006D", + "position": "416" + }, + "33": { + "key": " ;\n ,", + "p2": "003B", + "p8": "002C", + "position": "417" + }, + "34": { + "key": " :\n .", + "p2": "003A", + "p8": "002E", + "position": "418" + }, + "35": { + "key": " _\n -", + "p2": "005F", + "p8": "002D", + "position": "419" + }, + "36": { + "format": "right", + "key": " glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": " alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": " >\n < |", + "p2": "003E", + "p8": "003C", + "p9": "007C", + "position": "409" + }, + "73": { + "key": " ", + "position": "420" + }, + "79": { + "format": "smaller", + "key": " ", + "position": "551" + }, + "7B": { + "format": "smaller", + "key": " ", + "position": "550" + }, + "7D": { + "key": " ", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": " ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": " alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": " glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "fil": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " !\n 1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": " @\n 2", + "p2": "40", + "p8": "32", + "position": "111" + }, + "04": { + "key": " #\n 3", + "p2": "23", + "p8": "33", + "position": "112" + }, + "05": { + "key": " $\n 4", + "p2": "24", + "p8": "34", + "position": "113" + }, + "06": { + "key": " %\n 5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": " ^\n 6", + "p2": "005E", + "p8": "36", + "position": "115" + }, + "08": { + "key": " &\n 7", + "p2": "26", + "p8": "37", + "position": "116" + }, + "09": { + "key": " *\n 8", + "p2": "002A", + "p8": "38", + "position": "117" + }, + "0A": { + "key": " (\n 9", + "p2": "28", + "p8": "39", + "position": "118" + }, + "0B": { + "key": " )\n 0", + "p2": "29", + "p8": "30", + "position": "119" + }, + "0C": { + "key": " _\n -", + "p2": "005F", + "p8": "002D", + "position": "120" + }, + "0D": { + "key": " +\n =", + "p2": "002B", + "p8": "003D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "{\n[", + "p2": "007B", + "p8": "005B", + "position": "220" + }, + "1B": { + "key": "}\n]", + "p2": "007D", + "p8": "005D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "006B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "006C", + "position": "318" + }, + "27": { + "key": ":\n;", + "p2": "3A", + "p8": "3B", + "position": "319" + }, + "28": { + "key": "\"\n'", + "p2": "22", + "p8": "27", + "position": "320" + }, + "29": { + "key": " ~\n `", + "p2": "007E", + "p8": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "|\n\\", + "p2": "007C", + "p8": "005C", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "007A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "006E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "006D", + "position": "416" + }, + "33": { + "key": "<\n,", + "p2": "3C", + "p8": "2C", + "position": "417" + }, + "34": { + "key": ">\n.", + "p2": "3E", + "p8": "2E", + "position": "418" + }, + "35": { + "key": "?\n/", + "p2": "3F", + "p8": "2F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "fr": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " \u00e9chap", + "label": "\u00e9chap", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " 1\n &", + "p2": "31", + "p8": "26", + "position": "110" + }, + "03": { + "key": " 2\n \u00e9 ~", + "p2": "32", + "p8": "E9", + "p9": "007E", + "position": "111" + }, + "04": { + "key": " 3\n \" #", + "p2": "33", + "p8": "22", + "p9": "23", + "position": "112" + }, + "05": { + "key": " 4\n ' {", + "p2": "34", + "p8": "27", + "p9": "007B", + "position": "113" + }, + "06": { + "key": " 5\n ( [", + "p2": "35", + "p8": "28", + "p9": "005B", + "position": "114" + }, + "07": { + "key": " 6\n - |", + "p2": "36", + "p8": "2D", + "p9": "007C", + "position": "115" + }, + "08": { + "key": " 7\n \u00e8 `", + "p2": "37", + "p8": "E8", + "p9": "60", + "position": "116" + }, + "09": { + "key": " 8\n _ \\", + "p2": "38", + "p8": "5F", + "p9": "005C", + "position": "117" + }, + "0A": { + "key": " 9\n \u00e7 ^", + "p2": "39", + "p8": "E7", + "p9": "005E", + "position": "118" + }, + "0B": { + "key": " 0\n \u00e0 @", + "p2": "30", + "p8": "E0", + "p9": "40", + "position": "119" + }, + "0C": { + "key": " \u00b0\n ) ]", + "p2": "00B0", + "p8": "29", + "p9": "005D", + "position": "120" + }, + "0D": { + "key": " +\n = }", + "p2": "002B", + "p8": "003D", + "p9": "007D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "a", + "p5": "61", + "position": "210" + }, + "11": { + "key": "z", + "p5": "007A", + "position": "211" + }, + "12": { + "key": "e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "\u00a8\n^", + "p2": "A8", + "p8": "5E", + "position": "220" + }, + "1B": { + "key": "\u00a3\n$ \u00a4", + "p2": "A3", + "p8": "24", + "p9": "A4", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "q", + "p5": "71", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "006B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "006C", + "position": "318" + }, + "27": { + "key": "m", + "p5": "006D", + "position": "319" + }, + "28": { + "key": "%\n\u00f9", + "p2": "25", + "p8": "F9", + "position": "320" + }, + "29": { + "key": " \u00b2", + "p8": "B2", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u00b5\n*", + "p2": "B5", + "p8": "2A", + "position": "290" + }, + "2C": { + "key": "w", + "p5": "77", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "006E", + "position": "415" + }, + "32": { + "key": "?\n,", + "p2": "3F", + "p8": "2C", + "position": "416" + }, + "33": { + "key": ".\n;", + "p2": "2E", + "p8": "3B", + "position": "417" + }, + "34": { + "key": "/\n:", + "p2": "2F", + "p8": "3A", + "position": "418" + }, + "35": { + "key": "\u00a7\n!", + "p2": "A7", + "p8": "21", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": ">\n<", + "p2": "3E", + "p8": "3C", + "position": "409" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "fr_CA": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "\u00e9chap", + "label": "\u00e9chap", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "!\n1 \u00b1", + "p2": "21", + "p8": "31", + "p9": "B1", + "position": "110" + }, + "03": { + "key": "\"\n2 @", + "p2": "22", + "p8": "32", + "p9": "40", + "position": "111" + }, + "04": { + "key": "/\n3 \u00a3", + "p2": "2F", + "p8": "33", + "p9": "A3", + "position": "112" + }, + "05": { + "key": "$\n4 \u00a2", + "p2": "24", + "p8": "34", + "p9": "A2", + "position": "113" + }, + "06": { + "key": "%\n5 \u00a4", + "p2": "25", + "p8": "35", + "p9": "A4", + "position": "114" + }, + "07": { + "key": "?\n6 \u00ac", + "p2": "3F", + "p8": "36", + "p9": "AC", + "position": "115" + }, + "08": { + "key": "&\n7 \u00a6", + "p2": "26", + "p8": "37", + "p9": "A6", + "position": "116" + }, + "09": { + "key": "*\n8 \u00b2", + "p2": "2A", + "p8": "38", + "p9": "B2", + "position": "117" + }, + "0A": { + "key": "(\n9 \u00b3", + "p2": "28", + "p8": "39", + "p9": "B3", + "position": "118" + }, + "0B": { + "key": ")\n0 \u00bc", + "p2": "29", + "p8": "30", + "p9": "BC", + "position": "119" + }, + "0C": { + "key": "_\n- \u00bd", + "p2": "5F", + "p8": "2D", + "p9": "BD", + "position": "120" + }, + "0D": { + "key": "+\n= \u00be", + "p2": "2B", + "p8": "3D", + "p9": "BE", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o \u00a7", + "p5": "006F", + "p9": "00A7", + "position": "218" + }, + "19": { + "key": "p \u00b6", + "p5": "70", + "p9": "00B6", + "position": "219" + }, + "1A": { + "key": "^\n^ [", + "p2": "5E", + "p8": "5E", + "p9": "5B", + "position": "220" + }, + "1B": { + "key": "\u00a8\n\u00b8 ]", + "p2": "A8", + "p8": "B8", + "p9": "5D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": ":\n; ~", + "p2": "3A", + "p8": "3B", + "p9": "7E", + "position": "319" + }, + "28": { + "key": "`\n` {", + "p2": "60", + "p8": "60", + "p9": "7B", + "position": "320" + }, + "29": { + "key": "|\n# \\", + "p2": "7C", + "p8": "23", + "p9": "5C", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": ">\n< }", + "p2": "3E", + "p8": "3C", + "p9": "7D", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m \u00b5", + "p5": "6D", + "p9": "00B5", + "position": "416" + }, + "33": { + "key": "'\n, \u00af", + "p2": "27", + "p8": "2C", + "p9": "AF", + "position": "417" + }, + "34": { + "key": ".\n. \u00ad", + "p2": "2E", + "p8": "2E", + "p9": "AD", + "position": "418" + }, + "35": { + "key": "\u00c9\n\u00e9 \u00b4", + "p2": "C9", + "p8": "E9", + "p9": "B4", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": "\u00bb\n\u00ab \u00b0", + "p2": "BB", + "p8": "AB", + "p9": "B0", + "position": "409" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "hi": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! \n1 \u0967", + "p1": "21", + "p7": "31", + "p9": "967", + "position": "110" + }, + "03": { + "key": "@ \u0945\n2 \u0968", + "p1": "40", + "p3": "945", + "p7": "32", + "p9": "968", + "position": "111" + }, + "04": { + "key": "# \n3 \u0969", + "notes": "\u094d\u0930", + "p1": "23", + "p7": "33", + "p9": "969", + "position": "112" + }, + "05": { + "key": "$ \n4 \u096a", + "notes": "\u0930\u094d", + "p1": "24", + "p7": "34", + "p9": "96A", + "position": "113" + }, + "06": { + "key": "% \n5 \u096b", + "notes": "\u091c\u094d\u091e", + "p1": "25", + "p7": "35", + "p9": "96B", + "position": "114" + }, + "07": { + "key": "^ \n6 \u096c", + "notes": "\u0924\u094d\u0930", + "p1": "5E", + "p7": "36", + "p9": "96C", + "position": "115" + }, + "08": { + "key": "& \n7 \u096d", + "notes": "\u0915\u094d\u0937", + "p1": "26", + "p7": "37", + "p9": "96D", + "position": "116" + }, + "09": { + "key": "* \n8 \u096e", + "notes": "\u0936\u094d\u0930", + "p1": "2A", + "p7": "38", + "p9": "96E", + "position": "117" + }, + "0A": { + "key": "( (\n9 \u096f", + "p1": "28", + "p3": "28", + "p7": "39", + "p9": "96F", + "position": "118" + }, + "0B": { + "key": ") )\n0 \u0966", + "p1": "29", + "p3": "29", + "p7": "30", + "p9": "966", + "position": "119" + }, + "0C": { + "key": "_ \u0903\n- ", + "p1": "5F", + "p3": "903", + "p7": "2D", + "position": "120" + }, + "0D": { + "key": "+ \u090b\n= \u0943", + "p1": "2B", + "p3": "090B", + "p7": "3D", + "p9": "943", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " \u0914\nq \u094c", + "p3": "914", + "p7": "71", + "p9": "094C", + "position": "210" + }, + "11": { + "key": " \u0910\nw \u0948", + "p3": "910", + "p7": "77", + "p9": "948", + "position": "211" + }, + "12": { + "key": " \u0906\ne \u093e", + "p3": "906", + "p7": "65", + "p9": "093E", + "position": "212" + }, + "13": { + "key": " \u0908\nr \u0940", + "p3": "908", + "p7": "72", + "p9": "940", + "position": "213" + }, + "14": { + "key": " \u090a\nt \u0942", + "p3": "090A", + "p7": "74", + "p9": "942", + "position": "214" + }, + "15": { + "key": " \u092d\ny \u092c", + "p3": "092D", + "p7": "79", + "p9": "092C", + "position": "215" + }, + "16": { + "key": " \u0919\nu \u0939", + "p3": "919", + "p7": "75", + "p9": "939", + "position": "216" + }, + "17": { + "key": " \u0918\ni \u0917", + "p3": "918", + "p7": "69", + "p9": "917", + "position": "217" + }, + "18": { + "key": " \u0927\no \u0926", + "p3": "927", + "p7": "6F", + "p9": "926", + "position": "218" + }, + "19": { + "key": " \u091d\np \u091c", + "p3": "091D", + "p7": "70", + "p9": "091C", + "position": "219" + }, + "1A": { + "key": "{ \u0922\n[ \u0921", + "p1": "7B", + "p3": "922", + "p7": "5B", + "p9": "921", + "position": "220" + }, + "1B": { + "key": "} \u091e\n] \u093c", + "p1": "7D", + "p3": "091E", + "p7": "5D", + "p9": "093C", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": " \u0913\na \u094b", + "p3": "913", + "p7": "61", + "p9": "094B", + "position": "310" + }, + "1F": { + "key": " \u090f\ns \u0947", + "p3": "090F", + "p7": "73", + "p9": "947", + "position": "311" + }, + "20": { + "key": " \u0905\nd \u094d", + "p3": "905", + "p7": "64", + "p9": "094D", + "position": "312" + }, + "21": { + "key": " \u0907\nf \u093f", + "p3": "907", + "p7": "66", + "p9": "093F", + "position": "313" + }, + "22": { + "key": " \u0909\ng \u0941", + "p3": "909", + "p7": "67", + "p9": "941", + "position": "314" + }, + "23": { + "key": " \u092b\nh \u092a", + "p3": "092B", + "p7": "68", + "p9": "092A", + "position": "315" + }, + "24": { + "key": "j \u0930", + "p7": "6A", + "p9": "930", + "position": "316" + }, + "25": { + "key": " \u0916\nk \u0915", + "p3": "916", + "p7": "6B", + "p9": "915", + "position": "317" + }, + "26": { + "key": " \u0925\nl \u0924", + "p3": "925", + "p7": "6C", + "p9": "924", + "position": "318" + }, + "27": { + "key": ": \u091b\n; \u091a", + "p1": "3A", + "p3": "091B", + "p7": "3B", + "p9": "091A", + "position": "319" + }, + "28": { + "key": "\" \u0920\n' \u091f", + "p1": "22", + "p3": "920", + "p7": "27", + "p9": "091F", + "position": "320" + }, + "29": { + "key": "~ \u0912\n` \u094a", + "p1": "7E", + "p3": "912", + "p7": "60", + "p9": "094A", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "| \u0911\n\\ \u0949", + "p1": "7C", + "p3": "911", + "p7": "5C", + "p9": "949", + "position": "290" + }, + "2C": { + "key": " \u090e\nz \u0946", + "p3": "090E", + "p7": "7A", + "p9": "946", + "position": "410" + }, + "2D": { + "key": " \u0901\nx \u0902", + "p3": "901", + "p7": "78", + "p9": "902", + "position": "411" + }, + "2E": { + "key": " \u0923\nc \u092e", + "p3": "923", + "p7": "63", + "p9": "092E", + "position": "412" + }, + "2F": { + "key": "v \u0928", + "p7": "76", + "p9": "928", + "position": "413" + }, + "30": { + "key": "b \u0935", + "p7": "62", + "p9": "935", + "position": "414" + }, + "31": { + "key": " \u0933\nn \u0932", + "p3": "933", + "p7": "6E", + "p9": "932", + "position": "415" + }, + "32": { + "key": " \u0936\nm \u0938", + "p3": "936", + "p7": "6D", + "p9": "938", + "position": "416" + }, + "33": { + "key": "< \u0937\n, ,", + "p1": "3C", + "p3": "937", + "p7": "2C", + "p9": "002C", + "position": "417" + }, + "34": { + "key": "> \u0964\n. .", + "p1": "3E", + "p3": "964", + "p7": "2E", + "p9": "002E", + "position": "418" + }, + "35": { + "key": "? \n/ \u092f", + "p1": "3F", + "p7": "2F", + "p9": "092F", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "hr": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " !\n 1 ~", + "p2": "21", + "p8": "31", + "p9": "007E", + "position": "110" + }, + "03": { + "key": " \"\n 2 \u02c7", + "p2": "22", + "p8": "32", + "p9": "02C7", + "position": "111" + }, + "04": { + "key": " #\n 3 ^", + "p2": "23", + "p8": "33", + "p9": "005E", + "position": "112" + }, + "05": { + "key": " $\n 4 \u02d8", + "p2": "24", + "p8": "34", + "p9": "02D8", + "position": "113" + }, + "06": { + "key": " %\n 5 \u02da", + "p2": "25", + "p8": "35", + "p9": "02DA", + "position": "114" + }, + "07": { + "key": " &\n 6 \u02db", + "p2": "26", + "p8": "36", + "p9": "02DB", + "position": "115" + }, + "08": { + "key": " /\n 7 `", + "p2": "002F", + "p8": "37", + "p9": "60", + "position": "116" + }, + "09": { + "key": " (\n 8 \u02d9", + "p2": "28", + "p8": "38", + "p9": "02D9", + "position": "117" + }, + "0A": { + "key": " )\n 9 \u00b4", + "p2": "29", + "p8": "39", + "p9": "00B4", + "position": "118" + }, + "0B": { + "key": " =\n 0 \u02dd", + "p2": "003D", + "p8": "30", + "p9": "02DD", + "position": "119" + }, + "0C": { + "key": " ?\n ' \u00a8", + "p2": "003F", + "p8": "27", + "p9": "00A8", + "position": "120" + }, + "0D": { + "key": " *\n + \u00b8", + "p2": "002A", + "p8": "002B", + "p9": "00B8", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q \\", + "p5": "71", + "p9": "005C", + "position": "210" + }, + "11": { + "key": "w |", + "p5": "77", + "p9": "007C", + "position": "211" + }, + "12": { + "key": "e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "z", + "p5": "007A", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "\u0161 \u00f7", + "p5": "161", + "p9": "00F7", + "position": "220" + }, + "1B": { + "key": "\u0111 \u00d7", + "p5": "111", + "p9": "00D7", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f [", + "p5": "66", + "p9": "005B", + "position": "313" + }, + "22": { + "key": "g ]", + "p5": "67", + "p9": "005D", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": "k \u0142", + "p5": "006B", + "p9": "142", + "position": "317" + }, + "26": { + "key": "l \u0141", + "p5": "006C", + "p9": "141", + "position": "318" + }, + "27": { + "key": "\u010d", + "p5": "10D", + "position": "319" + }, + "28": { + "key": "\u0107 \u00df", + "p5": "107", + "p9": "00DF", + "position": "320" + }, + "29": { + "key": " \u00a8\n \u00b8", + "p2": "00A8", + "p8": "00B8", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u017e \u00a4", + "p5": "17E", + "p9": "00A4", + "position": "290" + }, + "2C": { + "key": "y", + "p5": "79", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v @", + "p5": "76", + "p9": "40", + "position": "413" + }, + "30": { + "key": "b {", + "p5": "62", + "p9": "007B", + "position": "414" + }, + "31": { + "key": "n }", + "p5": "006E", + "p9": "007D", + "position": "415" + }, + "32": { + "key": "m \u00a7", + "p5": "006D", + "p9": "00A7", + "position": "416" + }, + "33": { + "key": ";\n,", + "p2": "003B", + "p8": "002C", + "position": "417" + }, + "34": { + "key": ":\n.", + "p2": "003A", + "p8": "002E", + "position": "418" + }, + "35": { + "key": "_\n-", + "p2": "005F", + "p8": "002D", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": ">\n<", + "p2": "003E", + "p8": "003C", + "position": "409" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "key": " ", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "hu": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " '\n 1 ~", + "p2": "27", + "p8": "31", + "p9": "7e", + "position": "110" + }, + "03": { + "key": " \"\n 2 \u02c7", + "p2": "22", + "p8": "32", + "p9": "2c7", + "position": "111" + }, + "04": { + "key": " +\n 3 ^", + "p2": "2b", + "p8": "33", + "p9": "5e", + "position": "112" + }, + "05": { + "key": " !\n 4 \u02d8", + "p2": "21", + "p8": "34", + "p9": "2d8", + "position": "113" + }, + "06": { + "key": " %\n 5 \u02da", + "p2": "25", + "p8": "35", + "p9": "2da", + "position": "114" + }, + "07": { + "key": " /\n 6 \u02db", + "p2": "2f", + "p8": "36", + "p9": "2db", + "position": "115" + }, + "08": { + "key": " =\n 7 `", + "p2": "3d", + "p8": "37", + "p9": "60", + "position": "116" + }, + "09": { + "key": " (\n 8 \u02d9", + "p2": "28", + "p8": "38", + "p9": "2d9", + "position": "117" + }, + "0A": { + "key": " )\n 9 \u00b4", + "p2": "29", + "p8": "39", + "p9": "b4", + "position": "118" + }, + "0B": { + "key": " \u00f6 \u02dd", + "p5": "f6", + "p9": "2dd", + "position": "119" + }, + "0C": { + "key": " \u00fc \u00a8", + "p5": "fc", + "p9": "a8", + "position": "120" + }, + "0D": { + "key": " \u00f3 \u00b8", + "p5": "f3", + "p9": "b8", + "position": "121" + }, + "0E": { + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q q\n \\", + "p5": "71", + "p9": "5c", + "position": "210" + }, + "11": { + "key": "w w\n |", + "p5": "77", + "p9": "7c", + "position": "211" + }, + "12": { + "key": "e e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "z z", + "p5": "7a", + "position": "215" + }, + "16": { + "key": "u u\n \u20ac", + "p5": "75", + "p9": "20ac", + "position": "216" + }, + "17": { + "key": "i i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o o", + "p5": "6f", + "position": "218" + }, + "19": { + "key": "p p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "\u0151 \u0151\n \u00f7", + "p5": "151", + "p9": "f7", + "position": "220" + }, + "1B": { + "key": "\u00fa \u00fa\n \u00d7", + "p5": "fa", + "p9": "d7", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s \u0111", + "p5": "73", + "p9": "111", + "position": "311" + }, + "20": { + "key": "d \u0110", + "p5": "64", + "p9": "110", + "position": "312" + }, + "21": { + "key": "f [", + "p5": "66", + "p9": "5b", + "position": "313" + }, + "22": { + "key": "g ]", + "p5": "67", + "p9": "5d", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6a", + "position": "316" + }, + "25": { + "key": "k \u0142", + "p5": "6b", + "p9": "142", + "position": "317" + }, + "26": { + "key": "l \u0141", + "p5": "6c", + "p9": "141", + "position": "318" + }, + "27": { + "key": "\u00e9 $", + "p5": "e9", + "p9": "24", + "position": "319" + }, + "28": { + "key": "\u00e1 \u00df", + "p5": "e1", + "p9": "df", + "position": "320" + }, + "29": { + "key": " \u00a7\n 0", + "p2": "a7", + "p8": "30", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u0171 \u0171\n \u00a4", + "p5": "171", + "p9": "a4", + "position": "290" + }, + "2C": { + "key": "y >", + "p5": "79", + "p9": "3e", + "position": "410" + }, + "2D": { + "key": "x #", + "p5": "78", + "p9": "23", + "position": "411" + }, + "2E": { + "key": "c &", + "p5": "63", + "p9": "26", + "position": "412" + }, + "2F": { + "key": "v @", + "p5": "76", + "p9": "40", + "position": "413" + }, + "30": { + "key": "b {", + "p5": "62", + "p9": "7b", + "position": "414" + }, + "31": { + "key": "n }", + "p5": "6e", + "p9": "7d", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6d", + "position": "416" + }, + "33": { + "key": "?\n, ;", + "p2": "3f", + "p8": "2c", + "p9": "3b", + "position": "417" + }, + "34": { + "key": ":\n.", + "p2": "3a", + "p8": "2e", + "position": "418" + }, + "35": { + "key": "_\n- *", + "p2": "5f", + "p8": "2d", + "p9": "2a", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": "\u00ed <", + "p5": "ed", + "p9": "3c", + "position": "409" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller" + }, + "7B": { + "format": "smaller" + }, + "7D": { + "key": " ", + "position": "122" + }, + "E0 1D": { + "format": "smaller" + }, + "E0 38": { + "format": "smaller" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "id": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "!\n1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": "@\n2", + "p2": "40", + "p8": "32", + "position": "111" + }, + "04": { + "key": "#\n3", + "p2": "23", + "p8": "33", + "position": "112" + }, + "05": { + "key": "$\n4", + "p2": "24", + "p8": "34", + "position": "113" + }, + "06": { + "key": "%\n5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": "^\n6", + "p2": "5E", + "p8": "36", + "position": "115" + }, + "08": { + "key": "&\n7", + "p2": "26", + "p8": "37", + "position": "116" + }, + "09": { + "key": "*\n8", + "p2": "2A", + "p8": "38", + "position": "117" + }, + "0A": { + "key": "(\n9", + "p2": "28", + "p8": "39", + "position": "118" + }, + "0B": { + "key": ")\n0", + "p2": "29", + "p8": "30", + "position": "119" + }, + "0C": { + "key": "_\n-", + "p2": "5F", + "p8": "2D", + "position": "120" + }, + "0D": { + "key": "+\n=", + "p2": "2B", + "p8": "3D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "6F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "{\n[", + "p2": "7B", + "p8": "5B", + "position": "220" + }, + "1B": { + "key": "}\n]", + "p2": "7D", + "p8": "5D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": ":\n;", + "p2": "3A", + "p8": "3B", + "position": "319" + }, + "28": { + "key": "\"\n'", + "p2": "22", + "p8": "27", + "position": "320" + }, + "29": { + "key": "~\n`", + "p2": "7E", + "p8": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "|\n\\", + "p2": "7C", + "p8": "5C", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": "<\n,", + "p2": "3C", + "p8": "2C", + "position": "417" + }, + "34": { + "key": ">\n.", + "p2": "3E", + "p8": "2E", + "position": "418" + }, + "35": { + "key": "?\n/", + "p2": "3F", + "p8": "2F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "it": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " !\n 1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": " \"\n 2", + "p2": "22", + "p8": "32", + "position": "111" + }, + "04": { + "key": " \u00a3\n 3", + "p2": "A3", + "p8": "33", + "position": "112" + }, + "05": { + "key": " $\n 4", + "p2": "24", + "p8": "34", + "position": "113" + }, + "06": { + "key": " %\n 5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": " &\n 6", + "p2": "26", + "p8": "36", + "position": "115" + }, + "08": { + "key": " /\n 7", + "p2": "2F", + "p8": "37", + "position": "116" + }, + "09": { + "key": " (\n 8", + "p2": "28", + "p8": "38", + "position": "117" + }, + "0A": { + "key": " )\n 9", + "p2": "29", + "p8": "39", + "position": "118" + }, + "0B": { + "key": " =\n 0", + "p2": "3D", + "p8": "30", + "position": "119" + }, + "0C": { + "key": " ?\n '", + "p2": "3F", + "p8": "27", + "position": "120" + }, + "0D": { + "key": " ^\n \u00ec", + "p2": "5E", + "p8": "EC", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "\u00e9\n\u00e8 [", + "p2": "E9", + "p8": "E8", + "p9": "5B", + "position": "220" + }, + "1B": { + "key": "*\n+ ]", + "p2": "2A", + "p8": "2B", + "p9": "5D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "invio", + "label": "invio", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": "\u00e7\n\u00f2 @", + "p2": "E7", + "p8": "F2", + "p9": "40", + "position": "319" + }, + "28": { + "key": "\u00b0\n\u00e0 #", + "p2": "B0", + "p8": "E0", + "p9": "23", + "position": "320" + }, + "29": { + "key": " |\n \\", + "p2": "7C", + "p8": "5C", + "position": "100" + }, + "2A": { + "format": "left", + "key": "maiusc", + "label": "maiusc", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u00a7\n\u00f9 `", + "p2": "A7", + "p8": "F9", + "p9": "60", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": ";\n,", + "p2": "3B", + "p8": "2C", + "position": "417" + }, + "34": { + "key": ":\n.", + "p2": "3A", + "p8": "2E", + "position": "418" + }, + "35": { + "key": "_\n-", + "p2": "5F", + "p8": "2D", + "position": "419" + }, + "36": { + "format": "right", + "key": "maiusc", + "label": "maiusc", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": ">\n<", + "p2": "3E", + "p8": "3C", + "position": "409" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "iw": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! \n1 ", + "p1": "21", + "p7": "31", + "position": "110" + }, + "03": { + "key": "@ \n2 ", + "p1": "40", + "p7": "32", + "position": "111" + }, + "04": { + "key": "# \n3 ", + "p1": "23", + "p7": "33", + "position": "112" + }, + "05": { + "key": "$ \u20aa\n4 ", + "p1": "24", + "p3": "20aa", + "p7": "34", + "position": "113" + }, + "06": { + "key": "% \n5 ", + "p1": "25", + "p7": "35", + "position": "114" + }, + "07": { + "key": "^ \n6 ", + "p1": "5E", + "p7": "36", + "position": "115" + }, + "08": { + "key": "& \n7 ", + "p1": "26", + "p7": "37", + "position": "116" + }, + "09": { + "key": "* \n8 ", + "p1": "2A", + "p7": "38", + "position": "117" + }, + "0A": { + "key": "( \n9 ", + "p1": "28", + "p7": "39", + "position": "118" + }, + "0B": { + "key": ") \n0 ", + "p1": "29", + "p7": "30", + "position": "119" + }, + "0C": { + "key": "_ \n- ", + "p1": "5F", + "p7": "2D", + "position": "120" + }, + "0D": { + "key": "+ \n= ", + "p1": "2B", + "p7": "3D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q /", + "p5": "71", + "p9": "2F", + "position": "210" + }, + "11": { + "key": "w '", + "p5": "77", + "p9": "27", + "position": "211" + }, + "12": { + "key": " \u20ace\n \u05e7", + "notes": "5e7=\u05e7", + "p3": "20ac", + "p5": "65", + "p9": "5e7", + "position": "212" + }, + "13": { + "key": "r \u05e8", + "notes": "5e8=\u05e8", + "p5": "72", + "p9": "5e8", + "position": "213" + }, + "14": { + "key": "t \u05d0", + "p5": "74", + "p9": "5D0", + "position": "214" + }, + "15": { + "key": "y \u05d8", + "p5": "79", + "p9": "5D8", + "position": "215" + }, + "16": { + "key": "u \u05d5", + "p5": "75", + "p9": "5D5", + "position": "216" + }, + "17": { + "key": "i \u05df", + "p5": "69", + "p9": "5Df", + "position": "217" + }, + "18": { + "key": "o \u05dd", + "p5": "6F", + "p9": "5dd", + "position": "218" + }, + "19": { + "key": "p \u05e4", + "notes": "5e4=\u05e4", + "p5": "70", + "p9": "5e4", + "position": "219" + }, + "1A": { + "key": "{ \n[ ", + "p1": "7b", + "p7": "5B", + "position": "220" + }, + "1B": { + "key": "} \n] ", + "p1": "7D", + "p7": "5D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a \u05e9", + "notes": "5E9=\u05e9", + "p5": "61", + "p9": "5e9", + "position": "310" + }, + "1F": { + "key": "s \u05d3", + "p5": "73", + "p9": "5d3", + "position": "311" + }, + "20": { + "key": "d \u05d2", + "p5": "64", + "p9": "5d2", + "position": "312" + }, + "21": { + "key": "f \u05db", + "p5": "66", + "p9": "5db", + "position": "313" + }, + "22": { + "key": "g \u05e2", + "notes": "5e2=\u05e2", + "p5": "67", + "p9": "5e2", + "position": "314" + }, + "23": { + "key": "h \u05d9", + "p5": "68", + "p9": "5d9", + "position": "315" + }, + "24": { + "key": "j \u05d7", + "p5": "6A", + "p9": "5d7", + "position": "316" + }, + "25": { + "key": "k \u05dc", + "p5": "6B", + "p9": "5dc", + "position": "317" + }, + "26": { + "key": "l \u05da", + "p5": "6C", + "p9": "5da", + "position": "318" + }, + "27": { + "key": ": \n; \u05e3", + "notes": "5e3=\u05e3", + "p1": "3a", + "p7": "3b", + "p9": "5e3", + "position": "319" + }, + "28": { + "key": "\" \n\u00b4 ", + "p1": "22", + "p7": "b4", + "position": "320" + }, + "29": { + "key": "~ \n` ;", + "p1": "7E", + "p7": "60", + "p9": "3b", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u00a6 \n\\ ", + "p1": "A6", + "p7": "5C", + "position": "290" + }, + "2C": { + "key": "z \u05d6", + "p5": "7A", + "p9": "5d6", + "position": "410" + }, + "2D": { + "key": "x \u05e1", + "notes": "5e1=\u05e1", + "p5": "78", + "p9": "5e1", + "position": "411" + }, + "2E": { + "key": "c \u05d1", + "p5": "63", + "p9": "5d1", + "position": "412" + }, + "2F": { + "key": "v \u05d4", + "p5": "76", + "p9": "5d4", + "position": "413" + }, + "30": { + "key": "b \u05e0", + "notes": "5e0=\u05e0", + "p5": "62", + "p9": "5e0", + "position": "414" + }, + "31": { + "key": "n \u05de", + "p5": "6E", + "p9": "5de", + "position": "415" + }, + "32": { + "key": "m \u05e6", + "notes": "5e5=\u05e6 -Zadi (5e6)? -ginsberg", + "p5": "6D", + "p9": "5e6", + "position": "416" + }, + "33": { + "key": "< \n\u00b8 \u05ea", + "p1": "3c", + "p7": "b8", + "p9": "5ea", + "position": "417" + }, + "34": { + "key": "> \n. \u05e5", + "notes": "5e5=\u05e5", + "p1": "3e", + "p7": "2e", + "p9": "5e5", + "position": "418" + }, + "35": { + "key": "? \n/ .", + "p1": "3f", + "p7": "2f", + "p9": "2e", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "ja": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " ! \n 1 \u306c", + "p1": "21", + "p7": "31", + "p9": "306C", + "position": "110" + }, + "03": { + "key": " \" \n 2 \u3075", + "p1": "22", + "p7": "32", + "p9": "3075", + "position": "111" + }, + "04": { + "key": " # \u3041\n 3 \u3042", + "p1": "23", + "p3": "3041", + "p7": "33", + "p9": "3042", + "position": "112" + }, + "05": { + "key": " $ \u3045\n 4 \u3046", + "p1": "24", + "p3": "3045", + "p7": "34", + "p9": "3046", + "position": "113" + }, + "06": { + "key": " % \u3047\n 5 \u3048", + "p1": "25", + "p3": "3047", + "p7": "35", + "p9": "3048", + "position": "114" + }, + "07": { + "key": " & \u3049\n 6 \u304a", + "p1": "26", + "p3": "3049", + "p7": "36", + "p9": "304A", + "position": "115" + }, + "08": { + "key": " ' \u3083\n 7 \u3084", + "p1": "27", + "p3": "3083", + "p7": "37", + "p9": "3084", + "position": "116" + }, + "09": { + "key": " ( \u3085\n 8 \u3086", + "p1": "28", + "p3": "3085", + "p7": "38", + "p9": "3086", + "position": "117" + }, + "0A": { + "key": " ) \u3087\n 9 \u3088", + "p1": "29", + "p3": "3087", + "p7": "39", + "p9": "3088", + "position": "118" + }, + "0B": { + "key": " \u3092\n 0 \u308f", + "p3": "3092", + "p7": "30", + "p9": "308F", + "position": "119" + }, + "0C": { + "key": " = \n - \u307b", + "p1": "3D", + "p7": "2D", + "p9": "307B", + "position": "120" + }, + "0D": { + "key": " ~ \n ^ \u3078", + "p1": "7E", + "p7": "5E", + "p9": "3078", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q \n \u305f", + "p1": "71", + "p9": "305F", + "position": "210" + }, + "11": { + "key": "w \n \u3066", + "p1": "77", + "p9": "3066", + "position": "211" + }, + "12": { + "key": "e \u3043\n \u3044", + "p1": "65", + "p3": "3043", + "p9": "3044", + "position": "212" + }, + "13": { + "key": "r \n \u3059", + "p1": "72", + "p9": "3059", + "position": "213" + }, + "14": { + "key": "t \n \u304b", + "p1": "74", + "p9": "304B", + "position": "214" + }, + "15": { + "key": "y \n \u3093", + "p1": "79", + "p9": "3093", + "position": "215" + }, + "16": { + "key": "u \n \u306a", + "p1": "75", + "p9": "306A", + "position": "216" + }, + "17": { + "key": "i \n \u306b", + "p1": "69", + "p9": "306B", + "position": "217" + }, + "18": { + "key": "o \n \u3089", + "p1": "6F", + "p9": "3089", + "position": "218" + }, + "19": { + "key": "p \n \u305b", + "p1": "70", + "p9": "305B", + "position": "219" + }, + "1A": { + "key": "` \n@ \u3099", + "p1": "60", + "p7": "40", + "p9": "3099", + "position": "220" + }, + "1B": { + "key": "{ \u300c\n[ \u309a", + "p1": "7B", + "p3": "300C", + "p7": "5B", + "p9": "309A", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a \n \u3061", + "p1": "61", + "p9": "3061", + "position": "310" + }, + "1F": { + "key": "s \n \u3068", + "p1": "73", + "p9": "3068", + "position": "311" + }, + "20": { + "key": "d \n \u3057", + "p1": "64", + "p9": "3057", + "position": "312" + }, + "21": { + "key": "f \n \u306f", + "p1": "66", + "p9": "306F", + "position": "313" + }, + "22": { + "key": "g \n \u304d", + "p1": "67", + "p9": "304D", + "position": "314" + }, + "23": { + "key": "h \n \u304f", + "p1": "68", + "p9": "304F", + "position": "315" + }, + "24": { + "key": "j \n \u307e", + "p1": "6A", + "p9": "307E", + "position": "316" + }, + "25": { + "key": "k \n \u306e", + "p1": "6B", + "p9": "306E", + "position": "317" + }, + "26": { + "key": "l \n \u308a", + "p1": "6C", + "p9": "308A", + "position": "318" + }, + "27": { + "key": "+ \n; \u308c", + "p1": "2B", + "p7": "3B", + "p9": "308C", + "position": "319" + }, + "28": { + "key": "* \n: \u3051", + "p1": "2A", + "p7": "3A", + "p9": "3051", + "position": "320" + }, + "29": { + "key": " glyph_ime", + "label": "glyph_ime", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "} \u300d\n] \u3080", + "p1": "7D", + "p3": "300D", + "p7": "5D", + "p9": "3080", + "position": "290" + }, + "2C": { + "key": "z \n \u3064", + "p1": "7A", + "p9": "3064", + "position": "410" + }, + "2D": { + "key": "x \n \u3055", + "p1": "78", + "p9": "3055", + "position": "411" + }, + "2E": { + "key": "c \n \u305d", + "p1": "63", + "p9": "305D", + "position": "412" + }, + "2F": { + "key": "v \n \u3072", + "p1": "76", + "p9": "3072", + "position": "413" + }, + "30": { + "key": "b \n \u3053", + "p1": "62", + "p9": "3053", + "position": "414" + }, + "31": { + "key": "n \n \u307f", + "p1": "6E", + "p9": "307F", + "position": "415" + }, + "32": { + "key": "m \n \u3082", + "p1": "6D", + "p9": "3082", + "position": "416" + }, + "33": { + "key": "< \uff64\n, \u306d", + "p1": "3C", + "p3": "FF64", + "p7": "2C", + "p9": "306D", + "position": "417" + }, + "34": { + "key": "> \uff61\n. \u308b", + "p1": "3E", + "p3": "FF61", + "p7": "2E", + "p9": "308B", + "position": "418" + }, + "35": { + "key": "? \uff65\n/ \u3081", + "p1": "3F", + "p3": "FF65", + "p7": "2F", + "p9": "3081", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "key": "_ \n\\ \u308d", + "notes": "\u2013 \n\\ \u308d", + "p1": "5F", + "p7": "5C", + "p9": "308D", + "position": "420" + }, + "79": { + "format": "smaller", + "key": "\u304b\u306a", + "label": "\u304b\u306a", + "position": "551" + }, + "7B": { + "format": "smaller", + "key": "\u82f1\u6570", + "label": "\u82f1\u6570", + "position": "550" + }, + "7D": { + "key": "| \n\u00a5 \u30fc", + "p1": "7C", + "p7": "A5", + "p9": "30FC", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search (Updated to Japanese \"kensaku\" - ginsberg)", + "position": "300" + } + }, + "layoutName": "J" + }, + "ko": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! \n1 ", + "p1": "21", + "p7": "31", + "position": "110" + }, + "03": { + "key": "@ \n2 ", + "p1": "40", + "p7": "32", + "position": "111" + }, + "04": { + "key": "# \n3 ", + "p1": "23", + "p7": "33", + "position": "112" + }, + "05": { + "key": "$ \n4 ", + "p1": "24", + "p7": "34", + "position": "113" + }, + "06": { + "key": "% \n5 ", + "p1": "25", + "p7": "35", + "position": "114" + }, + "07": { + "key": "^ \n6 ", + "p1": "005E", + "p7": "36", + "position": "115" + }, + "08": { + "key": "& \n7 ", + "p1": "26", + "p7": "37", + "position": "116" + }, + "09": { + "key": "* \n8 ", + "p1": "002A", + "p7": "38", + "position": "117" + }, + "0A": { + "key": "( \n9 ", + "p1": "28", + "p7": "39", + "position": "118" + }, + "0B": { + "key": ") \n0 ", + "p1": "29", + "p7": "30", + "position": "119" + }, + "0C": { + "key": "_ \n- ", + "p1": "005F", + "p7": "002D", + "position": "120" + }, + "0D": { + "key": "+ \n= ", + "p1": "002B", + "p7": "003D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q \u3143\n \u3142", + "p1": "71", + "p3": "3143", + "p9": "3142", + "position": "210" + }, + "11": { + "key": "w \u3149\n \u3148", + "p1": "77", + "p3": "3149", + "p9": "3148", + "position": "211" + }, + "12": { + "key": "e \u3138\n \u3137", + "p1": "65", + "p3": "3138", + "p9": "3137", + "position": "212" + }, + "13": { + "key": "r \u3132\n \u3131", + "p1": "72", + "p3": "3132", + "p9": "3131", + "position": "213" + }, + "14": { + "key": "t \u3146\n \u3145", + "p1": "74", + "p3": "3146", + "p9": "3145", + "position": "214" + }, + "15": { + "key": "y \n \u315b", + "p1": "79", + "p9": "315B", + "position": "215" + }, + "16": { + "key": "u \n \u3155", + "p1": "75", + "p9": "3155", + "position": "216" + }, + "17": { + "key": "i \n \u3151", + "p1": "69", + "p9": "3151", + "position": "217" + }, + "18": { + "key": "o \u3152\n \u3150", + "p1": "6F", + "p3": "3152", + "p9": "3150", + "position": "218" + }, + "19": { + "key": "p \u3156\n \u3154", + "p1": "70", + "p3": "3156", + "p9": "3154", + "position": "219" + }, + "1A": { + "key": "{ \n[ ", + "p1": "007B", + "p7": "005B", + "position": "220" + }, + "1B": { + "key": "} \n] ", + "p1": "007D", + "p7": "005D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a \n \u3141", + "p1": "61", + "p9": "3141", + "position": "310" + }, + "1F": { + "key": "s \n \u3134", + "p1": "73", + "p9": "3134", + "position": "311" + }, + "20": { + "key": "d \n \u3147", + "p1": "64", + "p9": "3147", + "position": "312" + }, + "21": { + "key": "f \n \u3139", + "p1": "66", + "p9": "3139", + "position": "313" + }, + "22": { + "key": "g \n \u314e", + "p1": "67", + "p9": "314E", + "position": "314" + }, + "23": { + "key": "h \n \u3157", + "p1": "68", + "p9": "3157", + "position": "315" + }, + "24": { + "key": "j \n \u3153", + "p1": "6A", + "p9": "3153", + "position": "316" + }, + "25": { + "key": "k \n \u314f", + "p1": "6B", + "p9": "314F", + "position": "317" + }, + "26": { + "key": "l \n \u3163", + "p1": "6C", + "p9": "3163", + "position": "318" + }, + "27": { + "key": ": \n; ", + "p1": "003A", + "p7": "003B", + "position": "319" + }, + "28": { + "key": "\" \n' ", + "p1": "22", + "p7": "27", + "position": "320" + }, + "29": { + "key": "~ \n` ", + "p1": "007E", + "p7": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "| \n\\ \u20a9", + "p1": "007C", + "p7": "005C", + "p9": "20A9", + "position": "290" + }, + "2C": { + "key": "z \n \u314b", + "p1": "7A", + "p9": "314B", + "position": "410" + }, + "2D": { + "key": "x \n \u314c", + "p1": "78", + "p9": "314C", + "position": "411" + }, + "2E": { + "key": "c \n \u314a", + "p1": "63", + "p9": "314A", + "position": "412" + }, + "2F": { + "key": "v \n \u314d", + "p1": "76", + "p9": "314D", + "position": "413" + }, + "30": { + "key": "b \n \u3160", + "p1": "62", + "p9": "3160", + "position": "414" + }, + "31": { + "key": "n \n \u315c", + "p1": "6E", + "p9": "315C", + "position": "415" + }, + "32": { + "key": "m \n \u3161", + "p1": "6D", + "p9": "3161", + "position": "416" + }, + "33": { + "key": "< \n, ", + "p1": "003C", + "p7": "002C", + "position": "417" + }, + "34": { + "key": "> \n. ", + "p1": "003E", + "p7": "002E", + "position": "418" + }, + "35": { + "key": "? \n/ ", + "p1": "003F", + "p7": "002F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "key": "\ud55c/\uc601", + "label": "\ud55c/\uc601", + "notes": "ditto", + "position": "551" + }, + "7B": { + "format": "smaller", + "key": "\ud55c\uc790", + "label": "\ud55c\uc790", + "notes": "Korean CrOS device will not have 550/551 keys. If we add 550/551 keys later, 580/581 have to be Alt-R and Ctrl-R.", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": " \ud55c\uc790", + "label": " \ud55c\uc790", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "\ud55c/\uc601", + "label": "\ud55c/\uc601", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "lt": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "!\n1 \u0105", + "p2": "21", + "p8": "31", + "p9": "105", + "position": "110" + }, + "03": { + "key": "@\n2 \u010d", + "p2": "40", + "p8": "32", + "p9": "10D", + "position": "111" + }, + "04": { + "key": "#\n3 \u0119", + "p2": "23", + "p8": "33", + "p9": "119", + "position": "112" + }, + "05": { + "key": "$\n4 \u0117", + "p2": "24", + "p8": "34", + "p9": "117", + "position": "113" + }, + "06": { + "key": "%\n5 \u012f", + "p2": "25", + "p8": "35", + "p9": "12F", + "position": "114" + }, + "07": { + "key": "^\n6 \u0161", + "p2": "005E", + "p8": "36", + "p9": "161", + "position": "115" + }, + "08": { + "key": "&\n7 \u0173", + "p2": "26", + "p8": "37", + "p9": "173", + "position": "116" + }, + "09": { + "key": "*\n8 \u016b", + "p2": "002A", + "p8": "38", + "p9": "016B", + "position": "117" + }, + "0A": { + "key": "(\n9", + "p2": "28", + "p8": "39", + "position": "118" + }, + "0B": { + "key": ")\n0", + "p2": "29", + "p8": "30", + "position": "119" + }, + "0C": { + "key": "_\n-", + "p2": "005F", + "p8": "002D", + "position": "120" + }, + "0D": { + "key": "+\n= \u017e", + "p2": "002B", + "p8": "003D", + "p9": "017E", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "{\n[", + "p2": "007B", + "p8": "005B", + "position": "220" + }, + "1B": { + "key": "}\n]", + "p2": "007D", + "p8": "005D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "006B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "006C", + "position": "318" + }, + "27": { + "key": ":\n;", + "p2": "003A", + "p8": "003B", + "position": "319" + }, + "28": { + "key": "\"\n'", + "p2": "22", + "p8": "27", + "position": "320" + }, + "29": { + "key": "~\n` \u00b4", + "p2": "007E", + "p8": "60", + "p9": "00B4", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\\\n|", + "p2": "005C", + "p8": "007C", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "007A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "006E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "006D", + "position": "416" + }, + "33": { + "key": "<\n, \u201e", + "p2": "003C", + "p8": "002C", + "p9": "201E", + "position": "417" + }, + "34": { + "key": ">\n. \u201c", + "p2": "003E", + "p8": "002E", + "p9": "201C", + "position": "418" + }, + "35": { + "key": "?\n/", + "p2": "003F", + "p8": "002F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "lv": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "!\n1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": "@\n2", + "p2": "40", + "p8": "32", + "position": "111" + }, + "04": { + "key": "#\n3", + "p2": "23", + "p8": "33", + "position": "112" + }, + "05": { + "key": "$\n4", + "p2": "24", + "p8": "34", + "position": "113" + }, + "06": { + "key": "%\n5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": "^\n6", + "p2": "005E", + "p8": "36", + "position": "115" + }, + "08": { + "key": "&\n7", + "p2": "26", + "p8": "37", + "position": "116" + }, + "09": { + "key": "*\n8", + "p2": "002A", + "p8": "38", + "position": "117" + }, + "0A": { + "key": "(\n9", + "p2": "28", + "p8": "39", + "position": "118" + }, + "0B": { + "key": ")\n0", + "p2": "29", + "p8": "30", + "position": "119" + }, + "0C": { + "key": "_\n-", + "p2": "005F", + "p8": "002D", + "position": "120" + }, + "0D": { + "key": "+\n=", + "p2": "002B", + "p8": "003D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "{\n[", + "p2": "007B", + "p8": "005B", + "position": "220" + }, + "1B": { + "key": "}\n]", + "p2": "007D", + "p8": "005D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": ":\n;", + "p2": "003A", + "p8": "003B", + "position": "319" + }, + "28": { + "key": "\"\n'", + "p2": "22", + "p8": "27", + "position": "320" + }, + "29": { + "key": "~\n`", + "p2": "007E", + "p8": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "|\n\\", + "p2": "7C", + "p8": "005C", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": "<\n,", + "p2": "003C", + "p8": "002C", + "position": "417" + }, + "34": { + "key": ">\n.", + "p2": "003E", + "p8": "002E", + "position": "418" + }, + "35": { + "key": "?\n/", + "p2": "003F", + "p8": "002F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": "|\n\\", + "p2": "007C", + "p8": "005C", + "position": "409" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "nl": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "!\n1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": "@\n2", + "p2": "40", + "p8": "32", + "position": "111" + }, + "04": { + "key": "#\n3", + "p2": "23", + "p8": "33", + "position": "112" + }, + "05": { + "key": "$\n4", + "p2": "24", + "p8": "34", + "position": "113" + }, + "06": { + "key": "%\n5 \u20ac", + "p2": "25", + "p8": "35", + "p9": "20AC", + "position": "114" + }, + "07": { + "key": "^\n6", + "p2": "005E", + "p8": "36", + "position": "115" + }, + "08": { + "key": "&\n7", + "p2": "26", + "p8": "37", + "position": "116" + }, + "09": { + "key": "*\n8", + "p2": "002A", + "p8": "38", + "position": "117" + }, + "0A": { + "key": "(\n9", + "p2": "28", + "p8": "39", + "position": "118" + }, + "0B": { + "key": ")\n0", + "p2": "29", + "p8": "30", + "position": "119" + }, + "0C": { + "key": "_\n-", + "p2": "005F", + "p8": "002D", + "position": "120" + }, + "0D": { + "key": "+\n=", + "p2": "002B", + "p8": "003D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "6F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "{\n[", + "p2": "007B", + "p8": "005B", + "position": "220" + }, + "1B": { + "key": "}\n]", + "p2": "007D", + "p8": "005D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": ":\n;", + "p2": "003A", + "p8": "003B", + "position": "319" + }, + "28": { + "key": "\"\n'", + "p2": "22", + "p8": "27", + "position": "320" + }, + "29": { + "key": "~\n`", + "p2": "007E", + "p8": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "|\n\\", + "p2": "007C", + "p8": "005C", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": "<\n,", + "p2": "003C", + "p8": "002C", + "position": "417" + }, + "34": { + "key": ">\n.", + "p2": "003E", + "p8": "002E", + "position": "418" + }, + "35": { + "key": "?\n/", + "p2": "003F", + "p8": "002F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "no": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " ! \n 1 ", + "p1": "21", + "p7": "31", + "position": "110" + }, + "03": { + "key": " \" \n 2 @", + "p1": "22", + "p7": "32", + "p9": "40", + "position": "111" + }, + "04": { + "key": " # \n 3 \u00a3", + "p1": "23", + "p7": "33", + "p9": "00A3", + "position": "112" + }, + "05": { + "key": " \u00a4 \n 4 $", + "p1": "00A4", + "p7": "34", + "p9": "24", + "position": "113" + }, + "06": { + "key": " % \n 5 ", + "p1": "25", + "p7": "35", + "position": "114" + }, + "07": { + "key": " & \n 6 ", + "p1": "26", + "p7": "36", + "position": "115" + }, + "08": { + "key": " / \n 7 {", + "p1": "002F", + "p7": "37", + "p9": "007B", + "position": "116" + }, + "09": { + "key": " ( \n 8 [", + "p1": "28", + "p7": "38", + "p9": "005B", + "position": "117" + }, + "0A": { + "key": " ) \n 9 ]", + "p1": "29", + "p7": "39", + "p9": "005D", + "position": "118" + }, + "0B": { + "key": " = \n 0 }", + "p1": "003D", + "p7": "30", + "p9": "007D", + "position": "119" + }, + "0C": { + "key": " ? \n + ", + "p1": "003F", + "p7": "002B", + "position": "120" + }, + "0D": { + "key": " ` \n \\ \u00b4", + "p1": "60", + "p7": "005C", + "p9": "00B4", + "position": "121" + }, + "0E": { + "format": "right", + "key": " backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": " tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " q", + "p5": "71", + "position": "210" + }, + "11": { + "key": " w", + "p5": "77", + "position": "211" + }, + "12": { + "key": " e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": " r", + "p5": "72", + "position": "213" + }, + "14": { + "key": " t", + "p5": "74", + "position": "214" + }, + "15": { + "key": " y", + "p5": "79", + "position": "215" + }, + "16": { + "key": " u", + "p5": "75", + "position": "216" + }, + "17": { + "key": " i", + "p5": "69", + "position": "217" + }, + "18": { + "key": " o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": " p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": " \u00e5", + "p5": "E5", + "position": "220" + }, + "1B": { + "key": " \u00a8 ~", + "p5": "00A8", + "p9": "007E", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "006B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "006C", + "position": "318" + }, + "27": { + "key": "\u00f8", + "p5": "00F8", + "position": "319" + }, + "28": { + "key": "\u00e6", + "p5": "E6", + "position": "320" + }, + "29": { + "key": " \u00a7 \n | ", + "p1": "00A7", + "p7": "007C", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": " '", + "p5": "27", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "007A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "006E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "006D", + "position": "416" + }, + "33": { + "key": ";\n,", + "p2": "003B", + "p8": "002C", + "position": "417" + }, + "34": { + "key": ":\n.", + "p2": "003A", + "p8": "002E", + "position": "418" + }, + "35": { + "key": "_\n-", + "p2": "005F", + "p8": "002D", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": ">\n<", + "p2": "3e", + "p8": "003C", + "position": "409" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "key": " ", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "pl": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "!\n1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": "@\n2", + "p2": "40", + "p8": "32", + "position": "111" + }, + "04": { + "key": "#\n3", + "p2": "23", + "p8": "33", + "position": "112" + }, + "05": { + "key": "$\n4", + "p2": "24", + "p8": "34", + "position": "113" + }, + "06": { + "key": "%\n5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": "^\n6", + "p2": "5E", + "p8": "36", + "position": "115" + }, + "08": { + "key": "&\n7", + "p2": "26", + "p8": "37", + "position": "116" + }, + "09": { + "key": "*\n8", + "p2": "2A", + "p8": "38", + "position": "117" + }, + "0A": { + "key": "(\n9", + "p2": "28", + "p8": "39", + "position": "118" + }, + "0B": { + "key": ")\n0", + "p2": "29", + "p8": "30", + "position": "119" + }, + "0C": { + "key": "_\n-", + "p2": "5F", + "p8": "2D", + "position": "120" + }, + "0D": { + "key": "+\n=", + "p2": "2B", + "p8": "3D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "label": "q", + "p1": "51", + "position": "210" + }, + "11": { + "key": "w", + "label": "w", + "p1": "57", + "position": "211" + }, + "12": { + "key": "e", + "label": "e", + "p1": "45", + "position": "212" + }, + "13": { + "key": "r", + "label": "r", + "p1": "52", + "position": "213" + }, + "14": { + "key": "t", + "label": "t", + "p1": "54", + "position": "214" + }, + "15": { + "key": "y", + "label": "y", + "p1": "59", + "position": "215" + }, + "16": { + "key": "u", + "label": "u", + "p1": "55", + "position": "216" + }, + "17": { + "key": "i", + "label": "i", + "p1": "49", + "position": "217" + }, + "18": { + "key": "o", + "label": "o", + "p1": "4F", + "position": "218" + }, + "19": { + "key": "p", + "label": "p", + "p1": "50", + "position": "219" + }, + "1A": { + "key": "{\n[", + "p2": "7B", + "p8": "5B", + "position": "220" + }, + "1B": { + "key": "}\n]", + "p2": "7D", + "p8": "5D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "label": "a", + "p1": "41", + "position": "310" + }, + "1F": { + "key": "s", + "label": "s", + "p1": "53", + "position": "311" + }, + "20": { + "key": "d", + "label": "d", + "p1": "44", + "position": "312" + }, + "21": { + "key": "f", + "label": "f", + "p1": "46", + "position": "313" + }, + "22": { + "key": "g", + "label": "g", + "p1": "47", + "position": "314" + }, + "23": { + "key": "h", + "label": "h", + "p1": "48", + "position": "315" + }, + "24": { + "key": "j", + "label": "j", + "p1": "4A", + "position": "316" + }, + "25": { + "key": "k", + "label": "k", + "p1": "4B", + "position": "317" + }, + "26": { + "key": "l", + "label": "l", + "p1": "4C", + "position": "318" + }, + "27": { + "key": ":\n;", + "p2": "3A", + "p8": "3B", + "position": "319" + }, + "28": { + "key": "\"\n'", + "p2": "22", + "p8": "27", + "position": "320" + }, + "29": { + "key": "~\n`", + "p2": "7E", + "p8": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "|\n\\", + "p2": "7C", + "p8": "5C", + "position": "290" + }, + "2C": { + "key": "z", + "label": "z", + "p1": "5A", + "position": "410" + }, + "2D": { + "key": "x", + "label": "x", + "p1": "58", + "position": "411" + }, + "2E": { + "key": "c", + "label": "c", + "p1": "43", + "position": "412" + }, + "2F": { + "key": "v", + "label": "v", + "p1": "56", + "position": "413" + }, + "30": { + "key": "b", + "label": "b", + "p1": "42", + "position": "414" + }, + "31": { + "key": "n", + "label": "n", + "p1": "4E", + "position": "415" + }, + "32": { + "key": "m", + "label": "m", + "p1": "4D", + "position": "416" + }, + "33": { + "key": "<\n,", + "p2": "3C", + "p8": "2C", + "position": "417" + }, + "34": { + "key": ">\n.", + "p2": "3E", + "p8": "2E", + "position": "418" + }, + "35": { + "key": "?\n/", + "p2": "3F", + "p8": "2F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "pt_BR": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! \n1 \u00b9", + "p1": "21", + "p7": "31", + "p9": "B9", + "position": "110" + }, + "03": { + "key": "@ \n2 \u00b2", + "p1": "40", + "p7": "32", + "p9": "B2", + "position": "111" + }, + "04": { + "key": "# \n3 \u00b3", + "p1": "23", + "p7": "33", + "p9": "B3", + "position": "112" + }, + "05": { + "key": "$ \n4 \u00a3", + "p1": "24", + "p7": "34", + "p9": "A3", + "position": "113" + }, + "06": { + "key": "% \n5 \u00a2", + "p1": "25", + "p7": "35", + "p9": "A2", + "position": "114" + }, + "07": { + "key": "\u00a8 \n6 \u00ac", + "p1": "A8", + "p7": "36", + "p9": "00AC", + "position": "115" + }, + "08": { + "key": "& \n7 ", + "p1": "26", + "p7": "37", + "position": "116" + }, + "09": { + "key": "* \n8 ", + "p1": "2A", + "p7": "38", + "position": "117" + }, + "0A": { + "key": "( \n9 ", + "p1": "28", + "p7": "39", + "position": "118" + }, + "0B": { + "key": ") \n0 ", + "p1": "29", + "p7": "30", + "position": "119" + }, + "0C": { + "key": "_ \n- ", + "p1": "5F", + "p7": "2D", + "position": "120" + }, + "0D": { + "key": "+ \n= \u00a7", + "p1": "2B", + "p7": "3D", + "p9": "A7", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q /", + "p5": "71", + "p9": "2F", + "position": "210" + }, + "11": { + "key": "w ?", + "p5": "77", + "p9": "3F", + "position": "211" + }, + "12": { + "key": "e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "6F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "` \n\u00b4 ", + "p1": "60", + "p7": "B4", + "position": "220" + }, + "1B": { + "key": "{ \n[ \uda68\udf32", + "notes": "The a needs to be underlined", + "p1": "7B", + "p7": "5B", + "p9": "AA332", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": "\u00e7", + "p5": "e7", + "position": "319" + }, + "28": { + "key": "^ \n~ ", + "p1": "5E", + "p7": "7E", + "position": "320" + }, + "29": { + "key": "\" \n' ", + "p1": "22", + "p7": "27", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "} \n] \u00ba", + "notes": "The o needs to be underlined", + "p1": "7D", + "p7": "5D", + "p9": "BA", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c \u20a2", + "p5": "63", + "p9": "20A2", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": "< \n, ", + "p1": "3C", + "p7": "2C", + "position": "417" + }, + "34": { + "key": "> \n. ", + "p1": "3E", + "p7": "2E", + "position": "418" + }, + "35": { + "key": ": \n; ", + "p1": "3A", + "p7": "3B", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": "| \n\\ ", + "p1": "7C", + "p7": "5C", + "position": "409" + }, + "73": { + "key": "? \n/ ", + "p1": "3F", + "p7": "2f", + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "B" + }, + "pt_PT": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " !\n 1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": " \"\n 2 @", + "p2": "22", + "p8": "32", + "p9": "40", + "position": "111" + }, + "04": { + "key": " #\n 3 \u00a3", + "p2": "23", + "p8": "33", + "p9": "a3", + "position": "112" + }, + "05": { + "key": " $\n 4 \u00a7", + "p2": "24", + "p8": "34", + "p9": "a7", + "position": "113" + }, + "06": { + "key": " %\n 5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": " &\n 6", + "p2": "26", + "p8": "36", + "position": "115" + }, + "08": { + "key": " /\n 7 {", + "p2": "2f", + "p8": "37", + "p9": "7b", + "position": "116" + }, + "09": { + "key": " (\n 8 [", + "p2": "28", + "p8": "38", + "p9": "5b", + "position": "117" + }, + "0A": { + "key": " )\n 9 ]", + "p2": "29", + "p8": "39", + "p9": "5d", + "position": "118" + }, + "0B": { + "key": " =\n 0 }", + "p2": "3d", + "p8": "30", + "p9": "7d", + "position": "119" + }, + "0C": { + "key": " ?\n '", + "p2": "3f", + "p8": "27", + "position": "120" + }, + "0D": { + "key": " \u00bb\n \u00ab", + "p2": "bb", + "p8": "ab", + "position": "121" + }, + "0E": { + "format": "right", + "key": " glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e \u20ac", + "p5": "65", + "p9": "20ac", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "6F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "*\n+ \u00a8", + "p2": "2a", + "p8": "2b", + "p9": "a8", + "position": "220" + }, + "1B": { + "key": "`\n\u00b4", + "p2": "60", + "p8": "b4", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": "\u00e7", + "p5": "e7", + "position": "319" + }, + "28": { + "key": "\u00aa\n\u00ba", + "p2": "aa", + "p8": "ba", + "position": "320" + }, + "29": { + "key": " |\n \\", + "p2": "7c", + "p8": "5c", + "position": "100" + }, + "2A": { + "format": "glyph_shift", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "^\n~", + "p2": "5e", + "p8": "7e", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": ";\n,", + "p2": "3b", + "p8": "2c", + "position": "417" + }, + "34": { + "key": ":\n.", + "p2": "3a", + "p8": "2e", + "position": "418" + }, + "35": { + "key": "_\n-", + "p2": "5f", + "p8": "2d", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": ">\n<", + "p2": "3e", + "p8": "3c", + "position": "409" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "key": " ", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "ro": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " ! \n 1 ~", + "p1": "21", + "p7": "31", + "p9": "7E", + "position": "110" + }, + "03": { + "key": " \" \n 2 \u02c7", + "p1": "22", + "p7": "32", + "p9": "2C7", + "position": "111" + }, + "04": { + "key": " # \n 3 ^", + "p1": "23", + "p7": "33", + "p9": "5E", + "position": "112" + }, + "05": { + "key": " $ \n 4 \u02d8", + "notes": "Left $ instead of \u00a4, as \u00a4 is not usual comparing to $", + "p1": "24", + "p7": "34", + "p9": "2D8", + "position": "113" + }, + "06": { + "key": " % \n 5 \u02da", + "p1": "25", + "p7": "35", + "p9": "2DA", + "position": "114" + }, + "07": { + "key": " & \n 6 \u02db", + "p1": "26", + "p7": "36", + "p9": "2DB", + "position": "115" + }, + "08": { + "key": " / \n 7 `", + "p1": "2F", + "p7": "37", + "p9": "60", + "position": "116" + }, + "09": { + "key": " ( \n 8 \u02d9", + "p1": "28", + "p7": "38", + "p9": "2D9", + "position": "117" + }, + "0A": { + "key": " ) \n 9 \u00b4", + "p1": "29", + "p7": "39", + "p9": "B4", + "position": "118" + }, + "0B": { + "key": " = \n 0 \u02dd", + "p1": "3D", + "p7": "30", + "p9": "2DD", + "position": "119" + }, + "0C": { + "key": " ? \n + \u00a8", + "p1": "3F", + "p7": "2B", + "p9": "A8", + "position": "120" + }, + "0D": { + "key": " * \n ' \u00b8", + "notes": " ", + "p1": "2A", + "p7": "27", + "p9": "B8", + "position": "121" + }, + "0E": { + "format": "right", + "key": " backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": " tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " q \\", + "p5": "71", + "p9": "5c", + "position": "210" + }, + "11": { + "key": "w \u00a6", + "p5": "77", + "p9": "A6", + "position": "211" + }, + "12": { + "key": "e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": "r \u00ae", + "notes": "Added registered sign, as it is an often used symbol", + "p5": "72", + "p9": "AE", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "z", + "p5": "7A", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "6F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "\u0103 \u00f7", + "p5": "103", + "p9": "00F7", + "position": "220" + }, + "1B": { + "key": "\u00ee \u00d7", + "p5": "00EE", + "p9": "00D7", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a \u201e", + "notes": "Added Romanian quotes, as they are often used", + "p5": "61", + "p9": "201E", + "position": "310" + }, + "1F": { + "key": "s \u201d", + "notes": "Added Romanian quotes, as they are often used", + "p5": "73", + "p9": "201D", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l \u00a3", + "notes": "Added pound sign", + "p5": "6C", + "p9": "A3", + "position": "318" + }, + "27": { + "key": "\u015f @", + "p5": "015F", + "p9": "40", + "position": "319" + }, + "28": { + "key": "\u0163 \u00df", + "p5": "163", + "p9": "DF", + "position": "320" + }, + "29": { + "key": " [ \n ] ", + "p1": "5B", + "p7": "5D", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u00e2", + "p5": "E2", + "position": "290" + }, + "2C": { + "key": "y", + "p5": "79", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c \u00a9", + "notes": "Added copyright sign, as it is an often used symbol", + "p5": "63", + "p9": "00A9", + "position": "412" + }, + "2F": { + "key": "v @", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b {", + "p5": "62", + "p9": "7b", + "position": "414" + }, + "31": { + "key": "n }", + "p5": "6E", + "p9": "7d", + "position": "415" + }, + "32": { + "key": "m \u00a7", + "p5": "6D", + "p9": "a7", + "position": "416" + }, + "33": { + "key": ";; \n,, ", + "p1": "3B", + "p7": "2C", + "position": "417" + }, + "34": { + "key": ":: \n.. ", + "p1": "3A", + "p7": "2E", + "position": "418" + }, + "35": { + "key": "__ \n-- ", + "p1": "5F", + "p7": "2D", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": "<< \n>> ", + "p1": "3C", + "p7": "3E", + "position": "409" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "key": " \u00e2", + "p5": "E2", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "ru": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! \n1 ", + "p1": "21", + "p7": "31", + "position": "110" + }, + "03": { + "key": "@ \"\n2 ", + "p1": "40", + "p3": "22", + "p7": "32", + "position": "111" + }, + "04": { + "key": "# \u2116\n3 ", + "p1": "23", + "p3": "2116", + "p7": "33", + "position": "112" + }, + "05": { + "key": "$ ;\n4 ", + "p1": "24", + "p3": "3b", + "p7": "34", + "position": "113" + }, + "06": { + "key": "% \n5 ", + "p1": "25", + "p7": "35", + "position": "114" + }, + "07": { + "key": "^ :\n6 ", + "p1": "5e", + "p3": "3a", + "p7": "36", + "position": "115" + }, + "08": { + "key": "& ?\n7 ", + "p1": "26", + "p3": "3f", + "p7": "37", + "position": "116" + }, + "09": { + "key": "* \n8 ", + "p1": "2a", + "p7": "38", + "position": "117" + }, + "0A": { + "key": "( \n9 ", + "p1": "28", + "p7": "39", + "position": "118" + }, + "0B": { + "key": ") \n0 ", + "p1": "29", + "p7": "30", + "position": "119" + }, + "0C": { + "key": "_ \n- ", + "p1": "5f", + "p7": "2d", + "position": "120" + }, + "0D": { + "key": "+ \n= ", + "p1": "2b", + "p7": "3d", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q \n \u0439", + "p1": "71", + "p9": "439", + "position": "210" + }, + "11": { + "key": "w \n \u0446", + "p1": "77", + "p9": "446", + "position": "211" + }, + "12": { + "key": "e \n \u0443", + "p1": "65", + "p9": "443", + "position": "212" + }, + "13": { + "key": "r \n \u043a", + "p1": "72", + "p9": "43a", + "position": "213" + }, + "14": { + "key": "t \n \u0435", + "p1": "74", + "p9": "435", + "position": "214" + }, + "15": { + "key": "y \n \u043d", + "p1": "79", + "p9": "43d", + "position": "215" + }, + "16": { + "key": "u \n \u0433", + "p1": "75", + "p9": "433", + "position": "216" + }, + "17": { + "key": "i \n \u0448", + "p1": "69", + "p9": "448", + "position": "217" + }, + "18": { + "key": "o \n \u0449", + "p1": "6F", + "p9": "449", + "position": "218" + }, + "19": { + "key": "p \n \u0437", + "p1": "70", + "p9": "437", + "position": "219" + }, + "1A": { + "key": "{ \n[ \u0445", + "p1": "7b", + "p7": "5b", + "p9": "445", + "position": "220" + }, + "1B": { + "key": "} \n] \u044a", + "p1": "7d", + "p7": "5d", + "p9": "44a", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a \n \u0444", + "p1": "61", + "p9": "444", + "position": "310" + }, + "1F": { + "key": "s \n \u044b", + "p1": "73", + "p9": "44b", + "position": "311" + }, + "20": { + "key": "d \n \u0432", + "p1": "64", + "p9": "432", + "position": "312" + }, + "21": { + "key": "f \n \u0430", + "p1": "66", + "p9": "430", + "position": "313" + }, + "22": { + "key": "g \n \u043f", + "p1": "67", + "p9": "43f", + "position": "314" + }, + "23": { + "key": "h \n \u0440", + "p1": "68", + "p9": "440", + "position": "315" + }, + "24": { + "key": "j \n \u043e", + "p1": "6A", + "p9": "43e", + "position": "316" + }, + "25": { + "key": "k \n \u043b", + "p1": "6B", + "p9": "43b", + "position": "317" + }, + "26": { + "key": "l \n \u0434", + "p1": "6C", + "p9": "434", + "position": "318" + }, + "27": { + "key": ": \n; \u0436", + "p1": "3a", + "p7": "3b", + "p9": "436", + "position": "319" + }, + "28": { + "key": "\" \n' \u044d", + "p1": "22", + "p7": "27", + "p9": "44d", + "position": "320" + }, + "29": { + "key": "~ \n` \u0451", + "p1": "7e", + "p7": "60", + "p9": "451", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "| /\n\\ \\", + "p1": "7c", + "p3": "2f", + "p7": "5c", + "p9": "5c", + "position": "290" + }, + "2C": { + "key": "z \n \u044f", + "p1": "7A", + "p9": "44f", + "position": "410" + }, + "2D": { + "key": "x \n \u0447", + "p1": "78", + "p9": "447", + "position": "411" + }, + "2E": { + "key": "c \n \u0441", + "p1": "63", + "p9": "441", + "position": "412" + }, + "2F": { + "key": "v \n \u043c", + "p1": "76", + "p9": "43c", + "position": "413" + }, + "30": { + "key": "b \n \u0438", + "p1": "62", + "p9": "438", + "position": "414" + }, + "31": { + "key": "n \n \u0442", + "p1": "6E", + "p9": "442", + "position": "415" + }, + "32": { + "key": "m \n \u044c", + "p1": "6D", + "p9": "44c", + "position": "416" + }, + "33": { + "key": "< \n, \u0431", + "p1": "3c", + "p7": "2c", + "p9": "431", + "position": "417" + }, + "34": { + "key": "> \n. \u044e", + "p1": "3e", + "p7": "2e", + "p9": "44e", + "position": "418" + }, + "35": { + "key": "? ,\n/ .", + "p1": "3f", + "p3": "2c", + "p7": "2f", + "p9": "2e", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "\u043f\u043e\u0438\u0441\u043a", + "position": "300" + } + }, + "layoutName": "U" + }, + "sk": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! 1\n1 +", + "p1": "21", + "p3": "31", + "p7": "31", + "p9": "002b", + "position": "110" + }, + "03": { + "key": "@ 2\n2 \u013e", + "p1": "40", + "p3": "32", + "p7": "32", + "p9": "013E", + "position": "111" + }, + "04": { + "key": "# 3\n3 \u0161", + "p1": "23", + "p3": "33", + "p7": "33", + "p9": "161", + "position": "112" + }, + "05": { + "key": "$ 4\n4 \u010d", + "p1": "24", + "p3": "34", + "p7": "34", + "p9": "010d", + "position": "113" + }, + "06": { + "key": "% 5\n5 \u0165", + "p1": "25", + "p3": "35", + "p7": "35", + "p9": "165", + "position": "114" + }, + "07": { + "key": "^ 6\n6 \u017e", + "p1": "5e", + "p3": "36", + "p7": "36", + "p9": "017e", + "position": "115" + }, + "08": { + "key": "& 7\n7 \u00fd", + "p1": "26", + "p3": "37", + "p7": "37", + "p9": "fd", + "position": "116" + }, + "09": { + "key": "* 8\n8 \u00e1", + "p1": "2a", + "p3": "38", + "p7": "38", + "p9": "e1", + "position": "117" + }, + "0A": { + "key": "( 9\n9 \u00ed", + "p1": "28", + "p3": "39", + "p7": "39", + "p9": "ed", + "position": "118" + }, + "0B": { + "key": ") 0\n0 \u00e9", + "p1": "29", + "p3": "30", + "p7": "30", + "p9": "e9", + "position": "119" + }, + "0C": { + "key": "_ %\n- =", + "p1": "5f", + "p3": "25", + "p7": "2d", + "p9": "3d", + "position": "120" + }, + "0D": { + "key": "+ \u02c7\n= \u00b4", + "p1": "2b", + "p3": "2c7", + "p7": "3d", + "p9": "b4", + "position": "121" + }, + "0E": { + "format": "right", + "key": "#NUM!", + "notes": "backspace", + "p2": "backspace", + "p8": "glyph_backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "#NUM!", + "notes": "tab", + "p2": "tab", + "p8": "glyph_tab", + "position": "200" + }, + "10": { + "key": "q ", + "p1": "71", + "position": "210" + }, + "11": { + "key": "w ", + "p1": "77", + "position": "211" + }, + "12": { + "key": "e ", + "p1": "65", + "position": "212" + }, + "13": { + "key": "r ", + "p1": "72", + "position": "213" + }, + "14": { + "key": "t ", + "p1": "74", + "position": "214" + }, + "15": { + "key": "y \n z", + "p1": "79", + "p9": "7a", + "position": "215" + }, + "16": { + "key": "u ", + "p1": "75", + "position": "216" + }, + "17": { + "key": "i ", + "p1": "69", + "position": "217" + }, + "18": { + "key": "o ", + "p1": "6f", + "position": "218" + }, + "19": { + "key": "p ", + "p1": "70", + "position": "219" + }, + "1A": { + "key": "{ \u00b4\n[ \u00fa", + "p1": "7b", + "p3": "b4", + "p7": "5b", + "p9": "fa", + "position": "220" + }, + "1B": { + "key": "} (\n] \u00e4", + "p1": "7d", + "p3": "28", + "p7": "5d", + "p9": "e4", + "position": "221" + }, + "1C": { + "format": "right", + "key": "#NUM!", + "notes": "enter", + "p2": "enter", + "p8": "glyph_enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a ", + "p1": "61", + "position": "310" + }, + "1F": { + "key": "s ", + "p1": "73", + "position": "311" + }, + "20": { + "key": "d ", + "p1": "64", + "position": "312" + }, + "21": { + "key": "f ", + "p1": "66", + "position": "313" + }, + "22": { + "key": "g ", + "p1": "67", + "position": "314" + }, + "23": { + "key": "h ", + "p1": "68", + "position": "315" + }, + "24": { + "key": "j ", + "p1": "6a", + "position": "316" + }, + "25": { + "key": "k ", + "p1": "6b", + "position": "317" + }, + "26": { + "key": "l ", + "p1": "6c", + "position": "318" + }, + "27": { + "key": ": \"\n; \u00f4", + "p1": "3a", + "p3": "22", + "p7": "3b", + "p9": "f4", + "position": "319" + }, + "28": { + "key": "\" !\n' \u00a7", + "p1": "22", + "p3": "21", + "p7": "27", + "p9": "a7", + "position": "320" + }, + "29": { + "key": "~ \u00b0\n` ;", + "p1": "7e", + "p3": "b0", + "p7": "60", + "p9": "3b", + "position": "100" + }, + "2A": { + "format": "left", + "key": "#NUM!", + "notes": "left shift", + "p2": "shift", + "p8": "glyph_shift", + "position": "400" + }, + "2B": { + "key": "| )\n\\ \u0148", + "p1": "7c", + "p3": "29", + "p7": "5c", + "p9": "148", + "position": "290" + }, + "2C": { + "key": "z \n y", + "p1": "7a", + "p9": "79", + "position": "410" + }, + "2D": { + "key": "x ", + "p1": "78", + "position": "411" + }, + "2E": { + "key": "c ", + "p1": "63", + "position": "412" + }, + "2F": { + "key": "v ", + "p1": "76", + "position": "413" + }, + "30": { + "key": "b ", + "p1": "62", + "position": "414" + }, + "31": { + "key": "n ", + "p1": "6e", + "position": "415" + }, + "32": { + "key": "m ", + "p1": "6d", + "position": "416" + }, + "33": { + "key": "< ?\n, ,", + "p1": "3c", + "p3": "3f", + "p7": "2c", + "p9": "2c", + "position": "417" + }, + "34": { + "key": "> :\n. .", + "p1": "3e", + "p3": "3a", + "p7": "2e", + "p9": "2e", + "position": "418" + }, + "35": { + "key": "? _\n/ -", + "p1": "3f", + "p3": "5f", + "p7": "2f", + "p9": "2d", + "position": "419" + }, + "36": { + "format": "right", + "key": "#NUM!", + "notes": "right shift", + "p2": "shift", + "p8": "glyph_shift", + "position": "490" + }, + "37": { + "key": "* |\n& \\", + "p1": "2a", + "p3": "7c", + "p7": "26", + "p9": "5c", + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "sl": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " ! \n 1 ~", + "p1": "21", + "p8": "31", + "p9": "007E", + "position": "110" + }, + "03": { + "key": " \" \n 2 \u02c7", + "p1": "22", + "p8": "32", + "p9": "02C7", + "position": "111" + }, + "04": { + "key": " # \n 3 ^", + "p1": "23", + "p8": "33", + "p9": "005E", + "position": "112" + }, + "05": { + "key": " $ \n 4 \u02d8", + "p1": "24", + "p8": "34", + "p9": "02D8", + "position": "113" + }, + "06": { + "key": " % \n 5 \u02da", + "p1": "25", + "p8": "35", + "p9": "02DA", + "position": "114" + }, + "07": { + "key": " & \n 6 \u02db", + "p1": "26", + "p8": "36", + "p9": "02DB", + "position": "115" + }, + "08": { + "key": " / \n 7 `", + "p1": "002F", + "p8": "37", + "p9": "60", + "position": "116" + }, + "09": { + "key": " ( \n 8 \u02d9", + "p1": "28", + "p8": "38", + "p9": "02D9", + "position": "117" + }, + "0A": { + "key": " ) \n 9 \u00b4", + "p1": "29", + "p8": "39", + "p9": "00B4", + "position": "118" + }, + "0B": { + "key": " = \n 0 \u02dd", + "p1": "003D", + "p8": "30", + "p9": "02DD", + "position": "119" + }, + "0C": { + "key": " ? \n ' \u00a8", + "p1": "003F", + "p8": "27", + "p9": "00A8", + "position": "120" + }, + "0D": { + "key": " * \n + \u00b8", + "p1": "002A", + "p8": "002B", + "p9": "00B8", + "position": "121" + }, + "0E": { + "format": "right", + "key": " glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": " glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " Q \n \\", + "p1": "51", + "p9": "005C", + "position": "210" + }, + "11": { + "key": " W \n |", + "p1": "57", + "p9": "007C", + "position": "211" + }, + "12": { + "key": "E \n \u20ac", + "p1": "45", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": "R ", + "p1": "52", + "position": "213" + }, + "14": { + "key": "T ", + "p1": "54", + "position": "214" + }, + "15": { + "key": "Z ", + "p1": "005A", + "position": "215" + }, + "16": { + "key": "U ", + "p1": "55", + "position": "216" + }, + "17": { + "key": "I ", + "p1": "49", + "position": "217" + }, + "18": { + "key": "O ", + "p1": "004F", + "position": "218" + }, + "19": { + "key": "P ", + "p1": "50", + "position": "219" + }, + "1A": { + "key": "\u0160 \n \u00f7", + "p1": "160", + "p9": "00F7", + "position": "220" + }, + "1B": { + "key": "\u00d0 \n \u00d7", + "p1": "00D0", + "p9": "00D7", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "A ", + "p1": "41", + "position": "310" + }, + "1F": { + "key": "S ", + "p1": "53", + "position": "311" + }, + "20": { + "key": "D ", + "p1": "44", + "position": "312" + }, + "21": { + "key": "F \n [", + "p1": "46", + "p9": "005B", + "position": "313" + }, + "22": { + "key": "G \n ]", + "p1": "47", + "p9": "005D", + "position": "314" + }, + "23": { + "key": "H ", + "p1": "48", + "position": "315" + }, + "24": { + "key": "J ", + "p1": "004A", + "position": "316" + }, + "25": { + "key": "K \n \u0142", + "p1": "004B", + "p9": "142", + "position": "317" + }, + "26": { + "key": "L \n \u0141", + "p1": "004C", + "p9": "141", + "position": "318" + }, + "27": { + "key": "\u010c ", + "p1": "010C", + "position": "319" + }, + "28": { + "key": "\u0106 \n \u00df", + "p1": "106", + "p9": "00DF", + "position": "320" + }, + "29": { + "key": " \u00a8 \n \u00b8", + "p1": "00A8", + "p8": "00B8", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u017d \n \u00a4", + "p1": "017D", + "p9": "00A4", + "position": "290" + }, + "2C": { + "key": "Y ", + "p1": "59", + "position": "410" + }, + "2D": { + "key": "X ", + "p1": "58", + "position": "411" + }, + "2E": { + "key": "C ", + "p1": "43", + "position": "412" + }, + "2F": { + "key": "V \n @", + "p1": "56", + "p9": "40", + "position": "413" + }, + "30": { + "key": "B \n {", + "p1": "42", + "p9": "007B", + "position": "414" + }, + "31": { + "key": "N \n }", + "p1": "004E", + "p9": "007D", + "position": "415" + }, + "32": { + "key": "M \n \u00a7", + "p1": "004D", + "p9": "00A7", + "position": "416" + }, + "33": { + "key": "; \n,", + "p1": "003B", + "p8": "002C", + "position": "417" + }, + "34": { + "key": ": \n.", + "p1": "003A", + "p8": "002E", + "position": "418" + }, + "35": { + "key": "_ \n -", + "p1": "005F", + "p9": "002D", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "key": "> \n<", + "p1": "003E", + "p8": "003C", + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "key": " ", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "sr": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " \u0438\u0437\u043b", + "label": "\u0438\u0437\u043b", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " ! \n 1 ~", + "p1": "21", + "p8": "31", + "p9": "007E", + "position": "110" + }, + "03": { + "key": " \" \n 2", + "p1": "22", + "p8": "32", + "position": "111" + }, + "04": { + "key": " # \n 3 ^", + "p1": "23", + "p8": "33", + "p9": "005E", + "position": "112" + }, + "05": { + "key": " $ \n 4", + "p1": "24", + "p8": "34", + "position": "113" + }, + "06": { + "key": " % \n 5", + "p1": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": " & \n 6", + "p1": "26", + "p8": "36", + "position": "115" + }, + "08": { + "key": " ' \n 7 `", + "p1": "27", + "p8": "37", + "p9": "60", + "position": "116" + }, + "09": { + "key": " ( \n 8", + "p1": "28", + "p8": "38", + "position": "117" + }, + "0A": { + "key": " ) \n 9", + "p1": "29", + "p8": "39", + "position": "118" + }, + "0B": { + "key": " = \n 0", + "p1": "003D", + "p8": "30", + "position": "119" + }, + "0C": { + "key": " ? \n '", + "p1": "003F", + "p8": "27", + "position": "120" + }, + "0D": { + "key": " * \n +", + "p1": "002A", + "p8": "002B", + "position": "121" + }, + "0E": { + "format": "right", + "key": "glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "\u0459 \n \\", + "p1": "459", + "p9": "005C", + "position": "210" + }, + "11": { + "key": "\u045a \n |", + "p1": "045A", + "p9": "007C", + "position": "211" + }, + "12": { + "key": "\u0435 ", + "p1": "435", + "position": "212" + }, + "13": { + "key": "\u0440 ", + "p1": "440", + "position": "213" + }, + "14": { + "key": "\u0442 ", + "p1": "442", + "position": "214" + }, + "15": { + "key": "\u0437 ", + "p1": "437", + "position": "215" + }, + "16": { + "key": "\u0443 ", + "p1": "443", + "position": "216" + }, + "17": { + "key": "\u0438 ", + "p1": "438", + "position": "217" + }, + "18": { + "key": "\u043e ", + "p1": "043E", + "position": "218" + }, + "19": { + "key": "\u043f ", + "p1": "043F", + "position": "219" + }, + "1A": { + "key": "\u0448 ", + "p1": "448", + "position": "220" + }, + "1B": { + "key": "\u0442 ", + "p1": "442", + "position": "221" + }, + "1C": { + "format": "right", + "key": "glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "\u043a\u043d\u0442\u0440", + "label": "\u043a\u043d\u0442\u0440", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "\u0430 ", + "p1": "430", + "position": "310" + }, + "1F": { + "key": "\u0441 ", + "p1": "441", + "position": "311" + }, + "20": { + "key": "\u0434 ", + "p1": "434", + "position": "312" + }, + "21": { + "key": "\u0444 \n [", + "p1": "444", + "p9": "005B", + "position": "313" + }, + "22": { + "key": "\u0433 \n ]", + "p1": "433", + "p9": "005D", + "position": "314" + }, + "23": { + "key": "\u0445 ", + "p1": "445", + "position": "315" + }, + "24": { + "key": "\u0458 ", + "p1": "458", + "position": "316" + }, + "25": { + "key": "\u043a ", + "p1": "043A", + "position": "317" + }, + "26": { + "key": "\u043b ", + "p1": "043B", + "position": "318" + }, + "27": { + "key": "\u0447 ", + "p1": "447", + "position": "319" + }, + "28": { + "key": "\u045b ", + "p1": "045B", + "position": "320" + }, + "29": { + "key": " | \n \\", + "p1": "007C", + "p8": "005C", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u0436 ", + "p1": "436", + "position": "290" + }, + "2C": { + "key": "\u0455 ", + "p1": "455", + "position": "410" + }, + "2D": { + "key": "\u045f ", + "p1": "045F", + "position": "411" + }, + "2E": { + "key": "\u0446 ", + "p1": "446", + "position": "412" + }, + "2F": { + "key": "\u0432 \n @", + "p1": "432", + "p9": "40", + "position": "413" + }, + "30": { + "key": "\u0431 \n {", + "p1": "431", + "p9": "007B", + "position": "414" + }, + "31": { + "key": "\u043d \n }", + "p1": "043D", + "p9": "007D", + "position": "415" + }, + "32": { + "key": "\u043c \n \u00a7", + "p1": "043C", + "p9": "00A7", + "position": "416" + }, + "33": { + "key": "; \n,", + "p1": "003B", + "p8": "002C", + "position": "417" + }, + "34": { + "key": ": \n.", + "p1": "003A", + "p8": "002E", + "position": "418" + }, + "35": { + "key": "_ \n-", + "p1": "005F", + "p8": "002D", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "key": "> \n<", + "p1": "003E", + "p8": "003C", + "position": "409" + }, + "38": { + "format": "left", + "key": "\u0430\u043b\u0442", + "label": "\u0430\u043b\u0442", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "key": " ", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "\u043a\u043d\u0442\u0440", + "label": "\u043a\u043d\u0442\u0440", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "\u0430\u043b\u0442", + "label": "\u0430\u043b\u0442", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "sv": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " !\n 1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": " \"\n 2 @", + "p2": "22", + "p8": "32", + "p9": "40", + "position": "111" + }, + "04": { + "key": " #\n 3 \u00a3", + "p2": "23", + "p8": "33", + "p9": "00A3", + "position": "112" + }, + "05": { + "key": " \u00a4\n 4 $", + "p2": "00A4", + "p8": "34", + "p9": "24", + "position": "113" + }, + "06": { + "key": " %\n 5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": " &\n 6", + "p2": "26", + "p8": "36", + "position": "115" + }, + "08": { + "key": " /\n 7 {", + "p2": "002F", + "p8": "37", + "p9": "007B", + "position": "116" + }, + "09": { + "key": " (\n 8 [", + "p2": "28", + "p8": "38", + "p9": "005B", + "position": "117" + }, + "0A": { + "key": " )\n 9 ]", + "p2": "29", + "p8": "39", + "p9": "005D", + "position": "118" + }, + "0B": { + "key": " =\n 0 }", + "p2": "003D", + "p8": "30", + "p9": "007D", + "position": "119" + }, + "0C": { + "key": " ?\n + \\", + "p2": "003F", + "p8": "002B", + "p9": "005C", + "position": "120" + }, + "0D": { + "key": " `\n \u00b4", + "p2": "60", + "p8": "00B4", + "position": "121" + }, + "0E": { + "format": "right", + "key": " glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": " glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " q", + "p5": "71", + "position": "210" + }, + "11": { + "key": " w", + "p5": "77", + "position": "211" + }, + "12": { + "key": " e \u20ac", + "p5": "65", + "p9": "20AC", + "position": "212" + }, + "13": { + "key": " r", + "p5": "72", + "position": "213" + }, + "14": { + "key": " t", + "p5": "74", + "position": "214" + }, + "15": { + "key": " y", + "p5": "79", + "position": "215" + }, + "16": { + "key": " u", + "p5": "75", + "position": "216" + }, + "17": { + "key": " i", + "p5": "69", + "position": "217" + }, + "18": { + "key": " o", + "p5": "006F", + "position": "218" + }, + "19": { + "key": " p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": " \u00e5", + "p5": "E5", + "position": "220" + }, + "1B": { + "key": " ^\n \u00a8 ~", + "p2": "005E", + "p8": "00A8", + "p9": "007E", + "position": "221" + }, + "1C": { + "format": "right", + "key": " glyph_enter", + "label": "glyph_enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": " ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": " a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": " s", + "p5": "73", + "position": "311" + }, + "20": { + "key": " d", + "p5": "64", + "position": "312" + }, + "21": { + "key": " f", + "p5": "66", + "position": "313" + }, + "22": { + "key": " g", + "p5": "67", + "position": "314" + }, + "23": { + "key": " h", + "p5": "68", + "position": "315" + }, + "24": { + "key": " j", + "p5": "006A", + "position": "316" + }, + "25": { + "key": " k", + "p5": "006B", + "position": "317" + }, + "26": { + "key": " l", + "p5": "006C", + "position": "318" + }, + "27": { + "key": " \u00f6", + "p5": "00F6", + "position": "319" + }, + "28": { + "key": " \u00e4", + "p5": "E4", + "position": "320" + }, + "29": { + "key": " \u00bd\n \u00a7", + "p2": "00BD", + "p8": "00A7", + "position": "100" + }, + "2A": { + "format": "left", + "key": " glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": " *\n '", + "p2": "002A", + "p8": "27", + "position": "290" + }, + "2C": { + "key": " z", + "p5": "007A", + "position": "410" + }, + "2D": { + "key": " x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": " c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": " v", + "p5": "76", + "position": "413" + }, + "30": { + "key": " b", + "p5": "62", + "position": "414" + }, + "31": { + "key": " n", + "p5": "006E", + "position": "415" + }, + "32": { + "key": " m", + "p5": "006D", + "position": "416" + }, + "33": { + "key": " ;\n ,", + "p2": "003B", + "p8": "002C", + "position": "417" + }, + "34": { + "key": " :\n .", + "p2": "003A", + "p8": "002E", + "position": "418" + }, + "35": { + "key": " _\n -", + "p2": "005F", + "p8": "002D", + "position": "419" + }, + "36": { + "format": "right", + "key": " glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": " alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": " >\n < |", + "p2": "003E", + "p8": "003C", + "p9": "007C", + "position": "409" + }, + "73": { + "key": " ", + "position": "420" + }, + "79": { + "format": "smaller", + "key": " ", + "position": "551" + }, + "7B": { + "format": "smaller", + "key": " ", + "position": "550" + }, + "7D": { + "key": " ", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": " ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": " alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": " glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "th": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! +\n1 \u0e45", + "p1": "21", + "p3": "002B", + "p7": "31", + "p9": "E45", + "position": "110" + }, + "03": { + "key": "@ \u0e51\n2 /", + "p1": "40", + "p3": "E51", + "p7": "32", + "p9": "002F", + "position": "111" + }, + "04": { + "key": "# \u0e52\n3 -", + "p1": "23", + "p3": "E52", + "p7": "33", + "p9": "002D", + "position": "112" + }, + "05": { + "key": "$ \u0e53\n4 \u0e20", + "p1": "24", + "p3": "E53", + "p7": "34", + "p9": "E20", + "position": "113" + }, + "06": { + "key": "% \u0e54\n5 \u0e16", + "p1": "25", + "p3": "E54", + "p7": "35", + "p9": "E16", + "position": "114" + }, + "07": { + "key": "^ \u0e39\n6 \u0e38", + "p1": "005E", + "p3": "E39", + "p7": "36", + "p9": "E38", + "position": "115" + }, + "08": { + "key": "& \u0e3f\n7 \u0e36", + "p1": "26", + "p3": "0E3F", + "p7": "37", + "p9": "E36", + "position": "116" + }, + "09": { + "key": "* \u0e55\n8 \u0e04", + "p1": "002A", + "p3": "E55", + "p7": "38", + "p9": "E04", + "position": "117" + }, + "0A": { + "key": "( \u0e56\n9 \u0e15", + "p1": "28", + "p3": "E56", + "p7": "39", + "p9": "E15", + "position": "118" + }, + "0B": { + "key": ") \u0e57\n0 \u0e08", + "p1": "29", + "p3": "E57", + "p7": "30", + "p9": "E08", + "position": "119" + }, + "0C": { + "key": "_ \u0e58\n- \u0e02", + "p1": "005F", + "p3": "E58", + "p7": "002D", + "p9": "E02", + "position": "120" + }, + "0D": { + "key": "+ \u0e59\n= \u0e0a", + "p1": "002B", + "p3": "E59", + "p7": "003D", + "p9": "0E0A", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " \u0e50\nq \u0e46", + "p3": "E50", + "p7": "71", + "p9": "E46", + "position": "210" + }, + "11": { + "key": " \"\nw \u0e44", + "p3": "22", + "p7": "77", + "p9": "E44", + "position": "211" + }, + "12": { + "key": " \u0e0e\ne \u0e33", + "p3": "0E0E", + "p7": "65", + "p9": "E33", + "position": "212" + }, + "13": { + "key": " \u0e11\nr \u0e1e", + "p3": "E11", + "p7": "72", + "p9": "0E1E", + "position": "213" + }, + "14": { + "key": " \u0e18\nt \u0e30", + "p3": "E18", + "p7": "74", + "p9": "E30", + "position": "214" + }, + "15": { + "key": " \u0e4d\ny \u0e31", + "p3": "0E4D", + "p7": "79", + "p9": "E31", + "position": "215" + }, + "16": { + "key": " \u0e4a\nu \u0e35", + "p3": "0E4A", + "p7": "75", + "p9": "E35", + "position": "216" + }, + "17": { + "key": " \u0e13\ni \u0e23", + "p3": "E13", + "p7": "69", + "p9": "E23", + "position": "217" + }, + "18": { + "key": " \u0e2f\no \u0e19", + "p3": "0E2F", + "p7": "006F", + "p9": "E19", + "position": "218" + }, + "19": { + "key": " \u0e0d\np \u0e22", + "p3": "0E0D", + "p7": "70", + "p9": "E22", + "position": "219" + }, + "1A": { + "key": "{ \u0e10\n[ \u0e1a", + "p1": "007B", + "p3": "E10", + "p7": "005B", + "p9": "0E1A", + "position": "220" + }, + "1B": { + "key": "} ,\n] \u0e25", + "p1": "007D", + "p3": "002C", + "p7": "005D", + "p9": "E25", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": " \u0e24\na \u0e1f", + "p3": "E24", + "p7": "61", + "p9": "0E1F", + "position": "310" + }, + "1F": { + "key": " \u0e06\ns \u0e2b", + "p3": "E06", + "p7": "73", + "p9": "0E2B", + "position": "311" + }, + "20": { + "key": " \u0e0f\nd \u0e01", + "p3": "0E0F", + "p7": "64", + "p9": "E01", + "position": "312" + }, + "21": { + "key": " \u0e42\nf \u0e14", + "p3": "E42", + "p7": "66", + "p9": "E14", + "position": "313" + }, + "22": { + "key": " \u0e0c\ng \u0e40", + "p3": "0E0C", + "p7": "67", + "p9": "E40", + "position": "314" + }, + "23": { + "key": " \u0e47\nh \u0e49", + "p3": "E47", + "p7": "68", + "p9": "E49", + "position": "315" + }, + "24": { + "key": " \u0e4b\nj \u0e48", + "p3": "0E4B", + "p7": "006A", + "p9": "E48", + "position": "316" + }, + "25": { + "key": " \u0e29\nk \u0e32", + "p3": "E29", + "p7": "006B", + "p9": "E32", + "position": "317" + }, + "26": { + "key": " \u0e28\nl \u0e2a", + "p3": "E28", + "p7": "006C", + "p9": "0E2A", + "position": "318" + }, + "27": { + "key": ": \u0e0b\n; \u0e27", + "p1": "003A", + "p3": "0E0B", + "p7": "003B", + "p9": "E27", + "position": "319" + }, + "28": { + "key": "\" .\n' \u0e07", + "p1": "22", + "p3": "002E", + "p7": "27", + "p9": "E07", + "position": "320" + }, + "29": { + "key": "~ %\n` _", + "p1": "007E", + "p3": "25", + "p7": "60", + "p9": "005F", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u00a6 \u0e05\n\\ \u0e03", + "p1": "00A6", + "p3": "E05", + "p7": "005C", + "p9": "E03", + "position": "290" + }, + "2C": { + "key": " (\nz \u0e1c", + "p3": "28", + "p7": "007A", + "p9": "0E1C", + "position": "410" + }, + "2D": { + "key": " )\nx \u0e1b", + "p3": "29", + "p7": "78", + "p9": "0E1B", + "position": "411" + }, + "2E": { + "key": " \u0e09\nc \u0e41", + "p3": "E09", + "p7": "63", + "p9": "E41", + "position": "412" + }, + "2F": { + "key": " \u0e2e\nv \u0e2d", + "p3": "0E2E", + "p7": "76", + "p9": "0E2D", + "position": "413" + }, + "30": { + "key": " \u0e3a\nb \u0e34", + "p3": "0E3A", + "p7": "62", + "p9": "E34", + "position": "414" + }, + "31": { + "key": " \u0e4c\nn \u0e37", + "p3": "0E4C", + "p7": "006E", + "p9": "E37", + "position": "415" + }, + "32": { + "key": " ?\nm \u0e17", + "p3": "003F", + "p7": "006D", + "p9": "E17", + "position": "416" + }, + "33": { + "key": "< \u0e12\n, \u0e21", + "p1": "003C", + "p3": "E12", + "p7": "002C", + "p9": "E21", + "position": "417" + }, + "34": { + "key": "> \u0e2c\n. \u0e43", + "p1": "003E", + "p3": "0E2C", + "p7": "002E", + "p9": "E43", + "position": "418" + }, + "35": { + "key": "? \u0e26\n- \u0e1d", + "p1": "003F", + "p3": "E26", + "p7": "002D", + "p9": "0E1D", + "position": "419" + }, + "36": { + "format": "right", + "key": "shirt", + "label": "shirt", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "tr": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": " esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": " ! \n 1 ", + "p1": "21", + "p7": "31", + "position": "110" + }, + "03": { + "key": " ' \n 2 ", + "p1": "27", + "p7": "32", + "position": "111" + }, + "04": { + "key": " ^ \n 3 #", + "p1": "5e", + "p7": "33", + "p9": "23", + "position": "112" + }, + "05": { + "key": " + \n 4 $", + "p1": "2b", + "p7": "34", + "p9": "24", + "position": "113" + }, + "06": { + "key": " % \n 5 ", + "p1": "25", + "p7": "35", + "position": "114" + }, + "07": { + "key": " & \n 6 ", + "p1": "26", + "p7": "36", + "position": "115" + }, + "08": { + "key": " / \n 7 {", + "p1": "2f", + "p7": "37", + "p9": "7b", + "position": "116" + }, + "09": { + "key": " ( \n 8 [", + "p1": "28", + "p7": "38", + "p9": "5b", + "position": "117" + }, + "0A": { + "key": " ) \n 9 ]", + "p1": "29", + "p7": "39", + "p9": "5d", + "position": "118" + }, + "0B": { + "key": " = \n 0 }", + "p1": "3d", + "p7": "30", + "p9": "7d", + "position": "119" + }, + "0C": { + "key": " ? \n * \\", + "p1": "3f", + "p7": "2a", + "p9": "5c", + "position": "120" + }, + "0D": { + "key": " _ \n - ", + "p1": "5f", + "p7": "2d", + "position": "121" + }, + "0E": { + "format": "right", + "key": " glyph_backspace", + "label": "glyph_backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": " glyph_tab", + "label": "glyph_tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": " q @", + "p5": "71", + "p9": "40", + "position": "210" + }, + "11": { + "key": " w", + "p5": "77", + "position": "211" + }, + "12": { + "key": " e \u20ac", + "p5": "65", + "p9": "20ac", + "position": "212" + }, + "13": { + "key": " r", + "p5": "72", + "position": "213" + }, + "14": { + "key": " t", + "p5": "74", + "position": "214" + }, + "15": { + "key": " y", + "p5": "79", + "position": "215" + }, + "16": { + "key": " u", + "p5": "75", + "position": "216" + }, + "17": { + "key": " i", + "p5": "69", + "position": "217" + }, + "18": { + "key": " o", + "p5": "6F", + "position": "218" + }, + "19": { + "key": " p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "\u011f \u00a8", + "notes": "ok", + "p5": "11F", + "p9": "A8", + "position": "220" + }, + "1B": { + "key": "\u00fc ~", + "notes": "ok", + "p5": "0FC", + "p9": "7e", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": "\u015f \u00b4", + "notes": "ok", + "p5": "15f", + "p9": "B4", + "position": "319" + }, + "28": { + "key": "i", + "notes": "ok", + "p5": "69", + "position": "320" + }, + "29": { + "key": " \u00e9 \n \" ", + "p1": "E9", + "p7": "22", + "position": "100" + }, + "2A": { + "format": "left", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "; \n, `", + "p1": "3b", + "p7": "2c", + "p9": "60", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": "\u00f6", + "notes": "ok", + "p5": "0F6", + "position": "417" + }, + "34": { + "key": "\u00e7", + "notes": "ok", + "p5": "E7", + "position": "418" + }, + "35": { + "key": ": \n. ", + "p1": "3a", + "p7": "2e", + "position": "419" + }, + "36": { + "format": "right", + "key": "glyph_shift", + "label": "glyph_shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt\n", + "position": "501" + }, + "3B": { + "key": " glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": " glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": " glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": " glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": " glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": " glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": " glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": " glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": " glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": " glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "56": { + "key": "> \n< |", + "p1": "3e", + "p7": "3c", + "p9": "7c", + "position": "409" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "key": " ", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt gr", + "label": "alt gr", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "E" + }, + "uk": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! \n1 \u2013", + "p1": "21", + "p7": "31", + "p9": "2013", + "position": "110" + }, + "03": { + "key": "@ \n2 \"", + "p1": "40", + "p7": "32", + "p9": "22", + "position": "111" + }, + "04": { + "key": "\u2116 \n3 \u20ac", + "p1": "2116", + "p7": "33", + "p9": "20AC", + "position": "112" + }, + "05": { + "key": "; \n4 $", + "p1": "003B", + "p7": "34", + "p9": "24", + "position": "113" + }, + "06": { + "key": "% \n5 \u00ba", + "p1": "25", + "p7": "35", + "p9": "00BA", + "position": "114" + }, + "07": { + "key": ": \n6 ", + "p1": "003A", + "p7": "36", + "position": "115" + }, + "08": { + "key": "? \n7 ", + "p1": "003F", + "p7": "37", + "position": "116" + }, + "09": { + "key": "* \n8 \u00a7", + "p1": "002A", + "p7": "38", + "p9": "00A7", + "position": "117" + }, + "0A": { + "key": "( \n9 ", + "p1": "28", + "p7": "39", + "position": "118" + }, + "0B": { + "key": ") \n0 ", + "p1": "29", + "p7": "30", + "position": "119" + }, + "0C": { + "key": "_ \n- [", + "p1": "005F", + "p7": "002D", + "p9": "005B", + "position": "120" + }, + "0D": { + "key": "+ \n= ]", + "p1": "002B", + "p7": "003D", + "p9": "005D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q \n \u0439", + "p1": "71", + "p9": "439", + "position": "210" + }, + "11": { + "key": "w \n \u0446", + "p1": "77", + "p9": "446", + "position": "211" + }, + "12": { + "key": "e \n \u0443", + "p1": "65", + "p9": "443", + "position": "212" + }, + "13": { + "key": "r \n \u043a", + "p1": "72", + "p9": "043A", + "position": "213" + }, + "14": { + "key": "t \n \u0435", + "p1": "74", + "p9": "435", + "position": "214" + }, + "15": { + "key": "y \n \u043d", + "p1": "79", + "p9": "043D", + "position": "215" + }, + "16": { + "key": "u \n \u0433", + "p1": "75", + "p9": "433", + "position": "216" + }, + "17": { + "key": "i \n \u0448", + "p1": "69", + "p9": "448", + "position": "217" + }, + "18": { + "key": "o \n \u0449", + "p1": "006F", + "p9": "449", + "position": "218" + }, + "19": { + "key": "p \n \u0437", + "p1": "70", + "p9": "437", + "position": "219" + }, + "1A": { + "key": "{ \n\u0445 ", + "p1": "007B", + "p7": "445", + "position": "220" + }, + "1B": { + "key": "} \n\u044a \u0457", + "p1": "007D", + "p7": "044A", + "p9": "457", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a \n \u0444", + "p1": "61", + "p9": "444", + "position": "310" + }, + "1F": { + "key": "s \n\u044b \u0456", + "p1": "73", + "p7": "044B", + "p9": "456", + "position": "311" + }, + "20": { + "key": "d \n \u0432", + "p1": "64", + "p9": "432", + "position": "312" + }, + "21": { + "key": "f \n \u0430", + "p1": "66", + "p9": "430", + "position": "313" + }, + "22": { + "key": "g \n \u043f", + "p1": "67", + "p9": "043F", + "position": "314" + }, + "23": { + "key": "h \n \u0440", + "p1": "68", + "p9": "440", + "position": "315" + }, + "24": { + "key": "j \n \u043e", + "p1": "006A", + "p9": "043E", + "position": "316" + }, + "25": { + "key": "k \n \u043b", + "p1": "006B", + "p9": "043B", + "position": "317" + }, + "26": { + "key": "l \n \u0434", + "p1": "006C", + "p9": "434", + "position": "318" + }, + "27": { + "key": ": \n; \u0436", + "p1": "003A", + "p7": "003B", + "p9": "436", + "position": "319" + }, + "28": { + "key": "\" \u0454\n' \u044d", + "p1": "22", + "p3": "454", + "p7": "27", + "p9": "044D", + "position": "320" + }, + "29": { + "key": "\u0491 \n' \u20b4", + "p1": "491", + "p7": "27", + "p9": "20B4", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "\u00bb /\n\u00ab \\", + "p1": "00BB", + "p3": "002F", + "p7": "00AB", + "p9": "005C", + "position": "290" + }, + "2C": { + "key": "z \n \u044f", + "p1": "007A", + "p9": "044F", + "position": "410" + }, + "2D": { + "key": "x \n \u0447", + "p1": "78", + "p9": "447", + "position": "411" + }, + "2E": { + "key": "c \n \u0441", + "p1": "63", + "p9": "441", + "position": "412" + }, + "2F": { + "key": "v \n \u043c", + "p1": "76", + "p9": "043C", + "position": "413" + }, + "30": { + "key": "b \n \u0438", + "p1": "62", + "p9": "438", + "position": "414" + }, + "31": { + "key": "n \n \u0442", + "p1": "006E", + "p9": "442", + "position": "415" + }, + "32": { + "key": "m \n \u044c", + "p1": "006D", + "p9": "044C", + "position": "416" + }, + "33": { + "key": "< \n, \u0431", + "p1": "003C", + "p8": "002C", + "p9": "431", + "position": "417" + }, + "34": { + "key": "> \n. \u044e", + "p1": "003E", + "p8": "002E", + "p9": "044E", + "position": "418" + }, + "35": { + "key": "? ,\n/ .", + "p1": "003F", + "p3": "002C", + "p7": "002F", + "p9": "002E", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "vi": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "\u0103", + "p5": "103", + "position": "110" + }, + "03": { + "key": "\u00e2", + "p5": "E2", + "position": "111" + }, + "04": { + "key": "\u00ea", + "p5": "EA", + "position": "112" + }, + "05": { + "key": "\u00f4", + "p5": "F4", + "position": "113" + }, + "06": { + "key": "\u0300 \u0300", + "p2": "300", + "position": "114" + }, + "07": { + "key": "\u0309 \u0309", + "p2": "309", + "position": "115" + }, + "08": { + "key": "\u0303 \u0303", + "p2": "303", + "position": "116" + }, + "09": { + "key": "\u0301 \u0301", + "p2": "301", + "position": "117" + }, + "0A": { + "key": "\u0323 \u0323", + "p2": "323", + "position": "118" + }, + "0B": { + "key": "\u0111", + "p5": "111", + "position": "119" + }, + "0C": { + "key": "_\n-", + "p2": "5F", + "p8": "2D", + "position": "120" + }, + "0D": { + "key": "+\n\u20ab", + "p2": "2B", + "p8": "20AB", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "6F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "\u01b0", + "p5": "1B0", + "position": "220" + }, + "1B": { + "key": "\u01a1", + "p5": "1A1", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": ":\n;", + "p2": "3A", + "p8": "3B", + "position": "319" + }, + "28": { + "key": "\"\n'", + "p2": "22", + "p8": "27", + "position": "320" + }, + "29": { + "key": "~\n`", + "p2": "7E", + "p8": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": "<\n,", + "p2": "3C", + "p8": "2C", + "position": "417" + }, + "34": { + "key": ">\n.", + "p2": "3E", + "p8": "2E", + "position": "418" + }, + "35": { + "key": "?\n/", + "p2": "3F", + "p8": "2F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "37": { + "position": "409" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "73": { + "position": "420" + }, + "79": { + "format": "smaller", + "position": "551" + }, + "7B": { + "format": "smaller", + "position": "550" + }, + "7D": { + "key": "\u00a6\n\\", + "p2": "A6", + "p8": "5C", + "position": "122" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "zh_CN": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "!\n1", + "p2": "21", + "p8": "31", + "position": "110" + }, + "03": { + "key": "@\n2", + "p2": "40", + "p8": "32", + "position": "111" + }, + "04": { + "key": "#\n3", + "p2": "23", + "p8": "33", + "position": "112" + }, + "05": { + "key": "$\n4", + "p2": "24", + "p8": "34", + "position": "113" + }, + "06": { + "key": "%\n5", + "p2": "25", + "p8": "35", + "position": "114" + }, + "07": { + "key": "^\n6", + "p2": "005E", + "p8": "36", + "position": "115" + }, + "08": { + "key": "&\n7", + "p2": "26", + "p8": "37", + "position": "116" + }, + "09": { + "key": "*\n8", + "p2": "002A", + "p8": "38", + "position": "117" + }, + "0A": { + "key": "(\n9", + "p2": "28", + "p8": "39", + "position": "118" + }, + "0B": { + "key": ")\n0", + "p2": "29", + "p8": "30", + "position": "119" + }, + "0C": { + "key": "_\n-", + "p2": "005F", + "p8": "002D", + "position": "120" + }, + "0D": { + "key": "+\n=", + "p2": "002B", + "p8": "003D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q", + "p5": "71", + "position": "210" + }, + "11": { + "key": "w", + "p5": "77", + "position": "211" + }, + "12": { + "key": "e", + "p5": "65", + "position": "212" + }, + "13": { + "key": "r", + "p5": "72", + "position": "213" + }, + "14": { + "key": "t", + "p5": "74", + "position": "214" + }, + "15": { + "key": "y", + "p5": "79", + "position": "215" + }, + "16": { + "key": "u", + "p5": "75", + "position": "216" + }, + "17": { + "key": "i", + "p5": "69", + "position": "217" + }, + "18": { + "key": "o", + "p5": "6F", + "position": "218" + }, + "19": { + "key": "p", + "p5": "70", + "position": "219" + }, + "1A": { + "key": "{\n[", + "p2": "007B", + "p8": "005B", + "position": "220" + }, + "1B": { + "key": "}\n]", + "p2": "007D", + "p8": "005D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a", + "p5": "61", + "position": "310" + }, + "1F": { + "key": "s", + "p5": "73", + "position": "311" + }, + "20": { + "key": "d", + "p5": "64", + "position": "312" + }, + "21": { + "key": "f", + "p5": "66", + "position": "313" + }, + "22": { + "key": "g", + "p5": "67", + "position": "314" + }, + "23": { + "key": "h", + "p5": "68", + "position": "315" + }, + "24": { + "key": "j", + "p5": "6A", + "position": "316" + }, + "25": { + "key": "k", + "p5": "6B", + "position": "317" + }, + "26": { + "key": "l", + "p5": "6C", + "position": "318" + }, + "27": { + "key": ":\n;", + "p2": "003A", + "p8": "003B", + "position": "319" + }, + "28": { + "key": "\"\n'", + "p2": "22", + "p8": "27", + "position": "320" + }, + "29": { + "key": "~\n`", + "p2": "007E", + "p8": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "|\n\\", + "p2": "007C", + "p8": "005C", + "position": "290" + }, + "2C": { + "key": "z", + "p5": "7A", + "position": "410" + }, + "2D": { + "key": "x", + "p5": "78", + "position": "411" + }, + "2E": { + "key": "c", + "p5": "63", + "position": "412" + }, + "2F": { + "key": "v", + "p5": "76", + "position": "413" + }, + "30": { + "key": "b", + "p5": "62", + "position": "414" + }, + "31": { + "key": "n", + "p5": "6E", + "position": "415" + }, + "32": { + "key": "m", + "p5": "6D", + "position": "416" + }, + "33": { + "key": "<\n,", + "p2": "003C", + "p8": "002C", + "position": "417" + }, + "34": { + "key": ">\n.", + "p2": "003E", + "p8": "002E", + "position": "418" + }, + "35": { + "key": "?\n/", + "p2": "003F", + "p8": "002F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + }, + "zh_TW": { + "keys": { + "00": { + "key": "glyph_power", + "label": "glyph_power", + "notes": "power", + "position": "999" + }, + "01": { + "format": "smaller", + "key": "esc", + "label": "esc", + "notes": "escape", + "position": "0" + }, + "02": { + "key": "! \u3105\n1 ", + "p1": "21", + "p3": "3105", + "p7": "31", + "position": "110" + }, + "03": { + "key": "@ \u3109\n2 ", + "p1": "40", + "p3": "3109", + "p7": "32", + "position": "111" + }, + "04": { + "key": "# \u02c7\n3 ", + "p1": "23", + "p3": "02C7", + "p7": "33", + "position": "112" + }, + "05": { + "key": "$ \u02cb\n4 ", + "p1": "24", + "p3": "02CB", + "p7": "34", + "position": "113" + }, + "06": { + "key": "% \u3113\n5 ", + "p1": "25", + "p3": "3113", + "p7": "35", + "position": "114" + }, + "07": { + "key": "^ \u02ca\n6 ", + "p1": "005E", + "p3": "02CA", + "p7": "36", + "position": "115" + }, + "08": { + "key": "& \u02d9\n7 ", + "p1": "26", + "p3": "02D9", + "p7": "37", + "position": "116" + }, + "09": { + "key": "* \u311a\n8 ", + "p1": "002A", + "p3": "311A", + "p7": "38", + "position": "117" + }, + "0A": { + "key": "( \u311e\n9 ", + "p1": "28", + "p3": "311E", + "p7": "39", + "position": "118" + }, + "0B": { + "key": ") \u3122\n0 ", + "p1": "29", + "p3": "3122", + "p7": "30", + "position": "119" + }, + "0C": { + "key": "_ \u3126\n- ", + "p1": "005F", + "p3": "3126", + "p7": "002D", + "position": "120" + }, + "0D": { + "key": "+ \n= ", + "p1": "002B", + "p7": "003D", + "position": "121" + }, + "0E": { + "format": "right", + "key": "backspace", + "label": "backspace", + "notes": "backspace", + "position": "190" + }, + "0F": { + "format": "left", + "key": "tab", + "label": "tab", + "notes": "tab", + "position": "200" + }, + "10": { + "key": "q \u3106\n\u624b ", + "p1": "71", + "p3": "3106", + "p7": "624B", + "position": "210" + }, + "11": { + "key": "w \u310a\n\u7530 ", + "p1": "77", + "p3": "310A", + "p7": "7530", + "position": "211" + }, + "12": { + "key": "e \u310d\n\u6c34 ", + "p1": "65", + "p3": "310D", + "p7": "6C34", + "position": "212" + }, + "13": { + "key": "r \u3110\n\u53e3 ", + "notes": "Unicode not accepted by the cell. The correct Unicode is 53E3. 53000 is not correct - rainlau\nI clicked on cell, then clicked on 123 in Toolbar and selected Plain Text. Can't verify that character is correct, but it is now displaying in column M and in go/crux-keyboard -ginsberg", + "p1": "72", + "p3": "3110", + "p7": "53E3", + "position": "213" + }, + "14": { + "key": "t \u3114\n\u5eff ", + "p1": "74", + "p3": "3114", + "p7": "5EFF", + "position": "214" + }, + "15": { + "key": "y \u3117\n\u535c ", + "p1": "79", + "p3": "3117", + "p7": "535C", + "position": "215" + }, + "16": { + "key": "u \u3127\n\u5c71 ", + "p1": "75", + "p3": "3127", + "p7": "5C71", + "position": "216" + }, + "17": { + "key": "i \u311b\n\u6208 ", + "p1": "69", + "p3": "311B", + "p7": "6208", + "position": "217" + }, + "18": { + "key": "o \u311f\n\u4eba ", + "p1": "6F", + "p3": "311F", + "p7": "4EBA", + "position": "218" + }, + "19": { + "key": "p \u3123\n\u5fc3 ", + "p1": "70", + "p3": "3123", + "p7": "5FC3", + "position": "219" + }, + "1A": { + "key": "{ \n[ ", + "p1": "007B", + "p7": "005B", + "position": "220" + }, + "1B": { + "key": "} \n] ", + "p1": "007D", + "p7": "005D", + "position": "221" + }, + "1C": { + "format": "right", + "key": "enter", + "label": "enter", + "notes": "enter", + "position": "390" + }, + "1D": { + "format": "left", + "key": "ctrl", + "label": "ctrl", + "notes": "left ctrl", + "position": "500" + }, + "1E": { + "key": "a \u3107\n\u65e5 ", + "notes": "Unicode not accepted by the cell. The correct Unicode is 65E5. 65000 is not correct - rainlau", + "p1": "61", + "p3": "3107", + "p7": "65E5", + "position": "310" + }, + "1F": { + "key": "s \u310b\n\u5c38 ", + "p1": "73", + "p3": "310B", + "p7": "5C38", + "position": "311" + }, + "20": { + "key": "d \u310e\n\u6728 ", + "p1": "64", + "p3": "310E", + "p7": "6728", + "position": "312" + }, + "21": { + "key": "f \u3111\n\u706b ", + "p1": "66", + "p3": "3111", + "p7": "706B", + "position": "313" + }, + "22": { + "key": "g \u3115\n\u571f ", + "p1": "67", + "p3": "3115", + "p7": "571F", + "position": "314" + }, + "23": { + "key": "h \u3118\n\u7af9 ", + "p1": "68", + "p3": "3118", + "p7": "7AF9", + "position": "315" + }, + "24": { + "key": "j \u3128\n\u5341 ", + "p1": "6A", + "p3": "3128", + "p7": "5341", + "position": "316" + }, + "25": { + "key": "k \u311c\n\u5927 ", + "p1": "6B", + "p3": "311C", + "p7": "5927", + "position": "317" + }, + "26": { + "key": "l \u3120\n\u4e2d ", + "p1": "6C", + "p3": "3120", + "p7": "4E2D", + "position": "318" + }, + "27": { + "key": ": \u3124\n; ", + "p1": "003A", + "p3": "3124", + "p7": "003B", + "position": "319" + }, + "28": { + "key": "\" \n' ", + "p1": "22", + "p7": "27", + "position": "320" + }, + "29": { + "key": "~ \n` ", + "p1": "007E", + "p7": "60", + "position": "100" + }, + "2A": { + "format": "left", + "key": "shift", + "label": "shift", + "notes": "left shift", + "position": "400" + }, + "2B": { + "key": "| \n\\ ", + "p1": "007C", + "p7": "005C", + "position": "290" + }, + "2C": { + "key": "z \u3108\n\u91cd ", + "p1": "7A", + "p3": "3108", + "p7": "91CD", + "position": "410" + }, + "2D": { + "key": "x \u310c\n\u96e3 ", + "notes": "Unicode not accepted by the cell. The correct Unicode is 96E3 - rainlau\nDisplays in Column M and go/crux-keyboard, but the value I59 changes to \"96000\" - ginsberg\n\n\"96000\" is not the right one, it has to be able to displayed as 96E3 to be correct - rainlau\n\nI clicked on cell, then clicked on 123 in Toolbar and selected Plain Text. Can't verify that character is correct, but it is now displaying in column M and in go/crux-keyboard -ginsberg", + "p1": "78", + "p3": "310C", + "p7": "96E3", + "position": "411" + }, + "2E": { + "key": "c \u310f\n\u91d1 ", + "p1": "63", + "p3": "310F", + "p7": "91D1", + "position": "412" + }, + "2F": { + "key": "v \u3112\n\u5973 ", + "p1": "76", + "p3": "3112", + "p7": "5973", + "position": "413" + }, + "30": { + "key": "b \u3116\n\u6708 ", + "p1": "62", + "p3": "3116", + "p7": "6708", + "position": "414" + }, + "31": { + "key": "n \u3119\n\u5f13 ", + "p1": "6E", + "p3": "3119", + "p7": "5F13", + "position": "415" + }, + "32": { + "key": "m \u3129\n\u4e00 ", + "notes": "Displays in Column M and go/crux-keyboard", + "p1": "6D", + "p3": "3129", + "p7": "4E00", + "position": "416" + }, + "33": { + "key": "< \u311d\n, ", + "p1": "003C", + "p3": "311D", + "p7": "002C", + "position": "417" + }, + "34": { + "key": "> \u3121\n. ", + "p1": "003E", + "p3": "3121", + "p7": "002E", + "position": "418" + }, + "35": { + "key": "? \u3125\n/ ", + "p1": "003F", + "p3": "3125", + "p7": "002F", + "position": "419" + }, + "36": { + "format": "right", + "key": "shift", + "label": "shift", + "notes": "right shift", + "position": "490" + }, + "38": { + "format": "left", + "key": "alt", + "label": "alt", + "notes": "left alt", + "position": "501" + }, + "3B": { + "key": "glyph_back", + "label": "glyph_back", + "position": "10" + }, + "3C": { + "key": "glyph_forward", + "label": "glyph_forward", + "position": "11" + }, + "3D": { + "key": "glyph_overview", + "label": "glyph_overview", + "position": "12" + }, + "3E": { + "key": "glyph_fullscreen", + "label": "glyph_fullscreen", + "position": "13" + }, + "3F": { + "key": "glyph_tools", + "label": "glyph_tools", + "position": "14" + }, + "40": { + "key": "glyph_brightness_down", + "label": "glyph_brightness_down", + "position": "15" + }, + "41": { + "key": "glyph_brightness_up", + "label": "glyph_brightness_up", + "position": "16" + }, + "42": { + "key": "glyph_volume_mute", + "label": "glyph_volume_mute", + "position": "17" + }, + "43": { + "key": "glyph_volume_down", + "label": "glyph_volume_down", + "position": "18" + }, + "44": { + "key": "glyph_volume_up", + "label": "glyph_volume_up", + "position": "19" + }, + "E0 1D": { + "format": "smaller", + "key": "ctrl", + "label": "ctrl", + "notes": "right ctrl", + "position": "581" + }, + "E0 38": { + "format": "smaller", + "key": "alt", + "label": "alt", + "notes": "right alt", + "position": "580" + }, + "E0 48": { + "key": "glyph_arrow_up", + "label": "glyph_arrow_up", + "position": "590" + }, + "E0 4B": { + "key": "glyph_arrow_left", + "label": "glyph_arrow_left", + "position": "591" + }, + "E0 4D": { + "key": "glyph_arrow_right", + "label": "glyph_arrow_right", + "position": "593" + }, + "E0 50": { + "key": "glyph_arrow_down", + "label": "glyph_arrow_down", + "position": "592" + }, + "E0 5B": { + "format": "left", + "key": "glyph_search", + "label": "glyph_search", + "notes": "search", + "position": "300" + } + }, + "layoutName": "U" + } + }, + "layouts": { + "B": [ + [ + "", + 5.0, + 167.0, + 105.0, + 60.0 + ], + [ + "43", + 648.0, + 6.0, + 60.0, + 35.0 + ], + [ + "44", + 708.0, + 6.0, + 60.0, + 35.0 + ], + [ + "42", + 588.0, + 6.0, + 60.0, + 35.0 + ], + [ + "41", + 510.0, + 6.0, + 60.0, + 35.0 + ], + [ + "40", + 450.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3F", + 372.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3D", + 234.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3E", + 312.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3C", + 174.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3B", + 114.0, + 6.0, + 60.0, + 35.0 + ], + [ + "00", + 786.0, + 6.0, + 90.0, + 35.0 + ], + [ + "01", + 6.0, + 6.0, + 89.0, + 35.0 + ], + [ + "", + 815.0, + 107.0, + 30.0, + 60.0 + ], + [ + "73", + 740.0, + 227.0, + 60.0, + 60.0 + ], + [ + "56", + 80.0, + 227.0, + 60.0, + 60.0 + ], + [ + "39", + 245.0, + 287.0, + 330.0, + 60.0 + ], + [ + "0E", + 785.0, + 47.0, + 90.0, + 60.0 + ], + [ + "1C", + 830.0, + 107.0, + 45.0, + 120.0 + ], + [ + "36", + 800.0, + 227.0, + 75.0, + 60.0 + ], + [ + "0F", + 5.0, + 107.0, + 90.0, + 60.0 + ], + [ + "2A", + 5.0, + 227.0, + 75.0, + 60.0 + ], + [ + "E0 50", + 755.0, + 318.0, + 60.0, + 29.0 + ], + [ + "E0 48", + 755.0, + 287.0, + 60.0, + 31.0 + ], + [ + "E0 4D", + 815.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 4B", + 695.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 5B", + 5.0, + 167.0, + 90.0, + 60.0 + ], + [ + "38", + 125.0, + 287.0, + 120.0, + 60.0 + ], + [ + "1D", + 5.0, + 287.0, + 120.0, + 60.0 + ], + [ + "E0 1D", + 635.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 38", + 575.0, + 287.0, + 60.0, + 60.0 + ], + [ + "2B", + 770.0, + 167.0, + 60.0, + 60.0 + ], + [ + "0D", + 725.0, + 47.0, + 60.0, + 60.0 + ], + [ + "0C", + 665.0, + 47.0, + 60.0, + 60.0 + ], + [ + "0B", + 605.0, + 47.0, + 60.0, + 60.0 + ], + [ + "0A", + 545.0, + 47.0, + 60.0, + 60.0 + ], + [ + "09", + 485.0, + 47.0, + 60.0, + 60.0 + ], + [ + "08", + 425.0, + 47.0, + 60.0, + 60.0 + ], + [ + "07", + 365.0, + 47.0, + 60.0, + 60.0 + ], + [ + "06", + 305.0, + 47.0, + 60.0, + 60.0 + ], + [ + "05", + 245.0, + 47.0, + 60.0, + 60.0 + ], + [ + "04", + 185.0, + 47.0, + 60.0, + 60.0 + ], + [ + "03", + 125.0, + 47.0, + 60.0, + 60.0 + ], + [ + "02", + 65.0, + 47.0, + 60.0, + 60.0 + ], + [ + "29", + 5.0, + 47.0, + 60.0, + 60.0 + ], + [ + "35", + 680.0, + 227.0, + 60.0, + 60.0 + ], + [ + "34", + 620.0, + 227.0, + 60.0, + 60.0 + ], + [ + "33", + 560.0, + 227.0, + 60.0, + 60.0 + ], + [ + "32", + 500.0, + 227.0, + 60.0, + 60.0 + ], + [ + "31", + 440.0, + 227.0, + 60.0, + 60.0 + ], + [ + "30", + 380.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2F", + 320.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2E", + 260.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2D", + 200.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2C", + 140.0, + 227.0, + 60.0, + 60.0 + ], + [ + "28", + 710.0, + 167.0, + 60.0, + 60.0 + ], + [ + "27", + 650.0, + 167.0, + 60.0, + 60.0 + ], + [ + "26", + 590.0, + 167.0, + 60.0, + 60.0 + ], + [ + "25", + 530.0, + 167.0, + 60.0, + 60.0 + ], + [ + "24", + 470.0, + 167.0, + 60.0, + 60.0 + ], + [ + "23", + 410.0, + 167.0, + 60.0, + 60.0 + ], + [ + "22", + 350.0, + 167.0, + 60.0, + 60.0 + ], + [ + "21", + 290.0, + 167.0, + 60.0, + 60.0 + ], + [ + "20", + 230.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1F", + 170.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1E", + 110.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1B", + 755.0, + 107.0, + 60.0, + 60.0 + ], + [ + "1A", + 695.0, + 107.0, + 60.0, + 60.0 + ], + [ + "19", + 635.0, + 107.0, + 60.0, + 60.0 + ], + [ + "18", + 575.0, + 107.0, + 60.0, + 60.0 + ], + [ + "17", + 515.0, + 107.0, + 60.0, + 60.0 + ], + [ + "16", + 455.0, + 107.0, + 60.0, + 60.0 + ], + [ + "15", + 395.0, + 107.0, + 60.0, + 60.0 + ], + [ + "14", + 335.0, + 107.0, + 60.0, + 60.0 + ], + [ + "13", + 275.0, + 107.0, + 60.0, + 60.0 + ], + [ + "12", + 215.0, + 107.0, + 60.0, + 60.0 + ], + [ + "11", + 155.0, + 107.0, + 60.0, + 60.0 + ], + [ + "10", + 95.0, + 107.0, + 60.0, + 60.0 + ] + ], + "E": [ + [ + "", + 5.0, + 167.0, + 105.0, + 60.0 + ], + [ + "43", + 648.0, + 6.0, + 60.0, + 35.0 + ], + [ + "44", + 708.0, + 6.0, + 60.0, + 35.0 + ], + [ + "42", + 588.0, + 6.0, + 60.0, + 35.0 + ], + [ + "41", + 510.0, + 6.0, + 60.0, + 35.0 + ], + [ + "40", + 450.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3F", + 372.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3D", + 234.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3E", + 312.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3C", + 174.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3B", + 114.0, + 6.0, + 60.0, + 35.0 + ], + [ + "00", + 786.0, + 6.0, + 90.0, + 35.0 + ], + [ + "01", + 6.0, + 6.0, + 89.0, + 35.0 + ], + [ + "", + 815.0, + 107.0, + 30.0, + 60.0 + ], + [ + "56", + 80.0, + 227.0, + 60.0, + 60.0 + ], + [ + "39", + 245.0, + 287.0, + 330.0, + 60.0 + ], + [ + "0E", + 785.0, + 47.0, + 90.0, + 60.0 + ], + [ + "1C", + 830.0, + 107.0, + 45.0, + 120.0 + ], + [ + "36", + 740.0, + 227.0, + 135.0, + 60.0 + ], + [ + "0F", + 5.0, + 107.0, + 90.0, + 60.0 + ], + [ + "2A", + 5.0, + 227.0, + 75.0, + 60.0 + ], + [ + "E0 50", + 755.0, + 318.0, + 60.0, + 29.0 + ], + [ + "E0 48", + 755.0, + 287.0, + 60.0, + 31.0 + ], + [ + "E0 4D", + 815.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 4B", + 695.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 5B", + 5.0, + 167.0, + 90.0, + 60.0 + ], + [ + "38", + 125.0, + 287.0, + 120.0, + 60.0 + ], + [ + "1D", + 5.0, + 287.0, + 120.0, + 60.0 + ], + [ + "E0 1D", + 635.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 38", + 575.0, + 287.0, + 60.0, + 60.0 + ], + [ + "2B", + 770.5, + 167.0, + 60.0, + 60.0 + ], + [ + "0D", + 725.0, + 47.0, + 60.0, + 60.0 + ], + [ + "0C", + 665.0, + 47.0, + 60.0, + 60.0 + ], + [ + "0B", + 605.0, + 47.0, + 60.0, + 60.0 + ], + [ + "0A", + 545.0, + 47.0, + 60.0, + 60.0 + ], + [ + "09", + 485.0, + 47.0, + 60.0, + 60.0 + ], + [ + "08", + 425.0, + 47.0, + 60.0, + 60.0 + ], + [ + "07", + 365.0, + 47.0, + 60.0, + 60.0 + ], + [ + "06", + 305.0, + 47.0, + 60.0, + 60.0 + ], + [ + "05", + 245.0, + 47.0, + 60.0, + 60.0 + ], + [ + "04", + 185.0, + 47.0, + 60.0, + 60.0 + ], + [ + "03", + 125.0, + 47.0, + 60.0, + 60.0 + ], + [ + "02", + 65.0, + 47.0, + 60.0, + 60.0 + ], + [ + "29", + 5.0, + 47.0, + 60.0, + 60.0 + ], + [ + "35", + 680.0, + 227.0, + 60.0, + 60.0 + ], + [ + "34", + 620.0, + 227.0, + 60.0, + 60.0 + ], + [ + "33", + 560.0, + 227.0, + 60.0, + 60.0 + ], + [ + "32", + 500.0, + 227.0, + 60.0, + 60.0 + ], + [ + "31", + 440.0, + 227.0, + 60.0, + 60.0 + ], + [ + "30", + 380.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2F", + 320.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2E", + 260.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2D", + 200.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2C", + 140.0, + 227.0, + 60.0, + 60.0 + ], + [ + "28", + 710.0, + 167.0, + 60.0, + 60.0 + ], + [ + "27", + 650.0, + 167.0, + 60.0, + 60.0 + ], + [ + "26", + 590.0, + 167.0, + 60.0, + 60.0 + ], + [ + "25", + 530.0, + 167.0, + 60.0, + 60.0 + ], + [ + "24", + 470.0, + 167.0, + 60.0, + 60.0 + ], + [ + "23", + 410.0, + 167.0, + 60.0, + 60.0 + ], + [ + "22", + 350.0, + 167.0, + 60.0, + 60.0 + ], + [ + "21", + 290.0, + 167.0, + 60.0, + 60.0 + ], + [ + "20", + 230.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1F", + 170.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1E", + 110.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1B", + 755.0, + 107.0, + 60.0, + 60.0 + ], + [ + "1A", + 695.0, + 107.0, + 60.0, + 60.0 + ], + [ + "19", + 635.0, + 107.0, + 60.0, + 60.0 + ], + [ + "18", + 575.0, + 107.0, + 60.0, + 60.0 + ], + [ + "17", + 515.0, + 107.0, + 60.0, + 60.0 + ], + [ + "16", + 455.0, + 107.0, + 60.0, + 60.0 + ], + [ + "15", + 395.0, + 107.0, + 60.0, + 60.0 + ], + [ + "14", + 335.0, + 107.0, + 60.0, + 60.0 + ], + [ + "13", + 275.0, + 107.0, + 60.0, + 60.0 + ], + [ + "12", + 215.0, + 107.0, + 60.0, + 60.0 + ], + [ + "11", + 155.0, + 107.0, + 60.0, + 60.0 + ], + [ + "10", + 95.0, + 107.0, + 60.0, + 60.0 + ] + ], + "J": [ + [ + "", + 5.0, + 167.0, + 90.0, + 60.0 + ], + [ + "43", + 648.0, + 6.0, + 60.0, + 35.0 + ], + [ + "44", + 708.0, + 6.0, + 60.0, + 35.0 + ], + [ + "42", + 588.0, + 6.0, + 60.0, + 35.0 + ], + [ + "41", + 510.0, + 6.0, + 60.0, + 35.0 + ], + [ + "40", + 450.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3F", + 372.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3D", + 234.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3E", + 312.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3C", + 174.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3B", + 114.0, + 6.0, + 60.0, + 35.0 + ], + [ + "00", + 786.0, + 6.0, + 90.0, + 35.0 + ], + [ + "01", + 6.0, + 6.0, + 89.0, + 35.0 + ], + [ + "", + 800.0, + 107.0, + 30.0, + 60.0 + ], + [ + "73", + 725.0, + 227.0, + 60.0, + 60.0 + ], + [ + "39", + 260.0, + 287.0, + 240.0, + 60.0 + ], + [ + "79", + 500.0, + 287.0, + 75.0, + 60.0 + ], + [ + "7B", + 185.0, + 287.0, + 75.0, + 60.0 + ], + [ + "7D", + 759.0, + 47.0, + 56.0, + 60.0 + ], + [ + "0E", + 815.0, + 47.0, + 60.0, + 60.0 + ], + [ + "1C", + 815.0, + 107.0, + 60.0, + 120.0 + ], + [ + "36", + 785.0, + 227.0, + 90.0, + 60.0 + ], + [ + "0F", + 5.0, + 107.0, + 75.0, + 60.0 + ], + [ + "2A", + 5.0, + 227.0, + 120.0, + 60.0 + ], + [ + "E0 50", + 755.0, + 318.0, + 60.0, + 29.0 + ], + [ + "E0 48", + 755.0, + 287.0, + 60.0, + 31.0 + ], + [ + "E0 4D", + 815.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 4B", + 695.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 5B", + 5.0, + 167.0, + 75.0, + 60.0 + ], + [ + "38", + 95.0, + 287.0, + 90.0, + 60.0 + ], + [ + "1D", + 5.0, + 287.0, + 90.0, + 60.0 + ], + [ + "E0 1D", + 635.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 38", + 575.0, + 287.0, + 60.0, + 60.0 + ], + [ + "2B", + 755.0, + 167.0, + 60.0, + 60.0 + ], + [ + "0D", + 701.0, + 47.0, + 58.0, + 60.0 + ], + [ + "0C", + 643.0, + 47.0, + 58.0, + 60.0 + ], + [ + "0B", + 585.0, + 47.0, + 58.0, + 60.0 + ], + [ + "0A", + 527.0, + 47.0, + 58.0, + 60.0 + ], + [ + "09", + 469.0, + 47.0, + 58.0, + 60.0 + ], + [ + "08", + 411.0, + 47.0, + 58.0, + 60.0 + ], + [ + "07", + 353.0, + 47.0, + 58.0, + 60.0 + ], + [ + "06", + 295.0, + 47.0, + 58.0, + 60.0 + ], + [ + "05", + 237.0, + 47.0, + 58.0, + 60.0 + ], + [ + "04", + 179.0, + 47.0, + 58.0, + 60.0 + ], + [ + "03", + 121.0, + 47.0, + 58.0, + 60.0 + ], + [ + "02", + 63.0, + 47.0, + 58.0, + 60.0 + ], + [ + "29", + 5.0, + 47.0, + 58.0, + 60.0 + ], + [ + "35", + 665.0, + 227.0, + 60.0, + 60.0 + ], + [ + "34", + 605.0, + 227.0, + 60.0, + 60.0 + ], + [ + "33", + 545.0, + 227.0, + 60.0, + 60.0 + ], + [ + "32", + 485.0, + 227.0, + 60.0, + 60.0 + ], + [ + "31", + 425.0, + 227.0, + 60.0, + 60.0 + ], + [ + "30", + 365.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2F", + 305.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2E", + 245.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2D", + 185.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2C", + 125.0, + 227.0, + 60.0, + 60.0 + ], + [ + "28", + 695.0, + 167.0, + 60.0, + 60.0 + ], + [ + "27", + 635.0, + 167.0, + 60.0, + 60.0 + ], + [ + "26", + 575.0, + 167.0, + 60.0, + 60.0 + ], + [ + "25", + 515.0, + 167.0, + 60.0, + 60.0 + ], + [ + "24", + 455.0, + 167.0, + 60.0, + 60.0 + ], + [ + "23", + 395.0, + 167.0, + 60.0, + 60.0 + ], + [ + "22", + 335.0, + 167.0, + 60.0, + 60.0 + ], + [ + "21", + 275.0, + 167.0, + 60.0, + 60.0 + ], + [ + "20", + 215.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1F", + 155.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1E", + 95.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1B", + 740.0, + 107.0, + 60.0, + 60.0 + ], + [ + "1A", + 680.0, + 107.0, + 60.0, + 60.0 + ], + [ + "19", + 620.0, + 107.0, + 60.0, + 60.0 + ], + [ + "18", + 560.0, + 107.0, + 60.0, + 60.0 + ], + [ + "17", + 500.0, + 107.0, + 60.0, + 60.0 + ], + [ + "16", + 440.0, + 107.0, + 60.0, + 60.0 + ], + [ + "15", + 380.0, + 107.0, + 60.0, + 60.0 + ], + [ + "14", + 320.0, + 107.0, + 60.0, + 60.0 + ], + [ + "13", + 260.0, + 107.0, + 60.0, + 60.0 + ], + [ + "12", + 200.0, + 107.0, + 60.0, + 60.0 + ], + [ + "11", + 140.0, + 107.0, + 60.0, + 60.0 + ], + [ + "10", + 80.0, + 107.0, + 60.0, + 60.0 + ] + ], + "U": [ + [ + "", + 5.0, + 167.0, + 105.0, + 60.0 + ], + [ + "39", + 245.0, + 287.0, + 330.0, + 60.0 + ], + [ + "43", + 648.0, + 6.0, + 60.0, + 35.0 + ], + [ + "44", + 708.0, + 6.0, + 60.0, + 35.0 + ], + [ + "42", + 588.0, + 6.0, + 60.0, + 35.0 + ], + [ + "41", + 510.0, + 6.0, + 60.0, + 35.0 + ], + [ + "40", + 450.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3F", + 372.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3D", + 234.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3E", + 312.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3C", + 174.0, + 6.0, + 60.0, + 35.0 + ], + [ + "3B", + 114.0, + 6.0, + 60.0, + 35.0 + ], + [ + "00", + 786.0, + 6.0, + 90.0, + 35.0 + ], + [ + "01", + 6.0, + 6.0, + 89.0, + 35.0 + ], + [ + "0E", + 785.0, + 47.0, + 90.0, + 60.0 + ], + [ + "1C", + 770.0, + 167.0, + 105.0, + 60.0 + ], + [ + "36", + 740.0, + 227.0, + 135.0, + 60.0 + ], + [ + "0F", + 5.0, + 107.0, + 90.0, + 60.0 + ], + [ + "2A", + 5.0, + 227.0, + 135.0, + 60.0 + ], + [ + "E0 50", + 755.0, + 318.0, + 60.0, + 29.0 + ], + [ + "E0 48", + 755.0, + 287.0, + 60.0, + 31.0 + ], + [ + "E0 4D", + 815.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 4B", + 695.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 5B", + 5.0, + 167.0, + 90.0, + 60.0 + ], + [ + "38", + 125.0, + 287.0, + 120.0, + 60.0 + ], + [ + "1D", + 5.0, + 287.0, + 120.0, + 60.0 + ], + [ + "E0 1D", + 635.0, + 287.0, + 60.0, + 60.0 + ], + [ + "E0 38", + 575.0, + 287.0, + 60.0, + 60.0 + ], + [ + "2B", + 815.0, + 107.0, + 60.0, + 60.0 + ], + [ + "0D", + 725.0, + 47.0, + 60.0, + 60.0 + ], + [ + "0C", + 665.0, + 47.0, + 60.0, + 60.0 + ], + [ + "0B", + 605.0, + 47.0, + 60.0, + 60.0 + ], + [ + "0A", + 545.0, + 47.0, + 60.0, + 60.0 + ], + [ + "09", + 485.0, + 47.0, + 60.0, + 60.0 + ], + [ + "08", + 425.5, + 47.0, + 60.0, + 60.0 + ], + [ + "07", + 365.0, + 47.0, + 60.0, + 60.0 + ], + [ + "06", + 305.0, + 47.0, + 60.0, + 60.0 + ], + [ + "05", + 245.0, + 47.0, + 60.0, + 60.0 + ], + [ + "04", + 185.0, + 47.0, + 60.0, + 60.0 + ], + [ + "03", + 125.0, + 47.0, + 60.0, + 60.0 + ], + [ + "02", + 65.0, + 47.0, + 60.0, + 60.0 + ], + [ + "29", + 5.0, + 47.0, + 60.0, + 60.0 + ], + [ + "35", + 680.0, + 227.0, + 60.0, + 60.0 + ], + [ + "34", + 620.0, + 227.0, + 60.0, + 60.0 + ], + [ + "33", + 560.0, + 227.0, + 60.0, + 60.0 + ], + [ + "32", + 500.0, + 227.0, + 60.0, + 60.0 + ], + [ + "31", + 440.0, + 227.0, + 60.0, + 60.0 + ], + [ + "30", + 380.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2F", + 320.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2E", + 260.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2D", + 200.0, + 227.0, + 60.0, + 60.0 + ], + [ + "2C", + 140.0, + 227.0, + 60.0, + 60.0 + ], + [ + "28", + 710.0, + 167.0, + 60.0, + 60.0 + ], + [ + "27", + 650.0, + 167.0, + 60.0, + 60.0 + ], + [ + "26", + 590.0, + 167.0, + 60.0, + 60.0 + ], + [ + "25", + 530.0, + 167.0, + 60.0, + 60.0 + ], + [ + "24", + 470.0, + 167.0, + 60.0, + 60.0 + ], + [ + "23", + 410.0, + 167.0, + 60.0, + 60.0 + ], + [ + "22", + 350.0, + 167.0, + 60.0, + 60.0 + ], + [ + "21", + 290.0, + 167.0, + 60.0, + 60.0 + ], + [ + "20", + 230.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1F", + 170.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1E", + 110.0, + 167.0, + 60.0, + 60.0 + ], + [ + "1B", + 755.0, + 107.0, + 60.0, + 60.0 + ], + [ + "1A", + 695.0, + 107.0, + 60.0, + 60.0 + ], + [ + "19", + 635.0, + 107.0, + 60.0, + 60.0 + ], + [ + "18", + 575.0, + 107.0, + 60.0, + 60.0 + ], + [ + "17", + 515.0, + 107.0, + 60.0, + 60.0 + ], + [ + "16", + 455.0, + 107.0, + 60.0, + 60.0 + ], + [ + "15", + 395.0, + 107.0, + 60.0, + 60.0 + ], + [ + "14", + 335.0, + 107.0, + 60.0, + 60.0 + ], + [ + "13", + 275.0, + 107.0, + 60.0, + 60.0 + ], + [ + "12", + 215.0, + 107.0, + 60.0, + 60.0 + ], + [ + "11", + 155.0, + 107.0, + 60.0, + 60.0 + ], + [ + "10", + 95.0, + 107.0, + 60.0, + 60.0 + ] + ] + }, + "shortcut": { + "+ CTRL": "keyboardOverlayZoomIn", + ", CTRL": "keyboardOverlaySettings", + "- CTRL": "keyboardOverlayZoomOut", + "/ ALT CTRL": "keyboardOverlayViewKeyboardOverlay", + "/ CTRL": "keyboardOverlayHelp", + "0 CTRL": "keyboardOverlayResetZoom", + "1 CTRL": "keyboardOverlayActivateTab1", + "2 CTRL": "keyboardOverlayActivateTab2", + "3 CTRL": "keyboardOverlayActivateTab3", + "4 CTRL": "keyboardOverlayActivateTab4", + "5 CTRL": "keyboardOverlayActivateTab5", + "6 CTRL": "keyboardOverlayActivateTab6", + "7 CTRL": "keyboardOverlayActivateTab7", + "8 CTRL": "keyboardOverlayActivateTab8", + "9 CTRL": "keyboardOverlayActivateLastTab", + "a CTRL": "keyboardOverlaySelectAll", + "alt SHIFT": "keyboardOverlayCycleThroughInputMethods", + "arrowkeys CTRL": "keyboardOverlayWordMove", + "b CTRL SHIFT": "keyboardOverlayToggleBookmarkBar", + "backspace CTRL": "keyboardOverlayDeleteWord", + "backspace SHIFT": "keyboardOverlayForward", + "c CTRL": "keyboardOverlayCopy", + "d ALT": "keyboardOverlayFocusAddressBar", + "d CTRL": "keyboardOverlayBookmarkCurrentPage", + "d CTRL SHIFT": "keyboardOverlayBookmarkAllTabs", + "down ALT": "keyboardOverlayPageDown", + "down ALT CTRL": "keyboardOverlayEnd", + "e CTRL": "keyboardOverlayFocusAddressBarInSearchMode", + "enter ALT": "keyboardOverlayOpenAddressInNewTab", + "enter CTRL": "keyboardOverlayAddWwwAndComAndOpenAddress", + "enter SHIFT": "keyboardOverlayFindPrevious", + "esc SHIFT": "keyboardOverlayTaskManager", + "f ALT CTRL": "keyboardOverlayFullScreen", + "f CTRL": "keyboardOverlayFindText", + "fullscreen CTRL": "keyboardOverlayTakeScreenshot", + "g CTRL": "keyboardOverlayFindAgain", + "g CTRL SHIFT": "keyboardOverlayFindPrevious", + "h CTRL": "keyboardOverlayHistory", + "i CTRL SHIFT": "keyboardOverlayDeveloperTools", + "j CTRL": "keyboardOverlayDownloads", + "j CTRL SHIFT": "keyboardOverlayDomInspector", + "k CTRL": "keyboardOverlayFocusAddressBarInSearchMode", + "l CTRL": "keyboardOverlayFocusAddressBar", + "left ALT": "keyboardOverlayBack", + "left/right CTRL SHIFT": "keyboardOverlaySelectWordAtATime", + "m ALT CTRL": "keyboardOverlayUseExternalMonitor", + "n CTRL": "keyboardOverlayNewWindow", + "n CTRL SHIFT": "keyboardOverlayNewIncognitoWindow", + "o CTRL": "keyboardOverlayContentBrowser", + "p CTRL": "keyboardOverlayPrint", + "q CTRL SHIFT": "keyboardOverlaySignOut", + "r CTRL": "keyboardOverlayReloadCurrentPage", + "r CTRL SHIFT": "keyboardOverlayReloadIgnoringCache", + "right ALT": "keyboardOverlayForward", + "s CTRL": "keyboardOverlaySave", + "space CTRL": "keyboardOverlaySelectPreviousInputMethod", + "space SHIFT": "keyboardOverlayScrollUpOnePage", + "t CTRL": "keyboardOverlayNewTab", + "t CTRL SHIFT": "keyboardOverlayReopenLastClosedTab", + "tab ALT": "keyboardOverlayPreviousWindow", + "tab ALT SHIFT": "keyboardOverlayNextWindow", + "tab CTRL": "keyboardOverlayActivateNextTab", + "tab CTRL SHIFT": "keyboardOverlayActivatePreviousTab", + "u CTRL": "keyboardOverlayViewSource", + "up ALT": "keyboardOverlayPageUp", + "up ALT CTRL": "keyboardOverlayHome", + "v CTRL": "keyboardOverlayPaste", + "v CTRL SHIFT": "keyboardOverlayPasteAsPlainText", + "w CTRL": "keyboardOverlayCloseTab", + "w CTRL SHIFT": "keyboardOverlayCloseWindow", + "x CTRL": "keyboardOverlayCut", + "z CTRL": "keyboardOverlayUndo" + } +}; diff --git a/chrome/browser/resources/mobile_setup.html b/chrome/browser/resources/mobile_setup.html index 5fa2e49..eba150e 100644 --- a/chrome/browser/resources/mobile_setup.html +++ b/chrome/browser/resources/mobile_setup.html @@ -22,7 +22,7 @@ iframe { top: 0; bottom: 0; z-index: 10; - padding: 20px; + padding: 100px; -webkit-box-align: center; -webkit-box-pack: center; } @@ -45,7 +45,7 @@ iframe { text-align: center; } -#payment-form { +#paymentForm { display: -webkit-box; position: absolute; left: 0; @@ -61,7 +61,7 @@ iframe { height: 58px; } -#error-msg { +#errorMessage { margin: 20px; } @@ -69,13 +69,13 @@ iframe { display: -webkit-box; } -#final-logo { +#finalLogo { position: absolute; - right: 30px; + right: 130px; width: 150px; } -#activation-logo { +#activationLogo { background-position: center; margin-bottom: 20px; margin-top: 20px; @@ -90,80 +90,39 @@ iframe { height: 1px; } -body[state='connecting'] > #payment-form, -body[state='connecting'] > #final-message, -body[state='connecting'] > * > #error-message { - display: none -} -body[state='connecting'] > #system-status { - display: block -} - -body[state='error'] > #payment-form, -body[state='error'] > #final-message { - display: none -} -body[state='error'] > * > #error-message, -body[state='error'] > #system-status { - display: block -} - -body[state='payment'] > * > #error-message, -body[state='payment'] > #final-message, -body[state='payment'] > #system-status { - display: none -} -body[state='payment'] > #payment-form { - display: block -} - -body[state='activating'] > #payment-form, -body[state='activating'] > #final-message, -body[state='activating'] > * > #error-message { - display: none -} -body[state='activating'] > #system-status { - display: block -} - -body[state='connected'] > * > #error-message, -body[state='connected'] > #system-status { - display: none -} -body[state='connected'] > #payment-form, -body[state='connected'] > #final-message { - display: block +.hidden { + display: none; } .testing-only { + position: absolute; + left: 0; + top: 0; } </style> <script src="chrome://resources/js/cr.js"></script> +<script src="chrome://resources/js/local_strings.js"></script> <script src="chrome://resources/js/util.js"></script> <script src="mobile_setup.js"></script> -<script> - mobile.MobileSetup.getInstance().initialize('payment-form'); -</script> </head> -<body state="connecting" onload="setInterval(mobile.MobileSetup.drawProgress, 100);" - i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize"> - <iframe id="payment-form" frameborder="0"></iframe> - <div id="system-status" class="startup"> - <div class="status-header"><h3 id="header" - i18n-content="status_header"></h3></div> - <div id="error-message"></div> +<body onload="mobile.MobileSetup.loadPage();" + i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize"> + <iframe class="hidden" id="paymentForm" frameborder="0"></iframe> + <div id="systemStatus" class="startup hidden"> + <div><h3 id="statusHeader"></h3></div> + <div id="errorMessage"></div> <canvas id="canvas" width="56" height="56"></canvas> <div id="splitter"></div> - <div id="activation-logo" class="logo"></div> + <div id="activationLogo" class="logo"></div> </div> - <div id="final-message" class="overlay"> + <div id="finalMessage" class="overlay hidden"> <div class="box"> <div> <div class="header"><h3 i18n-content="completed_header"></h3></div> <div id="action" i18n-content="completed_text"></div> </div> - <div id="final-logo" class="logo"></div> + <div id="finalLogo" class="logo"></div> </div> </div> <div class="testing-only"> diff --git a/chrome/browser/resources/mobile_setup.js b/chrome/browser/resources/mobile_setup.js index f6c481d..16b02cf 100644 --- a/chrome/browser/resources/mobile_setup.js +++ b/chrome/browser/resources/mobile_setup.js @@ -10,13 +10,24 @@ cr.define('mobile', function() { cr.addSingletonGetter(MobileSetup); + MobileSetup.PLAN_ACTIVATION_LOADING = -1; + MobileSetup.PLAN_ACTIVATION_START = 0; + MobileSetup.PLAN_ACTIVATION_INITIATING_ACTIVATION = 1; + MobileSetup.PLAN_ACTIVATION_RECONNECTING = 2; + MobileSetup.PLAN_ACTIVATION_SHOWING_PAYMENT = 3; + MobileSetup.PLAN_ACTIVATION_DONE = 4; + MobileSetup.PLAN_ACTIVATION_ERROR = 5; + + MobileSetup.localStrings_ = new LocalStrings(); + MobileSetup.prototype = { // Mobile device information. deviceInfo_: null, - frame_name_ : '', - local_strings_: new LocalStrings(); + frameName_ : '', + initialized_ : false, + faked_transaction_ : false, // UI states. - state_ : 0, + state_ : -1, STATE_UNKNOWN_: "unknown", STATE_CONNECTING_: "connecting", STATE_ERROR_: "error", @@ -25,22 +36,47 @@ cr.define('mobile', function() { STATE_CONNECTED_: "connected", initialize: function(frame_name) { + if (this.initialized_) { + console.log('calling initialize() again?'); + return; + } + this.initialized_ = true; self = this; - this.frame_name_ = frame_name; - document.addEventListener('DOMContentLoaded', function(deviceInfo) { - chrome.send('getDeviceInfo', - ['mobile.MobileSetup.getDeviceInfoCallback']); - }); + this.frameName_ = frame_name; window.addEventListener('message', function(e) { self.onMessageReceived_(e); }); + $('cheat').addEventListener('click', function(e) { + console.log('calling setTransactionStatus from cheat.onclick'); + if (self.faked_transaction_) + return; + $('paymentForm').classList.add('hidden'); + chrome.send('setTransactionStatus', ['OK']); + self.faked_transaction_ = true; + }); + $(frame_name).addEventListener('load', function(e) { + // Flip the visibility of the payment page only after the frame is + // fully loaded. + if (self.state_ == MobileSetup.PLAN_ACTIVATION_SHOWING_PAYMENT) { + $('statusHeader').textContent = ''; + $('finalMessage').classList.add('hidden'); + $('errorMessage').classList.add('hidden'); + $('systemStatus').classList.add('hidden'); + $('paymentForm').classList.remove('hidden'); + } + }); + + this.changeState_(MobileSetup.PLAN_ACTIVATION_LOADING); + setInterval(mobile.MobileSetup.drawProgress, 100); + // Kick off activation process. + chrome.send('startActivation', []); }, - loadPaymentFrame: function(deviceInfo) { + loadPaymentFrame_: function(deviceInfo) { if (deviceInfo) { this.deviceInfo_ = deviceInfo; - $(this.frame_name_).contentWindow.location.href = + $(this.frameName_).contentWindow.location.href = this.deviceInfo_.payment_url; } }, @@ -53,40 +89,68 @@ cr.define('mobile', function() { if (e.data.type == 'requestDeviceInfoMsg') { this.sendDeviceInfo_(); } else if (e.data.type == 'reportTransactionStatusMsg') { + console.log('calling setTransactionStatus from onMessageReceived_'); + $('paymentForm').classList.add('hidden'); chrome.send('setTransactionStatus', [e.data.status]); } }, - changeState: function(new_state, errorText) { - if (state_ == new_state) + changeState_: function(deviceInfo) { + var new_state = deviceInfo.state; + if (this.state_ == new_state) return; - if (new_state == STATE_UNKNOWN_) - document.body.setAttribute('state', STATE_CONNECTING_); - else - document.body.setAttribute('state', new_state); + var main = $('mainbody'); + // Map handler state to UX. switch(new_state) { - case STATE_UNKNOWN_: - case STATE_CONNECTING_: - $('status-header').textContent = - local_strings_.getString('connecting_header'); - $('error-message').textContent = ''; + case MobileSetup.PLAN_ACTIVATION_LOADING: + case MobileSetup.PLAN_ACTIVATION_START: + $('statusHeader').textContent = + MobileSetup.localStrings_.getString('connecting_header'); + $('errorMessage').textContent = ''; + $('paymentForm').classList.add('hidden'); + $('finalMessage').classList.add('hidden'); + $('errorMessage').classList.add('hidden'); + $('systemStatus').classList.remove('hidden'); + break; + case MobileSetup.PLAN_ACTIVATION_INITIATING_ACTIVATION: + case MobileSetup.PLAN_ACTIVATION_RECONNECTING: + $('statusHeader').textContent = + MobileSetup.localStrings_.getString('activating_header'); + $('errorMessage').textContent = ''; + $('paymentForm').classList.add('hidden'); + $('finalMessage').classList.add('hidden'); + $('errorMessage').classList.add('hidden'); + $('systemStatus').classList.remove('hidden'); break; - case STATE_ERROR_: - $('status-header').textContent = - local_strings_.getString('error_header'); - $('error-message').textContent = errorText; + case MobileSetup.PLAN_ACTIVATION_SHOWING_PAYMENT: + $('paymentForm').classList.add('hidden'); + $('finalMessage').classList.add('hidden'); + $('errorMessage').classList.add('hidden'); + $('systemStatus').classList.remove('hidden'); + this.loadPaymentFrame_(deviceInfo); break; - case STATE_ACTIVATING_: - $('status-header').textContent = - local_strings_.getString('activating_header'); - $('error-message').textContent = ''; + case MobileSetup.PLAN_ACTIVATION_DONE: + $('statusHeader').textContent = ''; + $('errorMessage').classList.add('hidden'); + $('systemStatus').classList.add('hidden'); + $('paymentForm').classList.remove('hidden'); + $('finalMessage').classList.remove('hidden'); + break; + case MobileSetup.PLAN_ACTIVATION_ERROR: + $('statusHeader').textContent = + MobileSetup.localStrings_.getString('error_header'); + $('errorMessage').textContent = deviceInfo.error; + $('paymentForm').classList.add('hidden'); + $('finalMessage').classList.add('hidden'); + $('errorMessage').classList.remove('hidden'); + $('systemStatus').classList.remove('hidden'); break; } - state_ = new_state; + this.state_ = new_state; }, updateDeviceStatus_: function(deviceInfo) { - this.changeState(deviceInfo.state, deviceInfo.error); + this.changeState_(deviceInfo); }, sendDeviceInfo_ : function() { @@ -97,19 +161,16 @@ cr.define('mobile', function() { 'carrier': this.deviceInfo_.carrier, 'MEID': this.deviceInfo_.MEID, 'IMEI': this.deviceInfo_.IMEI, - 'IMSI': this.deviceInfo_.IMSI, - 'ESN': this.deviceInfo_.ESN, 'MDN': this.deviceInfo_.MDN } }; - $(this.frame_name_).contentWindow.postMessage(msg, + $(this.frameName_).contentWindow.postMessage(msg, this.deviceInfo_.payment_url); } }; MobileSetup.drawProgress = function () { - var canvas = $('wheel'); var ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); @@ -147,15 +208,14 @@ cr.define('mobile', function() { } }; - MobileSetup.getDeviceInfoCallback = function(deviceInfo) { - MobileSetup.getInstance().loadPaymentFrame(deviceInfo); - }; - - MobileSetup.deviceStateChanged = function(deviceInfo) { MobileSetup.getInstance().updateDeviceStatus_(deviceInfo); }; + MobileSetup.loadPage = function() { + mobile.MobileSetup.getInstance().initialize('paymentForm'); + }; + // Export return { MobileSetup: MobileSetup diff --git a/chrome/browser/resources/net_internals/index.html b/chrome/browser/resources/net_internals/index.html index d146242..1a95210 100644 --- a/chrome/browser/resources/net_internals/index.html +++ b/chrome/browser/resources/net_internals/index.html @@ -69,6 +69,17 @@ found in the LICENSE file. </tr></table> + <h4>Proxy auto-config initialization</h4> + <ul> + <li> + <a href='#events&q=type:INIT_PROXY_RESOLVER'>View all events</a> + </li> + <li> + Latest proxy resolver event: + <pre id=proxyResolverLog></pre> + </li> + </ul> + <h4> Proxies which have failed recently, and are marked as bad <input type=button value="Clear bad proxies" id=clearBadProxies /> diff --git a/chrome/browser/resources/net_internals/main.js b/chrome/browser/resources/net_internals/main.js index 5249789..7bfdb62 100644 --- a/chrome/browser/resources/net_internals/main.js +++ b/chrome/browser/resources/net_internals/main.js @@ -57,7 +57,8 @@ function onLoaded() { 'proxyEffectiveSettings', 'proxyReloadSettings', 'badProxiesTableBody', - 'clearBadProxies'); + 'clearBadProxies', + 'proxyResolverLog'); // Create a view which will display information on the host resolver. var dnsView = new DnsView('dnsTabContent', diff --git a/chrome/browser/resources/net_internals/proxyview.js b/chrome/browser/resources/net_internals/proxyview.js index 54bb3fc..0f7f68b 100644 --- a/chrome/browser/resources/net_internals/proxyview.js +++ b/chrome/browser/resources/net_internals/proxyview.js @@ -7,6 +7,7 @@ * * - Shows the current proxy settings. * - Has a button to reload these settings. + * - Shows the log entries for the most recent INIT_PROXY_RESOLVER source * - Shows the list of proxy hostnames that are cached as "bad". * - Has a button to clear the cached bad proxies. * @@ -17,13 +18,18 @@ function ProxyView(mainBoxId, effectiveSettingsDivId, reloadSettingsButtonId, badProxiesTbodyId, - clearBadProxiesButtonId) { + clearBadProxiesButtonId, + proxyResolverLogPreId) { DivView.call(this, mainBoxId); + this.latestProxySourceEntries_ = null; + this.latestProxySourceId_ = 0; + // Hook up the UI components. this.originalSettingsDiv_ = document.getElementById(originalSettingsDivId); this.effectiveSettingsDiv_ = document.getElementById(effectiveSettingsDivId); + this.proxyResolverLogPre_ = document.getElementById(proxyResolverLogPreId); this.badProxiesTbody_ = document.getElementById(badProxiesTbodyId); var reloadSettingsButton = document.getElementById(reloadSettingsButtonId); @@ -36,6 +42,7 @@ function ProxyView(mainBoxId, // Register to receive proxy information as it changes. g_browser.addProxySettingsObserver(this); g_browser.addBadProxiesObserver(this); + g_browser.addLogObserver(this); } inherits(ProxyView, DivView); @@ -69,3 +76,41 @@ ProxyView.prototype.onBadProxiesChanged = function(badProxies) { addTextNode(badUntilCell, badUntilDate.toLocaleString()); } }; + +ProxyView.prototype.onLogEntryAdded = function(logEntry) { + if (logEntry.source.type != LogSourceType.INIT_PROXY_RESOLVER || + this.latestProxySourceId_ > logEntry.source.id) { + return; + } + + if (logEntry.source.id > this.latestProxySourceId_) { + this.latestProxySourceId_ = logEntry.source.id; + this.latestProxySourceEntries_ = []; + } + + this.latestProxySourceEntries_.push(logEntry); + this.proxyResolverLogPre_.innerHTML = ''; + addTextNode(this.proxyResolverLogPre_, + PrintSourceEntriesAsText(this.latestProxySourceEntries_, false)); +}; + +/** + * Clears the display of and log entries for the last proxy lookup. + */ +ProxyView.prototype.clearLog_ = function() { + this.latestProxySourceEntries_ = []; + // Prevents display of partial logs. + ++this.latestProxySourceId_; + + this.proxyResolverLogPre_.innerHTML = ''; + addTextNode(this.proxyResolverLogPre_, 'Deleted.'); +}; + +ProxyView.prototype.onLogEntriesDeleted = function(sourceIds) { + if (sourceIds.indexOf(this.latestProxySourceId_) != -1) + this.clearLog_(); +}; + +ProxyView.prototype.onAllLogEntriesDeleted = function() { + this.clearLog_(); +}; diff --git a/chrome/browser/resources/network_menu.js b/chrome/browser/resources/network_menu.js index 4a90bc0..8413e99 100644 --- a/chrome/browser/resources/network_menu.js +++ b/chrome/browser/resources/network_menu.js @@ -143,7 +143,12 @@ NetworkMenuItem.prototype = { this.passwordEdit.value, '', this.rememberCheckbox.checked); } } else { - sendConnect(index, '', '', this.rememberCheckbox.checked); + if (this.attrs.remembered) { + sendConnect(index, this.attrs.passphrase, '', + this.rememberCheckbox.checked); + } else { + sendConnect(index, '', '', this.rememberCheckbox.checked); + } } }, diff --git a/chrome/browser/resources/new_new_tab.css b/chrome/browser/resources/new_new_tab.css index 4ab7852..fca01f6 100644 --- a/chrome/browser/resources/new_new_tab.css +++ b/chrome/browser/resources/new_new_tab.css @@ -197,11 +197,6 @@ html[dir=rtl] .item { max-width: 300px; } -.hbox { - display: -webkit-box; - -webkit-box-orient: horizontal; -} - .foreign-session-client { float: left; max-width: 114px; /* Selected so that we can fit 5 items in EN-US */ diff --git a/chrome/browser/resources/ntp/apps.js b/chrome/browser/resources/ntp/apps.js index 52cafff..81d7eb0 100644 --- a/chrome/browser/resources/ntp/apps.js +++ b/chrome/browser/resources/ntp/apps.js @@ -252,13 +252,6 @@ var apps = (function() { document.documentElement.setAttribute("install-animation-enabled", "false"); }); - - // Make sure apps is de-minimized... - setSectionVisible('apps', Section.APPS, true, MINIMIZED_APPS); - - // ...and expanded. - if ($('apps').classList.contains('hidden')) - toggleSectionVisibilityAndAnimate('APPS'); } var settingsButton = div.appendChild(new cr.ui.ContextMenuButton); diff --git a/chrome/browser/resources/options.html b/chrome/browser/resources/options.html index e14099f..f28de4f 100644 --- a/chrome/browser/resources/options.html +++ b/chrome/browser/resources/options.html @@ -30,6 +30,7 @@ <link rel="stylesheet" href="options/import_data_overlay.css"> <link rel="stylesheet" href="options/search_engine_manager.css"> <link rel="stylesheet" href="options/subpages_tab_controls.css"> +<link rel="stylesheet" href="options/zippy.css"> <if expr="pp_ifdef('chromeos')"> <link rel="stylesheet" href="options/about_page.css"> <link rel="stylesheet" href="options/chromeos_accounts_options_page.css"> @@ -116,12 +117,14 @@ <script src="options/font_settings.js"></script> <script src="options/font_settings_ui.js"></script> <script src="options/import_data_overlay.js"></script> +<script src="options/instant_confirm_overlay.js"></script> <script src="options/passwords_exceptions.js"></script> <script src="options/passwords_exceptions_list.js"></script> <script src="options/personal_options.js"></script> <script src="options/search_engine_manager.js"></script> <script src="options/search_engine_manager_engine_list.js"></script> <script src="options/sync_options.js"></script> +<script src="options/zippy.js"></script> <script> var AddStartupPageOverlay = options.AddStartupPageOverlay; @@ -137,6 +140,7 @@ var CookiesView = options.CookiesView; var EditSearchEngineOverlay = options.EditSearchEngineOverlay; var FontSettings = options.FontSettings; var ImportDataOverlay = options.ImportDataOverlay; +var InstantConfirmOverlay = options.InstantConfirmOverlay; var OptionsPage = options.OptionsPage; var PasswordsExceptions = options.PasswordsExceptions; var PersonalOptions = options.PersonalOptions; @@ -187,6 +191,7 @@ function load() { OptionsPage.registerOverlay(ClearBrowserDataOverlay.getInstance()); OptionsPage.registerOverlay(EditSearchEngineOverlay.getInstance()); OptionsPage.registerOverlay(ImportDataOverlay.getInstance()); + OptionsPage.registerOverlay(InstantConfirmOverlay.getInstance()); if (cr.isChromeOS) { OptionsPage.register(AccountsOptions.getInstance()); @@ -254,6 +259,8 @@ function load() { // Allow platform specific CSS rules. if (cr.isMac) document.documentElement.setAttribute('os', 'mac'); + if (cr.isViews) + document.documentElement.setAttribute('toolkit', 'views'); var end = new Date(); var total = end - start; @@ -279,6 +286,7 @@ window.onpopstate = function(e) { <include src="options/clear_browser_data_overlay.html"> <include src="options/edit_search_engine_overlay.html"> <include src="options/import_data_overlay.html"> + <include src="options/instant_confirm_overlay.html"> <if expr="pp_ifdef('chromeos')"> <include src="options/chromeos_language_add_language_overlay.html"> <include @@ -304,7 +312,8 @@ window.onpopstate = function(e) { <div id="mainview"> <div class="hidden" id="managed-prefs-banner"> <span id="managed-prefs-icon"></span> - <span i18n-content="managedPrefsBannerText"></span> + <span id="managed-prefs-text" + i18n-content="managedPrefsBannerText"></span> </div> <div id="mainview-content"> <if expr="pp_ifdef('chromeos')"> diff --git a/chrome/browser/resources/options/about_page.html b/chrome/browser/resources/options/about_page.html index 919cf52..edcd1e9 100644 --- a/chrome/browser/resources/options/about_page.html +++ b/chrome/browser/resources/options/about_page.html @@ -20,12 +20,11 @@ <div class="hidden" id="aboutPageMoreInfo"> <section> <h3 i18n-content="channel"></h3> - <!-- TODO seanparent: wire to channel API when available. --> - <select disabled> - <option value="release" i18n-content="release"></option> - <option value="beta" i18n-content="beta"></option> - <option selected value="development" - i18n-content="development"></option> + <select id="channelSelect"> + <!-- There is no "release" channel at this moment --> + <!-- <option value="release" i18n-content="release"></option> --> + <option value="beta-channel" i18n-content="beta"></option> + <option value="dev-channel" i18n-content="development"></option> </select> </section> <section> diff --git a/chrome/browser/resources/options/about_page.js b/chrome/browser/resources/options/about_page.js index a6490a3..8276057 100644 --- a/chrome/browser/resources/options/about_page.js +++ b/chrome/browser/resources/options/about_page.js @@ -32,6 +32,15 @@ cr.define('options', function() { $('aboutPageMoreInfo').classList.remove('hidden'); }; + if (cr.commandLine.options['--bwsi']) { + $('channelSelect').disabled = true; + } else { + $('channelSelect').onchange = function(event) { + var channel = event.target.value; + chrome.send('SetReleaseTrack', [channel]); + }; + } + // Notify the handler that the page is ready. chrome.send('PageReady'); }, @@ -49,6 +58,26 @@ cr.define('options', function() { updateEnable_: function(enable) { $('checkNow').disabled = !enable; }, + + // Updates the selected option in 'channelSelect' <select> element. + updateSelectedOption_: function(value) { + var options = $('channelSelect').querySelectorAll('option'); + for (var i = 0; i < options.length; i++) { + var option = options[i]; + if (option.value == value) { + option.selected = true; + } + } + }, + + // Changes the "check now" button to "restart now" button. + changeToRestartButton_: function() { + $('checkNow').textContent = localStrings.getString('restart_now'); + $('checkNow').disabled = false; + $('checkNow').onclick = function(event) { + chrome.send('RestartNow'); + }; + }, }; AboutPage.updateOSVersionCallback = function(versionString) { @@ -63,10 +92,18 @@ cr.define('options', function() { AboutPage.getInstance().updateEnable_(enable); }; + AboutPage.updateSelectedOptionCallback = function(value) { + AboutPage.getInstance().updateSelectedOption_(value); + }; + AboutPage.setUpdateImage = function(state) { $('updateIcon').className= 'update-icon ' + state; }; + AboutPage.changeToRestartButton = function() { + AboutPage.getInstance().changeToRestartButton_(); + }; + // Export return { AboutPage: AboutPage diff --git a/chrome/browser/resources/options/add_startup_page_overlay.css b/chrome/browser/resources/options/add_startup_page_overlay.css index 54d367e..a863eb6 100644 --- a/chrome/browser/resources/options/add_startup_page_overlay.css +++ b/chrome/browser/resources/options/add_startup_page_overlay.css @@ -30,6 +30,6 @@ display: inline; } -#addStartupPageOverlay .button-strip { +#addStartupPageOverlay .action-area { margin-top: 2ex; } diff --git a/chrome/browser/resources/options/add_startup_page_overlay.html b/chrome/browser/resources/options/add_startup_page_overlay.html index c075547..c2f97d2 100644 --- a/chrome/browser/resources/options/add_startup_page_overlay.html +++ b/chrome/browser/resources/options/add_startup_page_overlay.html @@ -8,17 +8,11 @@ <list id="addStartupRecentPageList"></list> - <div class="button-strip"> - <if expr="os == 'win32'"> - <button type="submit" id="addStartupPageAddButton" disabled - i18n-content="addStartupPageAddButton"></button> - </if> + <div class="action-area button-strip"> <button type="reset" i18n-content="addStartupPageCancelButton"></button> - <if expr="os != 'win32'"> - <button type="submit" id="addStartupPageAddButton" disabled - i18n-content="addStartupPageAddButton"></button> - </if> + <button type="submit" id="addStartupPageAddButton" disabled + i18n-content="addStartupPageAddButton"></button> </div> </form> </div> diff --git a/chrome/browser/resources/options/advanced_options.html b/chrome/browser/resources/options/advanced_options.html index f6b94ea..c317349 100644 --- a/chrome/browser/resources/options/advanced_options.html +++ b/chrome/browser/resources/options/advanced_options.html @@ -10,7 +10,7 @@ i18n-content="privacyClearDataButton"></button> </div> <div i18n-content="disableServices" class="disable-services-div"></div> - <div><a target="_blank" i18n-content="privacyLearnMoreLabel" + <div><a target="_blank" i18n-content="learnMore" i18n-values="href:privacyLearnMoreURL"></a></div> <label class="checkbox"> <input id="alternateErrorPagesEnabled" pref="alternate_error_pages.enabled" @@ -33,8 +33,8 @@ <span i18n-content="safeBrowsingEnableProtection"></span> </label> <if expr="pp_ifdef('_google_chrome')"> - <label class="checkbox"> - <input id="metricsReportingEnabled" + <label class="checkbox" id="metricsReportingSetting"> + <input id="metricsReportingEnabled" pref="cros.metrics.reportingEnabled" type="checkbox"> <span i18n-content="enableLogging"></span> </label> @@ -92,6 +92,23 @@ </if> <div><button id="fontSettingsConfigureFontsOnlyButton" i18n-content="fontSettingsConfigureFontsOnlyButton"></button></div> + <div> + <label style="display:inline;"> + <span i18n-content="defaultZoomLevelLabel"></span> + <select id="defaultZoomLevel"> + <option value="-3">57%</option> + <option value="-2">69%</option> + <option value="-1">83%</option> + <option value="0">100%</option> + <option value="1">120%</option> + <option value="2">144%</option> + <option value="3">172%</option> + <option value="4">207%</option> + <option value="5">248%</option> + <option value="6">298%</option> + </select> + </label> + </div> <if expr="os == 'win32'"> <div> <label style="display:inline;"> @@ -163,7 +180,7 @@ </label> <div> <a target="_blank" - i18n-content="chromeAppsLearnMoreBackgroundModeLabel" + i18n-content="learnMore" i18n-values="href:chromeAppsLearnMoreBackgroundModeURL"></a> </div> </div> diff --git a/chrome/browser/resources/options/advanced_options.js b/chrome/browser/resources/options/advanced_options.js index 0cd785d..db7cf1d 100644 --- a/chrome/browser/resources/options/advanced_options.js +++ b/chrome/browser/resources/options/advanced_options.js @@ -56,6 +56,10 @@ var OptionsPage = options.OptionsPage; OptionsPage.showPageByName('fontSettings'); chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']); }; + $('defaultZoomLevel').onchange = function(event) { + chrome.send('defaultZoomLevelAction', + [String(event.target.options[event.target.selectedIndex].value)]); + } $('optionsReset').onclick = function(event) { AlertOverlay.show(undefined, localStrings.getString('optionsResetMessage'), @@ -167,6 +171,26 @@ var OptionsPage = options.OptionsPage; AdvancedOptions.getInstance().showRestartRequiredAlert_(); } + AdvancedOptions.SetMetricsReportingSettingVisibility = function(visible) { + if (visible) { + $('metricsReportingSetting').style.display = 'block'; + } else { + $('metricsReportingSetting').style.display = 'none'; + } + } + + // Set the default zoom level selected item. + AdvancedOptions.SetDefaultZoomLevel = function(value) { + var selectCtl = $('defaultZoomLevel'); + for (var i = 0; i < selectCtl.options.length; i++) { + if (selectCtl.options[i].value == value) { + selectCtl.selectedIndex = i; + return; + } + } + selectCtl.selectedIndex = 4; // 100% + }; + // Set the download path. AdvancedOptions.SetDownloadLocationPath = function(path) { if (!cr.isChromeOS) @@ -226,6 +250,12 @@ var OptionsPage = options.OptionsPage; } }; + AdvancedOptions.HideCloudPrintProxySection = function() { + if (!cr.isChromeOS) { + $('cloud-print-proxy-section').style.display = 'none'; + } + }; + // Export return { AdvancedOptions: AdvancedOptions diff --git a/chrome/browser/resources/options/alert_overlay.html b/chrome/browser/resources/options/alert_overlay.html index 3ab780d..7512077 100644 --- a/chrome/browser/resources/options/alert_overlay.html +++ b/chrome/browser/resources/options/alert_overlay.html @@ -1,13 +1,8 @@ <div class="page hidden" id="alertOverlay"> <h1 id="alertOverlayTitle"></h1> <div id="alertOverlayMessage"></div> - <div class="button-strip"> - <if expr="os != 'darwin'"> - <button type="submit" id="alertOverlayOk"></button> - </if> + <div class="action-area button-strip"> <button type="reset" id="alertOverlayCancel"></button> - <if expr="os == 'darwin'"> - <button type="submit" id="alertOverlayOk"></button> - </if> + <button type="submit" id="alertOverlayOk"></button> </div> </div> diff --git a/chrome/browser/resources/options/autofill_edit_address_overlay.html b/chrome/browser/resources/options/autofill_edit_address_overlay.html index be05cd3..9528ccf 100644 --- a/chrome/browser/resources/options/autofill_edit_address_overlay.html +++ b/chrome/browser/resources/options/autofill_edit_address_overlay.html @@ -94,16 +94,10 @@ </label> </div> - <div class="button-strip"> - <if expr="os == 'win32'"> - <button type="submit" id="autoFillEditAddressApplyButton" disabled - i18n-content="ok"></button> - </if> + <div class="action-area button-strip"> <button type="reset" id="autoFillEditAddressCancelButton" - i18n-content="cancel"></button> - <if expr="os != 'win32'"> - <button type="submit" id="autoFillEditAddressApplyButton" disabled - i18n-content="ok"></button> - </if> + i18n-content="cancel"></button> + <button type="submit" id="autoFillEditAddressApplyButton" disabled + i18n-content="ok"></button> </div> </div> diff --git a/chrome/browser/resources/options/autofill_edit_address_overlay.js b/chrome/browser/resources/options/autofill_edit_address_overlay.js index a6eb980..46a17d0 100644 --- a/chrome/browser/resources/options/autofill_edit_address_overlay.js +++ b/chrome/browser/resources/options/autofill_edit_address_overlay.js @@ -5,8 +5,8 @@ cr.define('options', function() { const OptionsPage = options.OptionsPage; - // The unique ID of the loaded address. - var uniqueID; + // The GUID of the loaded address. + var guid; /** * AutoFillEditAddressOverlay class @@ -39,19 +39,19 @@ cr.define('options', function() { self.dismissOverlay_(); } - self.uniqueID = 0; + self.guid = ''; self.clearInputFields_(); self.connectInputEvents_(); }, /** - * Clears any uncommitted input, resets the stored unique ID and dismisses - * the overlay. + * Clears any uncommitted input, resets the stored GUID and dismisses the + * overlay. * @private */ dismissOverlay_: function() { this.clearInputFields_(); - this.uniqueID = 0; + this.guid = ''; OptionsPage.clearOverlays(); }, @@ -62,7 +62,7 @@ cr.define('options', function() { */ saveAddress_: function() { var address = new Array(); - address[0] = String(this.uniqueID); + address[0] = this.guid; address[1] = $('fullName').value; address[2] = $('companyName').value; address[3] = $('addrLine1').value; @@ -129,13 +129,13 @@ cr.define('options', function() { /** * Loads the address data from |address|, sets the input fields based on - * this data and stores the unique ID of the address. + * this data and stores the GUID of the address. * @private */ loadAddress_: function(address) { this.setInputFields_(address); this.inputFieldChanged_(); - this.uniqueID = address['uniqueID']; + this.guid = address['guid']; }, /** diff --git a/chrome/browser/resources/options/autofill_edit_creditcard_overlay.html b/chrome/browser/resources/options/autofill_edit_creditcard_overlay.html index a4aef34..6b2f30d 100644 --- a/chrome/browser/resources/options/autofill_edit_creditcard_overlay.html +++ b/chrome/browser/resources/options/autofill_edit_creditcard_overlay.html @@ -9,13 +9,6 @@ </div> <div> - <label id="billingAddressLabel"> - <span i18n-content="billingAddressLabel"></span><br> - <select id="billingAddress"></select> - </label> - </div> - - <div> <label id="creditCardNumberLabel"> <span i18n-content="creditCardNumberLabel"></span><br> <input type="text" id="creditCardNumber"> @@ -30,16 +23,10 @@ </label> </div> - <div class="button-strip"> - <if expr="os == 'win32'"> - <button type="submit" id="autoFillEditCreditCardApplyButton" disabled - i18n-content="ok"></button> - </if> + <div class="action-area button-strip"> <button type="reset" id="autoFillEditCreditCardCancelButton" i18n-content="cancel"></button> - <if expr="os != 'win32'"> - <button type="submit" id="autoFillEditCreditCardApplyButton" disabled - i18n-content="ok"></button> - </if> + <button type="submit" id="autoFillEditCreditCardApplyButton" disabled + i18n-content="ok"></button> </div> </div> diff --git a/chrome/browser/resources/options/autofill_edit_creditcard_overlay.js b/chrome/browser/resources/options/autofill_edit_creditcard_overlay.js index 47d4ad3..01b1722 100644 --- a/chrome/browser/resources/options/autofill_edit_creditcard_overlay.js +++ b/chrome/browser/resources/options/autofill_edit_creditcard_overlay.js @@ -5,11 +5,8 @@ cr.define('options', function() { const OptionsPage = options.OptionsPage; - // The unique ID of the loaded credit card. - var uniqueID; - - // The unique IDs of the billing addresses. - var billingAddressIDs; + // The GUID of the loaded credit card. + var guid; /** * AutoFillEditCreditCardOverlay class @@ -42,8 +39,7 @@ cr.define('options', function() { self.dismissOverlay_(); } - self.uniqueID = 0; - self.billingAddressIDs = new Array(); + self.guid = ''; self.clearInputFields_(); self.connectInputEvents_(); self.setDefaultSelectOptions_(); @@ -55,7 +51,7 @@ cr.define('options', function() { */ dismissOverlay_: function() { this.clearInputFields_(); - this.uniqueID = 0; + this.guid = ''; OptionsPage.clearOverlays(); }, @@ -65,19 +61,12 @@ cr.define('options', function() { * @private */ saveCreditCard_: function() { - var creditCard = new Array(6); - creditCard[0] = String(this.uniqueID); + var creditCard = new Array(5); + creditCard[0] = this.guid; creditCard[1] = $('nameOnCard').value; - creditCard[2] = '0'; - creditCard[3] = $('creditCardNumber').value; - creditCard[4] = $('expirationMonth').value; - creditCard[5] = $('expirationYear').value; - - // Set the ID if available. - if (this.billingAddressIDs.length != 0) { - creditCard[2] = - String(this.billingAddressIDs[$('billingAddress').selectedIndex]); - } + creditCard[2] = $('creditCardNumber').value; + creditCard[3] = $('expirationMonth').value; + creditCard[4] = $('expirationYear').value; chrome.send('updateCreditCard', creditCard); }, @@ -90,9 +79,10 @@ cr.define('options', function() { */ connectInputEvents_: function() { var self = this; - $('nameOnCard').oninput = $('billingAddress').onchange = - $('creditCardNumber').oninput = $('expirationMonth').onchange = - $('expirationYear').onchange = function(event) { + $('nameOnCard').oninput = $('creditCardNumber').oninput = + $('expirationMonth').onchange = $('expirationYear').onchange = + // TODO(isherman): What should the indentation of this line be? + function(event) { self.inputFieldChanged_(); } }, @@ -108,35 +98,14 @@ cr.define('options', function() { }, /** - * Clears the options from the billing address select control. - * @private - */ - clearBillingAddressControl_: function() { - $('billingAddress').length = 0; - }, - - /** - * Sets the default billing address in the 'Billing address' select control. - * @private - */ - setDefaultBillingAddress_: function() { - this.clearBillingAddressControl_(); - - var existingAddress = - new Option(localStrings.getString('chooseExistingAddress')); - $('billingAddress').add(existingAddress, null); - }, - - /** - * Sets the default values of the options in the 'Billing address' and - * 'Expiration date' select controls. + * Sets the default values of the options in the 'Expiration date' select + * controls. * @private */ setDefaultSelectOptions_: function() { - this.setDefaultBillingAddress_(); - // Set the 'Expiration month' default options. var expirationMonth = $('expirationMonth'); + expirationMonth.options.length = 0; for (var i = 1; i <= 12; ++i) { var text; if (i < 10) @@ -152,6 +121,8 @@ cr.define('options', function() { // Set the 'Expiration year' default options. var expirationYear = $('expirationYear'); + expirationYear.options.length = 0; + var date = new Date(); var year = parseInt(date.getFullYear()); for (var i = 0; i < 10; ++i) { @@ -169,7 +140,6 @@ cr.define('options', function() { */ clearInputFields_: function() { $('nameOnCard').value = ''; - $('billingAddress').selectedIndex = 0; $('creditCardNumber').value = ''; $('expirationMonth').selectedIndex = 0; $('expirationYear').selectedIndex = 0; @@ -189,12 +159,6 @@ cr.define('options', function() { // reload the select options just to be safe. this.setDefaultSelectOptions_(); - var id = parseInt(creditCard['billingAddress']); - for (var i = 0; i < this.billingAddressIDs.length; ++i) { - if (this.billingAddressIDs[i] == id) - $('billingAddress').selectedIndex = i; - } - var idx = parseInt(creditCard['expirationMonth'], 10); $('expirationMonth').selectedIndex = idx - 1; @@ -210,37 +174,13 @@ cr.define('options', function() { /** * Loads the credit card data from |creditCard|, sets the input fields based - * on this data and stores the unique ID of the credit card. + * on this data and stores the GUID of the credit card. * @private */ loadCreditCard_: function(creditCard) { this.setInputFields_(creditCard); this.inputFieldChanged_(); - this.uniqueID = creditCard['uniqueID']; - }, - - /** - * Sets the 'billingAddress' select control with the address labels in - * |addresses|. Also stores the unique IDs of the corresponding addresses - * on this object. - * @private - */ - setBillingAddresses_: function(addresses) { - this.billingAddressIDs = new Array(addresses.length); - - if (addresses.length == 0) { - this.setDefaultBillingAddress_(); - return; - } - - this.clearBillingAddressControl_(); - - for (var i = 0; i < addresses.length; ++i) { - var address = addresses[i]; - var option = new Option(address['label']); - this.billingAddressIDs[i] = address['uniqueID']; - billingAddress.add(option, null); - } + this.guid = creditCard['guid']; }, }; @@ -256,10 +196,6 @@ cr.define('options', function() { $('autoFillCreditCardTitle').textContent = title; }; - AutoFillEditCreditCardOverlay.setBillingAddresses = function(addresses) { - AutoFillEditCreditCardOverlay.getInstance().setBillingAddresses_(addresses); - }; - // Export return { AutoFillEditCreditCardOverlay: AutoFillEditCreditCardOverlay diff --git a/chrome/browser/resources/options/autofill_options.js b/chrome/browser/resources/options/autofill_options.js index 4b606ec..3b662f2 100644 --- a/chrome/browser/resources/options/autofill_options.js +++ b/chrome/browser/resources/options/autofill_options.js @@ -23,8 +23,8 @@ cr.define('options', function() { this.numAddresses = 0; this.numCreditCards = 0; this.activeNavTab = null; - this.addressIDs = null; - this.creditCardIDs = null; + this.addressGUIDs = null; + this.creditCardGUIDs = null; OptionsPage.call(this, 'autoFillOptions', templateData.autoFillOptionsTitle, 'autoFillOptionsPage'); @@ -43,7 +43,8 @@ cr.define('options', function() { self.updateButtonState_(); }; $('profileList').addEventListener('dblclick', function(event) { - self.editProfile_(); + if ($('autoFillEnabled').checked) + self.editProfile_(); }); $('addAddressButton').onclick = function(event) { self.showAddAddressOverlay_(); @@ -149,15 +150,14 @@ cr.define('options', function() { var profileList = $('profileList'); var blankAddress = profileList.options[addressOffset]; this.numAddresses = addresses.length; - this.addressIDs = new Array(this.numAddresses); + this.addressGUIDs = new Array(this.numAddresses); for (var i = 0; i < this.numAddresses; i++) { var address = addresses[i]; var option = new Option(address['label']); - this.addressIDs[i] = address['uniqueID']; + this.addressGUIDs[i] = address['guid']; profileList.add(option, blankAddress); } - AutoFillEditCreditCardOverlay.setBillingAddresses(addresses); this.updateButtonState_(); }, @@ -170,11 +170,11 @@ cr.define('options', function() { this.resetCreditCards_(); var profileList = $('profileList'); this.numCreditCards = creditCards.length; - this.creditCardIDs = new Array(this.numCreditCards); + this.creditCardGUIDs = new Array(this.numCreditCards); for (var i = 0; i < this.numCreditCards; i++) { var creditCard = creditCards[i]; var option = new Option(creditCard['label']); - this.creditCardIDs[i] = creditCard['uniqueID']; + this.creditCardGUIDs[i] = creditCard['guid']; profileList.add(option, null); } @@ -205,10 +205,9 @@ cr.define('options', function() { editProfile_: function() { var idx = $('profileList').selectedIndex; if ((profileIndex = this.getAddressIndex_(idx)) != -1) { - chrome.send('editAddress', [String(this.addressIDs[profileIndex])]); + chrome.send('editAddress', [this.addressGUIDs[profileIndex]]); } else if ((profileIndex = this.getCreditCardIndex_(idx)) != -1) { - chrome.send('editCreditCard', - [String(this.creditCardIDs[profileIndex])]); + chrome.send('editCreditCard', [this.creditCardGUIDs[profileIndex]]); } }, @@ -220,10 +219,9 @@ cr.define('options', function() { removeProfile_: function() { var idx = $('profileList').selectedIndex; if ((profileIndex = this.getAddressIndex_(idx)) != -1) - chrome.send('removeAddress', [String(this.addressIDs[profileIndex])]); + chrome.send('removeAddress', [this.addressGUIDs[profileIndex]]); else if ((profileIndex = this.getCreditCardIndex_(idx)) != -1) - chrome.send('removeCreditCard', - [String(this.creditCardIDs[profileIndex])]); + chrome.send('removeCreditCard', [this.creditCardGUIDs[profileIndex]]); }, /** diff --git a/chrome/browser/resources/options/browser_options.html b/chrome/browser/resources/options/browser_options.html index 7950913..ec1a30b 100644 --- a/chrome/browser/resources/options/browser_options.html +++ b/chrome/browser/resources/options/browser_options.html @@ -64,13 +64,30 @@ </section> <section> <h3 i18n-content="defaultSearchGroupName"></h3> - <div id="defaultSearchEngineGroup"> - <select id="defaultSearchEngine" - onchange="BrowserOptions.getInstance().setDefaultSearchEngine()"> - </select> - <button id="defaultSearchManageEnginesButton" - i18n-content="defaultSearchManageEnginesLink"></button> - </div> + <div id="defaultSearchEngineGroup"> + <div> + <select id="defaultSearchEngine" + onchange="BrowserOptions.getInstance().setDefaultSearchEngine()"> + </select> + <button id="defaultSearchManageEnginesButton" + i18n-content="defaultSearchManageEnginesLink"></button> + </div> + <label class="checkbox" id="instantOption"> + <!-- TODO(estade): metric? --> + <input type="checkbox" id="instantEnableCheckbox" + pref="instant.enabled"> + <span i18n-content="instantName"></span> + <!-- This hidden checkbox allows us to get/set the state of the + confirm_dialog_shown pref --> + <input type="checkbox" class="hidden" id="instantDialogShown" + pref="instant.confirm_dialog_shown"> + </label> + <div class="suboption"> + <span i18n-content="instantWarningText"></span> + <a target="_blank" i18n-values="href:instantLearnMoreLink" + i18n-content="learnMore"></a> + </div> + </div> </section> <if expr="not pp_ifdef('chromeos')"> <section> diff --git a/chrome/browser/resources/options/browser_options.js b/chrome/browser/resources/options/browser_options.js index 6b84fe0..df4b55e 100644 --- a/chrome/browser/resources/options/browser_options.js +++ b/chrome/browser/resources/options/browser_options.js @@ -54,13 +54,30 @@ cr.define('options', function() { chrome.send('coreOptionsUserMetricsAction', ['Options_ManageSearchEngines']); }; + $('instantEnableCheckbox').onclick = function(event) { + var alreadyConfirmed = $('instantDialogShown').checked; + + if (this.checked && !alreadyConfirmed) { + // Leave disabled for now. The PrefCheckbox handler already set it to + // true so undo that. + Preferences.setBooleanPref(this.pref, false, this.metric); + OptionsPage.showOverlay('instantConfirmOverlay'); + } + }; + var homepageField = $('homepageURL'); $('homepageUseNTPButton').onchange = this.handleHomepageUseNTPButtonChange_.bind(this); $('homepageUseURLButton').onchange = this.handleHomepageUseURLButtonChange_.bind(this); - $('homepageURL').onchange = - this.handleHomepageURLChange_.bind(this) + homepageField.onchange = + this.handleHomepageURLChange_.bind(this); + + // Ensure that changes are committed when closing the page. + window.addEventListener('unload', function() { + if (document.activeElement == homepageField) + homepageField.blur(); + }); if (!cr.isChromeOS) { $('defaultBrowserUseAsDefaultButton').onclick = function(event) { diff --git a/chrome/browser/resources/options/browser_options_page.css b/chrome/browser/resources/options/browser_options_page.css index b2c1f27..640554f 100644 --- a/chrome/browser/resources/options/browser_options_page.css +++ b/chrome/browser/resources/options/browser_options_page.css @@ -9,6 +9,12 @@ #defaultSearchEngineGroup { display: -webkit-box; + -webkit-box-orient: vertical; +} + +#defaultSearchEngineGroup > div { + display: -webkit-box; + -webkit-box-orient: horizontal; } #defaultSearchEngine { @@ -21,6 +27,10 @@ -webkit-margin-start: 10px; } +#instantOption { + margin-bottom: 0px; +} + #defaultBrowserState { color: #880000; } @@ -28,3 +38,14 @@ #defaultBrowserState.current { color: #008800; } + +#instantConfirmText { + font-family: inherit; + white-space: pre-wrap; + width: 500px; +} + +#instantConfirmLearnMore { + position: absolute; + bottom: 18px; +} diff --git a/chrome/browser/resources/options/certificate_backup_overlay.html b/chrome/browser/resources/options/certificate_backup_overlay.html index 0565e05..c5b5a18 100644 --- a/chrome/browser/resources/options/certificate_backup_overlay.html +++ b/chrome/browser/resources/options/certificate_backup_overlay.html @@ -32,11 +32,11 @@ <span i18n-content="certificateExportPasswordHelp"></span> </p> - <div class="button-strip"> - <button type="submit" id="certificateBackupOkButton" - i18n-content="ok" disabled></button> + <div class="action-area button-strip"> <button type="reset" id="certificateBackupCancelButton" i18n-content="cancel"></button> + <button type="submit" id="certificateBackupOkButton" + i18n-content="ok" disabled></button> </div> </div> diff --git a/chrome/browser/resources/options/certificate_edit_ca_trust_overlay.html b/chrome/browser/resources/options/certificate_edit_ca_trust_overlay.html index 561c88e..e3e5f6f 100644 --- a/chrome/browser/resources/options/certificate_edit_ca_trust_overlay.html +++ b/chrome/browser/resources/options/certificate_edit_ca_trust_overlay.html @@ -22,10 +22,10 @@ </label> </p> - <div class="button-strip"> - <button type="submit" id="certificateEditCaTrustOkButton" - i18n-content="ok"></button> + <div class="action-area button-strip"> <button type="reset" id="certificateEditCaTrustCancelButton" i18n-content="cancel"></button> + <button type="submit" id="certificateEditCaTrustOkButton" + i18n-content="ok"></button> </div> </div> diff --git a/chrome/browser/resources/options/certificate_import_error_overlay.html b/chrome/browser/resources/options/certificate_import_error_overlay.html index 5ff4b5b..b686920 100644 --- a/chrome/browser/resources/options/certificate_import_error_overlay.html +++ b/chrome/browser/resources/options/certificate_import_error_overlay.html @@ -2,7 +2,7 @@ <h1 id="certificateImportErrorOverlayTitle"></h1> <div id="certificateImportErrorOverlayMessage"></div> <ul id="certificateImportErrorOverlayCertErrors"></ul> - <div class="button-strip"> + <div class="action-area button-strip"> <button type="submit" id="certificateImportErrorOverlayOk" i18n-content="ok"></button> </div> diff --git a/chrome/browser/resources/options/certificate_manager.html b/chrome/browser/resources/options/certificate_manager.html index 496d6d2..187c503 100644 --- a/chrome/browser/resources/options/certificate_manager.html +++ b/chrome/browser/resources/options/certificate_manager.html @@ -9,8 +9,6 @@ <div class="subpages-nav-tabs"> <span i18n-content="personalCertsTabTitle" id="personal-certs-nav-tab" class="inactive-tab" tab-contents="personalCertsTab"></span><span - i18n-content="emailCertsTabTitle" id="email-certs-nav-tab" - class="inactive-tab" tab-contents="emailCertsTab"></span><span i18n-content="serverCertsTabTitle" id="server-certs-nav-tab" class="inactive-tab" tab-contents="serverCertsTab"></span><span i18n-content="caCertsTabTitle" id="ca-certs-nav-tab" @@ -48,32 +46,6 @@ </table> </div> - <div id="emailCertsTab" class="subpages-tab-contents"> - <table class="certificate-tree-table"> - <tr><td> - <span i18n-content="emailCertsTabDescription"></span> - </td></tr> - <tr><td> - <tree id="emailCertsTab-tree" class="certificate-tree"></tree> - </td></tr> - <tr><td> - <button id="emailCertsTab-view" i18n-content="view_certificate" - disabled></button> - <!-- We don't really care about email certificates, so not much point - bothering to implement these... - <button id="emailCertsTab-edit" i18n-content="edit_certificate" - disabled></button> - <button id="emailCertsTab-import" i18n-content="import_certificate" - ></button> - --> - <button id="emailCertsTab-export" i18n-content="export_certificate" - disabled></button> - <button id="emailCertsTab-delete" i18n-content="delete_certificate" - disabled></button> - </td></tr> - </table> - </div> - <div id="serverCertsTab" class="subpages-tab-contents"> <table class="certificate-tree-table"> <tr><td> diff --git a/chrome/browser/resources/options/certificate_manager.js b/chrome/browser/resources/options/certificate_manager.js index 5295bde..145cfcb 100644 --- a/chrome/browser/resources/options/certificate_manager.js +++ b/chrome/browser/resources/options/certificate_manager.js @@ -109,6 +109,7 @@ cr.define('options', function() { */ updateButtonState: function(data) { var isCert = !!data && data.id.substr(0, 5) == 'cert-'; + var readOnly = !!data && data.readonly; var hasChildren = this.tree.items.length > 0; this.viewButton.disabled = !isCert; if (this.editButton !== null) @@ -119,7 +120,7 @@ cr.define('options', function() { this.backupAllButton.disabled = !hasChildren; if (this.exportButton !== null) this.exportButton.disabled = !isCert; - this.deleteButton.disabled = !isCert; + this.deleteButton.disabled = !isCert || readOnly; }, /** @@ -160,7 +161,6 @@ cr.define('options', function() { OptionsPage.prototype.initializePage.call(this); this.personalTab = new CertificateManagerTab('personalCertsTab'); - this.emailTab = new CertificateManagerTab('emailCertsTab'); this.serverTab = new CertificateManagerTab('serverCertsTab'); this.caTab = new CertificateManagerTab('caCertsTab'); this.otherTab = new CertificateManagerTab('otherCertsTab'); diff --git a/chrome/browser/resources/options/certificate_restore_overlay.html b/chrome/browser/resources/options/certificate_restore_overlay.html index 7985ff2..b584982 100644 --- a/chrome/browser/resources/options/certificate_restore_overlay.html +++ b/chrome/browser/resources/options/certificate_restore_overlay.html @@ -10,10 +10,10 @@ </label> </p> - <div class="button-strip"> - <button type="submit" id="certificateRestoreOkButton" - i18n-content="ok"></button> + <div class="action-area button-strip"> <button type="reset" id="certificateRestoreCancelButton" i18n-content="cancel"></button> + <button type="submit" id="certificateRestoreOkButton" + i18n-content="ok"></button> </div> </div> diff --git a/chrome/browser/resources/options/chromeos_accounts_options.js b/chrome/browser/resources/options/chromeos_accounts_options.js index f633931..1cc5b5c 100644 --- a/chrome/browser/resources/options/chromeos_accounts_options.js +++ b/chrome/browser/resources/options/chromeos_accounts_options.js @@ -43,6 +43,9 @@ cr.define('options', function() { userNameEdit.disabled = !AccountsOptions.currentUserIsOwner(); this.addEventListener('visibleChange', this.handleVisibleChange_); + + $('allowGuestCheck').addEventListener('click', + this.handleAllowGuestCheckClick_); }, /** @@ -59,12 +62,23 @@ cr.define('options', function() { }, /** + * Handler for allow guest check click. + * @private + */ + handleAllowGuestCheckClick_: function(e) { + // Whitelist existing users when guest login is being disabled. + if (!$('allowGuestCheck').checked) { + chrome.send('whitelistExistingUsers', []); + } + }, + + /** * Handler for "add" event fired from userNameEdit. * @private * @param {Event} e Add event fired from userNameEdit. */ handleAddUser_: function(e) { - $('userList').addUser(e.user); + AccountsOptions.addUsers([e.user]); } }; @@ -80,7 +94,17 @@ cr.define('options', function() { */ AccountsOptions.setUserPictures = function(cache) { $('userList').setUserPictures(cache); - } + }; + + /** + * Adds given users to userList. + */ + AccountsOptions.addUsers = function(users) { + var userList = $('userList'); + for (var i = 0; i < users.length; ++i) { + userList.addUser(users[i]); + } + }; // Export return { diff --git a/chrome/browser/resources/options/chromeos_internet_detail.html b/chrome/browser/resources/options/chromeos_internet_detail.html index 1bfba82..98c4b46 100644 --- a/chrome/browser/resources/options/chromeos_internet_detail.html +++ b/chrome/browser/resources/options/chromeos_internet_detail.html @@ -23,7 +23,7 @@ <tr> <td colspan="2"> <label class="checkbox"> - <input id="rememberNetwork"type="checkbox"> + <input id="rememberNetwork" type="checkbox"> <span i18n-content="inetRememberNetwork"></span> </label> </td> @@ -61,15 +61,14 @@ <section id="passwordNetwork" class="password-details"> <table class="option-control-table"> <tr> - <td class="option-name" i18n-content="inetPass"></td> - <td class="option-value"><input id="inetPass"></td> + <td class="option-name" i18n-content="inetPassProtected"></td> </tr> </table> </section> </div> <div id="cellularPlanTab" class="subpages-tab-contents cellular-details"> <section> - <table class="option-control-table"> + <table class="option-control-table" id="details-plan-table"> <tr class="plan-loading-info"> <td colspan="2" i18n-content="planLoading" class="option-value"></td> </tr> @@ -104,10 +103,6 @@ </label> </div> </section> - <section> - <div><a id="customerSupport" target="_blank" href="#" - i18n-content="customerSupport"></a></div> - </section> </div> <div id="cellularConnTab" class="subpages-tab-contents cellular-details"> <section id="cellularNetworkOptions"> @@ -231,10 +226,12 @@ </table> </section> </div> - <div class="button-strip"> - <button id="detailsInternetLogin" - i18n-content="inetLogin"></button> + <div class="action-area button-strip"> <button id="detailsInternetDismiss" i18n-content="detailsInternetDismiss"></button> + <button id="detailsInternetOk" + i18n-content="detailsInternetOk"></button> + <button id="detailsInternetLogin" + i18n-content="inetLogin"></button> </div> </div> diff --git a/chrome/browser/resources/options/chromeos_internet_network_element.js b/chrome/browser/resources/options/chromeos_internet_network_element.js index 42c6fb1..7541f2b 100644 --- a/chrome/browser/resources/options/chromeos_internet_network_element.js +++ b/chrome/browser/resources/options/chromeos_internet_network_element.js @@ -47,11 +47,8 @@ cr.define('options.internet', function() { if (e.button == 0) { var el = e.target; // If click is on action buttons of a network item. - if (el.buttonType && el.networkType && el.servicePath) { - chrome.send('buttonClickCallback', - [String(el.networkType), el.servicePath, el.buttonType]); - } else { - if (el.className == 'other-network' || el.buttonType) { + if (!(el.buttonType && el.networkType && el.servicePath)) { + if (el.buttonType) { return; } // If click is on a network item or its label, walk up the DOM tree @@ -65,8 +62,9 @@ cr.define('options.internet', function() { if (item) { var data = item.data; - // Don't try to connect to Ethernet. - if (data && data.networkType == 1) + // Don't try to connect to Ethernet or unactivated Cellular. + if (data && (data.networkType == 1 || + (data.networkType == 5 && data.activation_state != 1))) return; for (var i = 0; i < this.childNodes.length; i++) { if (this.childNodes[i] != item) @@ -105,7 +103,9 @@ cr.define('options.internet', function() { connected: network[4], connecting: network[5], iconURL: network[6], - remembered: network[7] + remembered: network[7], + activation_state: network[8], + restricted: network[9] }; NetworkItem.decorate(el); return el; @@ -117,6 +117,19 @@ cr.define('options.internet', function() { * @type {number} */ NetworkItem.MIN_WIRELESS_PASSWORD_LENGTH = 5; + NetworkItem.MIN_WIRELESS_SSID_LENGTH = 1; + // Cellular activation states: + NetworkItem.ACTIVATION_STATE_UNKNOWN = 0; + NetworkItem.ACTIVATION_STATE_ACTIVATED = 1; + NetworkItem.ACTIVATION_STATE_ACTIVATING = 2; + NetworkItem.ACTIVATION_STATE_NOT_ACTIVATED = 3; + NetworkItem.ACTIVATION_STATE_PARTIALLY_ACTIVATED = 4; + NetworkItem.TYPE_UNKNOWN = 0; + NetworkItem.TYPE_ETHERNET = 1; + NetworkItem.TYPE_WIFI = 2; + NetworkItem.TYPE_WIMAX = 3; + NetworkItem.TYPE_BLUETOOTH = 4; + NetworkItem.TYPE_CELLULAR = 5; /** * Decorates an element as a network item. @@ -132,10 +145,9 @@ cr.define('options.internet', function() { /** @inheritDoc */ decorate: function() { - var isOtherNetworksItem = this.data.servicePath == '?'; - this.className = 'network-item'; this.connected = this.data.connected; + this.other = this.data.servicePath == '?'; this.id = this.data.servicePath; // textDiv holds icon, name and status text. var textDiv = this.ownerDocument.createElement('div'); @@ -149,7 +161,7 @@ cr.define('options.internet', function() { nameEl.textContent = this.data.networkName; textDiv.appendChild(nameEl); - if (isOtherNetworksItem) { + if (this.other) { // No status and buttons for "Other..." this.appendChild(textDiv); return; @@ -170,22 +182,67 @@ cr.define('options.internet', function() { this.appendChild(spacerDiv); var buttonsDiv = this.ownerDocument.createElement('div'); + var self = this; if (!this.data.remembered) { + var no_plan = + this.data.networkType == NetworkItem.TYPE_CELLULAR && + this.data.activation_state == + NetworkItem.ACTIVATION_STATE_ACTIVATED && + this.data.restricted && + this.data.connected; + var show_activate = + (this.data.networkType == NetworkItem.TYPE_CELLULAR && + this.data.activation_state != + NetworkItem.ACTIVATION_STATE_ACTIVATED && + this.data.activation_state != + NetworkItem.ACTIVATION_STATE_ACTIVATING); + + // Disconnect button if not ethernet and if cellular it should be + // activated. + if (this.data.networkType != NetworkItem.TYPE_ETHERNET && + !show_activate && this.data.connected) { + buttonsDiv.appendChild( + this.createButton_('disconnect_button', 'disconnect', + function(e) { + chrome.send('buttonClickCallback', + [String(self.data.networkType), + self.data.servicePath, + 'disconnect']); + })); + } + // Show [Activate] button for non-activated Cellular network. + if (show_activate || no_plan) { + var button_name = no_plan ? 'buyplan_button' : 'activate_button'; + buttonsDiv.appendChild( + this.createButton_(button_name, 'activate', + function(e) { + chrome.send('buttonClickCallback', + [String(self.data.networkType), + self.data.servicePath, + 'activate']); + })); + } if (this.data.connected) { - // disconnect button (if not ethernet) - if (this.data.networkType != 1) - buttonsDiv.appendChild(this.createButton_('disconnect_button', - 'disconnect')); - - // options button - buttonsDiv.appendChild(this.createButton_('options_button', - 'options')); + buttonsDiv.appendChild( + this.createButton_('options_button', 'options', + function(e) { + chrome.send('buttonClickCallback', + [String(self.data.networkType), + self.data.servicePath, + 'options']); + })); } } else { - // forget button - var button = this.createButton_('forget_button', 'forget'); - if (cr.commandLine.options['--bwsi']) { - // no disabling of networks while bwsi. + // Put "Forget this network" button. + var button = this.createButton_('forget_button', 'forget', + function(e) { + chrome.send('buttonClickCallback', + [String(self.data.networkType), + self.data.servicePath, + 'forget']); + }); + if (!AccountsOptions.currentUserIsOwner()) { + // Disable this for guest non-Owners. button.disabled = true; } @@ -194,7 +251,7 @@ cr.define('options.internet', function() { this.appendChild(buttonsDiv); }, - showPassword: function(password) { + showPassword: function() { if (this.connecting) return; var passwordDiv = this.ownerDocument.createElement('div'); @@ -221,8 +278,8 @@ cr.define('options.internet', function() { togglePassCheckbox.target = passInput; togglePassCheckbox.addEventListener('change', this.handleShowPass_); togglePassSpan.textContent = localStrings.getString('inetShowPass'); + togglePassLabel.appendChild(togglePassCheckbox); togglePassLabel.appendChild(togglePassSpan); - passwordDiv.appendChild(togglePassCheckbox); passwordDiv.appendChild(togglePassLabel); // Disable login button if there is no password. @@ -248,11 +305,10 @@ cr.define('options.internet', function() { hidePassword: function() { this.connecting = false; var children = this.childNodes; - for (var i = 0; i < children.length; i++) { - if (children[i].className == 'network-password' || - children[i].className == 'other-network') { + // Remove all password divs starting from the end. + for (var i = children.length-1; i >= 0; i--) { + if (children[i].className == 'network-password') { this.removeChild(children[i]); - return; } } }, @@ -260,24 +316,91 @@ cr.define('options.internet', function() { showOtherLogin: function() { if (this.connecting) return; - var passwordDiv = this.ownerDocument.createElement('div'); - passwordDiv.className = 'other-network'; + + var ssidDiv = this.ownerDocument.createElement('div'); + ssidDiv.className = 'network-password'; var ssidInput = this.ownerDocument.createElement('input'); ssidInput.placeholder = localStrings.getString('inetSsidPrompt'); - passwordDiv.appendChild(ssidInput); + ssidDiv.appendChild(ssidInput); + + var securityDiv = this.ownerDocument.createElement('div'); + securityDiv.className = 'network-password'; + var securityInput = this.ownerDocument.createElement('select'); + var securityNoneOption = this.ownerDocument.createElement('option'); + securityNoneOption.value = 'none'; + securityNoneOption.label = localStrings.getString('inetSecurityNone'); + securityInput.appendChild(securityNoneOption); + var securityWEPOption = this.ownerDocument.createElement('option'); + securityWEPOption.value = 'wep'; + securityWEPOption.label = localStrings.getString('inetSecurityWEP'); + securityInput.appendChild(securityWEPOption); + var securityWPAOption = this.ownerDocument.createElement('option'); + securityWPAOption.value = 'wpa'; + securityWPAOption.label = localStrings.getString('inetSecurityWPA'); + securityInput.appendChild(securityWPAOption); + var securityRSNOption = this.ownerDocument.createElement('option'); + securityRSNOption.value = 'rsn'; + securityRSNOption.label = localStrings.getString('inetSecurityRSN'); + securityInput.appendChild(securityRSNOption); + securityDiv.appendChild(securityInput); + + var passwordDiv = this.ownerDocument.createElement('div'); + passwordDiv.className = 'network-password'; var passInput = this.ownerDocument.createElement('input'); passInput.placeholder = localStrings.getString('inetPassPrompt'); - passInput.addEventListener('keydown', function(e) { - e.returnValue = e.keyCode != ' '.charCodeAt(); - }); + passInput.type = 'password'; + passInput.disabled = true; passwordDiv.appendChild(passInput); - var buttonEl = this.ownerDocument.createElement('button'); - buttonEl.textContent = localStrings.getString('inetLogin'); - buttonEl.buttonType = true; - buttonEl.addEventListener('click', this.handleOtherLogin_); + + var togglePassLabel = this.ownerDocument.createElement('label'); + togglePassLabel.style.display = 'inline'; + var togglePassSpan = this.ownerDocument.createElement('span'); + var togglePassCheckbox = this.ownerDocument.createElement('input'); + togglePassCheckbox.type = 'checkbox'; + togglePassCheckbox.checked = false; + togglePassCheckbox.target = passInput; + togglePassCheckbox.addEventListener('change', this.handleShowPass_); + togglePassSpan.textContent = localStrings.getString('inetShowPass'); + togglePassLabel.appendChild(togglePassCheckbox); + togglePassLabel.appendChild(togglePassSpan); + passwordDiv.appendChild(togglePassLabel); + + var buttonEl = + this.createButton_('inetLogin', true, this.handleOtherLogin_); + buttonEl.style.right = '0'; + buttonEl.style.position = 'absolute'; buttonEl.style.visibility = 'visible'; + buttonEl.disabled = true; passwordDiv.appendChild(buttonEl); + + this.appendChild(ssidDiv); + this.appendChild(securityDiv); this.appendChild(passwordDiv); + + securityInput.addEventListener('change', function(e) { + // If changed to None, then disable passInput and clear it out. + // Otherwise enable it. + if (securityInput.value == 'none') { + passInput.disabled = true; + passInput.value = ''; + } else { + passInput.disabled = false; + } + }); + + var keyup_listener = function(e) { + // Disable login button if ssid is not long enough or + // password is not long enough (unless no security) + var ssid_good = + ssidInput.value.length >= NetworkItem.MIN_WIRELESS_SSID_LENGTH; + var pass_good = + securityInput.value == 'none' || + passInput.value.length >= NetworkItem.MIN_WIRELESS_PASSWORD_LENGTH; + buttonEl.disabled = !ssid_good || !pass_good; + }; + ssidInput.addEventListener('keyup', keyup_listener); + securityInput.addEventListener('change', keyup_listener); + passInput.addEventListener('keyup', keyup_listener); this.connecting = true; }, @@ -292,13 +415,15 @@ cr.define('options.internet', function() { handleOtherLogin_: function(e) { var el = e.target; - var parent = el.parentNode; + var parent = el.parentNode.parentNode; el.disabled = true; - var ssid = parent.childNodes[0]; - var pass = parent.childNodes[1]; + var ssid = parent.childNodes[1].firstChild; + var sec = parent.childNodes[2].firstChild; + var pass = parent.childNodes[3].firstChild; + sec.disabled = true; ssid.disabled = true; pass.disabled = true; - chrome.send('loginToNetwork', [ssid.value, pass.value]); + chrome.send('loginToOtherNetwork', [sec.value, ssid.value, pass.value]); }, /** @@ -306,12 +431,11 @@ cr.define('options.internet', function() { * @param {Object} name The name of the localStrings to use for the text. * @param {Object} type The type of button. */ - createButton_: function(name, type) { + createButton_: function(name, type, callback) { var buttonEl = this.ownerDocument.createElement('button'); - buttonEl.textContent = localStrings.getString(name); buttonEl.buttonType = type; - buttonEl.networkType = this.data.networkType; - buttonEl.servicePath = this.data.servicePath; + buttonEl.textContent = localStrings.getString(name); + buttonEl.addEventListener('click', callback); return buttonEl; } }; @@ -329,6 +453,13 @@ cr.define('options.internet', function() { */ cr.defineProperty(NetworkItem, 'connecting', cr.PropertyKind.BOOL_ATTR); + /** + * Whether the underlying network is an other network for adding networks. + * Only used for display purpose. + * @type {boolean} + */ + cr.defineProperty(NetworkItem, 'other', cr.PropertyKind.BOOL_ATTR); + return { NetworkElement: NetworkElement }; diff --git a/chrome/browser/resources/options/chromeos_internet_options.html b/chrome/browser/resources/options/chromeos_internet_options.html index 2966b1a..4dde7d4 100644 --- a/chrome/browser/resources/options/chromeos_internet_options.html +++ b/chrome/browser/resources/options/chromeos_internet_options.html @@ -3,10 +3,13 @@ <section id="wirelessButtons"> <h3 i18n-content="generalNetworkingTitle"></h3> <div id="networkingControls"> - <button id="enableWifi" i18n-content="enableWifi"></button> - <button id="disableWifi" i18n-content="disableWifi"></button> - <button id="enableCellular" i18n-content="enableCellular"></button> - <button id="disableCellular" i18n-content="disableCellular"></button> + <button id="enableWifi" class="hidden" i18n-content="enableWifi"></button> + <button id="disableWifi" class="hidden" + i18n-content="disableWifi"></button> + <button id="enableCellular" class="hidden" + i18n-content="enableCellular"></button> + <button id="disableCellular" class="hidden" + i18n-content="disableCellular"></button> </div> </section> <section id="wiredSection"> diff --git a/chrome/browser/resources/options/chromeos_internet_options.js b/chrome/browser/resources/options/chromeos_internet_options.js index cd28a6f..b3105e5 100644 --- a/chrome/browser/resources/options/chromeos_internet_options.js +++ b/chrome/browser/resources/options/chromeos_internet_options.js @@ -44,24 +44,30 @@ cr.define('options', function() { $('rememberedSection').hidden = (templateData.rememberedList.length == 0); InternetOptions.setupAttributes(templateData); // Setting up the details page + $('detailsInternetOk').onclick = function(event) { + InternetOptions.setDetails(); + }; $('detailsInternetDismiss').onclick = function(event) { OptionsPage.clearOverlays(); }; - $('detailsInternetLogin').onclick = function(event) { InternetOptions.loginFromDetails(); }; $('enableWifi').onclick = function(event) { + event.target.disabled = true; chrome.send('enableWifi', []); }; $('disableWifi').onclick = function(event) { - chrome.send('disableWifi', []); + event.target.disabled = true; + chrome.send('disableWifi', []); }; $('enableCellular').onclick = function(event) { - chrome.send('enableCellular', []); + event.target.disabled = true; + chrome.send('enableCellular', []); }; $('disableCellular').onclick = function(event) { - chrome.send('disableCellular', []); + event.target.disabled = true; + chrome.send('disableCellular', []); }; $('purchaseMore').onclick = function(event) { chrome.send('buyDataPlan', []); @@ -91,8 +97,7 @@ cr.define('options', function() { if (data.certinpkcs) { chrome.send('loginToCertNetwork',[String(servicePath), String(data.certPath), - String(data.ident), - String(data.certPass)]); + String(data.ident)]); } else { chrome.send('loginToCertNetwork',[String(servicePath), String($('inetCert').value), @@ -107,36 +112,41 @@ cr.define('options', function() { if (data.type == 2) { var newinfo = []; newinfo.push(data.servicePath); - newinfo.push($('rememberNetwork').checked); - if (data.encrypted) { - if (data.certneeded) { - newinfo.push($('inetIdent').value); - newinfo.push($('inetCert').value); - newinfo.push($('inetCertPass').value); - } else { - newinfo.push($('inetPass').value); - } + newinfo.push($('rememberNetwork').checked ? "true" : "false"); + if (data.encrypted && data.certNeeded) { + newinfo.push($('inetIdent').value); + newinfo.push($('inetCert').value); + newinfo.push($('inetCertPass').value); } chrome.send('setDetails', newinfo); } + OptionsPage.clearOverlays(); }; InternetOptions.setupAttributes = function(data) { var buttons = $('wirelessButtons'); if (data.wifiEnabled) { - buttons.setAttribute('wifiEnabled', true); + $('disableWifi').disabled = false; + $('disableWifi').classList.remove('hidden'); + $('enableWifi').classList.add('hidden'); } else { - buttons.removeAttribute('wifiEnabled'); + $('enableWifi').disabled = false; + $('enableWifi').classList.remove('hidden'); + $('disableWifi').classList.add('hidden'); } if (data.cellularAvailable) { - buttons.setAttribute('cellularAvail', true); if (data.cellularEnabled) { - buttons.setAttribute('cellularEnabled', true); + $('disableCellular').disabled = false; + $('disableCellular').classList.remove('hidden'); + $('enableCellular').classList.add('hidden'); } else { - buttons.removeAttribute('cellularEnabled'); + $('enableCellular').disabled = false; + $('enableCellular').classList.remove('hidden'); + $('disableCellular').classList.add('hidden'); } } else { - buttons.removeAttribute('cellularAvail'); + $('enableCellular').classList.add('hidden'); + $('disableCellular').classList.add('hidden'); } }; @@ -226,24 +236,26 @@ cr.define('options', function() { page.removeAttribute('gsm'); $('inetSsid').textContent = data.ssid; $('rememberNetwork').checked = data.autoConnect; + if (!AccountsOptions.currentUserIsOwner()) { + // Disable this for guest non-Owners. + $('rememberNetwork').disabled = true; + } + page.removeAttribute('password'); + page.removeAttribute('cert'); + page.removeAttribute('certPkcs'); if (data.encrypted) { if (data.certNeeded) { - page.setAttribute('cert', true); if (data.certInPkcs) { page.setAttribute('certPkcs', true); $('inetIdentPkcs').value = data.ident; } else { - page.removeAttribute('certPkcs'); + page.setAttribute('cert', true); $('inetIdent').value = data.ident; $('inetCert').value = data.certPath; - $('inetCertPass').value = data.certPass; } } else { - page.removeAttribute('cert'); - $('inetPass').value = data.pass; + page.setAttribute('password', true); } - } else { - page.removeAttribute('cert'); } } else if(data.type == 5) { OptionsPage.showTab($('cellularPlanNavTab')); diff --git a/chrome/browser/resources/options/chromeos_internet_options_page.css b/chrome/browser/resources/options/chromeos_internet_options_page.css index 81378f3..9374dfa 100644 --- a/chrome/browser/resources/options/chromeos_internet_options_page.css +++ b/chrome/browser/resources/options/chromeos_internet_options_page.css @@ -20,7 +20,8 @@ } #networkingControls { - text-align: center; + display: -webkit-box; + -webkit-margin-start: 10px; } .networks { @@ -33,9 +34,8 @@ position: relative; } -.other-network { - left: 0px; - position: relative; +.network-password > input, select { + width: 200px; } .network-item { @@ -73,18 +73,27 @@ display: block; } +.network-item[connecting][other] { + height: 105px; +} + .network-item-text { -webkit-padding-start: 30px; - background: 0 50% no-repeat; + background: left center no-repeat; cursor: default; display: table-cell; - height: 24px; + height: 32px; line-height: 100%; max-width: 320px; overflow: hidden; vertical-align: middle; } +html[dir='rtl'] .network-item-text { + background: right center no-repeat; +} + + .network-item[connected] > * > .network-name-label { font-weight: bold; } @@ -115,12 +124,21 @@ #detailsInternetPage { min-width: 400px; min-height: 360px; + position: relative; } .details-button { float: right; } +#details-plan-table { + width: 100%; +} + +#planSummary { + width: 250px; +} + html[dir='rtl'] .details-button { float: left; } @@ -128,6 +146,7 @@ html[dir='rtl'] .details-button { #detailsInternetPage:not([connected]) > #advancedSection, #detailsInternetPage[connecting] > * > #detailsInternetLogin, #detailsInternetPage[connected] > * > #detailsInternetLogin, +#detailsInternetPage:not([wireless]) > * > #detailsInternetOk, #detailsInternetPage[ethernet] .wifi-details, #detailsInternetPage[ethernet] .cellular-details, #detailsInternetPage[cellular] .wifi-details, @@ -148,15 +167,5 @@ html[dir='rtl'] .details-button { } #wirelessButtons > * > button { - display: block; -} - - -#wirelessButtons[wifiEnabled] > * > #enableWifi, -#wirelessButtons:not([wifiEnabled]) > * > #disableWifi, -#wirelessButtons[cellularEnabled] > * > #enableCellular, -#wirelessButtons:not([cellularEnabled]) > * > #disableCellular, -#wirelessButtons:not([cellularAvail]) > * > #disableCellular, -#wirelessButtons:not([cellularAvail]) > * > #enableCellular { - display: none; + margin-left: 5px; } diff --git a/chrome/browser/resources/options/chromeos_labs.html b/chrome/browser/resources/options/chromeos_labs.html index 66d245b..56f6619 100644 --- a/chrome/browser/resources/options/chromeos_labs.html +++ b/chrome/browser/resources/options/chromeos_labs.html @@ -33,23 +33,6 @@ </div> </section> <section> - <h3 i18n-content="talk_title"></h3> - <div class="option"> - <table class="option-control-table"> - <tr> - <td class="option-name"> - <label class="checkbox"> - <input id="talk-check" - pref="extensions.settings.ggnioahjipcehijkhpdjekioddnjoben.state" - type="checkbox" value-type="number"> - <span i18n-content="talk"></span> - </label> - </td> - </tr> - </table> - </div> - </section> - <section> <h3 i18n-content="side_tabs_title"></h3> <div class="option"> <table class="option-control-table"> diff --git a/chrome/browser/resources/options/chromeos_language_options.html b/chrome/browser/resources/options/chromeos_language_options.html index 1e5dd6f..89531ad 100644 --- a/chrome/browser/resources/options/chromeos_language_options.html +++ b/chrome/browser/resources/options/chromeos_language_options.html @@ -31,9 +31,9 @@ </div> <div id="language-options-ui-notification-bar" class="language-options-notification"> - <div i18n-content="restart_required"></div> - <button id="language-options-ui-restart-button" - i18n-content="restart_button"></button> + <div i18n-content="sign_out_required"></div> + <button id="language-options-ui-sign-out-button" + i18n-content="sign_out_button"></button> </div> <h3 i18n-content="input_method"></h3> <div id="language-options-input-method-list" diff --git a/chrome/browser/resources/options/chromeos_language_options.js b/chrome/browser/resources/options/chromeos_language_options.js index 3474b4f..6b322ab 100644 --- a/chrome/browser/resources/options/chromeos_language_options.js +++ b/chrome/browser/resources/options/chromeos_language_options.js @@ -313,8 +313,8 @@ cr.define('options', function() { uiLanguageButton.onclick = function(e) { chrome.send('uiLanguageChange', [languageCode]); } - $('language-options-ui-restart-button').onclick = function(e) { - chrome.send('uiLanguageRestart'); + $('language-options-ui-sign-out-button').onclick = function(e) { + chrome.send('uiLanguageSignOut'); } } else { // If the language is not supported as UI language, the button diff --git a/chrome/browser/resources/options/chromeos_proxy_options.js b/chrome/browser/resources/options/chromeos_proxy_options.js index d2fcef7..b1e7f16 100644 --- a/chrome/browser/resources/options/chromeos_proxy_options.js +++ b/chrome/browser/resources/options/chromeos_proxy_options.js @@ -91,6 +91,7 @@ cr.define('options', function() { * @param {Event} e Click event. */ disableManual_: function(e) { + $('proxyAllProtocols').disabled = true; $('proxyHostName').disabled = true; $('proxyHostPort').disabled = true; $('proxyHostSingleName').disabled = true; @@ -114,6 +115,7 @@ cr.define('options', function() { * @param {Event} e Click event. */ enableManual_: function(e) { + $('proxyAllProtocols').disabled = false; $('proxyHostName').disabled = false; $('proxyHostPort').disabled = false; $('proxyHostSingleName').disabled = false; diff --git a/chrome/browser/resources/options/chromeos_system_options.js b/chrome/browser/resources/options/chromeos_system_options.js index cef3507..2e99193 100644 --- a/chrome/browser/resources/options/chromeos_system_options.js +++ b/chrome/browser/resources/options/chromeos_system_options.js @@ -32,6 +32,13 @@ cr.define('options', function() { var timezone = $('timezone-select'); if (timezone) { timezone.initializeValues(templateData.timezoneList); + // Disable the timezone setting for the guest mode, as this is a + // system wide setting. + if (cr.commandLine.options['--bwsi']) { + timezone.disabled = true; + // Mark that this is manually disabled. See also pref_ui.js. + timezone.manually_disabled = true; + } } $('language-button').onclick = function(event) { diff --git a/chrome/browser/resources/options/clear_browser_data_overlay.html b/chrome/browser/resources/options/clear_browser_data_overlay.html index a6ae60f..7bf216a 100644 --- a/chrome/browser/resources/options/clear_browser_data_overlay.html +++ b/chrome/browser/resources/options/clear_browser_data_overlay.html @@ -44,19 +44,16 @@ <select id="clearBrowsingDataTimePeriod" pref="browser.clear_data.time_period"></select> </div> - <div class="button-strip"> + <div class="action-area"> <span id="cbdThrobber"></span> - <if expr="os == 'win32'"> + <div class="button-strip"> + <button id="clearBrowsingDataDismiss" i18n-content="cancel" + onclick="ClearBrowserDataOverlay.dismiss();"> + </button> <button id="clearBrowsingDataCommit" - i18n-content="clearBrowsingDataCommit"></button> - </if> - <button id="clearBrowsingDataDismiss" i18n-content="cancel" - onclick="ClearBrowserDataOverlay.dismiss();"> - </button> - <if expr="os != 'win32'"> - <button id="clearBrowsingDataCommit" - i18n-content="clearBrowsingDataCommit"></button> - </if> + i18n-content="clearBrowsingDataCommit"> + </button> + </div> </div> <hr> <a target="_blank" i18n-content="flash_storage_settings" diff --git a/chrome/browser/resources/options/content_settings.css b/chrome/browser/resources/options/content_settings.css index a600a47..52f673c 100644 --- a/chrome/browser/resources/options/content_settings.css +++ b/chrome/browser/resources/options/content_settings.css @@ -6,9 +6,20 @@ found in the LICENSE file. .contentSettingsHeader { /* TODO(dhg): something nice. */ + margin: 0px; + margin-bottom: 1em; } -.exceptionsLink { - font-weight: bold; - cursor: pointer; +.zippy div[mode=otr] { + display: none; +} + +.zippy.show-otr div[mode=otr] { + display: block; + /* background reminiscent of an incognito window, but faded */ + background: -webkit-gradient(linear, + left top, + left bottom, + from(rgba(97, 122, 156, .25)), + to(rgba(82, 108, 145, .25))); } diff --git a/chrome/browser/resources/options/content_settings.html b/chrome/browser/resources/options/content_settings.html index eeb16c2..12640da 100644 --- a/chrome/browser/resources/options/content_settings.html +++ b/chrome/browser/resources/options/content_settings.html @@ -22,7 +22,6 @@ i18n-content="notifications_tab_label" id="notifications-nav-tab" class="inactive-tab" tab-contents="notificationsFilterTab"></span> </div> - <br> <!-- Cookie filter tab contents --> <div id="cookiesFilterTab" class="subpages-tab-contents"> @@ -46,20 +45,25 @@ </tr> </table> - <div class="exceptionsLink" i18n-content="content_exceptions"></div> - <div contentType="cookies" mode="normal"> - <list></list> - </div> - <div contentType="cookies" mode="otr"> - <span i18n-content="otr_exceptions_explanation"></span> - <list></list> + <div class="zippy"> + <div class="zippy-header" i18n-content="content_exceptions"></div> + <div class="zippy-content"> + <div contentType="cookies" mode="normal"> + <list></list> + </div> + <div contentType="cookies" mode="otr"> + <span i18n-content="otr_exceptions_explanation"></span> + <list></list> + </div> + </div> </div> <table class="option-control-table"> <tr> <td class="option-name"> <label class="checkbox"> - <input id="block-third-party-cookies" type="checkbox"> + <input id="block-third-party-cookies" + pref="profile.block_third_party_cookies" type="checkbox"> <span i18n-content="cookies_block_3rd_party"></span> </label> </td> @@ -109,13 +113,17 @@ </tr> </table> - <div class="exceptionsLink" i18n-content="content_exceptions"></div> - <div contentType="images" mode="normal"> - <list></list> - </div> - <div contentType="images" mode="otr"> - <span i18n-content="otr_exceptions_explanation"></span> - <list></list> + <div class="zippy"> + <div class="zippy-header" i18n-content="content_exceptions"></div> + <div class="zippy-content"> + <div contentType="images" mode="normal"> + <list></list> + </div> + <div contentType="images" mode="otr"> + <span i18n-content="otr_exceptions_explanation"></span> + <list></list> + </div> + </div> </div> </div> @@ -141,13 +149,17 @@ </tr> </table> - <div class="exceptionsLink" i18n-content="content_exceptions"></div> - <div contentType="javascript" mode="normal"> - <list></list> - </div> - <div contentType="javascript" mode="otr"> - <span i18n-content="otr_exceptions_explanation"></span> - <list></list> + <div class="zippy"> + <div class="zippy-header" i18n-content="content_exceptions"></div> + <div class="zippy-content"> + <div contentType="javascript" mode="normal"> + <list></list> + </div> + <div contentType="javascript" mode="otr"> + <span i18n-content="otr_exceptions_explanation"></span> + <list></list> + </div> + </div> </div> </div> @@ -181,14 +193,19 @@ </tr> </table> - <div class="exceptionsLink" i18n-content="content_exceptions"></div> - <div contentType="plugins" mode="normal"> - <list></list> - </div> - <div contentType="plugins" mode="otr"> - <span i18n-content="otr_exceptions_explanation"></span> - <list></list> + <div class="zippy"> + <div class="zippy-header" i18n-content="content_exceptions"></div> + <div class="zippy-content"> + <div contentType="plugins" mode="normal"> + <list></list> + </div> + <div contentType="plugins" mode="otr"> + <span i18n-content="otr_exceptions_explanation"></span> + <list></list> + </div> + </div> </div> + <a i18n-content="disable_individual_plugins" id="plugins-tab" href="#"></a> </div> @@ -214,13 +231,17 @@ </tr> </table> - <div class="exceptionsLink" i18n-content="content_exceptions"></div> - <div contentType="popups" mode="normal"> - <list></list> - </div> - <div contentType="popups" mode="otr"> - <span i18n-content="otr_exceptions_explanation"></span> - <list></list> + <div class="zippy"> + <div class="zippy-header" i18n-content="content_exceptions"></div> + <div class="zippy-content"> + <div contentType="popups" mode="normal"> + <list></list> + </div> + <div contentType="popups" mode="otr"> + <span i18n-content="otr_exceptions_explanation"></span> + <list></list> + </div> + </div> </div> </div> @@ -254,9 +275,13 @@ </tr> </table> - <div class="exceptionsLink" i18n-content="content_exceptions"></div> - <div contentType="location" mode="normal"> - <list></list> + <div class="zippy"> + <div class="zippy-header" i18n-content="content_exceptions"></div> + <div class="zippy-content"> + <div contentType="location" mode="normal"> + <list></list> + </div> + </div> </div> </div> @@ -290,9 +315,13 @@ </tr> </table> - <div class="exceptionsLink" i18n-content="content_exceptions"></div> - <div contentType="notifications" mode="normal"> - <list></list> + <div class="zippy"> + <div class="zippy-header" i18n-content="content_exceptions"></div> + <div class="zippy-content"> + <div contentType="notifications" mode="normal"> + <list></list> + </div> + </div> </div> </div> </div> diff --git a/chrome/browser/resources/options/content_settings.js b/chrome/browser/resources/options/content_settings.js index 37dec2b..1a0c89b 100644 --- a/chrome/browser/resources/options/content_settings.js +++ b/chrome/browser/resources/options/content_settings.js @@ -29,33 +29,23 @@ cr.define('options', function() { chrome.send('getContentFilterSettings'); - // Exceptions lists. ----------------------------------------------------- - function handleExceptionsLinkClickEvent(event) { - var exceptionsArea = event.target.parentNode. - querySelector('div[contentType][mode=normal]'); - exceptionsArea.classList.toggle('hidden'); - exceptionsArea.querySelector('list').redraw(); - - var otrExceptionsArea = event.target.parentNode. - querySelector('div[contentType][mode=otr]'); - if (otrExceptionsArea && otrExceptionsArea.otrProfileExists) { - otrExceptionsArea.classList.toggle('hidden'); - otrExceptionsArea.querySelector('list').redraw(); - } - - return false; - } - var exceptionsLinks = - this.pageDiv.querySelectorAll('.exceptionsLink'); - for (var i = 0; i < exceptionsLinks.length; i++) { - exceptionsLinks[i].onclick = handleExceptionsLinkClickEvent; - } - var exceptionsAreas = this.pageDiv.querySelectorAll('div[contentType]'); for (var i = 0; i < exceptionsAreas.length; i++) { options.contentSettings.ExceptionsArea.decorate(exceptionsAreas[i]); } + cr.ui.decorate('.zippy', options.Zippy); + this.pageDiv.addEventListener('measure', function(e) { + if (e.target.classList.contains('zippy')) { + var lists = e.target.querySelectorAll('list'); + for (var i = 0; i < lists.length; i++) { + if (lists[i].redraw) { + lists[i].redraw(); + } + } + } + }); + // Cookies filter page --------------------------------------------------- $('block-third-party-cookies').onclick = function(event) { chrome.send('setAllowThirdPartyCookies', @@ -116,6 +106,18 @@ cr.define('options', function() { document.querySelector('div[contentType=' + type + '][mode=otr]'); exceptionsArea.otrProfileExists = true; + // Find the containing zippy, set it to show OTR profiles, and remeasure it + // to make it smoothly animate to the new size. + var zippy = exceptionsArea; + while (zippy && + (!zippy.classList || !zippy.classList.contains('zippy'))) { + zippy = zippy.parentNode; + } + if (zippy) { + zippy.classList.add('show-otr'); + zippy.remeasure(); + } + var exceptionsList = exceptionsArea.querySelector('list'); exceptionsList.clear(); for (var i = 0; i < list.length; i++) { @@ -128,7 +130,6 @@ cr.define('options', function() { var parentExceptionsArea = document.querySelector('div[contentType=' + type + '][mode=normal]'); if (!parentExceptionsArea.classList.contains('hidden')) { - exceptionsArea.classList.remove('hidden'); exceptionsArea.querySelector('list').redraw(); } }; @@ -137,12 +138,18 @@ cr.define('options', function() { * Clears and hides the incognito exceptions lists. */ ContentSettings.OTRProfileDestroyed = function() { + // Find all zippies, set them to hide OTR profiles, and remeasure them + // to make them smoothly animate to the new size. + var zippies = document.querySelectorAll('.zippy'); + for (var i = 0; i < zippies.length; i++) { + zippies[i].classList.remove('show-otr'); + zippies[i].remeasure(); + } + var exceptionsAreas = document.querySelectorAll('div[contentType][mode=otr]'); - for (var i = 0; i < exceptionsAreas.length; i++) { exceptionsAreas[i].otrProfileExists = false; - exceptionsAreas[i].classList.add('hidden'); exceptionsAreas[i].querySelector('list').clear(); } }; @@ -178,4 +185,3 @@ cr.define('options', function() { }; }); - diff --git a/chrome/browser/resources/options/content_settings_exceptions_area.js b/chrome/browser/resources/options/content_settings_exceptions_area.js index 4d3f4ad..31abc1f 100644 --- a/chrome/browser/resources/options/content_settings_exceptions_area.js +++ b/chrome/browser/resources/options/content_settings_exceptions_area.js @@ -478,8 +478,6 @@ cr.define('options.contentSettings', function() { this.updateButtonSensitivity(); - this.classList.add('hidden'); - this.otrProfileExists = false; }, diff --git a/chrome/browser/resources/options/cookies_view.html b/chrome/browser/resources/options/cookies_view.html index 83ff149..7092290 100644 --- a/chrome/browser/resources/options/cookies_view.html +++ b/chrome/browser/resources/options/cookies_view.html @@ -147,12 +147,6 @@ <table> <tr> <td class="cookie-details-label" - i18n-content="label_indexed_db_name"> - </td> - <td><span id="indexedDBName"></span></td> - </tr> - <tr> - <td class="cookie-details-label" i18n-content="label_indexed_db_origin"> </td> <td><span id="indexedDBOrigin"></span></td> diff --git a/chrome/browser/resources/options/cookies_view.js b/chrome/browser/resources/options/cookies_view.js index 072e89a..e59f5bb 100644 --- a/chrome/browser/resources/options/cookies_view.js +++ b/chrome/browser/resources/options/cookies_view.js @@ -148,7 +148,6 @@ cr.define('options', function() { * Sets IndexedDB info to display. */ setIndexedDBInfo: function(indexedDB) { - $('indexedDBName').textContent = indexedDB.name; $('indexedDBOrigin').textContent = indexedDB.origin; $('indexedDBSize').textContent = indexedDB.size; $('indexedDBLastModified').textContent = indexedDB.modified; diff --git a/chrome/browser/resources/options/edit_search_engine_overlay.css b/chrome/browser/resources/options/edit_search_engine_overlay.css index 8083af5..7cc2088 100644 --- a/chrome/browser/resources/options/edit_search_engine_overlay.css +++ b/chrome/browser/resources/options/edit_search_engine_overlay.css @@ -16,7 +16,7 @@ -webkit-box-sizing: border-box; } -#editSearchEngineOverlay .button-strip { +#editSearchEngineOverlay .action-area { margin-top: 2ex; } diff --git a/chrome/browser/resources/options/edit_search_engine_overlay.html b/chrome/browser/resources/options/edit_search_engine_overlay.html index 85f7096..19c425a 100644 --- a/chrome/browser/resources/options/edit_search_engine_overlay.html +++ b/chrome/browser/resources/options/edit_search_engine_overlay.html @@ -30,17 +30,11 @@ <td></td> </table> - <div class="button-strip"> - <if expr="os == 'win32'"> - <button type="submit" id="editSearchEngineOkayButton" disabled - i18n-content="editSearchEngineOkayButton"></button> - </if> + <div class="action-area button-strip"> <button type="reset" i18n-content="editSearchEngineCancelButton"></button> - <if expr="os != 'win32'"> - <button type="submit" id="editSearchEngineOkayButton" disabled - i18n-content="editSearchEngineOkayButton"></button> - </if> + <button type="submit" id="editSearchEngineOkayButton" disabled + i18n-content="editSearchEngineOkayButton"></button> </div> </form> </div> diff --git a/chrome/browser/resources/options/import_data_overlay.html b/chrome/browser/resources/options/import_data_overlay.html index d0dd2c3..8607a0f 100644 --- a/chrome/browser/resources/options/import_data_overlay.html +++ b/chrome/browser/resources/options/import_data_overlay.html @@ -23,14 +23,11 @@ <label for="import-search" i18n-content="import_search"></label> </div> </div> - <div class="button-strip"> + <div class="action-area"> <span id="import-throbber"></span> - <if expr="os == 'win32'"> + <div class="button-strip"> + <button id="import-data-cancel" i18n-content="cancel"></button> <button id="import-data-commit" i18n-content="import_commit"></button> - </if> - <button id="import-data-cancel" i18n-content="cancel"></button> - <if expr="os != 'win32'"> - <button id="import-data-commit" i18n-content="import_commit"></button> - </if> + </div> </div> </div> diff --git a/chrome/browser/resources/options/import_data_overlay.js b/chrome/browser/resources/options/import_data_overlay.js index 6acbbdb..82b495c 100644 --- a/chrome/browser/resources/options/import_data_overlay.js +++ b/chrome/browser/resources/options/import_data_overlay.js @@ -17,12 +17,12 @@ cr.define('options', function() { 'importDataOverlay'); } - ImportDataOverlay.throbIntervalId = 0 + ImportDataOverlay.throbIntervalId = 0; cr.addSingletonGetter(ImportDataOverlay); ImportDataOverlay.prototype = { - // Inherit ImportDataOverlay from OptionsPage. + // Inherit from OptionsPage. __proto__: OptionsPage.prototype, /** diff --git a/chrome/browser/resources/options/instant_confirm_overlay.html b/chrome/browser/resources/options/instant_confirm_overlay.html new file mode 100644 index 0000000..fe0b8b1 --- /dev/null +++ b/chrome/browser/resources/options/instant_confirm_overlay.html @@ -0,0 +1,12 @@ +<div class="page hidden" id="instantConfirmOverlay"> + <h1 i18n-content="instantConfirmTitle"></h1> + <!-- The text has line breaks, so we must use a pre. --> + <pre id="instantConfirmText" i18n-content="instantConfirmMessage"></pre> + <a target="_blank" i18n-values="href:instantLearnMoreLink" + i18n-content="learnMore" id="instantConfirmLearnMore"></a> + <div class="action-area button-strip"> + <button id="instantConfirmCancel" i18n-content="cancel" + class="cancel-button"></button> + <button id="instantConfirmOk" i18n-content="ok"></button> + </div> +</div> diff --git a/chrome/browser/resources/options/instant_confirm_overlay.js b/chrome/browser/resources/options/instant_confirm_overlay.js new file mode 100644 index 0000000..53d0542 --- /dev/null +++ b/chrome/browser/resources/options/instant_confirm_overlay.js @@ -0,0 +1,44 @@ +// 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. + +cr.define('options', function() { + + var OptionsPage = options.OptionsPage; + + function InstantConfirmOverlay() { + OptionsPage.call(this, 'instantConfirmOverlay', + templateData.instantConfirmTitle, + 'instantConfirmOverlay'); + }; + + cr.addSingletonGetter(InstantConfirmOverlay); + + InstantConfirmOverlay.prototype = { + // Inherit from OptionsPage. + __proto__: OptionsPage.prototype, + + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + $('instantConfirmCancel').onclick = function() { + OptionsPage.clearOverlays(); + }; + $('instantConfirmOk').onclick = function() { + OptionsPage.clearOverlays(); + var instantDialogShown = $('instantDialogShown'); + Preferences.setBooleanPref(instantDialogShown.pref, true, + instantDialogShown.metric); + var instantEnabledCheckbox = $('instantEnableCheckbox'); + Preferences.setBooleanPref(instantEnableCheckbox.pref, true, + instantEnableCheckbox.metric); + }; + }, + }; + + // Export + return { + InstantConfirmOverlay: InstantConfirmOverlay + }; + +}); diff --git a/chrome/browser/resources/options/options_page.css b/chrome/browser/resources/options/options_page.css index 8f97c64..4aa545f 100644 --- a/chrome/browser/resources/options/options_page.css +++ b/chrome/browser/resources/options/options_page.css @@ -55,9 +55,28 @@ html[dir='rtl'] #close-overlay { left: -20px; } -.overlay .button-strip { - padding-top: 20px; - text-align: end; +.action-area { + padding: 12px; + position: absolute; + right: 0px; + bottom: 0px; + display: -webkit-box; + -webkit-box-orient: horizontal; + -webkit-box-align: center; +} + +.button-strip { + display: -webkit-box; + -webkit-box-orient: horizontal; +} + +html[toolkit=views] .button-strip { + -webkit-box-direction: reverse; +} + +.button-strip > button { + display: block; + -webkit-margin-start: 4px; } .overlay > div { @@ -65,6 +84,8 @@ html[dir='rtl'] #close-overlay { border-radius: 5px; padding: 15px; border: 1px solid #666; + padding-bottom: 50px; + position: relative; -webkit-box-shadow: 3px 3px 3px #666; } @@ -173,7 +194,7 @@ html[hide-menu=true] #mainview { } #mainview-content { - width: 550px; + width: 600px; padding: 0 24px; } @@ -202,6 +223,11 @@ html[hide-menu=true] #mainview { width: 24px; height: 21px; display: inline-block; + vertical-align: middle; +} + +#managed-prefs-text { + vertical-align: middle; } .page > h1 { @@ -351,6 +377,18 @@ button { * TODO(arv): Test the vertical position on Linux and CrOS as well. */ +/* + * Webkit does not move the absolute positioned input element properly and + * filed bug 48348 to track the problem. + * https://bugs.webkit.org/show_bug.cgi?id=48348 + * In the mean time, mark the outer label element 'relative' so that webkit + * aligns the input element properly. + */ +label.checkbox, +label.radio { + position: relative; +} + label.checkbox > input, label.radio > input { margin-top: 1px; diff --git a/chrome/browser/resources/options/options_page.js b/chrome/browser/resources/options/options_page.js index 4afad5b..4059117 100644 --- a/chrome/browser/resources/options/options_page.js +++ b/chrome/browser/resources/options/options_page.js @@ -33,7 +33,7 @@ cr.define('options', function() { OptionsPage.registeredSubPages_ = {}; /** - * Pages which are meant to behave like model dialogs. + * Pages which are meant to behave like modal dialogs. */ OptionsPage.registeredOverlayPages_ = {}; @@ -68,6 +68,9 @@ cr.define('options', function() { } }; + /** + * Clears overlays (i.e. hide all overlays). + */ OptionsPage.clearOverlays = function() { for (var name in OptionsPage.registeredOverlayPages_) { var page = OptionsPage.registeredOverlayPages_[name]; @@ -76,6 +79,17 @@ cr.define('options', function() { }; /** + * Clears overlays if the key event is ESC. + * @param {Event} e Key event. + * @private + */ + OptionsPage.clearOverlaysOnEsc_ = function(e) { + if (e.keyCode == 27) { // Esc + OptionsPage.clearOverlays(); + } + }; + + /** * Shows the tab contents for the given navigation tab. * @param {!Element} tab The tab that the user clicked. */ @@ -208,6 +222,8 @@ cr.define('options', function() { if (this.isOverlay) { var overlay = $('overlay'); overlay.classList.remove('hidden'); + document.addEventListener('keydown', + OptionsPage.clearOverlaysOnEsc_); } else { var banner = $('managed-prefs-banner'); banner.style.display = this.managed ? 'block' : 'none'; @@ -225,6 +241,8 @@ cr.define('options', function() { if (this.isOverlay) { var overlay = $('overlay'); overlay.classList.add('hidden'); + document.removeEventListener('keydown', + OptionsPage.clearOverlaysOnEsc_); } this.pageDiv.style.display = 'none'; if (this.tab) { diff --git a/chrome/browser/resources/options/pref_ui.js b/chrome/browser/resources/options/pref_ui.js index a0a656a..4f76bc9 100644 --- a/chrome/browser/resources/options/pref_ui.js +++ b/chrome/browser/resources/options/pref_ui.js @@ -25,10 +25,10 @@ cr.define('options', function() { // Listen to pref changes. Preferences.getInstance().addEventListener(this.pref, function(event) { - var value = (event.value && event.value['value'] != undefined) ? + var value = event.value && event.value['value'] != undefined ? event.value['value'] : event.value; self.checked = Boolean(value); - self.managed = (event.value && event.value['managed'] != undefined) ? + self.managed = event.value && event.value['managed'] != undefined ? event.value['managed'] : false; self.disabled = self.managed; }); @@ -42,7 +42,7 @@ cr.define('options', function() { Number(self.checked), self.metric); break; case 'boolean': - Preferences.setBooleanPref(self.pref, self.checked, + Preferences.setBooleanPref(self.pref, self.checked, self.metric); break; } @@ -96,9 +96,9 @@ cr.define('options', function() { // Listen to pref changes. Preferences.getInstance().addEventListener(this.pref, function(event) { - var value = (event.value && event.value['value'] != undefined) ? + var value = event.value && event.value['value'] != undefined ? event.value['value'] : event.value; - self.managed = (event.value && event.value['managed'] != undefined) ? + self.managed = event.value && event.value['managed'] != undefined ? event.value['managed'] : false; self.checked = String(value) == self.value; self.disabled = self.managed; @@ -156,9 +156,9 @@ cr.define('options', function() { // Listen to pref changes. Preferences.getInstance().addEventListener(this.pref, function(event) { - self.value = (event.value && event.value['value'] != undefined) ? + self.value = event.value && event.value['value'] != undefined ? event.value['value'] : event.value; - self.managed = (event.value && event.value['managed'] != undefined) ? + self.managed = event.value && event.value['managed'] != undefined ? event.value['managed'] : false; self.disabled = self.managed; }); @@ -268,11 +268,16 @@ cr.define('options', function() { // Listen to pref changes. Preferences.getInstance().addEventListener(this.pref, function(event) { - var value = (event.value && event.value['value'] != undefined) ? + var value = event.value && event.value['value'] != undefined ? event.value['value'] : event.value; - self.managed = (event.value && event.value['managed'] != undefined) ? + self.managed = event.value && event.value['managed'] != undefined ? event.value['managed'] : false; self.disabled = self.managed; + // Honor manually_disabled property, so options pages can + // disable preferences manually when needed. + if (self.manually_disabled) { + self.disabled = true; + } for (var i = 0; i < self.options.length; i++) { if (self.options[i].value == value) { self.selectedIndex = i; @@ -360,9 +365,9 @@ cr.define('options', function() { // Listen to pref changes. Preferences.getInstance().addEventListener(this.pref, function(event) { - self.value = (event.value && event.value['value'] != undefined) ? + self.value = event.value && event.value['value'] != undefined ? event.value['value'] : event.value; - self.managed = (event.value && event.value['managed'] != undefined) ? + self.managed = event.value && event.value['managed'] != undefined ? event.value['managed'] : false; self.disabled = self.managed; }); diff --git a/chrome/browser/resources/options/subpages_tab_controls.css b/chrome/browser/resources/options/subpages_tab_controls.css index 0fc7d21..273078a 100644 --- a/chrome/browser/resources/options/subpages_tab_controls.css +++ b/chrome/browser/resources/options/subpages_tab_controls.css @@ -10,7 +10,6 @@ found in the LICENSE file. left bottom, from(white), to(#f3f3f3)); - border-bottom: 1px solid #a0a0a0; padding: 4px 8px; } diff --git a/chrome/browser/resources/options/zippy.css b/chrome/browser/resources/options/zippy.css new file mode 100644 index 0000000..3d1d716 --- /dev/null +++ b/chrome/browser/resources/options/zippy.css @@ -0,0 +1,54 @@ +/* +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. +*/ + +.zippy { + margin-top: 1ex; + margin-bottom: 1ex; +} + +.zippy > .zippy-header { + font-weight: bold; + cursor: pointer; + background-color: #DFDFDF; /* medium light gray */ + background-image: url("../shared/images/minus.png"); + background-position: 3px 3px; + background-repeat: no-repeat; + -webkit-padding-start: 18px; + min-height: 18px; +} + +.zippy.collapsed > .zippy-header { + background-image: url("../shared/images/plus.png"); +} + +html[dir=rtl] .zippy > .zippy-header { + background-position: 3px right; +} + +.zippy .zippy-content { + background: #EFEFEF; /* extra light gray */ + padding-left: 3px; + padding-right: 3px; + overflow: hidden; +} + +.zippy.animated .zippy-content { + -webkit-transition: all .15s ease-in-out; +} + +.zippy.collapsed .zippy-content { + opacity: 0; + height: 0 !important; /* must override height in DOM style parameter */ +} + +.zippy.measure .zippy-content { + visibility: hidden; + position: absolute; +} + +.zippy.remeasure .zippy-content { + visibility: hidden; +} diff --git a/chrome/browser/resources/options/zippy.js b/chrome/browser/resources/options/zippy.js new file mode 100644 index 0000000..1259391 --- /dev/null +++ b/chrome/browser/resources/options/zippy.js @@ -0,0 +1,202 @@ +// 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. + +// A zippy is an area with a clickable header that controls whether the rest of +// the area is displayed or not. In most zippies the header contains a +/- sign +// to indicate the current state, and also to suggest that the content can be +// expanded (when collapsed) or collapsed (when expanded). + +// This implementation smoothly animates the content of the zippy when it is +// expanded and collapsed, using the -webkit-transition style attribute. + +cr.define('options', function() { + + ////////////////////////////////////////////////////////////////////////////// + // Zippy class: + + var Zippy = cr.ui.define('div'); + + Zippy.prototype = { + __proto__: HTMLDivElement.prototype, + + /** + * Initialization function for the cr.ui framework. + */ + decorate: function() { + this.classList.add('collapsed'); + this.classList.add('measure'); + var self = this; + this.addEventListener('click', function(e) { + var target = e.target; + while (target && !target.classList.contains('zippy')) { + if (target.classList.contains('zippy-header')) { + self.toggle(); + break; + } + target = target.parentNode; + } + }); + }, + + /** + * Toggle the expanded state of this zippy. + */ + toggle: function() { + this.expanded = !this.expanded; + }, + + /** + * Do an initial measure of this zippy. + * + * This function is called just after making the zippy's contents visible + * for the first time, and sets the calculated size into the zippy's style + * attribute. This is needed to allow the smooth animation work correctly, + * as 'auto' heights cannot be used in animations. + * + * We also dispatch a bubbling 'measure' event, allowing content to render + * itself only after the user requests it to be shown in the first place. + */ + measure: function() { + cr.dispatchSimpleEvent(this, 'measure', true); // true = allow bubbling + var contents = this.querySelectorAll('.zippy-content'); + for (var i = 0; i < contents.length; i++) { + contents[i].style.height = 'auto'; + } + // Use a separate loop to avoid relayout after each one. + for (var i = 0; i < contents.length; i++) { + contents[i].style.height = contents[i].offsetHeight + 'px'; + } + // Enable animation only after setting a fixed size. + this.classList.add('animated'); + // Collapse, disable measure mode, and then re-expand. + this.classList.add('collapsed'); + this.classList.remove('measure'); + this.classList.remove('collapsed'); + }, + + /** + * Remeasure this zippy. + * + * This works by disabling animation, setting the measure property to hide + * the zippy, letting its size auto-adjust, and then explicitly setting the + * size to the natural size. This is required to allow smooth transitions + * when the contents change size. + * + * Special care must be taken when remeasuring currently hidden zippies + * (that is, when the entire zippy is hidden, not just when it's collapsed), + * because the contents cannot be measured when the zippy is not displayed. + */ + remeasure: function() { + var contents = this.querySelectorAll('.zippy-content'); + + if (this.classList.contains('collapsed')) { + // Not currently expanded, set the measure class to remeasure later. + this.classList.add('measure'); + // Disable animation in case it was previously measured. + this.classList.remove('animated'); + return; + } + // There is still the possibility that the zippy is expanded but not + // visible, so we will need to take special care to detect this case. + + // First disable animation and get the current heights. + this.classList.remove('animated'); + var oldHeights = []; + for (var i = 0; i < contents.length; i++) { + oldHeights[i] = contents[i].style.height; + } + + // This will hide the contents but make them resize correctly, unless they + // are in a zippy which is itself not displayed. + this.classList.add('remeasure'); + for (var i = 0; i < contents.length; i++) { + contents[i].style.height = 'auto'; + } + + // Calculate the new heights and figure out if any are nonzero. + var newHeights = []; + var nonzero = false; + for (var i = 0; i < contents.length; i++) { + newHeights[i] = contents[i].offsetHeight; + if (contents[i].offsetHeight) { + nonzero = true; + } + } + + // There were no nonzero heights. We assume we're not currently showing, + // yet expanded. Set heights to auto, leave visible, and set the content + // to remeasure next time it is collapsed. + if (!nonzero) { + this.classList.remove('remeasure'); + // This function will be called before collapsing this zippy. + this.precollapse = function() { + for (var i = 0; i < contents.length; i++) { + contents[i].style.height = contents[i].offsetHeight + 'px'; + var x = contents[i].offsetHeight; // Just need to access it. + } + // Only now can we re-enable animation. + this.classList.add('animated'); + this.precollapse = null; + }; + return; + } + + // Restore the old heights so we can animate starting from them. + for (var i = 0; i < contents.length; i++) { + contents[i].style.height = oldHeights[i]; + } + + // Reshow the contents and force re-layout to avoid incorrect animation. + this.classList.remove('remeasure'); + for (var i = 0; i < contents.length; i++) { + var x = contents[i].offsetHeight; // Just need to access it. + } + + // Now enable animation and set the new heights. + this.classList.add('animated'); + for (var i = 0; i < contents.length; i++) { + if (newHeights[i]) { + contents[i].style.height = newHeights[i] + 'px'; + } + } + }, + + /** + * Returns true if this zippy is currently expanded. + * + * @type {boolean} + */ + get expanded() { + return !this.classList.contains('collapsed'); + }, + + /** + * Set the expanded state of this zippy. + * + * This mostly just toggles the 'collapsed' class on the zippy, but + * it also checks for and runs other code needed to help the smooth + * animation calculate the height of the contents correctly. + * + * @param {boolean} state Whether to expand the zippy. + */ + set expanded(state) { + if (this.expanded == state) { + return; + } + if (this.precollapse && !state) { + this.precollapse(); + } + this.classList.toggle('collapsed'); + if (this.classList.contains('measure')) { + this.measure(); + } + }, + }; + + // Export + return { + Zippy: Zippy + }; + +}); diff --git a/chrome/browser/resources/print_preview.html b/chrome/browser/resources/print_preview.html index 9ce2efb..d1eaeb2 100644 --- a/chrome/browser/resources/print_preview.html +++ b/chrome/browser/resources/print_preview.html @@ -9,18 +9,16 @@ <script src="chrome://resources/js/local_strings.js"></script> <script src="chrome://resources/js/util.js"></script> - +<script src="print_preview.js"></script> </head> <body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize"> <div id="navbar-container"> <div id="destination-container"> <h1>Destination</h1> - <div id="destination-list"> - LIST GOES HERE - </div> - <div id="print-buttons"> - <button id="printButton">Print</button> - <button id="cancelButton">Cancel</button> + <select id="printer-list"></select> + <div id="buttons"> + <button id="print-button">Print</button> + <button id="cancel-button">Cancel</button> </div> </div> <div id="options-container"> diff --git a/chrome/browser/resources/print_preview.js b/chrome/browser/resources/print_preview.js new file mode 100644 index 0000000..4fcf953 --- /dev/null +++ b/chrome/browser/resources/print_preview.js @@ -0,0 +1,34 @@ +// 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. + +var localStrings = new LocalStrings(); + +/** + * Window onload handler, sets up the page. + */ +function load() { + chrome.send('getPrinters'); +}; + +/** + * Fill the printer list drop down. + */ +function setPrinters(printers) { + if (printers.length > 0) { + for (var i = 0; i < printers.length; ++i) { + var option = document.createElement('option'); + option.textContent = printers[i]; + $('printer-list').add(option); + } + } else { + var option = document.createElement('option'); + option.textContent = localStrings.getString('no-printer'); + $('printer-list').add(option); + $('printer-list').disabled = true; + $('print-button').disabled = true; + } +} + +window.addEventListener('DOMContentLoaded', load); + diff --git a/chrome/browser/resources/safe_browsing_malware_block.html b/chrome/browser/resources/safe_browsing_malware_block.html index d8a5a39..8bd95a0 100644 --- a/chrome/browser/resources/safe_browsing_malware_block.html +++ b/chrome/browser/resources/safe_browsing_malware_block.html @@ -58,6 +58,7 @@ input { .helpbutton { float:right; } + .example { margin: 30px 90px 0px; border-top:1px solid #ccc; @@ -67,6 +68,114 @@ input { margin-left:5px; margin-right:5px; } + +.nicebutton { + color: #fff; + font-size: 11pt; + font-weight: bold; + padding: 7px 20px; + border-radius: 5px; + outline-width: 0; + border: 1px solid #4C7336; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3), + inset 0px 1px 0px rgba(255, 255, 255, 0.5); + text-shadow: -1px -1px 0px rgba(0, 0, 0, 0.2); +} + +#back { + border: 1px solid #4C7336; + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #81C05C), + color-stop(1.0, #59A02F)); +} + +#more { + border: 1px solid #23517B; + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #5E91C1), + color-stop(1.0, #2E689E)); +} + +#less { + border: 1px solid #23517B; + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #5E91C1), + color-stop(1.0, #2E689E)); +} + +#back:hover { + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #8CCE62), + color-stop(1.0, #59A02F)); +} + +#back:active { + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #59A02F), + color-stop(1.0, #8CCE62)); +} + +#back:focus { + border: 1px solid #000; + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #8CCE62), + color-stop(1.0, #59A02F)); +} + +#more:hover{ + border: 1px solid #23517B; + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #68A1D6), + color-stop(1.0, #2E689E)); +} + +#less:hover{ + border: 1px solid #23517B; + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #68A1D6), + color-stop(1.0, #2E689E)); +} + +#more:active{ + border: 1px solid #23517B; + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #2E689E), + color-stop(1.0, #68A1D6)); +} + +#less:active { + border: 1px solid #23517B; + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #2E689E), + color-stop(1.0, #68A1D6)); +} + +#more:focus { + border: 1px solid #000; + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #68A1D6), + color-stop(1.0, #2E689E)); +} + +#less:focus { + border: 1px solid #000; + background-image: -webkit-gradient(linear, + left top, left bottom, + color-stop(0.3, #68A1D6), + color-stop(1.0, #2E689E)); +} + </style> <script> @@ -78,6 +187,22 @@ input { function agreed(form) { form.continue_button.disabled = !form.continue_button.disabled; } + + function toggle() { + var moreButton = document.getElementById('more') + var lessButton = document.getElementById('less') + var content = document.getElementById('detailinfo') + if (content.style.display == 'none'){ + content.style.display = 'block' + moreButton.style.display = 'none' + lessButton.style.display = 'inline' + } else { + content.style.display = 'none' + moreButton.style.display = 'inline' + lessButton.style.display = 'none' + } + + } </script> </head> <body oncontextmenu="return false;"> @@ -87,16 +212,21 @@ input { <div class="box"> <div class="icon"><img src="shared/images/phishing_icon.png" alt="Malware Icon" onmousedown="return false;"/></div> <div class="title" i18n-content="headLine"></div> - <div class="main" i18n-values=".innerHTML:description1"></div> + <div class="main" i18n-values=".innerHTML:description1" style="margin-bottom:15px"></div> <div class="main" i18n-values=".innerHTML:description2"></div> - <div class="main"><a href="" i18n-content="description3" onclick="sendCommand('learnMore'); return false;" onmousedown="return false;"></a></div> + <div class="main"> <form class="submission"> - <input name="checky" id="checky" type="checkbox" onclick="agreed(this.form)"> <label for="checky" i18n-content="confirm_text"></label> - <input type="button" name="continue_button" i18n-values="value:continue_button" disabled="true" onclick="sendCommand('proceed')"><br> - <input type="button" name="back_button" i18n-values="value:back_button" onclick="sendCommand('takeMeBack')"> + <input type="button" class="nicebutton" id="back" i18n-values="value:back_button" onclick="sendCommand('takeMeBack')"> + <input type="button" class="nicebutton" id="more" i18n-values="value:more_info_button" onclick="toggle()"> + <input type="button" class="nicebutton" id="less" i18n-values="value:less_info_button" style="display:none" onclick="toggle()"> + <br /> </form> + <span i18n-values=".innerHTML:description5" id="detailinfo" style="display:none"></span> </div> + + <div class="main" i18n-values=".innerHTML:description3" style="margin-top:10px"></div> + </div> </td> </table> diff --git a/chrome/browser/resources/shared/js/cr.js b/chrome/browser/resources/shared/js/cr.js index 25ba498..a470234 100644 --- a/chrome/browser/resources/shared/js/cr.js +++ b/chrome/browser/resources/shared/js/cr.js @@ -23,6 +23,12 @@ const cr = (function() { const isChromeOS = /CrOS/.test(navigator.userAgent); /** + * Whether this uses the views toolkit or not. + * @type {boolean} + */ + const isViews = isWindows || isChromeOS; + + /** * Builds an object structure for the provided namespace path, * ensuring that names that already exist are not overwritten. For * example: diff --git a/chrome/browser/resources/shared/js/cr/link_controller.js b/chrome/browser/resources/shared/js/cr/link_controller.js index e6241de..07e323f 100644 --- a/chrome/browser/resources/shared/js/cr/link_controller.js +++ b/chrome/browser/resources/shared/js/cr/link_controller.js @@ -134,17 +134,8 @@ cr.define('cr', function() { var incognito = kind == LinkKind.INCOGNITO; if (kind == LinkKind.WINDOW || incognito) { chrome.windows.create({ - url: urls[0], + url: urls, incognito: incognito - }, function(window) { - urls.forEach(function(url, i) { - if (i > 0) - chrome.tabs.create({ - url: url, - windowId: window.id, - selected: false - }); - }); }); } else if (kind == LinkKind.FOREGROUND_TAB || kind == LinkKind.BACKGROUND_TAB) { diff --git a/chrome/browser/resources/textfields.html b/chrome/browser/resources/textfields.html new file mode 100644 index 0000000..b716006 --- /dev/null +++ b/chrome/browser/resources/textfields.html @@ -0,0 +1,49 @@ +<!DOCTYPE HTML> +<html i18n-values='dir:textdirection;'> +<style> + +html, body { + margin: 0; + overflow: hidden; +} + +input { + bottom: 0; + left: 0; + margin: 0; + position: absolute; + right: 0; + top: 0; +} + +</style> + +<body> + <input> +</body> + +<script> + +var textfield = document.querySelector('input'); +textfield.addEventListener('input', sendTextfieldValueToBrowser); + +/** + * Sends the textfield value to the browser. Called whenever the user presses a + * key. We first check if the key-press has really changed the text, then send + * the new value to the browser if so. + */ +function sendTextfieldValueToBrowser() { + chrome.send('textfieldValue', [textfield.value]); +} + +/** + * Sets textfield value + * @param {string} value + */ +function setTextfieldValue(value) { + textfield.value = value; + sendTextfieldValueToBrowser(); +} + +</script> +</html> diff --git a/chrome/browser/rlz/rlz.cc b/chrome/browser/rlz/rlz.cc index 2ba1ed0..1a22306 100644 --- a/chrome/browser/rlz/rlz.cc +++ b/chrome/browser/rlz/rlz.cc @@ -19,6 +19,7 @@ #include "base/string_util.h" #include "base/task.h" #include "base/thread.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profile.h" @@ -161,6 +162,9 @@ class DelayedInitTask : public Task { virtual ~DelayedInitTask() { } virtual void Run() { + // Needs to be evaluated. See http://crbug.com/62328/. + base::ThreadRestrictions::ScopedAllowIO allow_io; + // For non-interactive tests we don't do the rest of the initialization // because sometimes the very act of loading the dll causes QEMU to crash. if (::GetEnvironmentVariableW(ASCIIToWide(env_vars::kHeadless).c_str(), diff --git a/chrome/browser/safe_browsing/bloom_filter.cc b/chrome/browser/safe_browsing/bloom_filter.cc index 83d3e96..53f92bf 100644 --- a/chrome/browser/safe_browsing/bloom_filter.cc +++ b/chrome/browser/safe_browsing/bloom_filter.cc @@ -42,7 +42,8 @@ int BloomFilter::FilterSizeForKeyCount(int key_count) { // static void BloomFilter::RecordFailure(FailureType failure_type) { - UMA_HISTOGRAM_ENUMERATION("SB2.BloomFailure", failure_type, FAILURE_MAX); + UMA_HISTOGRAM_ENUMERATION("SB2.BloomFailure", failure_type, + FAILURE_FILTER_MAX); } BloomFilter::BloomFilter(int bit_size) { diff --git a/chrome/browser/safe_browsing/bloom_filter.h b/chrome/browser/safe_browsing/bloom_filter.h index f052118..fd69c0d 100644 --- a/chrome/browser/safe_browsing/bloom_filter.h +++ b/chrome/browser/safe_browsing/bloom_filter.h @@ -87,9 +87,9 @@ class BloomFilter : public base::RefCountedThreadSafe<BloomFilter> { FAILURE_FILTER_READ_DATA_SHORT, FAILURE_FILTER_READ_DATA, - // Histogram space is determined by the max. If this is exceeded, - // simply start a new histogram. - FAILURE_MAX = 50 + // Memory space for histograms is determined by the max. ALWAYS + // ADD NEW VALUES BEFORE THIS ONE. + FAILURE_FILTER_MAX }; static void RecordFailure(FailureType failure_type); diff --git a/chrome/browser/safe_browsing/bloom_filter_unittest.cc b/chrome/browser/safe_browsing/bloom_filter_unittest.cc index df44235..97c1558 100644 --- a/chrome/browser/safe_browsing/bloom_filter_unittest.cc +++ b/chrome/browser/safe_browsing/bloom_filter_unittest.cc @@ -30,8 +30,8 @@ TEST(SafeBrowsingBloomFilter, BloomFilterUse) { int count = 1000; // Build up the bloom filter. - scoped_refptr<BloomFilter> filter = - new BloomFilter(count * BloomFilter::kBloomFilterSizeRatio); + scoped_refptr<BloomFilter> filter( + new BloomFilter(count * BloomFilter::kBloomFilterSizeRatio)); typedef std::set<SBPrefix> Values; Values values; @@ -44,8 +44,8 @@ TEST(SafeBrowsingBloomFilter, BloomFilterUse) { // Check serialization works. char* data_copy = new char[filter->size()]; memcpy(data_copy, filter->data(), filter->size()); - scoped_refptr<BloomFilter> filter_copy = - new BloomFilter(data_copy, filter->size(), filter->hash_keys_); + scoped_refptr<BloomFilter> filter_copy( + new BloomFilter(data_copy, filter->size(), filter->hash_keys_)); // Check no false negatives by ensuring that every time we inserted exists. for (Values::const_iterator i = values.begin(); i != values.end(); ++i) @@ -74,16 +74,16 @@ TEST(SafeBrowsingBloomFilter, BloomFilterUse) { double fp_rate = found_count * 100.0 / count; CHECK(fp_rate < 5.0); - SB_DLOG(INFO) << "For safe browsing bloom filter of size " << count << - ", the FP rate was " << fp_rate << " %"; + VLOG(1) << "For safe browsing bloom filter of size " << count + << ", the FP rate was " << fp_rate << " %"; } // Test that we can read and write the bloom filter file. TEST(SafeBrowsingBloomFilter, BloomFilterFile) { // Create initial filter. const int kTestEntries = BloomFilter::kBloomFilterMinSize; - scoped_refptr<BloomFilter> filter_write = - new BloomFilter(kTestEntries * BloomFilter::kBloomFilterSizeRatio); + scoped_refptr<BloomFilter> filter_write( + new BloomFilter(kTestEntries * BloomFilter::kBloomFilterSizeRatio)); for (int i = 0; i < kTestEntries; ++i) filter_write->Insert(GenHash()); @@ -99,7 +99,7 @@ TEST(SafeBrowsingBloomFilter, BloomFilterFile) { // Create new empty filter and load from disk. BloomFilter* filter = BloomFilter::LoadFile(filter_path); ASSERT_TRUE(filter != NULL); - scoped_refptr<BloomFilter> filter_read = filter; + scoped_refptr<BloomFilter> filter_read(filter); // Check data consistency. EXPECT_EQ(filter_write->hash_keys_.size(), filter_read->hash_keys_.size()); diff --git a/chrome/browser/safe_browsing/chunk_range.cc b/chrome/browser/safe_browsing/chunk_range.cc index 101cb5a..b2f60ba 100644 --- a/chrome/browser/safe_browsing/chunk_range.cc +++ b/chrome/browser/safe_browsing/chunk_range.cc @@ -23,23 +23,30 @@ ChunkRange::ChunkRange(const ChunkRange& rhs) // Helper functions ----------------------------------------------------------- -// Traverse the chunks vector looking for contiguous integers. -void ChunksToRanges(const std::vector<int>& chunks, - std::vector<ChunkRange>* ranges) { - DCHECK(ranges); - for (size_t i = 0; i < chunks.size(); ++i) { - int start = static_cast<int>(i); - int next = start + 1; - while (next < static_cast<int>(chunks.size()) && - (chunks[start] == chunks[next] - 1 || - chunks[start] == chunks[next])) { - ++start; - ++next; +void ChunksToRangeString(const std::vector<int>& chunks, std::string* result) { + // The following code requires the range to be sorted. + std::vector<int> sorted_chunks(chunks); + std::sort(sorted_chunks.begin(), sorted_chunks.end()); + + DCHECK(result); + result->clear(); + std::vector<int>::const_iterator iter = sorted_chunks.begin(); + while (iter != sorted_chunks.end()) { + const int range_begin = *iter; + int range_end = *iter; + + // Extend the range forward across duplicates and increments. + for (; iter != sorted_chunks.end() && *iter <= range_end + 1; ++iter) { + range_end = *iter; + } + + if (!result->empty()) + result->append(","); + result->append(base::IntToString(range_begin)); + if (range_end > range_begin) { + result->append("-"); + result->append(base::IntToString(range_end)); } - ranges->push_back(ChunkRange(chunks[i], chunks[start])); - if (next >= static_cast<int>(chunks.size())) - break; - i = start; } } @@ -54,23 +61,6 @@ void RangesToChunks(const std::vector<ChunkRange>& ranges, } } -void RangesToString(const std::vector<ChunkRange>& ranges, - std::string* result) { - DCHECK(result); - result->clear(); - std::vector<ChunkRange>::const_iterator it = ranges.begin(); - for (; it != ranges.end(); ++it) { - if (!result->empty()) - result->append(","); - if (it->start() == it->stop()) { - std::string num_buf = base::IntToString(it->start()); - result->append(num_buf); - } else { - result->append(StringPrintf("%d-%d", it->start(), it->stop())); - } - } -} - bool StringToRanges(const std::string& input, std::vector<ChunkRange>* ranges) { DCHECK(ranges); diff --git a/chrome/browser/safe_browsing/chunk_range.h b/chrome/browser/safe_browsing/chunk_range.h index ace591f..cc6d92f 100644 --- a/chrome/browser/safe_browsing/chunk_range.h +++ b/chrome/browser/safe_browsing/chunk_range.h @@ -42,25 +42,20 @@ class ChunkRange { // Helper functions ------------------------------------------------------------ -// Convert a series of chunk numbers into a more compact range representation. -// The 'chunks' vector must be sorted in ascending order. -void ChunksToRanges(const std::vector<int>& chunks, - std::vector<ChunkRange>* ranges); - // Convert a set of ranges into individual chunk numbers. void RangesToChunks(const std::vector<ChunkRange>& ranges, std::vector<int>* chunks); -// Convert a series of chunk ranges into a string in protocol format. -void RangesToString(const std::vector<ChunkRange>& ranges, - std::string* result); - // Returns 'true' if the string was successfully converted to ChunkRanges, // 'false' if the input was malformed. // The string must be in the form: "1-100,398,415,1138-2001,2019". bool StringToRanges(const std::string& input, std::vector<ChunkRange>* ranges); +// Convenience for going from a list of chunks to a string in protocol +// format. +void ChunksToRangeString(const std::vector<int>& chunks, std::string* result); + // Tests if a chunk number is contained a sorted vector of ChunkRanges. bool IsChunkInRange(int chunk_number, const std::vector<ChunkRange>& ranges); diff --git a/chrome/browser/safe_browsing/chunk_range_unittest.cc b/chrome/browser/safe_browsing/chunk_range_unittest.cc index a31faff..67686b9 100644 --- a/chrome/browser/safe_browsing/chunk_range_unittest.cc +++ b/chrome/browser/safe_browsing/chunk_range_unittest.cc @@ -7,38 +7,10 @@ #include "chunk_range.h" #include "testing/gtest/include/gtest/gtest.h" -// Test formatting chunks into a string representation. -TEST(SafeBrowsingChunkRangeTest, TestRangesToString) { - std::vector<ChunkRange> ranges; - ranges.push_back(ChunkRange(1, 10)); - ranges.push_back(ChunkRange(15, 17)); - ranges.push_back(ChunkRange(21, 410)); - ranges.push_back(ChunkRange(991, 1000)); - - std::string range_string; - RangesToString(ranges, &range_string); - EXPECT_EQ(range_string, "1-10,15-17,21-410,991-1000"); - ranges.clear(); - - ranges.push_back(ChunkRange(4, 4)); - RangesToString(ranges, &range_string); - EXPECT_EQ(range_string, "4"); - - ranges.push_back(ChunkRange(7)); - ranges.push_back(ChunkRange(9)); - RangesToString(ranges, &range_string); - EXPECT_EQ(range_string, "4,7,9"); - - ranges.push_back(ChunkRange(42, 99)); - RangesToString(ranges, &range_string); - EXPECT_EQ(range_string, "4,7,9,42-99"); -} - - // Test various configurations of chunk numbers. -TEST(SafeBrowsingChunkRangeTest, TestChunksToRanges) { +TEST(SafeBrowsingChunkRangeTest, TestChunksToRangeString) { std::vector<int> chunks; - std::vector<ChunkRange> ranges; + std::string range_string; // Test one chunk range and one single value. chunks.push_back(1); @@ -46,15 +18,11 @@ TEST(SafeBrowsingChunkRangeTest, TestChunksToRanges) { chunks.push_back(3); chunks.push_back(4); chunks.push_back(7); - ChunksToRanges(chunks, &ranges); - EXPECT_EQ(ranges.size(), static_cast<size_t>(2)); - EXPECT_EQ(ranges[0].start(), 1); - EXPECT_EQ(ranges[0].stop(), 4); - EXPECT_EQ(ranges[1].start(), 7); - EXPECT_EQ(ranges[1].stop(), 7); + ChunksToRangeString(chunks, &range_string); + EXPECT_EQ(range_string, std::string("1-4,7")); chunks.clear(); - ranges.clear(); + range_string.clear(); // Test all chunk numbers in one range. chunks.push_back(3); @@ -65,13 +33,11 @@ TEST(SafeBrowsingChunkRangeTest, TestChunksToRanges) { chunks.push_back(8); chunks.push_back(9); chunks.push_back(10); - ChunksToRanges(chunks, &ranges); - EXPECT_EQ(ranges.size(), static_cast<size_t>(1)); - EXPECT_EQ(ranges[0].start(), 3); - EXPECT_EQ(ranges[0].stop(), 10); + ChunksToRangeString(chunks, &range_string); + EXPECT_EQ(range_string, std::string("3-10")); chunks.clear(); - ranges.clear(); + range_string.clear(); // Test no chunk numbers in contiguous ranges. chunks.push_back(3); @@ -82,21 +48,19 @@ TEST(SafeBrowsingChunkRangeTest, TestChunksToRanges) { chunks.push_back(13); chunks.push_back(15); chunks.push_back(17); - ChunksToRanges(chunks, &ranges); - EXPECT_EQ(ranges.size(), static_cast<size_t>(8)); + ChunksToRangeString(chunks, &range_string); + EXPECT_EQ(range_string, std::string("3,5,7,9,11,13,15,17")); chunks.clear(); - ranges.clear(); + range_string.clear(); // Test a single chunk number. chunks.push_back(17); - ChunksToRanges(chunks, &ranges); - EXPECT_EQ(ranges.size(), static_cast<size_t>(1)); - EXPECT_EQ(ranges[0].start(), 17); - EXPECT_EQ(ranges[0].stop(), 17); + ChunksToRangeString(chunks, &range_string); + EXPECT_EQ(range_string, std::string("17")); chunks.clear(); - ranges.clear(); + range_string.clear(); // Test duplicates. chunks.push_back(1); @@ -108,14 +72,21 @@ TEST(SafeBrowsingChunkRangeTest, TestChunksToRanges) { chunks.push_back(7); chunks.push_back(7); chunks.push_back(7); - ChunksToRanges(chunks, &ranges); - EXPECT_EQ(ranges.size(), static_cast<size_t>(2)); - EXPECT_EQ(ranges[0].start(), 1); - EXPECT_EQ(ranges[0].stop(), 3); - EXPECT_EQ(ranges[1].start(), 7); - EXPECT_EQ(ranges[1].stop(), 7); -} + ChunksToRangeString(chunks, &range_string); + EXPECT_EQ(range_string, std::string("1-3,7")); + // Test unsorted chunks. + chunks.push_back(4); + chunks.push_back(1); + chunks.push_back(7); + chunks.push_back(3); + chunks.push_back(2); + ChunksToRangeString(chunks, &range_string); + EXPECT_EQ(range_string, std::string("1-4,7")); + + chunks.clear(); + range_string.clear(); +} TEST(SafeBrowsingChunkRangeTest, TestStringToRanges) { std::vector<ChunkRange> ranges; diff --git a/chrome/browser/safe_browsing/filter_false_positive_perftest.cc b/chrome/browser/safe_browsing/filter_false_positive_perftest.cc index f763b51..eec1c2e 100644 --- a/chrome/browser/safe_browsing/filter_false_positive_perftest.cc +++ b/chrome/browser/safe_browsing/filter_false_positive_perftest.cc @@ -261,7 +261,7 @@ void CalculateBloomFilterFalsePositives( if (use_weights) { std::string::size_type pos = url.find_last_of(","); if (pos != std::string::npos) { - base::StringToInt(std::string(url, pos + 1), &weight); + base::StringToInt(url.begin() + pos + 1, url.end(), &weight); url = url.substr(0, pos); } } diff --git a/chrome/browser/safe_browsing/protocol_manager.cc b/chrome/browser/safe_browsing/protocol_manager.cc index d7ba1a8..ab8ad5c 100644 --- a/chrome/browser/safe_browsing/protocol_manager.cc +++ b/chrome/browser/safe_browsing/protocol_manager.cc @@ -207,11 +207,11 @@ void SafeBrowsingProtocolManager::OnURLFetchComplete( } else { HandleGetHashError(Time::Now()); if (status.status() == URLRequestStatus::FAILED) { - SB_DLOG(INFO) << "SafeBrowsing GetHash request for: " << source->url() - << " failed with os error: " << status.os_error(); + VLOG(1) << "SafeBrowsing GetHash request for: " << source->url() + << " failed with os error: " << status.os_error(); } else { - SB_DLOG(INFO) << "SafeBrowsing GetHash request for: " << source->url() - << " failed with error: " << response_code; + VLOG(1) << "SafeBrowsing GetHash request for: " << source->url() + << " failed with error: " << response_code; } } @@ -242,8 +242,8 @@ void SafeBrowsingProtocolManager::OnURLFetchComplete( data.data(), static_cast<int>(data.length())); if (!parsed_ok) { - SB_DLOG(INFO) << "SafeBrowsing request for: " << source->url() - << "failed parse."; + VLOG(1) << "SafeBrowsing request for: " << source->url() + << " failed parse."; must_back_off = true; chunk_request_urls_.clear(); UpdateFinished(false); @@ -281,11 +281,11 @@ void SafeBrowsingProtocolManager::OnURLFetchComplete( chunk_request_urls_.clear(); UpdateFinished(false); if (status.status() == URLRequestStatus::FAILED) { - SB_DLOG(INFO) << "SafeBrowsing request for: " << source->url() - << " failed with os error: " << status.os_error(); + VLOG(1) << "SafeBrowsing request for: " << source->url() + << " failed with os error: " << status.os_error(); } else { - SB_DLOG(INFO) << "SafeBrowsing request for: " << source->url() - << " failed with error: " << response_code; + VLOG(1) << "SafeBrowsing request for: " << source->url() + << " failed with error: " << response_code; } } } @@ -376,12 +376,12 @@ bool SafeBrowsingProtocolManager::HandleServiceResponse(const GURL& url, data_str.assign(data, length); std::string encoded_chunk; base::Base64Encode(data, &encoded_chunk); - SB_DLOG(INFO) << "ParseChunk error for chunk: " << chunk_url.url - << ", client_key: " << client_key_ - << ", wrapped_key: " << wrapped_key_ - << ", mac: " << chunk_url.mac - << ", Base64Encode(data): " << encoded_chunk - << ", length: " << length; + VLOG(1) << "ParseChunk error for chunk: " << chunk_url.url + << ", client_key: " << client_key_ + << ", wrapped_key: " << wrapped_key_ + << ", mac: " << chunk_url.mac + << ", Base64Encode(data): " << encoded_chunk + << ", length: " << length; #endif return false; } diff --git a/chrome/browser/safe_browsing/protocol_parser.cc b/chrome/browser/safe_browsing/protocol_parser.cc index 36f9a74..7290376 100644 --- a/chrome/browser/safe_browsing/protocol_parser.cc +++ b/chrome/browser/safe_browsing/protocol_parser.cc @@ -285,7 +285,7 @@ bool SafeBrowsingProtocolParser::ParseChunk(const char* data, const int chunk_number = atoi(cmd_parts[1].c_str()); const int hash_len = atoi(cmd_parts[2].c_str()); if (hash_len != sizeof(SBPrefix) && hash_len != sizeof(SBFullHash)) { - SB_DLOG(INFO) << "ParseChunk got unknown hashlen " << hash_len; + VLOG(1) << "ParseChunk got unknown hashlen " << hash_len; return false; } diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc index ace8abb..843ca72 100644 --- a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc +++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc @@ -44,7 +44,7 @@ static const char* const kSbDiagnosticUrl = static const char* const kSbReportPhishingUrl = "http://www.google.com/safebrowsing/report_error/"; -// URL for the "Learn more" link on the malware blocking page. +// URL for the "Learn more" link on the multi threat malware blocking page. static const char* const kLearnMoreMalwareUrl = "http://www.google.com/support/bin/answer.py?answer=45449&topic=360" "&sa=X&oi=malwarewarninglink&resnum=1&ct=help"; @@ -54,8 +54,12 @@ static const char* const kLearnMorePhishingUrl = "http://www.google.com/support/bin/answer.py?answer=106318"; static const wchar_t* const kSbDiagnosticHtml = - L"<a href=\"\" onClick=\"sendCommand('showDiagnostic'); return false;\" " - L"onMouseDown=\"return false;\">%ls</a>"; + L"<a href=\"\" onclick=\"sendCommand('showDiagnostic'); return false;\" " + L"onmousedown=\"return false;\">%ls</a>"; + +static const wchar_t* const kPLinkHtml = + L"<a href=\"\" onclick=\"sendCommand('proceed'); return false;\" " + L"onmousedown=\"return false;\">%ls</a>"; // The commands returned by the page when the user performs an action. static const char* const kShowDiagnosticCommand = "showDiagnostic"; @@ -209,7 +213,7 @@ void SafeBrowsingBlockingPage::PopulateMultipleThreatStringDictionary( l10n_util::GetStringF(IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION1, UTF8ToWide(tab()->GetURL().host())), l10n_util::GetString(IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION2), - l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION3)); + l10n_util::GetString(IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION3)); } else { // Just phishing. PopulateStringDictionary( @@ -222,9 +226,11 @@ void SafeBrowsingBlockingPage::PopulateMultipleThreatStringDictionary( } strings->SetString("confirm_text", - l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION_AGREE)); + l10n_util::GetStringUTF16( + IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION_AGREE)); strings->SetString("continue_button", - l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_PROCEED_BUTTON)); + l10n_util::GetStringUTF16( + IDS_SAFE_BROWSING_MULTI_MALWARE_PROCEED_BUTTON)); strings->SetString("back_button", l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_BACK_BUTTON)); strings->SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr"); @@ -232,41 +238,50 @@ void SafeBrowsingBlockingPage::PopulateMultipleThreatStringDictionary( void SafeBrowsingBlockingPage::PopulateMalwareStringDictionary( DictionaryValue* strings) { - std::wstring link = StringPrintf(kSbDiagnosticHtml, + std::wstring diagnostic_link = StringPrintf(kSbDiagnosticHtml, l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_DIAGNOSTIC_PAGE).c_str()); strings->SetString("badURL", url().host()); // Check to see if we're blocking the main page, or a sub-resource on the // main page. - std::wstring description1, description2; + std::wstring description1, description3, description5; if (is_main_frame_) { description1 = l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION1, UTF8ToWide(url().host())); - description2 = l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION2, - link, - UTF8ToWide(url().host())); } else { description1 = l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION4, UTF8ToWide(tab()->GetURL().host()), UTF8ToWide(url().host())); - description2 = l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION5, - link, - UTF8ToWide(url().host())); } + std::wstring proceed_link = StringPrintf(kPLinkHtml, + l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_PROCEED_LINK).c_str()); + description3 = l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION3, + proceed_link); + PopulateStringDictionary( strings, l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_TITLE), l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_HEADLINE), - description1, description2, - l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION3)); + description1, + l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION2), + description3); + + description5 = l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION5, + UTF8ToWide(url().host()), + UTF8ToWide(url().host()), + diagnostic_link); + + strings->SetString("description5", WideToUTF16Hack(description5)); - strings->SetString("confirm_text", - l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION_AGREE)); - strings->SetString("continue_button", - l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_PROCEED_BUTTON)); strings->SetString("back_button", l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_BACK_BUTTON)); + strings->SetString("more_info_button", + l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_MORE_INFO_BUTTON)); + strings->SetString("less_info_button", + l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_LESS_INFO_BUTTON)); + strings->SetString("proceed_link", + l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_PROCEED_LINK)); strings->SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr"); } @@ -331,9 +346,10 @@ void SafeBrowsingBlockingPage::CommandReceived(const std::string& cmd) { size_t colon_index = command.find(':'); if (colon_index != std::string::npos) { DCHECK(colon_index < command.size() - 1); - std::string index_str = command.substr(colon_index + 1); command = command.substr(0, colon_index); - bool result = base::StringToInt(index_str, &element_index); + bool result = base::StringToInt(command.begin() + colon_index + 1, + command.end(), + &element_index); DCHECK(result); } diff --git a/chrome/browser/safe_browsing/safe_browsing_database.cc b/chrome/browser/safe_browsing/safe_browsing_database.cc index 823362f..e741c60 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database.cc @@ -123,15 +123,8 @@ void GetChunkIds(const std::vector<int>& chunks, } } - std::sort(malware_chunks.begin(), malware_chunks.end()); - std::vector<ChunkRange> malware_ranges; - ChunksToRanges(malware_chunks, &malware_ranges); - RangesToString(malware_ranges, malware_list); - - std::sort(phishing_chunks.begin(), phishing_chunks.end()); - std::vector<ChunkRange> phishing_ranges; - ChunksToRanges(phishing_chunks, &phishing_ranges); - RangesToString(phishing_ranges, phishing_list); + ChunksToRangeString(malware_chunks, malware_list); + ChunksToRangeString(phishing_chunks, phishing_list); } // Order |SBAddFullHash| on the prefix part. |SBAddPrefixLess()| from @@ -163,7 +156,8 @@ FilePath SafeBrowsingDatabase::BloomFilterForFilename( // static void SafeBrowsingDatabase::RecordFailure(FailureType failure_type) { - UMA_HISTOGRAM_ENUMERATION("SB2.DatabaseFailure", failure_type, FAILURE_MAX); + UMA_HISTOGRAM_ENUMERATION("SB2.DatabaseFailure", failure_type, + FAILURE_DATABASE_MAX); } SafeBrowsingDatabaseNew::SafeBrowsingDatabaseNew(SafeBrowsingStore* store) @@ -609,9 +603,9 @@ void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) { static_cast<int>(io_after.WriteOperationCount - io_before.WriteOperationCount)); } - SB_DLOG(INFO) << "SafeBrowsingDatabaseImpl built bloom filter in " - << bloom_gen.InMilliseconds() - << " ms total. prefix count: "<< add_prefixes.size(); + VLOG(1) << "SafeBrowsingDatabaseImpl built bloom filter in " + << bloom_gen.InMilliseconds() << " ms total. prefix count: " + << add_prefixes.size(); UMA_HISTOGRAM_LONG_TIMES("SB2.BuildFilter", bloom_gen); UMA_HISTOGRAM_COUNTS("SB2.FilterKilobytes", bloom_filter_->size() / 1024); int64 size_64; @@ -663,8 +657,8 @@ void SafeBrowsingDatabaseNew::LoadBloomFilter() { const base::TimeTicks before = base::TimeTicks::Now(); bloom_filter_ = BloomFilter::LoadFile(bloom_filter_filename_); - SB_DLOG(INFO) << "SafeBrowsingDatabaseNew read bloom filter in " - << (base::TimeTicks::Now() - before).InMilliseconds() << " ms"; + VLOG(1) << "SafeBrowsingDatabaseNew read bloom filter in " + << (base::TimeTicks::Now() - before).InMilliseconds() << " ms"; if (!bloom_filter_.get()) { UMA_HISTOGRAM_COUNTS("SB2.FilterReadFail", 1); @@ -692,8 +686,8 @@ void SafeBrowsingDatabaseNew::WriteBloomFilter() { const base::TimeTicks before = base::TimeTicks::Now(); const bool write_ok = bloom_filter_->WriteFile(bloom_filter_filename_); - SB_DLOG(INFO) << "SafeBrowsingDatabaseNew wrote bloom filter in " << - (base::TimeTicks::Now() - before).InMilliseconds() << " ms"; + VLOG(1) << "SafeBrowsingDatabaseNew wrote bloom filter in " + << (base::TimeTicks::Now() - before).InMilliseconds() << " ms"; if (!write_ok) { UMA_HISTOGRAM_COUNTS("SB2.FilterWriteFail", 1); diff --git a/chrome/browser/safe_browsing/safe_browsing_database.h b/chrome/browser/safe_browsing/safe_browsing_database.h index ad04a7c..9ca53f7 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database.h +++ b/chrome/browser/safe_browsing/safe_browsing_database.h @@ -106,9 +106,9 @@ class SafeBrowsingDatabase { FAILURE_DATABASE_STORE_MISSING, FAILURE_DATABASE_STORE_DELETE, - // Histogram space is determined by the max. If this is exceeded, - // simply start a new histogram. - FAILURE_MAX = 50 + // Memory space for histograms is determined by the max. ALWAYS + // ADD NEW VALUES BEFORE THIS ONE. + FAILURE_DATABASE_MAX }; static void RecordFailure(FailureType failure_type); diff --git a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc index 0095efb..89cc553 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc @@ -51,7 +51,8 @@ class ScopedLogMessageIgnorer { } private: - static bool LogMessageIgnorer(int severity, const std::string& str) { + static bool LogMessageIgnorer(int severity, const char* file, int line, + size_t message_start, const std::string& str) { // Intercept FATAL, strip the stack backtrace, and log it without // the crash part. if (severity == logging::LOG_FATAL) { @@ -1051,14 +1052,14 @@ TEST_F(SafeBrowsingDatabaseTest, DISABLED_SqliteCorruptionHandling) { // Start an update. The insert will fail due to corruption. EXPECT_TRUE(database_->UpdateStarted(&lists)); - LOG(INFO) << "Expect failed check on: sqlite error 11"; + VLOG(1) << "Expect failed check on: sqlite error 11"; database_->InsertChunks(safe_browsing_util::kMalwareList, chunks); // Database file still exists until the corruption handler has run. EXPECT_TRUE(file_util::PathExists(database_filename_)); // Flush through the corruption-handler task. - LOG(INFO) << "Expect failed check on: SafeBrowsing database reset"; + VLOG(1) << "Expect failed check on: SafeBrowsing database reset"; MessageLoop::current()->RunAllPending(); } @@ -1143,7 +1144,7 @@ TEST_F(SafeBrowsingDatabaseTest, DISABLED_FileCorruptionHandling) { EXPECT_TRUE(file_util::PathExists(database_filename_)); // Flush through the corruption-handler task. - LOG(INFO) << "Expect failed check on: SafeBrowsing database reset"; + VLOG(1) << "Expect failed check on: SafeBrowsing database reset"; MessageLoop::current()->RunAllPending(); } diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc index 6f8619c..aac28cd 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service.cc +++ b/chrome/browser/safe_browsing/safe_browsing_service.cc @@ -1,7 +1,6 @@ // 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/safe_browsing/safe_browsing_service.h" @@ -57,6 +56,24 @@ struct SafeBrowsingService::WhiteListedEntry { UrlCheckResult result; }; +SafeBrowsingService::UnsafeResource::UnsafeResource() + : resource_type(ResourceType::MAIN_FRAME), + threat_type(URL_SAFE), + client(NULL), + render_process_host_id(-1), + render_view_id(-1) { +} + +SafeBrowsingService::UnsafeResource::~UnsafeResource() {} + +SafeBrowsingService::SafeBrowsingCheck::SafeBrowsingCheck() + : client(NULL), + need_get_hash(false), + result(URL_SAFE) { +} + +SafeBrowsingService::SafeBrowsingCheck::~SafeBrowsingCheck() {} + SafeBrowsingService::SafeBrowsingService() : database_(NULL), protocol_manager_(NULL), @@ -619,7 +636,7 @@ SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname( return URL_MALWARE; } - SB_DLOG(INFO) << "Unknown safe browsing list " << list_name; + DVLOG(1) << "Unknown safe browsing list " << list_name; return URL_SAFE; } @@ -652,8 +669,8 @@ void SafeBrowsingService::Start() { } // We will issue network fetches using the default profile's request context. - scoped_refptr<URLRequestContextGetter> request_context_getter = - GetDefaultProfile()->GetRequestContext(); + scoped_refptr<URLRequestContextGetter> request_context_getter( + GetDefaultProfile()->GetRequestContext()); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, @@ -808,9 +825,9 @@ void SafeBrowsingService::ReportSafeBrowsingHit( if (!enabled_) return; - DLOG(INFO) << "ReportSafeBrowsingHit: " << malicious_url << " " << page_url - << " " << referrer_url << " " << is_subresource - << " " << threat_type; + DVLOG(1) << "ReportSafeBrowsingHit: " << malicious_url << " " << page_url + << " " << referrer_url << " " << is_subresource << " " + << threat_type; protocol_manager_->ReportSafeBrowsingHit(malicious_url, page_url, referrer_url, is_subresource, threat_type); diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h index 050437a..acecf4e 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service.h +++ b/chrome/browser/safe_browsing/safe_browsing_service.h @@ -59,6 +59,9 @@ class SafeBrowsingService // Structure used to pass parameters between the IO and UI thread when // interacting with the blocking page. struct UnsafeResource { + UnsafeResource(); + ~UnsafeResource(); + GURL url; GURL original_url; ResourceType::Type resource_type; @@ -70,6 +73,9 @@ class SafeBrowsingService // Bundle of SafeBrowsing state for one URL check. struct SafeBrowsingCheck { + SafeBrowsingCheck(); + ~SafeBrowsingCheck(); + GURL url; Client* client; bool need_get_hash; @@ -77,6 +83,9 @@ class SafeBrowsingService UrlCheckResult result; std::vector<SBPrefix> prefix_hits; std::vector<SBFullHashResult> full_hits; + + private: + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingCheck); }; // Creates the safe browsing service. Need to initialize before using. diff --git a/chrome/browser/safe_browsing/safe_browsing_store.h b/chrome/browser/safe_browsing/safe_browsing_store.h index b6b44bd..418c55f 100644 --- a/chrome/browser/safe_browsing/safe_browsing_store.h +++ b/chrome/browser/safe_browsing/safe_browsing_store.h @@ -69,7 +69,7 @@ struct SBAddFullHash { int32 received; SBFullHash full_hash; - SBAddFullHash(int32 id, base::Time r, SBFullHash h) + SBAddFullHash(int32 id, base::Time r, const SBFullHash& h) : chunk_id(id), received(static_cast<int32>(r.ToTimeT())), full_hash(h) { @@ -77,7 +77,7 @@ struct SBAddFullHash { // Provided for ReadAddHashes() implementations, which already have // an int32 for the time. - SBAddFullHash(int32 id, int32 r, SBFullHash h) + SBAddFullHash(int32 id, int32 r, const SBFullHash& h) : chunk_id(id), received(r), full_hash(h) {} SBAddFullHash() : chunk_id(), received(), full_hash() {} @@ -91,7 +91,7 @@ struct SBSubFullHash { int32 add_chunk_id; SBFullHash full_hash; - SBSubFullHash(int32 id, int32 add_id, SBFullHash h) + SBSubFullHash(int32 id, int32 add_id, const SBFullHash& h) : chunk_id(id), add_chunk_id(add_id), full_hash(h) {} SBSubFullHash() : chunk_id(), add_chunk_id(), full_hash() {} @@ -182,11 +182,12 @@ class SafeBrowsingStore { virtual bool WriteAddPrefix(int32 chunk_id, SBPrefix prefix) = 0; virtual bool WriteAddHash(int32 chunk_id, - base::Time receive_time, SBFullHash full_hash) = 0; + base::Time receive_time, + const SBFullHash& full_hash) = 0; virtual bool WriteSubPrefix(int32 chunk_id, int32 add_chunk_id, SBPrefix prefix) = 0; virtual bool WriteSubHash(int32 chunk_id, int32 add_chunk_id, - SBFullHash full_hash) = 0; + const SBFullHash& full_hash) = 0; // Collect the chunk data and preferrably store it on disk to // release memory. Shoul not modify the data in-place. diff --git a/chrome/browser/safe_browsing/safe_browsing_store_file.cc b/chrome/browser/safe_browsing/safe_browsing_store_file.cc index 196cf0d..71f415f 100644 --- a/chrome/browser/safe_browsing/safe_browsing_store_file.cc +++ b/chrome/browser/safe_browsing/safe_browsing_store_file.cc @@ -239,7 +239,7 @@ bool SafeBrowsingStoreFile::WriteAddPrefix(int32 chunk_id, SBPrefix prefix) { bool SafeBrowsingStoreFile::WriteAddHash(int32 chunk_id, base::Time receive_time, - SBFullHash full_hash) { + const SBFullHash& full_hash) { add_hashes_.push_back(SBAddFullHash(chunk_id, receive_time, full_hash)); return true; } @@ -252,7 +252,7 @@ bool SafeBrowsingStoreFile::WriteSubPrefix(int32 chunk_id, } bool SafeBrowsingStoreFile::WriteSubHash(int32 chunk_id, int32 add_chunk_id, - SBFullHash full_hash) { + const SBFullHash& full_hash) { sub_hashes_.push_back(SBSubFullHash(chunk_id, add_chunk_id, full_hash)); return true; } diff --git a/chrome/browser/safe_browsing/safe_browsing_store_file.h b/chrome/browser/safe_browsing/safe_browsing_store_file.h index ec612e6..5ddc87a 100644 --- a/chrome/browser/safe_browsing/safe_browsing_store_file.h +++ b/chrome/browser/safe_browsing/safe_browsing_store_file.h @@ -127,11 +127,12 @@ class SafeBrowsingStoreFile : public SafeBrowsingStore { virtual bool BeginChunk(); virtual bool WriteAddPrefix(int32 chunk_id, SBPrefix prefix); virtual bool WriteAddHash(int32 chunk_id, - base::Time receive_time, SBFullHash full_hash); + base::Time receive_time, + const SBFullHash& full_hash); virtual bool WriteSubPrefix(int32 chunk_id, int32 add_chunk_id, SBPrefix prefix); virtual bool WriteSubHash(int32 chunk_id, int32 add_chunk_id, - SBFullHash full_hash); + const SBFullHash& full_hash); virtual bool FinishChunk(); virtual bool BeginUpdate(); @@ -179,9 +180,9 @@ class SafeBrowsingStoreFile : public SafeBrowsingStore { FORMAT_EVENT_SQLITE_DELETED, FORMAT_EVENT_SQLITE_DELETE_FAILED, - // Histogram space is determined by the max. If this is exceeded, - // simply start a new histogram. - FORMAT_EVENT_MAX = 50 + // Memory space for histograms is determined by the max. ALWAYS + // ADD NEW VALUES BEFORE THIS ONE. + FORMAT_EVENT_MAX }; // Helper to record an event related to format conversion from diff --git a/chrome/browser/safe_browsing/safe_browsing_store_sqlite.h b/chrome/browser/safe_browsing/safe_browsing_store_sqlite.h index 6f684c1..60e7fda 100644 --- a/chrome/browser/safe_browsing/safe_browsing_store_sqlite.h +++ b/chrome/browser/safe_browsing/safe_browsing_store_sqlite.h @@ -37,7 +37,8 @@ class SafeBrowsingStoreSqlite : public SafeBrowsingStore { return WriteAddPrefixes(prefixes); } virtual bool WriteAddHash(int32 chunk_id, - base::Time receive_time, SBFullHash full_hash) { + base::Time receive_time, + const SBFullHash& full_hash) { const std::vector<SBAddFullHash> hashes(1, SBAddFullHash(chunk_id, receive_time, full_hash)); return WriteAddHashes(hashes); @@ -49,7 +50,7 @@ class SafeBrowsingStoreSqlite : public SafeBrowsingStore { return WriteSubPrefixes(prefixes); } virtual bool WriteSubHash(int32 chunk_id, int32 add_chunk_id, - SBFullHash full_hash) { + const SBFullHash& full_hash) { const std::vector<SBSubFullHash> hashes(1, SBSubFullHash(chunk_id, add_chunk_id, full_hash)); return WriteSubHashes(hashes); diff --git a/chrome/browser/safe_browsing/safe_browsing_test.cc b/chrome/browser/safe_browsing/safe_browsing_test.cc index b4eb1d4..5c1f3e3 100644 --- a/chrome/browser/safe_browsing/safe_browsing_test.cc +++ b/chrome/browser/safe_browsing/safe_browsing_test.cc @@ -155,24 +155,19 @@ class SafeBrowsingTestServer { // Stop the python server test suite. bool Stop() { - if (server_handle_ == base::kNullProcessHandle) { + if (server_handle_ == base::kNullProcessHandle) return true; - } // First check if the process has already terminated. - bool ret = base::WaitForSingleProcess(server_handle_, 0); - if (!ret) { - ret = base::KillProcess(server_handle_, 1, true); - } - - if (ret) { - base::CloseProcessHandle(server_handle_); - server_handle_ = base::kNullProcessHandle; - LOG(INFO) << "Stopped."; - } else { - LOG(INFO) << "Kill failed?"; + if (!base::WaitForSingleProcess(server_handle_, 0) && + !base::KillProcess(server_handle_, 1, true)) { + VLOG(1) << "Kill failed?"; return false; } + + base::CloseProcessHandle(server_handle_); + server_handle_ = base::kNullProcessHandle; + VLOG(1) << "Stopped."; return true; } @@ -525,14 +520,19 @@ class SafeBrowsingServiceTestHelper DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceTestHelper); }; + +#if defined(OS_MACOSX) +// TODO(lzheng): http://crbug.com/62415, can not start on MacOS. +#define SafeBrowsingSystemTest DISABLED_SafeBrowsingSystemTest +#endif IN_PROC_BROWSER_TEST_F(SafeBrowsingServiceTest, SafeBrowsingSystemTest) { LOG(INFO) << "Start test"; const char* server_host = SafeBrowsingTestServer::Host(); int server_port = SafeBrowsingTestServer::Port(); ASSERT_TRUE(InitSafeBrowsingService()); - scoped_refptr<SafeBrowsingServiceTestHelper> safe_browsing_helper = - new SafeBrowsingServiceTestHelper(this); + scoped_refptr<SafeBrowsingServiceTestHelper> safe_browsing_helper( + new SafeBrowsingServiceTestHelper(this)); int last_step = 0; FilePath datafile_path = FilePath(kDataFile); SafeBrowsingTestServer test_server(datafile_path); @@ -640,4 +640,3 @@ IN_PROC_BROWSER_TEST_F(SafeBrowsingServiceTest, SafeBrowsingSystemTest) { // EXPECT_EQ("yes", safe_browsing_helper->response_data()); test_server.Stop(); } - diff --git a/chrome/browser/safe_browsing/safe_browsing_util.h b/chrome/browser/safe_browsing/safe_browsing_util.h index f147fe4..a4fd2b6 100644 --- a/chrome/browser/safe_browsing/safe_browsing_util.h +++ b/chrome/browser/safe_browsing/safe_browsing_util.h @@ -18,12 +18,6 @@ class GURL; -#ifdef SB_LOGGING_ENABLED -#define SB_DLOG(severity) DLOG_IF(INFO, 1) -#else -#define SB_DLOG(severity) DLOG_IF(INFO, 0) -#endif - class SBEntry; // A truncated hash's type. diff --git a/chrome/browser/search_engines/search_engine_type.h b/chrome/browser/search_engines/search_engine_type.h new file mode 100644 index 0000000..7e4dfd1 --- /dev/null +++ b/chrome/browser/search_engines/search_engine_type.h @@ -0,0 +1,52 @@ +// 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_SEARCH_ENGINES_SEARCH_ENGINE_TYPE_H_ +#define CHROME_BROWSER_SEARCH_ENGINES_SEARCH_ENGINE_TYPE_H_ +#pragma once + +// Fills logo slot for search engines with no logo available. +static const int kNoSearchEngineLogo = 0; + +// Enum to record the user's default search engine choice in UMA. Add new +// search engines at the bottom and do not delete from this list, so as not +// to disrupt UMA data already recorded. +enum SearchEngineType { + SEARCH_ENGINE_OTHER = 0, // At the top in case of future list changes. + SEARCH_ENGINE_GOOGLE, + SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOOJP, + SEARCH_ENGINE_BING, + SEARCH_ENGINE_ASK, + SEARCH_ENGINE_YANDEX, + SEARCH_ENGINE_SEZNAM, + SEARCH_ENGINE_CENTRUM, + SEARCH_ENGINE_NETSPRINT, + SEARCH_ENGINE_VIRGILIO, + SEARCH_ENGINE_MAILRU, + SEARCH_ENGINE_ABCSOK, + SEARCH_ENGINE_ALTAVISTA, + SEARCH_ENGINE_BAIDU, + SEARCH_ENGINE_DAUM, + SEARCH_ENGINE_DELFI, + SEARCH_ENGINE_DIRI, + SEARCH_ENGINE_GOO, + SEARCH_ENGINE_IN, + SEARCH_ENGINE_NAJDI, + SEARCH_ENGINE_NAVER, + SEARCH_ENGINE_NETI, + SEARCH_ENGINE_OK, + SEARCH_ENGINE_POGODAK, + SEARCH_ENGINE_POGODOK_MK, + SEARCH_ENGINE_RAMBLER, + SEARCH_ENGINE_SANOOK, + SEARCH_ENGINE_SAPO, + SEARCH_ENGINE_TUT, + SEARCH_ENGINE_WALLA, + SEARCH_ENGINE_ZOZNAM, + SEARCH_ENGINE_YAHOOQC, + SEARCH_ENGINE_MAX // Bounding max value needed for UMA histogram macro. +}; + +#endif // CHROME_BROWSER_SEARCH_ENGINES_SEARCH_ENGINE_TYPE_H_ diff --git a/chrome/browser/search_engines/search_terms_data.cc b/chrome/browser/search_engines/search_terms_data.cc index d3bb0a4..834d09a 100644 --- a/chrome/browser/search_engines/search_terms_data.cc +++ b/chrome/browser/search_engines/search_terms_data.cc @@ -5,6 +5,7 @@ #include "chrome/browser/search_engines/search_terms_data.h" #include "base/logging.h" +#include "base/thread_restrictions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/google/google_url_tracker.h" @@ -79,6 +80,8 @@ std::wstring UIThreadSearchTermsData::GetRlzParameterValue() const { // For organic brandcodes do not use rlz at all. Empty brandcode usually // means a chromium install. This is ok. std::wstring brand; + // See http://crbug.com/62337. + base::ThreadRestrictions::ScopedAllowIO allow_io; if (GoogleUpdateSettings::GetBrand(&brand) && !brand.empty() && !GoogleUpdateSettings::IsOrganic(brand)) RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz_string); diff --git a/chrome/browser/search_engines/template_url.cc b/chrome/browser/search_engines/template_url.cc index ac57fac..48f5d7d 100644 --- a/chrome/browser/search_engines/template_url.cc +++ b/chrome/browser/search_engines/template_url.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/search_engines/search_engine_type.h" #include "chrome/browser/search_engines/search_terms_data.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/common/url_constants.h" @@ -568,8 +569,8 @@ TemplateURL::TemplateURL() date_created_(base::Time::Now()), created_by_policy_(false), usage_count_(0), - search_engine_type_(TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER), - logo_id_(0), + search_engine_type_(SEARCH_ENGINE_OTHER), + logo_id_(kNoSearchEngineLogo), prepopulate_id_(0) { } diff --git a/chrome/browser/search_engines/template_url.h b/chrome/browser/search_engines/template_url.h index 074542e..ba2a96e 100644 --- a/chrome/browser/search_engines/template_url.h +++ b/chrome/browser/search_engines/template_url.h @@ -11,10 +11,11 @@ #include "base/gtest_prod_util.h" #include "base/time.h" +#include "chrome/browser/search_engines/search_engine_type.h" #include "chrome/browser/search_engines/template_url_id.h" -#include "chrome/browser/search_engines/template_url_prepopulate_data.h" #include "googleurl/src/gurl.h" +class PrefService; class SearchTermsData; class TemplateURL; class WebDataService; @@ -448,11 +449,10 @@ class TemplateURL { return input_encodings_; } - void set_search_engine_type(TemplateURLPrepopulateData::SearchEngineType - search_engine_type) { + void set_search_engine_type(SearchEngineType search_engine_type) { search_engine_type_ = search_engine_type; } - TemplateURLPrepopulateData::SearchEngineType search_engine_type() const { + SearchEngineType search_engine_type() const { return search_engine_type_; } @@ -509,7 +509,7 @@ class TemplateURL { base::Time date_created_; bool created_by_policy_; int usage_count_; - TemplateURLPrepopulateData::SearchEngineType search_engine_type_; + SearchEngineType search_engine_type_; int logo_id_; int prepopulate_id_; diff --git a/chrome/browser/search_engines/template_url_model.cc b/chrome/browser/search_engines/template_url_model.cc index e7bcb92..6c72f5f 100644 --- a/chrome/browser/search_engines/template_url_model.cc +++ b/chrome/browser/search_engines/template_url_model.cc @@ -308,7 +308,7 @@ void TemplateURLModel::RemoveAutoGeneratedSince(Time created_after) { RemoveAutoGeneratedBetween(created_after, Time()); } -void TemplateURLModel::RegisterExtensionKeyword(Extension* extension) { +void TemplateURLModel::RegisterExtensionKeyword(const Extension* extension) { // TODO(mpcomplete): disable the keyword when the extension is disabled. if (extension->omnibox_keyword().empty()) return; @@ -342,14 +342,14 @@ void TemplateURLModel::RegisterExtensionKeyword(Extension* extension) { NotifyObservers(); } -void TemplateURLModel::UnregisterExtensionKeyword(Extension* extension) { +void TemplateURLModel::UnregisterExtensionKeyword(const Extension* extension) { const TemplateURL* url = GetTemplateURLForExtension(extension); if (url) Remove(url); } const TemplateURL* TemplateURLModel::GetTemplateURLForExtension( - Extension* extension) const { + const Extension* extension) const { for (TemplateURLVector::const_iterator i = template_urls_.begin(); i != template_urls_.end(); ++i) { if ((*i)->IsExtensionKeyword() && (*i)->url()->GetHost() == extension->id()) @@ -606,8 +606,7 @@ void TemplateURLModel::SetKeywordSearchTermsForURL(const TemplateURL* t_url, profile_->GetHistoryService(Profile::EXPLICIT_ACCESS) : NULL; if (!history) return; - history->SetKeywordSearchTermsForURL(url, t_url->id(), - WideToUTF16Hack(term)); + history->SetKeywordSearchTermsForURL(url, t_url->id(), WideToUTF16Hack(term)); } void TemplateURLModel::Init(const Initializer* initializers, @@ -741,7 +740,7 @@ void TemplateURLModel::NotifyLoaded() { NotificationService::NoDetails()); for (size_t i = 0; i < pending_extension_ids_.size(); ++i) { - Extension* extension = profile_->GetExtensionsService()-> + const Extension* extension = profile_->GetExtensionsService()-> GetExtensionById(pending_extension_ids_[i], true); if (extension) RegisterExtensionKeyword(extension); @@ -1239,10 +1238,12 @@ void TemplateURLModel::RemoveNoNotify(const TemplateURL* template_url) { service_->RemoveKeyword(*template_url); if (profile_) { - HistoryService* history = - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - if (history) - history->DeleteAllSearchTermsForKeyword(template_url->id()); + Source<Profile> source(profile_); + TemplateURLID id = template_url->id(); + NotificationService::current()->Notify( + NotificationType::TEMPLATE_URL_REMOVED, + source, + Details<TemplateURLID>(&id)); } // We own the TemplateURL and need to delete it. diff --git a/chrome/browser/search_engines/template_url_model.h b/chrome/browser/search_engines/template_url_model.h index ce9a874..a16bad7 100644 --- a/chrome/browser/search_engines/template_url_model.h +++ b/chrome/browser/search_engines/template_url_model.h @@ -144,16 +144,17 @@ class TemplateURLModel : public WebDataServiceConsumer, // If the given extension has an omnibox keyword, adds a TemplateURL for that // keyword. Only 1 keyword is allowed for a given extension. If the keyword // already exists for this extension, does nothing. - void RegisterExtensionKeyword(Extension* extension); + void RegisterExtensionKeyword(const Extension* extension); // Removes the TemplateURL containing the keyword for the given extension, // if any. - void UnregisterExtensionKeyword(Extension* extension); + void UnregisterExtensionKeyword(const Extension* extension); // Returns the TemplateURL associated with the keyword for this extension. // This works by checking the extension ID, not the keyword, so it will work // even if the user changed the keyword. - const TemplateURL* GetTemplateURLForExtension(Extension* extension) const; + const TemplateURL* GetTemplateURLForExtension( + const Extension* extension) const; // Returns the set of URLs describing the keywords. The elements are owned // by TemplateURLModel and should not be deleted. diff --git a/chrome/browser/search_engines/template_url_prepopulate_data.cc b/chrome/browser/search_engines/template_url_prepopulate_data.cc index 34ec09b..127944a 100644 --- a/chrome/browser/search_engines/template_url_prepopulate_data.cc +++ b/chrome/browser/search_engines/template_url_prepopulate_data.cc @@ -15,6 +15,7 @@ #include "base/stl_util-inl.h" #include "base/utf_string_conversions.h" #include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/search_engines/search_engine_type.h" #include "chrome/browser/search_engines/search_terms_data.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" @@ -52,8 +53,8 @@ struct PrepopulatedEngine { // suggestions. const wchar_t* const instant_url; // If NULL, this engine does not support // instant. - // TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER if no logo is available. - const TemplateURLPrepopulateData::SearchEngineType search_engine_type; + // SEARCH_ENGINE_OTHER if no logo is available. + const SearchEngineType search_engine_type; const int logo_id; // Id for logo image in search engine dialog. // Unique id for this prepopulate engine (corresponds to // TemplateURL::prepopulate_id). This ID must be greater than zero and must @@ -89,7 +90,7 @@ const PrepopulatedEngine abcsok = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_ABCSOK, + SEARCH_ENGINE_ABCSOK, IDR_SEARCH_ENGINE_LOGO_ABCSOK, 72, }; @@ -102,7 +103,7 @@ const PrepopulatedEngine altavista = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_ALTAVISTA, + SEARCH_ENGINE_ALTAVISTA, IDR_SEARCH_ENGINE_LOGO_ALTAVISTA, 89, }; @@ -115,7 +116,7 @@ const PrepopulatedEngine altavista_ar = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_ALTAVISTA, + SEARCH_ENGINE_ALTAVISTA, IDR_SEARCH_ENGINE_LOGO_ALTAVISTA, 89, }; @@ -128,7 +129,7 @@ const PrepopulatedEngine altavista_se = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_ALTAVISTA, + SEARCH_ENGINE_ALTAVISTA, IDR_SEARCH_ENGINE_LOGO_ALTAVISTA, 89, }; @@ -141,8 +142,8 @@ const PrepopulatedEngine aol = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + IDR_SEARCH_ENGINE_LOGO_AOL, 35, }; @@ -154,8 +155,8 @@ const PrepopulatedEngine araby = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 12, }; @@ -167,7 +168,7 @@ const PrepopulatedEngine ask = { "UTF-8", L"http://ss.ask.com/query?q={searchTerms}&li=ff", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_ASK, + SEARCH_ENGINE_ASK, IDR_SEARCH_ENGINE_LOGO_ASK, 4, }; @@ -180,7 +181,7 @@ const PrepopulatedEngine ask_de = { "UTF-8", L"http://ss.de.ask.com/query?q={searchTerms}&li=ff", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_ASK, + SEARCH_ENGINE_ASK, IDR_SEARCH_ENGINE_LOGO_ASK, 4, }; @@ -193,7 +194,7 @@ const PrepopulatedEngine ask_es = { "UTF-8", L"http://ss.es.ask.com/query?q={searchTerms}&li=ff", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_ASK, + SEARCH_ENGINE_ASK, IDR_SEARCH_ENGINE_LOGO_ASK, 4, }; @@ -206,7 +207,7 @@ const PrepopulatedEngine ask_it = { "UTF-8", L"http://ss.it.ask.com/query?q={searchTerms}&li=ff", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_ASK, + SEARCH_ENGINE_ASK, IDR_SEARCH_ENGINE_LOGO_ASK, 4, }; @@ -219,7 +220,7 @@ const PrepopulatedEngine ask_nl = { "UTF-8", L"http://ss.nl.ask.com/query?q={searchTerms}&li=ff", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_ASK, + SEARCH_ENGINE_ASK, IDR_SEARCH_ENGINE_LOGO_ASK, 4, }; @@ -232,7 +233,7 @@ const PrepopulatedEngine ask_uk = { "UTF-8", L"http://ss.uk.ask.com/query?q={searchTerms}&li=ff", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_ASK, + SEARCH_ENGINE_ASK, IDR_SEARCH_ENGINE_LOGO_ASK, 4, }; @@ -245,8 +246,8 @@ const PrepopulatedEngine atlas_cz = { "windows-1250", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 27, }; @@ -258,8 +259,8 @@ const PrepopulatedEngine atlas_sk = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 27, }; @@ -271,7 +272,7 @@ const PrepopulatedEngine baidu = { "GB2312", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BAIDU, + SEARCH_ENGINE_BAIDU, IDR_SEARCH_ENGINE_LOGO_BAIDU, 21, }; @@ -284,7 +285,7 @@ const PrepopulatedEngine bing = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -297,7 +298,7 @@ const PrepopulatedEngine bing_ar_XA = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 7, // Can't be 3 as this has to appear in the Arabian countries' lists // alongside bing_en_XA. @@ -311,7 +312,7 @@ const PrepopulatedEngine bing_bg_BG = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -324,7 +325,7 @@ const PrepopulatedEngine bing_cs_CZ = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -337,7 +338,7 @@ const PrepopulatedEngine bing_da_DK = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -350,7 +351,7 @@ const PrepopulatedEngine bing_de_AT = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -363,7 +364,7 @@ const PrepopulatedEngine bing_de_CH = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -376,7 +377,7 @@ const PrepopulatedEngine bing_de_DE = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -389,7 +390,7 @@ const PrepopulatedEngine bing_el_GR = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -402,7 +403,7 @@ const PrepopulatedEngine bing_en_AU = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -415,7 +416,7 @@ const PrepopulatedEngine bing_en_CA = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -428,7 +429,7 @@ const PrepopulatedEngine bing_en_GB = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -441,7 +442,7 @@ const PrepopulatedEngine bing_en_ID = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -454,7 +455,7 @@ const PrepopulatedEngine bing_en_IE = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -467,7 +468,7 @@ const PrepopulatedEngine bing_en_IN = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -480,7 +481,7 @@ const PrepopulatedEngine bing_en_MY = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -493,7 +494,7 @@ const PrepopulatedEngine bing_en_NZ = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -506,7 +507,7 @@ const PrepopulatedEngine bing_en_PH = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -519,7 +520,7 @@ const PrepopulatedEngine bing_en_SG = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -532,7 +533,7 @@ const PrepopulatedEngine bing_en_US = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -545,7 +546,7 @@ const PrepopulatedEngine bing_en_XA = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -558,7 +559,7 @@ const PrepopulatedEngine bing_en_ZA = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -571,7 +572,7 @@ const PrepopulatedEngine bing_es_AR = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -584,7 +585,7 @@ const PrepopulatedEngine bing_es_CL = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -597,7 +598,7 @@ const PrepopulatedEngine bing_es_ES = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -610,7 +611,7 @@ const PrepopulatedEngine bing_es_MX = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -623,7 +624,7 @@ const PrepopulatedEngine bing_es_XL = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -636,7 +637,7 @@ const PrepopulatedEngine bing_et_EE = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -649,7 +650,7 @@ const PrepopulatedEngine bing_fi_FI = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -662,7 +663,7 @@ const PrepopulatedEngine bing_fr_BE = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 7, }; @@ -675,7 +676,7 @@ const PrepopulatedEngine bing_fr_CA = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 7, }; @@ -688,7 +689,7 @@ const PrepopulatedEngine bing_fr_CH = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 7, }; @@ -701,7 +702,7 @@ const PrepopulatedEngine bing_fr_FR = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -714,7 +715,7 @@ const PrepopulatedEngine bing_he_IL = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -727,7 +728,7 @@ const PrepopulatedEngine bing_hr_HR = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -740,7 +741,7 @@ const PrepopulatedEngine bing_hu_HU = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -753,7 +754,7 @@ const PrepopulatedEngine bing_it_IT = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -766,7 +767,7 @@ const PrepopulatedEngine bing_ja_JP = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -779,7 +780,7 @@ const PrepopulatedEngine bing_ko_KR = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -792,7 +793,7 @@ const PrepopulatedEngine bing_lt_LT = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -805,7 +806,7 @@ const PrepopulatedEngine bing_lv_LV = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -818,7 +819,7 @@ const PrepopulatedEngine bing_nb_NO = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -831,7 +832,7 @@ const PrepopulatedEngine bing_nl_BE = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -844,7 +845,7 @@ const PrepopulatedEngine bing_nl_NL = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -857,7 +858,7 @@ const PrepopulatedEngine bing_pl_PL = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -870,7 +871,7 @@ const PrepopulatedEngine bing_pt_BR = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -883,7 +884,7 @@ const PrepopulatedEngine bing_pt_PT = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -896,7 +897,7 @@ const PrepopulatedEngine bing_ro_RO = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -909,7 +910,7 @@ const PrepopulatedEngine bing_ru_RU = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -922,7 +923,7 @@ const PrepopulatedEngine bing_sl_SI = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -935,7 +936,7 @@ const PrepopulatedEngine bing_sk_SK = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -948,7 +949,7 @@ const PrepopulatedEngine bing_sv_SE = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -961,7 +962,7 @@ const PrepopulatedEngine bing_th_TH = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -974,7 +975,7 @@ const PrepopulatedEngine bing_tr_TR = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -987,7 +988,7 @@ const PrepopulatedEngine bing_uk_UA = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -1000,7 +1001,7 @@ const PrepopulatedEngine bing_zh_CN = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -1013,7 +1014,7 @@ const PrepopulatedEngine bing_zh_HK = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -1026,7 +1027,7 @@ const PrepopulatedEngine bing_zh_TW = { "UTF-8", L"http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_BING, + SEARCH_ENGINE_BING, IDR_SEARCH_ENGINE_LOGO_BING, 3, }; @@ -1039,7 +1040,7 @@ const PrepopulatedEngine centrum_cz = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_CENTRUM, + SEARCH_ENGINE_CENTRUM, IDR_SEARCH_ENGINE_LOGO_CENTRUM, 26, }; @@ -1052,7 +1053,7 @@ const PrepopulatedEngine centrum_sk = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_CENTRUM, + SEARCH_ENGINE_CENTRUM, IDR_SEARCH_ENGINE_LOGO_CENTRUM, 26, }; @@ -1065,7 +1066,7 @@ const PrepopulatedEngine daum = { "EUC-KR", L"http://sug.search.daum.net/search_nsuggest?mod=fxjson&q={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_DAUM, + SEARCH_ENGINE_DAUM, IDR_SEARCH_ENGINE_LOGO_DAUM, 68, }; @@ -1078,7 +1079,7 @@ const PrepopulatedEngine delfi_lt = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_DELFI, + SEARCH_ENGINE_DELFI, IDR_SEARCH_ENGINE_LOGO_DELFI, 45, }; @@ -1091,7 +1092,7 @@ const PrepopulatedEngine delfi_lv = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_DELFI, + SEARCH_ENGINE_DELFI, IDR_SEARCH_ENGINE_LOGO_DELFI, 45, }; @@ -1104,7 +1105,7 @@ const PrepopulatedEngine diri = { "windows-1251", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_DIRI, + SEARCH_ENGINE_DIRI, IDR_SEARCH_ENGINE_LOGO_DIRI, 32, }; @@ -1117,8 +1118,8 @@ const PrepopulatedEngine eniro_fi = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 29, }; @@ -1130,8 +1131,8 @@ const PrepopulatedEngine eniro_se = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 29, }; @@ -1143,8 +1144,8 @@ const PrepopulatedEngine fonecta_02_fi = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 46, }; @@ -1157,8 +1158,8 @@ const PrepopulatedEngine go = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 40, }; @@ -1170,7 +1171,7 @@ const PrepopulatedEngine goo = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_GOO, + SEARCH_ENGINE_GOO, IDR_SEARCH_ENGINE_LOGO_GOO, 23, }; @@ -1184,9 +1185,9 @@ const PrepopulatedEngine google = { L"q={searchTerms}", "UTF-8", L"{google:baseSuggestURL}search?client=chrome&hl={language}&q={searchTerms}", - L"{google:baseURL}search?{google:RLZ}sourceid=chrome-instant" - L"&ie={inputEncoding}&q={searchTerms}", - TemplateURLPrepopulateData::SEARCH_ENGINE_GOOGLE, + L"{google:baseURL}webhp?{google:RLZ}sourceid=chrome-instant" + L"&ie={inputEncoding}&ion=1{searchTerms}", + SEARCH_ENGINE_GOOGLE, IDR_SEARCH_ENGINE_LOGO_GOOGLE, 1, }; @@ -1199,8 +1200,8 @@ const PrepopulatedEngine guruji = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 38, }; @@ -1212,8 +1213,8 @@ const PrepopulatedEngine hispavista = { "iso-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 18, }; @@ -1225,7 +1226,7 @@ const PrepopulatedEngine in = { "ISO-8859-7", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_IN, + SEARCH_ENGINE_IN, IDR_SEARCH_ENGINE_LOGO_IN, 54, }; @@ -1238,8 +1239,8 @@ const PrepopulatedEngine jabse = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 19, }; @@ -1251,8 +1252,8 @@ const PrepopulatedEngine jubii = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 28, }; @@ -1264,8 +1265,8 @@ const PrepopulatedEngine kvasir = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 73, }; @@ -1277,8 +1278,8 @@ const PrepopulatedEngine latne = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 71, }; @@ -1290,8 +1291,8 @@ const PrepopulatedEngine leit = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 59, }; @@ -1303,8 +1304,8 @@ const PrepopulatedEngine libero = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 63, }; @@ -1316,7 +1317,7 @@ const PrepopulatedEngine mail_ru = { "windows-1251", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_MAILRU, + SEARCH_ENGINE_MAILRU, IDR_SEARCH_ENGINE_LOGO_MAILRU, 83, }; @@ -1329,8 +1330,8 @@ const PrepopulatedEngine maktoob = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 13, }; @@ -1342,8 +1343,8 @@ const PrepopulatedEngine masrawy = { "windows-1256", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 14, }; @@ -1355,8 +1356,8 @@ const PrepopulatedEngine mynet = { "windows-1254", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 101, }; @@ -1368,7 +1369,7 @@ const PrepopulatedEngine najdi = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_NAJDI, + SEARCH_ENGINE_NAJDI, IDR_SEARCH_ENGINE_LOGO_NAJDI, 87, }; @@ -1381,8 +1382,8 @@ const PrepopulatedEngine nate = { "EUC-KR", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 69, }; @@ -1396,7 +1397,7 @@ const PrepopulatedEngine naver = { L"http://ac.search.naver.com/autocompl?m=s&ie={inputEncoding}&oe=utf-8&" L"q={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_NAVER, + SEARCH_ENGINE_NAVER, IDR_SEARCH_ENGINE_LOGO_NAVER, 67, }; @@ -1409,7 +1410,7 @@ const PrepopulatedEngine neti = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_NETI, + SEARCH_ENGINE_NETI, IDR_SEARCH_ENGINE_LOGO_NETI, 44, }; @@ -1422,7 +1423,7 @@ const PrepopulatedEngine netsprint = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_NETSPRINT, + SEARCH_ENGINE_NETSPRINT, IDR_SEARCH_ENGINE_LOGO_NETSPRINT, 30, }; @@ -1435,8 +1436,8 @@ const PrepopulatedEngine nur_kz = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 20, }; @@ -1448,7 +1449,7 @@ const PrepopulatedEngine ok = { "ISO-8859-2", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OK, + SEARCH_ENGINE_OK, IDR_SEARCH_ENGINE_LOGO_OK, 6, }; @@ -1461,8 +1462,8 @@ const PrepopulatedEngine onet = { "ISO-8859-2", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 75, }; @@ -1474,7 +1475,7 @@ const PrepopulatedEngine pogodak_ba = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_POGODAK, + SEARCH_ENGINE_POGODAK, IDR_SEARCH_ENGINE_LOGO_POGODAK, 24, }; @@ -1487,7 +1488,7 @@ const PrepopulatedEngine pogodak_hr = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_POGODAK, + SEARCH_ENGINE_POGODAK, IDR_SEARCH_ENGINE_LOGO_POGODAK, 24, }; @@ -1500,7 +1501,7 @@ const PrepopulatedEngine pogodak_rs = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_POGODAK, + SEARCH_ENGINE_POGODAK, IDR_SEARCH_ENGINE_LOGO_POGODAK, 24, }; @@ -1513,7 +1514,7 @@ const PrepopulatedEngine pogodok = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_POGODOK_MK, + SEARCH_ENGINE_POGODOK_MK, IDR_SEARCH_ENGINE_LOGO_POGODOK_MK, 24, // Really the same engine as Pogodak, just has a small name change. }; @@ -1526,7 +1527,7 @@ const PrepopulatedEngine rambler = { "windows-1251", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_RAMBLER, + SEARCH_ENGINE_RAMBLER, IDR_SEARCH_ENGINE_LOGO_RAMBLER, 16, }; @@ -1539,8 +1540,8 @@ const PrepopulatedEngine rediff = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 37, }; @@ -1552,8 +1553,8 @@ const PrepopulatedEngine rednano = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 41, }; @@ -1565,7 +1566,7 @@ const PrepopulatedEngine sanook = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_SANOOK, + SEARCH_ENGINE_SANOOK, IDR_SEARCH_ENGINE_LOGO_SANOOK, 100, }; @@ -1578,7 +1579,7 @@ const PrepopulatedEngine sapo = { "UTF-8", L"http://pesquisa.sapo.pt/livesapo?q={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_SAPO, + SEARCH_ENGINE_SAPO, IDR_SEARCH_ENGINE_LOGO_SAPO, 77, }; @@ -1591,8 +1592,8 @@ const PrepopulatedEngine search_de_CH = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 51, }; @@ -1604,8 +1605,8 @@ const PrepopulatedEngine search_fr_CH = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 22, }; @@ -1618,7 +1619,7 @@ const PrepopulatedEngine seznam = { L"http:///suggest.fulltext.seznam.cz/?dict=fulltext_ff&phrase={searchTerms}&" L"encoding={inputEncoding}&response_encoding=utf-8", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_SEZNAM, + SEARCH_ENGINE_SEZNAM, IDR_SEARCH_ENGINE_LOGO_SEZNAM, 25, }; @@ -1631,8 +1632,8 @@ const PrepopulatedEngine spray = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 99, }; @@ -1644,8 +1645,8 @@ const PrepopulatedEngine terra_ar = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 90, }; @@ -1657,8 +1658,8 @@ const PrepopulatedEngine terra_es = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 90, }; @@ -1670,7 +1671,7 @@ const PrepopulatedEngine tut = { "windows-1251", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_TUT, + SEARCH_ENGINE_TUT, IDR_SEARCH_ENGINE_LOGO_TUT, 17, }; @@ -1683,8 +1684,8 @@ const PrepopulatedEngine uol = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 82, }; @@ -1696,7 +1697,7 @@ const PrepopulatedEngine virgilio = { "ISO-8859-1", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_VIRGILIO, + SEARCH_ENGINE_VIRGILIO, IDR_SEARCH_ENGINE_LOGO_VIRGILIO, 62, }; @@ -1709,7 +1710,7 @@ const PrepopulatedEngine walla = { "windows-1255", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_WALLA, + SEARCH_ENGINE_WALLA, IDR_SEARCH_ENGINE_LOGO_WALLA, 55, }; @@ -1722,8 +1723,8 @@ const PrepopulatedEngine wp = { "ISO-8859-2", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 76, }; @@ -1735,7 +1736,7 @@ const PrepopulatedEngine yahoo = { "UTF-8", L"http://ff.search.yahoo.com/gossip?output=fxjson&command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1753,7 +1754,7 @@ const PrepopulatedEngine yahoo_ar = { L"http://ar-sayt.ff.search.yahoo.com/gossip-ar-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1767,7 +1768,7 @@ const PrepopulatedEngine yahoo_at = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1782,7 +1783,7 @@ const PrepopulatedEngine yahoo_au = { L"http://aue-sayt.ff.search.yahoo.com/gossip-au-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1797,7 +1798,7 @@ const PrepopulatedEngine yahoo_br = { L"http://br-sayt.ff.search.yahoo.com/gossip-br-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1812,7 +1813,7 @@ const PrepopulatedEngine yahoo_ca = { L"http://gossip.ca.yahoo.com/gossip-ca-sayt?output=fxjsonp&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1826,7 +1827,7 @@ const PrepopulatedEngine yahoo_ch = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1841,7 +1842,7 @@ const PrepopulatedEngine yahoo_cl = { L"http://gossip.telemundo.yahoo.com/gossip-e1-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1857,7 +1858,7 @@ const PrepopulatedEngine yahoo_cn = { // returns in a proprietary format ('|' delimeted word list). NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1872,7 +1873,7 @@ const PrepopulatedEngine yahoo_co = { L"http://gossip.telemundo.yahoo.com/gossip-e1-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1887,7 +1888,7 @@ const PrepopulatedEngine yahoo_de = { L"http://de-sayt.ff.search.yahoo.com/gossip-de-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1901,7 +1902,7 @@ const PrepopulatedEngine yahoo_dk = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1916,7 +1917,7 @@ const PrepopulatedEngine yahoo_es = { L"http://es-sayt.ff.search.yahoo.com/gossip-es-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1930,7 +1931,7 @@ const PrepopulatedEngine yahoo_fi = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1945,7 +1946,7 @@ const PrepopulatedEngine yahoo_fr = { L"http://fr-sayt.ff.search.yahoo.com/gossip-fr-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1962,7 +1963,7 @@ const PrepopulatedEngine yahoo_hk = { // to fxjson, json, or js doesn't help. NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1977,7 +1978,7 @@ const PrepopulatedEngine yahoo_id = { L"http://id-sayt.ff.search.yahoo.com/gossip-id-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -1992,7 +1993,7 @@ const PrepopulatedEngine yahoo_in = { L"http://in-sayt.ff.search.yahoo.com/gossip-in-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2007,7 +2008,7 @@ const PrepopulatedEngine yahoo_it = { L"http://it-sayt.ff.search.yahoo.com/gossip-it-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2021,7 +2022,7 @@ const PrepopulatedEngine yahoo_jp = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOOJP, + SEARCH_ENGINE_YAHOOJP, IDR_SEARCH_ENGINE_LOGO_YAHOOJP, 2, }; @@ -2036,7 +2037,7 @@ const PrepopulatedEngine yahoo_kr = { L"http://kr.atc.search.yahoo.com/atcx.php?property=main&ot=fxjson&" L"ei=utf8&eo=utf8&command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2051,7 +2052,7 @@ const PrepopulatedEngine yahoo_malaysia = { L"http://my-sayt.ff.search.yahoo.com/gossip-my-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2066,7 +2067,7 @@ const PrepopulatedEngine yahoo_mx = { L"http://gossip.mx.yahoo.com/gossip-mx-sayt?output=fxjsonp&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2080,7 +2081,7 @@ const PrepopulatedEngine yahoo_nl = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2094,7 +2095,7 @@ const PrepopulatedEngine yahoo_no = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2109,7 +2110,7 @@ const PrepopulatedEngine yahoo_nz = { L"http://aue-sayt.ff.search.yahoo.com/gossip-nz-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2124,7 +2125,7 @@ const PrepopulatedEngine yahoo_pe = { L"http://gossip.telemundo.yahoo.com/gossip-e1-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2139,7 +2140,7 @@ const PrepopulatedEngine yahoo_ph = { L"http://ph-sayt.ff.search.yahoo.com/gossip-ph-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2153,7 +2154,7 @@ const PrepopulatedEngine yahoo_qc = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOOQC, + SEARCH_ENGINE_YAHOOQC, IDR_SEARCH_ENGINE_LOGO_YAHOOQC, 5, // Can't be 2 as this has to appear in the Canada list alongside yahoo_ca. }; @@ -2167,7 +2168,7 @@ const PrepopulatedEngine yahoo_ru = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2181,7 +2182,7 @@ const PrepopulatedEngine yahoo_se = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2196,7 +2197,7 @@ const PrepopulatedEngine yahoo_sg = { L"http://sg-sayt.ff.search.yahoo.com/gossip-sg-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2211,7 +2212,7 @@ const PrepopulatedEngine yahoo_th = { L"http://th-sayt.ff.search.yahoo.com/gossip-th-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2227,7 +2228,7 @@ const PrepopulatedEngine yahoo_tw = { // returns a JSON file prepended with 'fxsearch=('. NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2242,7 +2243,7 @@ const PrepopulatedEngine yahoo_uk = { L"http://uk-sayt.ff.search.yahoo.com/gossip-uk-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2257,7 +2258,7 @@ const PrepopulatedEngine yahoo_ve = { L"http://gossip.telemundo.yahoo.com/gossip-e1-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2272,7 +2273,7 @@ const PrepopulatedEngine yahoo_vn = { L"http://vn-sayt.ff.search.yahoo.com/gossip-vn-sayt?output=fxjson&" L"command={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO, + SEARCH_ENGINE_YAHOO, IDR_SEARCH_ENGINE_LOGO_YAHOO, 2, }; @@ -2285,8 +2286,8 @@ const PrepopulatedEngine yamli = { "UTF-8", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER, - IDR_SEARCH_ENGINE_LOGO_OTHER, + SEARCH_ENGINE_OTHER, + kNoSearchEngineLogo, 11, }; @@ -2298,7 +2299,7 @@ const PrepopulatedEngine yandex_ru = { "UTF-8", L"http://suggest.yandex.net/suggest-ff.cgi?part={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YANDEX, + SEARCH_ENGINE_YANDEX, IDR_SEARCH_ENGINE_LOGO_YANDEX, 15, }; @@ -2311,7 +2312,7 @@ const PrepopulatedEngine yandex_ua = { "UTF-8", L"http://suggest.yandex.net/suggest-ff.cgi?part={searchTerms}", NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_YANDEX, + SEARCH_ENGINE_YANDEX, IDR_SEARCH_ENGINE_LOGO_YANDEX, 15, }; @@ -2324,7 +2325,7 @@ const PrepopulatedEngine zoznam = { "windows-1250", NULL, NULL, - TemplateURLPrepopulateData::SEARCH_ENGINE_ZOZNAM, + SEARCH_ENGINE_ZOZNAM, IDR_SEARCH_ENGINE_LOGO_ZOZNAM, 85, }; @@ -3349,7 +3350,7 @@ void RegisterUserPrefs(PrefService* prefs) { int GetDataVersion(PrefService* prefs) { // Increment this if you change the above data in ways that mean users with // existing data should get a new version. - const int kCurrentDataVersion = 30; + const int kCurrentDataVersion = 32; if (!prefs) return kCurrentDataVersion; // If a version number exist in the preferences file, it overrides the @@ -3558,4 +3559,15 @@ TemplateURL* GetEngineForOrigin(PrefService* prefs, const GURL& url_to_find) { return NULL; } +int GetSearchEngineLogo(const GURL& url_to_find) { + GURL origin_to_find = url_to_find.GetOrigin(); + for (size_t i = 0; i < arraysize(kAllEngines); ++i) { + std::string url_utf8_string(ToUTF8(kAllEngines[i]->search_url)); + GURL url(url_utf8_string); + if (origin_to_find == url.GetOrigin()) + return kAllEngines[i]->logo_id; + } + return kNoSearchEngineLogo; +} + } // namespace TemplateURLPrepopulateData diff --git a/chrome/browser/search_engines/template_url_prepopulate_data.h b/chrome/browser/search_engines/template_url_prepopulate_data.h index 14888a2..f8b721a 100644 --- a/chrome/browser/search_engines/template_url_prepopulate_data.h +++ b/chrome/browser/search_engines/template_url_prepopulate_data.h @@ -14,46 +14,6 @@ class TemplateURL; namespace TemplateURLPrepopulateData { -// Enum to record the user's default search engine choice in UMA. Add new -// search engines at the bottom and do not delete from this list, so as not -// to disrupt UMA data already recorded. -enum SearchEngineType { - SEARCH_ENGINE_OTHER = 0, // At the top in case of future list changes. - SEARCH_ENGINE_GOOGLE, - SEARCH_ENGINE_YAHOO, - SEARCH_ENGINE_YAHOOJP, - SEARCH_ENGINE_BING, - SEARCH_ENGINE_ASK, - SEARCH_ENGINE_YANDEX, - SEARCH_ENGINE_SEZNAM, - SEARCH_ENGINE_CENTRUM, - SEARCH_ENGINE_NETSPRINT, - SEARCH_ENGINE_VIRGILIO, - SEARCH_ENGINE_MAILRU, - SEARCH_ENGINE_ABCSOK, - SEARCH_ENGINE_ALTAVISTA, - SEARCH_ENGINE_BAIDU, - SEARCH_ENGINE_DAUM, - SEARCH_ENGINE_DELFI, - SEARCH_ENGINE_DIRI, - SEARCH_ENGINE_GOO, - SEARCH_ENGINE_IN, - SEARCH_ENGINE_NAJDI, - SEARCH_ENGINE_NAVER, - SEARCH_ENGINE_NETI, - SEARCH_ENGINE_OK, - SEARCH_ENGINE_POGODAK, - SEARCH_ENGINE_POGODOK_MK, - SEARCH_ENGINE_RAMBLER, - SEARCH_ENGINE_SANOOK, - SEARCH_ENGINE_SAPO, - SEARCH_ENGINE_TUT, - SEARCH_ENGINE_WALLA, - SEARCH_ENGINE_ZOZNAM, - SEARCH_ENGINE_YAHOOQC, - SEARCH_ENGINE_MAX // Bounding max value needed for UMA histogram macro. -}; - void RegisterUserPrefs(PrefService* prefs); // Returns the current version of the prepopulate data, so callers can know when @@ -78,6 +38,9 @@ TemplateURL* GetPrepopulatedDefaultSearch(PrefService* prefs); // TemplateURL. TemplateURL* GetEngineForOrigin(PrefService* prefs, const GURL& url_to_find); +// Returns search engine logo for URLs known to have a search engine logo. +int GetSearchEngineLogo(const GURL& url_to_find); + } // namespace TemplateURLPrepopulateData #endif // CHROME_BROWSER_SEARCH_ENGINES_TEMPLATE_URL_PREPOPULATE_DATA_H_ diff --git a/chrome/browser/search_engines/template_url_prepopulate_data_unittest.cc b/chrome/browser/search_engines/template_url_prepopulate_data_unittest.cc index 4053005..5275146 100644 --- a/chrome/browser/search_engines/template_url_prepopulate_data_unittest.cc +++ b/chrome/browser/search_engines/template_url_prepopulate_data_unittest.cc @@ -6,12 +6,14 @@ #include "base/scoped_temp_dir.h" #include "base/scoped_vector.h" #include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/search_engines/search_engine_type.h" #include "chrome/browser/search_engines/search_terms_data.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_prepopulate_data.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/common/pref_names.h" #include "chrome/test/testing_profile.h" +#include "grit/theme_resources.h" #include "testing/gtest/include/gtest/gtest.h" typedef testing::Test TemplateURLPrepopulateDataTest; @@ -131,8 +133,7 @@ TEST_F(TemplateURLPrepopulateDataTest, ProvidersFromPrefs) { EXPECT_EQ("foi.com", t_urls[0]->GetFavIconURL().host()); EXPECT_EQ(1u, t_urls[0]->input_encodings().size()); EXPECT_EQ(1001, t_urls[0]->prepopulate_id()); - EXPECT_EQ(TemplateURLPrepopulateData::SEARCH_ENGINE_GOOGLE, - t_urls[0]->search_engine_type()); + EXPECT_EQ(SEARCH_ENGINE_GOOGLE, t_urls[0]->search_engine_type()); EXPECT_EQ(0, t_urls[0]->logo_id()); } @@ -178,3 +179,16 @@ TEST_F(TemplateURLPrepopulateDataTest, SearchEngineFromOrigin) { profile.GetPrefs(), not_a_search_engine)); } + +TEST_F(TemplateURLPrepopulateDataTest, GetSearchEngineLogo) { + GURL bad_engine("http://example.com/"); + EXPECT_EQ(kNoSearchEngineLogo, + TemplateURLPrepopulateData::GetSearchEngineLogo(bad_engine)); + GURL engine_with_logo("http://www.ask.com/"); + EXPECT_EQ(IDR_SEARCH_ENGINE_LOGO_ASK, + TemplateURLPrepopulateData::GetSearchEngineLogo(engine_with_logo)); + GURL engine_no_logo("http://araby.com/"); + EXPECT_EQ(kNoSearchEngineLogo, + TemplateURLPrepopulateData::GetSearchEngineLogo(engine_no_logo)); + +} diff --git a/chrome/browser/service/service_process_control.cc b/chrome/browser/service/service_process_control.cc index 534cc5a..74efc36 100644 --- a/chrome/browser/service/service_process_control.cc +++ b/chrome/browser/service/service_process_control.cc @@ -9,6 +9,7 @@ #include "base/process_util.h" #include "base/stl_util-inl.h" #include "base/thread.h" +#include "base/thread_restrictions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/io_thread.h" @@ -38,27 +39,37 @@ class ServiceProcessControl::Launcher void Run(Task* task) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + notify_task_.reset(task); BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, - NewRunnableMethod(this, &Launcher::DoRun, task)); + NewRunnableMethod(this, &Launcher::DoRun)); } bool launched() const { return launched_; } private: - void DoRun(Task* task) { + void DoRun() { + DCHECK(notify_task_.get()); base::LaunchApp(*cmd_line_.get(), false, true, NULL); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - NewRunnableMethod(this, &Launcher::DoDetectLaunched, task)); + NewRunnableMethod(this, &Launcher::DoDetectLaunched)); } - void DoDetectLaunched(Task* task) { + void DoDetectLaunched() { + DCHECK(notify_task_.get()); const uint32 kMaxLaunchDetectRetries = 10; - launched_ = CheckServiceProcessReady(); + { + // We should not be doing blocking disk IO from this thread! + // Temporarily allowed until we fix + // http://code.google.com/p/chromium/issues/detail?id=60207 + base::ThreadRestrictions::ScopedAllowIO allow_io; + launched_ = CheckServiceProcessReady(); + } + if (launched_ || (retry_count_ >= kMaxLaunchDetectRetries)) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - NewRunnableMethod(this, &Launcher::Notify, task)); + NewRunnableMethod(this, &Launcher::Notify)); return; } retry_count_++; @@ -66,17 +77,19 @@ class ServiceProcessControl::Launcher const int kDetectLaunchRetry = 2000; MessageLoop::current()->PostDelayedTask( FROM_HERE, - NewRunnableMethod(this, &Launcher::DoDetectLaunched, task), + NewRunnableMethod(this, &Launcher::DoDetectLaunched), kDetectLaunchRetry); } - void Notify(Task* task) { - task->Run(); - delete task; + void Notify() { + DCHECK(notify_task_.get()); + notify_task_->Run(); + notify_task_.reset(); } ServiceProcessControl* process_; scoped_ptr<CommandLine> cmd_line_; + scoped_ptr<Task> notify_task_; bool launched_; uint32 retry_count_; }; diff --git a/chrome/browser/session_history_uitest.cc b/chrome/browser/session_history_uitest.cc index 278672a..f30a93a 100644 --- a/chrome/browser/session_history_uitest.cc +++ b/chrome/browser/session_history_uitest.cc @@ -80,7 +80,14 @@ class SessionHistoryTest : public UITest { net::TestServer test_server_; }; -TEST_F(SessionHistoryTest, BasicBackForward) { +#if defined(OS_WIN) +// See http://crbug.com/61619 +#define MAYBE_BasicBackForward FLAKY_BasicBackForward +#else +#define MAYBE_BasicBackForward BasicBackForward +#endif + +TEST_F(SessionHistoryTest, MAYBE_BasicBackForward) { ASSERT_TRUE(test_server_.Start()); // about:blank should be loaded first. @@ -138,13 +145,14 @@ TEST_F(SessionHistoryTest, BasicBackForward) { EXPECT_EQ(L"bot3", GetTabTitle()); } -// Test that back/forward works when navigating in subframes. -// Fails on Windows. See crbug.com/TODO #if defined(OS_WIN) +// See http://crbug.com/61619 #define MAYBE_FrameBackForward FLAKY_FrameBackForward #else #define MAYBE_FrameBackForward FrameBackForward #endif + +// Test that back/forward works when navigating in subframes. TEST_F(SessionHistoryTest, MAYBE_FrameBackForward) { ASSERT_TRUE(test_server_.Start()); @@ -208,8 +216,15 @@ TEST_F(SessionHistoryTest, MAYBE_FrameBackForward) { EXPECT_EQ(frames, GetTabURL()); } +#if defined(OS_WIN) +// See http://crbug.com/61619 +#define MAYBE_FrameFormBackForward FLAKY_FrameFormBackForward +#else +#define MAYBE_FrameFormBackForward FrameFormBackForward +#endif + // Test that back/forward preserves POST data and document state in subframes. -TEST_F(SessionHistoryTest, FrameFormBackForward) { +TEST_F(SessionHistoryTest, MAYBE_FrameFormBackForward) { ASSERT_TRUE(test_server_.Start()); // about:blank should be loaded first. @@ -311,9 +326,18 @@ TEST_F(SessionHistoryTest, DISABLED_CrossFrameFormBackForward) { EXPECT_EQ(frames, GetTabURL()); } + +#if defined(OS_WIN) +// See http://crbug.com/61619 +#define MAYBE_FragmentBackForward FLAKY_FragmentBackForward +#else +// http://crbug.com/62156 +#define MAYBE_FragmentBackForward DISABLED_FragmentBackForward +#endif + // Test that back/forward entries are created for reference fragment // navigations. Bug 730379. -TEST_F(SessionHistoryTest, FragmentBackForward) { +TEST_F(SessionHistoryTest, MAYBE_FragmentBackForward) { ASSERT_TRUE(test_server_.Start()); // about:blank should be loaded first. @@ -491,7 +515,14 @@ TEST_F(SessionHistoryTest, FLAKY_HistorySearchXSS) { EXPECT_EQ(L"History", GetTabTitle()); } -TEST_F(SessionHistoryTest, LocationChangeInSubframe) { +#if defined(OS_WIN) +// See http://crbug.com/61619 +#define MAYBE_LocationChangeInSubframe FLAKY_LocationChangeInSubframe +#else +#define MAYBE_LocationChangeInSubframe LocationChangeInSubframe +#endif + +TEST_F(SessionHistoryTest, MAYBE_LocationChangeInSubframe) { ASSERT_TRUE(test_server_.Start()); ASSERT_TRUE(tab_->NavigateToURL(test_server_.GetURL( diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc index 77cf37e..1321422 100644 --- a/chrome/browser/sessions/session_restore.cc +++ b/chrome/browser/sessions/session_restore.cc @@ -13,6 +13,7 @@ #include "base/string_util.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/extensions/extensions_service.h" @@ -503,10 +504,6 @@ class SessionRestoreImpl : public NotificationObserver { } void RestoreTabsToBrowser(const SessionWindow& window, Browser* browser) { -#if defined(OS_CHROMEOS) - chromeos::BootTimesLoader::Get()->AddLoginTimeMarker( - "SessionRestore", true); -#endif DCHECK(!window.tabs.empty()); for (std::vector<SessionTab*>::const_iterator i = window.tabs.begin(); i != window.tabs.end(); ++i) { @@ -556,10 +553,12 @@ class SessionRestoreImpl : public NotificationObserver { if (i == 0) add_types |= TabStripModel::ADD_SELECTED; int index = browser->GetIndexForInsertionDuringRestore(i); - Browser::AddTabWithURLParams params(urls[i], PageTransition::START_PAGE); - params.index = index; - params.add_types = add_types; - browser->AddTabWithURL(¶ms); + browser::NavigateParams params(browser, urls[i], + PageTransition::START_PAGE); + params.disposition = i == 0 ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; + params.tabstrip_index = index; + params.tabstrip_add_types = add_types; + browser::Navigate(¶ms); } } diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc index 9ff065d..c05a34b 100644 --- a/chrome/browser/sessions/session_restore_browsertest.cc +++ b/chrome/browser/sessions/session_restore_browsertest.cc @@ -77,14 +77,10 @@ IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreIndividualTabFromWindow) { // Add and navigate three tabs. ui_test_utils::NavigateToURL(browser(), url1); - Browser::AddTabWithURLParams params1(url2, PageTransition::LINK); - browser()->AddTabWithURL(¶ms1); - EXPECT_EQ(browser(), params1.target); + browser()->AddSelectedTabWithURL(url2, PageTransition::LINK); ui_test_utils::WaitForNavigationInCurrentTab(browser()); - Browser::AddTabWithURLParams params2(url3, PageTransition::LINK); - browser()->AddTabWithURL(¶ms2); - EXPECT_EQ(browser(), params2.target); + browser()->AddSelectedTabWithURL(url3, PageTransition::LINK); ui_test_utils::WaitForNavigationInCurrentTab(browser()); TabRestoreService* service = browser()->profile()->GetTabRestoreService(); diff --git a/chrome/browser/sessions/session_restore_uitest.cc b/chrome/browser/sessions/session_restore_uitest.cc index 9e56711..051ecf7 100644 --- a/chrome/browser/sessions/session_restore_uitest.cc +++ b/chrome/browser/sessions/session_restore_uitest.cc @@ -6,7 +6,7 @@ #include "base/file_path.h" #include "base/scoped_ptr.h" #include "base/string_number_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/defaults.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" @@ -378,6 +378,7 @@ TEST_F(SessionRestoreUITest, DontRestoreWhileIncognito) { // Launches an app window, closes tabbed browser, launches and makes sure // we restore the tabbed browser url. +// Flaky: http://crbug.com/29110 TEST_F(SessionRestoreUITest, FLAKY_RestoreAfterClosingTabbedBrowserWithAppAndLaunching) { NavigateToURL(url1_); diff --git a/chrome/browser/sessions/session_service.cc b/chrome/browser/sessions/session_service.cc index 9b2db65..1cf4493 100644 --- a/chrome/browser/sessions/session_service.cc +++ b/chrome/browser/sessions/session_service.cc @@ -417,9 +417,9 @@ SessionService::Handle SessionService::GetCurrentSession( if (pending_window_close_ids_.empty()) { // If there are no pending window closes, we can get the current session // from memory. - scoped_refptr<InternalSessionRequest> request = new InternalSessionRequest( + scoped_refptr<InternalSessionRequest> request(new InternalSessionRequest( NewCallback(this, &SessionService::OnGotSessionCommands), - callback); + callback)); AddRequest(request, consumer); IdToRange tab_to_available_range; std::set<SessionID::id_type> windows_to_track; diff --git a/chrome/browser/sessions/session_service.h b/chrome/browser/sessions/session_service.h index 96bb27c..8dedb8d 100644 --- a/chrome/browser/sessions/session_service.h +++ b/chrome/browser/sessions/session_service.h @@ -240,7 +240,7 @@ class SessionService : public BaseSessionService, SessionCommand* CreatePinnedStateCommand(const SessionID& tab_id, bool is_pinned); - // Callback form the backend for getting the commands from the previous + // Callback from the backend for getting the commands from the previous // or save file. Converts the commands in SessionWindows and notifies // the real callback. void OnGotSessionCommands( diff --git a/chrome/browser/sessions/session_service_test_helper.h b/chrome/browser/sessions/session_service_test_helper.h index c0caa7e..062bb1c 100644 --- a/chrome/browser/sessions/session_service_test_helper.h +++ b/chrome/browser/sessions/session_service_test_helper.h @@ -63,6 +63,7 @@ class SessionServiceTestHelper { size_t nav_count); void set_service(SessionService* service) { service_ = service; } + SessionService* ReleaseService() { return service_.release(); } SessionService* service() { return service_.get(); } SessionBackend* backend(); diff --git a/chrome/browser/sessions/session_types.cc b/chrome/browser/sessions/session_types.cc index 995a9a2..2ebfbb1 100644 --- a/chrome/browser/sessions/session_types.cc +++ b/chrome/browser/sessions/session_types.cc @@ -110,7 +110,7 @@ SessionWindow::~SessionWindow() { // ForeignSession -------------------------------------------------------------- -ForeignSession::ForeignSession() : foreign_tession_tag("invalid") { +ForeignSession::ForeignSession() : foreign_session_tag("invalid") { } ForeignSession::~ForeignSession() { diff --git a/chrome/browser/sessions/session_types.h b/chrome/browser/sessions/session_types.h index 119392a..4f84246 100644 --- a/chrome/browser/sessions/session_types.h +++ b/chrome/browser/sessions/session_types.h @@ -185,7 +185,7 @@ struct ForeignSession { ~ForeignSession(); // Unique tag for each session. - std::string foreign_tession_tag; + std::string foreign_session_tag; std::vector<SessionWindow*> windows; }; diff --git a/chrome/browser/sessions/tab_restore_service.cc b/chrome/browser/sessions/tab_restore_service.cc index 3d78771..c295d76 100644 --- a/chrome/browser/sessions/tab_restore_service.cc +++ b/chrome/browser/sessions/tab_restore_service.cc @@ -476,7 +476,7 @@ void TabRestoreService::PopulateTab(Tab* tab, if (tab->current_navigation_index == -1 && entry_count > 0) tab->current_navigation_index = 0; - Extension* extension = controller->tab_contents()->extension_app(); + const Extension* extension = controller->tab_contents()->extension_app(); if (extension) tab->extension_app_id = extension->id(); diff --git a/chrome/browser/sidebar/sidebar_container.h b/chrome/browser/sidebar/sidebar_container.h index ac74ece..0f78988 100644 --- a/chrome/browser/sidebar/sidebar_container.h +++ b/chrome/browser/sidebar/sidebar_container.h @@ -105,7 +105,7 @@ class SidebarContainer virtual void LoadingStateChanged(TabContents* source) {} virtual void CloseContents(TabContents* source) {} virtual void MoveContents(TabContents* source, const gfx::Rect& pos) {} - virtual bool IsPopup(TabContents* source) { return false; } + virtual bool IsPopup(TabContents* source) const { return false; } virtual void URLStarredChanged(TabContents* source, bool starred) {} virtual void UpdateTargetURL(TabContents* source, const GURL& url) {} virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {} diff --git a/chrome/browser/sidebar/sidebar_manager.cc b/chrome/browser/sidebar/sidebar_manager.cc index 62c46c1..e77041c 100644 --- a/chrome/browser/sidebar/sidebar_manager.cc +++ b/chrome/browser/sidebar/sidebar_manager.cc @@ -18,6 +18,13 @@ #include "chrome/common/pref_names.h" #include "googleurl/src/gurl.h" +struct SidebarManager::SidebarStateForTab { + // Sidebars linked to this tab. + ContentIdToSidebarHostMap content_id_to_sidebar_host; + // Content id of the currently active (expanded and visible) sidebar. + std::string active_content_id; +}; + // static SidebarManager* SidebarManager::GetInstance() { return g_browser_process->sidebar_manager(); diff --git a/chrome/browser/sidebar/sidebar_manager.h b/chrome/browser/sidebar/sidebar_manager.h index a7fe8b1..6f2d20b 100644 --- a/chrome/browser/sidebar/sidebar_manager.h +++ b/chrome/browser/sidebar/sidebar_manager.h @@ -127,25 +127,18 @@ class SidebarManager : public NotificationObserver, // This map stores sidebars linked to a particular tab. Sidebars are // identified by their unique content id (string). - typedef std::map<std::string, SidebarContainer*> - ContentIdToSidebarHostMap; + typedef std::map<std::string, SidebarContainer*> ContentIdToSidebarHostMap; + // These two maps are for tracking dependencies between tabs and // their SidebarContainers. // // SidebarManager start listening to SidebarContainers when they are put // into these maps and removes them when they are closing. - typedef struct { - // Sidebars linked to this tab. - ContentIdToSidebarHostMap content_id_to_sidebar_host; - // Content id of the currently active (expanded and visible) sidebar. - std::string active_content_id; - } SidebarStateForTab; - typedef std::map<TabContents*, SidebarStateForTab> - TabToSidebarHostMap; + struct SidebarStateForTab; + typedef std::map<TabContents*, SidebarStateForTab> TabToSidebarHostMap; TabToSidebarHostMap tab_to_sidebar_host_; - typedef std::map<SidebarContainer*, TabContents*> - SidebarHostToTabMap; + typedef std::map<SidebarContainer*, TabContents*> SidebarHostToTabMap; SidebarHostToTabMap sidebar_host_to_tab_; DISALLOW_COPY_AND_ASSIGN(SidebarManager); diff --git a/chrome/browser/speech/endpointer/energy_endpointer_params.cc b/chrome/browser/speech/endpointer/energy_endpointer_params.cc new file mode 100644 index 0000000..1ab044a --- /dev/null +++ b/chrome/browser/speech/endpointer/energy_endpointer_params.cc @@ -0,0 +1,53 @@ +// 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/speech/endpointer/energy_endpointer_params.h" + +namespace speech_input { + +EnergyEndpointerParams::EnergyEndpointerParams() { + SetDefaults(); +} + +void EnergyEndpointerParams::SetDefaults() { + frame_period_ = 0.01f; + frame_duration_ = 0.01f; + endpoint_margin_ = 0.2f; + onset_window_ = 0.15f; + speech_on_window_ = 0.4f; + offset_window_ = 0.15f; + onset_detect_dur_ = 0.09f; + onset_confirm_dur_ = 0.075f; + on_maintain_dur_ = 0.10f; + offset_confirm_dur_ = 0.12f; + decision_threshold_ = 150.0f; + min_decision_threshold_ = 50.0f; + fast_update_dur_ = 0.2f; + sample_rate_ = 8000.0f; + min_fundamental_frequency_ = 57.143f; + max_fundamental_frequency_ = 400.0f; + contamination_rejection_period_ = 0.25f; +} + +void EnergyEndpointerParams::operator=(const EnergyEndpointerParams& source) { + frame_period_ = source.frame_period(); + frame_duration_ = source.frame_duration(); + endpoint_margin_ = source.endpoint_margin(); + onset_window_ = source.onset_window(); + speech_on_window_ = source.speech_on_window(); + offset_window_ = source.offset_window(); + onset_detect_dur_ = source.onset_detect_dur(); + onset_confirm_dur_ = source.onset_confirm_dur(); + on_maintain_dur_ = source.on_maintain_dur(); + offset_confirm_dur_ = source.offset_confirm_dur(); + decision_threshold_ = source.decision_threshold(); + min_decision_threshold_ = source.min_decision_threshold(); + fast_update_dur_ = source.fast_update_dur(); + sample_rate_ = source.sample_rate(); + min_fundamental_frequency_ = source.min_fundamental_frequency(); + max_fundamental_frequency_ = source.max_fundamental_frequency(); + contamination_rejection_period_ = source.contamination_rejection_period(); +} + +} // namespace speech_input diff --git a/chrome/browser/speech/endpointer/energy_endpointer_params.h b/chrome/browser/speech/endpointer/energy_endpointer_params.h index c99ff99..86e44c9 100644 --- a/chrome/browser/speech/endpointer/energy_endpointer_params.h +++ b/chrome/browser/speech/endpointer/energy_endpointer_params.h @@ -12,49 +12,11 @@ namespace speech_input { // Input parameters for the EnergyEndpointer class. class EnergyEndpointerParams { public: - EnergyEndpointerParams() { - SetDefaults(); - } - - void SetDefaults() { - frame_period_ = 0.01f; - frame_duration_ = 0.01f; - endpoint_margin_ = 0.2f; - onset_window_ = 0.15f; - speech_on_window_ = 0.4f; - offset_window_ = 0.15f; - onset_detect_dur_ = 0.09f; - onset_confirm_dur_ = 0.075f; - on_maintain_dur_ = 0.10f; - offset_confirm_dur_ = 0.12f; - decision_threshold_ = 150.0f; - min_decision_threshold_ = 50.0f; - fast_update_dur_ = 0.2f; - sample_rate_ = 8000.0f; - min_fundamental_frequency_ = 57.143f; - max_fundamental_frequency_ = 400.0f; - contamination_rejection_period_ = 0.25f; - } - - void operator=(const EnergyEndpointerParams& source) { - frame_period_ = source.frame_period(); - frame_duration_ = source.frame_duration(); - endpoint_margin_ = source.endpoint_margin(); - onset_window_ = source.onset_window(); - speech_on_window_ = source.speech_on_window(); - offset_window_ = source.offset_window(); - onset_detect_dur_ = source.onset_detect_dur(); - onset_confirm_dur_ = source.onset_confirm_dur(); - on_maintain_dur_ = source.on_maintain_dur(); - offset_confirm_dur_ = source.offset_confirm_dur(); - decision_threshold_ = source.decision_threshold(); - min_decision_threshold_ = source.min_decision_threshold(); - fast_update_dur_ = source.fast_update_dur(); - sample_rate_ = source.sample_rate(); - min_fundamental_frequency_ = source.min_fundamental_frequency(); - max_fundamental_frequency_ = source.max_fundamental_frequency(); - contamination_rejection_period_ = source.contamination_rejection_period(); - } + EnergyEndpointerParams(); + + void SetDefaults(); + + void operator=(const EnergyEndpointerParams& source); // Accessors and mutators float frame_period() const { return frame_period_; } diff --git a/chrome/browser/speech/speech_input_browsertest.cc b/chrome/browser/speech/speech_input_browsertest.cc index 2f428ce..472e30c 100644 --- a/chrome/browser/speech/speech_input_browsertest.cc +++ b/chrome/browser/speech/speech_input_browsertest.cc @@ -32,22 +32,29 @@ const char* kTestResult = "Pictures of the moon"; class FakeSpeechInputManager : public SpeechInputManager { public: - explicit FakeSpeechInputManager() + FakeSpeechInputManager() : caller_id_(0), delegate_(NULL) { } + std::string grammar() { + return grammar_; + } + // SpeechInputManager methods. void StartRecognition(Delegate* delegate, int caller_id, int render_process_id, int render_view_id, - const gfx::Rect& element_rect) { + const gfx::Rect& element_rect, + const std::string& language, + const std::string& grammar) { VLOG(1) << "StartRecognition invoked."; EXPECT_EQ(0, caller_id_); EXPECT_EQ(NULL, delegate_); caller_id_ = caller_id; delegate_ = delegate; + grammar_ = grammar; // Give the fake result in a short while. MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(this, &FakeSpeechInputManager::SetFakeRecognitionResult)); @@ -69,8 +76,9 @@ class FakeSpeechInputManager : public SpeechInputManager { if (caller_id_) { // Do a check in case we were cancelled.. VLOG(1) << "Setting fake recognition result."; delegate_->DidCompleteRecording(caller_id_); - delegate_->SetRecognitionResult(caller_id_, - ASCIIToUTF16(kTestResult)); + SpeechInputResultArray results; + results.push_back(SpeechInputResultItem(ASCIIToUTF16(kTestResult), 1.0)); + delegate_->SetRecognitionResult(caller_id_, results); delegate_->DidCompleteRecognition(caller_id_); caller_id_ = 0; delegate_ = NULL; @@ -80,14 +88,9 @@ class FakeSpeechInputManager : public SpeechInputManager { int caller_id_; Delegate* delegate_; + std::string grammar_; }; -// Factory method. -SpeechInputManager* fakeManagerAccessor() { - static FakeSpeechInputManager fake_speech_input_manager; - return &fake_speech_input_manager; -} - class SpeechInputBrowserTest : public InProcessBrowserTest { public: // InProcessBrowserTest methods @@ -95,49 +98,94 @@ class SpeechInputBrowserTest : public InProcessBrowserTest { const FilePath kTestDir(FILE_PATH_LITERAL("speech")); return ui_test_utils::GetTestUrl(kTestDir, FilePath(filename)); } + + protected: + void LoadAndRunSpeechInputTest(const FilePath::CharType* filename) { + // The test page calculates the speech button's coordinate in the page on + // load & sets that coordinate in the URL fragment. We send mouse down & up + // events at that coordinate to trigger speech recognition. + GURL test_url = testUrl(filename); + ui_test_utils::NavigateToURL(browser(), test_url); + std::string coords = browser()->GetSelectedTabContents()->GetURL().ref(); + VLOG(1) << "Coordinates given by script: " << coords; + int comma_pos = coords.find(','); + ASSERT_NE(-1, comma_pos); + int x = 0; + ASSERT_TRUE(base::StringToInt(coords.substr(0, comma_pos).c_str(), &x)); + int y = 0; + ASSERT_TRUE(base::StringToInt(coords.substr(comma_pos + 1).c_str(), &y)); + + WebKit::WebMouseEvent mouse_event; + mouse_event.type = WebKit::WebInputEvent::MouseDown; + mouse_event.button = WebKit::WebMouseEvent::ButtonLeft; + mouse_event.x = x; + mouse_event.y = y; + mouse_event.clickCount = 1; + TabContents* tab_contents = browser()->GetSelectedTabContents(); + tab_contents->render_view_host()->ForwardMouseEvent(mouse_event); + mouse_event.type = WebKit::WebInputEvent::MouseUp; + tab_contents->render_view_host()->ForwardMouseEvent(mouse_event); + + // The fake speech input manager would receive the speech input + // request and return the test string as recognition result. The test page + // then sets the URL fragment as 'pass' if it received the expected string. + ui_test_utils::WaitForNavigations(&tab_contents->controller(), 1); + EXPECT_EQ("pass", browser()->GetSelectedTabContents()->GetURL().ref()); + } + + // InProcessBrowserTest methods. + virtual void SetUpInProcessBrowserTestFixture() { + speech_input_manager_ = &fake_speech_input_manager_; + + // Inject the fake manager factory so that the test result is returned to + // the web page. + SpeechInputDispatcherHost::set_manager_accessor(&fakeManagerAccessor); + } + + virtual void TearDownInProcessBrowserTestFixture() { + speech_input_manager_ = NULL; + } + + // Factory method. + static SpeechInputManager* fakeManagerAccessor() { + return speech_input_manager_; + } + + FakeSpeechInputManager fake_speech_input_manager_; + + // This is used by the static |fakeManagerAccessor|, and it is a pointer + // rather than a direct instance per the style guide. + static SpeechInputManager* speech_input_manager_; }; +SpeechInputManager* SpeechInputBrowserTest::speech_input_manager_ = NULL; + // Marked as FLAKY due to http://crbug.com/51337 // // TODO(satish): Once this flakiness has been fixed, add a second test here to // check for sending many clicks in succession to the speech button and verify // that it doesn't cause any crash but works as expected. This should act as the // test for http://crbug.com/59173 -IN_PROC_BROWSER_TEST_F(SpeechInputBrowserTest, FLAKY_TestBasicRecognition) { - // Inject the fake manager factory so that the test result is returned to the - // web page. - SpeechInputDispatcherHost::set_manager_accessor(&fakeManagerAccessor); - - // The test page calculates the speech button's coordinate in the page on load - // and sets that coordinate in the URL fragment. We send mouse down & up - // events at that coordinate to trigger speech recognition. - GURL test_url = testUrl(FILE_PATH_LITERAL("basic_recognition.html")); - ui_test_utils::NavigateToURL(browser(), test_url); - std::string coords = browser()->GetSelectedTabContents()->GetURL().ref(); - VLOG(1) << "Coordinates given by script: " << coords; - int comma_pos = coords.find(','); - ASSERT_NE(-1, comma_pos); - int x = 0; - ASSERT_TRUE(base::StringToInt(coords.substr(0, comma_pos).c_str(), &x)); - int y = 0; - ASSERT_TRUE(base::StringToInt(coords.substr(comma_pos + 1).c_str(), &y)); - - WebKit::WebMouseEvent mouse_event; - mouse_event.type = WebKit::WebInputEvent::MouseDown; - mouse_event.button = WebKit::WebMouseEvent::ButtonLeft; - mouse_event.x = x; - mouse_event.y = y; - mouse_event.clickCount = 1; - TabContents* tab_contents = browser()->GetSelectedTabContents(); - tab_contents->render_view_host()->ForwardMouseEvent(mouse_event); - mouse_event.type = WebKit::WebInputEvent::MouseUp; - tab_contents->render_view_host()->ForwardMouseEvent(mouse_event); - - // The above defined fake speech input manager would receive the speech input - // request and return the test string as recognition result. The test page - // then sets the URL fragment as 'pass' if it received the expected string. - ui_test_utils::WaitForNavigations(&tab_contents->controller(), 1); - EXPECT_EQ("pass", browser()->GetSelectedTabContents()->GetURL().ref()); +#if defined(OS_WIN) +#define MAYBE_TestBasicRecognition FLAKY_TestBasicRecognition +#else +#define MAYBE_TestBasicRecognition TestBasicRecognition +#endif +IN_PROC_BROWSER_TEST_F(SpeechInputBrowserTest, MAYBE_TestBasicRecognition) { + LoadAndRunSpeechInputTest(FILE_PATH_LITERAL("basic_recognition.html")); + EXPECT_TRUE(fake_speech_input_manager_.grammar().empty()); +} + +// Marked as FLAKY due to http://crbug.com/51337 +#if defined(OS_WIN) +#define MAYBE_GrammarAttribute FLAKY_GrammarAttribute +#else +#define MAYBE_GrammarAttribute GrammarAttribute +#endif +IN_PROC_BROWSER_TEST_F(SpeechInputBrowserTest, MAYBE_GrammarAttribute) { + LoadAndRunSpeechInputTest(FILE_PATH_LITERAL("grammar_attribute.html")); + EXPECT_EQ("http://example.com/grammar.xml", + fake_speech_input_manager_.grammar()); } } // namespace speech_input diff --git a/chrome/browser/speech/speech_input_bubble_gtk.cc b/chrome/browser/speech/speech_input_bubble_gtk.cc index 55edac3..923f327 100644 --- a/chrome/browser/speech/speech_input_bubble_gtk.cc +++ b/chrome/browser/speech/speech_input_bubble_gtk.cc @@ -25,7 +25,7 @@ const int kIconHorizontalPadding = 30; const int kButtonBarHorizontalSpacing = 10; // Use black for text labels since the bubble has white background. -const GdkColor kLabelTextColor = gfx::kGdkBlack; +const GdkColor kLabelTextColor = gtk_util::kGdkBlack; // Implementation of SpeechInputBubble for GTK. This shows a speech input // info bubble on screen. diff --git a/chrome/browser/speech/speech_input_dispatcher_host.cc b/chrome/browser/speech/speech_input_dispatcher_host.cc index d9f22ee..1563af3 100644 --- a/chrome/browser/speech/speech_input_dispatcher_host.cc +++ b/chrome/browser/speech/speech_input_dispatcher_host.cc @@ -140,12 +140,15 @@ bool SpeechInputDispatcherHost::OnMessageReceived( void SpeechInputDispatcherHost::OnStartRecognition( int render_view_id, int request_id, - const gfx::Rect& element_rect) { + const gfx::Rect& element_rect, + const std::string& language, + const std::string& grammar) { int caller_id = callers_->CreateId(resource_message_filter_process_id_, render_view_id, request_id); manager()->StartRecognition(this, caller_id, resource_message_filter_process_id_, - render_view_id, element_rect); + render_view_id, element_rect, + language, grammar); } void SpeechInputDispatcherHost::OnCancelRecognition(int render_view_id, @@ -173,8 +176,8 @@ void SpeechInputDispatcherHost::SendMessageToRenderView(IPC::Message* message, &RenderViewHost::Send, message); } -void SpeechInputDispatcherHost::SetRecognitionResult(int caller_id, - const string16& result) { +void SpeechInputDispatcherHost::SetRecognitionResult( + int caller_id, const SpeechInputResultArray& result) { VLOG(1) << "SpeechInputDispatcherHost::SetRecognitionResult enter"; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); int caller_render_view_id = callers_->render_view_id(caller_id); diff --git a/chrome/browser/speech/speech_input_dispatcher_host.h b/chrome/browser/speech/speech_input_dispatcher_host.h index ec039f0..85ca5c0 100644 --- a/chrome/browser/speech/speech_input_dispatcher_host.h +++ b/chrome/browser/speech/speech_input_dispatcher_host.h @@ -23,7 +23,8 @@ class SpeechInputDispatcherHost explicit SpeechInputDispatcherHost(int resource_message_filter_process_id); // SpeechInputManager::Delegate methods. - void SetRecognitionResult(int caller_id, const string16& result); + void SetRecognitionResult(int caller_id, + const SpeechInputResultArray& result); void DidCompleteRecording(int caller_id); void DidCompleteRecognition(int caller_id); @@ -44,7 +45,9 @@ class SpeechInputDispatcherHost void SendMessageToRenderView(IPC::Message* message, int render_view_id); void OnStartRecognition(int render_view_id, int request_id, - const gfx::Rect& element_rect); + const gfx::Rect& element_rect, + const std::string& language, + const std::string& grammar); void OnCancelRecognition(int render_view_id, int request_id); void OnStopRecording(int render_view_id, int request_id); diff --git a/chrome/browser/speech/speech_input_manager.cc b/chrome/browser/speech/speech_input_manager.cc index 6d9ffd9..181234e 100644 --- a/chrome/browser/speech/speech_input_manager.cc +++ b/chrome/browser/speech/speech_input_manager.cc @@ -28,13 +28,16 @@ class SpeechInputManagerImpl : public SpeechInputManager, int caller_id, int render_process_id, int render_view_id, - const gfx::Rect& element_rect); + const gfx::Rect& element_rect, + const std::string& language, + const std::string& grammar); virtual void CancelRecognition(int caller_id); virtual void StopRecording(int caller_id); // SpeechRecognizer::Delegate methods. - virtual void SetRecognitionResult(int caller_id, bool error, - const string16& value); + virtual void SetRecognitionResult(int caller_id, + bool error, + const SpeechInputResultArray& result); virtual void DidCompleteRecording(int caller_id); virtual void DidCompleteRecognition(int caller_id); virtual void OnRecognizerError(int caller_id, @@ -103,7 +106,9 @@ void SpeechInputManagerImpl::StartRecognition( int caller_id, int render_process_id, int render_view_id, - const gfx::Rect& element_rect) { + const gfx::Rect& element_rect, + const std::string& language, + const std::string& grammar) { DCHECK(!HasPendingRequest(caller_id)); bubble_controller_->CreateBubble(caller_id, render_process_id, render_view_id, @@ -111,7 +116,8 @@ void SpeechInputManagerImpl::StartRecognition( SpeechInputRequest* request = &requests_[caller_id]; request->delegate = delegate; - request->recognizer = new SpeechRecognizer(this, caller_id); + request->recognizer = new SpeechRecognizer(this, caller_id, language, + grammar); request->is_active = false; StartRecognitionForRequest(caller_id); @@ -149,12 +155,10 @@ void SpeechInputManagerImpl::StopRecording(int caller_id) { requests_[caller_id].recognizer->StopRecording(); } -void SpeechInputManagerImpl::SetRecognitionResult(int caller_id, - bool error, - const string16& value) { +void SpeechInputManagerImpl::SetRecognitionResult( + int caller_id, bool error, const SpeechInputResultArray& result) { DCHECK(HasPendingRequest(caller_id)); - GetDelegate(caller_id)->SetRecognitionResult(caller_id, - (error ? string16() : value)); + GetDelegate(caller_id)->SetRecognitionResult(caller_id, result); } void SpeechInputManagerImpl::DidCompleteRecording(int caller_id) { diff --git a/chrome/browser/speech/speech_input_manager.h b/chrome/browser/speech/speech_input_manager.h index c220506..be9779f 100644 --- a/chrome/browser/speech/speech_input_manager.h +++ b/chrome/browser/speech/speech_input_manager.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_SPEECH_SPEECH_INPUT_MANAGER_H_ #include "base/basictypes.h" +#include "chrome/common/speech_input_result.h" #include "gfx/rect.h" #include "ipc/ipc_message.h" @@ -21,7 +22,9 @@ class SpeechInputManager { // Implemented by the dispatcher host to relay events to the render views. class Delegate { public: - virtual void SetRecognitionResult(int caller_id, const string16& value) = 0; + virtual void SetRecognitionResult( + int caller_id, + const SpeechInputResultArray& result) = 0; virtual void DidCompleteRecording(int caller_id) = 0; virtual void DidCompleteRecognition(int caller_id) = 0; @@ -50,7 +53,9 @@ class SpeechInputManager { int caller_id, int render_process_id, int render_view_id, - const gfx::Rect& element_rect) = 0; + const gfx::Rect& element_rect, + const std::string& language, + const std::string& grammar) = 0; virtual void CancelRecognition(int caller_id) = 0; virtual void StopRecording(int caller_id) = 0; }; diff --git a/chrome/browser/speech/speech_recognition_request.cc b/chrome/browser/speech/speech_recognition_request.cc index 4da62ef..e17f69b 100644 --- a/chrome/browser/speech/speech_recognition_request.cc +++ b/chrome/browser/speech/speech_recognition_request.cc @@ -4,22 +4,26 @@ #include "chrome/browser/speech/speech_recognition_request.h" +#include "app/l10n_util.h" #include "base/json/json_reader.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/common/net/url_request_context_getter.h" +#include "net/base/escape.h" #include "net/base/load_flags.h" #include "net/url_request/url_request_status.h" namespace { +const char* const kDefaultSpeechRecognitionUrl = + "http://www.google.com/speech-api/v1/recognize?client=chromium&"; const char* const kHypothesesString = "hypotheses"; const char* const kUtteranceString = "utterance"; +const char* const kConfidenceString = "confidence"; -bool ParseServerResponse(const std::string& response_body, string16* value) { - DCHECK(value); - +bool ParseServerResponse(const std::string& response_body, + speech_input::SpeechInputResultArray* result) { if (response_body.empty()) { LOG(WARNING) << "ParseServerResponse: Response was empty."; return false; @@ -61,21 +65,38 @@ bool ParseServerResponse(const std::string& response_body, string16* value) { return false; } - Value* first_hypotheses = NULL; - if (!hypotheses_list->Get(0, &first_hypotheses)) { - LOG(WARNING) << "ParseServerResponse: Unable to read hypotheses value."; - return false; + size_t index = 0; + for (; index < hypotheses_list->GetSize(); ++index) { + Value* hypothesis = NULL; + if (!hypotheses_list->Get(index, &hypothesis)) { + LOG(WARNING) << "ParseServerResponse: Unable to read hypothesis value."; + break; + } + DCHECK(hypothesis); + if (!hypothesis->IsType(Value::TYPE_DICTIONARY)) { + LOG(WARNING) << "ParseServerResponse: Unexpected value type " + << hypothesis->GetType(); + break; + } + + const DictionaryValue* hypothesis_value = + static_cast<DictionaryValue*>(hypothesis); + string16 utterance; + if (!hypothesis_value->GetString(kUtteranceString, &utterance)) { + LOG(WARNING) << "ParseServerResponse: Missing utterance value."; + break; + } + + // It is not an error if the 'confidence' field is missing. + double confidence = 0.0; + hypothesis_value->GetReal(kConfidenceString, &confidence); + + result->push_back(speech_input::SpeechInputResultItem(utterance, + confidence)); } - DCHECK(first_hypotheses); - if (!first_hypotheses->IsType(Value::TYPE_DICTIONARY)) { - LOG(WARNING) << "ParseServerResponse: Unexpected value type " - << first_hypotheses->GetType(); - return false; - } - const DictionaryValue* first_hypotheses_value = - static_cast<DictionaryValue*>(first_hypotheses); - if (!first_hypotheses_value->GetString(kUtteranceString, value)) { - LOG(WARNING) << "ParseServerResponse: Missing utterance value."; + + if (index < hypotheses_list->GetSize()) { + result->clear(); return false; } @@ -89,19 +110,36 @@ namespace speech_input { int SpeechRecognitionRequest::url_fetcher_id_for_tests = 0; SpeechRecognitionRequest::SpeechRecognitionRequest( - URLRequestContextGetter* context, const GURL& url, Delegate* delegate) + URLRequestContextGetter* context, Delegate* delegate) : url_context_(context), - url_(url), delegate_(delegate) { DCHECK(delegate); } -bool SpeechRecognitionRequest::Send(const std::string& content_type, +SpeechRecognitionRequest::~SpeechRecognitionRequest() {} + +bool SpeechRecognitionRequest::Send(const std::string& language, + const std::string& grammar, + const std::string& content_type, const std::string& audio_data) { DCHECK(!url_fetcher_.get()); - url_fetcher_.reset(URLFetcher::Create( - url_fetcher_id_for_tests, url_, URLFetcher::POST, this)); + std::vector<std::string> parts; + if (!language.empty()) { + parts.push_back("lang=" + EscapeQueryParamValue(language, true)); + } else { + std::string app_locale = l10n_util::GetApplicationLocale(""); + parts.push_back("lang=" + EscapeQueryParamValue(app_locale, true)); + } + + if (!grammar.empty()) + parts.push_back("grammar=" + EscapeQueryParamValue(grammar, true)); + GURL url(std::string(kDefaultSpeechRecognitionUrl) + JoinString(parts, '&')); + + url_fetcher_.reset(URLFetcher::Create(url_fetcher_id_for_tests, + url, + URLFetcher::POST, + this)); url_fetcher_->set_upload_data(content_type, audio_data); url_fetcher_->set_request_context(url_context_); @@ -124,16 +162,15 @@ void SpeechRecognitionRequest::OnURLFetchComplete( const ResponseCookies& cookies, const std::string& data) { DCHECK_EQ(url_fetcher_.get(), source); - DCHECK(url_.possibly_invalid_spec() == url.possibly_invalid_spec()); bool error = !status.is_success() || response_code != 200; - string16 value; + SpeechInputResultArray result; if (!error) - error = !ParseServerResponse(data, &value); + error = !ParseServerResponse(data, &result); url_fetcher_.reset(); DVLOG(1) << "SpeechRecognitionRequest: Invoking delegate with result."; - delegate_->SetRecognitionResult(error, value); + delegate_->SetRecognitionResult(error, result); } } // namespace speech_input diff --git a/chrome/browser/speech/speech_recognition_request.h b/chrome/browser/speech/speech_recognition_request.h index a8ffe2f..89beed1 100644 --- a/chrome/browser/speech/speech_recognition_request.h +++ b/chrome/browser/speech/speech_recognition_request.h @@ -10,6 +10,7 @@ #include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "chrome/common/net/url_fetcher.h" +#include "chrome/common/speech_input_result.h" #include "googleurl/src/gurl.h" class URLFetcher; @@ -27,7 +28,8 @@ class SpeechRecognitionRequest : public URLFetcher::Delegate { // Interface for receiving callbacks from this object. class Delegate { public: - virtual void SetRecognitionResult(bool error, const string16& value) = 0; + virtual void SetRecognitionResult( + bool error, const SpeechInputResultArray& result) = 0; protected: virtual ~Delegate() {} @@ -35,13 +37,17 @@ class SpeechRecognitionRequest : public URLFetcher::Delegate { // |url| is the server address to which the request wil be sent. SpeechRecognitionRequest(URLRequestContextGetter* context, - const GURL& url, Delegate* delegate); + virtual ~SpeechRecognitionRequest(); + // Sends a new request with the given audio data, returns true if successful. // The same object can be used to send multiple requests but only after the // previous request has completed. - bool Send(const std::string& content_type, const std::string& audio_data); + bool Send(const std::string& language, + const std::string& grammar, + const std::string& content_type, + const std::string& audio_data); bool HasPendingRequest() { return url_fetcher_ != NULL; } @@ -55,7 +61,6 @@ class SpeechRecognitionRequest : public URLFetcher::Delegate { private: scoped_refptr<URLRequestContextGetter> url_context_; - const GURL url_; Delegate* delegate_; scoped_ptr<URLFetcher> url_fetcher_; diff --git a/chrome/browser/speech/speech_recognition_request_unittest.cc b/chrome/browser/speech/speech_recognition_request_unittest.cc index dcf0e9b..a87bba8 100644 --- a/chrome/browser/speech/speech_recognition_request_unittest.cc +++ b/chrome/browser/speech/speech_recognition_request_unittest.cc @@ -21,7 +21,8 @@ class SpeechRecognitionRequestTest : public SpeechRecognitionRequestDelegate, void CreateAndTestRequest(bool success, const std::string& http_response); // SpeechRecognitionRequestDelegate methods. - virtual void SetRecognitionResult(bool error, const string16& result) { + virtual void SetRecognitionResult(bool error, + const SpeechInputResultArray& result) { error_ = error; result_ = result; } @@ -38,13 +39,13 @@ class SpeechRecognitionRequestTest : public SpeechRecognitionRequestDelegate, protected: TestURLFetcherFactory url_fetcher_factory_; bool error_; - string16 result_; + SpeechInputResultArray result_; }; void SpeechRecognitionRequestTest::CreateAndTestRequest( bool success, const std::string& http_response) { - SpeechRecognitionRequest request(NULL, GURL(""), this); - request.Send(std::string(), std::string()); + SpeechRecognitionRequest request(NULL, this); + request.Send(std::string(), std::string(), std::string(), std::string()); TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); URLRequestStatus status; @@ -62,24 +63,30 @@ TEST_F(SpeechRecognitionRequestTest, BasicTest) { CreateAndTestRequest(true, "{\"hypotheses\":[{\"utterance\":\"123456\",\"confidence\":0.9}]}"); EXPECT_FALSE(error_); - EXPECT_EQ(ASCIIToUTF16("123456"), result_); + EXPECT_EQ(1U, result_.size()); + EXPECT_EQ(ASCIIToUTF16("123456"), result_[0].utterance); + EXPECT_EQ(0.9, result_[0].confidence); // Normal success case with multiple results. CreateAndTestRequest(true, "{\"hypotheses\":[{\"utterance\":\"hello\",\"confidence\":0.9}," "{\"utterance\":\"123456\",\"confidence\":0.5}]}"); EXPECT_FALSE(error_); - EXPECT_EQ(ASCIIToUTF16("hello"), result_); + EXPECT_EQ(2u, result_.size()); + EXPECT_EQ(ASCIIToUTF16("hello"), result_[0].utterance); + EXPECT_EQ(0.9, result_[0].confidence); + EXPECT_EQ(ASCIIToUTF16("123456"), result_[1].utterance); + EXPECT_EQ(0.5, result_[1].confidence); // Http failure case. CreateAndTestRequest(false, ""); EXPECT_TRUE(error_); - EXPECT_EQ(ASCIIToUTF16(""), result_); + EXPECT_EQ(0U, result_.size()); // Malformed JSON case. CreateAndTestRequest(true, "{\"hypotheses\":[{\"unknownkey\":\"hello\"}]}"); EXPECT_TRUE(error_); - EXPECT_EQ(ASCIIToUTF16(""), result_); + EXPECT_EQ(0U, result_.size()); } } // namespace speech_input diff --git a/chrome/browser/speech/speech_recognizer.cc b/chrome/browser/speech/speech_recognizer.cc index 8ad4590..fd32c20 100644 --- a/chrome/browser/speech/speech_recognizer.cc +++ b/chrome/browser/speech/speech_recognizer.cc @@ -17,8 +17,6 @@ using std::list; using std::string; namespace { -const char* const kDefaultSpeechRecognitionUrl = - "http://www.google.com/speech-api/v1/recognize?lang=en-us&client=chromium"; const char* const kContentTypeSpeex = "audio/x-speex-with-header-byte; rate=16000"; const int kSpeexEncodingQuality = 8; @@ -109,9 +107,14 @@ void SpeexEncoder::Encode(const short* samples, } } -SpeechRecognizer::SpeechRecognizer(Delegate* delegate, int caller_id) +SpeechRecognizer::SpeechRecognizer(Delegate* delegate, + int caller_id, + const std::string& language, + const std::string& grammar) : delegate_(delegate), caller_id_(caller_id), + language_(language), + grammar_(grammar), encoder_(new SpeexEncoder()), endpointer_(kAudioSampleRate), num_samples_recorded_(0), @@ -209,12 +212,11 @@ void SpeechRecognizer::StopRecording() { it != audio_buffers_.end(); it++) { data.append(*(*it)); } + DCHECK(!request_.get()); request_.reset(new SpeechRecognitionRequest( - Profile::GetDefaultRequestContext(), - GURL(kDefaultSpeechRecognitionUrl), - this)); - request_->Send(kContentTypeSpeex, data); + Profile::GetDefaultRequestContext(), this)); + request_->Send(language_, grammar_, kContentTypeSpeex, data); ReleaseAudioBuffers(); // No need to keep the audio anymore. } @@ -314,13 +316,14 @@ void SpeechRecognizer::HandleOnData(string* data) { // here as POST chunks. } -void SpeechRecognizer::SetRecognitionResult(bool error, const string16& value) { - if (value.empty()) { +void SpeechRecognizer::SetRecognitionResult( + bool error, const SpeechInputResultArray& result) { + if (result.empty()) { InformErrorAndCancelRecognition(RECOGNIZER_ERROR_NO_RESULTS); return; } - delegate_->SetRecognitionResult(caller_id_, error, value); + delegate_->SetRecognitionResult(caller_id_, error, result); // Guard against the delegate freeing us until we finish our job. scoped_refptr<SpeechRecognizer> me(this); diff --git a/chrome/browser/speech/speech_recognizer.h b/chrome/browser/speech/speech_recognizer.h index 7e154ac..61226f4 100644 --- a/chrome/browser/speech/speech_recognizer.h +++ b/chrome/browser/speech/speech_recognizer.h @@ -36,9 +36,10 @@ class SpeechRecognizer // Implemented by the caller to receive recognition events. class Delegate { public: - virtual void SetRecognitionResult(int caller_id, - bool error, - const string16& value) = 0; + virtual void SetRecognitionResult( + int caller_id, + bool error, + const SpeechInputResultArray& result) = 0; // Invoked when audio recording stops, either due to the end pointer // detecting silence in user input or if |StopRecording| was called. The @@ -72,7 +73,10 @@ class SpeechRecognizer virtual ~Delegate() {} }; - SpeechRecognizer(Delegate* delegate, int caller_id); + SpeechRecognizer(Delegate* delegate, + int caller_id, + const std::string& language, + const std::string& grammar); ~SpeechRecognizer(); // Starts audio recording and does recognition after recording ends. The same @@ -96,7 +100,7 @@ class SpeechRecognizer uint32 size); // SpeechRecognitionRequest::Delegate methods. - void SetRecognitionResult(bool error, const string16& value); + void SetRecognitionResult(bool error, const SpeechInputResultArray& result); static const int kAudioSampleRate; static const int kAudioPacketIntervalMs; // Duration of each audio packet. @@ -116,6 +120,8 @@ class SpeechRecognizer Delegate* delegate_; int caller_id_; + std::string language_; + std::string grammar_; // Buffer holding the recorded audio. Owns the strings inside the list. typedef std::list<std::string*> AudioBufferQueue; diff --git a/chrome/browser/speech/speech_recognizer_unittest.cc b/chrome/browser/speech/speech_recognizer_unittest.cc index 172c07d..d71876f 100644 --- a/chrome/browser/speech/speech_recognizer_unittest.cc +++ b/chrome/browser/speech/speech_recognizer_unittest.cc @@ -23,7 +23,8 @@ class SpeechRecognizerTest : public SpeechRecognizerDelegate, SpeechRecognizerTest() : io_thread_(BrowserThread::IO, &message_loop_), ALLOW_THIS_IN_INITIALIZER_LIST( - recognizer_(new SpeechRecognizer(this, 1))), + recognizer_(new SpeechRecognizer(this, 1, std::string(), + std::string()))), recording_complete_(false), recognition_complete_(false), result_received_(false), @@ -44,7 +45,7 @@ class SpeechRecognizerTest : public SpeechRecognizerDelegate, // SpeechRecognizer::Delegate methods. virtual void SetRecognitionResult(int caller_id, bool error, - const string16& result) { + const SpeechInputResultArray& result) { result_received_ = true; } diff --git a/chrome/browser/spellcheck_host.cc b/chrome/browser/spellcheck_host.cc index 545eaaa..d9784c2 100644 --- a/chrome/browser/spellcheck_host.cc +++ b/chrome/browser/spellcheck_host.cc @@ -11,6 +11,7 @@ #include "base/logging.h" #include "base/path_service.h" #include "base/string_split.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/profile.h" @@ -28,7 +29,13 @@ namespace { FilePath GetFirstChoiceFilePath(const std::string& language) { FilePath dict_dir; - PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir); + { + // This should not do blocking IO from the UI thread! + // Temporarily allow it for now. + // http://code.google.com/p/chromium/issues/detail?id=60643 + base::ThreadRestrictions::ScopedAllowIO allow_io; + PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir); + } return SpellCheckCommon::GetVersionedFileName(language, dict_dir); } diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc index 8babda7..dc9c522 100644 --- a/chrome/browser/ssl/ssl_browser_tests.cc +++ b/chrome/browser/ssl/ssl_browser_tests.cc @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/stringprintf.h" #include "base/time.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/interstitial_page.h" @@ -20,13 +22,16 @@ const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data"); class SSLUITest : public InProcessBrowserTest { + typedef net::TestServer::HTTPSOptions HTTPSOptions; + public: SSLUITest() - : https_server_(net::TestServer::TYPE_HTTPS, FilePath(kDocRoot)), - https_server_expired_(net::TestServer::TYPE_HTTPS_EXPIRED_CERTIFICATE, - FilePath(kDocRoot)), + : https_server_( + HTTPSOptions(HTTPSOptions::CERT_OK), FilePath(kDocRoot)), + https_server_expired_( + HTTPSOptions(HTTPSOptions::CERT_EXPIRED), FilePath(kDocRoot)), https_server_mismatched_( - net::TestServer::TYPE_HTTPS_MISMATCHED_HOSTNAME, + HTTPSOptions(HTTPSOptions::CERT_MISMATCHED_NAME), FilePath(kDocRoot)) { EnableDOMAutomation(); } @@ -112,14 +117,28 @@ class SSLUITest : public InProcessBrowserTest { ui_test_utils::WaitForNavigation(&(tab->controller())); } + std::string GetFileWithHostAndPortReplacement( + const std::string& original_path, + const net::HostPortPair& host_port_pair) const { + return StringPrintf("%s?replace_orig=%s&replace_new=%s", + original_path.c_str(), + kReplaceText_, + host_port_pair.ToString().c_str()); + } + net::TestServer https_server_; net::TestServer https_server_expired_; net::TestServer https_server_mismatched_; private: DISALLOW_COPY_AND_ASSIGN(SSLUITest); + + static const char* const kReplaceText_; }; +// static +const char* const SSLUITest::kReplaceText_ = "REPLACE_WITH_HOST_AND_PORT"; + // Visits a regular page over http. IN_PROC_BROWSER_TEST_F(SSLUITest, TestHTTP) { ASSERT_TRUE(test_server()->Start()); @@ -138,8 +157,12 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, TestHTTPWithBrokenHTTPSResource) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(https_server_expired_.Start()); - ui_test_utils::NavigateToURL(browser(), - test_server()->GetURL("files/ssl/page_with_unsafe_contents.html")); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/ssl/page_with_unsafe_contents.html", + https_server_expired_.host_port_pair()); + + ui_test_utils::NavigateToURL( + browser(), test_server()->GetURL(replacement_path)); CheckUnauthenticatedState(browser()->GetSelectedTabContents()); } @@ -227,6 +250,11 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, MAYBE_TestHTTPSExpiredCertAndDontProceed) { } // Visits a page with https error and then goes back using Browser::GoBack. +#if defined(OS_WIN) +// Disabled on win. Times out. crbug.com/43575 and crbug.com/61528 +#define TestHTTPSExpiredCertAndGoBackViaButton \ + DISABLED_TestHTTPSExpiredCertAndGoBackViaButton +#endif IN_PROC_BROWSER_TEST_F(SSLUITest, TestHTTPSExpiredCertAndGoBackViaButton) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(https_server_expired_.Start()); @@ -254,6 +282,11 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, TestHTTPSExpiredCertAndGoBackViaButton) { // Visits a page with https error and then goes back using GoToOffset. // Marked as flaky, see bug 40932. +#if defined(OS_WIN) +// Disabled on win. Times out. crbug.com/43575 and crbug.com/61528 +#define FLAKY_TestHTTPSExpiredCertAndGoBackViaMenu \ + DISABLED_TestHTTPSExpiredCertAndGoBackViaMenu +#endif IN_PROC_BROWSER_TEST_F(SSLUITest, FLAKY_TestHTTPSExpiredCertAndGoBackViaMenu) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(https_server_expired_.Start()); @@ -349,9 +382,13 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, TestDisplaysInsecureContent) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(https_server_.Start()); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/ssl/page_displays_insecure_content.html", + test_server()->host_port_pair()); + // Load a page that displays insecure content. - ui_test_utils::NavigateToURL(browser(), https_server_.GetURL( - "files/ssl/page_displays_insecure_content.html")); + ui_test_utils::NavigateToURL(browser(), + https_server_.GetURL(replacement_path)); CheckAuthenticatedState(browser()->GetSelectedTabContents(), true); } @@ -380,8 +417,11 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, FLAKY_TestUnsafeContents) { ASSERT_TRUE(https_server_.Start()); ASSERT_TRUE(https_server_expired_.Start()); - ui_test_utils::NavigateToURL(browser(), https_server_.GetURL( - "files/ssl/page_with_unsafe_contents.html")); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/ssl/page_with_unsafe_contents.html", + https_server_expired_.host_port_pair()); + ui_test_utils::NavigateToURL(browser(), + https_server_.GetURL(replacement_path)); TabContents* tab = browser()->GetSelectedTabContents(); // When the bad content is filtered, the state is expected to be @@ -417,8 +457,11 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, TestDisplaysInsecureContentLoadedFromJS) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(https_server_.Start()); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/ssl/page_with_dynamic_insecure_content.html", + test_server()->host_port_pair()); ui_test_utils::NavigateToURL(browser(), https_server_.GetURL( - "files/ssl/page_with_dynamic_insecure_content.html")); + replacement_path)); TabContents* tab = browser()->GetSelectedTabContents(); CheckAuthenticatedState(tab, false); @@ -449,12 +492,17 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, TestDisplaysInsecureContentTwoTabs) { CheckAuthenticatedState(tab1, false); // Create a new tab. - GURL url = https_server_.GetURL( - "files/ssl/page_displays_insecure_content.html"); - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - params.index = 0; - params.instance = tab1->GetSiteInstance(); - TabContents* tab2 = browser()->AddTabWithURL(¶ms); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/ssl/page_displays_insecure_content.html", + test_server()->host_port_pair()); + + GURL url = https_server_.GetURL(replacement_path); + browser::NavigateParams params(browser(), url, PageTransition::TYPED); + params.disposition = NEW_FOREGROUND_TAB; + params.tabstrip_index = 0; + params.source_contents = tab1; + browser::Navigate(¶ms); + TabContents* tab2 = params.target_contents; ui_test_utils::WaitForNavigation(&(tab2->controller())); // The new tab has insecure content. @@ -479,12 +527,17 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, TestRunsInsecureContentTwoTabs) { // This tab should be fine. CheckAuthenticatedState(tab1, false); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/ssl/page_runs_insecure_content.html", + test_server()->host_port_pair()); + // Create a new tab. - GURL url = - https_server_.GetURL("files/ssl/page_runs_insecure_content.html"); - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - params.instance = tab1->GetSiteInstance(); - TabContents* tab2 = browser()->AddTabWithURL(¶ms); + GURL url = https_server_.GetURL(replacement_path); + browser::NavigateParams params(browser(), url, PageTransition::TYPED); + params.disposition = NEW_FOREGROUND_TAB; + params.source_contents = tab1; + browser::Navigate(¶ms); + TabContents* tab2 = params.target_contents; ui_test_utils::WaitForNavigation(&(tab2->controller())); // The new tab has insecure content. @@ -502,15 +555,20 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, TestDisplaysCachedInsecureContent) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(https_server_.Start()); - ui_test_utils::NavigateToURL(browser(), test_server()->GetURL( - "files/ssl/page_displays_insecure_content.html")); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/ssl/page_displays_insecure_content.html", + test_server()->host_port_pair()); + + // Load original page over HTTP. + const GURL url_http = test_server()->GetURL(replacement_path); + ui_test_utils::NavigateToURL(browser(), url_http); TabContents* tab = browser()->GetSelectedTabContents(); CheckUnauthenticatedState(tab); // Load again but over SSL. It should be marked as displaying insecure // content (even though the image comes from the WebCore memory cache). - ui_test_utils::NavigateToURL(browser(), https_server_.GetURL( - "files/ssl/page_displays_insecure_content.html")); + const GURL url_https = https_server_.GetURL(replacement_path); + ui_test_utils::NavigateToURL(browser(), url_https); CheckAuthenticatedState(tab, true); } @@ -521,15 +579,20 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, TestRunsCachedInsecureContent) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(https_server_.Start()); - ui_test_utils::NavigateToURL(browser(), - test_server()->GetURL("files/ssl/page_runs_insecure_content.html")); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/ssl/page_runs_insecure_content.html", + test_server()->host_port_pair()); + + // Load original page over HTTP. + const GURL url_http = test_server()->GetURL(replacement_path); + ui_test_utils::NavigateToURL(browser(), url_http); TabContents* tab = browser()->GetSelectedTabContents(); CheckUnauthenticatedState(tab); // Load again but over SSL. It should be marked as displaying insecure // content (even though the image comes from the WebCore memory cache). - ui_test_utils::NavigateToURL(browser(), https_server_.GetURL( - "files/ssl/page_runs_insecure_content.html")); + const GURL url_https = https_server_.GetURL(replacement_path); + ui_test_utils::NavigateToURL(browser(), url_https); CheckAuthenticationBrokenState(tab, 0, true, false); } @@ -608,8 +671,12 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, DISABLED_TestCloseTabWithUnsafePopup) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(https_server_expired_.Start()); + std::string replacement_path = GetFileWithHostAndPortReplacement( + "files/ssl/page_with_unsafe_popup.html", + https_server_expired_.host_port_pair()); + ui_test_utils::NavigateToURL(browser(), - test_server()->GetURL("files/ssl/page_with_unsafe_popup.html")); + test_server()->GetURL(replacement_path)); TabContents* tab1 = browser()->GetSelectedTabContents(); // It is probably overkill to add a notification for a popup-opening, let's @@ -626,13 +693,10 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, DISABLED_TestCloseTabWithUnsafePopup) { // Let's add another tab to make sure the browser does not exit when we close // the first tab. GURL url = test_server()->GetURL("files/ssl/google.html"); - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - TabContents* tab2 = browser()->AddTabWithURL(¶ms); + TabContents* tab2 = + browser()->AddSelectedTabWithURL(url, PageTransition::TYPED); ui_test_utils::WaitForNavigation(&(tab2->controller())); - // Ensure that the tab was created in the correct browser. - EXPECT_EQ(browser(), params.target); - // Close the first tab. browser()->CloseTabContents(tab1); } @@ -850,7 +914,7 @@ IN_PROC_BROWSER_TEST_F(SSLUITest, FLAKY_TestBadFrameNavigation) { // From an HTTP top frame, navigate to good and bad HTTPS (security state should // stay unauthenticated). -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_LINUX) // Disabled, flakily exceeds test timeout, http://crbug.com/43437. #define MAYBE_TestUnauthenticatedFrameNavigation \ DISABLED_TestUnauthenticatedFrameNavigation diff --git a/chrome/browser/ssl/ssl_host_state_unittest.cc b/chrome/browser/ssl/ssl_host_state_unittest.cc index 32e89ed..e239f90 100644 --- a/chrome/browser/ssl/ssl_host_state_unittest.cc +++ b/chrome/browser/ssl/ssl_host_state_unittest.cc @@ -112,9 +112,9 @@ TEST_F(SSLHostStateTest, DidHostRunInsecureContent) { } TEST_F(SSLHostStateTest, QueryPolicy) { - scoped_refptr<net::X509Certificate> google_cert = + scoped_refptr<net::X509Certificate> google_cert( net::X509Certificate::CreateFromBytes( - reinterpret_cast<const char*>(google_der), sizeof(google_der)); + reinterpret_cast<const char*>(google_der), sizeof(google_der))); SSLHostState state; diff --git a/chrome/browser/ssl/ssl_manager.cc b/chrome/browser/ssl/ssl_manager.cc index c894a44..208a9d2 100644 --- a/chrome/browser/ssl/ssl_manager.cc +++ b/chrome/browser/ssl/ssl_manager.cc @@ -210,14 +210,14 @@ void SSLManager::DidLoadFromMemoryCache(LoadFromMemoryCacheDetails* details) { // caches sub-resources. // This resource must have been loaded with no filtering because filtered // resouces aren't cachable. - scoped_refptr<SSLRequestInfo> info = new SSLRequestInfo( + scoped_refptr<SSLRequestInfo> info(new SSLRequestInfo( details->url(), ResourceType::SUB_RESOURCE, details->frame_origin(), details->main_frame_origin(), details->pid(), details->ssl_cert_id(), - details->ssl_cert_status()); + details->ssl_cert_status())); // Simulate loading this resource through the usual path. policy()->OnRequestStarted(info.get()); @@ -226,14 +226,14 @@ void SSLManager::DidLoadFromMemoryCache(LoadFromMemoryCacheDetails* details) { void SSLManager::DidStartResourceResponse(ResourceRequestDetails* details) { DCHECK(details); - scoped_refptr<SSLRequestInfo> info = new SSLRequestInfo( + scoped_refptr<SSLRequestInfo> info(new SSLRequestInfo( details->url(), details->resource_type(), details->frame_origin(), details->main_frame_origin(), details->origin_child_id(), details->ssl_cert_id(), - details->ssl_cert_status()); + details->ssl_cert_status())); // Notify our policy that we started a resource request. Ideally, the // policy should have the ability to cancel the request, but we can't do diff --git a/chrome/browser/ssl/ssl_request_info.cc b/chrome/browser/ssl/ssl_request_info.cc new file mode 100644 index 0000000..7ba88b0 --- /dev/null +++ b/chrome/browser/ssl/ssl_request_info.cc @@ -0,0 +1,23 @@ +// 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/ssl/ssl_request_info.h" + +SSLRequestInfo::SSLRequestInfo(const GURL& url, + ResourceType::Type resource_type, + const std::string& frame_origin, + const std::string& main_frame_origin, + int child_id, + int ssl_cert_id, + int ssl_cert_status) + : url_(url), + resource_type_(resource_type), + frame_origin_(frame_origin), + main_frame_origin_(main_frame_origin), + child_id_(child_id), + ssl_cert_id_(ssl_cert_id), + ssl_cert_status_(ssl_cert_status) { +} + +SSLRequestInfo::~SSLRequestInfo() {} diff --git a/chrome/browser/ssl/ssl_request_info.h b/chrome/browser/ssl/ssl_request_info.h index 2ce3e88..dc919cd 100644 --- a/chrome/browser/ssl/ssl_request_info.h +++ b/chrome/browser/ssl/ssl_request_info.h @@ -8,6 +8,7 @@ #include <string> +#include "base/ref_counted.h" #include "googleurl/src/gurl.h" #include "webkit/glue/resource_type.h" @@ -22,15 +23,7 @@ class SSLRequestInfo : public base::RefCounted<SSLRequestInfo> { const std::string& main_frame_origin, int child_id, int ssl_cert_id, - int ssl_cert_status) - : url_(url), - resource_type_(resource_type), - frame_origin_(frame_origin), - main_frame_origin_(main_frame_origin), - child_id_(child_id), - ssl_cert_id_(ssl_cert_id), - ssl_cert_status_(ssl_cert_status) { - } + int ssl_cert_status); const GURL& url() const { return url_; } ResourceType::Type resource_type() const { return resource_type_; } @@ -43,7 +36,7 @@ class SSLRequestInfo : public base::RefCounted<SSLRequestInfo> { private: friend class base::RefCounted<SSLRequestInfo>; - ~SSLRequestInfo() {} + virtual ~SSLRequestInfo(); GURL url_; ResourceType::Type resource_type_; diff --git a/chrome/browser/sync/abstract_profile_sync_service_test.h b/chrome/browser/sync/abstract_profile_sync_service_test.h index d13a3f8..57b3791 100644 --- a/chrome/browser/sync/abstract_profile_sync_service_test.h +++ b/chrome/browser/sync/abstract_profile_sync_service_test.h @@ -51,28 +51,6 @@ using syncable::WriteTransaction; class ProfileSyncServiceTestHelper { public: - static const std::string GetTagForType(ModelType type) { - switch (type) { - case syncable::AUTOFILL: - return browser_sync::kAutofillTag; - case syncable::PREFERENCES: - return browser_sync::kPreferencesTag; - case syncable::PASSWORDS: - return browser_sync::kPasswordTag; - case syncable::NIGORI: - return browser_sync::kNigoriTag; - case syncable::TYPED_URLS: - return browser_sync::kTypedUrlTag; - case syncable::SESSIONS: - return browser_sync::kSessionsTag; - case syncable::BOOKMARKS: - return "google_chrome_bookmarks"; - default: - NOTREACHED(); - return std::string(); - } - } - static bool CreateRoot(ModelType model_type, ProfileSyncService* service, TestIdFactory* ids) { UserShare* user_share = service->backend()->GetUserShareHandle(); @@ -82,7 +60,30 @@ class ProfileSyncServiceTestHelper { if (!dir.good()) return false; - std::string tag_name = GetTagForType(model_type); + std::string tag_name; + switch (model_type) { + case syncable::AUTOFILL: + tag_name = browser_sync::kAutofillTag; + break; + case syncable::PREFERENCES: + tag_name = browser_sync::kPreferencesTag; + break; + case syncable::PASSWORDS: + tag_name = browser_sync::kPasswordTag; + break; + case syncable::NIGORI: + tag_name = browser_sync::kNigoriTag; + break; + case syncable::TYPED_URLS: + tag_name = browser_sync::kTypedUrlTag; + break; + case syncable::SESSIONS: + tag_name = browser_sync::kSessionsTag; + break; + default: + return false; + } + WriteTransaction wtrans(dir, UNITTEST, __FILE__, __LINE__); MutableEntry node(&wtrans, CREATE, @@ -96,7 +97,7 @@ class ProfileSyncServiceTestHelper { node.Put(SERVER_VERSION, 20); node.Put(BASE_VERSION, 20); node.Put(IS_DEL, false); - EXPECT_TRUE(node.Put(syncable::ID, ids->MakeServer(tag_name))); + node.Put(syncable::ID, ids->MakeServer(tag_name)); sync_pb::EntitySpecifics specifics; syncable::AddDefaultExtensionValue(model_type, &specifics); node.Put(SPECIFICS, specifics); @@ -112,8 +113,7 @@ class AbstractProfileSyncServiceTest : public testing::Test { bool CreateRoot(ModelType model_type) { return ProfileSyncServiceTestHelper::CreateRoot(model_type, - service_.get(), - service_->id_factory()); + service_.get(), &ids_); } protected: @@ -123,6 +123,7 @@ class AbstractProfileSyncServiceTest : public testing::Test { ProfileSyncFactoryMock factory_; TokenService token_service_; scoped_ptr<TestProfileSyncService> service_; + TestIdFactory ids_; }; class CreateRootTask : public Task { diff --git a/chrome/browser/sync/engine/apply_updates_command_unittest.cc b/chrome/browser/sync/engine/apply_updates_command_unittest.cc index 2cf2825..b3ca18c 100644 --- a/chrome/browser/sync/engine/apply_updates_command_unittest.cc +++ b/chrome/browser/sync/engine/apply_updates_command_unittest.cc @@ -33,7 +33,8 @@ class ApplyUpdatesCommandTest : public SyncerCommandTest { virtual void SetUp() { workers()->clear(); mutable_routing_info()->clear(); - workers()->push_back(new ModelSafeWorker()); // GROUP_PASSIVE worker. + // GROUP_PASSIVE worker. + workers()->push_back(make_scoped_refptr(new ModelSafeWorker())); (*mutable_routing_info())[syncable::BOOKMARKS] = GROUP_PASSIVE; (*mutable_routing_info())[syncable::PASSWORDS] = GROUP_PASSIVE; (*mutable_routing_info())[syncable::NIGORI] = GROUP_PASSIVE; diff --git a/chrome/browser/sync/engine/process_commit_response_command_unittest.cc b/chrome/browser/sync/engine/process_commit_response_command_unittest.cc index b6afab8..b80a3cd 100644 --- a/chrome/browser/sync/engine/process_commit_response_command_unittest.cc +++ b/chrome/browser/sync/engine/process_commit_response_command_unittest.cc @@ -42,8 +42,10 @@ class ProcessCommitResponseCommandTestWithParam workers()->clear(); mutable_routing_info()->clear(); - workers()->push_back(new ModelSafeWorker()); // GROUP_PASSIVE worker. - workers()->push_back(new MockUIModelWorker()); // GROUP_UI worker. + // GROUP_PASSIVE worker. + workers()->push_back(make_scoped_refptr(new ModelSafeWorker())); + // GROUP_UI worker. + workers()->push_back(make_scoped_refptr(new MockUIModelWorker())); (*mutable_routing_info())[syncable::BOOKMARKS] = GROUP_UI; (*mutable_routing_info())[syncable::PREFERENCES] = GROUP_UI; (*mutable_routing_info())[syncable::AUTOFILL] = GROUP_PASSIVE; diff --git a/chrome/browser/sync/engine/store_timestamps_command.cc b/chrome/browser/sync/engine/store_timestamps_command.cc index 864e429..8cf8f58 100644 --- a/chrome/browser/sync/engine/store_timestamps_command.cc +++ b/chrome/browser/sync/engine/store_timestamps_command.cc @@ -34,8 +34,9 @@ void StoreTimestampsCommand::ExecuteImpl(sessions::SyncSession* session) { status->set_num_server_changes_remaining(changes_left); } - LOG_IF(INFO, updates.has_new_timestamp()) << "Get Updates got new timestamp: " - << updates.new_timestamp() << " for type mask: " + VLOG_IF(1, updates.has_new_timestamp()) + << "Get Updates got new timestamp: " << updates.new_timestamp() + << " for type mask: " << status->updates_request_parameters().data_types.to_string(); // Update the saved download timestamp for any items we fetched. diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc index 7bc7e7e..b453a52 100644 --- a/chrome/browser/sync/engine/syncapi.cc +++ b/chrome/browser/sync/engine/syncapi.cc @@ -89,6 +89,10 @@ typedef GoogleServiceAuthError AuthError; static const int kThreadExitTimeoutMsec = 60000; static const int kSSLPort = 443; +#if defined(OS_CHROMEOS) +static const int kChromeOSNetworkChangeReactionDelayHackMsec = 5000; +#endif // OS_CHROMEOS + // We manage the lifetime of sync_api::SyncManager::SyncInternal ourselves. DISABLE_RUNNABLE_METHOD_REFCOUNT(sync_api::SyncManager::SyncInternal); @@ -1169,6 +1173,9 @@ class SyncManager::SyncInternal // decryption. Otherwise, the cryptographer is made ready (is_ready()). void BootstrapEncryption(const std::string& restored_key_for_bootstrapping); + // Checks for server reachabilty and requests a nudge. + void OnIPAddressChangedImpl(); + // We couple the DirectoryManager and username together in a UserShare member // so we can return a handle to share_ to clients of the API for use when // constructing any transaction type. @@ -1456,24 +1463,14 @@ void SyncManager::SyncInternal::SendPendingXMPPNotification( } VLOG(1) << "Sending XMPP notification..."; OutgoingNotificationData notification_data; - if (notifier_options_.notification_method == notifier::NOTIFICATION_LEGACY) { - notification_data.service_id = browser_sync::kSyncLegacyServiceId; - notification_data.service_url = browser_sync::kSyncLegacyServiceUrl; - notification_data.send_content = false; - } else { - notification_data.service_id = browser_sync::kSyncServiceId; - notification_data.service_url = browser_sync::kSyncServiceUrl; - notification_data.send_content = true; - notification_data.priority = browser_sync::kSyncPriority; - notification_data.write_to_cache_only = true; - if (notifier_options_.notification_method == notifier::NOTIFICATION_NEW) { - notification_data.service_specific_data = - browser_sync::kSyncServiceSpecificData; - notification_data.require_subscription = true; - } else { - notification_data.require_subscription = false; - } - } + notification_data.service_id = browser_sync::kSyncServiceId; + notification_data.service_url = browser_sync::kSyncServiceUrl; + notification_data.send_content = true; + notification_data.priority = browser_sync::kSyncPriority; + notification_data.write_to_cache_only = true; + notification_data.service_specific_data = + browser_sync::kSyncServiceSpecificData; + notification_data.require_subscription = true; bool success = talk_mediator_->SendNotification(notification_data); if (success) { notification_pending_ = false; @@ -1554,20 +1551,17 @@ void SyncManager::SyncInternal::InitializeTalkMediator() { new sync_notifier::ServerNotifierThread( notifier_options_, state, this); talk_mediator_.reset( - new TalkMediatorImpl(server_notifier_thread, false)); + new TalkMediatorImpl(server_notifier_thread, + notifier_options_.invalidate_xmpp_login, + notifier_options_.allow_insecure_connection)); } else { notifier::MediatorThread* mediator_thread = new notifier::MediatorThreadImpl(notifier_options_); - talk_mediator_.reset(new TalkMediatorImpl(mediator_thread, false)); - if (notifier_options_.notification_method != - notifier::NOTIFICATION_LEGACY) { - if (notifier_options_.notification_method == - notifier::NOTIFICATION_TRANSITIONAL) { - talk_mediator_->AddSubscribedServiceUrl( - browser_sync::kSyncLegacyServiceUrl); - } - talk_mediator_->AddSubscribedServiceUrl(browser_sync::kSyncServiceUrl); - } + talk_mediator_.reset( + new TalkMediatorImpl(mediator_thread, + notifier_options_.invalidate_xmpp_login, + notifier_options_.allow_insecure_connection)); + talk_mediator_->AddSubscribedServiceUrl(browser_sync::kSyncServiceUrl); } talk_mediator_->SetDelegate(this); } @@ -1713,6 +1707,19 @@ void SyncManager::SyncInternal::Shutdown() { void SyncManager::SyncInternal::OnIPAddressChanged() { VLOG(1) << "IP address change detected"; +#if defined (OS_CHROMEOS) + // TODO(tim): This is a hack to intentionally lose a race with flimflam at + // shutdown, so we don't cause shutdown to wait for our http request. + // http://crosbug.com/8429 + MessageLoop::current()->PostDelayedTask(FROM_HERE, + method_factory_.NewRunnableMethod(&SyncInternal::OnIPAddressChangedImpl), + kChromeOSNetworkChangeReactionDelayHackMsec); +#else + OnIPAddressChangedImpl(); +#endif // defined(OS_CHROMEOS) +} + +void SyncManager::SyncInternal::OnIPAddressChangedImpl() { // TODO(akalin): CheckServerReachable() can block, which may cause // jank if we try to shut down sync. Fix this. connection_manager()->CheckServerReachable(); diff --git a/chrome/browser/sync/engine/syncer_proto_util.cc b/chrome/browser/sync/engine/syncer_proto_util.cc index ffc00d8..2603b0a 100644 --- a/chrome/browser/sync/engine/syncer_proto_util.cc +++ b/chrome/browser/sync/engine/syncer_proto_util.cc @@ -195,6 +195,10 @@ bool SyncerProtoUtil::PostClientToServerMessage( } switch (response->error_code()) { + case ClientToServerResponse::UNKNOWN: + LOG(WARNING) << "Sync protocol out-of-date. The server is using a more " + << "recent version."; + return false; case ClientToServerResponse::SUCCESS: LogResponseProfilingData(*response); return true; @@ -203,11 +207,14 @@ bool SyncerProtoUtil::PostClientToServerMessage( session->delegate()->OnSilencedUntil(base::TimeTicks::Now() + base::TimeDelta::FromSeconds(kSyncDelayAfterThrottled)); return false; + case ClientToServerResponse::TRANSIENT_ERROR: + return false; case ClientToServerResponse::USER_NOT_ACTIVATED: case ClientToServerResponse::AUTH_INVALID: case ClientToServerResponse::ACCESS_DENIED: // WARNING: PostAndProcessHeaders contains a hack for this case. LOG(WARNING) << "SyncerProtoUtil: Authentication expired."; + // TODO(sync): Was this meant to be a fall-through? default: NOTREACHED(); return false; diff --git a/chrome/browser/sync/engine/syncer_thread.cc b/chrome/browser/sync/engine/syncer_thread.cc index 01e7fcd..7906f38 100644 --- a/chrome/browser/sync/engine/syncer_thread.cc +++ b/chrome/browser/sync/engine/syncer_thread.cc @@ -254,8 +254,8 @@ void SyncerThread::ThreadMainLoop() { VLOG(1) << "Syncer thread waiting for database initialization."; while (vault_.syncer_ == NULL && !vault_.stop_syncer_thread_) vault_field_changed_.Wait(); - LOG_IF(INFO, !(vault_.syncer_ == NULL)) << "Syncer was found after DB " - "started."; + VLOG_IF(1, !(vault_.syncer_ == NULL)) << "Syncer was found after DB " + "started."; } while (!vault_.stop_syncer_thread_) { diff --git a/chrome/browser/sync/engine/syncer_util.cc b/chrome/browser/sync/engine/syncer_util.cc index b1893de..ba9d3a2 100644 --- a/chrome/browser/sync/engine/syncer_util.cc +++ b/chrome/browser/sync/engine/syncer_util.cc @@ -62,16 +62,13 @@ using syncable::WriteTransaction; namespace browser_sync { -using std::string; -using std::vector; - // Returns the number of unsynced entries. // static int SyncerUtil::GetUnsyncedEntries(syncable::BaseTransaction* trans, - vector<int64> *handles) { + std::vector<int64> *handles) { trans->directory()->GetUnsyncedMetaHandles(trans, handles); - LOG_IF(INFO, handles->size() > 0) - << "Have " << handles->size() << " unsynced items."; + VLOG_IF(1, !handles->empty()) << "Have " << handles->size() + << " unsynced items."; return handles->size(); } @@ -137,7 +134,7 @@ syncable::Id SyncerUtil::FindLocalIdToUpdate( // SyncEntity has NOT been applied to LOCAL fields. // DB has not yet been modified, no entries created for this update. - const string& client_id = trans->directory()->cache_guid(); + const std::string& client_id = trans->directory()->cache_guid(); if (update.has_client_defined_unique_tag() && !update.client_defined_unique_tag().empty()) { @@ -325,9 +322,9 @@ namespace { // Helper to synthesize a new-style sync_pb::EntitySpecifics for use locally, // when the server speaks only the old sync_pb::SyncEntity_BookmarkData-based // protocol. -void UpdateBookmarkSpecifics(const string& singleton_tag, - const string& url, - const string& favicon_bytes, +void UpdateBookmarkSpecifics(const std::string& singleton_tag, + const std::string& url, + const std::string& favicon_bytes, MutableEntry* local_entry) { // In the new-style protocol, the server no longer sends bookmark info for // the "google_chrome" folder. Mimic that here. @@ -349,7 +346,7 @@ void UpdateBookmarkSpecifics(const string& singleton_tag, void SyncerUtil::UpdateServerFieldsFromUpdate( MutableEntry* target, const SyncEntity& update, - const string& name) { + const std::string& name) { if (update.deleted()) { if (target->Get(SERVER_IS_DEL)) { // If we already think the item is server-deleted, we're done. @@ -386,11 +383,11 @@ void SyncerUtil::UpdateServerFieldsFromUpdate( ServerTimeToClientTime(update.mtime())); target->Put(SERVER_IS_DIR, update.IsFolder()); if (update.has_server_defined_unique_tag()) { - const string& tag = update.server_defined_unique_tag(); + const std::string& tag = update.server_defined_unique_tag(); target->Put(UNIQUE_SERVER_TAG, tag); } if (update.has_client_defined_unique_tag()) { - const string& tag = update.client_defined_unique_tag(); + const std::string& tag = update.client_defined_unique_tag(); target->Put(UNIQUE_CLIENT_TAG, tag); } // Store the datatype-specific part as a protobuf. @@ -572,7 +569,7 @@ bool SyncerUtil::AddItemThenPredecessors( syncable::Entry* item, syncable::IndexedBitField inclusion_filter, syncable::MetahandleSet* inserted_items, - vector<syncable::Id>* commit_ids) { + std::vector<syncable::Id>* commit_ids) { if (!inserted_items->insert(item->Get(META_HANDLE)).second) return false; @@ -600,8 +597,8 @@ void SyncerUtil::AddPredecessorsThenItem( syncable::Entry* item, syncable::IndexedBitField inclusion_filter, syncable::MetahandleSet* inserted_items, - vector<syncable::Id>* commit_ids) { - vector<syncable::Id>::size_type initial_size = commit_ids->size(); + std::vector<syncable::Id>* commit_ids) { + size_t initial_size = commit_ids->size(); if (!AddItemThenPredecessors(trans, item, inclusion_filter, inserted_items, commit_ids)) return; @@ -615,9 +612,9 @@ void SyncerUtil::AddPredecessorsThenItem( void SyncerUtil::AddUncommittedParentsAndTheirPredecessors( syncable::BaseTransaction* trans, syncable::MetahandleSet* inserted_items, - vector<syncable::Id>* commit_ids, + std::vector<syncable::Id>* commit_ids, syncable::Id parent_id) { - vector<syncable::Id>::size_type intial_commit_ids_size = commit_ids->size(); + size_t intial_commit_ids_size = commit_ids->size(); // Climb the tree adding entries leaf -> root. while (!parent_id.ServerKnows()) { Entry parent(trans, GET_BY_ID, parent_id); diff --git a/chrome/browser/sync/engine/verify_updates_command_unittest.cc b/chrome/browser/sync/engine/verify_updates_command_unittest.cc index 3ac4bf2..0c27b99 100644 --- a/chrome/browser/sync/engine/verify_updates_command_unittest.cc +++ b/chrome/browser/sync/engine/verify_updates_command_unittest.cc @@ -30,8 +30,8 @@ class VerifyUpdatesCommandTest : public SyncerCommandTest { virtual void SetUp() { workers()->clear(); mutable_routing_info()->clear(); - workers()->push_back(new MockDBModelWorker()); - workers()->push_back(new MockUIModelWorker()); + workers()->push_back(make_scoped_refptr(new MockDBModelWorker())); + workers()->push_back(make_scoped_refptr(new MockUIModelWorker())); (*mutable_routing_info())[syncable::PREFERENCES] = GROUP_UI; (*mutable_routing_info())[syncable::BOOKMARKS] = GROUP_UI; (*mutable_routing_info())[syncable::AUTOFILL] = GROUP_DB; diff --git a/chrome/browser/sync/glue/autofill_change_processor.cc b/chrome/browser/sync/glue/autofill_change_processor.cc index c025e84..d93f25d 100644 --- a/chrome/browser/sync/glue/autofill_change_processor.cc +++ b/chrome/browser/sync/glue/autofill_change_processor.cc @@ -20,6 +20,17 @@ namespace browser_sync { +struct AutofillChangeProcessor::AutofillChangeRecord { + sync_api::SyncManager::ChangeRecord::Action action_; + int64 id_; + sync_pb::AutofillSpecifics autofill_; + AutofillChangeRecord(sync_api::SyncManager::ChangeRecord::Action action, + int64 id, const sync_pb::AutofillSpecifics& autofill) + : action_(action), + id_(id), + autofill_(autofill) { } +}; + AutofillChangeProcessor::AutofillChangeProcessor( AutofillModelAssociator* model_associator, WebDatabase* web_database, @@ -38,6 +49,8 @@ AutofillChangeProcessor::AutofillChangeProcessor( StartObserving(); } +AutofillChangeProcessor::~AutofillChangeProcessor() {} + void AutofillChangeProcessor::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { @@ -485,7 +498,7 @@ void AutofillChangeProcessor::ApplySyncAutofillProfileDelete( NOTREACHED() << "Couldn't retrieve autofill profile: " << label; return; } - if (!web_database_->RemoveAutoFillProfile(p->unique_id())) { + if (!web_database_->RemoveAutoFillProfile(p->guid())) { NOTREACHED() << "Couldn't remove autofill profile: " << label; return; } diff --git a/chrome/browser/sync/glue/autofill_change_processor.h b/chrome/browser/sync/glue/autofill_change_processor.h index 0680d0c..7a84520 100644 --- a/chrome/browser/sync/glue/autofill_change_processor.h +++ b/chrome/browser/sync/glue/autofill_change_processor.h @@ -40,7 +40,7 @@ class AutofillChangeProcessor : public ChangeProcessor, WebDatabase* web_database, PersonalDataManager* personal_data, UnrecoverableErrorHandler* error_handler); - virtual ~AutofillChangeProcessor() {} + virtual ~AutofillChangeProcessor(); // NotificationObserver implementation. // WebDataService -> sync_api model change application. @@ -73,17 +73,6 @@ class AutofillChangeProcessor : public ChangeProcessor, virtual void StopImpl(); private: - struct AutofillChangeRecord { - sync_api::SyncManager::ChangeRecord::Action action_; - int64 id_; - sync_pb::AutofillSpecifics autofill_; - AutofillChangeRecord(sync_api::SyncManager::ChangeRecord::Action action, - int64 id, const sync_pb::AutofillSpecifics& autofill) - : action_(action), - id_(id), - autofill_(autofill) { } - }; - void StartObserving(); void StopObserving(); @@ -170,6 +159,7 @@ class AutofillChangeProcessor : public ChangeProcessor, // Record of changes from ApplyChangesFromSyncModel. These are then processed // in CommitChangesFromSyncModel. + struct AutofillChangeRecord; std::vector<AutofillChangeRecord> autofill_changes_; DISALLOW_COPY_AND_ASSIGN(AutofillChangeProcessor); diff --git a/chrome/browser/sync/glue/autofill_data_type_controller.cc b/chrome/browser/sync/glue/autofill_data_type_controller.cc index 4585101..2598977 100644 --- a/chrome/browser/sync/glue/autofill_data_type_controller.cc +++ b/chrome/browser/sync/glue/autofill_data_type_controller.cc @@ -29,7 +29,8 @@ AutofillDataTypeController::AutofillDataTypeController( state_(NOT_RUNNING), personal_data_(NULL), abort_association_(false), - abort_association_complete_(false, false) { + abort_association_complete_(false, false), + datatype_stopped_(false, false) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(profile_sync_factory); DCHECK(profile); @@ -136,10 +137,15 @@ void AutofillDataTypeController::Stop() { model_associator_->DisassociateModels(); set_state(NOT_RUNNING); - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + if (BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, NewRunnableMethod( this, - &AutofillDataTypeController::StopImpl)); + &AutofillDataTypeController::StopImpl))) { + // We need to ensure the data type has fully stoppped before continuing. In + // particular, during shutdown we may attempt to destroy the + // profile_sync_service before we've removed its observers (BUG 61804). + datatype_stopped_.Wait(); + } } void AutofillDataTypeController::StartImpl() { @@ -173,6 +179,8 @@ void AutofillDataTypeController::StartImpl() { bool merge_success = model_associator_->AssociateModels(); UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime", base::TimeTicks::Now() - start_time); + VLOG(1) << "Autofill association time: " << + (base::TimeTicks::Now() - start_time).InSeconds(); if (!merge_success) { StartFailed(ASSOCIATION_FAILED); return; @@ -223,6 +231,8 @@ void AutofillDataTypeController::StopImpl() { change_processor_.reset(); model_associator_.reset(); + + datatype_stopped_.Signal(); } void AutofillDataTypeController::StartFailed(StartResult result) { diff --git a/chrome/browser/sync/glue/autofill_data_type_controller.h b/chrome/browser/sync/glue/autofill_data_type_controller.h index 1def2d2..d22fcfa 100644 --- a/chrome/browser/sync/glue/autofill_data_type_controller.h +++ b/chrome/browser/sync/glue/autofill_data_type_controller.h @@ -109,6 +109,10 @@ class AutofillDataTypeController : public DataTypeController, bool abort_association_; base::WaitableEvent abort_association_complete_; + // Barrier to ensure that the datatype has been stopped on the DB thread + // from the UI thread. + base::WaitableEvent datatype_stopped_; + DISALLOW_COPY_AND_ASSIGN(AutofillDataTypeController); }; diff --git a/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc b/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc index 9c6a776..d0ede1a 100644 --- a/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc +++ b/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc @@ -191,8 +191,8 @@ TEST_F(AutofillDataTypeControllerTest, AbortWhileWDSStarting) { WillRepeatedly(Return(personal_data_manager_.get())); EXPECT_CALL(*(personal_data_manager_.get()), IsDataLoaded()). WillRepeatedly(Return(true)); - scoped_refptr<WebDataServiceFake> web_data_service_not_loaded = - new WebDataServiceFake(false); + scoped_refptr<WebDataServiceFake> web_data_service_not_loaded( + new WebDataServiceFake(false)); EXPECT_CALL(profile_, GetWebDataService(_)). WillOnce(Return(web_data_service_not_loaded.get())); autofill_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); diff --git a/chrome/browser/sync/glue/autofill_model_associator.cc b/chrome/browser/sync/glue/autofill_model_associator.cc index 43914c2..b550ab5 100644 --- a/chrome/browser/sync/glue/autofill_model_associator.cc +++ b/chrome/browser/sync/glue/autofill_model_associator.cc @@ -30,6 +30,25 @@ const char kAutofillProfileNamespaceTag[] = "autofill_profile|"; static const int kMaxNumAttemptsToFindUniqueLabel = 100; +struct AutofillModelAssociator::DataBundle { + std::set<AutofillKey> current_entries; + std::vector<AutofillEntry> new_entries; + std::set<string16> current_profiles; + std::vector<AutoFillProfile*> updated_profiles; + std::vector<AutoFillProfile*> new_profiles; // We own these pointers. + ~DataBundle() { STLDeleteElements(&new_profiles); } +}; + +AutofillModelAssociator::DoOptimisticRefreshTask::DoOptimisticRefreshTask( + PersonalDataManager* pdm) : pdm_(pdm) {} + +AutofillModelAssociator::DoOptimisticRefreshTask::~DoOptimisticRefreshTask() {} + +void AutofillModelAssociator::DoOptimisticRefreshTask::Run() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + pdm_->Refresh(); +} + AutofillModelAssociator::AutofillModelAssociator( ProfileSyncService* sync_service, WebDatabase* web_database, @@ -396,6 +415,17 @@ void AutofillModelAssociator::AbortAssociation() { abort_association_pending_ = true; } +const std::string* +AutofillModelAssociator::GetChromeNodeFromSyncId(int64 sync_id) { + return NULL; +} + +bool AutofillModelAssociator::InitSyncNodeFromChromeId( + std::string node_id, + sync_api::BaseNode* sync_node) { + return false; +} + int64 AutofillModelAssociator::GetSyncIdFromChromeId( const std::string autofill) { AutofillToSyncIdMap::const_iterator iter = id_map_.find(autofill); diff --git a/chrome/browser/sync/glue/autofill_model_associator.h b/chrome/browser/sync/glue/autofill_model_associator.h index f0dbe1a..d4e5363 100644 --- a/chrome/browser/sync/glue/autofill_model_associator.h +++ b/chrome/browser/sync/glue/autofill_model_associator.h @@ -15,7 +15,6 @@ #include "base/lock.h" #include "base/ref_counted.h" #include "chrome/browser/autofill/personal_data_manager.h" -#include "chrome/browser/browser_thread.h" #include "chrome/browser/sync/engine/syncapi.h" #include "chrome/browser/sync/glue/model_associator.h" #include "chrome/browser/sync/protocol/autofill_specifics.pb.h" @@ -56,11 +55,9 @@ class AutofillModelAssociator // PersonalDataManager living on the UI thread that it needs to refresh. class DoOptimisticRefreshTask : public Task { public: - explicit DoOptimisticRefreshTask(PersonalDataManager* pdm) : pdm_(pdm) {} - virtual void Run() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - pdm_->Refresh(); - } + explicit DoOptimisticRefreshTask(PersonalDataManager* pdm); + virtual ~DoOptimisticRefreshTask(); + virtual void Run(); private: scoped_refptr<PersonalDataManager> pdm_; }; @@ -81,15 +78,11 @@ class AutofillModelAssociator virtual void AbortAssociation(); // Not implemented. - virtual const std::string* GetChromeNodeFromSyncId(int64 sync_id) { - return NULL; - } + virtual const std::string* GetChromeNodeFromSyncId(int64 sync_id); // Not implemented. virtual bool InitSyncNodeFromChromeId(std::string node_id, - sync_api::BaseNode* sync_node) { - return false; - } + sync_api::BaseNode* sync_node); // Returns the sync id for the given autofill name, or sync_api::kInvalidId // if the autofill name is not associated to any sync id. @@ -137,14 +130,7 @@ class AutofillModelAssociator // A convenience wrapper of a bunch of state we pass around while associating // models, and send to the WebDatabase for persistence. - struct DataBundle { - std::set<AutofillKey> current_entries; - std::vector<AutofillEntry> new_entries; - std::set<string16> current_profiles; - std::vector<AutoFillProfile*> updated_profiles; - std::vector<AutoFillProfile*> new_profiles; // We own these pointers. - ~DataBundle() { STLDeleteElements(&new_profiles); } - }; + struct DataBundle; // Helper to query WebDatabase for the current autofill state. bool LoadAutofillData(std::vector<AutofillEntry>* entries, diff --git a/chrome/browser/sync/glue/extension_change_processor.cc b/chrome/browser/sync/glue/extension_change_processor.cc index 0cc64a9..fcbbd01 100644 --- a/chrome/browser/sync/glue/extension_change_processor.cc +++ b/chrome/browser/sync/glue/extension_change_processor.cc @@ -71,7 +71,7 @@ void ExtensionChangeProcessor::Observe(NotificationType type, RemoveServerData(traits_, id, profile_->GetProfileSyncService()); } } else { - const Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); CHECK(extension); VLOG(1) << "Updating server data for extension " << extension->id() << " (notification type = " << type.value << ")"; diff --git a/chrome/browser/sync/glue/extension_data.cc b/chrome/browser/sync/glue/extension_data.cc index eb4fea6..c62c362 100644 --- a/chrome/browser/sync/glue/extension_data.cc +++ b/chrome/browser/sync/glue/extension_data.cc @@ -19,6 +19,8 @@ ExtensionData ExtensionData::FromData( return extension_data; } +ExtensionData::~ExtensionData() {} + const sync_pb::ExtensionSpecifics& ExtensionData::merged_data() const { DcheckIsExtensionSpecificsValid(merged_data_); return merged_data_; diff --git a/chrome/browser/sync/glue/extension_data.h b/chrome/browser/sync/glue/extension_data.h index 67951b9..73316de 100644 --- a/chrome/browser/sync/glue/extension_data.h +++ b/chrome/browser/sync/glue/extension_data.h @@ -28,6 +28,8 @@ class ExtensionData { static ExtensionData FromData( Source source, const sync_pb::ExtensionSpecifics& data); + ~ExtensionData(); + // Implicit copy constructor and assignment operator welcome. // Returns the version of the data that all sources should diff --git a/chrome/browser/sync/glue/extension_sync.cc b/chrome/browser/sync/glue/extension_sync.cc index 51ccfd4..facf5f1 100644 --- a/chrome/browser/sync/glue/extension_sync.cc +++ b/chrome/browser/sync/glue/extension_sync.cc @@ -268,7 +268,7 @@ void TryUpdateClient( extension_data->merged_data(); DcheckIsExtensionSpecificsValid(specifics); const std::string& id = specifics.id(); - Extension* extension = extensions_service->GetExtensionById(id, true); + const Extension* extension = extensions_service->GetExtensionById(id, true); if (extension) { if (!IsExtensionValidAndSyncable(*extension, allowed_extension_types)) { LOG(DFATAL) << "TryUpdateClient() called for non-syncable extension " @@ -446,7 +446,7 @@ void UpdateClient(const ExtensionSyncTraits& traits, DcheckIsExtensionSpecificsValid(server_data); ExtensionData extension_data = ExtensionData::FromData(ExtensionData::SERVER, server_data); - Extension* extension = + const Extension* extension = extensions_service->GetExtensionById(server_data.id(), true); if (extension) { if (!IsExtensionValidAndSyncable( @@ -478,7 +478,7 @@ void UpdateClient(const ExtensionSyncTraits& traits, void RemoveFromClient(const ExtensionSyncTraits& traits, const std::string& id, ExtensionsService* extensions_service) { - Extension* extension = extensions_service->GetExtensionById(id, true); + const Extension* extension = extensions_service->GetExtensionById(id, true); if (extension) { if (IsExtensionValidAndSyncable(*extension, traits.allowed_extension_types)) { diff --git a/chrome/browser/sync/glue/extension_util.cc b/chrome/browser/sync/glue/extension_util.cc index a5d74d0..a0aa08e 100644 --- a/chrome/browser/sync/glue/extension_util.cc +++ b/chrome/browser/sync/glue/extension_util.cc @@ -232,7 +232,7 @@ bool IsExtensionOutdated(const Extension& extension, void SetExtensionProperties( const sync_pb::ExtensionSpecifics& specifics, - ExtensionsService* extensions_service, Extension* extension) { + ExtensionsService* extensions_service, const Extension* extension) { DcheckIsExtensionSpecificsValid(specifics); CHECK(extensions_service); CHECK(extension); diff --git a/chrome/browser/sync/glue/extension_util.h b/chrome/browser/sync/glue/extension_util.h index ca85e31..a46d1fb 100644 --- a/chrome/browser/sync/glue/extension_util.h +++ b/chrome/browser/sync/glue/extension_util.h @@ -132,7 +132,7 @@ bool IsExtensionOutdated(const Extension& extension, // valid. void SetExtensionProperties( const sync_pb::ExtensionSpecifics& specifics, - ExtensionsService* extensions_service, Extension* extension); + ExtensionsService* extensions_service, const Extension* extension); // Merge |specifics| into |merged_specifics|. Both must be valid and // have the same ID. The merge policy is currently to copy the diff --git a/chrome/browser/sync/glue/extension_util_unittest.cc b/chrome/browser/sync/glue/extension_util_unittest.cc index 56cf24e..f8eac78 100644 --- a/chrome/browser/sync/glue/extension_util_unittest.cc +++ b/chrome/browser/sync/glue/extension_util_unittest.cc @@ -36,10 +36,12 @@ const char kName2[] = "MyExtension2"; class ExtensionUtilTest : public testing::Test { }; -void MakeExtension(bool is_theme, const GURL& update_url, - const GURL& launch_url, bool converted_from_user_script, - Extension::Location location, int num_plugins, - Extension* extension) { +scoped_refptr<Extension> MakeExtension( + bool is_theme, const GURL& update_url, + const GURL& launch_url, + bool converted_from_user_script, + Extension::Location location, int num_plugins, + const FilePath& extension_path) { DictionaryValue source; source.SetString(extension_manifest_keys::kName, "PossiblySyncableExtension"); @@ -68,121 +70,123 @@ void MakeExtension(bool is_theme, const GURL& update_url, } std::string error; + scoped_refptr<Extension> extension = Extension::Create( + extension_path, location, source, false, &error); #if defined(OS_CHROMEOS) if (num_plugins > 0) { // plugins are illegal in extensions on chrome os. - EXPECT_FALSE(extension->InitFromValue(source, false, &error)); - return; + EXPECT_FALSE(extension); + return NULL; } #endif - extension->set_location(location); - EXPECT_TRUE(extension->InitFromValue(source, false, &error)); + EXPECT_TRUE(extension); EXPECT_EQ("", error); + return extension; } TEST_F(ExtensionUtilTest, GetExtensionType) { { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), GURL(), false, - Extension::INTERNAL, 0, &extension); - EXPECT_EQ(EXTENSION, GetExtensionType(extension)); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), GURL(), false, + Extension::INTERNAL, 0, file_path)); + EXPECT_EQ(EXTENSION, GetExtensionType(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(true, GURL(), GURL(), false, - Extension::INTERNAL, 0, &extension); - EXPECT_EQ(THEME, GetExtensionType(extension)); + scoped_refptr<Extension> extension( + MakeExtension(true, GURL(), GURL(), false, + Extension::INTERNAL, 0, file_path)); + EXPECT_EQ(THEME, GetExtensionType(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), GURL(), true, - Extension::INTERNAL, 0, &extension); - EXPECT_EQ(LOCAL_USER_SCRIPT, GetExtensionType(extension)); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), GURL(), true, + Extension::INTERNAL, 0, file_path)); + EXPECT_EQ(LOCAL_USER_SCRIPT, GetExtensionType(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL("http://www.google.com"), GURL(), true, - Extension::INTERNAL, 0, &extension); - EXPECT_EQ(UPDATEABLE_USER_SCRIPT, GetExtensionType(extension)); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL("http://www.google.com"), GURL(), true, + Extension::INTERNAL, 0, file_path)); + EXPECT_EQ(UPDATEABLE_USER_SCRIPT, GetExtensionType(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), - GURL("http://www.google.com"), false, - Extension::INTERNAL, 0, &extension); - EXPECT_EQ(APP, GetExtensionType(extension)); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), + GURL("http://www.google.com"), false, + Extension::INTERNAL, 0, file_path)); + EXPECT_EQ(APP, GetExtensionType(*extension)); } } TEST_F(ExtensionUtilTest, IsExtensionValid) { { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), GURL(), false, - Extension::INTERNAL, 0, &extension); - EXPECT_TRUE(IsExtensionValid(extension)); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), GURL(), false, + Extension::INTERNAL, 0, file_path)); + EXPECT_TRUE(IsExtensionValid(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(kValidUpdateUrl1), GURL(), - true, Extension::INTERNAL, 0, &extension); - EXPECT_TRUE(IsExtensionValid(extension)); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(kValidUpdateUrl1), GURL(), + true, Extension::INTERNAL, 0, file_path)); + EXPECT_TRUE(IsExtensionValid(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), GURL(), true, - Extension::INTERNAL, 0, &extension); - EXPECT_TRUE(IsExtensionValid(extension)); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), GURL(), true, + Extension::INTERNAL, 0, file_path)); + EXPECT_TRUE(IsExtensionValid(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(true, GURL(), GURL(), false, - Extension::INTERNAL, 0, &extension); - EXPECT_TRUE(IsExtensionValid(extension)); + scoped_refptr<Extension> extension( + MakeExtension(true, GURL(), GURL(), false, + Extension::INTERNAL, 0, file_path)); + EXPECT_TRUE(IsExtensionValid(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), - GURL("http://www.google.com"), false, - Extension::INTERNAL, 0, &extension); - EXPECT_TRUE(IsExtensionValid(extension)); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), + GURL("http://www.google.com"), false, + Extension::INTERNAL, 0, file_path)); + EXPECT_TRUE(IsExtensionValid(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), GURL(), false, - Extension::EXTERNAL_PREF, 0, &extension); - EXPECT_FALSE(IsExtensionValid(extension)); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), GURL(), false, + Extension::EXTERNAL_PREF, 0, file_path)); + EXPECT_FALSE(IsExtensionValid(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension( - false, GURL("http://third-party.update_url.com"), GURL(), true, - Extension::INTERNAL, 0, &extension); - EXPECT_FALSE(IsExtensionValid(extension)); + scoped_refptr<Extension> extension( + MakeExtension( + false, GURL("http://third-party.update_url.com"), GURL(), true, + Extension::INTERNAL, 0, file_path)); + EXPECT_FALSE(IsExtensionValid(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), GURL(), true, - Extension::INTERNAL, 1, &extension); - EXPECT_FALSE(IsExtensionValid(extension)); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), GURL(), true, + Extension::INTERNAL, 1, file_path)); + EXPECT_FALSE(extension && IsExtensionValid(*extension)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), GURL(), true, - Extension::INTERNAL, 2, &extension); - EXPECT_FALSE(IsExtensionValid(extension)); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), GURL(), true, + Extension::INTERNAL, 2, file_path)); + EXPECT_FALSE(extension && IsExtensionValid(*extension)); } } @@ -192,36 +196,36 @@ TEST_F(ExtensionUtilTest, IsExtensionValidAndSyncable) { allowed_extension_types.insert(APP); { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), GURL(), false, - Extension::INTERNAL, 0, &extension); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), GURL(), false, + Extension::INTERNAL, 0, file_path)); EXPECT_TRUE(IsExtensionValidAndSyncable( - extension, allowed_extension_types)); + *extension, allowed_extension_types)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), - GURL("http://www.google.com"), false, - Extension::INTERNAL, 0, &extension); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), + GURL("http://www.google.com"), false, + Extension::INTERNAL, 0, file_path)); EXPECT_TRUE(IsExtensionValidAndSyncable( - extension, allowed_extension_types)); + *extension, allowed_extension_types)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), GURL(), true, - Extension::INTERNAL, 0, &extension); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), GURL(), true, + Extension::INTERNAL, 0, file_path)); EXPECT_FALSE(IsExtensionValidAndSyncable( - extension, allowed_extension_types)); + *extension, allowed_extension_types)); } { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeExtension(false, GURL(), GURL(), false, - Extension::EXTERNAL_PREF, 0, &extension); + scoped_refptr<Extension> extension( + MakeExtension(false, GURL(), GURL(), false, + Extension::EXTERNAL_PREF, 0, file_path)); EXPECT_FALSE(IsExtensionValidAndSyncable( - extension, allowed_extension_types)); + *extension, allowed_extension_types)); } } @@ -450,30 +454,32 @@ TEST_F(ExtensionUtilTest, AreExtensionSpecificsNonUserPropertiesEqual) { EXPECT_TRUE(AreExtensionSpecificsNonUserPropertiesEqual(a, b)); } -void MakeSyncableExtension(const std::string& version_string, - const std::string& update_url_spec, - const std::string& name, - Extension* extension) { +scoped_refptr<Extension> MakeSyncableExtension( + const std::string& version_string, + const std::string& update_url_spec, + const std::string& name, + const FilePath& extension_path) { DictionaryValue source; source.SetString(extension_manifest_keys::kVersion, version_string); source.SetString(extension_manifest_keys::kUpdateURL, update_url_spec); source.SetString(extension_manifest_keys::kName, name); std::string error; - extension->set_location(Extension::INTERNAL); - EXPECT_TRUE(extension->InitFromValue(source, false, &error)); + scoped_refptr<Extension> extension = Extension::Create( + extension_path, Extension::INTERNAL, source, false, &error); + EXPECT_TRUE(extension); EXPECT_EQ("", error); + return extension; } TEST_F(ExtensionUtilTest, GetExtensionSpecificsHelper) { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeSyncableExtension(kValidVersion, kValidUpdateUrl1, kName, - &extension); + scoped_refptr<Extension> extension( + MakeSyncableExtension(kValidVersion, kValidUpdateUrl1, kName, file_path)); sync_pb::ExtensionSpecifics specifics; - GetExtensionSpecificsHelper(extension, true, false, &specifics); - EXPECT_EQ(extension.id(), specifics.id()); - EXPECT_EQ(extension.VersionString(), kValidVersion); - EXPECT_EQ(extension.update_url().spec(), kValidUpdateUrl1); + GetExtensionSpecificsHelper(*extension, true, false, &specifics); + EXPECT_EQ(extension->id(), specifics.id()); + EXPECT_EQ(extension->VersionString(), kValidVersion); + EXPECT_EQ(extension->update_url().spec(), kValidUpdateUrl1); EXPECT_TRUE(specifics.enabled()); EXPECT_FALSE(specifics.incognito_enabled()); EXPECT_EQ(kName, specifics.name()); @@ -481,19 +487,18 @@ TEST_F(ExtensionUtilTest, GetExtensionSpecificsHelper) { TEST_F(ExtensionUtilTest, IsExtensionOutdated) { FilePath file_path(kExtensionFilePath); - Extension extension(file_path); - MakeSyncableExtension(kVersion2, kValidUpdateUrl1, kName, - &extension); + scoped_refptr<Extension> extension( + MakeSyncableExtension(kVersion2, kValidUpdateUrl1, kName, file_path)); sync_pb::ExtensionSpecifics specifics; specifics.set_id(kValidId); specifics.set_update_url(kValidUpdateUrl1); specifics.set_version(kVersion1); - EXPECT_FALSE(IsExtensionOutdated(extension, specifics)); + EXPECT_FALSE(IsExtensionOutdated(*extension, specifics)); specifics.set_version(kVersion2); - EXPECT_FALSE(IsExtensionOutdated(extension, specifics)); + EXPECT_FALSE(IsExtensionOutdated(*extension, specifics)); specifics.set_version(kVersion3); - EXPECT_TRUE(IsExtensionOutdated(extension, specifics)); + EXPECT_TRUE(IsExtensionOutdated(*extension, specifics)); } // TODO(akalin): Make ExtensionsService/ExtensionUpdater testable diff --git a/chrome/browser/sync/glue/history_model_worker.cc b/chrome/browser/sync/glue/history_model_worker.cc index b6f3e5e..1c39501 100644 --- a/chrome/browser/sync/glue/history_model_worker.cc +++ b/chrome/browser/sync/glue/history_model_worker.cc @@ -46,7 +46,7 @@ HistoryModelWorker::~HistoryModelWorker() { void HistoryModelWorker::DoWorkAndWaitUntilDone(Callback0::Type* work) { WaitableEvent done(false, false); - scoped_refptr<WorkerTask> task = new WorkerTask(work, &done); + scoped_refptr<WorkerTask> task(new WorkerTask(work, &done)); history_service_->ScheduleDBTask(task.get(), this); done.Wait(); } diff --git a/chrome/browser/sync/glue/http_bridge.cc b/chrome/browser/sync/glue/http_bridge.cc index 3adaaf0..044cc75 100644 --- a/chrome/browser/sync/glue/http_bridge.cc +++ b/chrome/browser/sync/glue/http_bridge.cc @@ -44,7 +44,7 @@ URLRequestContext* HttpBridge::RequestContextGetter::GetURLRequestContext() { } scoped_refptr<base::MessageLoopProxy> -HttpBridge::RequestContextGetter::GetIOMessageLoopProxy() { +HttpBridge::RequestContextGetter::GetIOMessageLoopProxy() const { return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); } diff --git a/chrome/browser/sync/glue/http_bridge.h b/chrome/browser/sync/glue/http_bridge.h index 1951ce5..5b9d4b2 100644 --- a/chrome/browser/sync/glue/http_bridge.h +++ b/chrome/browser/sync/glue/http_bridge.h @@ -85,7 +85,7 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>, // URLRequestContextGetter implementation. virtual URLRequestContext* GetURLRequestContext(); - virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy(); + virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const; private: ~RequestContextGetter() {} diff --git a/chrome/browser/sync/glue/http_bridge_unittest.cc b/chrome/browser/sync/glue/http_bridge_unittest.cc index dc4948d..57fca13 100644 --- a/chrome/browser/sync/glue/http_bridge_unittest.cc +++ b/chrome/browser/sync/glue/http_bridge_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// 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. @@ -26,7 +26,7 @@ class TestURLRequestContextGetter : public URLRequestContextGetter { context_ = new TestURLRequestContext; return context_; } - virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() { + virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const { return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); } @@ -103,7 +103,7 @@ class HttpBridgeTest : public testing::Test { class DummyURLFetcher : public TestURLFetcher { public: - DummyURLFetcher() : TestURLFetcher(GURL(), POST, NULL) {} + DummyURLFetcher() : TestURLFetcher(0, GURL(), POST, NULL) {} net::HttpResponseHeaders* response_headers() const { return NULL; diff --git a/chrome/browser/sync/glue/password_model_associator.cc b/chrome/browser/sync/glue/password_model_associator.cc index 7d33a0e..f9ab890 100644 --- a/chrome/browser/sync/glue/password_model_associator.cc +++ b/chrome/browser/sync/glue/password_model_associator.cc @@ -37,6 +37,8 @@ PasswordModelAssociator::PasswordModelAssociator( #endif } +PasswordModelAssociator::~PasswordModelAssociator() {} + bool PasswordModelAssociator::AssociateModels() { DCHECK(expected_loop_ == MessageLoop::current()); { diff --git a/chrome/browser/sync/glue/password_model_associator.h b/chrome/browser/sync/glue/password_model_associator.h index 096c3af..22c81ba 100644 --- a/chrome/browser/sync/glue/password_model_associator.h +++ b/chrome/browser/sync/glue/password_model_associator.h @@ -52,7 +52,7 @@ class PasswordModelAssociator static syncable::ModelType model_type() { return syncable::PASSWORDS; } PasswordModelAssociator(ProfileSyncService* sync_service, PasswordStore* password_store); - virtual ~PasswordModelAssociator() { } + virtual ~PasswordModelAssociator(); // PerDataTypeAssociatorInterface implementation. // diff --git a/chrome/browser/sync/glue/password_model_worker.cc b/chrome/browser/sync/glue/password_model_worker.cc index 9c5b0d6..f832ac1 100644 --- a/chrome/browser/sync/glue/password_model_worker.cc +++ b/chrome/browser/sync/glue/password_model_worker.cc @@ -19,6 +19,8 @@ PasswordModelWorker::PasswordModelWorker(PasswordStore* password_store) DCHECK(password_store); } +PasswordModelWorker::~PasswordModelWorker() {} + void PasswordModelWorker::DoWorkAndWaitUntilDone(Callback0::Type* work) { WaitableEvent done(false, false); password_store_->ScheduleTask( diff --git a/chrome/browser/sync/glue/password_model_worker.h b/chrome/browser/sync/glue/password_model_worker.h index cd55c79..cd16874 100644 --- a/chrome/browser/sync/glue/password_model_worker.h +++ b/chrome/browser/sync/glue/password_model_worker.h @@ -26,6 +26,7 @@ namespace browser_sync { class PasswordModelWorker : public browser_sync::ModelSafeWorker { public: explicit PasswordModelWorker(PasswordStore* password_store); + virtual ~PasswordModelWorker(); // ModelSafeWorker implementation. Called on syncapi SyncerThread. void DoWorkAndWaitUntilDone(Callback0::Type* work); diff --git a/chrome/browser/sync/glue/session_model_associator.cc b/chrome/browser/sync/glue/session_model_associator.cc index 24ff417..b4666f8 100644 --- a/chrome/browser/sync/glue/session_model_associator.cc +++ b/chrome/browser/sync/glue/session_model_associator.cc @@ -192,7 +192,9 @@ bool SessionModelAssociator::GetSessionData( // Build vector of sessions from specifics data for (std::vector<const sync_pb::SessionSpecifics*>::const_iterator i = specifics_.begin(); i != specifics_.end(); ++i) { - AppendForeignSessionFromSpecifics(*i, sessions); + // Only include sessions with open windows. + if ((*i)->session_window_size() > 0) + AppendForeignSessionFromSpecifics(*i, sessions); } return true; @@ -202,7 +204,7 @@ void SessionModelAssociator::AppendForeignSessionFromSpecifics( const sync_pb::SessionSpecifics* specifics, std::vector<ForeignSession*>* session) { ForeignSession* foreign_session = new ForeignSession(); - foreign_session->foreign_tession_tag = specifics->session_tag(); + foreign_session->foreign_session_tag = specifics->session_tag(); session->insert(session->end(), foreign_session); for (int i = 0; i < specifics->session_window_size(); i++) { const sync_pb::SessionWindow* window = &specifics->session_window(i); @@ -248,7 +250,7 @@ void SessionModelAssociator::InitializeCurrentMachineTag() { // See issue 59672 current_machine_tag_ = "session_sync"; current_machine_tag_.append(dir->cache_guid()); - LOG(INFO) << "Creating machine tag: " << current_machine_tag_; + VLOG(1) << "Creating machine tag: " << current_machine_tag_; } // See PopulateSessionSpecificsTab for use. May add functionality that includes @@ -386,24 +388,16 @@ bool SessionModelAssociator::WindowHasNoTabsToSync( void SessionModelAssociator::OnGotSession(int handle, std::vector<SessionWindow*>* windows) { - sync_pb::SessionSpecifics session; + sync_pb::SessionSpecifics specifics; // Set the tag, then iterate through the vector of windows, extracting the // window data, along with the tabs data and tab navigation data to populate // the session specifics. - session.set_session_tag(GetCurrentMachineTag()); - for (std::vector<SessionWindow*>::const_iterator i = windows->begin(); - i != windows->end(); ++i) { - const SessionWindow* window = *i; - if (WindowHasNoTabsToSync(window)) { - continue; - } - sync_pb::SessionWindow* session_window = session.add_session_window(); - PopulateSessionSpecificsWindow(window, session_window); - } + specifics.set_session_tag(GetCurrentMachineTag()); + FillSpecificsFromSessions(windows, &specifics); bool has_nodes = false; if (!SyncModelHasUserCreatedNodes(&has_nodes)) return; - if (session.session_window_size() == 0 && has_nodes) + if (specifics.session_window_size() == 0 && has_nodes) return; sync_api::WriteTransaction trans( sync_service_->backend()->GetUserShareHandle()); @@ -412,7 +406,21 @@ void SessionModelAssociator::OnGotSession(int handle, LOG(ERROR) << kNoSessionsFolderError; return; } - UpdateSyncModel(&session, &trans, &root); + UpdateSyncModel(&specifics, &trans, &root); +} + +void SessionModelAssociator::FillSpecificsFromSessions( + std::vector<SessionWindow*>* windows, + sync_pb::SessionSpecifics* session) { + for (std::vector<SessionWindow*>::const_iterator i = windows->begin(); + i != windows->end(); ++i) { + const SessionWindow* window = *i; + if (WindowHasNoTabsToSync(window)) { + continue; + } + sync_pb::SessionWindow* session_window = session->add_session_window(); + PopulateSessionSpecificsWindow(window, session_window); + } } void SessionModelAssociator::AppendSessionTabNavigation( diff --git a/chrome/browser/sync/glue/session_model_associator.h b/chrome/browser/sync/glue/session_model_associator.h index ac7a234..89d30be 100644 --- a/chrome/browser/sync/glue/session_model_associator.h +++ b/chrome/browser/sync/glue/session_model_associator.h @@ -129,6 +129,10 @@ class SessionModelAssociator : public PerDataTypeAssociatorInterface< // Builds sessions from buffered specifics data bool GetSessionData(std::vector<ForeignSession*>* sessions); + // Helper method to generate session specifics from session windows. + void FillSpecificsFromSessions(std::vector<SessionWindow*>* windows, + sync_pb::SessionSpecifics* session); + // Helper method for converting session specifics to windows. void AppendForeignSessionFromSpecifics( const sync_pb::SessionSpecifics* specifics, diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc index 0637850..7d833bd 100644 --- a/chrome/browser/sync/glue/sync_backend_host.cc +++ b/chrome/browser/sync/glue/sync_backend_host.cc @@ -120,8 +120,8 @@ void SyncBackendHost::Initialize( // TODO(tim): Remove this special case once NIGORI is populated by // default. We piggy back off of the passwords flag for now to not // require both encryption and passwords flags. - bool enable_encryption = !CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableSyncPasswords) || types.count(syncable::PASSWORDS) > 0; + bool enable_encryption = CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableSyncPasswords) || types.count(syncable::PASSWORDS); if (enable_encryption) registrar_.routing_info[syncable::NIGORI] = GROUP_PASSIVE; diff --git a/chrome/browser/sync/glue/theme_change_processor.cc b/chrome/browser/sync/glue/theme_change_processor.cc index e22484c..55f9879 100644 --- a/chrome/browser/sync/glue/theme_change_processor.cc +++ b/chrome/browser/sync/glue/theme_change_processor.cc @@ -17,7 +17,7 @@ namespace browser_sync { namespace { -std::string GetThemeId(Extension* current_theme) { +std::string GetThemeId(const Extension* current_theme) { if (current_theme) { DCHECK(current_theme->is_theme()); } @@ -39,10 +39,10 @@ void ThemeChangeProcessor::Observe(NotificationType type, const NotificationDetails& details) { DCHECK(running()); DCHECK(profile_); - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); std::string current_or_future_theme_id = profile_->GetThemeProvider()->GetThemeID(); - Extension* current_theme = profile_->GetTheme(); + const Extension* current_theme = profile_->GetTheme(); switch (type.value) { case NotificationType::BROWSER_THEME_CHANGED: // We pay attention to this notification only when it signifies diff --git a/chrome/browser/sync/glue/theme_util.cc b/chrome/browser/sync/glue/theme_util.cc index f8b72d1..0f2a16b 100644 --- a/chrome/browser/sync/glue/theme_util.cc +++ b/chrome/browser/sync/glue/theme_util.cc @@ -87,7 +87,7 @@ void SetCurrentThemeFromThemeSpecifics( VLOG(1) << "Applying theme " << id << " with update_url " << update_url; ExtensionsService* extensions_service = profile->GetExtensionsService(); CHECK(extensions_service); - Extension* extension = extensions_service->GetExtensionById(id, true); + const Extension* extension = extensions_service->GetExtensionById(id, true); if (extension) { if (!extension->is_theme()) { VLOG(1) << "Extension " << id << " is not a theme; aborting"; diff --git a/chrome/browser/sync/glue/theme_util_unittest.cc b/chrome/browser/sync/glue/theme_util_unittest.cc index fb6641f..3eac301 100644 --- a/chrome/browser/sync/glue/theme_util_unittest.cc +++ b/chrome/browser/sync/glue/theme_util_unittest.cc @@ -23,17 +23,20 @@ using ::testing::Return; class ThemeUtilTest : public testing::Test { }; -void MakeThemeExtension(Extension* extension, - const std::string& name, - const std::string& update_url) { +scoped_refptr<Extension> MakeThemeExtension(const FilePath& extension_path, + const std::string& name, + const std::string& update_url) { DictionaryValue source; source.SetString(extension_manifest_keys::kName, name); source.Set(extension_manifest_keys::kTheme, new DictionaryValue()); source.SetString(extension_manifest_keys::kUpdateURL, update_url); source.SetString(extension_manifest_keys::kVersion, "0.0.0.0"); std::string error; - EXPECT_TRUE(extension->InitFromValue(source, false, &error)); + scoped_refptr<Extension> extension = Extension::Create( + extension_path, Extension::INTERNAL, source, false, &error); + EXPECT_TRUE(extension); EXPECT_EQ("", error); + return extension; } TEST_F(ThemeUtilTest, AreThemeSpecificsEqualHelper) { @@ -168,17 +171,17 @@ TEST_F(ThemeUtilTest, GetThemeSpecificsHelperCustomTheme) { theme_specifics.set_use_custom_theme(false); theme_specifics.set_use_system_theme_by_default(true); FilePath file_path(kExtensionFilePath); - Extension extension(file_path); const std::string kThemeName("name"); const std::string kThemeUpdateUrl("http://update.url/foo"); - MakeThemeExtension(&extension, kThemeName, kThemeUpdateUrl); - GetThemeSpecificsFromCurrentThemeHelper(&extension, false, false, + scoped_refptr<Extension> extension( + MakeThemeExtension(file_path, kThemeName, kThemeUpdateUrl)); + GetThemeSpecificsFromCurrentThemeHelper(extension.get(), false, false, &theme_specifics); EXPECT_TRUE(theme_specifics.use_custom_theme()); EXPECT_TRUE(theme_specifics.use_system_theme_by_default()); EXPECT_EQ(kThemeName, theme_specifics.custom_theme_name()); - EXPECT_EQ(extension.id(), theme_specifics.custom_theme_id()); + EXPECT_EQ(extension->id(), theme_specifics.custom_theme_id()); EXPECT_EQ(kThemeUpdateUrl, theme_specifics.custom_theme_update_url()); } @@ -186,18 +189,18 @@ TEST_F(ThemeUtilTest, GetThemeSpecificsHelperCustomThemeDistinct) { sync_pb::ThemeSpecifics theme_specifics; theme_specifics.set_use_custom_theme(false); FilePath file_path(kExtensionFilePath); - Extension extension(file_path); const std::string kThemeName("name"); const std::string kThemeUpdateUrl("http://update.url/foo"); - MakeThemeExtension(&extension, kThemeName, kThemeUpdateUrl); - GetThemeSpecificsFromCurrentThemeHelper(&extension, true, false, + scoped_refptr<Extension> extension( + MakeThemeExtension(file_path, kThemeName, kThemeUpdateUrl)); + GetThemeSpecificsFromCurrentThemeHelper(extension.get(), true, false, &theme_specifics); EXPECT_TRUE(theme_specifics.use_custom_theme()); EXPECT_TRUE(theme_specifics.has_use_system_theme_by_default()); EXPECT_FALSE(theme_specifics.use_system_theme_by_default()); EXPECT_EQ(kThemeName, theme_specifics.custom_theme_name()); - EXPECT_EQ(extension.id(), theme_specifics.custom_theme_id()); + EXPECT_EQ(extension->id(), theme_specifics.custom_theme_id()); EXPECT_EQ(kThemeUpdateUrl, theme_specifics.custom_theme_update_url()); } diff --git a/chrome/browser/sync/glue/typed_url_model_associator.cc b/chrome/browser/sync/glue/typed_url_model_associator.cc index dc9cc3a..ad15629 100644 --- a/chrome/browser/sync/glue/typed_url_model_associator.cc +++ b/chrome/browser/sync/glue/typed_url_model_associator.cc @@ -29,6 +29,8 @@ TypedUrlModelAssociator::TypedUrlModelAssociator( DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); } +TypedUrlModelAssociator::~TypedUrlModelAssociator() {} + bool TypedUrlModelAssociator::AssociateModels() { VLOG(1) << "Associating TypedUrl Models"; DCHECK(expected_loop_ == MessageLoop::current()); diff --git a/chrome/browser/sync/glue/typed_url_model_associator.h b/chrome/browser/sync/glue/typed_url_model_associator.h index e894100..a07bae9 100644 --- a/chrome/browser/sync/glue/typed_url_model_associator.h +++ b/chrome/browser/sync/glue/typed_url_model_associator.h @@ -57,7 +57,7 @@ class TypedUrlModelAssociator static syncable::ModelType model_type() { return syncable::TYPED_URLS; } TypedUrlModelAssociator(ProfileSyncService* sync_service, history::HistoryBackend* history_backend); - virtual ~TypedUrlModelAssociator() { } + virtual ~TypedUrlModelAssociator(); // PerDataTypeAssociatorInterface implementation. // diff --git a/chrome/browser/sync/notifier/DEPS b/chrome/browser/sync/notifier/DEPS index dd31bd4..346b3b5 100644 --- a/chrome/browser/sync/notifier/DEPS +++ b/chrome/browser/sync/notifier/DEPS @@ -3,6 +3,8 @@ include_rules = [ "+google/cacheinvalidation", # sync_notifier depends on the common jingle notifier classes. "+jingle/notifier", + # unit tests depend on talk/base. + "+talk/base", # sync_notifier depends on the xmpp part of libjingle. "+talk/xmpp", ] diff --git a/chrome/browser/sync/notifier/cache_invalidation_packet_handler.cc b/chrome/browser/sync/notifier/cache_invalidation_packet_handler.cc index dc1e7a3..7250c62 100644 --- a/chrome/browser/sync/notifier/cache_invalidation_packet_handler.cc +++ b/chrome/browser/sync/notifier/cache_invalidation_packet_handler.cc @@ -198,6 +198,9 @@ CacheInvalidationPacketHandler::CacheInvalidationPacketHandler( base::WeakPtr<talk_base::Task> base_task, invalidation::InvalidationClient* invalidation_client) : scoped_callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + handle_outbound_packet_callback_( + scoped_callback_factory_.NewCallback( + &CacheInvalidationPacketHandler::HandleOutboundPacket)), base_task_(base_task), invalidation_client_(invalidation_client), seq_(0), @@ -208,9 +211,8 @@ CacheInvalidationPacketHandler::CacheInvalidationPacketHandler( invalidation_client_->network_endpoint(); CHECK(network_endpoint); network_endpoint->RegisterOutboundListener( - scoped_callback_factory_.NewCallback( - &CacheInvalidationPacketHandler::HandleOutboundPacket)); - // Owned by base_task. + handle_outbound_packet_callback_.get()); + // Owned by base_task. Takes ownership of the callback. CacheInvalidationListenTask* listen_task = new CacheInvalidationListenTask( base_task_, scoped_callback_factory_.NewCallback( @@ -219,6 +221,7 @@ CacheInvalidationPacketHandler::CacheInvalidationPacketHandler( } CacheInvalidationPacketHandler::~CacheInvalidationPacketHandler() { + DCHECK(non_thread_safe_.CalledOnValidThread()); invalidation::NetworkEndpoint* network_endpoint = invalidation_client_->network_endpoint(); CHECK(network_endpoint); @@ -227,6 +230,7 @@ CacheInvalidationPacketHandler::~CacheInvalidationPacketHandler() { void CacheInvalidationPacketHandler::HandleOutboundPacket( invalidation::NetworkEndpoint* const& network_endpoint) { + DCHECK(non_thread_safe_.CalledOnValidThread()); if (!base_task_.get()) { return; } @@ -251,6 +255,7 @@ void CacheInvalidationPacketHandler::HandleOutboundPacket( void CacheInvalidationPacketHandler::HandleInboundPacket( const std::string& packet) { + DCHECK(non_thread_safe_.CalledOnValidThread()); invalidation::NetworkEndpoint* network_endpoint = invalidation_client_->network_endpoint(); std::string decoded_message; diff --git a/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h b/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h index 3c3bfec..16e86ff 100644 --- a/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h +++ b/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h @@ -12,7 +12,11 @@ #include <string> #include "base/basictypes.h" +#include "base/callback.h" +#include "base/gtest_prod_util.h" +#include "base/non_thread_safe.h" #include "base/scoped_callback_factory.h" +#include "base/scoped_ptr.h" #include "base/weak_ptr.h" #include "talk/xmpp/jid.h" @@ -27,8 +31,6 @@ class Task; namespace sync_notifier { -// TODO(akalin): Add a NonThreadSafe member to this class and use it. - class CacheInvalidationPacketHandler { public: // Starts routing packets from |invalidation_client| using @@ -45,13 +47,18 @@ class CacheInvalidationPacketHandler { ~CacheInvalidationPacketHandler(); private: + FRIEND_TEST(CacheInvalidationPacketHandlerTest, Basic); + void HandleOutboundPacket( invalidation::NetworkEndpoint* const& network_endpoint); void HandleInboundPacket(const std::string& packet); + NonThreadSafe non_thread_safe_; base::ScopedCallbackFactory<CacheInvalidationPacketHandler> scoped_callback_factory_; + scoped_ptr<CallbackRunner<Tuple1<invalidation::NetworkEndpoint* const&> > > + handle_outbound_packet_callback_; base::WeakPtr<talk_base::Task> base_task_; invalidation::InvalidationClient* invalidation_client_; diff --git a/chrome/browser/sync/notifier/cache_invalidation_packet_handler_unittest.cc b/chrome/browser/sync/notifier/cache_invalidation_packet_handler_unittest.cc new file mode 100644 index 0000000..31b7f0a --- /dev/null +++ b/chrome/browser/sync/notifier/cache_invalidation_packet_handler_unittest.cc @@ -0,0 +1,111 @@ +// 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/sync/notifier/cache_invalidation_packet_handler.h" + +#include "base/base64.h" +#include "base/message_loop.h" +#include "base/weak_ptr.h" +#include "google/cacheinvalidation/invalidation-client.h" +#include "jingle/notifier/base/task_pump.h" +#include "jingle/notifier/base/weak_xmpp_client.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "talk/base/task.h" +#include "talk/xmpp/asyncsocket.h" + +namespace sync_notifier { + +using ::testing::_; +using ::testing::NotNull; +using ::testing::Return; + +class MockNetworkEndpoint : public invalidation::NetworkEndpoint { + public: + MOCK_METHOD1(RegisterOutboundListener, + void(invalidation::NetworkCallback*)); + MOCK_METHOD1(HandleInboundMessage, void(const invalidation::string&)); + MOCK_METHOD1(TakeOutboundMessage, void(invalidation::string*)); + MOCK_METHOD1(AdviseNetworkStatus, void(bool)); +}; + +class MockInvalidationClient : public invalidation::InvalidationClient { + public: + MOCK_METHOD1(Register, void(const invalidation::ObjectId&)); + MOCK_METHOD1(Unregister, void(const invalidation::ObjectId&)); + MOCK_METHOD0(network_endpoint, invalidation::NetworkEndpoint*()); +}; + +class MockAsyncSocket : public buzz::AsyncSocket { + public: + virtual ~MockAsyncSocket() {} + + MOCK_METHOD0(state, State()); + MOCK_METHOD0(error, Error()); + MOCK_METHOD0(GetError, int()); + MOCK_METHOD1(Connect, bool(const talk_base::SocketAddress&)); + MOCK_METHOD3(Read, bool(char*, size_t, size_t*)); + MOCK_METHOD2(Write, bool(const char*, size_t)); + MOCK_METHOD0(Close, bool()); + MOCK_METHOD1(StartTls, bool(const std::string&)); +}; + +class CacheInvalidationPacketHandlerTest : public testing::Test { + public: + virtual ~CacheInvalidationPacketHandlerTest() {} +}; + +TEST_F(CacheInvalidationPacketHandlerTest, Basic) { + MessageLoop message_loop; + + notifier::TaskPump task_pump; + // Owned by |task_pump|. + notifier::WeakXmppClient* weak_xmpp_client = + new notifier::WeakXmppClient(&task_pump); + base::WeakPtr<talk_base::Task> base_task(weak_xmpp_client->AsWeakPtr()); + MockNetworkEndpoint mock_network_endpoint; + MockInvalidationClient mock_invalidation_client; + + EXPECT_CALL(mock_invalidation_client, network_endpoint()). + WillRepeatedly(Return(&mock_network_endpoint)); + + EXPECT_CALL(mock_network_endpoint, + RegisterOutboundListener(NotNull())).Times(1); + EXPECT_CALL(mock_network_endpoint, + RegisterOutboundListener(NULL)).Times(1); + const char kInboundMessage[] = "non-bogus"; + EXPECT_CALL(mock_network_endpoint, + HandleInboundMessage(kInboundMessage)).Times(1); + EXPECT_CALL(mock_network_endpoint, TakeOutboundMessage(_)).Times(1); + + weak_xmpp_client->Start(); + buzz::XmppClientSettings settings; + // Owned by |weak_xmpp_client|. + MockAsyncSocket* mock_async_socket = new MockAsyncSocket(); + EXPECT_CALL(*mock_async_socket, Connect(_)).WillOnce(Return(true)); + weak_xmpp_client->Connect(settings, "en", mock_async_socket, NULL); + // Initialize the XMPP client. + message_loop.RunAllPending(); + + { + CacheInvalidationPacketHandler handler( + base_task, &mock_invalidation_client); + // Take care of any tasks posted by the constructor. + message_loop.RunAllPending(); + + { + handler.HandleInboundPacket("bogus"); + std::string inbound_message_encoded; + EXPECT_TRUE( + base::Base64Encode(kInboundMessage, &inbound_message_encoded)); + handler.HandleInboundPacket(inbound_message_encoded); + } + + handler.HandleOutboundPacket(&mock_network_endpoint); + // Take care of any tasks posted by HandleOutboundPacket(). + message_loop.RunAllPending(); + } +} + +} // namespace sync_notifier diff --git a/chrome/browser/sync/profile_sync_factory_impl.cc b/chrome/browser/sync/profile_sync_factory_impl.cc index 2585f71..ed8f429 100644 --- a/chrome/browser/sync/profile_sync_factory_impl.cc +++ b/chrome/browser/sync/profile_sync_factory_impl.cc @@ -111,7 +111,7 @@ ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService( // Password sync is disabled by default. Register only if // explicitly enabled. - if (!command_line_->HasSwitch(switches::kDisableSyncPasswords)) { + if (command_line_->HasSwitch(switches::kEnableSyncPasswords)) { pss->RegisterDataTypeController( new PasswordDataTypeController(this, profile_, pss)); } diff --git a/chrome/browser/sync/profile_sync_factory_impl_unittest.cc b/chrome/browser/sync/profile_sync_factory_impl_unittest.cc index 2e1a708..f622ef4 100644 --- a/chrome/browser/sync/profile_sync_factory_impl_unittest.cc +++ b/chrome/browser/sync/profile_sync_factory_impl_unittest.cc @@ -43,14 +43,13 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDefault) { DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); - EXPECT_EQ(7U, controller_states_ptr->size()); + EXPECT_EQ(6U, controller_states_ptr->size()); EXPECT_EQ(1U, controller_states_ptr->count(syncable::BOOKMARKS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::PREFERENCES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::AUTOFILL)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::THEMES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::EXTENSIONS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::APPS)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::PASSWORDS)); } TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableAutofill) { @@ -60,14 +59,13 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableAutofill) { DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); - EXPECT_EQ(6U, controller_states_ptr->size()); + EXPECT_EQ(5U, controller_states_ptr->size()); EXPECT_EQ(1U, controller_states_ptr->count(syncable::BOOKMARKS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::PREFERENCES)); EXPECT_EQ(0U, controller_states_ptr->count(syncable::AUTOFILL)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::THEMES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::EXTENSIONS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::APPS)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::PASSWORDS)); } TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableBookmarks) { @@ -77,14 +75,13 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableBookmarks) { DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); - EXPECT_EQ(6U, controller_states_ptr->size()); + EXPECT_EQ(5U, controller_states_ptr->size()); EXPECT_EQ(0U, controller_states_ptr->count(syncable::BOOKMARKS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::PREFERENCES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::AUTOFILL)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::THEMES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::EXTENSIONS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::APPS)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::PASSWORDS)); } TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisablePreferences) { @@ -94,14 +91,13 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisablePreferences) { DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); - EXPECT_EQ(6U, controller_states_ptr->size()); + EXPECT_EQ(5U, controller_states_ptr->size()); EXPECT_EQ(1U, controller_states_ptr->count(syncable::BOOKMARKS)); EXPECT_EQ(0U, controller_states_ptr->count(syncable::PREFERENCES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::AUTOFILL)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::THEMES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::EXTENSIONS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::APPS)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::PASSWORDS)); } TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableThemes) { @@ -111,14 +107,13 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableThemes) { DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); - EXPECT_EQ(6U, controller_states_ptr->size()); + EXPECT_EQ(5U, controller_states_ptr->size()); EXPECT_EQ(1U, controller_states_ptr->count(syncable::BOOKMARKS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::PREFERENCES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::AUTOFILL)); EXPECT_EQ(0U, controller_states_ptr->count(syncable::THEMES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::EXTENSIONS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::APPS)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::PASSWORDS)); } TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableExtensions) { @@ -128,14 +123,13 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableExtensions) { DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); - EXPECT_EQ(6U, controller_states_ptr->size()); + EXPECT_EQ(5U, controller_states_ptr->size()); EXPECT_EQ(1U, controller_states_ptr->count(syncable::BOOKMARKS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::PREFERENCES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::AUTOFILL)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::THEMES)); EXPECT_EQ(0U, controller_states_ptr->count(syncable::EXTENSIONS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::APPS)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::PASSWORDS)); } TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableApps) { @@ -145,29 +139,11 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableApps) { DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); - EXPECT_EQ(6U, controller_states_ptr->size()); + EXPECT_EQ(5U, controller_states_ptr->size()); EXPECT_EQ(1U, controller_states_ptr->count(syncable::BOOKMARKS)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::PREFERENCES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::AUTOFILL)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::THEMES)); EXPECT_EQ(1U, controller_states_ptr->count(syncable::EXTENSIONS)); EXPECT_EQ(0U, controller_states_ptr->count(syncable::APPS)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::PASSWORDS)); -} - -TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisablePasswords) { - command_line_->AppendSwitch(switches::kDisableSyncPasswords); - scoped_ptr<ProfileSyncService> pss; - pss.reset(profile_sync_service_factory_->CreateProfileSyncService("")); - DataTypeController::StateMap controller_states; - DataTypeController::StateMap* controller_states_ptr = &controller_states; - pss->GetDataTypeControllerStates(controller_states_ptr); - EXPECT_EQ(6U, controller_states_ptr->size()); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::BOOKMARKS)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::PREFERENCES)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::AUTOFILL)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::THEMES)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::EXTENSIONS)); - EXPECT_EQ(1U, controller_states_ptr->count(syncable::APPS)); - EXPECT_EQ(0U, controller_states_ptr->count(syncable::PASSWORDS)); } diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc index cc0508b..7b210d0 100644 --- a/chrome/browser/sync/profile_sync_service.cc +++ b/chrome/browser/sync/profile_sync_service.cc @@ -8,12 +8,14 @@ #include <set> #include "app/l10n_util.h" +#include "base/basictypes.h" #include "base/callback.h" #include "base/command_line.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/stl_util-inl.h" #include "base/string16.h" +#include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/task.h" #include "base/utf_string_conversions.h" @@ -205,10 +207,10 @@ void ProfileSyncService::RegisterAuthNotifications() { Source<TokenService>(profile_->GetTokenService())); registrar_.Add(this, NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, - Source<SigninManager>(&signin_)); + Source<Profile>(profile_)); registrar_.Add(this, NotificationType::GOOGLE_SIGNIN_FAILED, - Source<SigninManager>(&signin_)); + Source<Profile>(profile_)); } void ProfileSyncService::RegisterDataTypeController( @@ -248,6 +250,32 @@ void ProfileSyncService::GetDataTypeControllerStates( (*state_map)[iter->first] = iter->second.get()->state(); } +namespace { + +// TODO(akalin): Figure out whether this should be a method of +// HostPortPair. +net::HostPortPair StringToHostPortPair(const std::string& host_port_str, + uint16 default_port) { + std::string::size_type colon_index = host_port_str.find(':'); + if (colon_index == std::string::npos) { + return net::HostPortPair(host_port_str, default_port); + } + + std::string host = host_port_str.substr(0, colon_index); + std::string port_str = host_port_str.substr(colon_index + 1); + int port = default_port; + if (!base::StringToInt(port_str, &port) || + (port <= 0) || (port > kuint16max)) { + LOG(WARNING) << "Could not parse valid port from " << port_str + << "; using port " << default_port; + return net::HostPortPair(host, default_port); + } + + return net::HostPortPair(host, port); +} + +} // namespace + void ProfileSyncService::InitSettings() { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); @@ -272,18 +300,30 @@ void ProfileSyncService::InitSettings() { std::string value(command_line.GetSwitchValueASCII( switches::kSyncNotificationHost)); if (!value.empty()) { - notifier_options_.xmpp_host_port.set_host(value); - notifier_options_.xmpp_host_port.set_port(notifier::kDefaultXmppPort); + notifier_options_.xmpp_host_port = + StringToHostPortPair(value, notifier::kDefaultXmppPort); } VLOG(1) << "Using " << notifier_options_.xmpp_host_port.ToString() << " for test sync notification server."; } notifier_options_.try_ssltcp_first = - command_line.HasSwitch(switches::kSyncUseSslTcp); + command_line.HasSwitch(switches::kSyncTrySsltcpFirstForXmpp); if (notifier_options_.try_ssltcp_first) VLOG(1) << "Trying SSL/TCP port before XMPP port for notifications."; + notifier_options_.invalidate_xmpp_login = + command_line.HasSwitch(switches::kSyncInvalidateXmppLogin); + if (notifier_options_.invalidate_xmpp_login) { + VLOG(1) << "Invalidating sync XMPP login."; + } + + notifier_options_.allow_insecure_connection = + command_line.HasSwitch(switches::kSyncAllowInsecureXmppConnection); + if (notifier_options_.allow_insecure_connection) { + VLOG(1) << "Allowing insecure XMPP connections."; + } + if (command_line.HasSwitch(switches::kSyncNotificationMethod)) { const std::string notification_method_str( command_line.GetSwitchValueASCII(switches::kSyncNotificationMethod)); @@ -390,7 +430,7 @@ void ProfileSyncService::CreateBackend() { void ProfileSyncService::StartUp() { // Don't start up multiple times. if (backend_.get()) { - LOG(INFO) << "Skipping bringing up backend host."; + VLOG(1) << "Skipping bringing up backend host."; return; } @@ -451,6 +491,7 @@ void ProfileSyncService::Shutdown(bool sync_disabled) { backend_initialized_ = false; observed_passphrase_required_ = false; last_attempted_user_email_.clear(); + last_auth_error_ = GoogleServiceAuthError::None(); } void ProfileSyncService::ClearServerData() { @@ -774,6 +815,12 @@ void ProfileSyncService::OnUserSubmittedAuth( signin_.SignOut(); } + // The user has submitted credentials, which indicates they don't + // want to suppress start up anymore. + PrefService* prefs = profile_->GetPrefs(); + prefs->SetBoolean(prefs::kSyncSuppressStart, false); + prefs->ScheduleSavePersistentPrefs(); + signin_.StartSignIn(username, password, last_auth_error_.captcha().token, diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h index c96799b..65f18f5 100644 --- a/chrome/browser/sync/profile_sync_service.h +++ b/chrome/browser/sync/profile_sync_service.h @@ -172,7 +172,7 @@ class ProfileSyncService : public browser_sync::SyncFrontend, // Whether sync is enabled by user or not. virtual bool HasSyncSetupCompleted() const; - void SetSyncSetupCompleted(); + virtual void SetSyncSetupCompleted(); // SyncFrontend implementation. virtual void OnBackendInitialized(); @@ -355,6 +355,7 @@ class ProfileSyncService : public browser_sync::SyncFrontend, const GURL& sync_service_url() const { return sync_service_url_; } + SigninManager* signin() { return &signin_; } protected: // Used by ProfileSyncServiceMock only. // @@ -407,7 +408,6 @@ class ProfileSyncService : public browser_sync::SyncFrontend, friend class ProfileSyncServicePasswordTest; friend class ProfileSyncServicePreferenceTest; friend class ProfileSyncServiceSessionTest; - friend class ProfileSyncServiceTestHarness; FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceTest, InitialState); FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceTest, UnrecoverableErrorSuspendsService); diff --git a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc index 6a7c862..17354b0 100644 --- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc @@ -265,7 +265,8 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest { } entries->push_back(AutofillEntry(key, timestamps)); } else if (autofill.has_profile()) { - AutoFillProfile p(UTF8ToUTF16(autofill.profile().label()), 0); + AutoFillProfile p; + p.set_label(UTF8ToUTF16(autofill.profile().label())); AutofillModelAssociator::OverwriteProfileWithServerData(&p, autofill.profile()); profiles->push_back(p); @@ -420,7 +421,7 @@ class FakeServerUpdater: public base::RefCountedThreadSafe<FakeServerUpdater> { item.Put(SPECIFICS, entity_specifics); item.Put(SERVER_SPECIFICS, entity_specifics); item.Put(BASE_VERSION, 1); - syncable::Id server_parent_id = service_->id_factory()->NewServerId(); + syncable::Id server_parent_id = ids_.NewServerId(); item.Put(syncable::ID, server_parent_id); syncable::Id new_predecessor = SyncerUtil::ComputePrevIdFromServerPosition(&trans, &item, @@ -473,6 +474,7 @@ class FakeServerUpdater: public base::RefCountedThreadSafe<FakeServerUpdater> { scoped_ptr<WaitableEvent> *wait_for_syncapi_; WaitableEvent is_finished_; syncable::Id parent_id_; + TestIdFactory ids_; }; // TODO(skrul): Test abort startup. @@ -528,7 +530,7 @@ TEST_F(ProfileSyncServiceAutofillTest, HasMixedNativeEmptySync) { std::vector<AutoFillProfile*> profiles; std::vector<AutoFillProfile> expected_profiles; // Owned by GetAutoFillProfiles caller. - AutoFillProfile* profile0 = new AutoFillProfile(string16(), 0); + AutoFillProfile* profile0 = new AutoFillProfile; autofill_test::SetProfileInfo(profile0, "Billing", "Marion", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", @@ -548,7 +550,7 @@ TEST_F(ProfileSyncServiceAutofillTest, HasMixedNativeEmptySync) { ASSERT_EQ(1U, entries.size()); EXPECT_TRUE(entries[0] == sync_entries[0]); EXPECT_EQ(1U, sync_profiles.size()); - EXPECT_EQ(expected_profiles[0], sync_profiles[0]); + EXPECT_EQ(0, expected_profiles[0].Compare(sync_profiles[0])); } bool ProfilesMatchExceptLabelImpl(AutoFillProfile p1, AutoFillProfile p2) { @@ -583,12 +585,12 @@ MATCHER_P(ProfileMatchesExceptLabel, profile, "") { TEST_F(ProfileSyncServiceAutofillTest, HasDuplicateProfileLabelsEmptySync) { std::vector<AutoFillProfile> expected_profiles; std::vector<AutoFillProfile*> profiles; - AutoFillProfile* profile0 = new AutoFillProfile(string16(), 0); + AutoFillProfile* profile0 = new AutoFillProfile; autofill_test::SetProfileInfo(profile0, "Billing", "Marion", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - AutoFillProfile* profile1 = new AutoFillProfile(string16(), 0); + AutoFillProfile* profile1 = new AutoFillProfile; autofill_test::SetProfileInfo(profile1, "Billing", "Same", "Label", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", @@ -615,7 +617,7 @@ TEST_F(ProfileSyncServiceAutofillTest, HasDuplicateProfileLabelsEmptySync) { ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); EXPECT_EQ(0U, sync_entries.size()); EXPECT_EQ(2U, sync_profiles.size()); - EXPECT_EQ(expected_profiles[0], sync_profiles[1]); + EXPECT_EQ(0, expected_profiles[0].Compare(sync_profiles[1])); EXPECT_TRUE(ProfilesMatchExceptLabelImpl(expected_profiles[1], sync_profiles[0])); EXPECT_EQ(sync_profiles[0].Label(), relabelled_profile.Label()); @@ -645,13 +647,13 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeWithDuplicatesEmptySync) { TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) { AutofillEntry native_entry(MakeAutofillEntry("native", "entry", 1)); AutofillEntry sync_entry(MakeAutofillEntry("sync", "entry", 2)); - AutoFillProfile sync_profile(string16(), 0); + AutoFillProfile sync_profile; autofill_test::SetProfileInfo(&sync_profile, "Billing", "Marion", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - AutoFillProfile* native_profile = new AutoFillProfile(string16(), 0); + AutoFillProfile* native_profile = new AutoFillProfile; autofill_test::SetProfileInfo(native_profile, "Work", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", @@ -677,10 +679,11 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) { AddAutofillEntriesTask task(this, sync_entries, sync_profiles); AutoFillProfile to_be_added(sync_profile); - to_be_added.set_unique_id(1); EXPECT_CALL(web_database_, UpdateAutofillEntries(ElementsAre(sync_entry))). WillOnce(Return(true)); - EXPECT_CALL(web_database_, AddAutoFillProfile(Eq(to_be_added))). + // TODO(dhollowa): Duplicate removal when contents match but GUIDs don't. + // http://crbug.com/58813 + EXPECT_CALL(web_database_, AddAutoFillProfile(_)). WillOnce(Return(true)); EXPECT_CALL(*personal_data_manager_, Refresh()); StartSyncService(&task, false); @@ -699,8 +702,8 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) { EXPECT_TRUE(expected_entries == new_sync_entries_set); EXPECT_EQ(2U, new_sync_profiles.size()); - EXPECT_EQ(expected_profiles[0], new_sync_profiles[0]); - EXPECT_EQ(expected_profiles[1], new_sync_profiles[1]); + EXPECT_EQ(0, expected_profiles[0].Compare(new_sync_profiles[0])); + EXPECT_EQ(0, expected_profiles[1].Compare(new_sync_profiles[1])); } TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) { @@ -734,13 +737,13 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) { } TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) { - AutoFillProfile sync_profile(string16(), 0); + AutoFillProfile sync_profile; autofill_test::SetProfileInfo(&sync_profile, "Billing", "Marion", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910", "01987654321"); - AutoFillProfile* native_profile = new AutoFillProfile(string16(), 0); + AutoFillProfile* native_profile = new AutoFillProfile; autofill_test::SetProfileInfo(native_profile, "Billing", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", @@ -757,7 +760,9 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) { sync_profiles.push_back(sync_profile); AddAutofillEntriesTask task(this, sync_entries, sync_profiles); - EXPECT_CALL(web_database_, UpdateAutoFillProfile(Eq(sync_profile))). + // TODO(dhollowa): Duplicate removal when contents match but GUIDs don't. + // http://crbug.com/58813 + EXPECT_CALL(web_database_, UpdateAutoFillProfile(_)). WillOnce(Return(true)); EXPECT_CALL(*personal_data_manager_, Refresh()); StartSyncService(&task, false); @@ -768,9 +773,7 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) { ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles)); ASSERT_EQ(1U, new_sync_profiles.size()); - // TODO(dhollowa): Replace with |AutoFillProfile::Compare|. - // http://crbug.com/58813 - EXPECT_TRUE(sync_profile == new_sync_profiles[0]); + EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0])); } TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) { @@ -790,7 +793,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) { AutofillChangeList changes; changes.push_back(AutofillChange(AutofillChange::ADD, added_entry.key())); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&db_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED, Source<WebDataService>(web_data_service_.get()), Details<AutofillChangeList>(&changes)); @@ -812,7 +815,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) { StartSyncService(&task, false); ASSERT_TRUE(task.success()); - AutoFillProfile added_profile(string16(), 0); + AutoFillProfile added_profile; autofill_test::SetProfileInfo(&added_profile, "Billing", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", @@ -820,7 +823,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) { AutofillProfileChange change(AutofillProfileChange::ADD, added_profile.Label(), &added_profile, string16()); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&db_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED, Source<WebDataService>(web_data_service_.get()), Details<AutofillProfileChange>(&change)); @@ -830,13 +833,11 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) { ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles)); ASSERT_EQ(1U, new_sync_profiles.size()); - // TODO(dhollowa): Replace with |AutoFillProfile::Compare|. - // http://crbug.com/58813 - EXPECT_TRUE(added_profile == new_sync_profiles[0]); + EXPECT_EQ(0, added_profile.Compare(new_sync_profiles[0])); } TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfileConflict) { - AutoFillProfile sync_profile(string16(), 0); + AutoFillProfile sync_profile; autofill_test::SetProfileInfo(&sync_profile, "Billing", "Marion", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", @@ -847,17 +848,18 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfileConflict) { sync_profiles.push_back(sync_profile); AddAutofillEntriesTask task(this, sync_entries, sync_profiles); - sync_profile.set_unique_id(1); EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true)); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true)); - EXPECT_CALL(web_database_, AddAutoFillProfile(Eq(sync_profile))). + // TODO(dhollowa): Duplicate removal when contents match but GUIDs don't. + // http://crbug.com/58813 + EXPECT_CALL(web_database_, AddAutoFillProfile(_)). WillOnce(Return(true)); EXPECT_CALL(*personal_data_manager_, Refresh()); SetIdleChangeProcessorExpectations(); StartSyncService(&task, false); ASSERT_TRUE(task.success()); - AutoFillProfile added_profile(string16(), 0); + AutoFillProfile added_profile; autofill_test::SetProfileInfo(&added_profile, "Billing", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", @@ -872,7 +874,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfileConflict) { WillOnce(DoAll(SaveArg<0>(&relabelled_profile), Return(true))); EXPECT_CALL(*personal_data_manager_, Refresh()); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&db_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED, Source<WebDataService>(web_data_service_.get()), Details<AutofillProfileChange>(&change)); @@ -882,8 +884,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfileConflict) { ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles)); ASSERT_EQ(2U, new_sync_profiles.size()); - sync_profile.set_unique_id(0); // The sync DB doesn't store IDs. - EXPECT_EQ(sync_profile, new_sync_profiles[1]); + EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[1])); EXPECT_TRUE(ProfilesMatchExceptLabelImpl(added_profile, new_sync_profiles[0])); EXPECT_EQ(new_sync_profiles[0].Label(), relabelled_profile.Label()); @@ -911,7 +912,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) { AutofillChangeList changes; changes.push_back(AutofillChange(AutofillChange::UPDATE, updated_entry.key())); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&db_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED, Source<WebDataService>(web_data_service_.get()), Details<AutofillChangeList>(&changes)); @@ -926,7 +927,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) { TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfile) { - AutoFillProfile* native_profile = new AutoFillProfile(string16(), 0); + AutoFillProfile* native_profile = new AutoFillProfile; autofill_test::SetProfileInfo(native_profile, "Billing", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", @@ -941,7 +942,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfile) { StartSyncService(&task, false); ASSERT_TRUE(task.success()); - AutoFillProfile update_profile(string16(), 0); + AutoFillProfile update_profile; autofill_test::SetProfileInfo(&update_profile, "Billing", "Changin'", "Mah", "Namez", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", @@ -950,7 +951,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfile) { AutofillProfileChange change(AutofillProfileChange::UPDATE, update_profile.Label(), &update_profile, ASCIIToUTF16("Billing")); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&db_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED, Source<WebDataService>(web_data_service_.get()), Details<AutofillProfileChange>(&change)); @@ -960,13 +961,11 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfile) { ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles)); ASSERT_EQ(1U, new_sync_profiles.size()); - // TODO(dhollowa): Replace with |AutoFillProfile::Compare|. - // http://crbug.com/58813 - EXPECT_TRUE(update_profile == new_sync_profiles[0]); + EXPECT_EQ(0, update_profile.Compare(new_sync_profiles[0])); } TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfileRelabel) { - AutoFillProfile* native_profile = new AutoFillProfile(string16(), 0); + AutoFillProfile* native_profile = new AutoFillProfile; autofill_test::SetProfileInfo(native_profile, "Billing", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", @@ -981,7 +980,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfileRelabel) { StartSyncService(&task, false); ASSERT_TRUE(task.success()); - AutoFillProfile update_profile(string16(), 0); + AutoFillProfile update_profile; autofill_test::SetProfileInfo(&update_profile, "TRYIN 2 FOOL U", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", @@ -990,7 +989,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfileRelabel) { AutofillProfileChange change(AutofillProfileChange::UPDATE, update_profile.Label(), &update_profile, ASCIIToUTF16("Billing")); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&db_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED, Source<WebDataService>(web_data_service_.get()), Details<AutofillProfileChange>(&change)); @@ -1000,16 +999,14 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfileRelabel) { ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles)); ASSERT_EQ(1U, new_sync_profiles.size()); - // TODO(dhollowa): Replace with |AutoFillProfile::Compare|. - // http://crbug.com/58813 - EXPECT_TRUE(update_profile == new_sync_profiles[0]); + EXPECT_EQ(0, update_profile.Compare(new_sync_profiles[0])); } TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfileRelabelConflict) { std::vector<AutoFillProfile*> native_profiles; - native_profiles.push_back(new AutoFillProfile(string16(), 0)); - native_profiles.push_back(new AutoFillProfile(string16(), 0)); + native_profiles.push_back(new AutoFillProfile); + native_profiles.push_back(new AutoFillProfile); autofill_test::SetProfileInfo(native_profiles[0], "Billing", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", @@ -1049,7 +1046,7 @@ TEST_F(ProfileSyncServiceAutofillTest, AutofillProfileChange change(AutofillProfileChange::UPDATE, josephine_update.Label(), &josephine_update, josephine.Label()); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&db_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED, Source<WebDataService>(web_data_service_.get()), Details<AutofillProfileChange>(&change)); @@ -1061,8 +1058,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles)); ASSERT_EQ(2U, new_sync_profiles.size()); - marion.set_unique_id(0); // The sync DB doesn't store IDs. - EXPECT_EQ(marion, new_sync_profiles[1]); + EXPECT_EQ(0, marion.Compare(new_sync_profiles[1])); EXPECT_TRUE(ProfilesMatchExceptLabelImpl(josephine_update, new_sync_profiles[0])); EXPECT_EQ(ASCIIToUTF16("ExistingLabel2"), new_sync_profiles[0].Label()); @@ -1087,7 +1083,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) { AutofillChangeList changes; changes.push_back(AutofillChange(AutofillChange::REMOVE, original_entry.key())); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&db_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED, Source<WebDataService>(web_data_service_.get()), Details<AutofillChangeList>(&changes)); @@ -1100,12 +1096,12 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) { } TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) { - AutoFillProfile sync_profile(string16(), 0); + AutoFillProfile sync_profile; autofill_test::SetProfileInfo(&sync_profile, "Billing", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", "32801", "US", "19482937549", "13502849239"); - AutoFillProfile* native_profile = new AutoFillProfile(string16(), 0); + AutoFillProfile* native_profile = new AutoFillProfile; autofill_test::SetProfileInfo(native_profile, "Billing", "Josephine", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", @@ -1127,7 +1123,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) { AutofillProfileChange change(AutofillProfileChange::REMOVE, sync_profile.Label(), NULL, string16()); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&db_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED, Source<WebDataService>(web_data_service_.get()), Details<AutofillProfileChange>(&change)); @@ -1155,7 +1151,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeError) { AutofillChangeList changes; changes.push_back(AutofillChange(AutofillChange::ADD, evil_entry.key())); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&db_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED, Source<WebDataService>(web_data_service_.get()), Details<AutofillChangeList>(&changes)); @@ -1187,8 +1183,8 @@ TEST_F(ProfileSyncServiceAutofillTest, DISABLED_ServerChangeRace) { // (true, false) means we have to reset after |Signal|, init to unsignaled. scoped_ptr<WaitableEvent> wait_for_start(new WaitableEvent(true, false)); scoped_ptr<WaitableEvent> wait_for_syncapi(new WaitableEvent(true, false)); - scoped_refptr<FakeServerUpdater> updater = new FakeServerUpdater( - service_.get(), &wait_for_start, &wait_for_syncapi); + scoped_refptr<FakeServerUpdater> updater(new FakeServerUpdater( + service_.get(), &wait_for_start, &wait_for_syncapi)); // This server side update will stall waiting for CommitWaiter. updater->CreateNewEntry(MakeAutofillEntry("server", "entry", 1)); diff --git a/chrome/browser/sync/profile_sync_service_harness.cc b/chrome/browser/sync/profile_sync_service_harness.cc new file mode 100644 index 0000000..4750033 --- /dev/null +++ b/chrome/browser/sync/profile_sync_service_harness.cc @@ -0,0 +1,538 @@ +// 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 "base/message_loop.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/defaults.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/sync/glue/sync_backend_host.h" +#include "chrome/browser/sync/profile_sync_service_harness.h" +#include "chrome/browser/sync/sessions/session_state.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/net/gaia/gaia_constants.h" +#include "chrome/common/net/gaia/google_service_auth_error.h" +#include "chrome/common/pref_names.h" + +// The default value for min_timestamp_needed_ when we're not in the +// WAITING_FOR_UPDATES state. +static const int kMinTimestampNeededNone = -1; + +// The amount of time for which we wait for a live sync operation to complete. +static const int kLiveSyncOperationTimeoutMs = 30000; + +// Simple class to implement a timeout using PostDelayedTask. If it is not +// aborted before picked up by a message queue, then it asserts with the message +// provided. This class is not thread safe. +class StateChangeTimeoutEvent + : public base::RefCountedThreadSafe<StateChangeTimeoutEvent> { + public: + StateChangeTimeoutEvent(ProfileSyncServiceHarness* caller, + const std::string& message); + + // The entry point to the class from PostDelayedTask. + void Callback(); + + // Cancels the actions of the callback. Returns true if success, false + // if the callback has already timed out. + bool Abort(); + + private: + friend class base::RefCountedThreadSafe<StateChangeTimeoutEvent>; + + ~StateChangeTimeoutEvent(); + + bool aborted_; + bool did_timeout_; + + // Due to synchronization of the IO loop, the caller will always be alive + // if the class is not aborted. + ProfileSyncServiceHarness* caller_; + + // Informative message to assert in the case of a timeout. + std::string message_; + + DISALLOW_COPY_AND_ASSIGN(StateChangeTimeoutEvent); +}; + +StateChangeTimeoutEvent::StateChangeTimeoutEvent( + ProfileSyncServiceHarness* caller, + const std::string& message) + : aborted_(false), did_timeout_(false), caller_(caller), message_(message) { +} + +StateChangeTimeoutEvent::~StateChangeTimeoutEvent() { +} + +void StateChangeTimeoutEvent::Callback() { + if (!aborted_) { + if (!caller_->RunStateChangeMachine()) { + // Report the message. + did_timeout_ = true; + DCHECK(!aborted_) << message_; + caller_->SignalStateComplete(); + } + } +} + +bool StateChangeTimeoutEvent::Abort() { + aborted_ = true; + caller_ = NULL; + return !did_timeout_; +} + +ProfileSyncServiceHarness::ProfileSyncServiceHarness( + Profile* profile, + const std::string& username, + const std::string& password, + int id) + : wait_state_(INITIAL_WAIT_STATE), + profile_(profile), + service_(NULL), + last_timestamp_(0), + min_timestamp_needed_(kMinTimestampNeededNone), + username_(username), + password_(password), + id_(id) { + if (IsSyncAlreadySetup()) { + service_ = profile_->GetProfileSyncService(); + service_->AddObserver(this); + wait_state_ = FULLY_SYNCED; + } +} + +// static +ProfileSyncServiceHarness* ProfileSyncServiceHarness::CreateAndAttach( + Profile* profile) { + if (!profile->HasProfileSyncService()) { + NOTREACHED() << "Profile has never signed into sync."; + return NULL; + } + return new ProfileSyncServiceHarness(profile, "", "", 0); +} + +void ProfileSyncServiceHarness::SetCredentials(const std::string& username, + const std::string& password) { + username_ = username; + password_ = password; +} + +bool ProfileSyncServiceHarness::IsSyncAlreadySetup() { + return profile_->HasProfileSyncService(); +} + +bool ProfileSyncServiceHarness::SetupSync() { + syncable::ModelTypeSet synced_datatypes; + for (int i = syncable::FIRST_REAL_MODEL_TYPE; + i < syncable::MODEL_TYPE_COUNT; ++i) { + synced_datatypes.insert(syncable::ModelTypeFromInt(i)); + } + return SetupSync(synced_datatypes); +} + +bool ProfileSyncServiceHarness::SetupSync( + const syncable::ModelTypeSet& synced_datatypes) { + // Initialize the sync client's profile sync service object. + service_ = profile_->GetProfileSyncService(); + if (service_ == NULL) { + LOG(ERROR) << "SetupSync(): service_ is null."; + return false; + } + + // Subscribe sync client to notifications from the profile sync service. + if (!service_->HasObserver(this)) + service_->AddObserver(this); + + // Authenticate sync client using GAIA credentials. + service_->signin()->StartSignIn(username_, password_, "", ""); + + // Wait for the OnBackendInitialized() callback. + wait_state_ = WAITING_FOR_ON_BACKEND_INITIALIZED; + if (!AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, + "Waiting for OnBackendInitialized().")) { + LOG(ERROR) << "OnBackendInitialized() not seen after " + << kLiveSyncOperationTimeoutMs / 1000 + << " seconds."; + return false; + } + + // Choose the datatypes to be synced. If all datatypes are to be synced, + // set sync_everything to true; otherwise, set it to false. + bool sync_everything = (synced_datatypes.size() == + (syncable::MODEL_TYPE_COUNT - syncable::FIRST_REAL_MODEL_TYPE)); + service()->OnUserChoseDatatypes(sync_everything, synced_datatypes); + + // Wait for initial sync cycle to complete. + DCHECK_EQ(wait_state_, WAITING_FOR_INITIAL_SYNC); + if (!AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, + "Waiting for initial sync cycle to complete.")) { + LOG(ERROR) << "Initial sync cycle did not complete after " + << kLiveSyncOperationTimeoutMs / 1000 + << " seconds."; + return false; + } + + // Indicate to the browser that sync setup is complete. + service()->SetSyncSetupCompleted(); + + return true; +} + +void ProfileSyncServiceHarness::SignalStateCompleteWithNextState( + WaitState next_state) { + wait_state_ = next_state; + SignalStateComplete(); +} + +void ProfileSyncServiceHarness::SignalStateComplete() { + MessageLoop::current()->Quit(); +} + +bool ProfileSyncServiceHarness::RunStateChangeMachine() { + WaitState original_wait_state = wait_state_; + switch (wait_state_) { + case WAITING_FOR_ON_BACKEND_INITIALIZED: { + LogClientInfo("WAITING_FOR_ON_BACKEND_INITIALIZED"); + if (service()->sync_initialized()) { + // The sync backend is initialized. Start waiting for the first sync + // cycle to complete. + SignalStateCompleteWithNextState(WAITING_FOR_INITIAL_SYNC); + } + break; + } + case WAITING_FOR_INITIAL_SYNC: { + LogClientInfo("WAITING_FOR_INITIAL_SYNC"); + if (IsSynced()) { + // The first sync cycle is now complete. We can start running tests. + SignalStateCompleteWithNextState(FULLY_SYNCED); + } + break; + } + case WAITING_FOR_SYNC_TO_FINISH: { + LogClientInfo("WAITING_FOR_SYNC_TO_FINISH"); + if (!IsSynced()) { + // The client is not yet fully synced. Continue waiting. + if (!GetStatus().server_reachable) { + // The client cannot reach the sync server because the network is + // disabled. There is no need to wait anymore. + SignalStateCompleteWithNextState(SERVER_UNREACHABLE); + } + break; + } + GetUpdatedTimestamp(); + SignalStateCompleteWithNextState(FULLY_SYNCED); + break; + } + case WAITING_FOR_UPDATES: { + LogClientInfo("WAITING_FOR_UPDATES"); + if (!IsSynced() || GetUpdatedTimestamp() < min_timestamp_needed_) { + // The client is not yet fully synced. Continue waiting until the client + // is at the required minimum timestamp. + break; + } + SignalStateCompleteWithNextState(FULLY_SYNCED); + break; + } + case SERVER_UNREACHABLE: { + LogClientInfo("SERVER_UNREACHABLE"); + if (GetStatus().server_reachable) { + // The client was offline due to the network being disabled, but is now + // back online. Wait for the pending sync cycle to complete. + SignalStateCompleteWithNextState(WAITING_FOR_SYNC_TO_FINISH); + } + break; + } + case FULLY_SYNCED: { + // The client is online and fully synced. There is nothing to do. + LogClientInfo("FULLY_SYNCED"); + break; + } + case WAITING_FOR_PASSPHRASE_ACCEPTED: { + LogClientInfo("WAITING_FOR_PASSPHRASE_ACCEPTED"); + if (!service()->observed_passphrase_required()) + SignalStateCompleteWithNextState(FULLY_SYNCED); + break; + } + case SYNC_DISABLED: { + // Syncing is disabled for the client. There is nothing to do. + LogClientInfo("SYNC_DISABLED"); + break; + } + default: + // Invalid state during observer callback which may be triggered by other + // classes using the the UI message loop. Defer to their handling. + break; + } + return original_wait_state != wait_state_; +} + +void ProfileSyncServiceHarness::OnStateChanged() { + RunStateChangeMachine(); +} + +bool ProfileSyncServiceHarness::AwaitPassphraseAccepted() { + LogClientInfo("AwaitPassphraseAccepted"); + if (wait_state_ == SYNC_DISABLED) { + LOG(ERROR) << "Sync disabled for Client " << id_ << "."; + return false; + } + if (!service()->observed_passphrase_required()) + return true; + wait_state_ = WAITING_FOR_PASSPHRASE_ACCEPTED; + return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, + "Waiting for passphrase accepted."); +} + +bool ProfileSyncServiceHarness::AwaitSyncCycleCompletion( + const std::string& reason) { + LogClientInfo("AwaitSyncCycleCompletion"); + if (wait_state_ == SYNC_DISABLED) { + LOG(ERROR) << "Sync disabled for Client " << id_ << "."; + return false; + } + if (!IsSynced()) { + if (wait_state_ == SERVER_UNREACHABLE) { + // Client was offline; wait for it to go online, and then wait for sync. + AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason); + DCHECK_EQ(wait_state_, WAITING_FOR_SYNC_TO_FINISH); + return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason); + } else { + DCHECK(service()->sync_initialized()); + wait_state_ = WAITING_FOR_SYNC_TO_FINISH; + AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason); + if (wait_state_ == FULLY_SYNCED) { + // Client is online; sync was successful. + return true; + } else if (wait_state_ == SERVER_UNREACHABLE){ + // Client is offline; sync was unsuccessful. + return false; + } else { + LOG(ERROR) << "Invalid wait state:" << wait_state_; + return false; + } + } + } else { + // Client is already synced; don't wait. + GetUpdatedTimestamp(); + return true; + } +} + +bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion( + ProfileSyncServiceHarness* partner) { + LogClientInfo("AwaitMutualSyncCycleCompletion"); + if (!AwaitSyncCycleCompletion("Sync cycle completion on active client.")) + return false; + return partner->WaitUntilTimestampIsAtLeast(last_timestamp_, + "Sync cycle completion on passive client."); +} + +bool ProfileSyncServiceHarness::AwaitGroupSyncCycleCompletion( + std::vector<ProfileSyncServiceHarness*>& partners) { + LogClientInfo("AwaitGroupSyncCycleCompletion"); + if (!AwaitSyncCycleCompletion("Sync cycle completion on active client.")) + return false; + bool return_value = true; + for (std::vector<ProfileSyncServiceHarness*>::iterator it = + partners.begin(); it != partners.end(); ++it) { + if ((this != *it) && ((*it)->wait_state_ != SYNC_DISABLED)) { + return_value = return_value && + (*it)->WaitUntilTimestampIsAtLeast(last_timestamp_, + "Sync cycle completion on partner client."); + } + } + return return_value; +} + +// static +bool ProfileSyncServiceHarness::AwaitQuiescence( + std::vector<ProfileSyncServiceHarness*>& clients) { + VLOG(1) << "AwaitQuiescence."; + bool return_value = true; + for (std::vector<ProfileSyncServiceHarness*>::iterator it = + clients.begin(); it != clients.end(); ++it) { + if ((*it)->wait_state_ != SYNC_DISABLED) + return_value = return_value && + (*it)->AwaitGroupSyncCycleCompletion(clients); + } + return return_value; +} + +bool ProfileSyncServiceHarness::WaitUntilTimestampIsAtLeast( + int64 timestamp, const std::string& reason) { + LogClientInfo("WaitUntilTimestampIsAtLeast"); + if (wait_state_ == SYNC_DISABLED) { + LOG(ERROR) << "Sync disabled for Client " << id_ << "."; + return false; + } + min_timestamp_needed_ = timestamp; + if (GetUpdatedTimestamp() < min_timestamp_needed_) { + wait_state_ = WAITING_FOR_UPDATES; + return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason); + } else { + return true; + } +} + +bool ProfileSyncServiceHarness::AwaitStatusChangeWithTimeout( + int timeout_milliseconds, + const std::string& reason) { + LogClientInfo("AwaitStatusChangeWithTimeout"); + if (wait_state_ == SYNC_DISABLED) { + LOG(ERROR) << "Sync disabled for Client " << id_ << "."; + return false; + } + scoped_refptr<StateChangeTimeoutEvent> timeout_signal( + new StateChangeTimeoutEvent(this, reason)); + MessageLoop* loop = MessageLoop::current(); + bool did_allow_nestable_tasks = loop->NestableTasksAllowed(); + loop->SetNestableTasksAllowed(true); + loop->PostDelayedTask( + FROM_HERE, + NewRunnableMethod(timeout_signal.get(), + &StateChangeTimeoutEvent::Callback), + timeout_milliseconds); + loop->Run(); + loop->SetNestableTasksAllowed(did_allow_nestable_tasks); + LogClientInfo("AwaitStatusChangeWithTimeout succeeded"); + return timeout_signal->Abort(); +} + +ProfileSyncService::Status ProfileSyncServiceHarness::GetStatus() { + DCHECK(service() != NULL) << "GetStatus(): service() is NULL."; + return service()->QueryDetailedSyncStatus(); +} + +bool ProfileSyncServiceHarness::IsSynced() { + if (service() == NULL) + return false; + const SyncSessionSnapshot* snap = GetLastSessionSnapshot(); + // TODO(rsimha): Remove additional checks of snap->has_more_to_sync and + // snap->unsynced_count once http://crbug.com/48989 is fixed. + return (snap && + ServiceIsPushingChanges() && + GetStatus().notifications_enabled && + !service()->backend()->HasUnsyncedItems() && + !snap->has_more_to_sync && + snap->unsynced_count == 0); +} + +const SyncSessionSnapshot* + ProfileSyncServiceHarness::GetLastSessionSnapshot() const { + DCHECK(service_ != NULL) << "Sync service has not yet been set up."; + if (service_->backend()) { + return service_->backend()->GetLastSessionSnapshot(); + } + return NULL; +} + +void ProfileSyncServiceHarness::EnableSyncForDatatype( + syncable::ModelType datatype) { + LogClientInfo("EnableSyncForDatatype"); + syncable::ModelTypeSet synced_datatypes; + if (wait_state_ == SYNC_DISABLED) { + wait_state_ = WAITING_FOR_ON_BACKEND_INITIALIZED; + synced_datatypes.insert(datatype); + DCHECK(SetupSync(synced_datatypes)) << "Reinitialization of Client " << id_ + << " failed."; + } else { + DCHECK(service() != NULL) << "EnableSyncForDatatype(): service() is null."; + service()->GetPreferredDataTypes(&synced_datatypes); + syncable::ModelTypeSet::iterator it = synced_datatypes.find( + syncable::ModelTypeFromInt(datatype)); + if (it == synced_datatypes.end()) { + synced_datatypes.insert(syncable::ModelTypeFromInt(datatype)); + service()->OnUserChoseDatatypes(false, synced_datatypes); + wait_state_ = WAITING_FOR_SYNC_TO_FINISH; + AwaitSyncCycleCompletion("Waiting for datatype configuration."); + VLOG(1) << "EnableSyncForDatatype(): Enabled sync for datatype " + << syncable::ModelTypeToString(datatype) << " on Client " << id_; + } else { + VLOG(1) << "EnableSyncForDatatype(): Sync already enabled for datatype " + << syncable::ModelTypeToString(datatype) << " on Client " << id_; + } + } +} + +void ProfileSyncServiceHarness::DisableSyncForDatatype( + syncable::ModelType datatype) { + LogClientInfo("DisableSyncForDatatype"); + syncable::ModelTypeSet synced_datatypes; + DCHECK(service() != NULL) << "DisableSyncForDatatype(): service() is null."; + service()->GetPreferredDataTypes(&synced_datatypes); + syncable::ModelTypeSet::iterator it = synced_datatypes.find(datatype); + if (it != synced_datatypes.end()) { + synced_datatypes.erase(it); + service()->OnUserChoseDatatypes(false, synced_datatypes); + AwaitSyncCycleCompletion("Waiting for datatype configuration."); + VLOG(1) << "DisableSyncForDatatype(): Disabled sync for datatype " + << syncable::ModelTypeToString(datatype) << " on Client " << id_; + } else { + VLOG(1) << "DisableSyncForDatatype(): Sync already disabled for datatype " + << syncable::ModelTypeToString(datatype) << " on Client " << id_; + } +} + +void ProfileSyncServiceHarness::EnableSyncForAllDatatypes() { + LogClientInfo("EnableSyncForAllDatatypes"); + if (wait_state_ == SYNC_DISABLED) { + wait_state_ = WAITING_FOR_ON_BACKEND_INITIALIZED; + DCHECK(SetupSync()) << "Reinitialization of Client " << id_ << " failed."; + } else { + syncable::ModelTypeSet synced_datatypes; + for (int i = syncable::FIRST_REAL_MODEL_TYPE; + i < syncable::MODEL_TYPE_COUNT; ++i) { + synced_datatypes.insert(syncable::ModelTypeFromInt(i)); + } + DCHECK(service() != NULL) << "EnableSyncForAllDatatypes(): service() is " + " null."; + service()->OnUserChoseDatatypes(true, synced_datatypes); + wait_state_ = WAITING_FOR_SYNC_TO_FINISH; + AwaitSyncCycleCompletion("Waiting for datatype configuration."); + VLOG(1) << "EnableSyncForAllDatatypes(): Enabled sync for all datatypes on " + "Client " << id_; + } +} + +void ProfileSyncServiceHarness::DisableSyncForAllDatatypes() { + LogClientInfo("DisableSyncForAllDatatypes"); + DCHECK(service() != NULL) << "EnableSyncForAllDatatypes(): service() is " + "null."; + service()->DisableForUser(); + wait_state_ = SYNC_DISABLED; + VLOG(1) << "DisableSyncForAllDatatypes(): Disabled sync for all datatypes on " + "Client " << id_; +} + +int64 ProfileSyncServiceHarness::GetUpdatedTimestamp() { + const SyncSessionSnapshot* snap = GetLastSessionSnapshot(); + DCHECK(snap != NULL) << "GetUpdatedTimestamp(): Sync snapshot is NULL."; + DCHECK_LE(last_timestamp_, snap->max_local_timestamp); + last_timestamp_ = snap->max_local_timestamp; + return last_timestamp_; +} + +void ProfileSyncServiceHarness::LogClientInfo(std::string message) { + if (service()) { + const SyncSessionSnapshot* snap = GetLastSessionSnapshot(); + if (snap) { + VLOG(1) << "Client " << id_ << ": " << message + << ": max_local_timestamp: " << snap->max_local_timestamp + << ", has_more_to_sync: " << snap->has_more_to_sync + << ", unsynced_count: " << snap->unsynced_count + << ", has_unsynced_items: " + << service()->backend()->HasUnsyncedItems() + << ", notifications_enabled: " + << GetStatus().notifications_enabled + << ", service_is_pushing_changes: " << ServiceIsPushingChanges(); + } else { + VLOG(1) << "Client " << id_ << ": " << message + << ": Sync session snapshot not available."; + } + } else { + VLOG(1) << "Client " << id_ << ": " << message + << ": Sync service not available."; + } +} diff --git a/chrome/browser/sync/profile_sync_service_harness.h b/chrome/browser/sync/profile_sync_service_harness.h new file mode 100644 index 0000000..a50bb8a --- /dev/null +++ b/chrome/browser/sync/profile_sync_service_harness.h @@ -0,0 +1,200 @@ +// 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_SYNC_PROFILE_SYNC_SERVICE_HARNESS_H_ +#define CHROME_BROWSER_SYNC_PROFILE_SYNC_SERVICE_HARNESS_H_ +#pragma once + +#include <string> +#include <vector> + +#include "base/time.h" +#include "chrome/browser/sync/profile_sync_service.h" + +using browser_sync::sessions::SyncSessionSnapshot; + +class Profile; + +// An instance of this class is basically our notion of a "sync client" for +// automation purposes. It harnesses the ProfileSyncService member of the +// profile passed to it on construction and automates certain things like setup +// and authentication. It provides ways to "wait" adequate periods of time for +// several clients to get to the same state. +class ProfileSyncServiceHarness : public ProfileSyncServiceObserver { + public: + ProfileSyncServiceHarness(Profile* profile, + const std::string& username, + const std::string& password, + int id); + + virtual ~ProfileSyncServiceHarness() {} + + // Creates a ProfileSyncServiceHarness object and attaches it to |profile|, a + // profile that is assumed to have been signed into sync in the past. Caller + // takes ownership. + static ProfileSyncServiceHarness* CreateAndAttach(Profile* profile); + + // Sets the GAIA credentials with which to sign in to sync. + void SetCredentials(const std::string& username, const std::string& password); + + // Returns true if sync has been enabled on |profile_|. + bool IsSyncAlreadySetup(); + + // Creates a ProfileSyncService for the profile passed at construction and + // enables sync for all available datatypes. Returns true only after sync has + // been fully initialized and authenticated, and we are ready to process + // changes. + bool SetupSync(); + + // Same as the above method, but enables sync only for the datatypes contained + // in |synced_datatypes|. + bool SetupSync(const syncable::ModelTypeSet& synced_datatypes); + + // ProfileSyncServiceObserver implementation. + virtual void OnStateChanged(); + + // Blocks the caller until this harness has completed a single sync cycle + // since the previous one. Returns true if a sync cycle has completed. + bool AwaitSyncCycleCompletion(const std::string& reason); + + // Blocks the caller until this harness has observed that the sync engine + // has "synced" up to at least the specified local timestamp. + bool WaitUntilTimestampIsAtLeast(int64 timestamp, const std::string& reason); + + // Calling this acts as a barrier and blocks the caller until |this| and + // |partner| have both completed a sync cycle. When calling this method, + // the |partner| should be the passive responder who responds to the actions + // of |this|. This method relies upon the synchronization of callbacks + // from the message queue. Returns true if two sync cycles have completed. + // Note: Use this method when exactly one client makes local change(s), and + // exactly one client is waiting to receive those changes. + bool AwaitMutualSyncCycleCompletion(ProfileSyncServiceHarness* partner); + + // Blocks the caller until |this| completes its ongoing sync cycle and every + // other client in |partners| has a timestamp that is greater than or equal to + // the timestamp of |this|. Note: Use this method when exactly one client + // makes local change(s), and more than one client is waiting to receive those + // changes. + bool AwaitGroupSyncCycleCompletion( + std::vector<ProfileSyncServiceHarness*>& partners); + + // Blocks the caller until every client in |clients| completes its ongoing + // sync cycle and all the clients' timestamps match. Note: Use this method + // when more than one client makes local change(s), and more than one client + // is waiting to receive those changes. + static bool AwaitQuiescence( + std::vector<ProfileSyncServiceHarness*>& clients); + + // If a SetPassphrase call has been issued with a valid passphrase, this + // will wait until the Cryptographer broadcasts SYNC_PASSPHRASE_ACCEPTED. + bool AwaitPassphraseAccepted(); + + // Returns the ProfileSyncService member of the the sync client. + ProfileSyncService* service() { return service_; } + + // Returns the status of the ProfileSyncService member of the the sync client. + ProfileSyncService::Status GetStatus(); + + // See ProfileSyncService::ShouldPushChanges(). + bool ServiceIsPushingChanges() { return service_->ShouldPushChanges(); } + + // Enables sync for a particular sync datatype. + void EnableSyncForDatatype(syncable::ModelType datatype); + + // Disables sync for a particular sync datatype. + void DisableSyncForDatatype(syncable::ModelType datatype); + + // Enables sync for all sync datatypes. + void EnableSyncForAllDatatypes(); + + // Disables sync for all sync datatypes. + void DisableSyncForAllDatatypes(); + + // Returns a snapshot of the current sync session. + const SyncSessionSnapshot* GetLastSessionSnapshot() const; + + private: + friend class StateChangeTimeoutEvent; + + enum WaitState { + // The sync client has just been initialized. + INITIAL_WAIT_STATE = 0, + + // The sync client awaits the OnBackendInitialized() callback. + WAITING_FOR_ON_BACKEND_INITIALIZED, + + // The sync client is waiting for the first sync cycle to complete. + WAITING_FOR_INITIAL_SYNC, + + // The sync client is waiting for an ongoing sync cycle to complete. + WAITING_FOR_SYNC_TO_FINISH, + + // The sync client anticipates incoming updates leading to a new sync cycle. + WAITING_FOR_UPDATES, + + // The sync client cannot reach the server. + SERVER_UNREACHABLE, + + // The sync client is fully synced and there are no pending updates. + FULLY_SYNCED, + + // Waiting for a set passphrase to be accepted by the cryptographer. + WAITING_FOR_PASSPHRASE_ACCEPTED, + + // Syncing is disabled for the client. + SYNC_DISABLED, + + NUMBER_OF_STATES, + }; + + // Called from the observer when the current wait state has been completed. + void SignalStateCompleteWithNextState(WaitState next_state); + + // Indicates that the operation being waited on is complete. + void SignalStateComplete(); + + // Finite state machine for controlling state. Returns true only if a state + // change has taken place. + bool RunStateChangeMachine(); + + // Returns true if a status change took place, false on timeout. + bool AwaitStatusChangeWithTimeout(int timeout_milliseconds, + const std::string& reason); + + // Returns true if the sync client has no unsynced items. + bool IsSynced(); + + // Logs message with relevant info about client's sync state (if available). + void LogClientInfo(std::string message); + + // Updates |last_timestamp_| with the timestamp of the current sync session. + // Returns the new value of |last_timestamp_|. + int64 GetUpdatedTimestamp(); + + WaitState wait_state_; + + Profile* profile_; + ProfileSyncService* service_; + + // This value tracks the max sync timestamp (e.g. synced-to revision) inside + // the sync engine. It gets updated when a sync cycle ends and the session + // snapshot implies syncing is "done". + int64 last_timestamp_; + + // The minimum value of the 'max_local_timestamp' member of a + // SyncSessionSnapshot we need to wait to observe in OnStateChanged when told + // to WaitUntilTimestampIsAtLeast(...). + int64 min_timestamp_needed_; + + // Credentials used for GAIA authentication. + std::string username_; + std::string password_; + + // Client ID, used for logging purposes. + int id_; + + DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceHarness); +}; + +#endif // CHROME_BROWSER_SYNC_PROFILE_SYNC_SERVICE_HARNESS_H_ diff --git a/chrome/browser/sync/profile_sync_service_password_unittest.cc b/chrome/browser/sync/profile_sync_service_password_unittest.cc index fede8a5..43ee1fb 100644 --- a/chrome/browser/sync/profile_sync_service_password_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_password_unittest.cc @@ -185,20 +185,22 @@ class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest { service_->Initialize(); MessageLoop::current()->Run(); - - EXPECT_CALL(observer_, - Observe( - NotificationType(NotificationType::SYNC_PASSPHRASE_ACCEPTED), - _,_)). - WillOnce(InvokeTask(node_task)); - EXPECT_CALL(observer_, - Observe( - NotificationType(NotificationType::SYNC_CONFIGURE_DONE), - _,_)). - WillOnce(QuitUIMessageLoop()); - service_->SetPassphrase("foo"); - MessageLoop::current()->Run(); - + // Only set the passphrase if we actually created the password and nigori + // root nodes. + if (root_task) { + EXPECT_CALL(observer_, + Observe( + NotificationType(NotificationType::SYNC_PASSPHRASE_ACCEPTED), + _,_)). + WillOnce(InvokeTask(node_task)); + EXPECT_CALL(observer_, + Observe( + NotificationType(NotificationType::SYNC_CONFIGURE_DONE), + _,_)). + WillOnce(QuitUIMessageLoop()); + service_->SetPassphrase("foo"); + MessageLoop::current()->Run(); + } } } @@ -268,6 +270,22 @@ class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest { scoped_refptr<MockPasswordStore> password_store_; NotificationRegistrar registrar_; + TestIdFactory ids_; +}; + +class CreatePasswordRootTask : public Task { + public: + explicit CreatePasswordRootTask(AbstractProfileSyncServiceTest* test) + : test_(test) { + } + + virtual void Run() { + test_->CreateRoot(syncable::NIGORI); + test_->CreateRoot(syncable::PASSWORDS); + } + + private: + AbstractProfileSyncServiceTest* test_; }; class AddPasswordEntriesTask : public Task { @@ -289,7 +307,10 @@ class AddPasswordEntriesTask : public Task { }; TEST_F(ProfileSyncServicePasswordTest, FailModelAssociation) { - StartSyncService(NULL, NULL, 1, 2); + // Create the nigori root node so that password model association is + // attempted, but not the password root node so that it fails. + CreateRootTask task(this, syncable::NIGORI); + StartSyncService(&task, NULL, 1, 2); EXPECT_TRUE(service_->unrecoverable_error_detected()); } @@ -299,7 +320,7 @@ TEST_F(ProfileSyncServicePasswordTest, EmptyNativeEmptySync) { EXPECT_CALL(*password_store_, FillBlacklistLogins(_)) .WillOnce(Return(true)); SetIdleChangeProcessorExpectations(); - CreateRootTask task(this, syncable::PASSWORDS); + CreatePasswordRootTask task(this); StartSyncService(&task, NULL); std::vector<PasswordForm> sync_entries; GetPasswordEntriesFromSyncDB(&sync_entries); @@ -329,7 +350,7 @@ TEST_F(ProfileSyncServicePasswordTest, HasNativeEntriesEmptySync) { EXPECT_CALL(*password_store_, FillBlacklistLogins(_)) .WillOnce(Return(true)); SetIdleChangeProcessorExpectations(); - CreateRootTask task(this, syncable::PASSWORDS); + CreatePasswordRootTask task(this); StartSyncService(&task, NULL); std::vector<PasswordForm> sync_forms; GetPasswordEntriesFromSyncDB(&sync_forms); @@ -381,7 +402,7 @@ TEST_F(ProfileSyncServicePasswordTest, HasNativeEntriesEmptySyncSameUsername) { EXPECT_CALL(*password_store_, FillBlacklistLogins(_)) .WillOnce(Return(true)); SetIdleChangeProcessorExpectations(); - CreateRootTask task(this, syncable::PASSWORDS); + CreatePasswordRootTask task(this); StartSyncService(&task, NULL); std::vector<PasswordForm> sync_forms; GetPasswordEntriesFromSyncDB(&sync_forms); @@ -436,7 +457,7 @@ TEST_F(ProfileSyncServicePasswordTest, HasNativeHasSyncNoMerge) { EXPECT_CALL(*password_store_, FillBlacklistLogins(_)).WillOnce(Return(true)); EXPECT_CALL(*password_store_, AddLoginImpl(_)).Times(1); - CreateRootTask root_task(this, syncable::PASSWORDS); + CreatePasswordRootTask root_task(this); AddPasswordEntriesTask node_task(this, sync_forms); StartSyncService(&root_task, &node_task); @@ -509,7 +530,7 @@ TEST_F(ProfileSyncServicePasswordTest, HasNativeHasSyncMergeEntry) { EXPECT_CALL(*password_store_, FillBlacklistLogins(_)).WillOnce(Return(true)); EXPECT_CALL(*password_store_, UpdateLoginImpl(_)).Times(1); - CreateRootTask root_task(this, syncable::PASSWORDS); + CreatePasswordRootTask root_task(this); AddPasswordEntriesTask node_task(this, sync_forms); StartSyncService(&root_task, &node_task); diff --git a/chrome/browser/sync/profile_sync_service_session_unittest.cc b/chrome/browser/sync/profile_sync_service_session_unittest.cc index 77790e4..6242f0e 100644 --- a/chrome/browser/sync/profile_sync_service_session_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_session_unittest.cc @@ -60,7 +60,7 @@ class ProfileSyncServiceSessionTest ProfileSyncService* sync_service() { return sync_service_.get(); } - TestIdFactory* ids() { return sync_service_->id_factory(); } + TestIdFactory* ids() { return &ids_; } protected: SessionService* service() { return helper_.service(); } @@ -134,6 +134,7 @@ class ProfileSyncServiceSessionTest SessionID window_id_; ProfileSyncFactoryMock factory_; scoped_ptr<TestProfileSyncService> sync_service_; + TestIdFactory ids_; const gfx::Rect window_bounds_; bool notified_of_update_; NotificationRegistrar registrar_; @@ -314,7 +315,7 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNode) { ASSERT_EQ(1U, foreign_sessions[0]->windows.size()); ASSERT_EQ(1U, foreign_sessions[0]->windows[0]->tabs.size()); ASSERT_EQ(1U, foreign_sessions[0]->windows[0]->tabs[0]->navigations.size()); - ASSERT_EQ(foreign_sessions[0]->foreign_tession_tag, machine_tag); + ASSERT_EQ(foreign_sessions[0]->foreign_session_tag, machine_tag); ASSERT_EQ(1, foreign_sessions[0]->windows[0]->selected_tab_index); ASSERT_EQ(1, foreign_sessions[0]->windows[0]->type); ASSERT_EQ(13, foreign_sessions[0]->windows[0]->tabs[0]->tab_visual_index); diff --git a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc index 0224291..17795c6 100644 --- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc @@ -279,6 +279,8 @@ class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest { ProfileSyncFactoryMock factory_; scoped_refptr<HistoryBackendMock> history_backend_; scoped_refptr<HistoryServiceMock> history_service_; + + TestIdFactory ids_; }; class AddTypedUrlEntriesTask : public Task { @@ -427,7 +429,7 @@ TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAdd) { history::URLsModifiedDetails details; details.changed_urls.push_back(added_entry); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&history_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_)); notifier->Notify(NotificationType::HISTORY_TYPED_URLS_MODIFIED, Details<history::URLsModifiedDetails>(&details)); @@ -460,7 +462,7 @@ TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdate) { history::URLsModifiedDetails details; details.changed_urls.push_back(updated_entry); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&history_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_)); notifier->Notify(NotificationType::HISTORY_TYPED_URLS_MODIFIED, Details<history::URLsModifiedDetails>(&details)); @@ -495,7 +497,7 @@ TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemove) { history::URLsDeletedDetails changes; changes.all_history = false; changes.urls.insert(GURL("http://mine.com")); - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&history_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_)); notifier->Notify(NotificationType::HISTORY_URLS_DELETED, Details<history::URLsDeletedDetails>(&changes)); @@ -529,7 +531,7 @@ TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveAll) { history::URLsDeletedDetails changes; changes.all_history = true; - scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&history_thread_); + scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_)); notifier->Notify(NotificationType::HISTORY_URLS_DELETED, Details<history::URLsDeletedDetails>(&changes)); diff --git a/chrome/browser/sync/profile_sync_service_unittest.cc b/chrome/browser/sync/profile_sync_service_unittest.cc index 2db71ad..0088829 100644 --- a/chrome/browser/sync/profile_sync_service_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_unittest.cc @@ -35,6 +35,7 @@ #include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/pref_names.h" #include "chrome/test/testing_profile.h" +#include "chrome/test/testing_pref_service.h" #include "testing/gmock/include/gmock/gmock.h" using std::vector; @@ -54,10 +55,10 @@ using testing::Invoke; class TestBookmarkModelAssociator : public BookmarkModelAssociator { public: - TestBookmarkModelAssociator(TestProfileSyncService* service, + TestBookmarkModelAssociator(ProfileSyncService* service, UnrecoverableErrorHandler* persist_ids_error_handler) : BookmarkModelAssociator(service, persist_ids_error_handler), - helper_(new TestModelAssociatorHelper(service->id_factory())) { + helper_(new TestModelAssociatorHelper()) { } virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id) { return helper_->GetSyncIdForTaggedNode(this, tag, sync_id); @@ -470,6 +471,16 @@ TEST_F(ProfileSyncServiceTest, InitialState) { ExpectModelMatch(); } +TEST_F(ProfileSyncServiceTest, DisabledByPolicy) { + profile_->GetTestingPrefService()->SetManagedPref( + prefs::kSyncManaged, + Value::CreateBooleanValue(true)); + service_.reset(new TestProfileSyncService(&factory_, profile_.get(), + "", true, NULL)); + service_->Initialize(); + EXPECT_TRUE(service_->IsManaged()); +} + TEST_F(ProfileSyncServiceTest, AbortedByShutdown) { service_.reset(new TestProfileSyncService(&factory_, profile_.get(), "test", true, NULL)); diff --git a/chrome/browser/sync/profile_sync_test_util.h b/chrome/browser/sync/profile_sync_test_util.h index 6e9b77a..aa8584e 100644 --- a/chrome/browser/sync/profile_sync_test_util.h +++ b/chrome/browser/sync/profile_sync_test_util.h @@ -19,13 +19,11 @@ #include "chrome/browser/browser_thread.h" #include "chrome/browser/profile.h" #include "chrome/browser/webdata/web_database.h" -#include "chrome/browser/sync/abstract_profile_sync_service_test.h" #include "chrome/browser/sync/glue/bookmark_change_processor.h" #include "chrome/browser/sync/glue/bookmark_data_type_controller.h" #include "chrome/browser/sync/glue/bookmark_model_associator.h" #include "chrome/browser/sync/glue/change_processor.h" #include "chrome/browser/sync/glue/data_type_manager_impl.h" -#include "chrome/browser/sync/glue/sync_backend_host.h" #include "chrome/browser/sync/profile_sync_factory.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/unrecoverable_error_handler.h" @@ -54,8 +52,6 @@ ACTION_P(InvokeTask, task) { class TestModelAssociatorHelper { public: - TestModelAssociatorHelper(browser_sync::TestIdFactory* id_factory) - : id_factory_(id_factory) {} template <class ModelAssociatorImpl> bool GetSyncIdForTaggedNode(ModelAssociatorImpl* associator, const std::string& tag, int64* sync_id) { @@ -65,32 +61,10 @@ class TestModelAssociatorHelper { return false; } - browser_sync::SyncBackendHost::UserShareHandle share( + sync_api::WriteTransaction trans( associator->sync_service()->backend()->GetUserShareHandle()); - bool root_exists = false; - ModelType type = ModelAssociatorImpl::model_type(); - { - sync_api::WriteTransaction trans(share); - sync_api::ReadNode uber_root(&trans); - uber_root.InitByRootLookup(); - - // Look up the top level data type node. - sync_api::ReadNode root_lookup(&trans); - root_exists = root_lookup.InitByTagLookup( - ProfileSyncServiceTestHelper::GetTagForType(type)); - } - - if (!root_exists) { - bool created = ProfileSyncServiceTestHelper::CreateRoot(type, - associator->sync_service(), id_factory_); - if (!created) - return false; - } - - sync_api::WriteTransaction trans(share); sync_api::ReadNode root(&trans); - EXPECT_TRUE(root.InitByTagLookup( - ProfileSyncServiceTestHelper::GetTagForType(type))); + root.InitByRootLookup(); // First, try to find a node with the title among the root's children. // This will be the case if we are testing model persistence, and @@ -124,8 +98,6 @@ class TestModelAssociatorHelper { } ~TestModelAssociatorHelper() {} - private: - browser_sync::TestIdFactory* id_factory_; }; class ProfileSyncServiceObserverMock : public ProfileSyncServiceObserver { diff --git a/chrome/browser/sync/protocol/sync.proto b/chrome/browser/sync/protocol/sync.proto index 40e9906..1ada951 100644 --- a/chrome/browser/sync/protocol/sync.proto +++ b/chrome/browser/sync/protocol/sync.proto @@ -363,7 +363,7 @@ message ClearUserDataResponse { message ClientToServerMessage { required string share = 1; - optional int32 protocol_version = 2 [default = 24]; + optional int32 protocol_version = 2 [default = 25]; enum Contents { COMMIT = 1; GET_UPDATES = 2; @@ -512,21 +512,34 @@ message ClientToServerResponse { enum ErrorType { SUCCESS = 0; - ACCESS_DENIED = 1; // Returned when the user doesn't have access to - // store (instead of HTTP 401). - NOT_MY_BIRTHDAY = 2; // Returned when the server and client disagree on - // the store birthday. - THROTTLED = 3; // Returned when the store has exceeded the allowed - // bandwidth utilization. - AUTH_EXPIRED = 4; // Auth token or cookie has expired. - USER_NOT_ACTIVATED = 5; // User doesn't have the Chrome bit set on that - // Google Account. - AUTH_INVALID = 6; // Auth token or cookie is otherwise invalid. - CLEAR_PENDING = 7; // A clear of the user data is pending (e.g. - // initiated by privacy request). Client should - // come back later. + ACCESS_DENIED = 1; // Returned when the user doesn't have access to + // store (instead of HTTP 401). + NOT_MY_BIRTHDAY = 2; // Returned when the server and client disagree on + // the store birthday. + THROTTLED = 3; // Returned when the store has exceeded the + // allowed bandwidth utilization. + AUTH_EXPIRED = 4; // Auth token or cookie has expired. + USER_NOT_ACTIVATED = 5; // User doesn't have the Chrome bit set on that + // Google Account. + AUTH_INVALID = 6; // Auth token or cookie is otherwise invalid. + CLEAR_PENDING = 7; // A clear of the user data is pending (e.g. + // initiated by privacy request). Client should + // come back later. + TRANSIENT_ERROR = 8; // A transient error occured (eg. backend + // timeout). Client should try again later. + UNKNOWN = 100; // Unknown value. This should never be explicitly + // used; it is the default value when an + // out-of-date client parses a value it doesn't + // recognize. } - optional ErrorType error_code = 4 [default = SUCCESS]; + // Up until protocol_version 24, the default was SUCCESS which made it + // impossible to add new enum values since older clients would parse any + // out-of-range value as SUCCESS. Starting with 25, unless explicitly set, + // the error_code will be UNKNOWN so that clients know when they're + // out-of-date. Note also that when using protocol_version < 25, + // TRANSIENT_ERROR is not supported. Instead, the server sends back a HTTP + // 400 error code. + optional ErrorType error_code = 4 [default = UNKNOWN]; optional string error_message = 5; // Opaque store ID; if it changes, the contents of the client's cache diff --git a/chrome/browser/sync/resources/configure.html b/chrome/browser/sync/resources/configure.html index 0ba65c4..ebab766 100644 --- a/chrome/browser/sync/resources/configure.html +++ b/chrome/browser/sync/resources/configure.html @@ -187,18 +187,22 @@ html[os='mac'] input[type='submit'] { setChooseDataTypesCheckboxes(args); setEncryptionCheckboxes(args); setErrorState(args); - }
-
- function checkAllDataTypeCheckboxes() {
- var checkboxes = document.getElementsByName("dataTypeCheckbox");
- for (var i = 0; i < checkboxes.length; i++) {
- checkboxes[i].checked = true;
- }
- }
-
- function setCheckboxesToKeepEverythingSynced(value) {
- setDataTypeCheckboxesEnabled(!value);
- if (value)
+ } + + function checkAllDataTypeCheckboxes() { + var checkboxes = document.getElementsByName("dataTypeCheckbox"); + for (var i = 0; i < checkboxes.length; i++) { + // Only check the visible ones (since there's no way to uncheck + // the invisible ones). + if (checkboxes[i].parentElement.className == "sync-item-show") { + checkboxes[i].checked = true; + } + } + } + + function setCheckboxesToKeepEverythingSynced(value) { + setDataTypeCheckboxesEnabled(!value); + if (value) checkAllDataTypeCheckboxes(); } @@ -302,7 +306,8 @@ html[os='mac'] input[type='submit'] { var atLeastOneChecked = false; var atLeastOneEnabled = false; for (var i = 0; i < checkboxes.length; i++) { - if (!checkboxes[i].disabled && checkboxes[i].style.display != 'none') { + if (!checkboxes[i].disabled && + checkboxes[i].parentElement.className == "sync-item-show") { atLeastOneEnabled = true; if (checkboxes[i].checked) { atLeastOneChecked = true; diff --git a/chrome/browser/sync/resources/gaia_login.html b/chrome/browser/sync/resources/gaia_login.html index a4f7bb2..bd19b34 100644 --- a/chrome/browser/sync/resources/gaia_login.html +++ b/chrome/browser/sync/resources/gaia_login.html @@ -311,6 +311,10 @@ if (d) d.style.display = display; } + + function hideBlurb() { + setElementDisplay('top_blurb', 'none'); + } function setBlurbError() { if (g_is_captcha_challenge_active) diff --git a/chrome/browser/sync/signin_manager.cc b/chrome/browser/sync/signin_manager.cc index fb83337..97b15a0 100644 --- a/chrome/browser/sync/signin_manager.cc +++ b/chrome/browser/sync/signin_manager.cc @@ -83,6 +83,9 @@ void SigninManager::ProvideSecondFactorAccessCode( } void SigninManager::SignOut() { + if (!profile_) + return; + client_login_.reset(); last_result_ = ClientLoginResult(); username_.clear(); @@ -111,7 +114,7 @@ void SigninManager::OnGetUserInfoSuccess(const std::string& key, GoogleServiceSigninSuccessDetails details(username_, password_); NotificationService::current()->Notify( NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, - Source<SigninManager>(this), + Source<Profile>(profile_), Details<const GoogleServiceSigninSuccessDetails>(&details)); password_.clear(); // Don't need it anymore. @@ -137,7 +140,7 @@ void SigninManager::OnGetUserInfoFailure(const GoogleServiceAuthError& error) { void SigninManager::OnClientLoginFailure(const GoogleServiceAuthError& error) { NotificationService::current()->Notify( NotificationType::GOOGLE_SIGNIN_FAILED, - Source<SigninManager>(this), + Source<Profile>(profile_), Details<const GoogleServiceAuthError>(&error)); // We don't sign-out if the password was valid and we're just dealing with diff --git a/chrome/browser/sync/signin_manager_unittest.cc b/chrome/browser/sync/signin_manager_unittest.cc index 5659533..e2dc8e3 100644 --- a/chrome/browser/sync/signin_manager_unittest.cc +++ b/chrome/browser/sync/signin_manager_unittest.cc @@ -22,9 +22,9 @@ class SigninManagerTest : public TokenServiceTestHarness { TokenServiceTestHarness::SetUp(); manager_.reset(new SigninManager()); google_login_success_.ListenFor(NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, - Source<SigninManager>(manager_.get())); + Source<Profile>(profile_.get())); google_login_failure_.ListenFor(NotificationType::GOOGLE_SIGNIN_FAILED, - Source<SigninManager>(manager_.get())); + Source<Profile>(profile_.get())); URLFetcher::set_factory(&factory_); } diff --git a/chrome/browser/sync/sync_setup_flow.cc b/chrome/browser/sync/sync_setup_flow.cc index 1dbd60c..08dcfa1 100644 --- a/chrome/browser/sync/sync_setup_flow.cc +++ b/chrome/browser/sync/sync_setup_flow.cc @@ -14,6 +14,9 @@ #include "base/values.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/login/user_manager.h" +#endif #if defined(OS_MACOSX) #include "chrome/browser/cocoa/html_dialog_window_controller_cppsafe.h" #endif @@ -393,6 +396,13 @@ void SyncSetupFlow::GetArgsForGaiaLogin(const ProfileSyncService* service, args->SetBoolean("editable_user", true); } else { string16 user(service->GetAuthenticatedUsername()); +#if defined(OS_CHROMEOS) + if (user.empty()) { + std::string email = + chromeos::UserManager::Get()->logged_in_user().email(); + user = UTF8ToUTF16(email); + } +#endif args->SetString("user", user); args->SetInteger("error", 0); args->SetBoolean("editable_user", user.empty()); diff --git a/chrome/browser/sync/sync_setup_flow.h b/chrome/browser/sync/sync_setup_flow.h index 2879146..4b4b034 100644 --- a/chrome/browser/sync/sync_setup_flow.h +++ b/chrome/browser/sync/sync_setup_flow.h @@ -106,6 +106,7 @@ class SyncSetupFlow : public HtmlDialogUIDelegate { virtual bool IsDialogModal() const { return false; } + virtual bool ShouldShowDialogTitle() const { return true; } void OnUserSubmittedAuth(const std::string& username, const std::string& password, diff --git a/chrome/browser/sync/sync_ui_util_mac_unittest.mm b/chrome/browser/sync/sync_ui_util_mac_unittest.mm index 14208c7..96f72e7 100644 --- a/chrome/browser/sync/sync_ui_util_mac_unittest.mm +++ b/chrome/browser/sync/sync_ui_util_mac_unittest.mm @@ -8,7 +8,7 @@ #include "app/l10n_util_mac.h" #include "base/scoped_nsobject.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/cocoa/cocoa_test_helper.h" #include "grit/generated_resources.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chrome/browser/sync/syncable/model_type.cc b/chrome/browser/sync/syncable/model_type.cc index 5a06be8..2d17ebc 100644 --- a/chrome/browser/sync/syncable/model_type.cc +++ b/chrome/browser/sync/syncable/model_type.cc @@ -150,6 +150,33 @@ std::string ModelTypeToString(ModelType model_type) { } } +ModelType ModelTypeFromString(const std::string& model_type_string) { + if (model_type_string == "Bookmarks") + return BOOKMARKS; + else if (model_type_string == "Preferences") + return PREFERENCES; + else if (model_type_string == "Passwords") + return PASSWORDS; + else if (model_type_string == "Autofill") + return AUTOFILL; + else if (model_type_string == "Themes") + return THEMES; + else if (model_type_string == "Typed URLs") + return TYPED_URLS; + else if (model_type_string == "Extensions") + return EXTENSIONS; + else if (model_type_string == "Encryption keys") + return NIGORI; + else if (model_type_string == "Sessions") + return SESSIONS; + else if (model_type_string == "Apps") + return APPS; + else + NOTREACHED() << "No known model type corresponding to " + << model_type_string << "."; + return UNSPECIFIED; +} + // TODO(akalin): Figure out a better way to do these mappings. namespace { @@ -247,8 +274,7 @@ bool NotificationTypeToRealModelType(const std::string& notification_type, } else if (notification_type == kSessionNotificationType) { *model_type = SESSIONS; return true; - } - else if (notification_type == kUnknownNotificationType) { + } else if (notification_type == kUnknownNotificationType) { // TODO(akalin): This is a hack to make new sync data types work with // server-issued notifications. Remove this when it's not needed // anymore. diff --git a/chrome/browser/sync/syncable/model_type.h b/chrome/browser/sync/syncable/model_type.h index 077d243..c619b7e 100644 --- a/chrome/browser/sync/syncable/model_type.h +++ b/chrome/browser/sync/syncable/model_type.h @@ -89,8 +89,12 @@ ModelType GetModelType(const sync_pb::SyncEntity& sync_entity); // prefer using GetModelType where possible. ModelType GetModelTypeFromSpecifics(const sync_pb::EntitySpecifics& specifics); +// Returns a string that represents the name of |model_type|. std::string ModelTypeToString(ModelType model_type); +// Returns the ModelType corresponding to the name |model_type_string|. +ModelType ModelTypeFromString(const std::string& model_type_string); + // Convert a real model type to a notification type (used for // subscribing to server-issued notifications). Returns true iff // |model_type| was a real model type and |notification_type| was diff --git a/chrome/browser/sync/test_profile_sync_service.cc b/chrome/browser/sync/test_profile_sync_service.cc deleted file mode 100644 index 1ef298f..0000000 --- a/chrome/browser/sync/test_profile_sync_service.cc +++ /dev/null @@ -1,181 +0,0 @@ -// 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/sync/test_profile_sync_service.h" -#include "chrome/browser/sync/abstract_profile_sync_service_test.h" - -namespace browser_sync { - -SyncBackendHostForProfileSyncTest::SyncBackendHostForProfileSyncTest( - TestProfileSyncService* service, - Profile* profile, - const FilePath& profile_path, - const DataTypeController::TypeMap& data_type_controllers, - Task* initial_condition_setup_task, - int num_expected_resumes, - int num_expected_pauses, - bool set_initial_sync_ended_on_init, - bool synchronous_init) - : browser_sync::SyncBackendHost(service, profile, profile_path, - data_type_controllers), - initial_condition_setup_task_(initial_condition_setup_task), - set_initial_sync_ended_on_init_(set_initial_sync_ended_on_init), - synchronous_init_(synchronous_init), - test_service_(service) { - // By default, the RequestPause and RequestResume methods will - // send the confirmation notification and return true. - ON_CALL(*this, RequestPause()). - WillByDefault(testing::DoAll(CallOnPaused(core_), - testing::Return(true))); - ON_CALL(*this, RequestResume()). - WillByDefault(testing::DoAll(CallOnResumed(core_), - testing::Return(true))); - ON_CALL(*this, RequestNudge()).WillByDefault(testing::Invoke(this, - &SyncBackendHostForProfileSyncTest:: - SimulateSyncCycleCompletedInitialSyncEnded)); - - EXPECT_CALL(*this, RequestPause()).Times(num_expected_pauses); - EXPECT_CALL(*this, RequestResume()).Times(num_expected_resumes); - EXPECT_CALL(*this, RequestNudge()). - Times(set_initial_sync_ended_on_init ? 0 : 1); -} - -void SyncBackendHostForProfileSyncTest::SetInitialSyncEndedForEnabledTypes() { - UserShare* user_share = core_->syncapi()->GetUserShare(); - DirectoryManager* dir_manager = user_share->dir_manager.get(); - - ScopedDirLookup dir(dir_manager, user_share->name); - if (!dir.good()) - FAIL(); - - ModelSafeRoutingInfo enabled_types; - GetModelSafeRoutingInfo(&enabled_types); - for (ModelSafeRoutingInfo::const_iterator i = enabled_types.begin(); - i != enabled_types.end(); ++i) { - dir->set_initial_sync_ended_for_type(i->first, true); - } -} - -void SyncBackendHostForProfileSyncTest:: - HandleInitializationCompletedOnFrontendLoop() { - set_syncapi_initialized(); // Need to do this asap so task below works. - - // Set up any nodes the test wants around before model association. - if (initial_condition_setup_task_) { - initial_condition_setup_task_->Run(); - } - - // Pretend we downloaded initial updates and set initial sync ended bits - // if we were asked to. - if (set_initial_sync_ended_on_init_) { - UserShare* user_share = core_->syncapi()->GetUserShare(); - DirectoryManager* dir_manager = user_share->dir_manager.get(); - - ScopedDirLookup dir(dir_manager, user_share->name); - if (!dir.good()) - FAIL(); - - // Do this check as some tests load from storage and we don't want to add - // twice. - // TODO(tim): This is madness. Too much test setup code! - if (!dir->initial_sync_ended_for_type(syncable::NIGORI)) { - ProfileSyncServiceTestHelper::CreateRoot( - syncable::NIGORI, test_service_, test_service_->id_factory()); - } - - SetInitialSyncEndedForEnabledTypes(); - } - - SyncBackendHost::HandleInitializationCompletedOnFrontendLoop(); -} - -// Called when a nudge comes in. -void SyncBackendHostForProfileSyncTest:: - SimulateSyncCycleCompletedInitialSyncEnded() { - syncable::ModelTypeBitSet sync_ended; - ModelSafeRoutingInfo enabled_types; - GetModelSafeRoutingInfo(&enabled_types); - for (ModelSafeRoutingInfo::const_iterator i = enabled_types.begin(); - i != enabled_types.end(); ++i) { - sync_ended.set(i->first); - } - core_->HandleSyncCycleCompletedOnFrontendLoop(new SyncSessionSnapshot( - SyncerStatus(), ErrorCounters(), 0, 0, false, - sync_ended, false, false, 0, 0, false)); -} - -void SyncBackendHostForProfileSyncTest::InitCore( - const Core::DoInitializeOptions& options) { - std::wstring user = L"testuser"; - core_loop()->PostTask(FROM_HERE, - NewRunnableMethod(core_.get(), - &SyncBackendHost::Core::DoInitializeForTest, - user, - options.http_bridge_factory, - options.delete_sync_data_folder)); - - // TODO(akalin): Figure out a better way to do this. - if (synchronous_init_) { - // The SyncBackend posts a task to the current loop when - // initialization completes. - MessageLoop::current()->Run(); - } -} - -void SyncBackendHostForProfileSyncTest:: - SetDefaultExpectationsForWorkerCreation(ProfileMock* profile) { - EXPECT_CALL(*profile, GetPasswordStore(testing::_)). - WillOnce(testing::Return((PasswordStore*)NULL)); - EXPECT_CALL(*profile, GetHistoryService(testing::_)). - WillOnce(testing::Return((HistoryService*)NULL)); -} - -} // namespace browser_sync - -TestProfileSyncService::TestProfileSyncService(ProfileSyncFactory* factory, - Profile* profile, - const std::string& test_user, - bool synchronous_backend_initialization, - Task* initial_condition_setup_task) - : ProfileSyncService(factory, profile, - !test_user.empty() ? - test_user : ""), - synchronous_backend_initialization_( - synchronous_backend_initialization), - synchronous_sync_configuration_(false), - num_expected_resumes_(1), - num_expected_pauses_(1), - initial_condition_setup_task_(initial_condition_setup_task), - set_initial_sync_ended_on_init_(true) { - RegisterPreferences(); - SetSyncSetupCompleted(); -} - -void TestProfileSyncService::CreateBackend() { - backend_.reset(new browser_sync::SyncBackendHostForProfileSyncTest( - this, profile(), - profile()->GetPath(), data_type_controllers(), - initial_condition_setup_task_.release(), - num_expected_resumes_, num_expected_pauses_, - set_initial_sync_ended_on_init_, - synchronous_backend_initialization_)); -} - -void TestProfileSyncService::OnBackendInitialized() { - ProfileSyncService::OnBackendInitialized(); - // TODO(akalin): Figure out a better way to do this. - if (synchronous_backend_initialization_) { - MessageLoop::current()->Quit(); - } -} - -void TestProfileSyncService::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - ProfileSyncService::Observe(type, source, details); - if (type == NotificationType::SYNC_CONFIGURE_DONE && - !synchronous_sync_configuration_) { - MessageLoop::current()->Quit(); - } -} diff --git a/chrome/browser/sync/test_profile_sync_service.h b/chrome/browser/sync/test_profile_sync_service.h index b4115ad..3cad8dc 100644 --- a/chrome/browser/sync/test_profile_sync_service.h +++ b/chrome/browser/sync/test_profile_sync_service.h @@ -22,7 +22,6 @@ #include "chrome/common/notification_service.h" #include "chrome/test/profile_mock.h" #include "chrome/test/sync/test_http_bridge_factory.h" -#include "chrome/test/sync/engine/test_id_factory.h" #include "testing/gmock/include/gmock/gmock.h" using browser_sync::ModelSafeRoutingInfo; @@ -34,8 +33,6 @@ using syncable::DirectoryManager; using syncable::ModelType; using syncable::ScopedDirLookup; -class TestProfileSyncService; - ACTION_P(CallOnPaused, core) { core->OnPaused(); }; @@ -63,7 +60,7 @@ class SyncBackendHostForProfileSyncTest : public SyncBackendHost { // this is false, configuring data types will require a syncer nudge. // |synchronous_init| causes initialization to block until the syncapi has // completed setting itself up and called us back. - SyncBackendHostForProfileSyncTest(TestProfileSyncService* service, + SyncBackendHostForProfileSyncTest(SyncFrontend* frontend, Profile* profile, const FilePath& profile_path, const DataTypeController::TypeMap& data_type_controllers, @@ -71,33 +68,114 @@ class SyncBackendHostForProfileSyncTest : public SyncBackendHost { int num_expected_resumes, int num_expected_pauses, bool set_initial_sync_ended_on_init, - bool synchronous_init); + bool synchronous_init) + : browser_sync::SyncBackendHost(frontend, profile, profile_path, + data_type_controllers), + initial_condition_setup_task_(initial_condition_setup_task), + set_initial_sync_ended_on_init_(set_initial_sync_ended_on_init), + synchronous_init_(synchronous_init) { + // By default, the RequestPause and RequestResume methods will + // send the confirmation notification and return true. + ON_CALL(*this, RequestPause()). + WillByDefault(testing::DoAll(CallOnPaused(core_), + testing::Return(true))); + ON_CALL(*this, RequestResume()). + WillByDefault(testing::DoAll(CallOnResumed(core_), + testing::Return(true))); + ON_CALL(*this, RequestNudge()).WillByDefault(testing::Invoke(this, + &SyncBackendHostForProfileSyncTest:: + SimulateSyncCycleCompletedInitialSyncEnded)); + + EXPECT_CALL(*this, RequestPause()).Times(num_expected_pauses); + EXPECT_CALL(*this, RequestResume()).Times(num_expected_resumes); + EXPECT_CALL(*this, RequestNudge()). + Times(set_initial_sync_ended_on_init ? 0 : 1); + } MOCK_METHOD0(RequestPause, bool()); MOCK_METHOD0(RequestResume, bool()); MOCK_METHOD0(RequestNudge, void()); - void SetInitialSyncEndedForEnabledTypes(); + void SetInitialSyncEndedForEnabledTypes() { + UserShare* user_share = core_->syncapi()->GetUserShare(); + DirectoryManager* dir_manager = user_share->dir_manager.get(); + + ScopedDirLookup dir(dir_manager, user_share->name); + if (!dir.good()) + FAIL(); + + ModelSafeRoutingInfo enabled_types; + GetModelSafeRoutingInfo(&enabled_types); + for (ModelSafeRoutingInfo::const_iterator i = enabled_types.begin(); + i != enabled_types.end(); ++i) { + dir->set_initial_sync_ended_for_type(i->first, true); + } + } - virtual void HandleInitializationCompletedOnFrontendLoop(); + virtual void HandleInitializationCompletedOnFrontendLoop() { + set_syncapi_initialized(); // Need to do this asap so task below works. + + // Set up any nodes the test wants around before model association. + if (initial_condition_setup_task_) { + initial_condition_setup_task_->Run(); + } + + // Pretend we downloaded initial updates and set initial sync ended bits + // if we were asked to. + if (set_initial_sync_ended_on_init_) + SetInitialSyncEndedForEnabledTypes(); + + SyncBackendHost::HandleInitializationCompletedOnFrontendLoop(); + } // Called when a nudge comes in. - void SimulateSyncCycleCompletedInitialSyncEnded(); + void SimulateSyncCycleCompletedInitialSyncEnded() { + syncable::ModelTypeBitSet sync_ended; + ModelSafeRoutingInfo enabled_types; + GetModelSafeRoutingInfo(&enabled_types); + for (ModelSafeRoutingInfo::const_iterator i = enabled_types.begin(); + i != enabled_types.end(); ++i) { + sync_ended.set(i->first); + } + core_->HandleSyncCycleCompletedOnFrontendLoop(new SyncSessionSnapshot( + SyncerStatus(), ErrorCounters(), 0, 0, false, + sync_ended, false, false, 0, 0, false)); + } virtual sync_api::HttpPostProviderFactory* MakeHttpBridgeFactory( URLRequestContextGetter* getter) { return new browser_sync::TestHttpBridgeFactory; } - virtual void InitCore(const Core::DoInitializeOptions& options); + virtual void InitCore(const Core::DoInitializeOptions& options) { + std::wstring user = L"testuser"; + core_loop()->PostTask(FROM_HERE, + NewRunnableMethod(core_.get(), + &SyncBackendHost::Core::DoInitializeForTest, + user, + options.http_bridge_factory, + options.delete_sync_data_folder)); - static void SetDefaultExpectationsForWorkerCreation(ProfileMock* profile); + // TODO(akalin): Figure out a better way to do this. + if (synchronous_init_) { + // The SyncBackend posts a task to the current loop when + // initialization completes. + MessageLoop::current()->Run(); + } + } + + static void SetDefaultExpectationsForWorkerCreation(ProfileMock* profile) { + EXPECT_CALL(*profile, GetPasswordStore(testing::_)). + WillOnce(testing::Return((PasswordStore*)NULL)); + EXPECT_CALL(*profile, GetHistoryService(testing::_)). + WillOnce(testing::Return((HistoryService*)NULL)); + } private: Task* initial_condition_setup_task_; bool set_initial_sync_ended_on_init_; bool synchronous_init_; - TestProfileSyncService* test_service_; + }; } // namespace browser_sync @@ -108,16 +186,49 @@ class TestProfileSyncService : public ProfileSyncService { Profile* profile, const std::string& test_user, bool synchronous_backend_initialization, - Task* initial_condition_setup_task); + Task* initial_condition_setup_task) + : ProfileSyncService(factory, profile, + !test_user.empty() ? + test_user : ""), + synchronous_backend_initialization_( + synchronous_backend_initialization), + synchronous_sync_configuration_(false), + num_expected_resumes_(1), + num_expected_pauses_(1), + initial_condition_setup_task_(initial_condition_setup_task), + set_initial_sync_ended_on_init_(true) { + RegisterPreferences(); + SetSyncSetupCompleted(); + } virtual ~TestProfileSyncService() { } - virtual void CreateBackend(); + virtual void CreateBackend() { + backend_.reset(new browser_sync::SyncBackendHostForProfileSyncTest( + this, profile(), + profile()->GetPath(), data_type_controllers(), + initial_condition_setup_task_.release(), + num_expected_resumes_, num_expected_pauses_, + set_initial_sync_ended_on_init_, + synchronous_backend_initialization_)); + } - virtual void OnBackendInitialized(); + virtual void OnBackendInitialized() { + ProfileSyncService::OnBackendInitialized(); + // TODO(akalin): Figure out a better way to do this. + if (synchronous_backend_initialization_) { + MessageLoop::current()->Quit(); + } + } virtual void Observe(NotificationType type, const NotificationSource& source, - const NotificationDetails& details); + const NotificationDetails& details) { + ProfileSyncService::Observe(type, source, details); + if (type == NotificationType::SYNC_CONFIGURE_DONE && + !synchronous_sync_configuration_) { + MessageLoop::current()->Quit(); + } + } void set_num_expected_resumes(int times) { num_expected_resumes_ = times; @@ -132,8 +243,6 @@ class TestProfileSyncService : public ProfileSyncService { synchronous_sync_configuration_ = true; } - browser_sync::TestIdFactory* id_factory() { return &id_factory_; } - private: // When testing under ChromiumOS, this method must not return an empty // value value in order for the profile sync service to start. @@ -152,8 +261,6 @@ class TestProfileSyncService : public ProfileSyncService { scoped_ptr<Task> initial_condition_setup_task_; bool set_initial_sync_ended_on_init_; - browser_sync::TestIdFactory id_factory_; - }; #endif // CHROME_BROWSER_SYNC_TEST_PROFILE_SYNC_SERVICE_H_ diff --git a/chrome/browser/sync/tools/sync_listen_notifications.cc b/chrome/browser/sync/tools/sync_listen_notifications.cc index c619e14..0b20db1 100644 --- a/chrome/browser/sync/tools/sync_listen_notifications.cc +++ b/chrome/browser/sync/tools/sync_listen_notifications.cc @@ -18,17 +18,18 @@ #include "chrome/browser/sync/notifier/chrome_invalidation_client.h" #include "chrome/browser/sync/notifier/chrome_system_resources.h" #include "chrome/browser/sync/sync_constants.h" -#include "chrome/common/chrome_switches.h" -#include "jingle/notifier/base/notification_method.h" #include "jingle/notifier/base/xmpp_connection.h" #include "jingle/notifier/listener/listen_task.h" #include "jingle/notifier/listener/notification_constants.h" +#include "jingle/notifier/listener/notification_defines.h" +#include "jingle/notifier/listener/send_update_task.h" #include "jingle/notifier/listener/subscribe_task.h" #include "jingle/notifier/listener/xml_element_util.h" #include "net/base/ssl_config_service.h" #include "net/socket/client_socket_factory.h" #include "talk/base/cryptstring.h" #include "talk/base/logging.h" +#include "talk/base/sigslot.h" #include "talk/base/task.h" #include "talk/xmpp/jid.h" #include "talk/xmpp/xmppclientsettings.h" @@ -98,22 +99,19 @@ class XmppNotificationClient : public notifier::XmppConnection::Delegate { }; // Delegate for legacy notifications. -class LegacyNotifierDelegate : public XmppNotificationClient::Observer { +class LegacyNotifierDelegate + : public XmppNotificationClient::Observer, + public sigslot::has_slots<> { public: + explicit LegacyNotifierDelegate(bool send_initial_update) + : send_initial_update_(send_initial_update) {} + virtual ~LegacyNotifierDelegate() {} virtual void OnConnect(base::WeakPtr<talk_base::Task> base_task) { LOG(INFO) << "Logged in"; - notifier::NotificationMethod notification_method = - notifier::NOTIFICATION_TRANSITIONAL; std::vector<std::string> subscribed_services_list; - if (notification_method != notifier::NOTIFICATION_LEGACY) { - if (notification_method == notifier::NOTIFICATION_TRANSITIONAL) { - subscribed_services_list.push_back( - browser_sync::kSyncLegacyServiceUrl); - } - subscribed_services_list.push_back(browser_sync::kSyncServiceUrl); - } + subscribed_services_list.push_back(browser_sync::kSyncServiceUrl); // Owned by base_task. notifier::SubscribeTask* subscribe_task = new notifier::SubscribeTask(base_task, subscribed_services_list); @@ -121,10 +119,29 @@ class LegacyNotifierDelegate : public XmppNotificationClient::Observer { // Owned by xmpp_client. notifier::ListenTask* listen_task = new notifier::ListenTask(base_task); + listen_task->SignalUpdateAvailable.connect( + this, &LegacyNotifierDelegate::OnUpdateAvailable); listen_task->Start(); + if (send_initial_update_) { + // Owned by xmpp_client. + notifier::SendUpdateTask* send_update_task = + new notifier::SendUpdateTask(base_task, + OutgoingNotificationData()); + send_update_task->Start(); + } } virtual void OnError() {} + + void OnUpdateAvailable( + const IncomingNotificationData& notification_data) { + LOG(INFO) << "Notification received: " + << notification_data.service_url << " " + << notification_data.service_specific_data; + } + + private: + bool send_initial_update_; }; // The actual listener for sync notifications. @@ -156,23 +173,23 @@ class ChromeInvalidationListener DISALLOW_COPY_AND_ASSIGN(ChromeInvalidationListener); }; -// Delegate for server-side notifications. -class CacheInvalidationNotifierDelegate +// Delegate for server-issued notifications. +class ServerNotifierDelegate : public XmppNotificationClient::Observer, public sync_notifier::StateWriter { public: - explicit CacheInvalidationNotifierDelegate( - const std::string& cache_invalidation_state) - : cache_invalidation_state_(cache_invalidation_state) {} + explicit ServerNotifierDelegate( + const std::string& server_notifier_state) + : server_notifier_state_(server_notifier_state) {} - virtual ~CacheInvalidationNotifierDelegate() {} + virtual ~ServerNotifierDelegate() {} virtual void OnConnect(base::WeakPtr<talk_base::Task> base_task) { LOG(INFO) << "Logged in"; // TODO(akalin): app_name should be per-client unique. const std::string kAppName = "cc_sync_listen_notifications"; - chrome_invalidation_client_.Start(kAppName, cache_invalidation_state_, + chrome_invalidation_client_.Start(kAppName, server_notifier_state_, &chrome_invalidation_listener_, this, base_task); chrome_invalidation_client_.RegisterTypes(); @@ -193,7 +210,7 @@ class CacheInvalidationNotifierDelegate // Opaque blob capturing the notifications state of a previous run // (i.e., the base64-decoded value of a string output by // WriteState()). - std::string cache_invalidation_state_; + std::string server_notifier_state_; sync_notifier::ChromeInvalidationClient chrome_invalidation_client_; }; @@ -211,24 +228,22 @@ int main(int argc, char* argv[]) { // Parse command line. const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - std::string email = command_line.GetSwitchValueASCII(switches::kSyncEmail); + std::string email = command_line.GetSwitchValueASCII("email"); if (email.empty()) { printf("Usage: %s --email=foo@bar.com [--password=mypassword] " "[--server=talk.google.com] [--port=5222] [--allow-plain] " - "[--disable-tls] [--use-cache-invalidation] [--use-ssl-tcp] " - "[--cache-invalidtion-state]\n", + "[--disable-tls] [--use-ssl-tcp] [--server-notifier-state] " + "[--use-legacy-notifier] " + "[--legacy-notifier-send-initial-update]\n", argv[0]); return -1; } - std::string password = - command_line.GetSwitchValueASCII(switches::kSyncPassword); - std::string server = - command_line.GetSwitchValueASCII(switches::kSyncServer); + std::string password = command_line.GetSwitchValueASCII("password"); + std::string server = command_line.GetSwitchValueASCII("server"); if (server.empty()) { server = "talk.google.com"; } - std::string port_str = - command_line.GetSwitchValueASCII(switches::kSyncPort); + std::string port_str = command_line.GetSwitchValueASCII("port"); int port = 5222; if (!port_str.empty()) { int port_from_port_str = std::strtol(port_str.c_str(), NULL, 10); @@ -238,22 +253,25 @@ int main(int argc, char* argv[]) { port = port_from_port_str; } } - bool allow_plain = command_line.HasSwitch(switches::kSyncAllowPlain); - bool disable_tls = command_line.HasSwitch(switches::kSyncDisableTls); - bool use_ssl_tcp = command_line.HasSwitch(switches::kSyncUseSslTcp); + bool allow_plain = command_line.HasSwitch("allow-plain"); + bool disable_tls = command_line.HasSwitch("disable-tls"); + bool use_ssl_tcp = command_line.HasSwitch("use-ssl-tcp"); if (use_ssl_tcp && (port != 443)) { - LOG(WARNING) << switches::kSyncUseSslTcp << " is set but port is " << port + LOG(WARNING) << "--use-ssl-tcp is set but port is " << port << " instead of 443"; } - std::string cache_invalidation_state; - std::string cache_invalidation_state_encoded = - command_line.GetSwitchValueASCII("cache-invalidation-state"); - if (!cache_invalidation_state_encoded.empty() && - !base::Base64Decode(cache_invalidation_state_encoded, - &cache_invalidation_state)) { + std::string server_notifier_state; + std::string server_notifier_state_encoded = + command_line.GetSwitchValueASCII("server-notifier-state"); + if (!server_notifier_state_encoded.empty() && + !base::Base64Decode(server_notifier_state_encoded, + &server_notifier_state)) { LOG(ERROR) << "Could not decode state: " - << cache_invalidation_state_encoded; + << server_notifier_state_encoded; } + bool use_legacy_notifier = command_line.HasSwitch("use-legacy-notifier"); + bool legacy_notifier_send_initial_update = + command_line.HasSwitch("legacy-notifier-send-initial-update"); // Build XMPP client settings. buzz::XmppClientSettings xmpp_client_settings; @@ -280,18 +298,16 @@ int main(int argc, char* argv[]) { MessageLoopForIO message_loop; // Connect and listen. - LegacyNotifierDelegate legacy_notifier_delegate; - CacheInvalidationNotifierDelegate cache_invalidation_notifier_delegate( - cache_invalidation_state); + ServerNotifierDelegate server_notifier_delegate( + server_notifier_state); + LegacyNotifierDelegate legacy_notifier_delegate( + legacy_notifier_send_initial_update); std::vector<XmppNotificationClient::Observer*> observers; - // TODO(akalin): Add switch to listen to both. - if (command_line.HasSwitch(switches::kSyncUseCacheInvalidation)) { - observers.push_back(&cache_invalidation_notifier_delegate); - } else { + if (use_legacy_notifier) { observers.push_back(&legacy_notifier_delegate); + } else { + observers.push_back(&server_notifier_delegate); } - // TODO(akalin): Revert the move of all switches in this file into - // chrome_switches.h. XmppNotificationClient xmpp_notification_client( observers.begin(), observers.end()); xmpp_notification_client.Run(xmpp_client_settings); diff --git a/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc b/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc index cffa963..d5f88d4 100644 --- a/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc +++ b/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc @@ -53,7 +53,7 @@ class BookmarkAPIEventTask : public Task { done_->Signal(); } private: - scoped_ptr<Extension> extension_; + scoped_refptr<Extension> extension_; scoped_refptr<FunctionType> function_; size_t repeats_; base::WaitableEvent* done_; @@ -68,13 +68,12 @@ class BookmarkAPIEventGenerator { template <class T> void NewEvent(const FilePath::StringType& extension_path, T* bookmarks_function, size_t repeats) { - FilePath path(extension_path); - Extension* extension = new Extension(path); std::string error; DictionaryValue input; input.SetString(keys::kVersion, kTestExtensionVersion); input.SetString(keys::kName, kTestExtensionName); - extension->InitFromValue(input, false, &error); + scoped_refptr<Extension> extension(Extension::Create( + FilePath(extension_path), Extension::INVALID, input, false, &error)); bookmarks_function->set_name(T::function_name()); base::WaitableEvent done_event(false, false); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, @@ -128,14 +127,13 @@ class ExtensionsActivityMonitorTest : public testing::Test { static std::string GetExtensionIdForPath( const FilePath::StringType& extension_path) { std::string error; - FilePath path(extension_path); - Extension e(path); DictionaryValue input; input.SetString(keys::kVersion, kTestExtensionVersion); input.SetString(keys::kName, kTestExtensionName); - e.InitFromValue(input, false, &error); + scoped_refptr<Extension> extension(Extension::Create( + FilePath(extension_path), Extension::INVALID, input, false, &error)); EXPECT_EQ("", error); - return e.id(); + return extension->id(); } private: NotificationService* service_; diff --git a/chrome/browser/sync/util/nigori.cc b/chrome/browser/sync/util/nigori.cc index 77cca2b..49ed1db 100644 --- a/chrome/browser/sync/util/nigori.cc +++ b/chrome/browser/sync/util/nigori.cc @@ -6,6 +6,8 @@ #if defined(OS_WIN) #include <winsock2.h> // for htonl +#else +#include <arpa/inet.h> #endif #include <sstream> diff --git a/chrome/browser/tab_contents/background_contents.h b/chrome/browser/tab_contents/background_contents.h index 0e48416..63a30d9 100644 --- a/chrome/browser/tab_contents/background_contents.h +++ b/chrome/browser/tab_contents/background_contents.h @@ -7,6 +7,7 @@ #pragma once #include <string> +#include <vector> #include "chrome/browser/js_modal_dialog.h" #include "chrome/browser/renderer_host/render_view_host_delegate.h" @@ -90,6 +91,12 @@ class BackgroundContents : public RenderViewHostDelegate, const gfx::Rect& initial_pos); virtual void ShowCreatedFullscreenWidget(int route_id); virtual void ShowContextMenu(const ContextMenuParams& params) {} + virtual void ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned) {} virtual void StartDragging(const WebDropData& drop_data, WebKit::WebDragOperationsMask allowed_operations, const SkBitmap& image, diff --git a/chrome/browser/tab_contents/interstitial_page.cc b/chrome/browser/tab_contents/interstitial_page.cc index 8663eef..ae1e078 100644 --- a/chrome/browser/tab_contents/interstitial_page.cc +++ b/chrome/browser/tab_contents/interstitial_page.cc @@ -104,6 +104,12 @@ class InterstitialPage::InterstitialPageRVHViewDelegate const gfx::Rect& initial_pos); virtual void ShowCreatedFullscreenWidget(int route_id); virtual void ShowContextMenu(const ContextMenuParams& params); + virtual void ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned); virtual void StartDragging(const WebDropData& drop_data, WebDragOperationsMask operations_allowed, const SkBitmap& image, @@ -615,6 +621,15 @@ void InterstitialPage::InterstitialPageRVHViewDelegate::ShowContextMenu( const ContextMenuParams& params) { } +void InterstitialPage::InterstitialPageRVHViewDelegate::ShowPopupMenu( + const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned) { +} + void InterstitialPage::InterstitialPageRVHViewDelegate::StartDragging( const WebDropData& drop_data, WebDragOperationsMask allowed_operations, @@ -699,8 +714,9 @@ int InterstitialPage::GetBrowserWindowID() const { } void InterstitialPage::UpdateInspectorSetting(const std::string& key, - const std::string& value) { - RenderViewHostDelegateHelper::UpdateInspectorSetting(tab_->profile(), key, value); + const std::string& value) { + RenderViewHostDelegateHelper::UpdateInspectorSetting( + tab_->profile(), key, value); } void InterstitialPage::ClearInspectorSettings() { diff --git a/chrome/browser/tab_contents/popup_menu_helper_mac.h b/chrome/browser/tab_contents/popup_menu_helper_mac.h new file mode 100644 index 0000000..08a4c19 --- /dev/null +++ b/chrome/browser/tab_contents/popup_menu_helper_mac.h @@ -0,0 +1,46 @@ +// 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_TAB_CONTENTS_POPUP_MENU_HELPER_MAC_H_ +#define CHROME_BROWSER_TAB_CONTENTS_POPUP_MENU_HELPER_MAC_H_ + +#include <vector> + +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "gfx/rect.h" + +class RenderViewHost; +struct WebMenuItem; + +class PopupMenuHelper : public NotificationObserver { + public: + // Creates a PopupMenuHelper that will notify |render_view_host| when a user + // selects or cancels the popup. + explicit PopupMenuHelper(RenderViewHost* render_view_host); + + // Shows the popup menu and notifies the RenderViewHost of the selection/ + // cancel. + // This call is blocking. + void ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned); + + private: + // NotificationObserver implementation: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + NotificationRegistrar notification_registrar_; + + RenderViewHost* render_view_host_; + + DISALLOW_COPY_AND_ASSIGN(PopupMenuHelper); +}; + +#endif // CHROME_BROWSER_TAB_CONTENTS_POPUP_MENU_HELPER_MAC_H_ diff --git a/chrome/browser/tab_contents/popup_menu_helper_mac.mm b/chrome/browser/tab_contents/popup_menu_helper_mac.mm new file mode 100644 index 0000000..0e910b7 --- /dev/null +++ b/chrome/browser/tab_contents/popup_menu_helper_mac.mm @@ -0,0 +1,87 @@ +// 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. + +#import <Carbon/Carbon.h> + +#include "chrome/browser/tab_contents/popup_menu_helper_mac.h" + +#import "base/chrome_application_mac.h" +#include "base/message_loop.h" +#include "base/scoped_nsobject.h" +#import "chrome/browser/cocoa/base_view.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view_mac.h" +#include "chrome/common/notification_source.h" +#include "webkit/glue/webmenurunner_mac.h" + +PopupMenuHelper::PopupMenuHelper(RenderViewHost* render_view_host) + : render_view_host_(render_view_host) { + notification_registrar_.Add( + this, NotificationType::RENDER_WIDGET_HOST_DESTROYED, + Source<RenderWidgetHost>(render_view_host)); +} + +void PopupMenuHelper::ShowPopupMenu( + const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned) { + // Retain the Cocoa view for the duration of the pop-up so that it can't be + // dealloced if my Destroy() method is called while the pop-up's up (which + // would in turn delete me, causing a crash once the -runMenuInView + // call returns. That's what was happening in <http://crbug.com/33250>). + RenderWidgetHostViewMac* rwhvm = + static_cast<RenderWidgetHostViewMac*>(render_view_host_->view()); + scoped_nsobject<RenderWidgetHostViewCocoa> cocoa_view + ([rwhvm->native_view() retain]); + + // Display the menu. + scoped_nsobject<WebMenuRunner> menu_runner; + menu_runner.reset([[WebMenuRunner alloc] initWithItems:items + fontSize:item_font_size + rightAligned:right_aligned]); + + { + // Make sure events can be pumped while the menu is up. + MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); + + // One of the events that could be pumped is |window.close()|. + // User-initiated event-tracking loops protect against this by + // setting flags in -[CrApplication sendEvent:], but since + // web-content menus are initiated by IPC message the setup has to + // be done manually. + chrome_application_mac::ScopedSendingEvent sendingEventScoper; + + // Now run a SYNCHRONOUS NESTED EVENT LOOP until the pop-up is finished. + [menu_runner runMenuInView:cocoa_view + withBounds:[cocoa_view flipRectToNSRect:bounds] + initialIndex:selected_item]; + } + + if (!render_view_host_) { + // Bad news, the RenderViewHost got deleted while we were off running the + // menu. Nothing to do. + return; + } + + if ([menu_runner menuItemWasChosen]) { + render_view_host_->DidSelectPopupMenuItem( + [menu_runner indexOfSelectedItem]); + } else { + render_view_host_->DidCancelPopupMenu(); + } +} + +void PopupMenuHelper::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::RENDER_WIDGET_HOST_DESTROYED); + RenderViewHost* rvh = Source<RenderViewHost>(source).ptr(); + DCHECK_EQ(render_view_host_, rvh); + render_view_host_ = NULL; +} + diff --git a/chrome/browser/tab_contents/render_view_context_menu.cc b/chrome/browser/tab_contents/render_view_context_menu.cc index 4dc7b11..989c6c9 100644 --- a/chrome/browser/tab_contents/render_view_context_menu.cc +++ b/chrome/browser/tab_contents/render_view_context_menu.cc @@ -14,14 +14,16 @@ #include "base/stl_util-inl.h" #include "base/string_util.h" #include "base/time.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/autocomplete/autocomplete_classifier.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/child_process_security_policy.h" #include "chrome/browser/debugger/devtools_manager.h" #include "chrome/browser/debugger/devtools_window.h" #include "chrome/browser/download/download_manager.h" +#include "chrome/browser/extensions/extension_event_router.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/fonts_languages_window.h" #include "chrome/browser/metrics/user_metrics.h" @@ -64,7 +66,7 @@ const size_t RenderViewContextMenu::kMaxSelectionTextLength = 50; // static bool RenderViewContextMenu::IsDevToolsURL(const GURL& url) { - return url.SchemeIs(chrome::kChromeUIScheme) && + return url.SchemeIs(chrome::kChromeDevToolsScheme) && url.host() == chrome::kChromeUIDevToolsHost; } @@ -149,10 +151,12 @@ static const GURL& GetDocumentURL(const ContextMenuParams& params) { } // Given a list of items, returns the ones that match given the contents -// of |params|. +// of |params| and the profile. static ExtensionMenuItem::List GetRelevantExtensionItems( const ExtensionMenuItem::List& items, - const ContextMenuParams& params) { + const ContextMenuParams& params, + Profile* profile, + bool can_cross_incognito) { ExtensionMenuItem::List result; for (ExtensionMenuItem::List::const_iterator i = items.begin(); i != items.end(); ++i) { @@ -170,7 +174,8 @@ static ExtensionMenuItem::List GetRelevantExtensionItems( if (!ExtensionPatternMatch(item->target_url_patterns(), target_url)) continue; - result.push_back(*i); + if (item->id().profile == profile || can_cross_incognito) + result.push_back(*i); } return result; } @@ -179,7 +184,8 @@ void RenderViewContextMenu::AppendExtensionItems( const std::string& extension_id, int* index) { ExtensionsService* service = profile_->GetExtensionsService(); ExtensionMenuManager* manager = service->menu_manager(); - Extension* extension = service->GetExtensionById(extension_id, false); + const Extension* extension = service->GetExtensionById(extension_id, false); + bool can_cross_incognito = service->CanCrossIncognito(extension); DCHECK_GE(*index, 0); int max_index = IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST - IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; @@ -191,7 +197,8 @@ void RenderViewContextMenu::AppendExtensionItems( if (!all_items || all_items->empty()) return; ExtensionMenuItem::List items = - GetRelevantExtensionItems(*all_items, params_); + GetRelevantExtensionItems(*all_items, params_, profile_, + can_cross_incognito); if (items.empty()) return; @@ -214,7 +221,8 @@ void RenderViewContextMenu::AppendExtensionItems( extension_item_map_[menu_id] = item->id(); title = item->TitleWithReplacement(PrintableSelectionText(), kMaxExtensionItemTitleLength); - submenu_items = GetRelevantExtensionItems(item->children(), params_); + submenu_items = GetRelevantExtensionItems(item->children(), params_, + profile_, can_cross_incognito); } // Now add our item(s) to the menu_model_. @@ -224,13 +232,15 @@ void RenderViewContextMenu::AppendExtensionItems( menus::SimpleMenuModel* submenu = new menus::SimpleMenuModel(this); extension_menu_models_.push_back(submenu); menu_model_.AddSubMenu(menu_id, title, submenu); - RecursivelyAppendExtensionItems(submenu_items, submenu, index); + RecursivelyAppendExtensionItems(submenu_items, can_cross_incognito, submenu, + index); } SetExtensionIcon(extension_id); } void RenderViewContextMenu::RecursivelyAppendExtensionItems( const ExtensionMenuItem::List& items, + bool can_cross_incognito, menus::SimpleMenuModel* menu_model, int *index) { string16 selection_text = PrintableSelectionText(); @@ -257,14 +267,16 @@ void RenderViewContextMenu::RecursivelyAppendExtensionItems( kMaxExtensionItemTitleLength); if (item->type() == ExtensionMenuItem::NORMAL) { ExtensionMenuItem::List children = - GetRelevantExtensionItems(item->children(), params_); + GetRelevantExtensionItems(item->children(), params_, + profile_, can_cross_incognito); if (children.size() == 0) { menu_model->AddItem(menu_id, title); } else { menus::SimpleMenuModel* submenu = new menus::SimpleMenuModel(this); extension_menu_models_.push_back(submenu); menu_model->AddSubMenu(menu_id, title, submenu); - RecursivelyAppendExtensionItems(children, submenu, index); + RecursivelyAppendExtensionItems(children, can_cross_incognito, + submenu, index); } } else if (item->type() == ExtensionMenuItem::CHECKBOX) { menu_model->AddCheckItem(menu_id, title); @@ -317,7 +329,7 @@ void RenderViewContextMenu::AppendAllExtensionItems() { std::set<std::string> ids = menu_manager->ExtensionIds(); std::vector<std::pair<std::string, std::string> > sorted_ids; for (std::set<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) { - Extension* extension = service->GetExtensionById(*i, false); + const Extension* extension = service->GetExtensionById(*i, false); if (extension) sorted_ids.push_back( std::pair<std::string, std::string>(extension->name(), *i)); @@ -742,6 +754,12 @@ bool RenderViewContextMenu::IsCommandIdEnabled(int id) const { return false; } + if (id == IDC_SAVE_PAGE && + (source_tab_contents_->content_restrictions() & + CONTENT_RESTRICTION_SAVE)) { + return false; + } + // Allow Spell Check language items on sub menu for text area context menu. if ((id >= IDC_SPELLCHECK_LANGUAGES_FIRST) && (id < IDC_SPELLCHECK_LANGUAGES_LAST)) { @@ -1428,6 +1446,10 @@ bool RenderViewContextMenu::IsDevCommandEnabled(int id) const { if (IsDevToolsURL(active_entry->url()) && !command_line.HasSwitch(switches::kProcessPerTab)) return false; + // Don't enable the web inspector if the developer tools are disabled via + // the preference dev-tools-disabled. + if (profile_->GetPrefs()->GetBoolean(prefs::kDevToolsDisabled)) + return false; } return true; diff --git a/chrome/browser/tab_contents/render_view_context_menu.h b/chrome/browser/tab_contents/render_view_context_menu.h index 9fd3108..2f1a83e 100644 --- a/chrome/browser/tab_contents/render_view_context_menu.h +++ b/chrome/browser/tab_contents/render_view_context_menu.h @@ -103,6 +103,7 @@ class RenderViewContextMenu : public menus::SimpleMenuModel::Delegate { // Used for recursively adding submenus of extension items. void RecursivelyAppendExtensionItems( const std::vector<ExtensionMenuItem*>& items, + bool can_cross_incognito, menus::SimpleMenuModel* menu_model, int *index); // This will set the icon on the most recently-added item in the menu_model_. diff --git a/chrome/browser/tab_contents/render_view_context_menu_gtk.cc b/chrome/browser/tab_contents/render_view_context_menu_gtk.cc index 28d2fd8..120ea9e 100644 --- a/chrome/browser/tab_contents/render_view_context_menu_gtk.cc +++ b/chrome/browser/tab_contents/render_view_context_menu_gtk.cc @@ -7,7 +7,7 @@ #include <gtk/gtk.h> #include "base/string_util.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "webkit/glue/context_menu.h" diff --git a/chrome/browser/tab_contents/render_view_context_menu_mac.mm b/chrome/browser/tab_contents/render_view_context_menu_mac.mm index 1e4e4c4..2b9ca36 100644 --- a/chrome/browser/tab_contents/render_view_context_menu_mac.mm +++ b/chrome/browser/tab_contents/render_view_context_menu_mac.mm @@ -8,7 +8,7 @@ #include "base/message_loop.h" #include "base/scoped_nsobject.h" #include "base/sys_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #import "chrome/browser/cocoa/menu_controller.h" #include "grit/generated_resources.h" diff --git a/chrome/browser/tab_contents/render_view_host_delegate_helper.cc b/chrome/browser/tab_contents/render_view_host_delegate_helper.cc index b94ab7d..15ff1ca 100644 --- a/chrome/browser/tab_contents/render_view_host_delegate_helper.cc +++ b/chrome/browser/tab_contents/render_view_host_delegate_helper.cc @@ -35,7 +35,7 @@ RenderViewHostDelegateViewHelper::MaybeCreateBackgroundContents( int route_id, Profile* profile, SiteInstance* site, - GURL opener_url, + const GURL& opener_url, const string16& frame_name) { ExtensionsService* extensions_service = profile->GetExtensionsService(); @@ -45,7 +45,8 @@ RenderViewHostDelegateViewHelper::MaybeCreateBackgroundContents( !extensions_service->is_ready()) return NULL; - Extension* extension = extensions_service->GetExtensionByURL(opener_url); + const Extension* extension = + extensions_service->GetExtensionByURL(opener_url); if (!extension) extension = extensions_service->GetExtensionByWebExtent(opener_url); if (!extension || diff --git a/chrome/browser/tab_contents/render_view_host_delegate_helper.h b/chrome/browser/tab_contents/render_view_host_delegate_helper.h index 5afe3c7..372b234 100644 --- a/chrome/browser/tab_contents/render_view_host_delegate_helper.h +++ b/chrome/browser/tab_contents/render_view_host_delegate_helper.h @@ -76,7 +76,7 @@ class RenderViewHostDelegateViewHelper { int route_id, Profile* profile, SiteInstance* site, - GURL opener_url, + const GURL& opener_url, const string16& frame_name); // Tracks created RenderViewHost objects that have not been shown yet. diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc index eb6f1c0..91995c7 100644 --- a/chrome/browser/tab_contents/tab_contents.cc +++ b/chrome/browser/tab_contents/tab_contents.cc @@ -110,6 +110,7 @@ #include "third_party/WebKit/WebKit/chromium/public/WebView.h" #include "webkit/glue/webpreferences.h" #include "webkit/glue/password_form.h" +#include "webkit/glue/plugins/plugin_list.h" // Cross-Site Navigations // @@ -167,6 +168,7 @@ const int kJavascriptMessageExpectedDelay = 1000; // The list of prefs we want to observe. const char* kPrefsToObserve[] = { prefs::kAlternateErrorPagesEnabled, + prefs::kDefaultZoomLevel, prefs::kWebKitJavaEnabled, prefs::kWebKitJavascriptEnabled, prefs::kWebKitLoadsImagesAutomatically, @@ -586,7 +588,7 @@ RenderProcessHost* TabContents::GetRenderProcessHost() const { return render_manager_.current_host()->process(); } -void TabContents::SetExtensionApp(Extension* extension) { +void TabContents::SetExtensionApp(const Extension* extension) { DCHECK(!extension || extension->GetFullLaunchURL().is_valid()); extension_app_ = extension; @@ -604,7 +606,7 @@ void TabContents::SetExtensionAppById(const std::string& extension_app_id) { ExtensionsService* extension_service = profile()->GetExtensionsService(); if (extension_service && extension_service->is_ready()) { - Extension* extension = + const Extension* extension = extension_service->GetExtensionById(extension_app_id, false); if (extension) SetExtensionApp(extension); @@ -1465,12 +1467,10 @@ void TabContents::UpdateHistoryPageTitle(const NavigationEntry& entry) { hs->SetPageTitle(entry.virtual_url(), entry.title()); } -int TabContents::GetZoomPercent(bool* enable_increment, - bool* enable_decrement) { - *enable_decrement = *enable_increment = false; +double TabContents::GetZoomLevel() const { HostZoomMap* zoom_map = profile()->GetHostZoomMap(); if (!zoom_map) - return 100; + return 0; double zoom_level; if (temporary_zoom_settings_) { @@ -1479,9 +1479,14 @@ int TabContents::GetZoomPercent(bool* enable_increment, } else { zoom_level = zoom_map->GetZoomLevel(GetURL()); } + return zoom_level; +} +int TabContents::GetZoomPercent(bool* enable_increment, + bool* enable_decrement) { + *enable_decrement = *enable_increment = false; int percent = static_cast<int>( - WebKit::WebView::zoomLevelToZoomFactor(zoom_level) * 100); + WebKit::WebView::zoomLevelToZoomFactor(GetZoomLevel()) * 100); *enable_decrement = percent > minimum_zoom_percent_; *enable_increment = percent < maximum_zoom_percent_; return percent; @@ -1759,6 +1764,10 @@ void TabContents::UpdateWebPreferences() { render_view_host()->UpdateWebPreferences(GetWebkitPrefs()); } +void TabContents::UpdateZoomLevel() { + render_view_host()->SetZoomLevel(GetZoomLevel()); +} + void TabContents::UpdateMaxPageIDIfNecessary(SiteInstance* site_instance, RenderViewHost* rvh) { // If we are creating a RVH for a restored controller, then we might @@ -2018,25 +2027,19 @@ void TabContents::OnCrashedPlugin(const FilePath& plugin_path) { DCHECK(!plugin_path.value().empty()); std::wstring plugin_name = plugin_path.ToWStringHack(); -#if defined(OS_WIN) || defined(OS_MACOSX) - scoped_ptr<FileVersionInfo> version_info( - FileVersionInfo::CreateFileVersionInfo(plugin_path)); - if (version_info.get()) { - const std::wstring& product_name = version_info->product_name(); - if (!product_name.empty()) { - plugin_name = product_name; + WebPluginInfo plugin_info; + if (NPAPI::PluginList::Singleton()->GetPluginInfoByPath( + plugin_path, &plugin_info) && + !plugin_info.name.empty()) { + plugin_name = UTF16ToWide(plugin_info.name); #if defined(OS_MACOSX) - // Many plugins on the Mac have .plugin in the actual name, which looks - // terrible, so look for that and strip it off if present. - const std::wstring plugin_extension(L".plugin"); - if (EndsWith(plugin_name, plugin_extension, true)) - plugin_name.erase(plugin_name.length() - plugin_extension.length()); + // Many plugins on the Mac have .plugin in the actual name, which looks + // terrible, so look for that and strip it off if present. + const std::wstring plugin_extension(L".plugin"); + if (EndsWith(plugin_name, plugin_extension, true)) + plugin_name.erase(plugin_name.length() - plugin_extension.length()); #endif // OS_MACOSX - } } -#else - NOTIMPLEMENTED() << " convert plugin path to plugin name"; -#endif SkBitmap* crash_icon = ResourceBundle::GetSharedInstance().GetBitmapNamed( IDR_INFOBAR_PLUGIN_CRASHED); AddInfoBar(new SimpleAlertInfoBarDelegate( @@ -2112,9 +2115,16 @@ void TabContents::OnPageTranslated(int32 page_id, Details<PageTranslatedDetails>(&details)); } -void TabContents::OnSetSuggestResult(int32 page_id, const std::string& result) { +void TabContents::OnSetSuggestions( + int32 page_id, + const std::vector<std::string>& suggestions) { + if (delegate()) + delegate()->OnSetSuggestions(page_id, suggestions); +} + +void TabContents::OnInstantSupportDetermined(int32 page_id, bool result) { if (delegate()) - delegate()->OnSetSuggestResult(page_id, result); + delegate()->OnInstantSupportDetermined(page_id, result); } void TabContents::DidStartProvisionalLoadForFrame( @@ -2238,8 +2248,19 @@ void TabContents::DidFailProvisionalLoadWithError( Details<ProvisionalLoadDetails>(&details)); } -void TabContents::DocumentLoadedInFrame() { +void TabContents::DocumentLoadedInFrame(long long frame_id) { controller_.DocumentLoadedInFrame(); + NotificationService::current()->Notify( + NotificationType::FRAME_DOM_CONTENT_LOADED, + Source<NavigationController>(&controller_), + Details<long long>(&frame_id)); +} + +void TabContents::DidFinishLoad(long long frame_id) { + NotificationService::current()->Notify( + NotificationType::FRAME_DID_FINISH_LOAD, + Source<NavigationController>(&controller_), + Details<long long>(&frame_id)); } void TabContents::OnContentSettingsAccessed(bool content_was_blocked) { @@ -2564,16 +2585,19 @@ void TabContents::UpdateTargetURL(int32 page_id, const GURL& url) { void TabContents::UpdateThumbnail(const GURL& url, const SkBitmap& bitmap, const ThumbnailScore& score) { + if (profile()->IsOffTheRecord()) + return; + // Tell History about this thumbnail if (history::TopSites::IsEnabled()) { - if (!profile()->IsOffTheRecord()) - profile()->GetTopSites()->SetPageThumbnail(url, bitmap, score); + history::TopSites* ts = profile()->GetTopSites(); + if (ts) + ts->SetPageThumbnail(url, bitmap, score); } else { - HistoryService* hs; - if (!profile()->IsOffTheRecord() && - (hs = profile()->GetHistoryService(Profile::IMPLICIT_ACCESS))) { + HistoryService* hs = + profile()->GetHistoryService(Profile::IMPLICIT_ACCESS); + if (hs) hs->SetPageThumbnail(url, bitmap, score); - } } } @@ -2878,7 +2902,8 @@ WebPreferences TabContents::GetWebkitPrefs() { // Force accelerated compositing and 2d canvas off for chrome: and // chrome-extension: pages. - if (GetURL().SchemeIs(chrome::kChromeUIScheme)) { + if (GetURL().SchemeIs(chrome::kChromeDevToolsScheme) || + GetURL().SchemeIs(chrome::kChromeUIScheme)) { web_prefs.accelerated_compositing_enabled = false; web_prefs.accelerated_2d_canvas_enabled = false; } @@ -3080,6 +3105,8 @@ void TabContents::Observe(NotificationType type, } else if ((*pref_name_in == prefs::kDefaultCharset) || StartsWithASCII(*pref_name_in, "webkit.webprefs.", true)) { UpdateWebPreferences(); + } else if (*pref_name_in == prefs::kDefaultZoomLevel) { + UpdateZoomLevel(); } else { NOTREACHED() << "unexpected pref change notification" << *pref_name_in; } @@ -3143,7 +3170,7 @@ void TabContents::Observe(NotificationType type, } } -void TabContents::UpdateExtensionAppIcon(Extension* extension) { +void TabContents::UpdateExtensionAppIcon(const Extension* extension) { extension_app_icon_.reset(); if (extension) { @@ -3160,12 +3187,12 @@ void TabContents::UpdateExtensionAppIcon(Extension* extension) { } } -Extension* TabContents::GetExtensionContaining(const GURL& url) { +const Extension* TabContents::GetExtensionContaining(const GURL& url) { ExtensionsService* extensions_service = profile()->GetExtensionsService(); if (!extensions_service) return NULL; - Extension* extension = extensions_service->GetExtensionByURL(url); + const Extension* extension = extensions_service->GetExtensionByURL(url); return extension ? extension : extensions_service->GetExtensionByWebExtent(url); } diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h index 9782aa4..05c3a62 100644 --- a/chrome/browser/tab_contents/tab_contents.h +++ b/chrome/browser/tab_contents/tab_contents.h @@ -207,6 +207,12 @@ class TabContents : public PageNavigator, RenderViewHost* render_view_host() const { return render_manager_.current_host(); } + + DOMUI* dom_ui() const { + return render_manager_.dom_ui() ? render_manager_.dom_ui() + : render_manager_.pending_dom_ui(); + } + // Returns the currently active RenderWidgetHostView. This may change over // time and can be NULL (during setup and teardown). RenderWidgetHostView* GetRenderWidgetHostView() const { @@ -232,14 +238,14 @@ class TabContents : public PageNavigator, // NOTE: this should only be manipulated before the tab is added to a browser. // TODO(sky): resolve if this is the right way to identify an app tab. If it // is, than this should be passed in the constructor. - void SetExtensionApp(Extension* extension); + void SetExtensionApp(const Extension* extension); // Convenience for setting the app extension by id. This does nothing if // |extension_app_id| is empty, or an extension can't be found given the // specified id. void SetExtensionAppById(const std::string& extension_app_id); - Extension* extension_app() const { return extension_app_; } + const Extension* extension_app() const { return extension_app_; } bool is_app() const { return extension_app_ != NULL; } // If an app extension has been explicitly set for this TabContents its icon @@ -737,6 +743,9 @@ class TabContents : public PageNavigator, // the page title and we know we want to update history. void UpdateHistoryPageTitle(const NavigationEntry& entry); + // Gets the zoom level for this tab. + double GetZoomLevel() const; + // Gets the zoom percent for this tab. int GetZoomPercent(bool* enable_increment, bool* enable_decrement); @@ -832,6 +841,9 @@ class TabContents : public PageNavigator, // Send webkit specific settings to the renderer. void UpdateWebPreferences(); + // Instruct the renderer to update the zoom level. + void UpdateZoomLevel(); + // If our controller was restored and the page id is > than the site // instance's page id, the site instances page id is updated as well as the // renderers max page id. @@ -903,7 +915,9 @@ class TabContents : public PageNavigator, const std::string& original_lang, const std::string& translated_lang, TranslateErrors::Type error_type); - virtual void OnSetSuggestResult(int32 page_id, const std::string& result); + virtual void OnSetSuggestions(int32 page_id, + const std::vector<std::string>& suggestions); + virtual void OnInstantSupportDetermined(int32 page_id, bool result); // RenderViewHostDelegate::Resource implementation. virtual void DidStartProvisionalLoadForFrame(RenderViewHost* render_view_host, @@ -931,7 +945,8 @@ class TabContents : public PageNavigator, int error_code, const GURL& url, bool showing_repost_interstitial); - virtual void DocumentLoadedInFrame(); + virtual void DocumentLoadedInFrame(long long frame_id); + virtual void DidFinishLoad(long long frame_id); // RenderViewHostDelegate implementation. virtual RenderViewHostDelegate::View* GetViewDelegate(); @@ -1068,11 +1083,11 @@ class TabContents : public PageNavigator, // App extensions related methods: // Returns the first extension whose extent contains |url|. - Extension* GetExtensionContaining(const GURL& url); + const Extension* GetExtensionContaining(const GURL& url); // Resets app_icon_ and if |extension| is non-null creates a new // ImageLoadingTracker to load the extension's image. - void UpdateExtensionAppIcon(Extension* extension); + void UpdateExtensionAppIcon(const Extension* extension); // ImageLoadingTracker::Observer. virtual void OnImageLoaded(SkBitmap* image, ExtensionResource resource, @@ -1251,7 +1266,7 @@ class TabContents : public PageNavigator, // If non-null this tab is an app tab and this is the extension the tab was // created for. - Extension* extension_app_; + const Extension* extension_app_; // Icon for extension_app_ (if non-null) or extension_for_current_page_. SkBitmap extension_app_icon_; diff --git a/chrome/browser/tab_contents/tab_contents_delegate.cc b/chrome/browser/tab_contents/tab_contents_delegate.cc index 674fd75..25fd157 100644 --- a/chrome/browser/tab_contents/tab_contents_delegate.cc +++ b/chrome/browser/tab_contents/tab_contents_delegate.cc @@ -18,7 +18,7 @@ TabContents* TabContentsDelegate::GetConstrainingContents(TabContents* source) { return source; } -bool TabContentsDelegate::ShouldFocusConstrainedWindow(TabContents* source) { +bool TabContentsDelegate::ShouldFocusConstrainedWindow() { return true; } @@ -191,8 +191,13 @@ bool TabContentsDelegate::ShouldEnablePreferredSizeNotifications() { void TabContentsDelegate::UpdatePreferredSize(const gfx::Size& pref_size) { } -void TabContentsDelegate::OnSetSuggestResult(int32 page_id, - const std::string& result) { +void TabContentsDelegate::OnSetSuggestions( + int32 page_id, + const std::vector<std::string>& suggestions) { +} + +void TabContentsDelegate::OnInstantSupportDetermined(int32 page_id, + bool result) { } void TabContentsDelegate::ContentRestrictionsChanged(TabContents* source) { diff --git a/chrome/browser/tab_contents/tab_contents_delegate.h b/chrome/browser/tab_contents/tab_contents_delegate.h index dca24b4..63ef115 100644 --- a/chrome/browser/tab_contents/tab_contents_delegate.h +++ b/chrome/browser/tab_contents/tab_contents_delegate.h @@ -7,6 +7,7 @@ #pragma once #include <string> +#include <vector> #include "base/basictypes.h" #include "chrome/browser/automation/automation_resource_routing_delegate.h" @@ -104,7 +105,7 @@ class TabContentsDelegate : public AutomationResourceRoutingDelegate { virtual TabContents* GetConstrainingContents(TabContents* source); // Returns true if constrained windows should be focused. Default is true. - virtual bool ShouldFocusConstrainedWindow(TabContents* source); + virtual bool ShouldFocusConstrainedWindow(); // Invoked prior to the TabContents showing a constrained window. virtual void WillShowConstrainedWindow(TabContents* source); @@ -305,7 +306,11 @@ class TabContentsDelegate : public AutomationResourceRoutingDelegate { virtual void UpdatePreferredSize(const gfx::Size& pref_size); // Notifies the delegate that the page has a suggest result. - virtual void OnSetSuggestResult(int32 page_id, const std::string& result); + virtual void OnSetSuggestions(int32 page_id, + const std::vector<std::string>& result); + + // Notifies the delegate whether the page supports instant-style interaction. + virtual void OnInstantSupportDetermined(int32 page_id, bool result); // Notifies the delegate that the content restrictions for this tab has // changed. diff --git a/chrome/browser/tab_contents/tab_contents_view.cc b/chrome/browser/tab_contents/tab_contents_view.cc index 83eb7a4..47e71f2 100644 --- a/chrome/browser/tab_contents/tab_contents_view.cc +++ b/chrome/browser/tab_contents/tab_contents_view.cc @@ -113,10 +113,6 @@ bool TabContentsView::IsEventTracking() const { return false; } -bool TabContentsView::ShouldDrawDropShadow() { - return false; -} - TabContentsView::TabContentsView() : tab_contents_(NULL) {} void TabContentsView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) { diff --git a/chrome/browser/tab_contents/tab_contents_view.h b/chrome/browser/tab_contents/tab_contents_view.h index 4550781..c6b9a61 100644 --- a/chrome/browser/tab_contents/tab_contents_view.h +++ b/chrome/browser/tab_contents/tab_contents_view.h @@ -153,8 +153,6 @@ class TabContentsView : public RenderViewHostDelegate::View { virtual bool IsEventTracking() const; virtual void CloseTabAfterEventTracking() {} - virtual bool ShouldDrawDropShadow(); - protected: TabContentsView(); // Abstract interface. diff --git a/chrome/browser/tab_contents/tab_contents_view_gtk.cc b/chrome/browser/tab_contents/tab_contents_view_gtk.cc index 77f8aad..7ec34e4 100644 --- a/chrome/browser/tab_contents/tab_contents_view_gtk.cc +++ b/chrome/browser/tab_contents/tab_contents_view_gtk.cc @@ -247,19 +247,6 @@ void TabContentsViewGtk::RestoreFocus() { SetInitialFocus(); } -bool TabContentsViewGtk::ShouldDrawDropShadow() { - GtkWindow* window = GetTopLevelNativeWindow(); - if (!window) - return false; - - BrowserWindowGtk* browser_window = - BrowserWindowGtk::GetBrowserWindowForNativeWindow(window); - if (!browser_window) - return false; - - return browser_window->ShouldDrawInfobarDropShadowOnRenderView(); -} - void TabContentsViewGtk::SetFocusedWidget(GtkWidget* widget) { focus_store_.SetWidget(widget); } @@ -311,6 +298,17 @@ void TabContentsViewGtk::ShowContextMenu(const ContextMenuParams& params) { context_menu_->Popup(point); } +void TabContentsViewGtk::ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned) { + // We are not using external popup menus on Linux, they are rendered by + // WebKit. + NOTREACHED(); +} + // Render view DnD ------------------------------------------------------------- void TabContentsViewGtk::StartDragging(const WebDropData& drop_data, diff --git a/chrome/browser/tab_contents/tab_contents_view_gtk.h b/chrome/browser/tab_contents/tab_contents_view_gtk.h index 6f92a9b..9a016a1 100644 --- a/chrome/browser/tab_contents/tab_contents_view_gtk.h +++ b/chrome/browser/tab_contents/tab_contents_view_gtk.h @@ -8,6 +8,8 @@ #include <gtk/gtk.h> +#include <vector> + #include "app/gtk_signal.h" #include "base/scoped_ptr.h" #include "chrome/browser/gtk/focus_store_gtk.h" @@ -57,10 +59,15 @@ class TabContentsViewGtk : public TabContentsView, virtual void SetInitialFocus(); virtual void StoreFocus(); virtual void RestoreFocus(); - virtual bool ShouldDrawDropShadow(); // Backend implementation of RenderViewHostDelegate::View. virtual void ShowContextMenu(const ContextMenuParams& params); + virtual void ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned); virtual void StartDragging(const WebDropData& drop_data, WebKit::WebDragOperationsMask allowed_ops, const SkBitmap& image, diff --git a/chrome/browser/tab_contents/tab_contents_view_mac.h b/chrome/browser/tab_contents/tab_contents_view_mac.h index 63d29f3..47b88cf 100644 --- a/chrome/browser/tab_contents/tab_contents_view_mac.h +++ b/chrome/browser/tab_contents/tab_contents_view_mac.h @@ -9,6 +9,7 @@ #import <Cocoa/Cocoa.h> #include <string> +#include <vector> #include "base/scoped_nsobject.h" #include "chrome/browser/cocoa/base_view.h" @@ -76,6 +77,12 @@ class TabContentsViewMac : public TabContentsView, // Backend implementation of RenderViewHostDelegate::View. virtual void ShowContextMenu(const ContextMenuParams& params); + virtual void ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned); virtual void StartDragging(const WebDropData& drop_data, WebKit::WebDragOperationsMask allowed_operations, const SkBitmap& image, diff --git a/chrome/browser/tab_contents/tab_contents_view_mac.mm b/chrome/browser/tab_contents/tab_contents_view_mac.mm index eb550ab..34ce802 100644 --- a/chrome/browser/tab_contents/tab_contents_view_mac.mm +++ b/chrome/browser/tab_contents/tab_contents_view_mac.mm @@ -20,6 +20,7 @@ #include "chrome/browser/renderer_host/render_view_host_factory.h" #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/browser/renderer_host/render_widget_host_view_mac.h" +#include "chrome/browser/tab_contents/popup_menu_helper_mac.h" #include "chrome/browser/tab_contents/render_view_context_menu_mac.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" @@ -55,6 +56,7 @@ COMPILE_ASSERT_MATCHING_ENUM(DragOperationEvery); offset:(NSPoint)offset; - (void)cancelDeferredClose; - (void)closeTabAfterEvent; +- (void)viewDidBecomeFirstResponder:(NSNotification*)notification; @end // static @@ -109,6 +111,13 @@ RenderWidgetHostView* TabContentsViewMac::CreateViewForWidget( [cocoa_view_.get() addSubview:view_view positioned:NSWindowBelow relativeTo:nil]; + // For some reason known only to Cocoa, the autorecalculation of the key view + // loop set on the window doesn't set the next key view when the subview is + // added. On 10.6 things magically work fine; on 10.5 they fail + // <http://crbug.com/61493>. Digging into Cocoa key view loop code yielded + // madness; TODO(avi,rohit): look at this again and figure out what's really + // going on. + [cocoa_view_.get() setNextKeyView:view_view]; return view; } @@ -252,6 +261,19 @@ void TabContentsViewMac::ShowContextMenu(const ContextMenuParams& params) { menu.Init(); } +// Display a popup menu for WebKit using Cocoa widgets. +void TabContentsViewMac::ShowPopupMenu( + const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned) { + PopupMenuHelper popup_menu_helper(tab_contents()->render_view_host()); + popup_menu_helper.ShowPopupMenu(bounds, item_height, item_font_size, + selected_item, items, right_aligned); +} + RenderWidgetHostView* TabContentsViewMac::CreateNewWidgetInternal( int route_id, WebKit::WebPopupType popup_type) { @@ -263,9 +285,6 @@ RenderWidgetHostView* TabContentsViewMac::CreateNewWidgetInternal( static_cast<RenderWidgetHostViewMac*>(widget_view); [widget_view_mac->native_view() retain]; - // |widget_view_mac| needs to know how to position itself in our view. - widget_view_mac->set_parent_view(cocoa_view_); - return widget_view; } @@ -335,6 +354,12 @@ void TabContentsViewMac::Observe(NotificationType type, // by TabContentsController, so we can't just override -viewID method to // return it. view_id_util::SetID(self, VIEW_ID_TAB_CONTAINER); + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(viewDidBecomeFirstResponder:) + name:kViewDidBecomeFirstResponder + object:nil]; } return self; } @@ -347,6 +372,9 @@ void TabContentsViewMac::Observe(NotificationType type, // This probably isn't strictly necessary, but can't hurt. [self unregisterDraggedTypes]; + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; } @@ -471,4 +499,19 @@ void TabContentsViewMac::Observe(NotificationType type, tabContentsView_->CloseTab(); } +- (void)viewDidBecomeFirstResponder:(NSNotification*)notification { + NSView* view = [notification object]; + if (![[self subviews] containsObject:view]) + return; + + NSSelectionDirection direction = + [[[notification userInfo] objectForKey:kSelectionDirection] + unsignedIntegerValue]; + if (direction == NSDirectSelection) + return; + + [self tabContents]-> + FocusThroughTabTraversal(direction == NSSelectingPrevious); +} + @end diff --git a/chrome/browser/tab_contents/tab_specific_content_settings.cc b/chrome/browser/tab_contents/tab_specific_content_settings.cc index f0989a3..edafe6c 100644 --- a/chrome/browser/tab_contents/tab_specific_content_settings.cc +++ b/chrome/browser/tab_contents/tab_specific_content_settings.cc @@ -110,16 +110,15 @@ void TabSpecificContentSettings::OnCookieAccessed( void TabSpecificContentSettings::OnIndexedDBAccessed( const GURL& url, - const string16& name, const string16& description, bool blocked_by_policy) { if (blocked_by_policy) { blocked_local_shared_objects_.indexed_dbs()->AddIndexedDB( - url, name, description); + url, description); OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES, std::string()); }else { allowed_local_shared_objects_.indexed_dbs()->AddIndexedDB( - url, name, description); + url, description); OnContentAccessed(CONTENT_SETTINGS_TYPE_COOKIES); } } diff --git a/chrome/browser/tab_contents/tab_specific_content_settings.h b/chrome/browser/tab_contents/tab_specific_content_settings.h index 7f0aac7..5cc9213 100644 --- a/chrome/browser/tab_contents/tab_specific_content_settings.h +++ b/chrome/browser/tab_contents/tab_specific_content_settings.h @@ -95,7 +95,6 @@ class TabSpecificContentSettings const std::string& cookie_line, bool blocked_by_policy); virtual void OnIndexedDBAccessed(const GURL& url, - const string16& name, const string16& description, bool blocked_by_policy); virtual void OnLocalStorageAccessed(const GURL& url, diff --git a/chrome/browser/tab_contents/view_source_uitest.cc b/chrome/browser/tab_contents/view_source_uitest.cc index 654b747..3e22b70 100644 --- a/chrome/browser/tab_contents/view_source_uitest.cc +++ b/chrome/browser/tab_contents/view_source_uitest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/common/url_constants.h" #include "chrome/test/automation/browser_proxy.h" #include "chrome/test/automation/tab_proxy.h" diff --git a/chrome/browser/tab_contents/web_drag_dest_gtk.cc b/chrome/browser/tab_contents/web_drag_dest_gtk.cc index c0b3d9a..2c38987 100644 --- a/chrome/browser/tab_contents/web_drag_dest_gtk.cc +++ b/chrome/browser/tab_contents/web_drag_dest_gtk.cc @@ -14,6 +14,7 @@ #include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/url_constants.h" #include "net/base/net_util.h" using WebKit::WebDragOperation; @@ -169,8 +170,10 @@ void WebDragDestGtk::OnDragDataReceived( // dragging files. To avoid exposing file system paths to web content, // file URLs are never set as the URL content for the drop. // TODO(estade): Can the filenames have a non-UTF8 encoding? + GURL url(*uri_iter); FilePath file_path; - if (net::FileURLToFilePath(GURL(*uri_iter), &file_path)) { + if (url.SchemeIs(chrome::kFileScheme) && + net::FileURLToFilePath(url, &file_path)) { drop_data_->filenames.push_back(UTF8ToUTF16(file_path.value())); // This is a hack. Some file managers also populate text/plain with // a file URL when dragging files, so we clear it to avoid exposing @@ -178,7 +181,7 @@ void WebDragDestGtk::OnDragDataReceived( drop_data_->plain_text.clear(); } else if (!drop_data_->url.is_valid()) { // Also set the first non-file URL as the URL content for the drop. - drop_data_->url = GURL(*uri_iter); + drop_data_->url = url; } } g_strfreev(uris); diff --git a/chrome/browser/tab_restore_uitest.cc b/chrome/browser/tab_restore_uitest.cc index 4e48d5a..eba8f50 100644 --- a/chrome/browser/tab_restore_uitest.cc +++ b/chrome/browser/tab_restore_uitest.cc @@ -5,7 +5,7 @@ #include "base/basictypes.h" #include "base/command_line.h" #include "base/file_path.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" @@ -208,7 +208,7 @@ TEST_F(TabRestoreUITest, MiddleTab) { // Close a tab, switch windows, then restore the tab. The tab should be in its // original window and position, and active. -// This test is flaky. See http://crbug.com/14132 and 11213. +// This test is flaky. See http://crbug.com/54894 TEST_F(TabRestoreUITest, FLAKY_RestoreToDifferentWindow) { scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0)); ASSERT_TRUE(browser_proxy.get()); diff --git a/chrome/browser/tabs/default_tab_handler.cc b/chrome/browser/tabs/default_tab_handler.cc index e3968b7..980913b 100644 --- a/chrome/browser/tabs/default_tab_handler.cc +++ b/chrome/browser/tabs/default_tab_handler.cc @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/metrics/nacl_histogram.h" #include "chrome/browser/tabs/default_tab_handler.h" - #include "chrome/browser/browser.h" #include "chrome/browser/tabs/tab_strip_model.h" @@ -13,7 +13,8 @@ DefaultTabHandler::DefaultTabHandler(TabHandlerDelegate* delegate) : delegate_(delegate), ALLOW_THIS_IN_INITIALIZER_LIST( - model_(new TabStripModel(this, delegate->GetProfile()))) { + model_(new TabStripModel(this, delegate->GetProfile()))) { + UmaNaclHistogramEnumeration(FIRST_TAB_NACL_BASELINE); model_->AddObserver(this); } @@ -34,6 +35,7 @@ TabStripModel* DefaultTabHandler::GetTabStripModel() const { // DefaultTabHandler, TabStripModelDelegate implementation: TabContents* DefaultTabHandler::AddBlankTab(bool foreground) { + UmaNaclHistogramEnumeration(NEW_TAB_NACL_BASELINE); return delegate_->AsBrowser()->AddBlankTab(foreground); } diff --git a/chrome/browser/tabs/pinned_tab_codec.cc b/chrome/browser/tabs/pinned_tab_codec.cc index 7b9e0b3..f6d2a08 100644 --- a/chrome/browser/tabs/pinned_tab_codec.cc +++ b/chrome/browser/tabs/pinned_tab_codec.cc @@ -43,7 +43,7 @@ static void EncodePinnedTab(TabStripModel* model, TabContents* tab_contents = model->GetTabContentsAt(index); if (model->IsAppTab(index)) { - Extension* extension = tab_contents->extension_app(); + const Extension* extension = tab_contents->extension_app(); DCHECK(extension); value->SetString(kAppID, extension->id()); // For apps we use the launch url. We do this for the following reason: diff --git a/chrome/browser/tabs/tab_strip_model.cc b/chrome/browser/tabs/tab_strip_model.cc index bc9c796..5862727 100644 --- a/chrome/browser/tabs/tab_strip_model.cc +++ b/chrome/browser/tabs/tab_strip_model.cc @@ -442,10 +442,10 @@ void TabStripModel::SetTabPinned(int index, bool pinned) { contents_data_[index]->pinned = pinned; if (pinned && index != non_mini_tab_index) { MoveTabContentsAtImpl(index, non_mini_tab_index, false); - return; // Don't send TabPinnedStateChanged notification. + index = non_mini_tab_index; } else if (!pinned && index + 1 != non_mini_tab_index) { MoveTabContentsAtImpl(index, non_mini_tab_index - 1, false); - return; // Don't send TabPinnedStateChanged notification. + index = non_mini_tab_index - 1; } FOR_EACH_OBSERVER(TabStripModelObserver, observers_, @@ -756,7 +756,7 @@ void TabStripModel::Observe(NotificationType type, } case NotificationType::EXTENSION_UNLOADED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); // Iterate backwards as we may remove items while iterating. for (int i = count() - 1; i >= 0; i--) { TabContents* contents = GetTabContentsAt(i); @@ -878,7 +878,6 @@ TabContents* TabStripModel::GetContentsAt(int index) const { void TabStripModel::ChangeSelectedContentsFrom( TabContents* old_contents, int to_index, bool user_gesture) { - DCHECK(ContainsIndex(to_index)); TabContents* new_contents = GetContentsAt(to_index); if (old_contents == new_contents) return; diff --git a/chrome/browser/tabs/tab_strip_model_observer.h b/chrome/browser/tabs/tab_strip_model_observer.h index 67a7ea3..d39e436 100644 --- a/chrome/browser/tabs/tab_strip_model_observer.h +++ b/chrome/browser/tabs/tab_strip_model_observer.h @@ -91,14 +91,11 @@ class TabStripModelObserver { TabContents* new_contents, int index); - // Invoked when the pinned state of a tab changes. This is not invoked if the - // tab ends up moving as a result of the mini state changing. - // See note in TabMiniStateChanged as to how this relates to - // TabMiniStateChanged. + // Invoked when the pinned state of a tab changes. See note in + // TabMiniStateChanged as to how this relates to TabMiniStateChanged. virtual void TabPinnedStateChanged(TabContents* contents, int index); - // Invoked if the mini state of a tab changes. This is not invoked if the - // tab ends up moving as a result of the mini state changing. + // Invoked if the mini state of a tab changes. // NOTE: this is sent when the pinned state of a non-app tab changes and is // sent in addition to TabPinnedStateChanged. UI code typically need not care // about TabPinnedStateChanged, but instead this. diff --git a/chrome/browser/tabs/tab_strip_model_unittest.cc b/chrome/browser/tabs/tab_strip_model_unittest.cc index 976509f..81847e9 100644 --- a/chrome/browser/tabs/tab_strip_model_unittest.cc +++ b/chrome/browser/tabs/tab_strip_model_unittest.cc @@ -1511,12 +1511,13 @@ TEST_F(TabStripModelTest, Apps) { #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - Extension extension_app(path); - extension_app.mutable_static_data_->launch_web_url = "http://www.google.com"; + scoped_refptr<Extension> extension_app(new Extension(path, + Extension::INVALID)); + extension_app->launch_web_url_ = "http://www.google.com"; TabContents* contents1 = CreateTabContents(); - contents1->SetExtensionApp(&extension_app); + contents1->SetExtensionApp(extension_app); TabContents* contents2 = CreateTabContents(); - contents2->SetExtensionApp(&extension_app); + contents2->SetExtensionApp(extension_app); TabContents* contents3 = CreateTabContents(); SetID(contents1, 1); @@ -1684,12 +1685,15 @@ TEST_F(TabStripModelTest, Pinning) { { tabstrip.SetTabPinned(2, true); - // The pinning should have resulted in a move. - ASSERT_EQ(1, observer.GetStateCount()); + // The pinning should have resulted in a move and a pinned notification. + ASSERT_EQ(2, observer.GetStateCount()); State state(contents3, 0, MockTabStripModelObserver::MOVE); state.src_index = 2; EXPECT_TRUE(observer.StateEquals(0, state)); + state = State(contents3, 0, MockTabStripModelObserver::PINNED); + EXPECT_TRUE(observer.StateEquals(1, state)); + // And verify the state. EXPECT_EQ("3p 1 2", GetPinnedState(tabstrip)); @@ -1728,11 +1732,14 @@ TEST_F(TabStripModelTest, Pinning) { { tabstrip.SetTabPinned(0, false); - ASSERT_EQ(1, observer.GetStateCount()); + ASSERT_EQ(2, observer.GetStateCount()); State state(contents3, 1, MockTabStripModelObserver::MOVE); state.src_index = 0; EXPECT_TRUE(observer.StateEquals(0, state)); + state = State(contents3, 1, MockTabStripModelObserver::PINNED); + EXPECT_TRUE(observer.StateEquals(1, state)); + // And verify the state. EXPECT_EQ("1p 3 2", GetPinnedState(tabstrip)); diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc index 6544f2f..9e3c181 100644 --- a/chrome/browser/task_manager/task_manager_browsertest.cc +++ b/chrome/browser/task_manager/task_manager_browsertest.cc @@ -6,10 +6,16 @@ #include "app/l10n_util.h" #include "base/file_path.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/browser.h" +#include "chrome/browser/browser_navigator.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/extensions/crashed_extension_infobar.h" #include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/notifications/desktop_notification_service.h" +#include "chrome/browser/notifications/notification_test_util.h" +#include "chrome/browser/notifications/notification_ui_manager.h" #include "chrome/browser/tab_contents/infobar_delegate.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tabs/tab_strip_model.h" @@ -102,10 +108,7 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeTabContentsChanges) { // Open a new tab and make sure we notice that. GURL url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory), FilePath(kTitle1File))); - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - params.index = 0; - browser()->AddTabWithURL(¶ms); - EXPECT_EQ(browser(), params.target); + AddTabAtIndex(0, url, PageTransition::TYPED); WaitForResourceChange(3); // Close the tab and verify that we notice. @@ -117,11 +120,13 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeTabContentsChanges) { #if defined(OS_WIN) // http://crbug.com/31663 -#define NoticeExtensionChanges DISABLED_NoticeExtensionChanges +#define MAYBE_NoticeExtensionChanges DISABLED_NoticeExtensionChanges +#else +// Flaky test bug filed in http://crbug.com/51701 +#define MAYBE_NoticeExtensionChanges FLAKY_NoticeExtensionChanges #endif -// Flaky test bug filed in http://crbug.com/51701 -IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, FLAKY_NoticeExtensionChanges) { +IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, MAYBE_NoticeExtensionChanges) { EXPECT_EQ(0, model()->ResourceCount()); // Show the task manager. This populates the model, and helps with debugging @@ -138,6 +143,39 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, FLAKY_NoticeExtensionChanges) { WaitForResourceChange(3); } +IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeNotificationChanges) { + EXPECT_EQ(0, model()->ResourceCount()); + + // Show the task manager. + browser()->window()->ShowTaskManager(); + // Expect to see the browser and the New Tab Page renderer. + EXPECT_EQ(2, model()->ResourceCount()); + + // Show a notification. + NotificationUIManager* notifications = + g_browser_process->notification_ui_manager(); + + string16 content = DesktopNotificationService::CreateDataUrl( + GURL(), ASCIIToUTF16("Hello World!"), string16(), + WebKit::WebTextDirectionDefault); + + scoped_refptr<NotificationDelegate> del1(new MockNotificationDelegate("n1")); + Notification n1( + GURL(), GURL(content), ASCIIToUTF16("Test 1"), string16(), del1.get()); + scoped_refptr<NotificationDelegate> del2(new MockNotificationDelegate("n2")); + Notification n2( + GURL(), GURL(content), ASCIIToUTF16("Test 2"), string16(), del2.get()); + + notifications->Add(n1, browser()->profile()); + WaitForResourceChange(3); + notifications->Add(n2, browser()->profile()); + WaitForResourceChange(4); + notifications->Cancel(n1); + WaitForResourceChange(3); + notifications->Cancel(n2); + WaitForResourceChange(2); +} + // Times out on Vista; disabled to keep tests fast. http://crbug.com/44991 #if defined(OS_WIN) #define KillExtension DISABLED_KillExtension @@ -249,9 +287,7 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, // Open a new tab and make sure we notice that. GURL url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory), FilePath(kTitle1File))); - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - params.index = 0; - browser()->AddTabWithURL(¶ms); + AddTabAtIndex(0, url, PageTransition::TYPED); WaitForResourceChange(3); // Check that we get some value for the cache columns. diff --git a/chrome/browser/task_manager/task_manager_resource_providers.cc b/chrome/browser/task_manager/task_manager_resource_providers.cc index d1b6b7f..0c3d5d8 100644 --- a/chrome/browser/task_manager/task_manager_resource_providers.cc +++ b/chrome/browser/task_manager/task_manager_resource_providers.cc @@ -15,7 +15,7 @@ #include "base/stl_util-inl.h" #include "base/string_util.h" #include "base/thread.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_child_process_host.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" @@ -321,7 +321,7 @@ void TaskManagerTabContentsResourceProvider::Observe(NotificationType type, SkBitmap* TaskManagerChildProcessResource::default_icon_ = NULL; TaskManagerChildProcessResource::TaskManagerChildProcessResource( - ChildProcessInfo child_proc) + const ChildProcessInfo& child_proc) : child_process_(child_proc), title_(), network_usage_support_(false) { @@ -465,7 +465,7 @@ void TaskManagerChildProcessResourceProvider::Observe( } void TaskManagerChildProcessResourceProvider::Add( - ChildProcessInfo child_process_info) { + const ChildProcessInfo& child_process_info) { if (!updating_) return; std::map<ChildProcessInfo, TaskManagerChildProcessResource*>:: @@ -481,7 +481,7 @@ void TaskManagerChildProcessResourceProvider::Add( } void TaskManagerChildProcessResourceProvider::Remove( - ChildProcessInfo child_process_info) { + const ChildProcessInfo& child_process_info) { if (!updating_) return; std::map<ChildProcessInfo, TaskManagerChildProcessResource*> @@ -509,7 +509,7 @@ void TaskManagerChildProcessResourceProvider::Remove( } void TaskManagerChildProcessResourceProvider::AddToTaskManager( - ChildProcessInfo child_process_info) { + const ChildProcessInfo& child_process_info) { TaskManagerChildProcessResource* resource = new TaskManagerChildProcessResource(child_process_info); resources_[child_process_info] = resource; diff --git a/chrome/browser/task_manager/task_manager_resource_providers.h b/chrome/browser/task_manager/task_manager_resource_providers.h index 44e24b6..5145a7e 100644 --- a/chrome/browser/task_manager/task_manager_resource_providers.h +++ b/chrome/browser/task_manager/task_manager_resource_providers.h @@ -116,7 +116,7 @@ class TaskManagerTabContentsResourceProvider class TaskManagerChildProcessResource : public TaskManager::Resource { public: - explicit TaskManagerChildProcessResource(ChildProcessInfo child_proc); + explicit TaskManagerChildProcessResource(const ChildProcessInfo& child_proc); ~TaskManagerChildProcessResource(); // TaskManagerResource methods: @@ -183,10 +183,10 @@ class TaskManagerChildProcessResourceProvider private: virtual ~TaskManagerChildProcessResourceProvider(); - void Add(ChildProcessInfo child_process_info); - void Remove(ChildProcessInfo child_process_info); + void Add(const ChildProcessInfo& child_process_info); + void Remove(const ChildProcessInfo& child_process_info); - void AddToTaskManager(ChildProcessInfo child_process_info); + void AddToTaskManager(const ChildProcessInfo& child_process_info); TaskManager* task_manager_; diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc index cd7c2f9..62f09ef 100644 --- a/chrome/browser/themes/browser_theme_pack.cc +++ b/chrome/browser/themes/browser_theme_pack.cc @@ -8,6 +8,7 @@ #include "base/data_pack.h" #include "base/stl_util-inl.h" #include "base/string_util.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/browser_thread.h" @@ -326,7 +327,8 @@ BrowserThemePack::~BrowserThemePack() { } // static -BrowserThemePack* BrowserThemePack::BuildFromExtension(Extension* extension) { +BrowserThemePack* BrowserThemePack::BuildFromExtension( + const Extension* extension) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(extension); DCHECK(extension->is_theme()); @@ -367,7 +369,7 @@ BrowserThemePack* BrowserThemePack::BuildFromExtension(Extension* extension) { scoped_refptr<BrowserThemePack> BrowserThemePack::BuildFromDataPack( FilePath path, const std::string& expected_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - scoped_refptr<BrowserThemePack> pack = new BrowserThemePack; + scoped_refptr<BrowserThemePack> pack(new BrowserThemePack); pack->data_pack_.reset(new base::DataPack); if (!pack->data_pack_->Load(path)) { @@ -576,7 +578,7 @@ BrowserThemePack::BrowserThemePack() source_images_(NULL) { } -void BrowserThemePack::BuildHeader(Extension* extension) { +void BrowserThemePack::BuildHeader(const Extension* extension) { header_ = new BrowserThemePackHeader; header_->version = kThemePackVersion; @@ -841,6 +843,10 @@ void BrowserThemePack::BuildSourceImagesArray(const FilePathMap& file_paths) { bool BrowserThemePack::LoadRawBitmapsTo( const FilePathMap& file_paths, ImageCache* raw_bitmaps) { + // Themes should be loaded on the file thread, not the UI thread. + // http://crbug.com/61838 + base::ThreadRestrictions::ScopedAllowIO allow_io; + for (FilePathMap::const_iterator it = file_paths.begin(); it != file_paths.end(); ++it) { scoped_refptr<RefCountedMemory> raw_data(ReadFileData(it->second)); @@ -934,7 +940,7 @@ void BrowserThemePack::GenerateFrameImages(ImageCache* bitmaps) const { } void BrowserThemePack::GenerateTintedButtons( - color_utils::HSL button_tint, + const color_utils::HSL& button_tint, ImageCache* processed_bitmaps) const { if (button_tint.h != -1 || button_tint.s != -1 || button_tint.l != -1) { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); diff --git a/chrome/browser/themes/browser_theme_pack.h b/chrome/browser/themes/browser_theme_pack.h index 85d27af..3771462 100644 --- a/chrome/browser/themes/browser_theme_pack.h +++ b/chrome/browser/themes/browser_theme_pack.h @@ -13,6 +13,7 @@ #include "base/scoped_ptr.h" #include "base/ref_counted.h" #include "gfx/color_utils.h" +#include "chrome/browser/browser_thread.h" #include "chrome/common/extensions/extension.h" namespace base { @@ -35,14 +36,17 @@ class RefCountedMemory; // UI thread that consumes a BrowserThemePack. There is no locking; thread // safety between the writing thread and the UI thread is ensured by having the // data be immutable. -class BrowserThemePack : public base::RefCountedThreadSafe<BrowserThemePack> { +// +// BrowserThemePacks are always deleted on the file thread because in the +// common case, they are backed by mmapped data and the unmmapping operation +// will trip our IO on the UI thread detector. +class BrowserThemePack : public base::RefCountedThreadSafe< + BrowserThemePack, BrowserThread::DeleteOnFileThread> { public: - ~BrowserThemePack(); - // Builds the theme pack from all data from |extension|. This is often done // on a separate thread as it takes so long. This can fail and return NULL in // the case where the theme has invalid data. - static BrowserThemePack* BuildFromExtension(Extension* extension); + static BrowserThemePack* BuildFromExtension(const Extension* extension); // Builds the theme pack from a previously performed WriteToDisk(). This // operation should be relatively fast, as it should be an mmap() and some @@ -79,6 +83,8 @@ class BrowserThemePack : public base::RefCountedThreadSafe<BrowserThemePack> { bool HasCustomImage(int id) const; private: + friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>; + friend class DeleteTask<BrowserThemePack>; friend class BrowserThemePackTest; // Cached images. We cache all retrieved and generated bitmaps and keep @@ -98,8 +104,10 @@ class BrowserThemePack : public base::RefCountedThreadSafe<BrowserThemePack> { // Default. Everything is empty. BrowserThemePack(); + virtual ~BrowserThemePack(); + // Builds a header ready to write to disk. - void BuildHeader(Extension* extension); + void BuildHeader(const Extension* extension); // Transforms the JSON tint values into their final versions in the |tints_| // array. @@ -136,7 +144,7 @@ class BrowserThemePack : public base::RefCountedThreadSafe<BrowserThemePack> { // Generates button images tinted with |button_tint| and places them in // processed_bitmaps. - void GenerateTintedButtons(color_utils::HSL button_tint, + void GenerateTintedButtons(const color_utils::HSL& button_tint, ImageCache* processed_bitmaps) const; // Generates the semi-transparent tab background images, putting the results diff --git a/chrome/browser/themes/browser_theme_pack_unittest.cc b/chrome/browser/themes/browser_theme_pack_unittest.cc index 87fc2c7..5d4be22 100644 --- a/chrome/browser/themes/browser_theme_pack_unittest.cc +++ b/chrome/browser/themes/browser_theme_pack_unittest.cc @@ -397,8 +397,6 @@ TEST_F(BrowserThemePackTest, CanBuildAndReadPack) { // Part 1: Build the pack from an extension. { FilePath star_gazing_path = GetStarGazingPath(); - Extension extension(star_gazing_path); - FilePath manifest_path = star_gazing_path.AppendASCII("manifest.json"); std::string error; @@ -407,11 +405,13 @@ TEST_F(BrowserThemePackTest, CanBuildAndReadPack) { static_cast<DictionaryValue*>(serializer.Deserialize(NULL, &error))); EXPECT_EQ("", error); ASSERT_TRUE(valid_value.get()); - ASSERT_TRUE(extension.InitFromValue(*valid_value, true, &error)); + scoped_refptr<Extension> extension(Extension::Create( + star_gazing_path, Extension::INVALID, *valid_value, true, &error)); + ASSERT_TRUE(extension.get()); ASSERT_EQ("", error); - scoped_refptr<BrowserThemePack> pack = - BrowserThemePack::BuildFromExtension(&extension); + scoped_refptr<BrowserThemePack> pack( + BrowserThemePack::BuildFromExtension(extension.get())); ASSERT_TRUE(pack.get()); ASSERT_TRUE(pack->WriteToDisk(file)); VerifyStarGazing(pack.get()); diff --git a/chrome/browser/themes/browser_theme_provider.cc b/chrome/browser/themes/browser_theme_provider.cc index 58a6c31..ae40eef 100644 --- a/chrome/browser/themes/browser_theme_provider.cc +++ b/chrome/browser/themes/browser_theme_provider.cc @@ -288,7 +288,7 @@ RefCountedMemory* BrowserThemeProvider::GetRawData(int id) const { return data; } -void BrowserThemeProvider::SetTheme(Extension* extension) { +void BrowserThemeProvider::SetTheme(const Extension* extension) { // Clear our image cache. FreePlatformCaches(); @@ -557,7 +557,8 @@ void BrowserThemeProvider::LoadThemePrefs() { // theme is being migrated. ExtensionsService* service = profile_->GetExtensionsService(); if (service) { - Extension* extension = service->GetExtensionById(current_id, false); + const Extension* extension = + service->GetExtensionById(current_id, false); if (extension) { DLOG(ERROR) << "Migrating theme"; BuildFromExtension(extension); @@ -573,13 +574,13 @@ void BrowserThemeProvider::LoadThemePrefs() { } } -void BrowserThemeProvider::NotifyThemeChanged(Extension* extension) { +void BrowserThemeProvider::NotifyThemeChanged(const Extension* extension) { VLOG(1) << "Sending BROWSER_THEME_CHANGED"; // Redraw! NotificationService* service = NotificationService::current(); service->Notify(NotificationType::BROWSER_THEME_CHANGED, Source<BrowserThemeProvider>(this), - Details<Extension>(extension)); + Details<const Extension>(extension)); #if defined(OS_MACOSX) NotifyPlatformThemeChanged(); #endif // OS_MACOSX @@ -600,9 +601,9 @@ void BrowserThemeProvider::SaveThemeID(const std::string& id) { profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, id); } -void BrowserThemeProvider::BuildFromExtension(Extension* extension) { - scoped_refptr<BrowserThemePack> pack = - BrowserThemePack::BuildFromExtension(extension); +void BrowserThemeProvider::BuildFromExtension(const Extension* extension) { + scoped_refptr<BrowserThemePack> pack( + BrowserThemePack::BuildFromExtension(extension)); if (!pack.get()) { // TODO(erg): We've failed to install the theme; perhaps we should tell the // user? http://crbug.com/34780 diff --git a/chrome/browser/themes/browser_theme_provider.h b/chrome/browser/themes/browser_theme_provider.h index f359072..a47f39c 100644 --- a/chrome/browser/themes/browser_theme_provider.h +++ b/chrome/browser/themes/browser_theme_provider.h @@ -152,7 +152,7 @@ class BrowserThemeProvider : public NonThreadSafe, #endif // Set the current theme to the theme defined in |extension|. - virtual void SetTheme(Extension* extension); + virtual void SetTheme(const Extension* extension); // Reset the theme to default. virtual void UseDefaultTheme(); @@ -221,7 +221,7 @@ class BrowserThemeProvider : public NonThreadSafe, // Let all the browser views know that themes have changed. // extension is NULL iff the theme is being set to the // default/system theme. - virtual void NotifyThemeChanged(Extension* extension); + virtual void NotifyThemeChanged(const Extension* extension); #if defined(OS_MACOSX) // Let all the browser views know that themes have changed in a platform way. @@ -245,7 +245,7 @@ class BrowserThemeProvider : public NonThreadSafe, // Implementation of SetTheme() (and the fallback from LoadThemePrefs() in // case we don't have a theme pack). - void BuildFromExtension(Extension* extension); + void BuildFromExtension(const Extension* extension); // Remove preference values for themes that are no longer in use. void RemoveUnusedThemes(); diff --git a/chrome/browser/toolbar_model.cc b/chrome/browser/toolbar_model.cc index e8cdaaf..fb6d050 100644 --- a/chrome/browser/toolbar_model.cc +++ b/chrome/browser/toolbar_model.cc @@ -5,6 +5,7 @@ #include "chrome/browser/toolbar_model.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/autocomplete/autocomplete.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/browser.h" #include "chrome/browser/cert_store.h" diff --git a/chrome/browser/translate/options_menu_model.cc b/chrome/browser/translate/options_menu_model.cc index bb5b157..975140e 100644 --- a/chrome/browser/translate/options_menu_model.cc +++ b/chrome/browser/translate/options_menu_model.cc @@ -6,7 +6,7 @@ #include "app/l10n_util.h" #include "base/metrics/histogram.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" diff --git a/chrome/browser/translate/translate_manager_unittest.cc b/chrome/browser/translate/translate_manager_unittest.cc index f578b3f..3055ed6 100644 --- a/chrome/browser/translate/translate_manager_unittest.cc +++ b/chrome/browser/translate/translate_manager_unittest.cc @@ -5,7 +5,7 @@ #include "chrome/browser/renderer_host/test/test_render_view_host.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prefs/pref_change_registrar.h" #include "chrome/browser/renderer_host/mock_render_process_host.h" diff --git a/chrome/browser/browser.cc b/chrome/browser/ui/browser.cc index aaded8a..b1bdddf 100644 --- a/chrome/browser/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -21,9 +21,10 @@ #include "base/path_service.h" #include "base/string_util.h" #include "base/thread.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "gfx/point.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/autofill/autofill_manager.h" #if defined(OS_WIN) #include "chrome/browser/autofill/autofill_ie_toolbar_import_win.h" @@ -31,6 +32,7 @@ #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/bookmarks/bookmark_utils.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_shutdown.h" #include "chrome/browser/browser_window.h" @@ -140,8 +142,13 @@ using base::TimeDelta; static const int kUIUpdateCoalescingTimeMS = 200; // The URL to be loaded to display Help. +#if defined(OS_CHROMEOS) +static const char* const kHelpContentUrl = + "chrome-extension://nifaohjgppdbmalmmgkmfdlodaggnbpe/main.html"; +#else static const char* const kHelpContentUrl = "http://www.google.com/support/chrome/"; +#endif // The URL to be loaded to display the "Report a broken page" form. static const std::string kBrokenPageUrl = @@ -150,6 +157,9 @@ static const std::string kBrokenPageUrl = static const std::string kHashMark = "#"; +// The URL for the privacy dashboard. +static const char kPrivacyDashboardUrl[] = "https://www.google.com/dashboard"; + /////////////////////////////////////////////////////////////////////////////// namespace { @@ -168,22 +178,6 @@ bool TabHasUnloadListener(TabContents* contents) { !contents->render_view_host()->SuddenTerminationAllowed(); } -// Returns true if two URLs are equal ignoring their ref (hash fragment). -bool CompareURLsIgnoreRef(const GURL& url, const GURL& other) { - if (url == other) - return true; - // If neither has a ref than there is no point in stripping the refs and - // the URLs are different since the comparison failed in the previous if - // statement. - if (!url.has_ref() && !other.has_ref()) - return false; - url_canon::Replacements<char> replacements; - replacements.ClearRef(); - GURL url_no_ref = url.ReplaceComponents(replacements); - GURL other_no_ref = other.ReplaceComponents(replacements); - return url_no_ref == other_no_ref; -} - } // namespace extern bool g_log_bug53991; @@ -233,6 +227,8 @@ Browser::Browser(Type type, Profile* profile) PrefService* local_state = g_browser_process->local_state(); if (local_state) printing_enabled_.Init(prefs::kPrintingEnabled, local_state, this); + dev_tools_disabled_.Init(prefs::kDevToolsDisabled, + profile_->GetPrefs(), this); InitCommandState(); BrowserList::AddBrowser(this); @@ -261,9 +257,9 @@ Browser::Browser(Type type, Profile* profile) } Browser::~Browser() { - LOG_IF(INFO, g_log_bug53991) << - "~Browser: " << profile_->IsOffTheRecord() << - "; stillActive=" << BrowserList::IsOffTheRecordSessionActive(); + VLOG_IF(1, g_log_bug53991) << "~Browser: " << profile_->IsOffTheRecord() + << "; stillActive=" + << BrowserList::IsOffTheRecordSessionActive(); if (profile_->GetProfileSyncService()) profile_->GetProfileSyncService()->RemoveObserver(this); @@ -337,7 +333,7 @@ Browser* Browser::CreateForType(Type type, Profile* profile) { // static Browser* Browser::CreateForApp(const std::string& app_name, - Extension* extension, + const Extension* extension, Profile* profile, bool is_panel) { Browser::Type type = TYPE_APP; @@ -380,13 +376,19 @@ void Browser::CreateBrowserWindow() { window_ = BrowserWindow::CreateBrowserWindow(this); #if defined(OS_WIN) - // Set the app user model id for this application to that of the application - // name. See http://crbug.com/7028. - win_util::SetAppIdForWindow( - type_ & TYPE_APP ? - ShellIntegration::GetAppId(UTF8ToWide(app_name_), profile_->GetPath()) : - ShellIntegration::GetChromiumAppId(profile_->GetPath()), - window()->GetNativeHandle()); + { + // TODO: This might hit the disk + // http://code.google.com/p/chromium/issues/detail?id=61638 + base::ThreadRestrictions::ScopedAllowIO allow_io; + + // Set the app user model id for this application to that of the application + // name. See http://crbug.com/7028. + win_util::SetAppIdForWindow( + type_ & TYPE_APP ? + ShellIntegration::GetAppId(UTF8ToWide(app_name_), profile_->GetPath()) : + ShellIntegration::GetChromiumAppId(profile_->GetPath()), + window()->GetNativeHandle()); + } #endif NotificationService::current()->Notify( @@ -491,7 +493,8 @@ TabContents* Browser::OpenApplication(Profile* profile, // If the extension with |app_id| could't be found, most likely because it // was uninstalled. - Extension* extension = extensions_service->GetExtensionById(app_id, false); + const Extension* extension = + extensions_service->GetExtensionById(app_id, false); if (!extension) return NULL; @@ -502,7 +505,7 @@ TabContents* Browser::OpenApplication(Profile* profile, // static TabContents* Browser::OpenApplication( Profile* profile, - Extension* extension, + const Extension* extension, extension_misc::LaunchContainer container, TabContents* existing_tab) { TabContents* tab = NULL; @@ -530,7 +533,7 @@ TabContents* Browser::OpenApplication( // static TabContents* Browser::OpenApplicationWindow( Profile* profile, - Extension* extension, + const Extension* extension, extension_misc::LaunchContainer container, const GURL& url_input) { GURL url; @@ -581,7 +584,7 @@ TabContents* Browser::OpenApplicationWindow(Profile* profile, GURL& url) { // static TabContents* Browser::OpenApplicationTab(Profile* profile, - Extension* extension, + const Extension* extension, TabContents* existing_tab) { Browser* browser = BrowserList::GetLastActiveWithProfile(profile); TabContents* contents = NULL; @@ -601,9 +604,9 @@ TabContents* Browser::OpenApplicationTab(Profile* profile, // TODO(erikkay): START_PAGE doesn't seem like the right transition in all // cases. - AddTabWithURLParams params(extension->GetFullLaunchURL(), - PageTransition::START_PAGE); - params.add_types = add_type; + browser::NavigateParams params(browser, extension->GetFullLaunchURL(), + PageTransition::START_PAGE); + params.tabstrip_add_types = add_type; // Launch the application in the existing TabContents, if it was supplied. if (existing_tab) { @@ -612,14 +615,18 @@ TabContents* Browser::OpenApplicationTab(Profile* profile, existing_tab->OpenURL(extension->GetFullLaunchURL(), existing_tab->GetURL(), CURRENT_TAB, PageTransition::LINK); - if (params.add_types & TabStripModel::ADD_PINNED) + if (params.tabstrip_add_types & TabStripModel::ADD_PINNED) { model->SetTabPinned(tab_index, true); - if (params.add_types & TabStripModel::ADD_SELECTED) + tab_index = model->GetIndexOfTabContents(existing_tab); + } + if (params.tabstrip_add_types & TabStripModel::ADD_SELECTED) model->SelectTabContentsAt(tab_index, true); contents = existing_tab; } else { - contents = browser->AddTabWithURL(¶ms); + params.disposition = NEW_FOREGROUND_TAB; + browser::Navigate(¶ms); + contents = params.target_contents; } if (launch_type == ExtensionPrefs::LAUNCH_FULLSCREEN) @@ -907,57 +914,10 @@ int Browser::GetIndexForInsertionDuringRestore(int relative_index) { TabContents* Browser::AddSelectedTabWithURL(const GURL& url, PageTransition::Type transition) { - AddTabWithURLParams params(url, transition); - return AddTabWithURL(¶ms); -} - -Browser::AddTabWithURLParams::AddTabWithURLParams( - const GURL& a_url, - PageTransition::Type a_transition) - : url(a_url), - transition(a_transition), - index(-1), - add_types(TabStripModel::ADD_SELECTED), - instance(NULL), - target(NULL) { -} - -Browser::AddTabWithURLParams::~AddTabWithURLParams() { -} - -TabContents* Browser::AddTabWithURL(AddTabWithURLParams* params) { - TabContents* contents = NULL; - if (CanSupportWindowFeature(FEATURE_TABSTRIP) || tabstrip_model()->empty()) { - GURL url_to_load = params->url; - if (url_to_load.is_empty()) - url_to_load = GetHomePage(); - contents = CreateTabContentsForURL(url_to_load, params->referrer, profile_, - params->transition, false, - params->instance); - contents->SetExtensionAppById(params->extension_app_id); - tab_handler_->GetTabStripModel()->AddTabContents(contents, params->index, - params->transition, - params->add_types); - // TODO(sky): figure out why this is needed. Without it we seem to get - // failures in startup tests. - // By default, content believes it is not hidden. When adding contents - // in the background, tell it that it's hidden. - if ((params->add_types & TabStripModel::ADD_SELECTED) == 0) { - // TabStripModel::AddTabContents invokes HideContents if not foreground. - contents->WasHidden(); - } - params->target = this; - } else { - // We're in an app window or a popup window. Find an existing browser to - // open this URL in, creating one if none exists. - Browser* browser = - BrowserList::FindBrowserWithFeature(profile_, FEATURE_TABSTRIP); - if (!browser) - browser = Browser::Create(profile_); - contents = browser->AddTabWithURL(params); - DCHECK(params->target); - } - return contents; + browser::NavigateParams params(this, url, transition); + params.disposition = NEW_FOREGROUND_TAB; + browser::Navigate(¶ms); + return params.target_contents; } TabContents* Browser::AddTab(TabContents* tab_contents, @@ -1069,27 +1029,10 @@ bool Browser::NavigateToIndexWithDisposition(int index, } void Browser::ShowSingletonTab(const GURL& url) { - // In case the URL was rewritten by the BrowserURLHandler we need to ensure - // that we do not open another URL that will get redirected to the rewritten - // URL. - GURL rewritten_url(url); - bool reverse_on_redirect = false; - BrowserURLHandler::RewriteURLIfNecessary(&rewritten_url, profile_, - &reverse_on_redirect); - - // See if we already have a tab with the given URL and select it if so. - TabStripModel* model = tab_handler_->GetTabStripModel(); - for (int i = 0; i < model->count(); i++) { - TabContents* tc = model->GetTabContentsAt(i); - if (CompareURLsIgnoreRef(tc->GetURL(), url) || - CompareURLsIgnoreRef(tc->GetURL(), rewritten_url)) { - model->SelectTabContentsAt(i, false); - return; - } - } - - // Otherwise, just create a new tab. - AddSelectedTabWithURL(url, PageTransition::AUTO_BOOKMARK); + browser::NavigateParams params(this, url, PageTransition::AUTO_BOOKMARK); + params.disposition = SINGLETON_TAB; + params.show_window = true; + browser::Navigate(¶ms); } void Browser::UpdateCommandsForFullscreenMode(bool is_fullscreen) { @@ -1291,15 +1234,15 @@ void Browser::OpenCurrentURL() { return; GURL url(WideToUTF8(location_bar->GetInputString())); - + browser::NavigateParams params(this, url, location_bar->GetPageTransition()); + params.disposition = open_disposition; // Use ADD_INHERIT_OPENER so that all pages opened by the omnibox at least // inherit the opener. In some cases the tabstrip will determine the group // should be inherited, in which case the group is inherited instead of the // opener. - OpenURLAtIndex(NULL, url, GURL(), open_disposition, - location_bar->GetPageTransition(), -1, - TabStripModel::ADD_FORCE_INDEX | - TabStripModel::ADD_INHERIT_OPENER); + params.tabstrip_add_types = + TabStripModel::ADD_FORCE_INDEX | TabStripModel::ADD_INHERIT_OPENER; + browser::Navigate(¶ms); } void Browser::Stop() { @@ -1473,8 +1416,15 @@ void Browser::Search() { } // Exit fullscreen to show omnibox. - if (window_->IsFullscreen()) + if (window_->IsFullscreen()) { ToggleFullscreenMode(); + // ToggleFullscreenMode is asynchronous, so we don't have omnibox + // visible at this point. Wait for next event cycle which toggles + // the visibility of omnibox before creating new tab. + MessageLoop::current()->PostTask( + FROM_HERE, method_factory_.NewRunnableMethod(&Browser::Search)); + return; + } // Otherwise just open it. NewTab(); @@ -1517,6 +1467,9 @@ void Browser::BookmarkCurrentPage() { void Browser::SavePage() { UserMetrics::RecordAction(UserMetricsAction("SavePage"), profile_); + TabContents* current_tab = GetSelectedTabContents(); + if (current_tab && current_tab->contents_mime_type() == "application/pdf") + UserMetrics::RecordAction(UserMetricsAction("PDF.SavePage"), profile_); GetSelectedTabContents()->OnSavePage(); } @@ -1777,6 +1730,11 @@ void Browser::ShowExtensionsTab() { ShowSingletonTab(GURL(chrome::kChromeUIExtensionsURL)); } +void Browser::ShowAboutConflictsTab() { + UserMetrics::RecordAction(UserMetricsAction("AboutConflicts"), profile_); + ShowSingletonTab(GURL(chrome::kChromeUIConflictsURL)); +} + void Browser::ShowBrokenPageTab(TabContents* contents) { UserMetrics::RecordAction(UserMetricsAction("ReportBug"), profile_); string16 page_title = contents->GetTitle(); @@ -1806,8 +1764,9 @@ void Browser::ShowOptionsTab(const std::string& sub_page) { // this may cause us to unnecessarily reload the same page. We can't // really detect that unless the options page is permitted to change the // URL in the address bar, but security policy doesn't allow that. - OpenURLAtIndex(tc, url, GURL(), CURRENT_TAB, PageTransition::GENERATED, - -1, -1); + browser::NavigateParams params(this, url, PageTransition::GENERATED); + params.source_contents = tc; + browser::Navigate(¶ms); model->SelectTabContentsAt(i, false); return; } @@ -1897,21 +1856,19 @@ void Browser::OpenHelpTab() { } void Browser::OpenThemeGalleryTabAndActivate() { - OpenURL(GURL(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)), - GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); - window_->Activate(); + AddSelectedTabWithURL(GURL(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)), + PageTransition::LINK); } void Browser::OpenPrivacyDashboardTabAndActivate() { - OpenURL(GURL(l10n_util::GetStringUTF8(IDS_PRIVACY_DASHBOARD_URL)), - GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); + OpenURL(GURL(kPrivacyDashboardUrl), GURL(), + NEW_FOREGROUND_TAB, PageTransition::LINK); window_->Activate(); } void Browser::OpenAutoFillHelpTabAndActivate() { - OpenURL(GURL(l10n_util::GetStringUTF8(IDS_AUTOFILL_HELP_URL)), - GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); - window_->Activate(); + AddSelectedTabWithURL(GURL(l10n_util::GetStringUTF8(IDS_AUTOFILL_HELP_URL)), + PageTransition::LINK); } void Browser::OpenSearchEngineOptionsDialog() { @@ -2044,6 +2001,8 @@ void Browser::RegisterUserPrefs(PrefService* prefs) { prefs->RegisterBooleanPref(prefs::kEnableTranslate, true); prefs->RegisterBooleanPref(prefs::kRemotingHasSetupCompleted, false); prefs->RegisterStringPref(prefs::kCloudPrintEmail, std::string()); + prefs->RegisterBooleanPref(prefs::kDevToolsDisabled, false); + prefs->RegisterRealPref(prefs::kDefaultZoomLevel, 0.0); } // static @@ -2252,6 +2211,7 @@ void Browser::ExecuteCommandWithDisposition( case IDC_IMPORT_SETTINGS: OpenImportSettingsDialog(); break; case IDC_ABOUT: OpenAboutChromeDialog(); break; case IDC_UPGRADE_DIALOG: OpenUpdateChromeDialog(); break; + case IDC_VIEW_INCOMPATIBILITIES: ShowAboutConflictsTab(); break; case IDC_HELP_PAGE: OpenHelpTab(); break; #if defined(OS_CHROMEOS) case IDC_SYSTEM_OPTIONS: OpenSystemOptionsDialog(); break; @@ -2325,6 +2285,11 @@ GURL Browser::GetHomePage() const { // --homepage overrides any preferences. const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kHomePage)) { + // TODO(evanm): clean up usage of DIR_CURRENT. + // http://code.google.com/p/chromium/issues/detail?id=60630 + // For now, allow this code to call getcwd(). + base::ThreadRestrictions::ScopedAllowIO allow_io; + FilePath browser_directory; PathService::Get(base::DIR_CURRENT, &browser_directory); GURL home_page(URLFixerUpper::FixupRelativeFile(browser_directory, @@ -2382,13 +2347,15 @@ TabContents* Browser::AddBlankTabAt(int index, bool foreground) { // TabContents, but we want to include the time it takes to create the // TabContents object too. base::TimeTicks new_tab_start_time = base::TimeTicks::Now(); - AddTabWithURLParams params(GURL(chrome::kChromeUINewTabURL), - PageTransition::TYPED); - params.add_types = + browser::NavigateParams params(this, GURL(chrome::kChromeUINewTabURL), + PageTransition::TYPED); + params.tabstrip_add_types = foreground ? TabStripModel::ADD_SELECTED : TabStripModel::ADD_NONE; - TabContents* contents = AddTabWithURL(¶ms); - contents->set_new_tab_start_time(new_tab_start_time); - return contents; + params.disposition = foreground ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; + params.tabstrip_index = index; + browser::Navigate(¶ms); + params.target_contents->set_new_tab_start_time(new_tab_start_time); + return params.target_contents; } Browser* Browser::CreateNewStripWithContents(TabContents* detached_contents, @@ -2744,8 +2711,12 @@ void Browser::OpenURLFromTab(TabContents* source, const GURL& referrer, WindowOpenDisposition disposition, PageTransition::Type transition) { - OpenURLAtIndex(source, url, referrer, disposition, transition, -1, - TabStripModel::ADD_NONE); + browser::NavigateParams params(this, url, transition); + params.source_contents = source; + params.referrer = referrer; + params.disposition = disposition; + params.tabstrip_add_types = TabStripModel::ADD_NONE; + browser::Navigate(¶ms); } void Browser::NavigationStateChanged(const TabContents* source, @@ -2765,73 +2736,34 @@ void Browser::AddNewContents(TabContents* source, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture) { - DCHECK(disposition != SAVE_TO_DISK); // No code for this yet - DCHECK(disposition != CURRENT_TAB); // Can't create a new contents for the - // current tab. + // No code for this yet + DCHECK(disposition != SAVE_TO_DISK); + // Can't create a new contents for the current tab - invalid case. + DCHECK(disposition != CURRENT_TAB); + // TODO(beng): This belongs behind the platform-specific View interface. + // That's why it's there. #if defined(OS_CHROMEOS) - if (disposition == NEW_POPUP) { - // If the popup is bigger than a given factor of the screen, then - // turn it into a foreground tab (on chrome os only) - // Also check for width or height == 0, which would otherwise indicate - // a tab sized popup window. - GdkScreen* screen = gdk_screen_get_default(); - int max_width = gdk_screen_get_width(screen) * kPopupMaxWidthFactor; - int max_height = gdk_screen_get_height(screen) * kPopupMaxHeightFactor; - if (initial_pos.width() > max_width || initial_pos.width() == 0 || - initial_pos.height() > max_height || initial_pos.height() == 0) { - disposition = NEW_FOREGROUND_TAB; - } - } -#endif - - // If this is a window with no tabstrip, we can only have one tab so we need - // to process this in tabbed browser window. - if (!CanSupportWindowFeature(FEATURE_TABSTRIP) && - tab_handler_->GetTabStripModel()->count() > 0 && - disposition != NEW_WINDOW && - disposition != NEW_POPUP) { - Browser* b = GetOrCreateTabbedBrowser(profile_); - DCHECK(b); - PageTransition::Type transition = PageTransition::LINK; - // If we were called from an "installed webapp" we want to emulate the code - // that is run from browser_init.cc for links from external applications. - // This means we need to open the tab with the START PAGE transition. - // AddNewContents doesn't support this but the TabStripModel's - // AddTabContents method does. - if (type_ & TYPE_APP) - transition = PageTransition::START_PAGE; - b->tabstrip_model()->AddTabContents( - new_contents, -1, transition, TabStripModel::ADD_SELECTED); - b->window()->Show(); - return; - } - if (disposition == NEW_POPUP) { - Type browser_type; - if ((type_ & TYPE_APP) || (source && source->is_app())) { - // New app popup, or an app is creating a popup. - browser_type = TYPE_APP_POPUP; - } else { - browser_type = TYPE_POPUP; + // If the popup is bigger than a given factor of the screen, then + // turn it into a foreground tab (on chrome os only) + // Also check for width or height == 0, which would otherwise indicate + // a tab sized popup window. + GdkScreen* screen = gdk_screen_get_default(); + int max_width = gdk_screen_get_width(screen) * kPopupMaxWidthFactor; + int max_height = gdk_screen_get_height(screen) * kPopupMaxHeightFactor; + if (initial_pos.width() > max_width || initial_pos.width() == 0 || + initial_pos.height() > max_height || initial_pos.height() == 0) { + disposition = NEW_FOREGROUND_TAB; } - Browser* browser = Browser::CreateForPopup(browser_type, - profile_, - new_contents, - initial_pos); - browser->window()->Show(); - - } else if (disposition == NEW_WINDOW) { - Browser* browser = Browser::Create(profile_); - browser->AddNewContents(source, new_contents, NEW_FOREGROUND_TAB, - initial_pos, user_gesture); - browser->window()->Show(); - } else if (disposition != SUPPRESS_OPEN) { - tab_handler_->GetTabStripModel()->AddTabContents( - new_contents, -1, PageTransition::LINK, - disposition == NEW_FOREGROUND_TAB ? TabStripModel::ADD_SELECTED : - TabStripModel::ADD_NONE); } +#endif + + browser::NavigateParams params(this, new_contents); + params.source_contents = source; + params.disposition = disposition; + params.window_bounds = initial_pos; + browser::Navigate(¶ms); } void Browser::ActivateContents(TabContents* contents) { @@ -3149,7 +3081,6 @@ void Browser::ShowRepostFormWarningDialog(TabContents *tab_contents) { } void Browser::ShowContentSettingsWindow(ContentSettingsType content_type) { - if (CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableTabbedOptions)) { ShowOptionsTab( @@ -3250,7 +3181,7 @@ void Browser::Observe(NotificationType type, if (profile_->IsSameProfile(profile)) { ExtensionsService* service = profile->GetExtensionsService(); DCHECK(service); - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (service->extension_prefs()->DidExtensionEscalatePermissions( extension->id())) ShowExtensionDisabledUI(service, profile_, extension); @@ -3263,7 +3194,7 @@ void Browser::Observe(NotificationType type, window()->GetLocationBar()->UpdatePageActions(); // Close any tabs from the unloaded extension. - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); TabStripModel* model = tab_handler_->GetTabStripModel(); for (int i = model->count() - 1; i >= 0; --i) { TabContents* tc = model->GetTabContentsAt(i); @@ -3299,7 +3230,7 @@ void Browser::Observe(NotificationType type, TabContents* tab_contents = GetSelectedTabContents(); if (!tab_contents) break; - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); CrashedExtensionInfoBarDelegate* delegate = NULL; for (int i = 0; i < tab_contents->infobar_delegate_count();) { delegate = tab_contents->GetInfoBarDelegateAt(i)-> @@ -3358,6 +3289,10 @@ void Browser::Observe(NotificationType type, } else { CreateInstantIfNecessary(); } + } else if (pref_name == prefs::kDevToolsDisabled) { + UpdateCommandsForDevTools(); + if (dev_tools_disabled_.GetValue()) + g_browser_process->devtools_manager()->CloseAllClientHosts(); } else { NOTREACHED(); } @@ -3490,9 +3425,7 @@ void Browser::InitCommandState() { // Show various bits of UI command_updater_.UpdateCommandEnabled(IDC_OPEN_FILE, true); command_updater_.UpdateCommandEnabled(IDC_CREATE_SHORTCUTS, false); - command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS, true); - command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_CONSOLE, true); - command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_INSPECT, true); + UpdateCommandsForDevTools(); command_updater_.UpdateCommandEnabled(IDC_TASK_MANAGER, true); command_updater_.UpdateCommandEnabled(IDC_SHOW_HISTORY, true); command_updater_.UpdateCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER, @@ -3560,9 +3493,11 @@ void Browser::InitCommandState() { // Show various bits of UI command_updater_.UpdateCommandEnabled(IDC_CLEAR_BROWSING_DATA, normal_window); - // The upgrade entry should always be enabled. Whether it is visible is a - // separate matter determined on menu show. + // The upgrade entry and the view incompatibility entry should always be + // enabled. Whether they are visible is a separate matter determined on menu + // show. command_updater_.UpdateCommandEnabled(IDC_UPGRADE_DIALOG, true); + command_updater_.UpdateCommandEnabled(IDC_VIEW_INCOMPATIBILITIES, true); // Initialize other commands whose state changes based on fullscreen mode. UpdateCommandsForFullscreenMode(false); @@ -3662,6 +3597,16 @@ void Browser::UpdateReloadStopState(bool is_loading, bool force) { command_updater_.UpdateCommandEnabled(IDC_STOP, is_loading); } +void Browser::UpdateCommandsForDevTools() { + bool dev_tools_enabled = !dev_tools_disabled_.GetValue(); + command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS, + dev_tools_enabled); + command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_CONSOLE, + dev_tools_enabled); + command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_INSPECT, + dev_tools_enabled); +} + /////////////////////////////////////////////////////////////////////////////// // Browser, UI update coalescing and handling (private): @@ -4030,138 +3975,6 @@ Browser* Browser::GetOrCreateTabbedBrowser(Profile* profile) { return browser; } -void Browser::OpenURLAtIndex(TabContents* source, - const GURL& url, - const GURL& referrer, - WindowOpenDisposition disposition, - PageTransition::Type transition, - int index, - int add_types) { - // TODO(beng): Move all this code into a separate helper that has unit tests. - - // No code for these yet - DCHECK((disposition != NEW_POPUP) && (disposition != SAVE_TO_DISK)); - - TabContents* current_tab = source ? source : GetSelectedTabContents(); - bool source_tab_was_frontmost = (current_tab == GetSelectedTabContents()); - TabContents* new_contents = NULL; - - // Opening a bookmark counts as a user gesture, so we don't need to avoid - // carpet-bombing here. - PageTransition::Type baseTransitionType = - PageTransition::StripQualifier(transition); - if ((baseTransitionType == PageTransition::TYPED || - baseTransitionType == PageTransition::AUTO_BOOKMARK) && - current_tab != NULL) { - RenderViewHostDelegate::BrowserIntegration* delegate = current_tab; - delegate->OnUserGesture(); - } - - // If the URL is part of the same web site, then load it in the same - // SiteInstance (and thus the same process). This is an optimization to - // reduce process overhead; it is not necessary for compatibility. (That is, - // the new tab will not have script connections to the previous tab, so it - // does not need to be part of the same SiteInstance or BrowsingInstance.) - // Default to loading in a new SiteInstance and BrowsingInstance. - // TODO(creis): should this apply to applications? - SiteInstance* instance = NULL; - // Don't use this logic when "--process-per-tab" is specified. - if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerTab)) { - if (current_tab) { - const GURL& current_url = current_tab->GetURL(); - if (SiteInstance::IsSameWebSite(profile_, current_url, url)) - instance = current_tab->GetSiteInstance(); - } - } - - // If this browser doeesn't support tabs, we can only have one tab so a new - // tab always goes into a tabbed browser window. - if (!CanSupportWindowFeature(FEATURE_TABSTRIP) && - disposition != CURRENT_TAB && disposition != NEW_WINDOW) { - // If the disposition is OFF_THE_RECORD we don't want to create a new - // browser that will itself create another OTR browser. This will result in - // a browser leak (and crash below because no tab is created or selected). - if (disposition == OFF_THE_RECORD) { - OpenURLOffTheRecord(profile_, url); - return; - } - - Browser* b = GetOrCreateTabbedBrowser(profile_); - DCHECK(b); - - // If we have just created a new browser window, make sure we select the - // tab. - if (b->tab_count() == 0 && disposition == NEW_BACKGROUND_TAB) - disposition = NEW_FOREGROUND_TAB; - - b->OpenURL(url, referrer, disposition, transition); - b->window()->Show(); - return; - } - - if (profile_->IsOffTheRecord() && disposition == OFF_THE_RECORD) - disposition = NEW_FOREGROUND_TAB; - - if (disposition == SINGLETON_TAB) { - ShowSingletonTab(url); - return; - } else if (disposition == NEW_WINDOW) { - Browser* browser = Browser::Create(profile_); - AddTabWithURLParams params(url, transition); - params.referrer = referrer; - params.index = index; - params.add_types = TabStripModel::ADD_SELECTED | add_types; - params.instance = instance; - new_contents = browser->AddTabWithURL(¶ms); - browser->window()->Show(); - } else if ((disposition == CURRENT_TAB) && current_tab) { - tab_handler_->GetTabStripModel()->TabNavigating(current_tab, transition); - - bool user_initiated = (PageTransition::StripQualifier(transition) == - PageTransition::AUTO_BOOKMARK); - - if (user_initiated && source_tab_was_frontmost && - window_->GetLocationBar()) { - // Forcibly reset the location bar if the url is going to change in the - // current tab, since otherwise it won't discard any ongoing user edits, - // since it doesn't realize this is a user-initiated action. - window_->GetLocationBar()->Revert(); - } - - current_tab->controller().LoadURL(url, referrer, transition); - new_contents = current_tab; - if (GetStatusBubble()) - GetStatusBubble()->Hide(); - - // Update the location bar. This is synchronous. We specifically don't - // update the load state since the load hasn't started yet and updating it - // will put it out of sync with the actual state like whether we're - // displaying a favicon, which controls the throbber. If we updated it here, - // the throbber will show the default favicon for a split second when - // navigating away from the new tab page. - ScheduleUIUpdate(current_tab, TabContents::INVALIDATE_URL); - } else if (disposition == OFF_THE_RECORD) { - OpenURLOffTheRecord(profile_, url); - return; - } else if (disposition != SUPPRESS_OPEN) { - if (disposition != NEW_BACKGROUND_TAB) - add_types |= TabStripModel::ADD_SELECTED; - AddTabWithURLParams params(url, transition); - params.referrer = referrer; - params.index = index; - params.add_types = add_types; - params.instance = instance; - new_contents = AddTabWithURL(¶ms); - } - - if (disposition != NEW_BACKGROUND_TAB && source_tab_was_frontmost && - new_contents) { - // Give the focus to the newly navigated tab, if the source tab was - // front-most. - new_contents->Focus(); - } -} - void Browser::FindInPage(bool find_next, bool forward_direction) { ShowFindBar(); if (find_next) { @@ -4271,6 +4084,6 @@ bool Browser::OpenInstant(WindowOpenDisposition disposition) { void Browser::CreateInstantIfNecessary() { if (type() == TYPE_NORMAL && InstantController::IsEnabled(profile()) && !profile()->IsOffTheRecord()) { - instant_.reset(new InstantController(this)); + instant_.reset(new InstantController(profile_, this)); } } diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h new file mode 100644 index 0000000..abd2be9 --- /dev/null +++ b/chrome/browser/ui/browser.h @@ -0,0 +1,1094 @@ +// 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_UI_BROWSER_H_ +#define CHROME_BROWSER_UI_BROWSER_H_ +#pragma once + +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/gtest_prod_util.h" +#include "base/scoped_ptr.h" +#include "base/string16.h" +#include "base/task.h" +#include "chrome/browser/command_updater.h" +#include "chrome/browser/debugger/devtools_toggle_action.h" +#include "chrome/browser/instant/instant_delegate.h" +#include "chrome/browser/prefs/pref_member.h" +#include "chrome/browser/sessions/session_id.h" +#include "chrome/browser/sessions/tab_restore_service_observer.h" +#include "chrome/browser/shell_dialogs.h" +#include "chrome/browser/sync/profile_sync_service_observer.h" +#include "chrome/browser/tabs/tab_handler.h" +#include "chrome/browser/tabs/tab_strip_model_delegate.h" // TODO(beng): remove +#include "chrome/browser/tabs/tab_strip_model_observer.h" // TODO(beng): remove +#include "chrome/browser/tab_contents/page_navigator.h" +#include "chrome/browser/tab_contents/tab_contents_delegate.h" +#include "chrome/browser/toolbar_model.h" +#include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/common/page_transition_types.h" +#include "chrome/common/page_zoom.h" +#include "gfx/rect.h" + +class BrowserWindow; +class Extension; +class FindBarController; +class InstantController; +class PrefService; +class Profile; +class SessionStorageNamespace; +class SkBitmap; +class StatusBubble; +class TabNavigation; +class TabStripModel; +namespace gfx { +class Point; +} + +class Browser : public TabHandlerDelegate, + public TabContentsDelegate, + public PageNavigator, + public CommandUpdater::CommandUpdaterDelegate, + public NotificationObserver, + public SelectFileDialog::Listener, + public TabRestoreServiceObserver, + public ProfileSyncServiceObserver, + public InstantDelegate { + public: + // If you change the values in this enum you'll need to update browser_proxy. + // TODO(sky): move into a common place that is referenced by both ui_tests + // and chrome. + enum Type { + TYPE_NORMAL = 1, + TYPE_POPUP = 2, + // The old-style app created via "Create application shortcuts". + TYPE_APP = 4, + // The new-style app created by installing a crx. This kinda needs to be + // separate because we require larger icons and an application name that + // are found in the crx. If we ever decide to create this kind of app + // using some other system (eg some web standard), maybe we should + // generalize this name to TYPE_MULTITAB or something. + TYPE_EXTENSION_APP = 8, + TYPE_APP_POPUP = TYPE_APP | TYPE_POPUP, + TYPE_DEVTOOLS = TYPE_APP | 16, + + // TODO(skerner): crbug/56776: Until the panel UI is complete on all + // platforms, apps that set app.launch.container = "panel" have type + // APP_POPUP. (see Browser::CreateForApp) + // NOTE: TYPE_APP_PANEL is a superset of TYPE_APP_POPUP. + TYPE_APP_PANEL = TYPE_APP | TYPE_POPUP | 32, + TYPE_ANY = TYPE_NORMAL | + TYPE_POPUP | + TYPE_APP | + TYPE_EXTENSION_APP | + TYPE_DEVTOOLS | + TYPE_APP_PANEL + }; + + // Possible elements of the Browser window. + enum WindowFeature { + FEATURE_NONE = 0, + FEATURE_TITLEBAR = 1, + FEATURE_TABSTRIP = 2, + FEATURE_TOOLBAR = 4, + FEATURE_LOCATIONBAR = 8, + FEATURE_BOOKMARKBAR = 16, + FEATURE_INFOBAR = 32, + FEATURE_SIDEBAR = 64, + FEATURE_DOWNLOADSHELF = 128 + }; + + // Maximized state on creation. + enum MaximizedState { + // The maximized state is set to the default, which varies depending upon + // what the user has done. + MAXIMIZED_STATE_DEFAULT, + + // Maximized state is explicitly maximized. + MAXIMIZED_STATE_MAXIMIZED, + + // Maximized state is explicitly not maximized (normal). + MAXIMIZED_STATE_UNMAXIMIZED + }; + + // Constructors, Creation, Showing ////////////////////////////////////////// + + // Creates a new browser of the given |type| and for the given |profile|. The + // Browser has a NULL window after its construction, CreateBrowserWindow must + // be called after configuration for window() to be valid. + // Avoid using this constructor directly if you can use one of the Create*() + // methods below. This applies to almost all non-testing code. + Browser(Type type, Profile* profile); + virtual ~Browser(); + + // Creates a normal tabbed browser with the specified profile. The Browser's + // window is created by this function call. + static Browser* Create(Profile* profile); + + // Like Create, but creates a browser of the specified (popup) type, with the + // specified contents, in a popup window of the specified size/position. + static Browser* CreateForPopup(Type type, Profile* profile, + TabContents* new_contents, + const gfx::Rect& initial_bounds); + + // Like Create, but creates a browser of the specified type. + static Browser* CreateForType(Type type, Profile* profile); + + // Like Create, but creates a toolbar-less "app" window for the specified + // app. |app_name| is required and is used to identify the window to the + // shell. |extension| is optional. If supplied, we create a window with + // a bigger icon and title text, that supports tabs. + static Browser* CreateForApp(const std::string& app_name, + const Extension* extension, + Profile* profile, + bool is_panel); + + // Like Create, but creates a tabstrip-less and toolbar-less + // DevTools "app" window. + static Browser* CreateForDevTools(Profile* profile); + + // Returns the extension app associated with this window, if any. + const Extension* extension_app() { return extension_app_; } + + // Set overrides for the initial window bounds and maximized state. + void set_override_bounds(const gfx::Rect& bounds) { + override_bounds_ = bounds; + } + void set_maximized_state(MaximizedState state) { + maximized_state_ = state; + } + // Return true if the initial window bounds have been overridden. + bool bounds_overridden() const { + return !override_bounds_.IsEmpty(); + } + + // Creates the Browser Window. Prefer to use the static helpers above where + // possible. This does not show the window. You need to call window()->Show() + // to show it. + void CreateBrowserWindow(); + + // Accessors //////////////////////////////////////////////////////////////// + + Type type() const { return type_; } + Profile* profile() const { return profile_; } + const std::vector<std::wstring>& user_data_dir_profiles() const; + + // Returns the InstantController or NULL if there is no InstantController for + // this Browser. + InstantController* instant() const { return instant_.get(); } + +#if defined(UNIT_TEST) + // Sets the BrowserWindow. This is intended for testing and generally not + // useful outside of testing. Use CreateBrowserWindow outside of testing, or + // the static convenience methods that create a BrowserWindow for you. + void set_window(BrowserWindow* window) { + DCHECK(!window_); + window_ = window; + } +#endif + + BrowserWindow* window() const { return window_; } + ToolbarModel* toolbar_model() { return &toolbar_model_; } + const SessionID& session_id() const { return session_id_; } + CommandUpdater* command_updater() { return &command_updater_; } + + // Get the FindBarController for this browser, creating it if it does not + // yet exist. + FindBarController* GetFindBarController(); + + // Returns true if a FindBarController exists for this browser. + bool HasFindBarController() const; + + // Setters ///////////////////////////////////////////////////////////////// + + void set_user_data_dir_profiles(const std::vector<std::wstring>& profiles); + + // Browser Creation Helpers ///////////////////////////////////////////////// + + // Opens a new window with the default blank tab. + static void OpenEmptyWindow(Profile* profile); + + // Opens a new window with the tabs from |profile|'s TabRestoreService. + static void OpenWindowWithRestoredTabs(Profile* profile); + + // Opens the specified URL in a new browser window in an incognito session. + // If there is already an existing active incognito session for the specified + // |profile|, that session is re-used. + static void OpenURLOffTheRecord(Profile* profile, const GURL& url); + + // Open an application specified by |app_id| in the appropriate launch + // container. |existing_tab| is reused if it is not NULL and the launch + // container is a tab. Returns NULL if the app_id is invalid or if + // ExtensionsService isn't ready/available. + static TabContents* OpenApplication(Profile* profile, + const std::string& app_id, + TabContents* existing_tab); + + // Open |extension| in |container|, using |existing_tab| if not NULL and if + // the correct container type. Returns the TabContents* that was created or + // NULL. + static TabContents* OpenApplication( + Profile* profile, + const Extension* extension, + extension_misc::LaunchContainer container, + TabContents* existing_tab); + + // Opens a new application window for the specified url. If |as_panel| + // is true, the application will be opened as a Browser::Type::APP_PANEL in + // app panel window, otherwise it will be opened as as either + // Browser::Type::APP a.k.a. "thin frame" (if |extension| is NULL) or + // Browser::Type::EXTENSION_APP (if |extension| is non-NULL). + static TabContents* OpenApplicationWindow( + Profile* profile, + const Extension* extension, + extension_misc::LaunchContainer container, + const GURL& url); + + // Open an application for |extension| in a new application window or panel. + static TabContents* OpenApplicationWindow(Profile* profile, GURL& url); + + // Open an application for |extension| in a new application tab, or + // |existing_tab| if not NULL. Returns NULL if there are no appropriate + // existing browser windows for |profile|. + static TabContents* OpenApplicationTab(Profile* profile, + const Extension* extension, + TabContents* existing_tab); + + // Opens a new window and opens the bookmark manager. + static void OpenBookmarkManagerWindow(Profile* profile); + +#if defined(OS_MACOSX) + // Open a new window with history/downloads/help/options (needed on Mac when + // there are no windows). + static void OpenHistoryWindow(Profile* profile); + static void OpenDownloadsWindow(Profile* profile); + static void OpenHelpWindow(Profile* profile); + static void OpenOptionsWindow(Profile* profile); +#endif + + // Opens a window with the extensions tab in it - needed by long-lived + // extensions which may run with no windows open. + static void OpenExtensionsWindow(Profile* profile); + + // State Storage and Retrieval for UI /////////////////////////////////////// + + // Save and restore the window position. + std::string GetWindowPlacementKey() const; + bool ShouldSaveWindowPlacement() const; + void SaveWindowPlacement(const gfx::Rect& bounds, bool maximized); + gfx::Rect GetSavedWindowBounds() const; + bool GetSavedMaximizedState() const; + + // Gets the FavIcon of the page in the selected tab. + SkBitmap GetCurrentPageIcon() const; + + // Gets the title of the window based on the selected tab's title. + string16 GetWindowTitleForCurrentTab() const; + + // Prepares a title string for display (removes embedded newlines, etc). + static void FormatTitleForDisplay(string16* title); + + // OnBeforeUnload handling ////////////////////////////////////////////////// + + // Gives beforeunload handlers the chance to cancel the close. + bool ShouldCloseWindow(); + + bool IsAttemptingToCloseBrowser() const { + return is_attempting_to_close_browser_; + } + + // Invoked when the window containing us is closing. Performs the necessary + // cleanup. + void OnWindowClosing(); + + // In-progress download termination handling ///////////////////////////////// + + // Called when the user has decided whether to proceed or not with the browser + // closure. |cancel_downloads| is true if the downloads should be canceled + // and the browser closed, false if the browser should stay open and the + // downloads running. + void InProgressDownloadResponse(bool cancel_downloads); + + // TabStripModel pass-thrus ///////////////////////////////////////////////// + + TabStripModel* tabstrip_model() const { + // TODO(beng): remove this accessor. It violates google style. + return tab_handler_->GetTabStripModel(); + } + + int tab_count() const; + int selected_index() const; + int GetIndexOfController(const NavigationController* controller) const; + TabContents* GetTabContentsAt(int index) const; + TabContents* GetSelectedTabContents() const; + void SelectTabContentsAt(int index, bool user_gesture); + void CloseAllTabs(); + + // Tab adding/showing functions ///////////////////////////////////////////// + + // Returns the index to insert a tab at during session restore and startup. + // |relative_index| gives the index of the url into the number of tabs that + // are going to be opened. For example, if three urls are passed in on the + // command line this is invoked three times with the values 0, 1 and 2. + int GetIndexForInsertionDuringRestore(int relative_index); + + // Adds a selected tab with the specified URL and transition, returns the + // created TabContents. + TabContents* AddSelectedTabWithURL(const GURL& url, + PageTransition::Type transition); + + // Add a new tab, given a TabContents. A TabContents appropriate to + // display the last committed entry is created and returned. + TabContents* AddTab(TabContents* tab_contents, PageTransition::Type type); + + // Add a tab with its session history restored from the SessionRestore + // system. If select is true, the tab is selected. |tab_index| gives the index + // to insert the tab at. |selected_navigation| is the index of the + // TabNavigation in |navigations| to select. If |extension_app_id| is + // non-empty the tab is an app tab and |extension_app_id| is the id of the + // extension. If |pin| is true and |tab_index|/ is the last pinned tab, then + // the newly created tab is pinned. If |from_last_session| is true, + // |navigations| are from the previous session. + TabContents* AddRestoredTab(const std::vector<TabNavigation>& navigations, + int tab_index, + int selected_navigation, + const std::string& extension_app_id, + bool select, + bool pin, + bool from_last_session, + SessionStorageNamespace* storage_namespace); + // Creates a new tab with the already-created TabContents 'new_contents'. + // The window for the added contents will be reparented correctly when this + // method returns. If |disposition| is NEW_POPUP, |pos| should hold the + // initial position. + void AddTabContents(TabContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture); + void CloseTabContents(TabContents* contents); + + // Show a dialog with HTML content. |delegate| contains a pointer to the + // delegate who knows how to display the dialog (which file URL and JSON + // string input to use during initialization). |parent_window| is the window + // that should be parent of the dialog, or NULL for the default. + void BrowserShowHtmlDialog(HtmlDialogUIDelegate* delegate, + gfx::NativeWindow parent_window); + + // Called when a popup select is about to be displayed. + void BrowserRenderWidgetShowing(); + + // Notification that some of our content has changed size as + // part of an animation. + void ToolbarSizeChanged(bool is_animating); + + // Replaces the state of the currently selected tab with the session + // history restored from the SessionRestore system. + void ReplaceRestoredTab( + const std::vector<TabNavigation>& navigations, + int selected_navigation, + bool from_last_session, + const std::string& extension_app_id, + SessionStorageNamespace* session_storage_namespace); + + // Navigate to an index in the tab history, opening a new tab depending on the + // disposition. + bool NavigateToIndexWithDisposition(int index, WindowOpenDisposition disp); + + // Show a given a URL. If a tab with the same URL (ignoring the ref) is + // already visible in this browser, it becomes selected. Otherwise a new tab + // is created. + void ShowSingletonTab(const GURL& url); + + // Update commands whose state depends on whether the window is in fullscreen + // mode. This is a public function because on Linux, fullscreen mode is an + // async call to X. Once we get the fullscreen callback, the browser window + // will call this method. + void UpdateCommandsForFullscreenMode(bool is_fullscreen); + + // Assorted browser commands //////////////////////////////////////////////// + + // NOTE: Within each of the following sections, the IDs are ordered roughly by + // how they appear in the GUI/menus (left to right, top to bottom, etc.). + + // Navigation commands + void GoBack(WindowOpenDisposition disposition); + void GoForward(WindowOpenDisposition disposition); + void Reload(WindowOpenDisposition disposition); + void ReloadIgnoringCache(WindowOpenDisposition disposition); // Shift-reload. + void Home(WindowOpenDisposition disposition); + void OpenCurrentURL(); + void Stop(); + // Window management commands + void NewWindow(); + void NewIncognitoWindow(); + void CloseWindow(); + void NewTab(); + void CloseTab(); + void SelectNextTab(); + void SelectPreviousTab(); + void OpenTabpose(); + void MoveTabNext(); + void MoveTabPrevious(); + void SelectNumberedTab(int index); + void SelectLastTab(); + void DuplicateTab(); + void WriteCurrentURLToClipboard(); + void ConvertPopupToTabbedBrowser(); + // In kiosk mode, the first toggle is valid, the rest is discarded. + void ToggleFullscreenMode(); + void Exit(); +#if defined(OS_CHROMEOS) + void ToggleCompactNavigationBar(); + void Search(); +#endif + + // Page-related commands + void BookmarkCurrentPage(); + void SavePage(); + void ViewSource(); + void ShowFindBar(); + + // Returns true if the Browser supports the specified feature. The value of + // this varies during the lifetime of the browser. For example, if the window + // is fullscreen this may return a different value. If you only care about + // whether or not it's possible for the browser to support a particular + // feature use |CanSupportWindowFeature|. + bool SupportsWindowFeature(WindowFeature feature) const; + + // Returns true if the Browser can support the specified feature. See comment + // in |SupportsWindowFeature| for details on this. + bool CanSupportWindowFeature(WindowFeature feature) const; + +// TODO(port): port these, and re-merge the two function declaration lists. + // Page-related commands. + void Print(); + void EmailPageLocation(); + void ToggleEncodingAutoDetect(); + void OverrideEncoding(int encoding_id); + + // Clipboard commands + void Cut(); + void Copy(); + void Paste(); + + // Find-in-page + void Find(); + void FindNext(); + void FindPrevious(); + + // Zoom + void Zoom(PageZoom::Function zoom_function); + + // Focus various bits of UI + void FocusToolbar(); + void FocusLocationBar(); // Also selects any existing text. + void FocusSearch(); + void FocusAppMenu(); + void FocusBookmarksToolbar(); + void FocusChromeOSStatus(); + void FocusNextPane(); + void FocusPreviousPane(); + + // Show various bits of UI + void OpenFile(); + void OpenCreateShortcutsDialog(); + void ToggleDevToolsWindow(DevToolsToggleAction action); + void OpenTaskManager(); + void OpenBugReportDialog(); + + void ToggleBookmarkBar(); + + void OpenBookmarkManager(); + void ShowAppMenu(); + void ShowBookmarkManagerTab(); + void ShowHistoryTab(); + void ShowDownloadsTab(); + void ShowExtensionsTab(); + void ShowAboutConflictsTab(); + void ShowBrokenPageTab(TabContents* contents); + void ShowOptionsTab(const std::string& sub_page); + void OpenClearBrowsingDataDialog(); + void OpenOptionsDialog(); + void OpenKeywordEditor(); + void OpenPasswordManager(); + void OpenSyncMyBookmarksDialog(); +#if defined(ENABLE_REMOTING) + void OpenRemotingSetupDialog(); +#endif + void OpenImportSettingsDialog(); + void OpenAboutChromeDialog(); + void OpenUpdateChromeDialog(); + void OpenHelpTab(); + // Used by the "Get themes" link in the options dialog. + void OpenThemeGalleryTabAndActivate(); + void OpenAutoFillHelpTabAndActivate(); + void OpenPrivacyDashboardTabAndActivate(); + void OpenSearchEngineOptionsDialog(); +#if defined(OS_CHROMEOS) + void OpenSystemOptionsDialog(); + void OpenInternetOptionsDialog(); + void OpenLanguageOptionsDialog(); + void OpenSystemTabAndActivate(); + void OpenMobilePlanTabAndActivate(); +#endif + void OpenPluginsTabAndActivate(); + + virtual void UpdateDownloadShelfVisibility(bool visible); + + // Overridden from TabStripModelDelegate: + virtual bool UseVerticalTabs() const; + + ///////////////////////////////////////////////////////////////////////////// + + // Sets the value of homepage related prefs to new values. Since we do not + // want to change these values for existing users, we can not change the + // default values under RegisterUserPrefs. Also if user already has an + // existing profile we do not want to override those preferences so we only + // set new values if they have not been set already. This method gets called + // during First Run. + static void SetNewHomePagePrefs(PrefService* prefs); + + static void RegisterPrefs(PrefService* prefs); + static void RegisterUserPrefs(PrefService* prefs); + + // Helper function to run unload listeners on a TabContents. + static bool RunUnloadEventsHelper(TabContents* contents); + + // Returns the Browser which contains the tab with the given + // NavigationController, also filling in |index| (if valid) with the tab's + // index in the tab strip. + // Returns NULL if not found. + // This call is O(N) in the number of tabs. + static Browser* GetBrowserForController( + const NavigationController* controller, int* index); + + // Retrieve the last active tabbed browser with a profile matching |profile|. + static Browser* GetTabbedBrowser(Profile* profile, bool match_incognito); + + // Retrieve the last active tabbed browser with a profile matching |profile|. + // Creates a new Browser if none are available. + static Browser* GetOrCreateTabbedBrowser(Profile* profile); + + // Calls ExecuteCommandWithDisposition with the given disposition. + void ExecuteCommandWithDisposition(int id, WindowOpenDisposition); + + // Returns whether the |id| is a reserved command, whose keyboard shortcuts + // should not be sent to the renderer. + bool IsReservedCommand(int id); + + // Sets if command execution shall be blocked. If |block| is true then + // following calls to ExecuteCommand() or ExecuteCommandWithDisposition() + // method will not execute the command, and the last blocked command will be + // recorded for retrieval. + void SetBlockCommandExecution(bool block); + + // Gets the last blocked command after calling SetBlockCommandExecution(true). + // Returns the command id or -1 if there is no command blocked. The + // disposition type of the command will be stored in |*disposition| if it's + // not null. + int GetLastBlockedCommand(WindowOpenDisposition* disposition); + + // Called by browser::Navigate() when a navigation has occurred in a tab in + // this Browser. Updates the UI for the start of this navigation. + void UpdateUIForNavigationInTab(TabContents* contents, + PageTransition::Type transition, + bool user_initiated); + + // Called by browser::Navigate() to retrieve the home page if no URL is + // specified. + GURL GetHomePage() const; + + // Interface implementations //////////////////////////////////////////////// + + // Overridden from PageNavigator: + virtual void OpenURL(const GURL& url, const GURL& referrer, + WindowOpenDisposition disposition, + PageTransition::Type transition); + + // Overridden from CommandUpdater::CommandUpdaterDelegate: + virtual void ExecuteCommand(int id); + + // Overridden from TabRestoreServiceObserver: + virtual void TabRestoreServiceChanged(TabRestoreService* service); + virtual void TabRestoreServiceDestroyed(TabRestoreService* service); + + + // Overridden from TabHandlerDelegate: + virtual Profile* GetProfile() const; + virtual Browser* AsBrowser(); + + // Overridden from TabStripModelDelegate: + virtual TabContents* AddBlankTab(bool foreground); + virtual TabContents* AddBlankTabAt(int index, bool foreground); + virtual Browser* CreateNewStripWithContents(TabContents* detached_contents, + const gfx::Rect& window_bounds, + const DockInfo& dock_info, + bool maximize); + virtual void ContinueDraggingDetachedTab(TabContents* contents, + const gfx::Rect& window_bounds, + const gfx::Rect& tab_bounds); + virtual int GetDragActions() const; + // Construct a TabContents for a given URL, profile and transition type. + // If instance is not null, its process will be used to render the tab. + virtual TabContents* CreateTabContentsForURL(const GURL& url, + const GURL& referrer, + Profile* profile, + PageTransition::Type transition, + bool defer_load, + SiteInstance* instance) const; + virtual bool CanDuplicateContentsAt(int index); + virtual void DuplicateContentsAt(int index); + virtual void CloseFrameAfterDragSession(); + virtual void CreateHistoricalTab(TabContents* contents); + virtual bool RunUnloadListenerBeforeClosing(TabContents* contents); + virtual bool CanCloseContentsAt(int index); + virtual bool CanBookmarkAllTabs() const; + virtual void BookmarkAllTabs(); + virtual bool CanCloseTab() const; + virtual void ToggleUseVerticalTabs(); + virtual bool CanRestoreTab(); + virtual void RestoreTab(); + virtual bool LargeIconsPermitted() const; + + // Overridden from TabStripModelObserver: + virtual void TabInsertedAt(TabContents* contents, + int index, + bool foreground); + virtual void TabClosingAt(TabStripModel* tab_strip_model, + TabContents* contents, + int index); + virtual void TabDetachedAt(TabContents* contents, int index); + virtual void TabDeselectedAt(TabContents* contents, int index); + virtual void TabSelectedAt(TabContents* old_contents, + TabContents* new_contents, + int index, + bool user_gesture); + virtual void TabMoved(TabContents* contents, + int from_index, + int to_index); + virtual void TabReplacedAt(TabContents* old_contents, + TabContents* new_contents, + int index); + virtual void TabPinnedStateChanged(TabContents* contents, int index); + virtual void TabStripEmpty(); + + private: + FRIEND_TEST_ALL_PREFIXES(BrowserTest, NoTabsInPopups); + + // Used to describe why a tab is being detached. This is used by + // TabDetachedAtImpl. + enum DetachType { + // Result of TabDetachedAt. + DETACH_TYPE_DETACH, + + // Result of TabReplacedAt. + DETACH_TYPE_REPLACE, + + // Result of the tab strip not having any significant tabs. + DETACH_TYPE_EMPTY + }; + + // Overridden from TabContentsDelegate: + virtual void OpenURLFromTab(TabContents* source, + const GURL& url, + const GURL& referrer, + WindowOpenDisposition disposition, + PageTransition::Type transition); + virtual void NavigationStateChanged(const TabContents* source, + unsigned changed_flags); + virtual void AddNewContents(TabContents* source, + TabContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture); + virtual void ActivateContents(TabContents* contents); + virtual void DeactivateContents(TabContents* contents); + virtual void LoadingStateChanged(TabContents* source); + virtual void CloseContents(TabContents* source); + virtual void MoveContents(TabContents* source, const gfx::Rect& pos); + virtual void DetachContents(TabContents* source); + virtual bool IsPopup(const TabContents* source) const; + virtual bool CanReloadContents(TabContents* source) const; + virtual void ToolbarSizeChanged(TabContents* source, bool is_animating); + virtual void URLStarredChanged(TabContents* source, bool starred); + virtual void UpdateTargetURL(TabContents* source, const GURL& url); + virtual void ContentsMouseEvent( + TabContents* source, const gfx::Point& location, bool motion); + virtual void ContentsZoomChange(bool zoom_in); + virtual void OnContentSettingsChange(TabContents* source); + virtual void SetTabContentBlocked(TabContents* contents, bool blocked); + virtual void TabContentsFocused(TabContents* tab_content); + virtual bool TakeFocus(bool reverse); + virtual bool IsApplication() const; + virtual void ConvertContentsToApplication(TabContents* source); + virtual bool ShouldDisplayURLField(); + virtual gfx::Rect GetRootWindowResizerRect() const; + virtual void ShowHtmlDialog(HtmlDialogUIDelegate* delegate, + gfx::NativeWindow parent_window); + virtual void BeforeUnloadFired(TabContents* source, + bool proceed, + bool* proceed_to_fire_unload); + virtual void SetFocusToLocationBar(bool select_all); + virtual void RenderWidgetShowing(); + virtual int GetExtraRenderViewHeight() const; + virtual void OnStartDownload(DownloadItem* download, TabContents* tab); + virtual void ConfirmSetDefaultSearchProvider( + TabContents* tab_contents, + TemplateURL* template_url, + TemplateURLModel* template_url_model); + virtual void ConfirmAddSearchProvider(const TemplateURL* template_url, + Profile* profile); + virtual void ShowPageInfo(Profile* profile, + const GURL& url, + const NavigationEntry::SSLStatus& ssl, + bool show_history); + virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, + bool* is_keyboard_shortcut); + virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event); + virtual void ShowRepostFormWarningDialog(TabContents* tab_contents); + virtual void ShowContentSettingsWindow(ContentSettingsType content_type); + virtual void ShowCollectedCookiesDialog(TabContents* tab_contents); + virtual bool ShouldAddNavigationToHistory( + const history::HistoryAddPageArgs& add_page_args, + NavigationType::Type navigation_type); + virtual void OnDidGetApplicationInfo(TabContents* tab_contents, + int32 page_id); + virtual void ContentRestrictionsChanged(TabContents* source); + + // Overridden from SelectFileDialog::Listener: + virtual void FileSelected(const FilePath& path, int index, void* params); + + // Overridden from NotificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Overridden from ProfileSyncServiceObserver: + virtual void OnStateChanged(); + + // Overriden from InstantDelegate: + virtual void ShowInstant(TabContents* preview_contents); + virtual void HideInstant(); + virtual void CommitInstant(TabContents* preview_contents); + virtual void SetSuggestedText(const string16& text); + virtual gfx::Rect GetInstantBounds(); + + // Command and state updating /////////////////////////////////////////////// + + // Initialize state for all browser commands. + void InitCommandState(); + + // Update commands whose state depends on the tab's state. + void UpdateCommandsForTabState(); + + // Updates commands when the content's restrictions change. + void UpdateCommandsForContentRestrictionState(); + + // Updates commands for enabling developer tools. + void UpdateCommandsForDevTools(); + + // Updates the printing command state. + void UpdatePrintingState(int content_restrictions); + + // Ask the Reload/Stop button to change its icon, and update the Stop command + // state. |is_loading| is true if the current TabContents is loading. + // |force| is true if the button should change its icon immediately. + void UpdateReloadStopState(bool is_loading, bool force); + + // UI update coalescing and handling //////////////////////////////////////// + + // Asks the toolbar (and as such the location bar) to update its state to + // reflect the current tab's current URL, security state, etc. + // If |should_restore_state| is true, we're switching (back?) to this tab and + // should restore any previous location bar state (such as user editing) as + // well. + void UpdateToolbar(bool should_restore_state); + + // Does one or both of the following for each bit in |changed_flags|: + // . If the update should be processed immediately, it is. + // . If the update should processed asynchronously (to avoid lots of ui + // updates), then scheduled_updates_ is updated for the |source| and update + // pair and a task is scheduled (assuming it isn't running already) + // that invokes ProcessPendingUIUpdates. + void ScheduleUIUpdate(const TabContents* source, unsigned changed_flags); + + // Processes all pending updates to the UI that have been scheduled by + // ScheduleUIUpdate in scheduled_updates_. + void ProcessPendingUIUpdates(); + + // Removes all entries from scheduled_updates_ whose source is contents. + void RemoveScheduledUpdatesFor(TabContents* contents); + + // Getters for UI /////////////////////////////////////////////////////////// + + // TODO(beng): remove, and provide AutomationProvider a better way to access + // the LocationBarView's edit. + friend class AutomationProvider; + friend class TestingAutomationProvider; + + // Returns the StatusBubble from the current toolbar. It is possible for + // this to return NULL if called before the toolbar has initialized. + // TODO(beng): remove this. + StatusBubble* GetStatusBubble(); + + // Session restore functions //////////////////////////////////////////////// + + // Notifies the history database of the index for all tabs whose index is + // >= index. + void SyncHistoryWithTabs(int index); + + // OnBeforeUnload handling ////////////////////////////////////////////////// + + typedef std::set<TabContents*> UnloadListenerSet; + + // Processes the next tab that needs it's beforeunload/unload event fired. + void ProcessPendingTabs(); + + // Whether we've completed firing all the tabs' beforeunload/unload events. + bool HasCompletedUnloadProcessing() const; + + // Clears all the state associated with processing tabs' beforeunload/unload + // events since the user cancelled closing the window. + void CancelWindowClose(); + + // Removes |tab| from the passed |set|. + // Returns whether the tab was in the set in the first place. + // TODO(beng): this method needs a better name! + bool RemoveFromSet(UnloadListenerSet* set, TabContents* tab); + + // Cleans up state appropriately when we are trying to close the browser and + // the tab has finished firing its unload handler. We also use this in the + // cases where a tab crashes or hangs even if the beforeunload/unload haven't + // successfully fired. + void ClearUnloadState(TabContents* tab); + + // In-progress download termination handling ///////////////////////////////// + + // Called when the window is closing to check if potential in-progress + // downloads should prevent it from closing. + // Returns true if the window can close, false otherwise. + bool CanCloseWithInProgressDownloads(); + + // Assorted utility functions /////////////////////////////////////////////// + + // Checks whether |source| is about to navigate across extension extents, and + // if so, navigates in the correct window. For example if this is a normal + // browser and we're about to navigate into an extent, this method will + // navigate the app's window instead. If we're in an app window and + // navigating out of the app, this method will find and navigate a normal + // browser instead. + // + // Returns true if the navigation was handled, eg, it was opened in some other + // browser. + // + // Returns false if it was not handled. In this case, the method may also + // modify |disposition| to a more suitable value. + bool HandleCrossAppNavigation(TabContents* source, + const GURL& url, + const GURL& referrer, + WindowOpenDisposition *disposition, + PageTransition::Type transition); + + // Shows the Find Bar, optionally selecting the next entry that matches the + // existing search string for that Tab. |forward_direction| controls the + // search direction. + void FindInPage(bool find_next, bool forward_direction); + + // Closes the frame. + // TODO(beng): figure out if we need this now that the frame itself closes + // after a return to the message loop. + void CloseFrame(); + + void TabDetachedAtImpl(TabContents* contents, int index, DetachType type); + + // Create a preference dictionary for the provided application name. This is + // done only once per application name / per session. + static void RegisterAppPrefs(const std::string& app_name); + + // Shared code between Reload() and ReloadIgnoringCache(). + void ReloadInternal(WindowOpenDisposition disposition, bool ignore_cache); + + // Return true if the window dispositions means opening a new tab. + bool ShouldOpenNewTabForWindowDisposition(WindowOpenDisposition disposition); + + // Depending on the disposition, return the current tab or a clone of the + // current tab. + TabContents* GetOrCloneTabForDisposition(WindowOpenDisposition disposition); + + // Sets the insertion policy of the tabstrip based on whether vertical tabs + // are enabled. + void UpdateTabStripModelInsertionPolicy(); + + // Invoked when the use vertical tabs preference changes. Resets the insertion + // policy of the tab strip model and notifies the window. + void UseVerticalTabsChanged(); + + // Implementation of SupportsWindowFeature and CanSupportWindowFeature. If + // |check_fullscreen| is true, the set of features reflect the actual state of + // the browser, otherwise the set of features reflect the possible state of + // the browser. + bool SupportsWindowFeatureImpl(WindowFeature feature, + bool check_fullscreen) const; + + // Determines if closing of browser can really be permitted after normal + // sequence of downloads and unload handlers have given the go-ahead to close. + // It is called from ShouldCloseWindow. It checks with + // TabCloseableStateWatcher to confirm if browser can really be closed. + // Appropriate action is taken by watcher as it sees fit. + // If watcher denies closing of browser, CancelWindowClose is called to + // cancel closing of window. + bool IsClosingPermitted(); + + // Commits the current instant, returning true on success. This is intended + // for use from OpenCurrentURL. + bool OpenInstant(WindowOpenDisposition disposition); + + // If this browser should have instant one is created, otherwise does nothing. + void CreateInstantIfNecessary(); + + // Data members ///////////////////////////////////////////////////////////// + + NotificationRegistrar registrar_; + + // This Browser's type. + const Type type_; + + // This Browser's profile. + Profile* const profile_; + + // This Browser's window. + BrowserWindow* window_; + + // This Browser's current TabHandler. + scoped_ptr<TabHandler> tab_handler_; + + // The CommandUpdater that manages the browser window commands. + CommandUpdater command_updater_; + + // An optional application name which is used to retrieve and save window + // positions. + std::string app_name_; + + // Unique identifier of this browser for session restore. This id is only + // unique within the current session, and is not guaranteed to be unique + // across sessions. + const SessionID session_id_; + + // The model for the toolbar view. + ToolbarModel toolbar_model_; + + // UI update coalescing and handling //////////////////////////////////////// + + typedef std::map<const TabContents*, int> UpdateMap; + + // Maps from TabContents to pending UI updates that need to be processed. + // We don't update things like the URL or tab title right away to avoid + // flickering and extra painting. + // See ScheduleUIUpdate and ProcessPendingUIUpdates. + UpdateMap scheduled_updates_; + + // The following factory is used for chrome update coalescing. + ScopedRunnableMethodFactory<Browser> chrome_updater_factory_; + + // OnBeforeUnload handling ////////////////////////////////////////////////// + + // Tracks tabs that need there beforeunload event fired before we can + // close the browser. Only gets populated when we try to close the browser. + UnloadListenerSet tabs_needing_before_unload_fired_; + + // Tracks tabs that need there unload event fired before we can + // close the browser. Only gets populated when we try to close the browser. + UnloadListenerSet tabs_needing_unload_fired_; + + // Whether we are processing the beforeunload and unload events of each tab + // in preparation for closing the browser. + bool is_attempting_to_close_browser_; + + // In-progress download termination handling ///////////////////////////////// + + enum CancelDownloadConfirmationState { + NOT_PROMPTED, // We have not asked the user. + WAITING_FOR_RESPONSE, // We have asked the user and have not received a + // reponse yet. + RESPONSE_RECEIVED // The user was prompted and made a decision already. + }; + + // State used to figure-out whether we should prompt the user for confirmation + // when the browser is closed with in-progress downloads. + CancelDownloadConfirmationState cancel_download_confirmation_state_; + + ///////////////////////////////////////////////////////////////////////////// + + // Override values for the bounds of the window and its maximized state. + // These are supplied by callers that don't want to use the default values. + // The default values are typically loaded from local state (last session), + // obtained from the last window of the same type, or obtained from the + // shell shortcut's startup info. + gfx::Rect override_bounds_; + MaximizedState maximized_state_; + + // The following factory is used to close the frame at a later time. + ScopedRunnableMethodFactory<Browser> method_factory_; + + // The Find Bar. This may be NULL if there is no Find Bar, and if it is + // non-NULL, it may or may not be visible. + scoped_ptr<FindBarController> find_bar_controller_; + + // Dialog box used for opening and saving files. + scoped_refptr<SelectFileDialog> select_file_dialog_; + + // Keep track of the encoding auto detect pref. + BooleanPrefMember encoding_auto_detect_; + + // Keep track of the printing enabled pref. + BooleanPrefMember printing_enabled_; + + // Keep track of the development tools disabled pref. + BooleanPrefMember dev_tools_disabled_; + + // Keep track of when instant enabled changes. + BooleanPrefMember instant_enabled_; + + // Indicates if command execution is blocked. + bool block_command_execution_; + + // Stores the last blocked command id when |block_command_execution_| is true. + int last_blocked_command_id_; + + // Stores the disposition type of the last blocked command. + WindowOpenDisposition last_blocked_command_disposition_; + + // Different types of action when web app info is available. + // OnDidGetApplicationInfo uses this to dispatch calls. + enum WebAppAction { + NONE, // No action at all. + CREATE_SHORTCUT, // Bring up create application shortcut dialog. + UPDATE_SHORTCUT // Update icon for app shortcut. + }; + + // Which deferred action to perform when OnDidGetApplicationInfo is notified + // from a TabContents. Currently, only one pending action is allowed. + WebAppAction pending_web_app_action_; + + // The extension app associated with this window, if any. + const Extension* extension_app_; + + // Tracks the display mode of the tabstrip. + mutable BooleanPrefMember use_vertical_tabs_; + + // The profile's tab restore service. The service is owned by the profile, + // and we install ourselves as an observer. + TabRestoreService* tab_restore_service_; + + scoped_ptr<InstantController> instant_; + + DISALLOW_COPY_AND_ASSIGN(Browser); +}; + +#endif // CHROME_BROWSER_UI_BROWSER_H_ diff --git a/chrome/browser/ui/browser_init.cc b/chrome/browser/ui/browser_init.cc new file mode 100644 index 0000000..f28029f --- /dev/null +++ b/chrome/browser/ui/browser_init.cc @@ -0,0 +1,1035 @@ +// 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/browser_init.h" + +#include <algorithm> // For max(). + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/environment.h" +#include "base/event_recorder.h" +#include "base/file_path.h" +#include "base/metrics/histogram.h" +#include "base/path_service.h" +#include "base/scoped_ptr.h" +#include "base/string_number_conversions.h" +#include "base/thread_restrictions.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/automation/automation_provider.h" +#include "chrome/browser/automation/automation_provider_list.h" +#include "chrome/browser/automation/chrome_frame_automation_provider.h" +#include "chrome/browser/automation/testing_automation_provider.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_navigator.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/child_process_security_policy.h" +#include "chrome/browser/defaults.h" +#include "chrome/browser/extensions/extension_creator.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/extensions/pack_extension_job.h" +#include "chrome/browser/first_run/first_run.h" +#include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/url_fixer_upper.h" +#include "chrome/browser/notifications/desktop_notification_service.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/prefs/session_startup_pref.h" +#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/search_engines/template_url.h" +#include "chrome/browser/search_engines/template_url_model.h" +#include "chrome/browser/sessions/session_restore.h" +#include "chrome/browser/sessions/session_service.h" +#include "chrome/browser/shell_integration.h" +#include "chrome/browser/tab_contents/infobar_delegate.h" +#include "chrome/browser/tab_contents/navigation_controller.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/browser/tabs/pinned_tab_codec.h" +#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/result_codes.h" +#include "chrome/common/url_constants.h" +#include "chrome/installer/util/browser_distribution.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" +#include "grit/theme_resources.h" +#include "net/base/net_util.h" +#include "net/url_request/url_request.h" +#include "webkit/glue/webkit_glue.h" + +#if defined(OS_MACOSX) +#include "chrome/browser/cocoa/keystone_infobar.h" +#endif + +#if defined(OS_WIN) +#include "app/win_util.h" +#endif + +#if defined(TOOLKIT_GTK) +#include "chrome/browser/gtk/gtk_util.h" +#endif + +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/mount_library.h" +#include "chrome/browser/chromeos/cros/network_library.h" +#include "chrome/browser/chromeos/customization_document.h" +#include "chrome/browser/chromeos/gview_request_interceptor.h" +#include "chrome/browser/chromeos/low_battery_observer.h" +#include "chrome/browser/chromeos/network_message_observer.h" +#include "chrome/browser/chromeos/network_state_notifier.h" +#include "chrome/browser/chromeos/system_key_event_listener.h" +#include "chrome/browser/chromeos/update_observer.h" +#include "chrome/browser/chromeos/usb_mount_observer.h" +#include "chrome/browser/chromeos/wm_message_listener.h" +#include "chrome/browser/chromeos/wm_overview_controller.h" +#include "chrome/browser/dom_ui/mediaplayer_ui.h" +#endif + +namespace { + +class SetAsDefaultBrowserTask : public Task { + public: + SetAsDefaultBrowserTask() { } + virtual void Run() { + ShellIntegration::SetAsDefaultBrowser(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserTask); +}; + +// The delegate for the infobar shown when Chrome is not the default browser. +class DefaultBrowserInfoBarDelegate : public ConfirmInfoBarDelegate { + public: + explicit DefaultBrowserInfoBarDelegate(TabContents* contents) + : ConfirmInfoBarDelegate(contents), + profile_(contents->profile()), + action_taken_(false), + should_expire_(false), + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { + // We want the info-bar to stick-around for few seconds and then be hidden + // on the next navigation after that. + MessageLoop::current()->PostDelayedTask(FROM_HERE, + method_factory_.NewRunnableMethod( + &DefaultBrowserInfoBarDelegate::Expire), + 8000); // 8 seconds. + } + + virtual bool ShouldExpire( + const NavigationController::LoadCommittedDetails& details) const { + return should_expire_; + } + + // Overridden from ConfirmInfoBarDelegate: + virtual void InfoBarClosed() { + if (!action_taken_) + UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.Ignored", 1); + delete this; + } + + virtual string16 GetMessageText() const { + return l10n_util::GetStringUTF16(IDS_DEFAULT_BROWSER_INFOBAR_SHORT_TEXT); + } + + virtual SkBitmap* GetIcon() const { + return ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_PRODUCT_ICON_32); + } + + virtual int GetButtons() const { + return BUTTON_OK | BUTTON_CANCEL | BUTTON_OK_DEFAULT; + } + + virtual string16 GetButtonLabel(InfoBarButton button) const { + return button == BUTTON_OK ? + l10n_util::GetStringUTF16(IDS_SET_AS_DEFAULT_INFOBAR_BUTTON_LABEL) : + l10n_util::GetStringUTF16(IDS_DONT_ASK_AGAIN_INFOBAR_BUTTON_LABEL); + } + + virtual bool NeedElevation(InfoBarButton button) const { + return button == BUTTON_OK; + } + + virtual bool Accept() { + action_taken_ = true; + UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.SetAsDefault", 1); + g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, + new SetAsDefaultBrowserTask()); + return true; + } + + virtual bool Cancel() { + action_taken_ = true; + UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.DontSetAsDefault", 1); + // User clicked "Don't ask me again", remember that. + profile_->GetPrefs()->SetBoolean(prefs::kCheckDefaultBrowser, false); + return true; + } + + void Expire() { + should_expire_ = true; + } + + private: + // The Profile that we restore sessions from. + Profile* profile_; + + // Whether the user clicked one of the buttons. + bool action_taken_; + + // Whether the info-bar should be dismissed on the next navigation. + bool should_expire_; + + // Used to delay the expiration of the info-bar. + ScopedRunnableMethodFactory<DefaultBrowserInfoBarDelegate> method_factory_; + + DISALLOW_COPY_AND_ASSIGN(DefaultBrowserInfoBarDelegate); +}; + +class NotifyNotDefaultBrowserTask : public Task { + public: + NotifyNotDefaultBrowserTask() { } + + virtual void Run() { + Browser* browser = BrowserList::GetLastActive(); + if (!browser) { + // Reached during ui tests. + return; + } + TabContents* tab = browser->GetSelectedTabContents(); + // Don't show the info-bar if there are already info-bars showing. + // In ChromeBot tests, there might be a race. This line appears to get + // called during shutdown and |tab| can be NULL. + if (!tab || tab->infobar_delegate_count() > 0) + return; + tab->AddInfoBar(new DefaultBrowserInfoBarDelegate(tab)); + } + + private: + DISALLOW_COPY_AND_ASSIGN(NotifyNotDefaultBrowserTask); +}; + +class CheckDefaultBrowserTask : public Task { + public: + CheckDefaultBrowserTask() { + } + + virtual void Run() { + if (ShellIntegration::IsDefaultBrowser()) + return; +#if defined(OS_WIN) + if (!BrowserDistribution::GetDistribution()->CanSetAsDefault()) + return; +#endif + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, new NotifyNotDefaultBrowserTask()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(CheckDefaultBrowserTask); +}; + +// A delegate for the InfoBar shown when the previous session has crashed. The +// bar deletes itself automatically after it is closed. +class SessionCrashedInfoBarDelegate : public ConfirmInfoBarDelegate { + public: + explicit SessionCrashedInfoBarDelegate(TabContents* contents) + : ConfirmInfoBarDelegate(contents), + profile_(contents->profile()) { + } + + // Overridden from ConfirmInfoBarDelegate: + virtual void InfoBarClosed() { + delete this; + } + virtual string16 GetMessageText() const { + return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE); + } + virtual SkBitmap* GetIcon() const { + return ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_INFOBAR_RESTORE_SESSION); + } + virtual int GetButtons() const { return BUTTON_OK; } + virtual string16 GetButtonLabel(InfoBarButton button) const { + return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON); + } + virtual bool Accept() { + // Restore the session. + SessionRestore::RestoreSession(profile_, NULL, true, false, + std::vector<GURL>()); + return true; + } + + private: + // The Profile that we restore sessions from. + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(SessionCrashedInfoBarDelegate); +}; + +SessionStartupPref GetSessionStartupPref(const CommandLine& command_line, + Profile* profile) { + SessionStartupPref pref = SessionStartupPref::GetStartupPref(profile); + if (command_line.HasSwitch(switches::kRestoreLastSession)) + pref.type = SessionStartupPref::LAST; + if (command_line.HasSwitch(switches::kIncognito) && + pref.type == SessionStartupPref::LAST) { + // We don't store session information when incognito. If the user has + // chosen to restore last session and launched incognito, fallback to + // default launch behavior. + pref.type = SessionStartupPref::DEFAULT; + } + return pref; +} + +enum LaunchMode { + LM_TO_BE_DECIDED = 0, // Possibly direct launch or via a shortcut. + LM_AS_WEBAPP, // Launched as a installed web application. + LM_WITH_URLS, // Launched with urls in the cmd line. + LM_SHORTCUT_NONE, // Not launched from a shortcut. + LM_SHORTCUT_NONAME, // Launched from shortcut but no name available. + LM_SHORTCUT_UNKNOWN, // Launched from user-defined shortcut. + LM_SHORTCUT_QUICKLAUNCH, // Launched from the quick launch bar. + LM_SHORTCUT_DESKTOP, // Launched from a desktop shortcut. + LM_SHORTCUT_STARTMENU, // Launched from start menu. + LM_LINUX_MAC_BEOS // Other OS buckets start here. +}; + +#if defined(OS_WIN) +// Undocumented flag in the startup info structure tells us what shortcut was +// used to launch the browser. See http://www.catch22.net/tuts/undoc01 for +// more information. Confirmed to work on XP, Vista and Win7. +LaunchMode GetLaunchShortcutKind() { + STARTUPINFOW si = { sizeof(si) }; + GetStartupInfoW(&si); + if (si.dwFlags & 0x800) { + if (!si.lpTitle) + return LM_SHORTCUT_NONAME; + std::wstring shortcut(si.lpTitle); + // The windows quick launch path is not localized. + if (shortcut.find(L"\\Quick Launch\\") != std::wstring::npos) + return LM_SHORTCUT_QUICKLAUNCH; + scoped_ptr<base::Environment> env(base::Environment::Create()); + std::string appdata_path; + env->GetVar("USERPROFILE", &appdata_path); + if (!appdata_path.empty() && + shortcut.find(ASCIIToWide(appdata_path)) != std::wstring::npos) + return LM_SHORTCUT_DESKTOP; + return LM_SHORTCUT_UNKNOWN; + } + return LM_SHORTCUT_NONE; +} +#else +// TODO(cpu): Port to other platforms. +LaunchMode GetLaunchShortcutKind() { + return LM_LINUX_MAC_BEOS; +} +#endif + +// Log in a histogram the frequency of launching by the different methods. See +// LaunchMode enum for the actual values of the buckets. +void RecordLaunchModeHistogram(LaunchMode mode) { + int bucket = (mode == LM_TO_BE_DECIDED) ? GetLaunchShortcutKind() : mode; + UMA_HISTOGRAM_COUNTS_100("Launch.Modes", bucket); +} + +static bool in_startup = false; + +GURL GetWelcomePageURL() { + std::string welcome_url = l10n_util::GetStringUTF8(IDS_WELCOME_PAGE_URL); + return GURL(welcome_url); +} + +void UrlsToTabs(const std::vector<GURL>& urls, + std::vector<BrowserInit::LaunchWithProfile::Tab>* tabs) { + for (size_t i = 0; i < urls.size(); ++i) { + BrowserInit::LaunchWithProfile::Tab tab; + tab.is_pinned = false; + tab.url = urls[i]; + tabs->push_back(tab); + } +} + +} // namespace + +BrowserInit::BrowserInit() {} + +BrowserInit::~BrowserInit() {} + +void BrowserInit::AddFirstRunTab(const GURL& url) { + first_run_tabs_.push_back(url); +} + +// static +bool BrowserInit::InProcessStartup() { + return in_startup; +} + +bool BrowserInit::LaunchBrowser(const CommandLine& command_line, + Profile* profile, + const FilePath& cur_dir, + bool process_startup, + int* return_code) { + in_startup = process_startup; + DCHECK(profile); + + // Continue with the off-the-record profile from here on if --incognito + if (command_line.HasSwitch(switches::kIncognito)) + profile = profile->GetOffTheRecordProfile(); + + BrowserInit::LaunchWithProfile lwp(cur_dir, command_line, this); + bool launched = lwp.Launch(profile, process_startup); + in_startup = false; + + if (!launched) { + LOG(ERROR) << "launch error"; + if (return_code) + *return_code = ResultCodes::INVALID_CMDLINE_URL; + return false; + } + +#if defined(OS_CHROMEOS) + // Create the WmMessageListener so that it can listen for messages regardless + // of what window has focus. + chromeos::WmMessageListener::instance(); + + // Create the SystemKeyEventListener so it can listen for system keyboard + // messages regardless of focus. + chromeos::SystemKeyEventListener::instance(); + + // Create the WmOverviewController so it can register with the listener. + chromeos::WmOverviewController::instance(); + + // Install the GView request interceptor that will redirect requests + // of compatible documents (PDF, etc) to the GView document viewer. + const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); + if (parsed_command_line.HasSwitch(switches::kEnableGView)) { + chromeos::GViewRequestInterceptor::GetGViewRequestInterceptor(); + } + if (process_startup) { + // TODO(dhg): Try to make this just USBMountObserver::Get()->set_profile + // and have the constructor take care of everything else. + chromeos::MountLibrary* lib = + chromeos::CrosLibrary::Get()->GetMountLibrary(); + chromeos::USBMountObserver* observe = chromeos::USBMountObserver::Get(); + lib->AddObserver(observe); + observe->ScanForDevices(lib); + // Connect the chromeos notifications + + // This observer is a singleton. It is never deleted but the pointer is kept + // in a global so that it isn't reported as a leak. + static chromeos::LowBatteryObserver* low_battery_observer = + new chromeos::LowBatteryObserver(profile); + chromeos::CrosLibrary::Get()->GetPowerLibrary()->AddObserver( + low_battery_observer); + + static chromeos::UpdateObserver* update_observer = + new chromeos::UpdateObserver(profile); + chromeos::CrosLibrary::Get()->GetUpdateLibrary()->AddObserver( + update_observer); + + static chromeos::NetworkMessageObserver* network_message_observer = + new chromeos::NetworkMessageObserver(profile); + chromeos::CrosLibrary::Get()->GetNetworkLibrary() + ->AddNetworkManagerObserver(network_message_observer); + chromeos::CrosLibrary::Get()->GetNetworkLibrary() + ->AddCellularDataPlanObserver(network_message_observer); + + chromeos::CrosLibrary::Get()->GetNetworkLibrary() + ->AddNetworkManagerObserver(chromeos::NetworkStateNotifier::Get()); + } +#endif + return true; +} + +// Tab ------------------------------------------------------------------------ + +BrowserInit::LaunchWithProfile::Tab::Tab() : is_app(false), is_pinned(true) {} + +BrowserInit::LaunchWithProfile::Tab::~Tab() {} + +// LaunchWithProfile ---------------------------------------------------------- + +BrowserInit::LaunchWithProfile::LaunchWithProfile( + const FilePath& cur_dir, + const CommandLine& command_line) + : cur_dir_(cur_dir), + command_line_(command_line), + profile_(NULL), + browser_init_(NULL) { +} + +BrowserInit::LaunchWithProfile::LaunchWithProfile( + const FilePath& cur_dir, + const CommandLine& command_line, + BrowserInit* browser_init) + : cur_dir_(cur_dir), + command_line_(command_line), + profile_(NULL), + browser_init_(browser_init) { +} + +BrowserInit::LaunchWithProfile::~LaunchWithProfile() { +} + +bool BrowserInit::LaunchWithProfile::Launch(Profile* profile, + bool process_startup) { + DCHECK(profile); + profile_ = profile; + + if (command_line_.HasSwitch(switches::kDnsLogDetails)) + chrome_browser_net::EnablePredictorDetailedLog(true); + if (command_line_.HasSwitch(switches::kDnsPrefetchDisable)) + chrome_browser_net::EnablePredictor(false); + + if (command_line_.HasSwitch(switches::kDumpHistogramsOnExit)) + base::StatisticsRecorder::set_dump_on_exit(true); + + if (command_line_.HasSwitch(switches::kRemoteShellPort)) { + std::string port_str = + command_line_.GetSwitchValueASCII(switches::kRemoteShellPort); + int64 port; + if (base::StringToInt64(port_str, &port) && port > 0 && port < 65535) + g_browser_process->InitDebuggerWrapper(static_cast<int>(port), false); + else + DLOG(WARNING) << "Invalid remote shell port number " << port; + } else if (command_line_.HasSwitch(switches::kRemoteDebuggingPort)) { + std::string port_str = + command_line_.GetSwitchValueASCII(switches::kRemoteDebuggingPort); + int64 port; + if (base::StringToInt64(port_str, &port) && port > 0 && port < 65535) + g_browser_process->InitDebuggerWrapper(static_cast<int>(port), true); + else + DLOG(WARNING) << "Invalid http debugger port number " << port; + } + + if (command_line_.HasSwitch(switches::kUserAgent)) { + webkit_glue::SetUserAgent(command_line_.GetSwitchValueASCII( + switches::kUserAgent)); + } + + // Open the required browser windows and tabs. + // First, see if we're being run as an application window. + if (!OpenApplicationWindow(profile)) { + std::vector<GURL> urls_to_open = GetURLsFromCommandLine(profile_); + RecordLaunchModeHistogram(urls_to_open.empty()? + LM_TO_BE_DECIDED : LM_WITH_URLS); + ProcessLaunchURLs(process_startup, urls_to_open); + + // If this is an app launch, but we didn't open an app window, it may + // be an app tab. + std::string app_id; + if (IsAppLaunch(NULL, &app_id) && !app_id.empty()) { + // TODO(erikkay): This could fail if |app_id| is invalid (the app was + // uninstalled). We may want to show some reasonable error here. + Browser::OpenApplication(profile, app_id, NULL); + } + + if (process_startup) { + if (browser_defaults::kOSSupportsOtherBrowsers && + !command_line_.HasSwitch(switches::kNoDefaultBrowserCheck)) { + // Check whether we are the default browser. + CheckDefaultBrowser(profile); + } +#if defined(OS_MACOSX) + // Check whether the auto-update system needs to be promoted from user + // to system. + KeystoneInfoBar::PromotionInfoBar(profile); +#endif + } + } else { + RecordLaunchModeHistogram(LM_AS_WEBAPP); + } + +#if defined(OS_WIN) + // Print the selected page if the command line switch exists. Note that the + // current selected tab would be the page which will be printed. + if (command_line_.HasSwitch(switches::kPrint)) { + Browser* browser = BrowserList::GetLastActive(); + browser->Print(); + } +#endif + + // If we're recording or playing back, startup the EventRecorder now + // unless otherwise specified. + if (!command_line_.HasSwitch(switches::kNoEvents)) { + FilePath script_path; + PathService::Get(chrome::FILE_RECORDED_SCRIPT, &script_path); + + bool record_mode = command_line_.HasSwitch(switches::kRecordMode); + bool playback_mode = command_line_.HasSwitch(switches::kPlaybackMode); + + if (record_mode && chrome::kRecordModeEnabled) + base::EventRecorder::current()->StartRecording(script_path); + if (playback_mode) + base::EventRecorder::current()->StartPlayback(script_path); + } + +#if defined(OS_WIN) + if (process_startup) + ShellIntegration::MigrateChromiumShortcuts(); +#endif // defined(OS_WIN) + + return true; +} + +bool BrowserInit::LaunchWithProfile::IsAppLaunch(std::string* app_url, + std::string* app_id) { + if (command_line_.HasSwitch(switches::kApp)) { + if (app_url) + *app_url = command_line_.GetSwitchValueASCII(switches::kApp); + return true; + } + if (command_line_.HasSwitch(switches::kAppId)) { + if (app_id) + *app_id = command_line_.GetSwitchValueASCII(switches::kAppId); + return true; + } + return false; +} + +bool BrowserInit::LaunchWithProfile::OpenApplicationWindow(Profile* profile) { + std::string url_string, app_id; + if (!IsAppLaunch(&url_string, &app_id)) + return false; + + // http://crbug.com/37548 + // TODO(rafaelw): There are two legitimate cases where the extensions + // service could not be ready at this point which need to be handled: + // 1) The locale has changed and the manifests stored in the preferences + // need to be relocalized. + // 2) An externally installed extension will be found and installed. + // Note that this can also fail if the app_id is simply invalid. + // TODO(rafaelw): Do something reasonable here. Pop up a warning panel? + // Open an URL to the gallery page of the extension id? + if (!app_id.empty()) + return Browser::OpenApplication(profile, app_id, NULL) != NULL; + + if (url_string.empty()) + return false; + +#if defined(OS_WIN) // Fix up Windows shortcuts. + ReplaceSubstringsAfterOffset(&url_string, 0, "\\x", "%"); +#endif + GURL url(url_string); + + // Restrict allowed URLs for --app switch. + if (!url.is_empty() && url.is_valid()) { + ChildProcessSecurityPolicy *policy = + ChildProcessSecurityPolicy::GetInstance(); + if (policy->IsWebSafeScheme(url.scheme()) || + url.SchemeIs(chrome::kFileScheme)) { + Browser::OpenApplicationWindow(profile, url); + return true; + } + } + return false; +} + +void BrowserInit::LaunchWithProfile::ProcessLaunchURLs( + bool process_startup, + const std::vector<GURL>& urls_to_open) { + // If we're starting up in "background mode" (no open browser window) then + // don't open any browser windows. + if (process_startup && command_line_.HasSwitch(switches::kNoStartupWindow)) + return; + + if (process_startup && ProcessStartupURLs(urls_to_open)) { + // ProcessStartupURLs processed the urls, nothing else to do. + return; + } + + if (!process_startup && + (profile_->GetSessionService() && + profile_->GetSessionService()->RestoreIfNecessary(urls_to_open))) { + // We're already running and session restore wanted to run. This can happen + // at various points, such as if there is only an app window running and the + // user double clicked the chrome icon. Return so we don't open the urls. + return; + } + + // Session restore didn't occur, open the urls. + + Browser* browser = NULL; + std::vector<GURL> adjust_urls = urls_to_open; + if (adjust_urls.empty()) + AddStartupURLs(&adjust_urls); + else if (!command_line_.HasSwitch(switches::kOpenInNewWindow)) + browser = BrowserList::GetLastActive(); + + OpenURLsInBrowser(browser, process_startup, adjust_urls); +} + +bool BrowserInit::LaunchWithProfile::ProcessStartupURLs( + const std::vector<GURL>& urls_to_open) { + SessionStartupPref pref = GetSessionStartupPref(command_line_, profile_); + if (command_line_.HasSwitch(switches::kTestingChannelID) && + !command_line_.HasSwitch(switches::kRestoreLastSession) && + browser_defaults::kDefaultSessionStartupType != + SessionStartupPref::DEFAULT) { + // When we have non DEFAULT session start type, then we won't open up a + // fresh session. But none of the tests are written with this in mind, so + // we explicitly ignore it during testing. + return false; + } + + if (pref.type == SessionStartupPref::LAST) { + if (!profile_->DidLastSessionExitCleanly() && + !command_line_.HasSwitch(switches::kRestoreLastSession)) { + // The last session crashed. It's possible automatically loading the + // page will trigger another crash, locking the user out of chrome. + // To avoid this, don't restore on startup but instead show the crashed + // infobar. + return false; + } + SessionRestore::RestoreSessionSynchronously(profile_, urls_to_open); + return true; + } + + std::vector<Tab> tabs = PinnedTabCodec::ReadPinnedTabs(profile_); + + if (!urls_to_open.empty()) { + // If urls were specified on the command line, use them. + UrlsToTabs(urls_to_open, &tabs); + } else if (pref.type == SessionStartupPref::URLS && !pref.urls.empty()) { + // Only use the set of urls specified in preferences if nothing was + // specified on the command line. + UrlsToTabs(pref.urls, &tabs); + } + + if (tabs.empty()) + return false; + + OpenTabsInBrowser(NULL, true, tabs); + return true; +} + +Browser* BrowserInit::LaunchWithProfile::OpenURLsInBrowser( + Browser* browser, + bool process_startup, + const std::vector<GURL>& urls) { + std::vector<Tab> tabs; + UrlsToTabs(urls, &tabs); + return OpenTabsInBrowser(browser, process_startup, tabs); +} + +Browser* BrowserInit::LaunchWithProfile::OpenTabsInBrowser( + Browser* browser, + bool process_startup, + const std::vector<Tab>& tabs) { + DCHECK(!tabs.empty()); + // If we don't yet have a profile, try to use the one we're given from + // |browser|. While we may not end up actually using |browser| (since it + // could be a popup window), we can at least use the profile. + if (!profile_ && browser) + profile_ = browser->profile(); + + if (!browser || browser->type() != Browser::TYPE_NORMAL) { + browser = Browser::Create(profile_); + } else { +#if defined(TOOLKIT_GTK) + // Setting the time of the last action on the window here allows us to steal + // focus, which is what the user wants when opening a new tab in an existing + // browser window. + gtk_util::SetWMLastUserActionTime(browser->window()->GetNativeHandle()); +#endif + } + +#if !defined(OS_MACOSX) + // In kiosk mode, we want to always be fullscreen, so switch to that now. + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode)) + browser->ToggleFullscreenMode(); +#endif + + bool first_tab = true; + for (size_t i = 0; i < tabs.size(); ++i) { + // We skip URLs that we'd have to launch an external protocol handler for. + // This avoids us getting into an infinite loop asking ourselves to open + // a URL, should the handler be (incorrectly) configured to be us. Anyone + // asking us to open such a URL should really ask the handler directly. + if (!process_startup && !URLRequest::IsHandledURL(tabs[i].url)) + continue; + + int add_types = first_tab ? TabStripModel::ADD_SELECTED : + TabStripModel::ADD_NONE; + add_types |= TabStripModel::ADD_FORCE_INDEX; + if (tabs[i].is_pinned) + add_types |= TabStripModel::ADD_PINNED; + int index = browser->GetIndexForInsertionDuringRestore(i); + + browser::NavigateParams params(browser, tabs[i].url, + PageTransition::START_PAGE); + params.disposition = first_tab ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; + params.tabstrip_index = index; + params.tabstrip_add_types = add_types; + params.extension_app_id = tabs[i].app_id; + browser::Navigate(¶ms); + + if (profile_ && first_tab && process_startup) { + AddCrashedInfoBarIfNecessary(params.target_contents); + AddBadFlagsInfoBarIfNecessary(params.target_contents); + } + + first_tab = false; + } + browser->window()->Show(); + // TODO(jcampan): http://crbug.com/8123 we should not need to set the initial + // focus explicitly. + browser->GetSelectedTabContents()->view()->SetInitialFocus(); + + return browser; +} + +void BrowserInit::LaunchWithProfile::AddCrashedInfoBarIfNecessary( + TabContents* tab) { + // Assume that if the user is launching incognito they were previously + // running incognito so that we have nothing to restore from. + if (!profile_->DidLastSessionExitCleanly() && + !profile_->IsOffTheRecord()) { + // The last session didn't exit cleanly. Show an infobar to the user + // so that they can restore if they want. The delegate deletes itself when + // it is closed. + tab->AddInfoBar(new SessionCrashedInfoBarDelegate(tab)); + } +} + +void BrowserInit::LaunchWithProfile::AddBadFlagsInfoBarIfNecessary( + TabContents* tab) { + // Unsupported flags for which to display a warning that "stability and + // security will suffer". + static const char* kBadFlags[] = { + // All imply disabling the sandbox. + switches::kSingleProcess, + switches::kNoSandbox, + switches::kInProcessWebGL, + NULL + }; + + const char* bad_flag = NULL; + for (const char** flag = kBadFlags; *flag; ++flag) { + if (command_line_.HasSwitch(*flag)) { + bad_flag = *flag; + break; + } + } + + if (bad_flag) { + tab->AddInfoBar(new SimpleAlertInfoBarDelegate(tab, + l10n_util::GetStringFUTF16(IDS_BAD_FLAGS_WARNING_MESSAGE, + UTF8ToUTF16(std::string("--") + bad_flag)), + NULL, false)); + } +} + +std::vector<GURL> BrowserInit::LaunchWithProfile::GetURLsFromCommandLine( + Profile* profile) { + std::vector<GURL> urls; + const std::vector<CommandLine::StringType>& params = command_line_.args(); + + for (size_t i = 0; i < params.size(); ++i) { + FilePath param = FilePath(params[i]); + // Handle Vista way of searching - "? <search-term>" + if (param.value().find(FILE_PATH_LITERAL("? ")) == 0) { + const TemplateURL* default_provider = + profile->GetTemplateURLModel()->GetDefaultSearchProvider(); + if (!default_provider || !default_provider->url()) { + // No search provider available. Just treat this as regular URL. + urls.push_back(URLFixerUpper::FixupRelativeFile(cur_dir_, param)); + continue; + } + const TemplateURLRef* search_url = default_provider->url(); + DCHECK(search_url->SupportsReplacement()); + std::wstring search_term = param.ToWStringHack().substr(2); + urls.push_back(GURL(search_url->ReplaceSearchTerms( + *default_provider, search_term, + TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring()))); + } else { + // This will create a file URL or a regular URL. + // This call can (in rare circumstances) block the UI thread. + // Allow it until this bug is fixed. + // http://code.google.com/p/chromium/issues/detail?id=60641 + GURL url; + { + base::ThreadRestrictions::ScopedAllowIO allow_io; + url = URLFixerUpper::FixupRelativeFile(cur_dir_, param); + } + // Exclude dangerous schemes. + if (url.is_valid()) { + ChildProcessSecurityPolicy *policy = + ChildProcessSecurityPolicy::GetInstance(); + if (policy->IsWebSafeScheme(url.scheme()) || + url.SchemeIs(chrome::kFileScheme) || + !url.spec().compare(chrome::kAboutBlankURL)) { + urls.push_back(url); + } + } + } + } + return urls; +} + +void BrowserInit::LaunchWithProfile::AddStartupURLs( + std::vector<GURL>* startup_urls) const { + // If we have urls specified beforehand (i.e. from command line) use them + // and nothing else. + if (!startup_urls->empty()) + return; + // If we have urls specified by the first run master preferences use them + // and nothing else. + if (browser_init_) { + if (!browser_init_->first_run_tabs_.empty()) { + std::vector<GURL>::iterator it = browser_init_->first_run_tabs_.begin(); + while (it != browser_init_->first_run_tabs_.end()) { + // Replace magic names for the actual urls. + if (it->host() == "new_tab_page") { + startup_urls->push_back(GURL()); + } else if (it->host() == "welcome_page") { + startup_urls->push_back(GetWelcomePageURL()); + } else { + startup_urls->push_back(*it); + } + ++it; + } + browser_init_->first_run_tabs_.clear(); + return; + } + } + + // Otherwise open at least the new tab page (and the welcome page, if this + // is the first time the browser is being started), or the set of URLs + // specified on the command line. + startup_urls->push_back(GURL()); // New tab page. + PrefService* prefs = g_browser_process->local_state(); + if (prefs->FindPreference(prefs::kShouldShowWelcomePage) && + prefs->GetBoolean(prefs::kShouldShowWelcomePage)) { + // Reset the preference so we don't show the welcome page next time. + prefs->ClearPref(prefs::kShouldShowWelcomePage); + startup_urls->push_back(GetWelcomePageURL()); + } +} + +void BrowserInit::LaunchWithProfile::CheckDefaultBrowser(Profile* profile) { + // We do not check if we are the default browser if: + // - the user said "don't ask me again" on the infobar earlier. + // - this is the first launch after the first run flow. + if (!profile->GetPrefs()->GetBoolean(prefs::kCheckDefaultBrowser) || + FirstRun::IsChromeFirstRun()) { + return; + } + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, new CheckDefaultBrowserTask()); +} + +bool BrowserInit::ProcessCmdLineImpl(const CommandLine& command_line, + const FilePath& cur_dir, + bool process_startup, + Profile* profile, + int* return_code, + BrowserInit* browser_init) { + DCHECK(profile); + if (process_startup) { + if (command_line.HasSwitch(switches::kDisablePromptOnRepost)) + NavigationController::DisablePromptOnRepost(); + + // Look for the testing channel ID ONLY during process startup + if (command_line.HasSwitch(switches::kTestingChannelID)) { + std::string testing_channel_id = command_line.GetSwitchValueASCII( + switches::kTestingChannelID); + // TODO(sanjeevr) Check if we need to make this a singleton for + // compatibility with the old testing code + // If there are any extra parameters, we expect each one to generate a + // new tab; if there are none then we get one homepage tab. + int expected_tab_count = 1; + if (command_line.HasSwitch(switches::kNoStartupWindow)) { + expected_tab_count = 0; + } else if (command_line.HasSwitch(switches::kRestoreLastSession)) { + std::string restore_session_value( + command_line.GetSwitchValueASCII(switches::kRestoreLastSession)); + base::StringToInt(restore_session_value, &expected_tab_count); + } else { + expected_tab_count = + std::max(1, static_cast<int>(command_line.args().size())); + } + CreateAutomationProvider<TestingAutomationProvider>( + testing_channel_id, + profile, + static_cast<size_t>(expected_tab_count)); + } + } + + bool silent_launch = false; + + if (command_line.HasSwitch(switches::kAutomationClientChannelID)) { + std::string automation_channel_id = command_line.GetSwitchValueASCII( + switches::kAutomationClientChannelID); + // If there are any extra parameters, we expect each one to generate a + // new tab; if there are none then we have no tabs + size_t expected_tabs = + std::max(static_cast<int>(command_line.args().size()), 0); + if (expected_tabs == 0) + silent_launch = true; + + if (command_line.HasSwitch(switches::kChromeFrame)) { + CreateAutomationProvider<ChromeFrameAutomationProvider>( + automation_channel_id, profile, expected_tabs); + } else { + CreateAutomationProvider<AutomationProvider>(automation_channel_id, + profile, expected_tabs); + } + } + + // If we have been invoked to display a desktop notification on behalf of + // the service process, we do not want to open any browser windows. + if (command_line.HasSwitch(switches::kNotifyCloudPrintTokenExpired)) { + silent_launch = true; + profile->GetCloudPrintProxyService()->ShowTokenExpiredNotification(); + } + + if (command_line.HasSwitch(switches::kExplicitlyAllowedPorts)) { + std::string allowed_ports = + command_line.GetSwitchValueASCII(switches::kExplicitlyAllowedPorts); + net::SetExplicitlyAllowedPorts(allowed_ports); + } + +#if defined(OS_CHROMEOS) + // The browser will be launched after the user logs in. + if (command_line.HasSwitch(switches::kLoginManager) || + command_line.HasSwitch(switches::kLoginPassword)) { + silent_launch = true; + } +#endif + + // If we don't want to launch a new browser window or tab (in the case + // of an automation request), we are done here. + if (!silent_launch) { + return browser_init->LaunchBrowser( + command_line, profile, cur_dir, process_startup, return_code); + } + return true; +} + +template <class AutomationProviderClass> +void BrowserInit::CreateAutomationProvider(const std::string& channel_id, + Profile* profile, + size_t expected_tabs) { + scoped_refptr<AutomationProviderClass> automation = + new AutomationProviderClass(profile); + automation->ConnectToChannel(channel_id); + automation->SetExpectedTabCount(expected_tabs); + + AutomationProviderList* list = + g_browser_process->InitAutomationProviderList(); + DCHECK(list); + list->AddProvider(automation); +} diff --git a/chrome/browser/ui/browser_init.h b/chrome/browser/ui/browser_init.h new file mode 100644 index 0000000..07e99e9 --- /dev/null +++ b/chrome/browser/ui/browser_init.h @@ -0,0 +1,199 @@ +// 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_UI_BROWSER_INIT_H_ +#define CHROME_BROWSER_UI_BROWSER_INIT_H_ +#pragma once + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/gtest_prod_util.h" +#include "googleurl/src/gurl.h" + +class Browser; +class CommandLine; +class GURL; +class Profile; +class TabContents; + +// class containing helpers for BrowserMain to spin up a new instance and +// initialize the profile. +class BrowserInit { + public: + BrowserInit(); + ~BrowserInit(); + + // Adds a url to be opened during first run. This overrides the standard + // tabs shown at first run. + void AddFirstRunTab(const GURL& url); + + // This function is equivalent to ProcessCommandLine but should only be + // called during actual process startup. + bool Start(const CommandLine& cmd_line, const FilePath& cur_dir, + Profile* profile, int* return_code) { + return ProcessCmdLineImpl(cmd_line, cur_dir, true, profile, return_code, + this); + } + + // This function performs command-line handling and is invoked when process + // starts as well as when we get a start request from another process (via the + // WM_COPYDATA message). |command_line| holds the command line we need to + // process - either from this process or from some other one (if + // |process_startup| is true and we are being called from + // ProcessSingleton::OnCopyData). + static bool ProcessCommandLine(const CommandLine& cmd_line, + const FilePath& cur_dir, bool process_startup, + Profile* profile, int* return_code) { + return ProcessCmdLineImpl(cmd_line, cur_dir, process_startup, profile, + return_code, NULL); + } + + template <class AutomationProviderClass> + static void CreateAutomationProvider(const std::string& channel_id, + Profile* profile, + size_t expected_tabs); + + // Returns true if the browser is coming up. + static bool InProcessStartup(); + + // Launches a browser window associated with |profile|. |command_line| should + // be the command line passed to this process. |cur_dir| can be empty, which + // implies that the directory of the executable should be used. + // |process_startup| indicates whether this is the first browser. + bool LaunchBrowser(const CommandLine& command_line, Profile* profile, + const FilePath& cur_dir, bool process_startup, + int* return_code); + + // LaunchWithProfile --------------------------------------------------------- + // + // Assists launching the application and appending the initial tabs for a + // browser window. + + class LaunchWithProfile { + public: + // Used by OpenTabsInBrowser. + struct Tab { + Tab(); + ~Tab(); + + // The url to load. + GURL url; + + // If true, the tab corresponds to an app an |app_id| gives the id of the + // app. + bool is_app; + + // True if the is tab pinned. + bool is_pinned; + + // Id of the app. + std::string app_id; + }; + + // There are two ctors. The first one implies a NULL browser_init object + // and thus no access to distribution-specific first-run behaviors. The + // second one is always called when the browser starts even if it is not + // the first run. + LaunchWithProfile(const FilePath& cur_dir, const CommandLine& command_line); + LaunchWithProfile(const FilePath& cur_dir, const CommandLine& command_line, + BrowserInit* browser_init); + ~LaunchWithProfile(); + + // Creates the necessary windows for startup. Returns true on success, + // false on failure. process_startup is true if Chrome is just + // starting up. If process_startup is false, it indicates Chrome was + // already running and the user wants to launch another instance. + bool Launch(Profile* profile, bool process_startup); + + // Convenience for OpenTabsInBrowser that converts |urls| into a set of + // Tabs. + Browser* OpenURLsInBrowser(Browser* browser, + bool process_startup, + const std::vector<GURL>& urls); + + // Creates a tab for each of the Tabs in |tabs|. If browser is non-null + // and a tabbed browser, the tabs are added to it. Otherwise a new tabbed + // browser is created and the tabs are added to it. The browser the tabs + // are added to is returned, which is either |browser| or the newly created + // browser. + Browser* OpenTabsInBrowser(Browser* browser, + bool process_startup, + const std::vector<Tab>& tabs); + + private: + FRIEND_TEST_ALL_PREFIXES(BrowserTest, RestorePinnedTabs); + + // If the process was launched with the web application command line flags, + // e.g. --app=http://www.google.com/ or --app_id=... return true. + // In this case |app_url| or |app_id| are populated if they're non-null. + bool IsAppLaunch(std::string* app_url, std::string* app_id); + + // If IsAppLaunch is true, tries to open an application window. + // If the app is specified to start in a tab, or IsAppLaunch is false, + // returns false to specify default processing. + bool OpenApplicationWindow(Profile* profile); + + // Invoked from OpenURLsInBrowser to handle processing of urls. This may + // do any of the following: + // . Invoke ProcessStartupURLs if |process_startup| is true. + // . Restore the last session if necessary. + // . Open the urls directly. + void ProcessLaunchURLs(bool process_startup, + const std::vector<GURL>& urls_to_open); + + // Does the following: + // . If the user's startup pref is to restore the last session (or the + // command line flag is present to force using last session), it is + // restored, and true is returned. + // . Attempts to restore any pinned tabs from last run of chrome and: + // . If urls_to_open is non-empty, they are opened and true is returned. + // . If the user's startup pref is to launch a specific set of URLs they + // are opened. + // + // Otherwise false is returned, which indicates the caller must create a + // new browser. + bool ProcessStartupURLs(const std::vector<GURL>& urls_to_open); + + // If the last session didn't exit cleanly and tab is a web contents tab, + // an infobar is added allowing the user to restore the last session. + void AddCrashedInfoBarIfNecessary(TabContents* tab); + + // If we have been started with unsupported flags like --single-process, + // politely nag the user about it. + void AddBadFlagsInfoBarIfNecessary(TabContents* tab); + + // Returns the list of URLs to open from the command line. The returned + // vector is empty if the user didn't specify any URLs on the command line. + std::vector<GURL> GetURLsFromCommandLine(Profile* profile); + + // Adds additional startup URLs to the specified vector. + void AddStartupURLs(std::vector<GURL>* startup_urls) const; + + // Checks whether Chrome is still the default browser (unless the user + // previously instructed not to do so) and warns the user if it is not. + void CheckDefaultBrowser(Profile* profile); + + const FilePath cur_dir_; + const CommandLine& command_line_; + Profile* profile_; + BrowserInit* browser_init_; + DISALLOW_COPY_AND_ASSIGN(LaunchWithProfile); + }; + + private: + static bool ProcessCmdLineImpl(const CommandLine& command_line, + const FilePath& cur_dir, bool process_startup, + Profile* profile, int* return_code, + BrowserInit* browser_init); + + // Additional tabs to open during first run. + std::vector<GURL> first_run_tabs_; + + DISALLOW_COPY_AND_ASSIGN(BrowserInit); +}; + +#endif // CHROME_BROWSER_UI_BROWSER_INIT_H_ diff --git a/chrome/browser/browser_init_browsertest.cc b/chrome/browser/ui/browser_init_browsertest.cc index ec52cae..d036c4d 100644 --- a/chrome/browser/browser_init_browsertest.cc +++ b/chrome/browser/ui/browser_init_browsertest.cc @@ -60,33 +60,4 @@ IN_PROC_BROWSER_TEST_F(BrowserInitTest, OpenURLsPopup) { BrowserList::RemoveObserver(&observer); } -// Test that we prevent openning potentially dangerous schemes from the -// command line. -// TODO(jschuh): FLAKY because the process doesn't have sufficient time -// to start on most BuildBot runs and I don't want to add longer delays to -// the test. I'll circle back and make this work properly when i get a chance. -IN_PROC_BROWSER_TEST_F(BrowserInitTest, FLAKY_BlockBadURLs) { - const char* testurlstr = "http://localhost/"; - const GURL testurl(testurlstr); - CommandLine cmdline(CommandLine::NO_PROGRAM); - cmdline.AppendArg(testurlstr); - cmdline.AppendArg("javascript:alert('boo')"); - cmdline.AppendArg(testurlstr); - cmdline.AppendArg("view-source:http://localhost/"); - - // This will pick up the current browser instance. - BrowserInit::LaunchWithProfile launch(FilePath(), cmdline); - launch.Launch(browser()->profile(), false); - - // TODO(jschuh): Give the browser a chance to start first. - PlatformThread::Sleep(50); - - // Skip about:blank in the first tab - for (int i = 1; i < browser()->tab_count(); i++) { - const GURL &url = browser()->GetTabContentsAt(i)->GetURL(); - ASSERT_EQ(url, testurl); - } - ASSERT_EQ(browser()->tab_count(), 3); -} - } // namespace diff --git a/chrome/browser/browser_list.cc b/chrome/browser/ui/browser_list.cc index 2652c1a..2652c1a 100644 --- a/chrome/browser/browser_list.cc +++ b/chrome/browser/ui/browser_list.cc diff --git a/chrome/browser/ui/browser_list.h b/chrome/browser/ui/browser_list.h new file mode 100644 index 0000000..bef9013 --- /dev/null +++ b/chrome/browser/ui/browser_list.h @@ -0,0 +1,235 @@ +// 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_UI_BROWSER_LIST_H_ +#define CHROME_BROWSER_UI_BROWSER_LIST_H_ +#pragma once + +#include <vector> + +#include "base/observer_list.h" +#include "chrome/browser/browser.h" + +// Stores a list of all Browser objects. +class BrowserList { + public: + typedef std::vector<Browser*> BrowserVector; + typedef BrowserVector::iterator iterator; + typedef BrowserVector::const_iterator const_iterator; + typedef BrowserVector::const_reverse_iterator const_reverse_iterator; + + // It is not allowed to change the global window list (add or remove any + // browser windows while handling observer callbacks. + class Observer { + public: + // Called immediately after a browser is added to the list + virtual void OnBrowserAdded(const Browser* browser) = 0; + + // Called immediately after a browser is removed from the list + virtual void OnBrowserRemoved(const Browser* browser) = 0; + + // Called immediately after a browser is set active (SetLastActive) + virtual void OnBrowserSetLastActive(const Browser* browser) {} + + protected: + virtual ~Observer() {} + }; + + // Adds and removes browsers from the global list. The browser object should + // be valid BEFORE these calls (for the benefit of observers), so notify and + // THEN delete the object. + static void AddBrowser(Browser* browser); + static void RemoveBrowser(Browser* browser); + + static void AddObserver(Observer* observer); + static void RemoveObserver(Observer* observer); + + // Called by Browser objects when their window is activated (focused). This + // allows us to determine what the last active Browser was. + static void SetLastActive(Browser* browser); + + // Returns the Browser object whose window was most recently active. If the + // most recently open Browser's window was closed, returns the first Browser + // in the list. If no Browsers exist, returns NULL. + // + // WARNING: this is NULL until a browser becomes active. If during startup + // a browser does not become active (perhaps the user launches Chrome, then + // clicks on another app before the first browser window appears) then this + // returns NULL. + // WARNING #2: this will always be NULL in unit tests run on the bots. + static Browser* GetLastActive(); + + // Identical in behavior to GetLastActive(), except that the most recently + // open browser owned by |profile| is returned. If none exist, returns NULL. + // WARNING: see warnings in GetLastActive(). + static Browser* GetLastActiveWithProfile(Profile *profile); + + // Find an existing browser window with the provided type. Searches in the + // order of last activation. Only browsers that have been active can be + // returned. If |match_incognito| is true, will match a browser with either + // a regular or incognito profile that matches the given one. Returns NULL if + // no such browser currently exists. + static Browser* FindBrowserWithType(Profile* p, Browser::Type t, + bool match_incognito); + + // Find an existing browser window that can provide the specified type (this + // uses Browser::CanSupportsWindowFeature, not + // Browser::SupportsWindowFeature). This searches in the order of last + // activation. Only browsers that have been active can be returned. Returns + // NULL if no such browser currently exists. + static Browser* FindBrowserWithFeature(Profile* p, + Browser::WindowFeature feature); + + // Find an existing browser window with the provided profile. Searches in the + // order of last activation. Only browsers that have been active can be + // returned. Returns NULL if no such browser currently exists. + static Browser* FindBrowserWithProfile(Profile* p); + + // Find an existing browser with the provided ID. Returns NULL if no such + // browser currently exists. + static Browser* FindBrowserWithID(SessionID::id_type desired_id); + + // Checks if the browser can be automatically restarted to install upgrades + // The browser can be automatically restarted when: + // 1. It's in the background mode (no visible windows). + // 2. An update exe is present in the install folder. + static bool CanRestartForUpdate(); + + // Closes all browsers and exits. This is equivalent to + // CloseAllBrowsers(true) on platforms where the application exits when no + // more windows are remaining. On other platforms (the Mac), this will + // additionally exit the application. + static void CloseAllBrowsersAndExit(); + + // Closes all browsers. If the session is ending the windows are closed + // directly. Otherwise the windows are closed by way of posting a WM_CLOSE + // message. + static void CloseAllBrowsers(); + + // Begins shutdown of the application when the Windows session is ending. + static void WindowsSessionEnding(); + + // Returns true if there is at least one Browser with the specified profile. + static bool HasBrowserWithProfile(Profile* profile); + + // Tells the BrowserList to keep the application alive after the last Browser + // closes. This is implemented as a count, so callers should pair their calls + // to StartKeepAlive() with matching calls to EndKeepAlive() when they no + // longer need to keep the application running. + static void StartKeepAlive(); + + // Stops keeping the application alive after the last Browser is closed. + // Should match a previous call to StartKeepAlive(). + static void EndKeepAlive(); + + // Returns true if application will continue running after the last Browser + // closes. + static bool WillKeepAlive(); + + static const_iterator begin() { return browsers_.begin(); } + static const_iterator end() { return browsers_.end(); } + + static bool empty() { return browsers_.empty(); } + static size_t size() { return browsers_.size(); } + + // Returns iterated access to list of open browsers ordered by when + // they were last active. The underlying data structure is a vector + // and we push_back on recent access so a reverse iterator gives the + // latest accessed browser first. + static const_reverse_iterator begin_last_active() { + return last_active_browsers_.rbegin(); + } + + static const_reverse_iterator end_last_active() { + return last_active_browsers_.rend(); + } + + // Return the number of browsers with the following profile which are + // currently open. + static size_t GetBrowserCount(Profile* p); + + // Return the number of browsers with the following profile and type which are + // currently open. + static size_t GetBrowserCountForType(Profile* p, Browser::Type type); + + // Returns true if at least one off the record session is active. + static bool IsOffTheRecordSessionActive(); + + // Called once there are no more browsers open and the application is exiting. + static void AllBrowsersClosedAndAppExiting(); + + private: + // Helper method to remove a browser instance from a list of browsers + static void RemoveBrowserFrom(Browser* browser, BrowserVector* browser_list); + + static BrowserVector browsers_; + static BrowserVector last_active_browsers_; + static ObserverList<Observer> observers_; + + // Counter of calls to StartKeepAlive(). If non-zero, the application will + // continue running after the last browser has exited. + static int keep_alive_count_; +}; + +class TabContents; + +// Iterates through all web view hosts in all browser windows. Because the +// renderers act asynchronously, getting a host through this interface does +// not guarantee that the renderer is ready to go. Doing anything to affect +// browser windows or tabs while iterating may cause incorrect behavior. +// +// Example: +// for (TabContentsIterator iterator; !iterator.done(); ++iterator) { +// TabContents* cur = *iterator; +// -or- +// iterator->operationOnTabContents(); +// ... +// } +class TabContentsIterator { + public: + TabContentsIterator(); + + // Returns true if we are past the last Browser. + bool done() const { + return cur_ == NULL; + } + + // Returns the current TabContents, valid as long as !Done() + TabContents* operator->() const { + return cur_; + } + TabContents* operator*() const { + return cur_; + } + + // Incrementing operators, valid as long as !Done() + TabContents* operator++() { // ++preincrement + Advance(); + return cur_; + } + TabContents* operator++(int) { // postincrement++ + TabContents* tmp = cur_; + Advance(); + return tmp; + } + + private: + // Loads the next host into Cur. This is designed so that for the initial + // call when browser_iterator_ points to the first browser and + // web_view_index_ is -1, it will fill the first host. + void Advance(); + + // iterator over all the Browser objects + BrowserList::const_iterator browser_iterator_; + + // tab index into the current Browser of the current web view + int web_view_index_; + + // Current TabContents, or NULL if we're at the end of the list. This can + // be extracted given the browser iterator and index, but it's nice to cache + // this since the caller may access the current host many times. + TabContents* cur_; +}; + +#endif // CHROME_BROWSER_UI_BROWSER_LIST_H_ diff --git a/chrome/browser/browser_list_gtk.cc b/chrome/browser/ui/browser_list_gtk.cc index b6e0f06..b6e0f06 100644 --- a/chrome/browser/browser_list_gtk.cc +++ b/chrome/browser/ui/browser_list_gtk.cc diff --git a/chrome/browser/browser_list_mac.mm b/chrome/browser/ui/browser_list_mac.mm index 5c2ed9a..5c2ed9a 100644 --- a/chrome/browser/browser_list_mac.mm +++ b/chrome/browser/ui/browser_list_mac.mm diff --git a/chrome/browser/browser_list_stub.cc b/chrome/browser/ui/browser_list_stub.cc index dad4ab4..dad4ab4 100644 --- a/chrome/browser/browser_list_stub.cc +++ b/chrome/browser/ui/browser_list_stub.cc diff --git a/chrome/browser/browser_list_win.cc b/chrome/browser/ui/browser_list_win.cc index 37ee4fc..37ee4fc 100644 --- a/chrome/browser/browser_list_win.cc +++ b/chrome/browser/ui/browser_list_win.cc diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc new file mode 100644 index 0000000..54edd14 --- /dev/null +++ b/chrome/browser/ui/browser_navigator.cc @@ -0,0 +1,382 @@ +// 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/browser_navigator.h" + +#include "base/command_line.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_url_handler.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/location_bar.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/site_instance.h" +#include "chrome/browser/status_bubble.h" +#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/chrome_switches.h" + +namespace { + +// Returns the SiteInstance for |source_contents| if it represents the same +// website as |url|, or NULL otherwise. |source_contents| cannot be NULL. +SiteInstance* GetSiteInstance(TabContents* source_contents, const GURL& url) { + if (!source_contents) + return NULL; + + // Don't use this logic when "--process-per-tab" is specified. + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerTab) && + SiteInstance::IsSameWebSite(source_contents->profile(), + source_contents->GetURL(), + url)) { + return source_contents->GetSiteInstance(); + } + return NULL; +} + +// Returns true if the specified Browser can open tabs. Not all Browsers support +// multiple tabs, such as app frames and popups. This function returns false for +// those types of Browser. +bool WindowCanOpenTabs(Browser* browser) { + return browser->CanSupportWindowFeature(Browser::FEATURE_TABSTRIP) || + browser->tabstrip_model()->empty(); +} + +// Finds an existing Browser compatible with |profile|, making a new one if no +// such Browser is located. +Browser* GetOrCreateBrowser(Profile* profile) { + Browser* browser = BrowserList::FindBrowserWithType(profile, + Browser::TYPE_NORMAL, + false); + return browser ? browser : Browser::Create(profile); +} + +// Returns true if two URLs are equal ignoring their ref (hash fragment). +bool CompareURLsIgnoreRef(const GURL& url, const GURL& other) { + if (url == other) + return true; + // If neither has a ref than there is no point in stripping the refs and + // the URLs are different since the comparison failed in the previous if + // statement. + if (!url.has_ref() && !other.has_ref()) + return false; + url_canon::Replacements<char> replacements; + replacements.ClearRef(); + GURL url_no_ref = url.ReplaceComponents(replacements); + GURL other_no_ref = other.ReplaceComponents(replacements); + return url_no_ref == other_no_ref; +} + +// Returns the index of an existing singleton tab in |params->browser| matching +// the URL specified in |params|. +int GetIndexOfSingletonTab(browser::NavigateParams* params) { + if (params->disposition != SINGLETON_TAB) + return -1; + + // In case the URL was rewritten by the BrowserURLHandler we need to ensure + // that we do not open another URL that will get redirected to the rewritten + // URL. + GURL rewritten_url(params->url); + bool reverse_on_redirect = false; + BrowserURLHandler::RewriteURLIfNecessary(&rewritten_url, + params->browser->profile(), + &reverse_on_redirect); + + for (int i = 0; i < params->browser->tab_count(); ++i) { + TabContents* tab = params->browser->GetTabContentsAt(i); + if (CompareURLsIgnoreRef(tab->GetURL(), params->url) || + CompareURLsIgnoreRef(tab->GetURL(), rewritten_url)) { + params->target_contents = tab; + return i; + } + } + return -1; +} + +// Returns a Browser that can host the navigation or tab addition specified in +// |params|. This might just return the same Browser specified in |params|, or +// some other if that Browser is deemed incompatible. +Browser* GetBrowserForDisposition(browser::NavigateParams* params) { + // If no source TabContents was specified, we use the selected one from the + // target browser. This must happen first, before GetBrowserForDisposition() + // has a chance to replace |params->browser| with another one. + if (!params->source_contents && params->browser) + params->source_contents = params->browser->GetSelectedTabContents(); + + Profile* profile = + params->browser ? params->browser->profile() : params->profile; + + switch (params->disposition) { + case CURRENT_TAB: + if (!params->browser && profile) { + // We specified a profile instead of a browser; find or create one. + params->browser = Browser::GetOrCreateTabbedBrowser(profile); + } + return params->browser; + case SINGLETON_TAB: + case NEW_FOREGROUND_TAB: + case NEW_BACKGROUND_TAB: + // See if we can open the tab in the window this navigator is bound to. + if (params->browser && WindowCanOpenTabs(params->browser)) + return params->browser; + // Find a compatible window and re-execute this command in it. Otherwise + // re-run with NEW_WINDOW. + if (profile) + return GetOrCreateBrowser(profile); + return NULL; + case NEW_POPUP: { + // Make a new popup window. Coerce app-style if |params->browser| or the + // |source| represents an app. + Browser::Type type = Browser::TYPE_POPUP; + if ((params->browser && params->browser->type() == Browser::TYPE_APP) || + (params->source_contents && params->source_contents->is_app())) { + type = Browser::TYPE_APP_POPUP; + } + if (profile) { + Browser* browser = new Browser(type, profile); + browser->set_override_bounds(params->window_bounds); + browser->CreateBrowserWindow(); + return browser; + } + return NULL; + } + case NEW_WINDOW: + // Make a new normal browser window. + if (profile) { + Browser* browser = new Browser(Browser::TYPE_NORMAL, profile); + browser->CreateBrowserWindow(); + return browser; + } + return NULL; + case OFF_THE_RECORD: + // Make or find an incognito window. + if (profile) + return GetOrCreateBrowser(profile->GetOffTheRecordProfile()); + return NULL; + // The following types all result in no navigation. + case SUPPRESS_OPEN: + case SAVE_TO_DISK: + case IGNORE_ACTION: + return NULL; + default: + NOTREACHED(); + } + return NULL; +} + +// Fix disposition and other parameter values depending on prevailing +// conditions. +void NormalizeDisposition(browser::NavigateParams* params) { + // Calculate the WindowOpenDisposition if necessary. + if (params->browser->tabstrip_model()->empty() && + (params->disposition == NEW_BACKGROUND_TAB || + params->disposition == CURRENT_TAB || + params->disposition == SINGLETON_TAB)) { + params->disposition = NEW_FOREGROUND_TAB; + } + if (params->browser->profile()->IsOffTheRecord() && + params->disposition == OFF_THE_RECORD) { + params->disposition = NEW_FOREGROUND_TAB; + } + + // Disposition trumps add types. ADD_SELECTED is a default, so we need to + // remove it if disposition implies the tab is going to open in the + // background. + if (params->disposition == NEW_BACKGROUND_TAB) + params->tabstrip_add_types &= ~TabStripModel::ADD_SELECTED; + + // Code that wants to open a new window typically expects it to be shown + // automatically. + if (params->disposition == NEW_WINDOW || params->disposition == NEW_POPUP) { + params->show_window = true; + params->tabstrip_add_types |= TabStripModel::ADD_SELECTED; + } +} + +// This class makes sure the Browser object held in |params| is made visible +// by the time it goes out of scope, provided |params| wants it to be shown. +class ScopedBrowserDisplayer { + public: + explicit ScopedBrowserDisplayer(browser::NavigateParams* params) + : params_(params) { + } + ~ScopedBrowserDisplayer() { + if (params_->show_window) + params_->browser->window()->Show(); + } + private: + browser::NavigateParams* params_; + DISALLOW_COPY_AND_ASSIGN(ScopedBrowserDisplayer); +}; + +// This class manages the lifetime of a TabContents created by the Navigate() +// function. When Navigate() creates a TabContents for a URL, an instance of +// this class takes ownership of it via TakeOwnership() until the TabContents +// is added to a tab strip at which time ownership is relinquished via +// ReleaseOwnership(). If this object goes out of scope without being added +// to a tab strip, the created TabContents is deleted to avoid a leak and the +// params->target_contents field is set to NULL. +class ScopedTargetContentsOwner { + public: + explicit ScopedTargetContentsOwner(browser::NavigateParams* params) + : params_(params) { + } + ~ScopedTargetContentsOwner() { + if (target_contents_owner_.get()) + params_->target_contents = NULL; + } + + // Assumes ownership of |params_|' target_contents until ReleaseOwnership + // is called. + void TakeOwnership() { + target_contents_owner_.reset(params_->target_contents); + } + + // Relinquishes ownership of |params_|' target_contents. + TabContents* ReleaseOwnership() { + return target_contents_owner_.release(); + } + + private: + browser::NavigateParams* params_; + scoped_ptr<TabContents> target_contents_owner_; + DISALLOW_COPY_AND_ASSIGN(ScopedTargetContentsOwner); +}; + +} // namespace + +namespace browser { + +NavigateParams::NavigateParams( + Browser* a_browser, + const GURL& a_url, + PageTransition::Type a_transition) + : url(a_url), + target_contents(NULL), + source_contents(NULL), + disposition(CURRENT_TAB), + transition(a_transition), + tabstrip_index(-1), + tabstrip_add_types(TabStripModel::ADD_SELECTED), + show_window(false), + browser(a_browser), + profile(NULL) { +} + +NavigateParams::NavigateParams(Browser* a_browser, + TabContents* a_target_contents) + : target_contents(a_target_contents), + source_contents(NULL), + disposition(CURRENT_TAB), + transition(PageTransition::LINK), + tabstrip_index(-1), + tabstrip_add_types(TabStripModel::ADD_SELECTED), + show_window(false), + browser(a_browser), + profile(NULL) { +} + +NavigateParams::~NavigateParams() { +} + +void Navigate(NavigateParams* params) { + params->browser = GetBrowserForDisposition(params); + if (!params->browser) + return; + // Navigate() must not return early after this point. + + // Make sure the Browser is shown if params call for it. + ScopedBrowserDisplayer displayer(params); + + // Makes sure any TabContents created by this function is destroyed if + // not properly added to a tab strip. + ScopedTargetContentsOwner target_contents_owner(params); + + // Some dispositions need coercion to base types. + NormalizeDisposition(params); + + // Determine if the navigation was user initiated. If it was, we need to + // inform the target TabContents, and we may need to update the UI. + PageTransition::Type base_transition = + PageTransition::StripQualifier(params->transition); + bool user_initiated = base_transition == PageTransition::TYPED || + base_transition == PageTransition::AUTO_BOOKMARK; + + // If no target TabContents was specified, we need to construct one if we are + // supposed to target a new tab. + if (!params->target_contents) { + if (params->disposition != CURRENT_TAB) { + params->target_contents = + new TabContents(params->browser->profile(), + GetSiteInstance(params->source_contents, params->url), + MSG_ROUTING_NONE, + params->source_contents, + NULL); + // This function takes ownership of |params->target_contents| until it + // is added to a TabStripModel. + target_contents_owner.TakeOwnership(); + params->target_contents->SetExtensionAppById(params->extension_app_id); + // TODO(sky): figure out why this is needed. Without it we seem to get + // failures in startup tests. + // By default, content believes it is not hidden. When adding contents + // in the background, tell it that it's hidden. + if ((params->tabstrip_add_types & TabStripModel::ADD_SELECTED) == 0) { + // TabStripModel::AddTabContents invokes HideContents if not foreground. + params->target_contents->WasHidden(); + } + } else { + // ... otherwise if we're loading in the current tab, the target is the + // same as the source. + params->target_contents = params->source_contents; + DCHECK(params->target_contents); + } + + if (user_initiated) { + RenderViewHostDelegate::BrowserIntegration* integration = + params->target_contents; + integration->OnUserGesture(); + } + + // Perform the actual navigation. + GURL url = params->url.is_empty() ? params->browser->GetHomePage() + : params->url; + params->target_contents->controller().LoadURL(url, params->referrer, + params->transition); + } else { + // |target_contents| was specified non-NULL, and so we assume it has already + // been navigated appropriately. We need to do nothing more other than + // add it to the appropriate tabstrip. + } + + if (params->source_contents == params->target_contents) { + // The navigation occurred in the source tab, so update the UI. + params->browser->UpdateUIForNavigationInTab(params->target_contents, + params->transition, + user_initiated); + } else { + // The navigation occurred in some other tab. + int singleton_index = GetIndexOfSingletonTab(params); + if (params->disposition == SINGLETON_TAB && singleton_index >= 0) { + // The navigation should re-select an existing tab in the target Browser. + params->browser->SelectTabContentsAt(singleton_index, user_initiated); + } else { + // If some non-default value is set for the index, we should tell the + // TabStripModel to respect it. + if (params->tabstrip_index != -1) + params->tabstrip_add_types |= TabStripModel::ADD_FORCE_INDEX; + + // The navigation should insert a new tab into the target Browser. + params->browser->tabstrip_model()->AddTabContents( + params->target_contents, + params->tabstrip_index, + params->transition, + params->tabstrip_add_types); + // Now that the |params->target_contents| is safely owned by the target + // Browser's TabStripModel, we can release ownership. + target_contents_owner.ReleaseOwnership(); + } + } +} + +} // namespace browser diff --git a/chrome/browser/ui/browser_navigator.h b/chrome/browser/ui/browser_navigator.h new file mode 100644 index 0000000..aee08db --- /dev/null +++ b/chrome/browser/ui/browser_navigator.h @@ -0,0 +1,146 @@ +// 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_UI_BROWSER_NAVIGATOR_H_ +#define CHROME_BROWSER_UI_BROWSER_NAVIGATOR_H_ +#pragma once + +#include <string> + +#include "chrome/common/page_transition_types.h" +#include "gfx/rect.h" +#include "googleurl/src/gurl.h" +#include "webkit/glue/window_open_disposition.h" + +class Browser; +class Profile; +class TabContents; + +namespace browser { + +// Parameters that tell Navigate() what to do. +// +// Some basic examples: +// +// Simple Navigate to URL in current tab: +// browser::NavigateParams params(browser, GURL("http://www.google.com/"), +// PageTransition::LINK); +// browser::Navigate(¶ms); +// +// Open bookmark in new background tab: +// browser::NavigateParams params(browser, url, PageTransition::AUTO_BOOKMARK); +// params.disposition = NEW_BACKGROUND_TAB; +// browser::Navigate(¶ms); +// +// Opens a popup TabContents: +// browser::NavigateParams params(browser, popup_contents); +// params.source_contents = source_contents; +// browser::Navigate(¶ms); +// +// See browser_navigator_browsertest.cc for more examples. +// +struct NavigateParams { + NavigateParams(Browser* browser, + const GURL& a_url, + PageTransition::Type a_transition); + NavigateParams(Browser* browser, TabContents* a_target_contents); + ~NavigateParams(); + + // The URL/referrer to be loaded. Ignored if |target_contents| is non-NULL. + GURL url; + GURL referrer; + + // [in] A TabContents to be navigated or inserted into the target Browser's + // tabstrip. If NULL, |url| or the homepage will be used instead. When + // non-NULL, Navigate() assumes it has already been navigated to its + // intended destination and will not load any URL in it (i.e. |url| is + // ignored). + // Default is NULL. + // [out] The TabContents in which the navigation occurred or that was + // inserted. Guaranteed non-NULL except for note below: + // Note: If this field is set to NULL by the caller and Navigate() creates + // a new TabContents, this field will remain NULL and the TabContents + // deleted if the TabContents it created is not added to a TabStripModel + // before Navigate() returns. + TabContents* target_contents; + + // [in] The TabContents that initiated the Navigate() request if such context + // is necessary. Default is NULL, i.e. no context. + // [out] If NULL, this value will be set to the selected TabContents in the + // originating browser prior to the operation performed by Navigate(). + TabContents* source_contents; + + // The disposition requested by the navigation source. Default is + // CURRENT_TAB. What follows is a set of coercions that happen to this value + // when other factors are at play: + // + // [in]: Condition: [out]: + // NEW_BACKGROUND_TAB target browser tabstrip is empty NEW_FOREGROUND_TAB + // CURRENT_TAB " " " NEW_FOREGROUND_TAB + // OFF_THE_RECORD target browser profile is incog. NEW_FOREGROUND_TAB + // + // If disposition is NEW_WINDOW or NEW_POPUP, |show_window| is set to true + // automatically. + // If disposition is NEW_BACKGROUND_TAB, TabStripModel::ADD_SELECTED is + // removed from |tabstrip_add_types| automatically. + WindowOpenDisposition disposition; + + // The transition type of the navigation. Default is PageTransition::LINK + // when target_contents is specified in the constructor. + PageTransition::Type transition; + + // The index the caller would like the tab to be positioned at in the + // TabStrip. The actual index will be determined by the TabHandler in + // accordance with |add_types|. Defaults to -1 (allows the TabHandler to + // decide). + int tabstrip_index; + + // A bitmask of values defined in TabStripModel::AddTabTypes. Helps + // determine where to insert a new tab and whether or not it should be + // selected, among other properties. Default is ADD_SELECTED. + int tabstrip_add_types; + + // If non-empty, the new tab is an app tab. + std::string extension_app_id; + + // If non-empty, specifies the desired initial position and size of the + // window if |disposition| == NEW_POPUP. + // TODO(beng): Figure out if this can be used to create Browser windows + // for other callsites that use set_override_bounds, or + // remove this comment. + gfx::Rect window_bounds; + + // True if the target window should be made visible at the end of the call + // to Navigate(). This activates the window if it was already visible. + // Default is false. + bool show_window; + + // [in] Specifies a Browser object where the navigation could occur or the + // tab could be added. Navigate() is not obliged to use this Browser if + // it is not compatible with the operation being performed. If NULL, + // |profile| should be specified to find or create a matching Browser. + // [out] Specifies the Browser object where the navigation occurred or the + // tab was added. Guaranteed non-NULL unless the disposition did not + // require a navigation, in which case this is set to NULL + // (SUPPRESS_OPEN, SAVE_TO_DISK, IGNORE_ACTION). + // Note: If |show_window| is set to false and a new Browser is created by + // Navigate(), the caller is responsible for showing it so that its + // window can assume responsibility for the Browser's lifetime (Browser + // objects are deleted when the user closes a visible browser window). + Browser* browser; + + // If |browser| == NULL, specifies a Profile to use when finding or + // creating a Browser. + Profile* profile; + + private: + NavigateParams(); +}; + +// Navigates according to the configuration specified in |params|. +void Navigate(NavigateParams* params); + +} // namespace browser + +#endif // CHROME_BROWSER_UI_BROWSER_NAVIGATOR_H_ diff --git a/chrome/browser/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc index 4047524..2b292ab 100644 --- a/chrome/browser/browser_navigator_browsertest.cc +++ b/chrome/browser/ui/browser_navigator_browsertest.cc @@ -407,4 +407,61 @@ IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, Tabstrip_InsertAtIndex) { EXPECT_EQ(2, browser()->tab_count()); } +// This test verifies that constructing params with a NULL browser has +// the same result as navigating to a new foreground tab in the (only) +// active browser. Tests are the same as for Disposition_NewForegroundTab. +IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, NullBrowser_NewForegroundTab) { + TabContents* old_contents = browser()->GetSelectedTabContents(); + // Navigate with a NULL browser. + browser::NavigateParams p(MakeNavigateParams(NULL)); + p.disposition = NEW_FOREGROUND_TAB; + p.profile = browser()->profile(); + browser::Navigate(&p); + + // Navigate() should have found browser() and create a new tab. + EXPECT_EQ(browser(), p.browser); + EXPECT_NE(old_contents, browser()->GetSelectedTabContents()); + EXPECT_EQ(browser()->GetSelectedTabContents(), p.target_contents); + EXPECT_EQ(2, browser()->tab_count()); +} + +// This test verifies that constructing params with a NULL browser and +// a specific profile matches the specified profile. +IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, NullBrowser_MatchProfile) { + // Create a new browser with using the incognito profile. + Browser* incognito = + Browser::Create(browser()->profile()->GetOffTheRecordProfile()); + + // Navigate with a NULL browser and the incognito profile. + browser::NavigateParams p(MakeNavigateParams(NULL)); + p.disposition = NEW_FOREGROUND_TAB; + p.profile = incognito->profile(); + browser::Navigate(&p); + + // Navigate() should have found incognito, not browser(). + EXPECT_EQ(incognito, p.browser); + EXPECT_EQ(incognito->GetSelectedTabContents(), p.target_contents); + EXPECT_EQ(1, incognito->tab_count()); +} + +// This test verifies that constructing params with a NULL browser and +// disposition = NEW_WINDOW always opens exactly one new window. +IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, NullBrowser_NewWindow) { + browser::NavigateParams p(MakeNavigateParams(NULL)); + p.disposition = NEW_WINDOW; + p.profile = browser()->profile(); + browser::Navigate(&p); + + // Navigate() should have created a new browser. + EXPECT_NE(browser(), p.browser); + EXPECT_EQ(Browser::TYPE_NORMAL, p.browser->type()); + + // We should now have two windows, the browser() provided by the framework and + // the new normal window. + EXPECT_EQ(2u, BrowserList::size()); + EXPECT_EQ(1, browser()->tab_count()); + EXPECT_EQ(1, p.browser->tab_count()); +} + + } // namespace diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h new file mode 100644 index 0000000..c365a27 --- /dev/null +++ b/chrome/browser/ui/browser_window.h @@ -0,0 +1,384 @@ +// 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_UI_BROWSER_WINDOW_H_ +#define CHROME_BROWSER_UI_BROWSER_WINDOW_H_ +#pragma once + +#include "chrome/browser/tab_contents/navigation_entry.h" +#include "chrome/common/content_settings_types.h" +#include "gfx/native_widget_types.h" + +class Browser; +class BrowserWindowTesting; +class DownloadShelf; +class FindBar; +class GURL; +class HtmlDialogUIDelegate; +class LocationBar; +class Profile; +class StatusBubble; +class TabContents; +class TemplateURL; +class TemplateURLModel; +#if !defined(OS_MACOSX) +class ToolbarView; +#endif +struct NativeWebKeyboardEvent; + +namespace gfx { +class Rect; +} + +namespace views { +class Window; +} + +//////////////////////////////////////////////////////////////////////////////// +// BrowserWindow interface +// An interface implemented by the "view" of the Browser window. +// +// NOTE: All getters may return NULL. +class BrowserWindow { + public: + // Show the window, or activates it if it's already visible. + virtual void Show() = 0; + + // Sets the window's size and position to the specified values. + virtual void SetBounds(const gfx::Rect& bounds) = 0; + + // Closes the frame as soon as possible. If the frame is not in a drag + // session, it will close immediately; otherwise, it will move offscreen (so + // events are still fired) until the drag ends, then close. This assumes + // that the Browser is not immediately destroyed, but will be eventually + // destroyed by other means (eg, the tab strip going to zero elements). + // Bad things happen if the Browser dtor is called directly as a result of + // invoking this method. + virtual void Close() = 0; + + // Activates (brings to front) the window. Restores the window from minimized + // state if necessary. + virtual void Activate() = 0; + + // Deactivates the window, making the next window in the Z order the active + // window. + virtual void Deactivate() = 0; + + // Returns true if the window is currently the active/focused window. + virtual bool IsActive() const = 0; + + // Flashes the taskbar item associated with this frame. + virtual void FlashFrame() = 0; + + // Return a platform dependent identifier for this frame. On Windows, this + // returns an HWND. + virtual gfx::NativeWindow GetNativeHandle() = 0; + + // Returns a pointer to the testing interface to the Browser window, or NULL + // if there is none. + virtual BrowserWindowTesting* GetBrowserWindowTesting() = 0; + + // Return the status bubble associated with the frame + virtual StatusBubble* GetStatusBubble() = 0; + + // Inform the receiving frame that an animation has progressed in the + // selected tab. + // TODO(beng): Remove. Infobars/Boomarks bars should talk directly to + // BrowserView. + virtual void SelectedTabToolbarSizeChanged(bool is_animating) = 0; + + // Inform the frame that the selected tab favicon or title has changed. Some + // frames may need to refresh their title bar. + virtual void UpdateTitleBar() = 0; + + // Invoked when the visibility of the bookmark bar. + // NOTE: this is NOT sent when the user toggles the visibility of this, + // but rather when the user transitions from a page that forces + // it to be visibile to one that doesn't have it visible (or + // vice-versa). + // TODO(sky): see about routing visibility pref changing through here too. + virtual void ShelfVisibilityChanged() = 0; + + // Inform the frame that the dev tools window for the selected tab has + // changed. + virtual void UpdateDevTools() = 0; + + // Update any loading animations running in the window. |should_animate| is + // true if there are tabs loading and the animations should continue, false + // if there are no active loads and the animations should end. + virtual void UpdateLoadingAnimations(bool should_animate) = 0; + + // Sets the starred state for the current tab. + virtual void SetStarredState(bool is_starred) = 0; + + // Returns the nonmaximized bounds of the frame (even if the frame is + // currently maximized or minimized) in terms of the screen coordinates. + virtual gfx::Rect GetRestoredBounds() const = 0; + + // TODO(beng): REMOVE? + // Returns true if the frame is maximized (aka zoomed). + virtual bool IsMaximized() const = 0; + + // Accessors for fullscreen mode state. + virtual void SetFullscreen(bool fullscreen) = 0; + virtual bool IsFullscreen() const = 0; + + // Returns true if the fullscreen bubble is visible. + virtual bool IsFullscreenBubbleVisible() const = 0; + + // Returns the location bar. + virtual LocationBar* GetLocationBar() const = 0; + + // Tries to focus the location bar. Clears the window focus (to avoid + // inconsistent state) if this fails. + virtual void SetFocusToLocationBar(bool select_all) = 0; + + // Informs the view whether or not a load is in progress for the current tab. + // The view can use this notification to update the reload/stop button. + virtual void UpdateReloadStopState(bool is_loading, bool force) = 0; + + // Updates the toolbar with the state for the specified |contents|. + virtual void UpdateToolbar(TabContents* contents, + bool should_restore_state) = 0; + + // Focuses the toolbar (for accessibility). + virtual void FocusToolbar() = 0; + + // Focuses the app menu like it was a menu bar. + // + // Not used on the Mac, which has a "normal" menu bar. + virtual void FocusAppMenu() = 0; + + // Focuses the bookmarks toolbar (for accessibility). + virtual void FocusBookmarksToolbar() = 0; + + // Focuses the Chrome OS status view (for accessibility). + virtual void FocusChromeOSStatus() = 0; + + // Moves keyboard focus to the next pane. + virtual void RotatePaneFocus(bool forwards) = 0; + + // Returns whether the bookmark bar is visible or not. + virtual bool IsBookmarkBarVisible() const = 0; + + // Returns whether the bookmark bar is animating or not. + virtual bool IsBookmarkBarAnimating() const = 0; + + // Returns whether the tool bar is visible or not. + virtual bool IsToolbarVisible() const = 0; + + // Returns the rect where the resize corner should be drawn by the render + // widget host view (on top of what the renderer returns). We return an empty + // rect to identify that there shouldn't be a resize corner (in the cases + // where we take care of it ourselves at the browser level). + virtual gfx::Rect GetRootWindowResizerRect() const = 0; + + // Tells the frame not to render as inactive until the next activation change. + // This is required on Windows when dropdown selects are shown to prevent the + // select from deactivating the browser frame. A stub implementation is + // provided here since the functionality is Windows-specific. + virtual void DisableInactiveFrame() {} + + // Shows a confirmation dialog box for setting the default search engine + // described by |template_url|. Takes ownership of |template_url|. + virtual void ConfirmSetDefaultSearchProvider( + TabContents* tab_contents, + TemplateURL* template_url, + TemplateURLModel* template_url_model) { + // TODO(levin): Implement this for non-Windows platforms and make it pure. + } + + // Shows a confirmation dialog box for adding a search engine described by + // |template_url|. Takes ownership of |template_url|. + virtual void ConfirmAddSearchProvider(const TemplateURL* template_url, + Profile* profile) = 0; + + // Shows or hides the bookmark bar depending on its current visibility. + virtual void ToggleBookmarkBar() = 0; + + // Shows the About Chrome dialog box. + virtual views::Window* ShowAboutChromeDialog() = 0; + + // Shows the Update Recommended dialog box. + virtual void ShowUpdateChromeDialog() = 0; + + // Shows the Task manager. + virtual void ShowTaskManager() = 0; + + // Shows the Bookmark bubble. |url| is the URL being bookmarked, + // |already_bookmarked| is true if the url is already bookmarked. + virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) = 0; + + // Whether or not the shelf view is visible. + virtual bool IsDownloadShelfVisible() const = 0; + + // Returns the DownloadShelf. + virtual DownloadShelf* GetDownloadShelf() = 0; + + // Shows the Report a Bug dialog box. + virtual void ShowReportBugDialog() = 0; + + // Shows the Clear Browsing Data dialog box. + virtual void ShowClearBrowsingDataDialog() = 0; + + // Shows the Import Bookmarks & Settings dialog box. + virtual void ShowImportDialog() = 0; + + // Shows the Search Engines dialog box. + virtual void ShowSearchEnginesDialog() = 0; + + // Shows the Password Manager dialog box. + virtual void ShowPasswordManager() = 0; + + // Shows the repost form confirmation dialog box. + virtual void ShowRepostFormWarningDialog(TabContents* tab_contents) = 0; + + // Shows the Content Settings dialog box. + virtual void ShowContentSettingsWindow(ContentSettingsType content_type, + Profile* profile) = 0; + + // Shows the collected cookies dialog box. + virtual void ShowCollectedCookiesDialog(TabContents* tab_contents) = 0; + + // Shows a dialog to the user that something is wrong with the profile. + // |message_id| is the ID for a string in the string table which will be + // displayed in the dialog. + virtual void ShowProfileErrorDialog(int message_id) = 0; + + // Show the bubble that indicates to the user that a theme is being installed. + virtual void ShowThemeInstallBubble() = 0; + + // Shows the confirmation dialog box warning that the browser is closing with + // in-progress downloads. + // This method should call Browser::InProgressDownloadResponse once the user + // has confirmed. + virtual void ConfirmBrowserCloseWithPendingDownloads() = 0; + + // Shows a dialog box with HTML content, e.g. for Gears. |parent_window| is + // the window the dialog should be opened modal to and is a native window + // handle. + virtual void ShowHTMLDialog(HtmlDialogUIDelegate* delegate, + gfx::NativeWindow parent_window) = 0; + + // Asks the window to continue a drag operation begun in a different browser + // window. |tab_bounds| are the bounds of the Tab view that was dragged from + // the source window, in screen coordinates. The corresponding Tab view in + // this new window will be positioned at these bounds for a seamless + // appearance. + virtual void ContinueDraggingDetachedTab(const gfx::Rect& tab_bounds) {} + + // BrowserThemeProvider calls this when a user has changed his or her theme, + // indicating that it's time to redraw everything. + virtual void UserChangedTheme() = 0; + + // Get extra vertical height that the render view should add to its requests + // to webkit. This can help prevent sending extraneous layout/repaint requests + // when the delegate is in the process of resizing the tab contents view (e.g. + // during infobar animations). + virtual int GetExtraRenderViewHeight() const = 0; + + // Notification that |tab_contents| got the focus through user action (click + // on the page). + virtual void TabContentsFocused(TabContents* tab_contents) = 0; + + // Shows the page info using the specified information. + // |url| is the url of the page/frame the info applies to, |ssl| is the SSL + // information for that page/frame. If |show_history| is true, a section + // showing how many times that URL has been visited is added to the page info. + virtual void ShowPageInfo(Profile* profile, + const GURL& url, + const NavigationEntry::SSLStatus& ssl, + bool show_history) = 0; + + // Shows the app menu (for accessibility). + virtual void ShowAppMenu() = 0; + + // Allows the BrowserWindow object to handle the specified keyboard event + // before sending it to the renderer. + // Returns true if the |event| was handled. Otherwise, if the |event| would + // be handled in HandleKeyboardEvent() method as a normal keyboard shortcut, + // |*is_keyboard_shortcut| should be set to true. + virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, + bool* is_keyboard_shortcut) = 0; + + // Allows the BrowserWindow object to handle the specified keyboard event, + // if the renderer did not process it. + virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) = 0; + + // Shows the create web app shortcut dialog box. + virtual void ShowCreateShortcutsDialog(TabContents* tab_contents) = 0; + + // Clipboard commands applied to the whole browser window. + virtual void Cut() = 0; + virtual void Copy() = 0; + virtual void Paste() = 0; + + // Switches between available tabstrip display modes. + virtual void ToggleTabStripMode() = 0; + +#if defined(OS_MACOSX) + // Opens the tabpose view. + virtual void OpenTabpose() = 0; +#endif + + // Invoked when instant's tab contents should be shown. + virtual void ShowInstant(TabContents* preview_contents) = 0; + + // Invoked when the instant's tab contents should be hidden. + virtual void HideInstant() = 0; + + // Returns the desired bounds for instant in screen coordinates. Note that if + // instant isn't currently visible this returns the bounds instant would be + // placed at. + virtual gfx::Rect GetInstantBounds() = 0; + + // Construct a BrowserWindow implementation for the specified |browser|. + static BrowserWindow* CreateBrowserWindow(Browser* browser); + + // Construct a FindBar implementation for the specified |browser|. + static FindBar* CreateFindBar(Browser* browser_window); + + protected: + friend class BrowserList; + friend class BrowserView; + virtual void DestroyBrowser() = 0; + + virtual ~BrowserWindow() {} +}; + +#if defined(OS_WIN) || defined(TOOLKIT_VIEWS) +class BookmarkBarView; +class LocationBarView; + +namespace views { +class View; +} +#endif // defined(OS_WIN) + +// A BrowserWindow utility interface used for accessing elements of the browser +// UI used only by UI test automation. +class BrowserWindowTesting { + public: +#if defined(OS_WIN) || defined(TOOLKIT_VIEWS) + // Returns the BookmarkBarView. + virtual BookmarkBarView* GetBookmarkBarView() const = 0; + + // Returns the LocationBarView. + virtual LocationBarView* GetLocationBarView() const = 0; + + // Returns the TabContentsContainer. + virtual views::View* GetTabContentsContainerView() const = 0; + + // Returns the TabContentsContainer. + virtual views::View* GetSidebarContainerView() const = 0; + + // Returns the ToolbarView. + virtual ToolbarView* GetToolbarView() const = 0; +#endif + + protected: + virtual ~BrowserWindowTesting() {} +}; + +#endif // CHROME_BROWSER_UI_BROWSER_WINDOW_H_ diff --git a/chrome/browser/unload_uitest.cc b/chrome/browser/unload_uitest.cc index 3ddd9b0..6abc736 100644 --- a/chrome/browser/unload_uitest.cc +++ b/chrome/browser/unload_uitest.cc @@ -324,7 +324,7 @@ TEST_F(UnloadTest, BrowserCloseBeforeUnloadCancel) { } #if defined(OS_LINUX) -// Fails sometimes on Linux valgrind. http://crbug.com/32615 +// Fails sometimes on Linux valgrind. http://crbug.com/45675 #define MAYBE_BrowserCloseWithInnerFocusedFrame \ FLAKY_BrowserCloseWithInnerFocusedFrame #else @@ -414,7 +414,8 @@ TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnloadAlert) { #define MAYBE_BrowserCloseTabWhenOtherTabHasListener \ DISABLED_BrowserCloseTabWhenOtherTabHasListener #else -// Flaky on Linux as well. http://crbug.com/45562 +// Flaky on Linux under valgrind. http://crbug.com/46781 +// TODO(stuartmorgan): Switch to just disabling for valgrind. #define MAYBE_BrowserCloseTabWhenOtherTabHasListener \ FLAKY_BrowserCloseTabWhenOtherTabHasListener #endif diff --git a/chrome/browser/upgrade_detector.cc b/chrome/browser/upgrade_detector.cc index 558ea70..6a65c7a 100644 --- a/chrome/browser/upgrade_detector.cc +++ b/chrome/browser/upgrade_detector.cc @@ -42,7 +42,7 @@ int GetCheckForUpgradeEveryMs() { std::string interval = cmd_line.GetSwitchValueASCII(switches::kCheckForUpdateIntervalSec); if (!interval.empty() && base::StringToInt(interval, &interval_ms)) - return interval_ms * 1000; // Command line value is in seconds. + return interval_ms * 1000; // Command line value is in seconds. // Otherwise check once an hour for dev channel and once a day for all other // channels/builds. @@ -59,17 +59,6 @@ int GetCheckForUpgradeEveryMs() { // How long to wait before notifying the user about the upgrade. const int kNotifyUserAfterMs = 0; -// The thread to run the upgrade detection code on. We use FILE for Linux -// because we don't want to block the UI thread while launching a background -// process and reading its output; on the Mac, checking for an upgrade -// requires reading a file. -const BrowserThread::ID kDetectUpgradeTaskID = -#if defined(OS_POSIX) - BrowserThread::FILE; -#else - BrowserThread::UI; -#endif - // This task checks the currently running version of Chrome against the // installed version. If the installed version is newer, it runs the passed // callback task. Otherwise it just deletes the task. @@ -88,7 +77,7 @@ class DetectUpgradeTask : public Task { } virtual void Run() { - DCHECK(BrowserThread::CurrentlyOn(kDetectUpgradeTaskID)); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); using installer::Version; scoped_ptr<Version> installed_version; @@ -179,7 +168,11 @@ void UpgradeDetector::CheckForUpgrade() { method_factory_.RevokeAll(); Task* callback_task = method_factory_.NewRunnableMethod(&UpgradeDetector::UpgradeDetected); - BrowserThread::PostTask(kDetectUpgradeTaskID, FROM_HERE, + // We use FILE as the thread to run the upgrade detection code on all + // platforms. For Linux, this is because we don't want to block the UI thread + // while launching a background process and reading its output; on the Mac and + // on Windows checking for an upgrade requires reading a file. + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, new DetectUpgradeTask(callback_task)); } diff --git a/chrome/browser/user_style_sheet_watcher_unittest.cc b/chrome/browser/user_style_sheet_watcher_unittest.cc index ad55136..c791cb9 100644 --- a/chrome/browser/user_style_sheet_watcher_unittest.cc +++ b/chrome/browser/user_style_sheet_watcher_unittest.cc @@ -23,8 +23,8 @@ TEST(UserStyleSheetWatcherTest, StyleLoad) { ASSERT_TRUE(file_util::WriteFile(style_sheet_file, css_file_contents.data(), css_file_contents.length())); - scoped_refptr<UserStyleSheetWatcher> style_sheet_watcher = - new UserStyleSheetWatcher(dir.path()); + scoped_refptr<UserStyleSheetWatcher> style_sheet_watcher( + new UserStyleSheetWatcher(dir.path())); MessageLoop loop; BrowserThread ui_thread(BrowserThread::UI, &loop); BrowserThread file_thread(BrowserThread::FILE, &loop); diff --git a/chrome/browser/utility.sb b/chrome/browser/utility.sb index 1d3a12b..4bbac11 100644 --- a/chrome/browser/utility.sb +++ b/chrome/browser/utility.sb @@ -14,4 +14,4 @@ ; *** The contents of chrome/common/common.sb are implicitly included here. *** ; Enable full access to given directory if needed. -;ENABLE_DIRECTORY_ACCESS (allow file-read* file-write* (regex #"DIR_TO_ALLOW_ACCESS"))
\ No newline at end of file +;ENABLE_DIRECTORY_ACCESS
\ No newline at end of file diff --git a/chrome/browser/views/about_ipc_dialog.cc b/chrome/browser/views/about_ipc_dialog.cc index a23b122..358e28f 100644 --- a/chrome/browser/views/about_ipc_dialog.cc +++ b/chrome/browser/views/about_ipc_dialog.cc @@ -19,6 +19,7 @@ #include "base/thread.h" #include "base/time.h" #include "base/utf_string_conversions.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/browser_process.h" #include "chrome/common/chrome_constants.h" diff --git a/chrome/browser/views/accelerator_table_gtk.cc b/chrome/browser/views/accelerator_table_gtk.cc index 3baa65e..261420e 100644 --- a/chrome/browser/views/accelerator_table_gtk.cc +++ b/chrome/browser/views/accelerator_table_gtk.cc @@ -6,14 +6,14 @@ #include "app/keyboard_codes.h" #include "base/basictypes.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" namespace browser { // NOTE: Keep this list in the same (mostly-alphabetical) order as // the Windows accelerators in ../../app/chrome_dll.rc. const AcceleratorMapping kAcceleratorMap[] = { - // Keycode Shift Ctrl Alt Command ID + // Keycode Shift Ctrl Alt Command ID { app::VKEY_A, true, true, false, IDC_AUTOFILL_DEFAULT }, { app::VKEY_LEFT, false, false, true, IDC_BACK }, { app::VKEY_BACK, false, false, false, IDC_BACK }, @@ -81,6 +81,10 @@ const AcceleratorMapping kAcceleratorMap[] = { #if !defined(OS_CHROMEOS) { app::VKEY_F1, false, false, false, IDC_HELP_PAGE }, #endif +#if defined(OS_CHROMEOS) + { app::VKEY_OEM_2, false, true, false, IDC_HELP_PAGE }, + { app::VKEY_OEM_2, true, true, false, IDC_HELP_PAGE }, +#endif { app::VKEY_I, true, true, false, IDC_DEV_TOOLS }, { app::VKEY_J, true, true, false, IDC_DEV_TOOLS_CONSOLE }, { app::VKEY_C, true, true, false, IDC_DEV_TOOLS_INSPECT }, diff --git a/chrome/browser/views/app_launched_animation_win.cc b/chrome/browser/views/app_launched_animation_win.cc index 8a23d43..50efc5d 100644 --- a/chrome/browser/views/app_launched_animation_win.cc +++ b/chrome/browser/views/app_launched_animation_win.cc @@ -38,7 +38,7 @@ class AppLaunchedAnimationWin : public AnimationDelegate, public ImageLoadingTracker::Observer, public views::ImageView { public: - AppLaunchedAnimationWin(Extension* extension, const gfx::Rect& rect); + AppLaunchedAnimationWin(const Extension* extension, const gfx::Rect& rect); private: // AnimationDelegate @@ -65,7 +65,7 @@ class AppLaunchedAnimationWin : public AnimationDelegate, DISALLOW_COPY_AND_ASSIGN(AppLaunchedAnimationWin); }; -AppLaunchedAnimationWin::AppLaunchedAnimationWin(Extension* extension, +AppLaunchedAnimationWin::AppLaunchedAnimationWin(const Extension* extension, const gfx::Rect& rect) : popup_(NULL), rect_(rect), @@ -128,7 +128,8 @@ void AppLaunchedAnimationWin::OnImageLoaded(SkBitmap* image, } // namespace // static -void AppLaunchedAnimation::Show(Extension* extension, const gfx::Rect& rect) { +void AppLaunchedAnimation::Show(const Extension* extension, + const gfx::Rect& rect) { // The animation will delete itself when it's finished. new AppLaunchedAnimationWin(extension, rect); } diff --git a/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.cc b/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.cc index 807ee00..bfd7321 100644 --- a/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.cc +++ b/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.cc @@ -13,6 +13,7 @@ #include "base/i18n/rtl.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete/autocomplete_edit_view.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/instant/instant_opt_in.h" #include "chrome/browser/views/bubble_border.h" diff --git a/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h b/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h index 2924ce2..9a3e2bc 100644 --- a/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h +++ b/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h @@ -22,6 +22,7 @@ class AutocompleteEditModel; class AutocompleteEditViewWin; +struct AutocompleteMatch; class BubbleBorder; class Profile; diff --git a/chrome/browser/views/autofill_profiles_view_win.cc b/chrome/browser/views/autofill_profiles_view_win.cc index 5c991f8..414e0ba 100644 --- a/chrome/browser/views/autofill_profiles_view_win.cc +++ b/chrome/browser/views/autofill_profiles_view_win.cc @@ -56,28 +56,6 @@ const int kDialogPadding = 7; const int kSubViewHorizotalInsets = 18; const int kSubViewVerticalInsets = 5; -// This is a helper to compare items that were just created with items returned -// from the db. -// ProfileType could be either AutofillProfile or CreditCard. -// The second argument could have an incomplete ID (0) - change it to first -// argument's id for comparison and then change it back. -template<class ProfileType> bool IsEqualDataWithIncompleteId( - ProfileType const * data_to_compare, ProfileType* data_with_incomplete_id) { - if (!data_with_incomplete_id->unique_id()) { - bool label_unset = data_with_incomplete_id->Label().empty(); - if (label_unset) - data_with_incomplete_id->set_label(data_to_compare->Label()); - data_with_incomplete_id->set_unique_id(data_to_compare->unique_id()); - bool are_equal = (*data_to_compare == *data_with_incomplete_id); - data_with_incomplete_id->set_unique_id(0); - if (label_unset) - data_with_incomplete_id->set_label(string16()); - return are_equal; - } else { - return (*data_to_compare == *data_with_incomplete_id); - } -} - }; // namespace ///////////////////////////////////////////////////////////////////////////// @@ -104,7 +82,6 @@ AutoFillProfilesView::AutoFillProfilesView( remove_button_(NULL), scroll_view_(NULL), focus_manager_(NULL), - billing_model_(true), child_dialog_opened_(false) { DCHECK(preferences_); enable_auto_fill_.Init(prefs::kAutoFillEnabled, preferences_, this); @@ -156,16 +133,16 @@ void AutoFillProfilesView::AddClicked(int group_type) { std::vector<EditableSetInfo>::iterator it = profiles_set_.end(); int added_item_index = -1; if (group_type == ContentListTableModel::kAddressGroup) { - AutoFillProfile address(std::wstring(), 0); + AutoFillProfile address; info.reset(new EditableSetInfo(&address)); } else if (group_type == ContentListTableModel::kCreditCardGroup) { - CreditCard credit_card(std::wstring(), 0); + CreditCard credit_card; info.reset(new EditableSetInfo(&credit_card)); } else { NOTREACHED(); } EditableSetViewContents *edit_view = new - EditableSetViewContents(this, &billing_model_, true, *info); + EditableSetViewContents(this, true, *info); views::Window::CreateChromeWindow(window()->GetNativeWindow(), gfx::Rect(), edit_view); edit_view->window()->Show(); @@ -185,7 +162,7 @@ void AutoFillProfilesView::EditClicked() { it = credit_card_set_.begin() + (index - profiles_set_.size()); EditableSetViewContents *edit_view = new - EditableSetViewContents(this, &billing_model_, false, *it); + EditableSetViewContents(this, false, *it); views::Window::CreateChromeWindow(window()->GetNativeWindow(), gfx::Rect(), edit_view); edit_view->window()->Show(); @@ -203,7 +180,6 @@ void AutoFillProfilesView::DeleteClicked() { last_view_row = table_model_->RowCount() - 1; if (last_view_row >= 0) scroll_view_->Select(scroll_view_->ViewToModel(last_view_row)); - UpdateBillingModel(); UpdateWidgetState(); SaveData(); } @@ -216,18 +192,16 @@ void AutoFillProfilesView::EditAccepted(EditableSetInfo* data, std::vector<EditableSetInfo>::iterator end_it; end_it = data->is_address ? profiles_set_.end() : credit_card_set_.end(); for (; it != end_it; ++it) { - if (it->unique_id() == data->unique_id()) { + if (it->guid() == data->guid()) { *it = *data; break; } if (new_item) { if (data->is_address) { - if (IsEqualDataWithIncompleteId<AutoFillProfile>(&it->address, - &data->address)) + if (it->address.Compare(data->address) == 0) break; } else { - if (IsEqualDataWithIncompleteId<CreditCard>(&it->credit_card, - &data->credit_card)) + if (it->credit_card.Compare(data->credit_card) == 0) break; } } @@ -238,7 +212,6 @@ void AutoFillProfilesView::EditAccepted(EditableSetInfo* data, else credit_card_set_.push_back(*data); } - UpdateBillingModel(); UpdateWidgetState(); SaveData(); } @@ -261,19 +234,6 @@ void AutoFillProfilesView::UpdateWidgetState() { autofill_enabled); } -void AutoFillProfilesView::UpdateProfileLabels() { - std::vector<AutoFillProfile*> profiles; - profiles.resize(profiles_set_.size()); - for (size_t i = 0; i < profiles_set_.size(); ++i) { - profiles[i] = &(profiles_set_[i].address); - } - AutoFillProfile::AdjustInferredLabels(&profiles); -} - -void AutoFillProfilesView::UpdateBillingModel() { - billing_model_.SetAddressLabels(profiles_set_); -} - void AutoFillProfilesView::ChildWindowOpened() { child_dialog_opened_ = true; UpdateWidgetState(); @@ -453,7 +413,6 @@ void AutoFillProfilesView::OnPersonalDataChanged() { ++address_it) { profiles_set_.push_back(EditableSetInfo(*address_it)); } - UpdateProfileLabels(); credit_card_set_.clear(); for (std::vector<CreditCard*>::const_iterator cc_it = @@ -492,8 +451,6 @@ void AutoFillProfilesView::Init() { l10n_util::GetString(IDS_OPTIONS_AUTOFILL_ENABLE)); enable_auto_fill_button_->set_listener(this); - billing_model_.SetAddressLabels(profiles_set_); - table_model_.reset(new ContentListTableModel(&profiles_set_, &credit_card_set_)); std::vector<TableColumn> columns; @@ -572,7 +529,6 @@ void AutoFillProfilesView::GetData() { profiles_set_.push_back(EditableSetInfo(*address_it)); } } - UpdateProfileLabels(); if (!imported_data_present) { credit_card_set_.reserve(personal_data_manager_->credit_cards().size()); @@ -732,14 +688,11 @@ AutoFillProfilesView::EditableSetViewContents::TextFieldToAutoFill // AutoFillProfilesView::EditableSetViewContents, public: AutoFillProfilesView::EditableSetViewContents::EditableSetViewContents( AutoFillProfilesView* observer, - AddressComboBoxModel* billing_model, bool new_item, const EditableSetInfo& field_set) : temporary_info_(field_set), has_credit_card_number_been_edited_(false), observer_(observer), - billing_model_(billing_model), - combo_box_billing_(NULL), new_item_(new_item) { ZeroMemory(text_fields_, sizeof(text_fields_)); } @@ -863,7 +816,6 @@ AutoFillProfilesView::EditableSetViewContents::GetWindowTitle() const { } void AutoFillProfilesView::EditableSetViewContents::WindowClosing() { - billing_model_->ClearComboBoxes(); observer_->ChildWindowClosed(); } @@ -876,7 +828,6 @@ bool AutoFillProfilesView::EditableSetViewContents::Cancel() { // Remove added item - it is last in the list. if (temporary_info_.is_address) { observer_->profiles_set_.pop_back(); - observer_->UpdateBillingModel(); } else { observer_->credit_card_set_.pop_back(); } @@ -946,16 +897,7 @@ bool AutoFillProfilesView::EditableSetViewContents::HandleKeystroke( // views::Combobox::Listener implementations: void AutoFillProfilesView::EditableSetViewContents::ItemChanged( views::Combobox* combo_box, int prev_index, int new_index) { - DCHECK(billing_model_); - if (combo_box == combo_box_billing_) { - if (new_index == -1) { - NOTREACHED(); - } else { - DCHECK(new_index < static_cast<int>(observer_->profiles_set_.size())); - temporary_info_.credit_card.set_billing_address_id( - observer_->profiles_set_[new_index].address.unique_id()); - } - } else if (combo_box == combo_box_month_) { + if (combo_box == combo_box_month_) { if (new_index == -1) { NOTREACHED(); } else { @@ -1078,7 +1020,6 @@ void AutoFillProfilesView::EditableSetViewContents::InitAddressFields( void AutoFillProfilesView::EditableSetViewContents::InitCreditCardFields( views::GridLayout* layout) { DCHECK(!temporary_info_.is_address); - DCHECK(billing_model_); // Create combo box models. combo_box_model_month_.reset(new StringVectorComboboxModel); @@ -1120,20 +1061,6 @@ void AutoFillProfilesView::EditableSetViewContents::InitCreditCardFields( layout->StartRow(0, double_column_fill_view_set_id_); layout->AddView(text_fields_[TEXT_CC_NAME]); - // Address combo boxes. - combo_box_billing_ = new views::Combobox(billing_model_); - combo_box_billing_->set_listener(this); - int billing_id = temporary_info_.credit_card.billing_address_id(); - if (billing_id) - combo_box_billing_->SetSelectedItem(billing_model_->GetIndex(billing_id)); - billing_model_->UsedWithComboBox(combo_box_billing_); - - layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); - layout->StartRow(0, double_column_fill_view_set_id_); - layout->AddView(CreateLeftAlignedLabel(IDS_AUTOFILL_DIALOG_BILLING_ADDRESS)); - layout->StartRow(0, double_column_fill_view_set_id_); - layout->AddView(combo_box_billing_); - // Layout credit card info layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); layout->StartRow(0, double_column_ccnumber_cvc_); @@ -1288,78 +1215,6 @@ bool AutoFillProfilesView::EditableSetViewContents::UpdateContentsPhoneViews( return false; } -///////////////////////////////////////////////////////////////////////////// -// AutoFillProfilesView::AddressComboBoxModel, public: -AutoFillProfilesView::AddressComboBoxModel::AddressComboBoxModel( - bool is_billing) - : is_billing_(is_billing) { -} - -void AutoFillProfilesView::AddressComboBoxModel::SetAddressLabels( - const std::vector<EditableSetInfo>& address_labels) { - address_labels_.clear(); - for (size_t i = 0; i < address_labels.size(); ++i) { - const EditableSetInfo& item = address_labels[i]; - DCHECK(item.is_address); - FieldTypeSet fields; - item.address.GetAvailableFieldTypes(&fields); - if (fields.find(ADDRESS_HOME_LINE1) == fields.end() && - fields.find(ADDRESS_HOME_LINE2) == fields.end() && - fields.find(ADDRESS_HOME_APT_NUM) == fields.end() && - fields.find(ADDRESS_HOME_CITY) == fields.end() && - fields.find(ADDRESS_HOME_STATE) == fields.end() && - fields.find(ADDRESS_HOME_ZIP) == fields.end() && - fields.find(ADDRESS_HOME_COUNTRY) == fields.end()) { - // No address information in this profile; it's useless as a billing - // address. - continue; - } - address_labels_.push_back(item); - } - NotifyChanged(); -} - -void AutoFillProfilesView::AddressComboBoxModel::UsedWithComboBox( - views::Combobox* combo_box) { - combo_boxes_.push_back(combo_box); -} - -void AutoFillProfilesView::AddressComboBoxModel::NotifyChanged() { - for (std::list<views::Combobox*>::iterator it = combo_boxes_.begin(); - it != combo_boxes_.end(); - ++it) - (*it)->ModelChanged(); -} - -int AutoFillProfilesView::AddressComboBoxModel::GetIndex(int unique_id) { - int shift = is_billing_ ? 0 : 1; - for (size_t i = 0; i < address_labels_.size(); ++i) { - if (address_labels_.at(i).address.unique_id() == unique_id) - return i + shift; - } - return -1; -} - -///////////////////////////////////////////////////////////////////////////// -// AutoFillProfilesView::AddressComboBoxModel, ComboboxModel methods -int AutoFillProfilesView::AddressComboBoxModel::GetItemCount() { - int shift = is_billing_ ? 0 : 1; - return static_cast<int>(address_labels_.size()) + shift; -} - -string16 AutoFillProfilesView::AddressComboBoxModel::GetItemAt(int index) { - int shift = is_billing_ ? 0 : 1; - DCHECK(index < (static_cast<int>(address_labels_.size()) + shift)); - if (!is_billing_ && !index) - return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAME_AS_BILLING); - DCHECK(address_labels_.at(index - shift).is_address); - string16 label = - WideToUTF16Hack(address_labels_.at(index - shift).address.Label()); - if (label.empty()) - label = l10n_util::GetStringUTF16(IDS_AUTOFILL_NEW_ADDRESS); - return label; -} - void AutoFillProfilesView::StringVectorComboboxModel::set_cb_strings( std::vector<std::wstring> *source) { cb_strings_.swap(*source); @@ -1423,7 +1278,7 @@ std::wstring AutoFillProfilesView::ContentListTableModel::GetText( int row, int column_id) { DCHECK(row < static_cast<int>(profiles_->size() + credit_cards_->size())); if (row < static_cast<int>(profiles_->size())) { - return profiles_->at(row).address.PreviewSummary(); + return profiles_->at(row).address.Label(); } else { row -= profiles_->size(); return credit_cards_->at(row).credit_card.PreviewSummary(); diff --git a/chrome/browser/views/autofill_profiles_view_win.h b/chrome/browser/views/autofill_profiles_view_win.h index dc0f051..efeaf59 100644 --- a/chrome/browser/views/autofill_profiles_view_win.h +++ b/chrome/browser/views/autofill_profiles_view_win.h @@ -89,13 +89,6 @@ class AutoFillProfilesView : public views::View, // Updates state of the buttons. void UpdateWidgetState(); - // Updates inferred labels. - void UpdateProfileLabels(); - - // Updates the billing model. This is invoked any time the profile_set_ - // changes. - void UpdateBillingModel(); - // Following two functions are called from opened child dialog to // disable/enable buttons. void ChildWindowOpened(); @@ -173,11 +166,11 @@ class AutoFillProfilesView : public views::View, is_address(false) { } - int unique_id() const { + std::string guid() const { if (is_address) - return address.unique_id(); + return address.guid(); else - return credit_card.unique_id(); + return credit_card.guid(); } }; @@ -249,7 +242,6 @@ class AutoFillProfilesView : public views::View, }; // Forward declaration. - class AddressComboBoxModel; class StringVectorComboboxModel; // Sub-view for editing/adding a credit card or address. @@ -260,7 +252,6 @@ class AutoFillProfilesView : public views::View, public views::Combobox::Listener { public: EditableSetViewContents(AutoFillProfilesView* observer, - AddressComboBoxModel* billing_model, bool new_item, const EditableSetInfo& field_set); virtual ~EditableSetViewContents() {} @@ -339,8 +330,6 @@ class AutoFillProfilesView : public views::View, EditableSetInfo temporary_info_; bool has_credit_card_number_been_edited_; AutoFillProfilesView* observer_; - AddressComboBoxModel* billing_model_; - views::Combobox* combo_box_billing_; scoped_ptr<StringVectorComboboxModel> combo_box_model_month_; views::Combobox* combo_box_month_; scoped_ptr<StringVectorComboboxModel> combo_box_model_year_; @@ -368,43 +357,6 @@ class AutoFillProfilesView : public views::View, DISALLOW_COPY_AND_ASSIGN(EditableSetViewContents); }; - // Encapsulates ComboboxModel for address. - class AddressComboBoxModel : public ComboboxModel { - public: - explicit AddressComboBoxModel(bool is_billing); - virtual ~AddressComboBoxModel() {} - - // Updates address_labels_ from |address_labels|. - void SetAddressLabels(const std::vector<EditableSetInfo>& address_labels); - - // When you add a CB view that relies on this model, call this function - // so the CB can be notified if strings change. Can be called multiple - // times if several combo boxes relying on the model. - // Model does not own |combo_box|. - void UsedWithComboBox(views::Combobox *combo_box); - - // Need to be called when comboboxes are destroyed. - void ClearComboBoxes() { combo_boxes_.clear(); } - - // Call this function if one of the labels has changed - void NotifyChanged(); - - // Gets index of the item in the model or -1 if not found. - int GetIndex(int unique_id); - - // Overridden from ComboboxModel: - // Public as they are used from EditableSetViewContents. - virtual int GetItemCount(); - virtual string16 GetItemAt(int index); - - private: - std::list<views::Combobox*> combo_boxes_; - std::vector<EditableSetInfo> address_labels_; - bool is_billing_; - - DISALLOW_COPY_AND_ASSIGN(AddressComboBoxModel); - }; - class StringVectorComboboxModel : public ComboboxModel { public: StringVectorComboboxModel() {} @@ -468,8 +420,6 @@ class AutoFillProfilesView : public views::View, std::vector<EditableSetInfo> profiles_set_; std::vector<EditableSetInfo> credit_card_set_; - AddressComboBoxModel billing_model_; - BooleanPrefMember enable_auto_fill_; views::Checkbox* enable_auto_fill_button_; diff --git a/chrome/browser/views/bookmark_bar_view.cc b/chrome/browser/views/bookmark_bar_view.cc index 8ce71e7..9188aa5 100644 --- a/chrome/browser/views/bookmark_bar_view.cc +++ b/chrome/browser/views/bookmark_bar_view.cc @@ -160,10 +160,9 @@ static std::wstring CreateToolTipForURLAndTitle(const gfx::Point& screen_loc, // "/http://www.yahoo.com" when rendered, as is, in an RTL context since // the Unicode BiDi algorithm puts certain characters on the left by // default. - std::wstring elided_url(gfx::ElideUrl(url, tt_font, max_width, languages)); - elided_url = UTF16ToWide(base::i18n::GetDisplayStringInLTRDirectionality( - WideToUTF16(elided_url))); - result.append(elided_url); + string16 elided_url(gfx::ElideUrl(url, tt_font, max_width, languages)); + elided_url = base::i18n::GetDisplayStringInLTRDirectionality(elided_url); + result.append(UTF16ToWideHack(elided_url)); } return result; } @@ -448,11 +447,13 @@ void BookmarkBarView::SetProfile(Profile* profile) { delete GetChildViewAt(0); model_ = profile_->GetBookmarkModel(); - model_->AddObserver(this); - if (model_->IsLoaded()) - Loaded(model_); - // else case: we'll receive notification back from the BookmarkModel when done - // loading, then we'll populate the bar. + if (model_) { + model_->AddObserver(this); + if (model_->IsLoaded()) + Loaded(model_); + // else case: we'll receive notification back from the BookmarkModel when + // done loading, then we'll populate the bar. + } } void BookmarkBarView::SetPageNavigator(PageNavigator* navigator) { diff --git a/chrome/browser/views/bookmark_bar_view_test.cc b/chrome/browser/views/bookmark_bar_view_test.cc index 06e1242..d30d0e4 100644 --- a/chrome/browser/views/bookmark_bar_view_test.cc +++ b/chrome/browser/views/bookmark_bar_view_test.cc @@ -53,6 +53,13 @@ #endif +#if defined(OS_LINUX) +// See bug http://crbug.com/60444 for details. +#define MAYBE_ScrollButtonScrolls DISABLED_ScrollButtonScrolls +#else +#define MAYBE_ScrollButtonScrolls ScrollButtonScrolls +#endif + namespace { class ViewsDelegateImpl : public views::ViewsDelegate { @@ -849,7 +856,7 @@ class BookmarkBarViewTest9 : public BookmarkBarViewEventTestBase { views::MenuItemView* first_menu_; }; -VIEW_TEST(BookmarkBarViewTest9, ScrollButtonScrolls) +VIEW_TEST(BookmarkBarViewTest9, MAYBE_ScrollButtonScrolls) // Tests up/down/left/enter key messages. class BookmarkBarViewTest10 : public BookmarkBarViewEventTestBase { diff --git a/chrome/browser/views/bookmark_bubble_view.cc b/chrome/browser/views/bookmark_bubble_view.cc index c13f53f..a1f651b 100644 --- a/chrome/browser/views/bookmark_bubble_view.cc +++ b/chrome/browser/views/bookmark_bubble_view.cc @@ -9,7 +9,7 @@ #include "app/resource_bundle.h" #include "base/string16.h" #include "base/string_util.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/bookmarks/bookmark_editor.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/bookmarks/bookmark_utils.h" @@ -88,9 +88,9 @@ void BookmarkBubbleView::Show(views::Window* parent, return; bubble_ = new BookmarkBubbleView(delegate, profile, url, newly_bookmarked); - InfoBubble* info_bubble = - InfoBubble::Show(parent->GetClientView()->GetWidget(), bounds, - BubbleBorder::TOP_RIGHT, bubble_, bubble_); + InfoBubble* info_bubble = InfoBubble::Show( + parent->GetClientView()->GetWidget(), bounds, BubbleBorder::TOP_RIGHT, + bubble_, bubble_); bubble_->set_info_bubble(info_bubble); GURL url_ptr(url); NotificationService::current()->Notify( diff --git a/chrome/browser/views/browser_actions_container.cc b/chrome/browser/views/browser_actions_container.cc index d2db56c..b630806 100644 --- a/chrome/browser/views/browser_actions_container.cc +++ b/chrome/browser/views/browser_actions_container.cc @@ -58,7 +58,7 @@ bool BrowserActionsContainer::disable_animations_during_testing_ = false; //////////////////////////////////////////////////////////////////////////////// // BrowserActionButton -BrowserActionButton::BrowserActionButton(Extension* extension, +BrowserActionButton::BrowserActionButton(const Extension* extension, BrowserActionsContainer* panel) : ALLOW_THIS_IN_INITIALIZER_LIST( MenuButton(this, std::wstring(), NULL, false)), @@ -281,7 +281,7 @@ BrowserActionButton::~BrowserActionButton() { //////////////////////////////////////////////////////////////////////////////// // BrowserActionView -BrowserActionView::BrowserActionView(Extension* extension, +BrowserActionView::BrowserActionView(const Extension* extension, BrowserActionsContainer* panel) : panel_(panel) { button_ = new BrowserActionButton(extension, panel); @@ -838,7 +838,7 @@ void BrowserActionsContainer::MoveBrowserAction(const std::string& extension_id, size_t new_index) { ExtensionsService* service = profile_->GetExtensionsService(); if (service) { - Extension* extension = service->GetExtensionById(extension_id, false); + const Extension* extension = service->GetExtensionById(extension_id, false); model_->MoveBrowserAction(extension, new_index); SchedulePaint(); } @@ -886,7 +886,7 @@ int BrowserActionsContainer::IconHeight() { return icon_height; } -void BrowserActionsContainer::BrowserActionAdded(Extension* extension, +void BrowserActionsContainer::BrowserActionAdded(const Extension* extension, int index) { #if defined(DEBUG) for (size_t i = 0; i < browser_action_views_.size(); ++i) { @@ -915,7 +915,8 @@ void BrowserActionsContainer::BrowserActionAdded(Extension* extension, // Enlarge the container if it was already at maximum size and we're not in // the middle of upgrading. - if ((model_->GetVisibleIconCount() < 0) && !extension->being_upgraded()) { + if ((model_->GetVisibleIconCount() < 0) && + !profile_->GetExtensionsService()->IsBeingUpgraded(extension)) { suppress_chevron_ = true; SaveDesiredSizeAndAnimate(Tween::LINEAR, visible_actions + 1); } else { @@ -924,7 +925,7 @@ void BrowserActionsContainer::BrowserActionAdded(Extension* extension, } } -void BrowserActionsContainer::BrowserActionRemoved(Extension* extension) { +void BrowserActionsContainer::BrowserActionRemoved(const Extension* extension) { CloseOverflowMenu(); if (popup_ && popup_->host()->extension() == extension) @@ -940,7 +941,7 @@ void BrowserActionsContainer::BrowserActionRemoved(Extension* extension) { // If the extension is being upgraded we don't want the bar to shrink // because the icon is just going to get re-added to the same location. - if (extension->being_upgraded()) + if (profile_->GetExtensionsService()->IsBeingUpgraded(extension)) return; if (browser_action_views_.size() > visible_actions) { @@ -961,7 +962,7 @@ void BrowserActionsContainer::BrowserActionRemoved(Extension* extension) { } } -void BrowserActionsContainer::BrowserActionMoved(Extension* extension, +void BrowserActionsContainer::BrowserActionMoved(const Extension* extension, int index) { if (!ShouldDisplayBrowserAction(extension)) return; @@ -1091,7 +1092,8 @@ void BrowserActionsContainer::SaveDesiredSizeAndAnimate( } } -bool BrowserActionsContainer::ShouldDisplayBrowserAction(Extension* extension) { +bool BrowserActionsContainer::ShouldDisplayBrowserAction( + const Extension* extension) { // Only display incognito-enabled extensions while in incognito mode. return (!profile_->IsOffTheRecord() || profile_->GetExtensionsService()->IsIncognitoEnabled(extension)); diff --git a/chrome/browser/views/browser_actions_container.h b/chrome/browser/views/browser_actions_container.h index cd376f7..0bc5fdd 100644 --- a/chrome/browser/views/browser_actions_container.h +++ b/chrome/browser/views/browser_actions_container.h @@ -54,13 +54,14 @@ class BrowserActionButton : public views::MenuButton, public ImageLoadingTracker::Observer, public NotificationObserver { public: - BrowserActionButton(Extension* extension, BrowserActionsContainer* panel); + BrowserActionButton(const Extension* extension, + BrowserActionsContainer* panel); // Call this instead of delete. void Destroy(); ExtensionAction* browser_action() const { return browser_action_; } - Extension* extension() { return extension_; } + const Extension* extension() { return extension_; } // Called to update the display to match the browser action's state. void UpdateState(); @@ -112,7 +113,7 @@ class BrowserActionButton : public views::MenuButton, ExtensionAction* browser_action_; // The extension associated with the browser action we're displaying. - Extension* extension_; + const Extension* extension_; // The object that is waiting for the image loading to complete // asynchronously. @@ -146,7 +147,7 @@ class BrowserActionButton : public views::MenuButton, class BrowserActionView : public views::View { public: - BrowserActionView(Extension* extension, BrowserActionsContainer* panel); + BrowserActionView(const Extension* extension, BrowserActionsContainer* panel); virtual ~BrowserActionView(); BrowserActionButton* button() { return button_; } @@ -391,9 +392,9 @@ class BrowserActionsContainer static int IconHeight(); // ExtensionToolbarModel::Observer implementation. - virtual void BrowserActionAdded(Extension* extension, int index); - virtual void BrowserActionRemoved(Extension* extension); - virtual void BrowserActionMoved(Extension* extension, int index); + virtual void BrowserActionAdded(const Extension* extension, int index); + virtual void BrowserActionRemoved(const Extension* extension); + virtual void BrowserActionMoved(const Extension* extension, int index); virtual void ModelLoaded(); void LoadImages(); @@ -441,7 +442,7 @@ class BrowserActionsContainer // Returns true if this extension should be shown in this toolbar. This can // return false if we are in an incognito window and the extension is disabled // for incognito. - bool ShouldDisplayBrowserAction(Extension* extension); + bool ShouldDisplayBrowserAction(const Extension* extension); // The vector of browser actions (icons/image buttons for each action). Note // that not every BrowserAction in the ToolbarModel will necessarily be in diff --git a/chrome/browser/views/browser_actions_container_browsertest.cc b/chrome/browser/views/browser_actions_container_browsertest.cc index 664b510..ee30ddb 100644 --- a/chrome/browser/views/browser_actions_container_browsertest.cc +++ b/chrome/browser/views/browser_actions_container_browsertest.cc @@ -172,7 +172,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, TestCrash57536) { .AppendASCII("browser_action") .AppendASCII("crash_57536"))); - Extension* extension = service->extensions()->at(size_before); + const Extension* extension = service->extensions()->at(size_before); std::cout << "Creating bitmap\n" << std::flush; diff --git a/chrome/browser/views/clear_server_data.cc b/chrome/browser/views/clear_server_data.cc index d01daf6..2cd3be7 100644 --- a/chrome/browser/views/clear_server_data.cc +++ b/chrome/browser/views/clear_server_data.cc @@ -250,9 +250,7 @@ void ClearServerDataView::LinkActivated(views::Link* source, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); browser->window()->Show(); } else { - browser->OpenURL(GURL(l10n_util::GetStringUTF8(IDS_PRIVACY_DASHBOARD_URL)), - GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); - browser->window()->Show(); + browser->OpenPrivacyDashboardTabAndActivate(); } } diff --git a/chrome/browser/views/constrained_html_delegate_win.cc b/chrome/browser/views/constrained_html_delegate_win.cc new file mode 100644 index 0000000..6d53e22 --- /dev/null +++ b/chrome/browser/views/constrained_html_delegate_win.cc @@ -0,0 +1,113 @@ +// 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/dom_ui/constrained_html_ui.h" + +#include "chrome/browser/dom_ui/html_dialog_tab_contents_delegate.h" +#include "chrome/browser/dom_ui/html_dialog_ui.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/views/tab_contents/tab_contents_container.h" +#include "gfx/rect.h" +#include "ipc/ipc_message.h" +#include "views/view.h" +#include "views/widget/widget_win.h" +#include "views/window/window_delegate.h" + +class ConstrainedHtmlDelegateWin : public TabContentsContainer, + public ConstrainedHtmlUIDelegate, + public ConstrainedWindowDelegate, + public HtmlDialogTabContentsDelegate { + public: + ConstrainedHtmlDelegateWin(Profile* profile, + HtmlDialogUIDelegate* delegate); + ~ConstrainedHtmlDelegateWin(); + + // ConstrainedHtmlUIDelegate interface. + virtual HtmlDialogUIDelegate* GetHtmlDialogUIDelegate(); + virtual void OnDialogClose(); + + // ConstrainedWindowDelegate (aka views::WindowDelegate) interface. + virtual bool CanResize() const { return true; } + virtual views::View* GetContentsView() { + return this; + } + virtual void WindowClosing() { + html_delegate_->OnDialogClosed(""); + } + + // HtmlDialogTabContentsDelegate interface. + void MoveContents(TabContents* source, const gfx::Rect& pos) {} + void ToolbarSizeChanged(TabContents* source, bool is_animating) {} + void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {} + + // Overridden from TabContentsContainer. + virtual gfx::Size GetPreferredSize() { + gfx::Size size; + html_delegate_->GetDialogSize(&size); + return size; + } + + virtual void ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child) { + TabContentsContainer::ViewHierarchyChanged(is_add, parent, child); + if (is_add && child == this) { + ChangeTabContents(&html_tab_contents_); + } + } + + void set_window(ConstrainedWindow* window) { + window_ = window; + } + +private: + TabContents html_tab_contents_; + + HtmlDialogUIDelegate* html_delegate_; + + // The constrained window that owns |this|. Saved so we can close it later. + ConstrainedWindow* window_; +}; + +ConstrainedHtmlDelegateWin::ConstrainedHtmlDelegateWin( + Profile* profile, + HtmlDialogUIDelegate* delegate) + : HtmlDialogTabContentsDelegate(profile), + html_tab_contents_(profile, NULL, MSG_ROUTING_NONE, NULL, NULL), + html_delegate_(delegate), + window_(NULL) { + CHECK(delegate); + html_tab_contents_.set_delegate(this); + + // Set |this| as a property so the ConstrainedHtmlUI can retrieve it. + ConstrainedHtmlUI::GetPropertyAccessor().SetProperty( + html_tab_contents_.property_bag(), this); + html_tab_contents_.controller().LoadURL(delegate->GetDialogContentURL(), + GURL(), + PageTransition::START_PAGE); +} + +ConstrainedHtmlDelegateWin::~ConstrainedHtmlDelegateWin() { +} + +HtmlDialogUIDelegate* ConstrainedHtmlDelegateWin::GetHtmlDialogUIDelegate() { + return html_delegate_; +} + +void ConstrainedHtmlDelegateWin::OnDialogClose() { + window_->CloseConstrainedWindow(); +} + +// static +void ConstrainedHtmlUI::CreateConstrainedHtmlDialog( + Profile* profile, + HtmlDialogUIDelegate* delegate, + TabContents* container) { + ConstrainedHtmlDelegateWin* constrained_delegate = + new ConstrainedHtmlDelegateWin(profile, delegate); + ConstrainedWindow* constrained_window = + container->CreateConstrainedDialog(constrained_delegate); + constrained_delegate->set_window(constrained_window); +} diff --git a/chrome/browser/views/constrained_html_dialog_browsertest.cc b/chrome/browser/views/constrained_html_dialog_browsertest.cc deleted file mode 100644 index c868fb8..0000000 --- a/chrome/browser/views/constrained_html_dialog_browsertest.cc +++ /dev/null @@ -1,93 +0,0 @@ -// 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. - -#if defined(OS_WIN) - -#include "chrome/test/ui/ui_test.h" - -#include "base/file_path.h" -#include "base/message_loop.h" -#include "chrome/browser/browser_thread.h" -#include "chrome/browser/dom_ui/constrained_html_dialog.h" -#include "chrome/browser/renderer_host/render_widget_host_view.h" -#include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/browser/views/constrained_html_dialog_win.h" -#include "chrome/browser/views/html_dialog_view.h" -#include "chrome/common/url_constants.h" -#include "chrome/test/in_process_browser_test.h" -#include "chrome/test/ui_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "views/widget/widget.h" -#include "views/window/window.h" - -using testing::Eq; - -namespace { - -// Window non-client-area means that the minimum size for the window -// won't be the actual minimum size - our layout and resizing code -// makes sure the chrome is always visible. -const int kMinimumWidthToTestFor = 20; -const int kMinimumHeightToTestFor = 30; - -class TestHtmlDialogUIDelegate : public HtmlDialogUIDelegate { - public: - TestHtmlDialogUIDelegate() {} - virtual ~TestHtmlDialogUIDelegate() {} - - // HTMLDialogUIDelegate implementation: - virtual bool IsDialogModal() const { - return true; - } - virtual std::wstring GetDialogTitle() const { - return std::wstring(L"Test"); - } - virtual GURL GetDialogContentURL() const { - return GURL(chrome::kAboutAboutURL); - } - virtual void GetDOMMessageHandlers( - std::vector<DOMMessageHandler*>* handlers) const {} - virtual void GetDialogSize(gfx::Size* size) const { - size->set_width(400); - size->set_height(400); - } - virtual std::string GetDialogArgs() const { - return std::string(); - } - virtual void OnDialogClosed(const std::string& json_retval) { } - virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; - } -}; - -} // namespace - -class ConstrainedHtmlDialogBrowserTest : public InProcessBrowserTest { - public: - ConstrainedHtmlDialogBrowserTest() {} -}; - -IN_PROC_BROWSER_TEST_F(ConstrainedHtmlDialogBrowserTest, LoadWindow) { - HtmlDialogUIDelegate* delegate = new TestHtmlDialogUIDelegate(); - - ConstrainedHtmlDialogWin* dialog = - new ConstrainedHtmlDialogWin(browser()->profile(), delegate); - - TabContents* tab_contents = browser()->GetSelectedTabContents(); - ASSERT_TRUE(tab_contents != NULL); - ConstrainedWindow* window = tab_contents->CreateConstrainedDialog( - dialog->GetConstrainedWindowDelegate()); - - EXPECT_EQ(1, tab_contents->constrained_window_count()); - - gfx::Rect bounds = dialog->GetContentsView()->bounds(); - EXPECT_LT(0, bounds.width()); - EXPECT_LT(0, bounds.height()); - EXPECT_GE(400, bounds.width()); - EXPECT_GE(400, bounds.height()); -} - -#endif // OS_WIN diff --git a/chrome/browser/views/constrained_html_dialog_win.cc b/chrome/browser/views/constrained_html_dialog_win.cc deleted file mode 100644 index 2cdc81c..0000000 --- a/chrome/browser/views/constrained_html_dialog_win.cc +++ /dev/null @@ -1,118 +0,0 @@ -// 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/views/constrained_html_dialog_win.h" - -#include "chrome/browser/dom_ui/html_dialog_ui.h" -#include "chrome/browser/profile.h" -#include "chrome/browser/renderer_host/render_view_host.h" -#include "chrome/browser/renderer_host/render_view_host_delegate.h" -#include "chrome/browser/renderer_host/render_widget_host_view_win.h" -#include "chrome/browser/renderer_host/site_instance.h" -#include "chrome/common/bindings_policy.h" -#include "chrome/common/renderer_preferences.h" -#include "views/widget/widget_win.h" -#include "views/window/window_delegate.h" - -class ConstrainedHtmlDialogHostView : public views::NativeViewHost { - public: - explicit ConstrainedHtmlDialogHostView(ConstrainedHtmlDialogWin* dialog) - : dialog_(dialog), - initialized_(false) { - } - - virtual void ViewHierarchyChanged(bool is_add, - views::View* parent, - views::View* child) { - NativeViewHost::ViewHierarchyChanged(is_add, parent, child); - if (is_add && GetWidget() && !initialized_) { - dialog_->Init(GetWidget()->GetNativeView()); - initialized_ = true; - } - } - - private: - ConstrainedHtmlDialogWin* dialog_; - bool initialized_; -}; - -ConstrainedHtmlDialogWin::ConstrainedHtmlDialogWin( - Profile* profile, - HtmlDialogUIDelegate* delegate) - : ConstrainedHtmlDialog(profile, delegate) { - CHECK(delegate); - native_view_host_ = new ConstrainedHtmlDialogHostView(this); - - // Size the native view to match the delegate preferred size. - gfx::Size size; - delegate->GetDialogSize(&size); - native_view_host_->SetPreferredSize(size); - - // Create a site instance for the correct URL. - dialog_url_ = delegate->GetDialogContentURL(); - site_instance_ = - SiteInstance::CreateSiteInstanceForURL(profile, dialog_url_); -} - -ConstrainedHtmlDialogWin::~ConstrainedHtmlDialogWin() { -} - -ConstrainedWindowDelegate* -ConstrainedHtmlDialogWin::GetConstrainedWindowDelegate() { - return this; -} - -bool ConstrainedHtmlDialogWin::CanResize() const { - return true; -} - -views::View* ConstrainedHtmlDialogWin::GetContentsView() { - return native_view_host_; -} - -void ConstrainedHtmlDialogWin::WindowClosing() { - html_dialog_ui_delegate()->OnDialogClosed(""); -} - -int ConstrainedHtmlDialogWin::GetBrowserWindowID() const { - return 0; -} - -ViewType::Type ConstrainedHtmlDialogWin::GetRenderViewType() const { - return ViewType::HTML_DIALOG_UI; -} - -RendererPreferences ConstrainedHtmlDialogWin::GetRendererPrefs( - Profile* profile) const { - return RendererPreferences(); -} - -void ConstrainedHtmlDialogWin::ProcessDOMUIMessage( - const ViewHostMsg_DomMessage_Params& params) { - DOMUI::ProcessDOMUIMessage(params); -} - -void ConstrainedHtmlDialogWin::Init(gfx::NativeView parent_view) { - render_view_host_.reset(new RenderViewHost(site_instance_, - this, - MSG_ROUTING_NONE, - NULL)); - render_view_host_->AllowBindings(BindingsPolicy::DOM_UI); - render_widget_host_view_ = - new RenderWidgetHostViewWin(render_view_host_.get()); - render_view_host_->set_view(render_widget_host_view_); - render_view_host_->CreateRenderView(string16()); - render_view_host_->NavigateToURL(dialog_url_); - HWND hwnd = render_widget_host_view_->Create(parent_view); - render_widget_host_view_->ShowWindow(SW_SHOW); - native_view_host_->Attach(hwnd); - - InitializeDOMUI(render_view_host_.get()); -} - -// static -ConstrainedHtmlDialog* ConstrainedHtmlDialog::CreateConstrainedHTMLDialog( - Profile* profile, HtmlDialogUIDelegate* delegate) { - return new ConstrainedHtmlDialogWin(profile, delegate); -} diff --git a/chrome/browser/views/constrained_html_dialog_win.h b/chrome/browser/views/constrained_html_dialog_win.h deleted file mode 100644 index 6faa821..0000000 --- a/chrome/browser/views/constrained_html_dialog_win.h +++ /dev/null @@ -1,66 +0,0 @@ -// 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_VIEWS_CONSTRAINED_HTML_DIALOG_WIN_H_ -#define CHROME_BROWSER_VIEWS_CONSTRAINED_HTML_DIALOG_WIN_H_ -#pragma once - -#include <string> - -#include "chrome/browser/dom_ui/constrained_html_dialog.h" -#include "chrome/browser/dom_ui/html_dialog_ui.h" -#include "chrome/browser/renderer_host/render_view_host_delegate.h" -#include "chrome/browser/renderer_host/site_instance.h" -#include "chrome/common/bindings_policy.h" -#include "chrome/common/renderer_preferences.h" -#include "views/controls/native/native_view_host.h" -#include "views/window/window_delegate.h" - -class RenderViewHost; -class RenderWidgetHostViewWin; - -class ConstrainedHtmlDialogWin : public ConstrainedHtmlDialog, - public ConstrainedWindowDelegate, - public RenderViewHostDelegate { - public: - ConstrainedHtmlDialogWin(Profile* profile, - HtmlDialogUIDelegate* delegate); - ~ConstrainedHtmlDialogWin(); - - // Called when the dialog is actually being added to the views hierarchy. - void Init(gfx::NativeView parent_window); - - // ConstrainedHtmlDialog override. - virtual ConstrainedWindowDelegate* GetConstrainedWindowDelegate(); - - // ConstrainedWindowDelegate (aka views::WindowDelegate) override. - virtual bool CanResize() const; - virtual views::View* GetContentsView(); - virtual void WindowClosing(); - - // RenderViewHostDelegate overrides. - virtual int GetBrowserWindowID() const; - virtual ViewType::Type GetRenderViewType() const; - virtual RendererPreferences GetRendererPrefs(Profile* profile) const; - virtual void ProcessDOMUIMessage( - const ViewHostMsg_DomMessage_Params& params); - virtual void UpdateInspectorSetting(const std::string& key, - const std::string& value) {} - virtual void ClearInspectorSettings() {} - - private: - SiteInstance* site_instance_; - scoped_ptr<RenderViewHost> render_view_host_; - - // URL to be displayed in the dialog. - GURL dialog_url_; - - // Pointer owned by the |render_view_host_| object. - RenderWidgetHostViewWin* render_widget_host_view_; - - // View pointer owned by the views hierarchy. - views::NativeViewHost* native_view_host_; -}; - -#endif // CHROME_BROWSER_VIEWS_CONSTRAINED_HTML_DIALOG_WIN_H_ diff --git a/chrome/browser/views/constrained_window_win.cc b/chrome/browser/views/constrained_window_win.cc index f997c3d..7fa1dc6 100644 --- a/chrome/browser/views/constrained_window_win.cc +++ b/chrome/browser/views/constrained_window_win.cc @@ -6,7 +6,7 @@ #include "app/resource_bundle.h" #include "app/win_util.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" @@ -564,7 +564,7 @@ views::NonClientFrameView* ConstrainedWindowWin::CreateFrameViewForWindow() { void ConstrainedWindowWin::FocusConstrainedWindow() { if ((!owner_->delegate() || - owner_->delegate()->ShouldFocusConstrainedWindow(owner_)) && + owner_->delegate()->ShouldFocusConstrainedWindow()) && GetDelegate() && GetDelegate()->GetInitiallyFocusedView()) { GetDelegate()->GetInitiallyFocusedView()->RequestFocus(); } diff --git a/chrome/browser/views/content_setting_bubble_contents.cc b/chrome/browser/views/content_setting_bubble_contents.cc index 240fbcf..cea404d 100644 --- a/chrome/browser/views/content_setting_bubble_contents.cc +++ b/chrome/browser/views/content_setting_bubble_contents.cc @@ -30,6 +30,14 @@ #include "views/standard_layout.h" #include "webkit/glue/plugins/plugin_list.h" +// If we don't clamp the maximum width, then very long URLs and titles can make +// the bubble arbitrarily wide. +const int kMaxContentsWidth = 500; + +// When we have multiline labels, we should set a minimum width lest we get very +// narrow bubbles with lots of line-wrapping. +const int kMinMultiLineContentsWidth = 250; + class ContentSettingBubbleContents::Favicon : public views::ImageView { public: Favicon(const SkBitmap& image, @@ -115,6 +123,16 @@ ContentSettingBubbleContents::ContentSettingBubbleContents( ContentSettingBubbleContents::~ContentSettingBubbleContents() { } +gfx::Size ContentSettingBubbleContents::GetPreferredSize() { + gfx::Size preferred_size(views::View::GetPreferredSize()); + int preferred_width = + (!content_setting_bubble_model_->bubble_content().domain_lists.empty() && + (kMinMultiLineContentsWidth > preferred_size.width())) ? + kMinMultiLineContentsWidth : preferred_size.width(); + preferred_size.set_width(std::min(preferred_width, kMaxContentsWidth)); + return preferred_size; +} + void ContentSettingBubbleContents::ViewHierarchyChanged(bool is_add, View* parent, View* child) { @@ -240,6 +258,7 @@ void ContentSettingBubbleContents::InitControlLayout() { views::Link* link = new views::Link(UTF8ToWide(i->title)); link->SetController(this); + link->SetElideInMiddle(true); popup_links_[link] = i - bubble_content.popup_items.begin(); layout->AddView(new Favicon((*i).bitmap, this, link)); layout->AddView(link); @@ -289,11 +308,6 @@ void ContentSettingBubbleContents::InitControlLayout() { layout->StartRow(0, single_column_set_id); views::Label* section_title = new views::Label(UTF8ToWide(i->title)); section_title->SetMultiLine(true); - // TODO(joth): Should need to have hard coded size here, but without it - // we get empty space at very end of bubble (as it's initially sized really - // tall & skinny but then widens once the link/buttons are added - // at the end of this method). - section_title->SizeToFit(256); section_title->SetHorizontalAlignment(views::Label::ALIGN_LEFT); layout->AddView(section_title, 1, 1, GridLayout::FILL, GridLayout::LEADING); for (std::set<std::string>::const_iterator j = i->hosts.begin(); diff --git a/chrome/browser/views/content_setting_bubble_contents.h b/chrome/browser/views/content_setting_bubble_contents.h index b469cc2..b35744f 100644 --- a/chrome/browser/views/content_setting_bubble_contents.h +++ b/chrome/browser/views/content_setting_bubble_contents.h @@ -48,6 +48,8 @@ class ContentSettingBubbleContents : public views::View, // the bubble and must keep it alive. void set_info_bubble(InfoBubble* info_bubble) { info_bubble_ = info_bubble; } + virtual gfx::Size GetPreferredSize(); + private: class Favicon; diff --git a/chrome/browser/views/download_item_view.cc b/chrome/browser/views/download_item_view.cc index 695abe4..3ceaf52 100644 --- a/chrome/browser/views/download_item_view.cc +++ b/chrome/browser/views/download_item_view.cc @@ -209,7 +209,7 @@ DownloadItemView::DownloadItemView(DownloadItem* download, dangerous_mode_body_image_set_ = dangerous_mode_body_image_set; LoadIcon(); - tooltip_text_ = download_->GetFileName().ToWStringHack(); + tooltip_text_ = download_->GetFileNameToReportUser().ToWStringHack(); font_ = ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont); box_height_ = std::max<int>(2 * kVerticalPadding + font_.GetHeight() + @@ -257,11 +257,11 @@ DownloadItemView::DownloadItemView(DownloadItem* download, // Ensure the file name is not too long. // Extract the file extension (if any). - FilePath filepath(download->original_name()); + FilePath filename(download->target_name()); #if defined(OS_LINUX) - std::wstring extension = base::SysNativeMBToWide(filepath.Extension()); + std::wstring extension = base::SysNativeMBToWide(filename.Extension()); #else - std::wstring extension = filepath.Extension(); + std::wstring extension = filename.Extension(); #endif // Remove leading '.' @@ -269,9 +269,9 @@ DownloadItemView::DownloadItemView(DownloadItem* download, extension = extension.substr(1); #if defined(OS_LINUX) std::wstring rootname = - base::SysNativeMBToWide(filepath.BaseName().RemoveExtension().value()); + base::SysNativeMBToWide(filename.RemoveExtension().value()); #else - std::wstring rootname = filepath.BaseName().RemoveExtension().value(); + std::wstring rootname = filename.RemoveExtension().value(); #endif // Elide giant extensions (this shouldn't currently be hit, but might @@ -602,7 +602,7 @@ void DownloadItemView::Paint(gfx::Canvas* canvas) { if (!IsDangerousMode()) { string16 filename; if (!disabled_while_opening_) { - filename = gfx::ElideFilename(download_->GetFileName(), + filename = gfx::ElideFilename(download_->GetFileNameToReportUser(), font_, kTextWidth); } else { // First, Calculate the download status opening string width. @@ -612,7 +612,7 @@ void DownloadItemView::Paint(gfx::Canvas* canvas) { int status_string_width = font_.GetStringWidth(status_string); // Then, elide the file name. string16 filename_string = - gfx::ElideFilename(download_->GetFileName(), font_, + gfx::ElideFilename(download_->GetFileNameToReportUser(), font_, kTextWidth - status_string_width); // Last, concat the whole string. filename = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_OPENING, @@ -637,7 +637,7 @@ void DownloadItemView::Paint(gfx::Canvas* canvas) { // Paint the icon. IconManager* im = g_browser_process->icon_manager(); SkBitmap* icon = IsDangerousMode() ? warning_icon_ : - im->LookupIcon(download_->full_path(), IconLoader::SMALL); + im->LookupIcon(download_->GetUserVerifiedFileName(), IconLoader::SMALL); // We count on the fact that the icon manager will cache the icons and if one // is available, it will be cached here. We *don't* want to request the icon @@ -731,7 +731,7 @@ void DownloadItemView::ClearDangerousMode() { // We need to load the icon now that the download_ has the real path. LoadIcon(); - tooltip_text_ = download_->GetFileName().ToWStringHack(); + tooltip_text_ = download_->GetFileNameToReportUser().ToWStringHack(); // Force the shelf to layout again as our size has changed. parent_->Layout(); @@ -849,7 +849,7 @@ bool DownloadItemView::OnMouseDragged(const views::MouseEvent& event) { if (dragging_) { if (download_->state() == DownloadItem::COMPLETE) { IconManager* im = g_browser_process->icon_manager(); - SkBitmap* icon = im->LookupIcon(download_->full_path(), + SkBitmap* icon = im->LookupIcon(download_->GetUserVerifiedFileName(), IconLoader::SMALL); if (icon) { views::Widget* widget = GetWidget(); @@ -960,7 +960,8 @@ void DownloadItemView::OnExtractIconComplete(IconManager::Handle handle, void DownloadItemView::LoadIcon() { IconManager* im = g_browser_process->icon_manager(); - im->LoadIcon(download_->full_path(), IconLoader::SMALL, &icon_consumer_, + im->LoadIcon(download_->GetUserVerifiedFileName(), + IconLoader::SMALL, &icon_consumer_, NewCallback(this, &DownloadItemView::OnExtractIconComplete)); } @@ -1062,7 +1063,7 @@ void DownloadItemView::UpdateAccessibleName() { new_name = dangerous_download_label_->GetText(); } else { new_name = status_text_ + L" " + - download_->GetFileName().ToWStringHack(); + download_->GetFileNameToReportUser().ToWStringHack(); } // If the name has changed, call SetAccessibleName and notify diff --git a/chrome/browser/views/extensions/extension_install_prompt.cc b/chrome/browser/views/extensions/extension_install_prompt.cc index f75b557..5cf1e7b 100644 --- a/chrome/browser/views/extensions/extension_install_prompt.cc +++ b/chrome/browser/views/extensions/extension_install_prompt.cc @@ -9,6 +9,7 @@ #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/extensions/extension_install_ui.h" +#include "chrome/browser/views/window.h" #include "chrome/common/extensions/extension.h" #include "grit/generated_resources.h" #include "views/controls/button/checkbox.h" @@ -35,8 +36,10 @@ const int kIconSize = 69; class InstallDialogContent : public views::View, public views::DialogDelegate { public: InstallDialogContent(ExtensionInstallUI::Delegate* delegate, - Extension* extension, SkBitmap* icon, ExtensionInstallUI::PromptType type) - : delegate_(delegate), icon_(NULL), type_(type) { + const Extension* extension, + SkBitmap* icon, + ExtensionInstallUI::PromptType type) + : delegate_(delegate), icon_(NULL), type_(type) { // Scale down to icon size, but allow smaller icons (don't scale up). gfx::Size size(icon->width(), icon->height()); if (size.width() > kIconSize || size.height() > kIconSize) @@ -141,7 +144,10 @@ class InstallDialogContent : public views::View, public views::DialogDelegate { // static void ExtensionInstallUI::ShowExtensionInstallUIPromptImpl( - Profile* profile, Delegate* delegate, Extension* extension, SkBitmap* icon, + Profile* profile, + Delegate* delegate, + const Extension* extension, + SkBitmap* icon, PromptType type) { Browser* browser = BrowserList::GetLastActiveWithProfile(profile); if (!browser) { @@ -155,7 +161,7 @@ void ExtensionInstallUI::ShowExtensionInstallUIPromptImpl( return; } - views::Window::CreateChromeWindow(window->GetNativeHandle(), gfx::Rect(), + browser::CreateViewsWindow(window->GetNativeHandle(), gfx::Rect(), new InstallDialogContent(delegate, extension, icon, type))->Show(); } diff --git a/chrome/browser/views/extensions/extension_install_prompt2.cc b/chrome/browser/views/extensions/extension_install_prompt2.cc index 467ae20..ba9631e 100644 --- a/chrome/browser/views/extensions/extension_install_prompt2.cc +++ b/chrome/browser/views/extensions/extension_install_prompt2.cc @@ -58,7 +58,7 @@ class InstallDialogContent2 : public views::View, public views::DialogDelegate { public: InstallDialogContent2(ExtensionInstallUI::Delegate* delegate, - Extension* extension, + const Extension* extension, SkBitmap* icon, const std::vector<string16>& permissions); @@ -109,7 +109,7 @@ class InstallDialogContent2 InstallDialogContent2::InstallDialogContent2( - ExtensionInstallUI::Delegate* delegate, Extension* extension, + ExtensionInstallUI::Delegate* delegate, const Extension* extension, SkBitmap* icon, const std::vector<string16>& permissions) : delegate_(delegate), icon_(NULL), @@ -292,7 +292,8 @@ void InstallDialogContent2::Layout() { // static void ExtensionInstallUI::ShowExtensionInstallUIPrompt2Impl( - Profile* profile, Delegate* delegate, Extension* extension, SkBitmap* icon, + Profile* profile, Delegate* delegate, const Extension* extension, + SkBitmap* icon, const std::vector<string16>& permissions) { #if defined(OS_CHROMEOS) // Use a normal browser window as parent on ChromeOS. diff --git a/chrome/browser/views/extensions/extension_installed_bubble.cc b/chrome/browser/views/extensions/extension_installed_bubble.cc index 4997ac6..8cb9397 100644 --- a/chrome/browser/views/extensions/extension_installed_bubble.cc +++ b/chrome/browser/views/extensions/extension_installed_bubble.cc @@ -69,7 +69,7 @@ const int kAnimationWaitMaxRetry = 10; class InstalledBubbleContent : public views::View, public views::ButtonListener { public: - InstalledBubbleContent(Extension* extension, + InstalledBubbleContent(const Extension* extension, ExtensionInstalledBubble::BubbleType type, SkBitmap* icon) : info_bubble_(NULL), @@ -241,12 +241,13 @@ class InstalledBubbleContent : public views::View, DISALLOW_COPY_AND_ASSIGN(InstalledBubbleContent); }; -void ExtensionInstalledBubble::Show(Extension *extension, Browser *browser, +void ExtensionInstalledBubble::Show(const Extension* extension, + Browser *browser, SkBitmap icon) { new ExtensionInstalledBubble(extension, browser, icon); } -ExtensionInstalledBubble::ExtensionInstalledBubble(Extension *extension, +ExtensionInstalledBubble::ExtensionInstalledBubble(const Extension* extension, Browser *browser, SkBitmap icon) : extension_(extension), @@ -272,20 +273,26 @@ ExtensionInstalledBubble::ExtensionInstalledBubble(Extension *extension, // be sure that a BrowserAction or PageAction has had views created which we // can inspect for the purpose of previewing of pointing to them. registrar_.Add(this, NotificationType::EXTENSION_LOADED, - NotificationService::AllSources()); + Source<Profile>(browser->profile())); + registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, + Source<Profile>(browser->profile())); } void ExtensionInstalledBubble::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { if (type == NotificationType::EXTENSION_LOADED) { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (extension == extension_) { animation_wait_retries_ = 0; // PostTask to ourself to allow all EXTENSION_LOADED Observers to run. MessageLoopForUI::current()->PostTask(FROM_HERE, NewRunnableMethod(this, &ExtensionInstalledBubble::ShowInternal)); } + } else if (type == NotificationType::EXTENSION_UNLOADED) { + const Extension* extension = Details<const Extension>(details).ptr(); + if (extension == extension_) + extension_ = NULL; } else { NOTREACHED() << L"Received unexpected notification"; } @@ -354,25 +361,27 @@ void ExtensionInstalledBubble::ShowInternal() { // InfoBubbleDelegate void ExtensionInstalledBubble::InfoBubbleClosing(InfoBubble* info_bubble, bool closed_by_escape) { - if (type_ == PAGE_ACTION) { - BrowserView* browser_view = BrowserView::GetBrowserViewForNativeWindow( - browser_->window()->GetNativeHandle()); - browser_view->GetLocationBarView()->SetPreviewEnabledPageAction( - extension_->page_action(), - false); // preview_enabled - } else if (type_ == EXTENSION_APP) { - if (bubble_content_->create_shortcut()) { - ShellIntegration::ShortcutInfo shortcut_info; - shortcut_info.url = extension_->GetFullLaunchURL(); - shortcut_info.extension_id = UTF8ToUTF16(extension_->id()); - shortcut_info.title = UTF8ToUTF16(extension_->name()); - shortcut_info.description = UTF8ToUTF16(extension_->description()); - shortcut_info.favicon = icon_; - shortcut_info.create_on_desktop = true; - shortcut_info.create_in_applications_menu = false; - shortcut_info.create_in_quick_launch_bar = false; - web_app::CreateShortcut(browser_->profile()->GetPath(), shortcut_info, - NULL); + if (extension_) { + if (type_ == PAGE_ACTION) { + BrowserView* browser_view = BrowserView::GetBrowserViewForNativeWindow( + browser_->window()->GetNativeHandle()); + browser_view->GetLocationBarView()->SetPreviewEnabledPageAction( + extension_->page_action(), + false); // preview_enabled + } else if (type_ == EXTENSION_APP) { + if (bubble_content_->create_shortcut()) { + ShellIntegration::ShortcutInfo shortcut_info; + shortcut_info.url = extension_->GetFullLaunchURL(); + shortcut_info.extension_id = UTF8ToUTF16(extension_->id()); + shortcut_info.title = UTF8ToUTF16(extension_->name()); + shortcut_info.description = UTF8ToUTF16(extension_->description()); + shortcut_info.favicon = icon_; + shortcut_info.create_on_desktop = true; + shortcut_info.create_in_applications_menu = false; + shortcut_info.create_in_quick_launch_bar = false; + web_app::CreateShortcut(browser_->profile()->GetPath(), shortcut_info, + NULL); + } } } diff --git a/chrome/browser/views/extensions/extension_installed_bubble.h b/chrome/browser/views/extensions/extension_installed_bubble.h index 81c12a6..a35f820 100644 --- a/chrome/browser/views/extensions/extension_installed_bubble.h +++ b/chrome/browser/views/extensions/extension_installed_bubble.h @@ -44,13 +44,13 @@ class ExtensionInstalledBubble // the extension has loaded. |extension| is the installed extension. |browser| // is the browser window which will host the bubble. |icon| is the install // icon of the extension. - static void Show(Extension *extension, Browser *browser, SkBitmap icon); + static void Show(const Extension* extension, Browser *browser, SkBitmap icon); private: friend class base::RefCountedThreadSafe<ExtensionInstalledBubble>; // Private ctor. Registers a listener for EXTENSION_LOADED. - ExtensionInstalledBubble(Extension *extension, Browser *browser, + ExtensionInstalledBubble(const Extension* extension, Browser *browser, SkBitmap icon); ~ExtensionInstalledBubble() {} @@ -69,7 +69,7 @@ class ExtensionInstalledBubble virtual bool CloseOnEscape() { return true; } virtual bool FadeInOnShow() { return true; } - Extension* extension_; + const Extension* extension_; Browser* browser_; SkBitmap icon_; NotificationRegistrar registrar_; diff --git a/chrome/browser/views/extensions/extension_view.cc b/chrome/browser/views/extensions/extension_view.cc index 4ba38ca..fcace1a 100644 --- a/chrome/browser/views/extensions/extension_view.cc +++ b/chrome/browser/views/extensions/extension_view.cc @@ -39,7 +39,7 @@ ExtensionView::~ExtensionView() { CleanUp(); } -Extension* ExtensionView::extension() const { +const Extension* ExtensionView::extension() const { return host_->extension(); } diff --git a/chrome/browser/views/extensions/extension_view.h b/chrome/browser/views/extensions/extension_view.h index 6eded74..037c2e8 100644 --- a/chrome/browser/views/extensions/extension_view.h +++ b/chrome/browser/views/extensions/extension_view.h @@ -36,7 +36,7 @@ class ExtensionView : public views::NativeViewHost { ExtensionHost* host() const { return host_; } Browser* browser() const { return browser_; } - Extension* extension() const; + const Extension* extension() const; RenderViewHost* render_view_host() const; void DidStopLoading(); void SetIsClipped(bool is_clipped); diff --git a/chrome/browser/views/external_protocol_dialog.cc b/chrome/browser/views/external_protocol_dialog.cc index 3022c28..5b6d975 100644 --- a/chrome/browser/views/external_protocol_dialog.cc +++ b/chrome/browser/views/external_protocol_dialog.cc @@ -9,6 +9,7 @@ #include "base/metrics/histogram.h" #include "base/string_util.h" #include "base/thread.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "base/win/registry.h" #include "chrome/browser/browser_process.h" @@ -157,6 +158,10 @@ ExternalProtocolDialog::ExternalProtocolDialog(TabContents* tab_contents, // static std::wstring ExternalProtocolDialog::GetApplicationForProtocol( const GURL& url) { + // We shouldn't be accessing the registry from the UI thread, since it can go + // to disk. http://crbug.com/61996 + base::ThreadRestrictions::ScopedAllowIO allow_io; + std::wstring url_spec = ASCIIToWide(url.possibly_invalid_spec()); std::wstring cmd_key_path = ASCIIToWide(url.scheme() + "\\shell\\open\\command"); diff --git a/chrome/browser/views/find_bar_host_interactive_uitest.cc b/chrome/browser/views/find_bar_host_interactive_uitest.cc index 9c2750e..12537fb 100644 --- a/chrome/browser/views/find_bar_host_interactive_uitest.cc +++ b/chrome/browser/views/find_bar_host_interactive_uitest.cc @@ -50,9 +50,7 @@ IN_PROC_BROWSER_TEST_F(FindInPageTest, CrashEscHandlers) { browser()->Find(); // Open another tab (tab B). - Browser::AddTabWithURLParams params(url, PageTransition::TYPED); - browser()->AddTabWithURL(¶ms); - EXPECT_EQ(browser(), params.target); + browser()->AddSelectedTabWithURL(url, PageTransition::TYPED); browser()->Find(); EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), diff --git a/chrome/browser/views/find_bar_host_uitest.cc b/chrome/browser/views/find_bar_host_uitest.cc index df4ea0e..8dd6dbb 100644 --- a/chrome/browser/views/find_bar_host_uitest.cc +++ b/chrome/browser/views/find_bar_host_uitest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/test/automation/browser_proxy.h" #include "chrome/test/automation/tab_proxy.h" #include "chrome/test/ui/ui_test.h" diff --git a/chrome/browser/views/first_run_search_engine_view.cc b/chrome/browser/views/first_run_search_engine_view.cc index e4ba4a6..6688d9e 100644 --- a/chrome/browser/views/first_run_search_engine_view.cc +++ b/chrome/browser/views/first_run_search_engine_view.cc @@ -15,9 +15,9 @@ #include "base/time.h" #include "chrome/browser/options_window.h" #include "chrome/browser/profile.h" +#include "chrome/browser/search_engines/search_engine_type.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" -#include "chrome/browser/search_engines/template_url_prepopulate_data.h" #include "gfx/canvas.h" #include "gfx/font.h" #include "grit/browser_resources.h" @@ -36,7 +36,6 @@ #include "views/window/window.h" using base::Time; -using TemplateURLPrepopulateData::SearchEngineType; namespace { @@ -63,7 +62,7 @@ SearchEngineChoice::SearchEngineChoice(views::ButtonListener* listener, use_images = true; #endif int logo_id = search_engine_->logo_id(); - if (use_images && logo_id > 0) { + if (use_images && logo_id != kNoSearchEngineLogo) { is_image_label_ = true; views::ImageView* logo_image = new views::ImageView(); SkBitmap* logo_bmp = diff --git a/chrome/browser/views/frame/browser_root_view.cc b/chrome/browser/views/frame/browser_root_view.cc index d2985dd..e423603 100644 --- a/chrome/browser/views/frame/browser_root_view.cc +++ b/chrome/browser/views/frame/browser_root_view.cc @@ -9,6 +9,7 @@ #include "app/os_exchange_data.h" #include "chrome/browser/autocomplete/autocomplete.h" #include "chrome/browser/autocomplete/autocomplete_classifier.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/location_bar.h" #include "chrome/browser/profile.h" #include "chrome/browser/views/frame/browser_view.h" diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc index b09242b..949b73b 100644 --- a/chrome/browser/views/frame/browser_view.cc +++ b/chrome/browser/views/frame/browser_view.cc @@ -14,6 +14,7 @@ #include "base/i18n/rtl.h" #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/app_modal_dialog_queue.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" @@ -555,12 +556,6 @@ gfx::Rect BrowserView::GetClientAreaBounds() const { return container_bounds; } -bool BrowserView::ShouldFindBarBlendWithBookmarksBar() const { - if (bookmark_bar_view_.get()) - return bookmark_bar_view_->IsAlwaysShown(); - return false; -} - gfx::Rect BrowserView::GetFindBarBoundingBox() const { return GetBrowserViewLayout()->GetFindBarBoundingBox(); } @@ -1068,9 +1063,7 @@ views::Window* BrowserView::ShowAboutChromeDialog() { } void BrowserView::ShowUpdateChromeDialog() { -#if defined(OS_WIN) UpdateRecommendedMessageBox::ShowMessageBox(GetWindow()->GetNativeWindow()); -#endif } void BrowserView::ShowTaskManager() { @@ -1851,7 +1844,7 @@ void BrowserView::Init() { // Stow a pointer to this object onto the window handle so that we can get // at it later when all we have is a native view. #if defined(OS_WIN) - SetProp(GetWidget()->GetNativeView(), kBrowserViewKey, this); + GetWidget()->SetNativeWindowProperty(kBrowserViewKey, this); #else g_object_set_data(G_OBJECT(GetWidget()->GetNativeView()), kBrowserViewKey, this); diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h index 32272ab..d113525 100644 --- a/chrome/browser/views/frame/browser_view.h +++ b/chrome/browser/views/frame/browser_view.h @@ -128,12 +128,6 @@ class BrowserView : public BrowserBubbleHost, // BrowserView's parent. gfx::Rect GetClientAreaBounds() const; - // Returns true if the Find Bar should be rendered such that it appears to - // blend with the Bookmarks Bar. False if it should appear to blend with the - // main Toolbar. The return value will vary depending on whether or not the - // Bookmark Bar is always shown. - bool ShouldFindBarBlendWithBookmarksBar() const; - // Returns the constraining bounding box that should be used to lay out the // FindBar within. This is _not_ the size of the find bar, just the bounding // box it should be laid out within. The coordinate system of the returned diff --git a/chrome/browser/views/frame/glass_browser_frame_view.cc b/chrome/browser/views/frame/glass_browser_frame_view.cc index b532273..fdc1956 100644 --- a/chrome/browser/views/frame/glass_browser_frame_view.cc +++ b/chrome/browser/views/frame/glass_browser_frame_view.cc @@ -6,6 +6,7 @@ #include "app/resource_bundle.h" #include "app/theme_provider.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/themes/browser_theme_provider.h" #include "chrome/browser/views/frame/browser_view.h" diff --git a/chrome/browser/views/frame/opaque_browser_frame_view.cc b/chrome/browser/views/frame/opaque_browser_frame_view.cc index c3c1a0a..94d0ffa 100644 --- a/chrome/browser/views/frame/opaque_browser_frame_view.cc +++ b/chrome/browser/views/frame/opaque_browser_frame_view.cc @@ -919,7 +919,14 @@ void OpaqueBrowserFrameView::LayoutWindowControls() { close_button_size.height()); #if defined(OS_CHROMEOS) - if (is_maximized) { + // LayoutWindowControls could be triggered from WindowGtk::UpdateWindowTitle, + // which could happen when user navigates in fullscreen mode. And because + // BrowserFrameChromeos::IsMaximized return false for fullscreen mode, we + // explicitly test fullscreen mode here and make it use the same code path + // as maximized mode. + // TODO(oshima): Optimize the relayout logic to defer the frame view's + // relayout until it is necessary, i.e when it becomes visible. + if (is_maximized || frame_->GetWindow()->IsFullscreen()) { minimize_button_->SetVisible(false); restore_button_->SetVisible(false); maximize_button_->SetVisible(false); diff --git a/chrome/browser/views/fullscreen_exit_bubble.cc b/chrome/browser/views/fullscreen_exit_bubble.cc index 4777f51..635e9d2 100644 --- a/chrome/browser/views/fullscreen_exit_bubble.cc +++ b/chrome/browser/views/fullscreen_exit_bubble.cc @@ -7,7 +7,7 @@ #include "app/keyboard_codes.h" #include "app/l10n_util.h" #include "app/resource_bundle.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "gfx/canvas_skia.h" #include "grit/generated_resources.h" #include "views/screen.h" diff --git a/chrome/browser/views/generic_info_view_unittest.cc b/chrome/browser/views/generic_info_view_unittest.cc index 2fa0e9d..6c26603 100644 --- a/chrome/browser/views/generic_info_view_unittest.cc +++ b/chrome/browser/views/generic_info_view_unittest.cc @@ -11,10 +11,11 @@ #include "views/controls/label.h" #include "views/controls/textfield/textfield.h" #include "views/widget/root_view.h" +#include "views/window/window.h" + #if defined(OS_WIN) #include "views/widget/widget_win.h" #endif -#include "views/window/window.h" // This class is only used on windows for now. #if defined(OS_WIN) @@ -59,5 +60,6 @@ TEST_F(GenericInfoViewTest, GenericInfoView) { string16 product_desc = l10n_util::GetString(IDS_PRODUCT_DESCRIPTION); EXPECT_EQ(product_name, view2->name_views_[0]->GetText()); EXPECT_EQ(product_desc, view2->name_views_[1]->GetText()); + window->CloseNow(); } #endif // OS_WIN diff --git a/chrome/browser/views/html_dialog_view.cc b/chrome/browser/views/html_dialog_view.cc index 7d12d78..f28e609 100644 --- a/chrome/browser/views/html_dialog_view.cc +++ b/chrome/browser/views/html_dialog_view.cc @@ -98,6 +98,10 @@ views::View* HtmlDialogView::GetInitiallyFocusedView() { return this; } +bool HtmlDialogView::ShouldShowWindowTitle() const { + return ShouldShowDialogTitle(); +} + //////////////////////////////////////////////////////////////////////////////// // HtmlDialogUIDelegate implementation: @@ -150,6 +154,13 @@ void HtmlDialogView::OnCloseContents(TabContents* source, delegate_->OnCloseContents(source, out_close_dialog); } +bool HtmlDialogView::ShouldShowDialogTitle() const { + if (delegate_) + return delegate_->ShouldShowDialogTitle(); + else + return true; +} + //////////////////////////////////////////////////////////////////////////////// // TabContentsDelegate implementation: diff --git a/chrome/browser/views/html_dialog_view.h b/chrome/browser/views/html_dialog_view.h index 9c05291..1d42b6f 100644 --- a/chrome/browser/views/html_dialog_view.h +++ b/chrome/browser/views/html_dialog_view.h @@ -55,6 +55,7 @@ class HtmlDialogView virtual void WindowClosing(); virtual views::View* GetContentsView(); virtual views::View* GetInitiallyFocusedView(); + virtual bool ShouldShowWindowTitle() const; // Overridden from HtmlDialogUIDelegate: virtual bool IsDialogModal() const; @@ -66,6 +67,7 @@ class HtmlDialogView virtual std::string GetDialogArgs() const; virtual void OnDialogClosed(const std::string& json_retval); virtual void OnCloseContents(TabContents* source, bool* out_close_dialog); + virtual bool ShouldShowDialogTitle() const; // Overridden from TabContentsDelegate: virtual void MoveContents(TabContents* source, const gfx::Rect& pos); diff --git a/chrome/browser/views/html_dialog_view_browsertest.cc b/chrome/browser/views/html_dialog_view_browsertest.cc index bbcc6a0..a2bcd35 100644 --- a/chrome/browser/views/html_dialog_view_browsertest.cc +++ b/chrome/browser/views/html_dialog_view_browsertest.cc @@ -58,6 +58,7 @@ class TestHtmlDialogUIDelegate : public HtmlDialogUIDelegate { if (out_close_dialog) *out_close_dialog = true; } + virtual bool ShouldShowDialogTitle() const { return true; } }; } // namespace diff --git a/chrome/browser/views/importing_progress_view.cc b/chrome/browser/views/importing_progress_view.cc index f3c2627..ae3135e 100644 --- a/chrome/browser/views/importing_progress_view.cc +++ b/chrome/browser/views/importing_progress_view.cc @@ -290,7 +290,7 @@ void ImportingProgressView::InitControlLayout() { // StartImportingWithUI void StartImportingWithUI(HWND parent_window, - int16 items, + uint16 items, ImporterHost* coordinator, const ProfileInfo& source_profile, Profile* target_profile, diff --git a/chrome/browser/views/indexed_db_info_view.cc b/chrome/browser/views/indexed_db_info_view.cc index 1b0b28c..733ef6e 100644 --- a/chrome/browser/views/indexed_db_info_view.cc +++ b/chrome/browser/views/indexed_db_info_view.cc @@ -23,8 +23,7 @@ static const int kIndexedDBInfoViewInsetSize = 3; // IndexedDBInfoView, public: IndexedDBInfoView::IndexedDBInfoView() - : name_value_field_(NULL), - origin_value_field_(NULL), + : origin_value_field_(NULL), size_value_field_(NULL), last_modified_value_field_(NULL) { } @@ -34,10 +33,6 @@ IndexedDBInfoView::~IndexedDBInfoView() { void IndexedDBInfoView::SetIndexedDBInfo( const BrowsingDataIndexedDBHelper::IndexedDBInfo& indexed_db_info) { - name_value_field_->SetText( - indexed_db_info.database_name.empty() ? - l10n_util::GetString(IDS_COOKIES_WEB_DATABASE_UNNAMED_NAME) : - UTF8ToWide(indexed_db_info.database_name)); origin_value_field_->SetText(UTF8ToWide(indexed_db_info.origin)); size_value_field_->SetText( FormatBytes(indexed_db_info.size, @@ -49,7 +44,6 @@ void IndexedDBInfoView::SetIndexedDBInfo( } void IndexedDBInfoView::EnableIndexedDBDisplay(bool enabled) { - name_value_field_->SetEnabled(enabled); origin_value_field_->SetEnabled(enabled); size_value_field_->SetEnabled(enabled); last_modified_value_field_->SetEnabled(enabled); @@ -58,7 +52,6 @@ void IndexedDBInfoView::EnableIndexedDBDisplay(bool enabled) { void IndexedDBInfoView::ClearIndexedDBDisplay() { std::wstring no_cookie_string = l10n_util::GetString(IDS_COOKIES_COOKIE_NONESELECTED); - name_value_field_->SetText(no_cookie_string); origin_value_field_->SetText(no_cookie_string); size_value_field_->SetText(no_cookie_string); last_modified_value_field_->SetText(no_cookie_string); @@ -84,9 +77,6 @@ void IndexedDBInfoView::Init() { kIndexedDBInfoViewBorderSize, border_color); set_border(border); - views::Label* name_label = new views::Label( - l10n_util::GetString(IDS_COOKIES_COOKIE_NAME_LABEL)); - name_value_field_ = new views::Textfield; views::Label* origin_label = new views::Label( l10n_util::GetString(IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL)); origin_value_field_ = new views::Textfield; @@ -115,10 +105,6 @@ void IndexedDBInfoView::Init() { GridLayout::USE_PREF, 0, 0); layout->StartRow(0, three_column_layout_id); - layout->AddView(name_label); - layout->AddView(name_value_field_); - layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); - layout->StartRow(0, three_column_layout_id); layout->AddView(origin_label); layout->AddView(origin_value_field_); layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); @@ -133,9 +119,6 @@ void IndexedDBInfoView::Init() { // Color these borderless text areas the same as the containing dialog. SkColor text_area_background = color_utils::GetSysSkColor(COLOR_3DFACE); // Now that the Textfields are in the view hierarchy, we can initialize them. - name_value_field_->SetReadOnly(true); - name_value_field_->RemoveBorder(); - name_value_field_->SetBackgroundColor(text_area_background); origin_value_field_->SetReadOnly(true); origin_value_field_->RemoveBorder(); origin_value_field_->SetBackgroundColor(text_area_background); diff --git a/chrome/browser/views/indexed_db_info_view.h b/chrome/browser/views/indexed_db_info_view.h index 20ba89b..4be00a5 100644 --- a/chrome/browser/views/indexed_db_info_view.h +++ b/chrome/browser/views/indexed_db_info_view.h @@ -45,7 +45,6 @@ class IndexedDBInfoView : public views::View { void Init(); // Individual property labels - views::Textfield* name_value_field_; views::Textfield* origin_value_field_; views::Textfield* size_value_field_; views::Textfield* last_modified_value_field_; diff --git a/chrome/browser/views/infobars/extension_infobar.cc b/chrome/browser/views/infobars/extension_infobar.cc index 441a95e..e2fb43f 100644 --- a/chrome/browser/views/infobars/extension_infobar.cc +++ b/chrome/browser/views/infobars/extension_infobar.cc @@ -164,7 +164,7 @@ void ExtensionInfoBar::SetupIconAndMenu() { menu_->SetVisible(false); AddChildView(menu_); - Extension* extension = delegate_->extension_host()->extension(); + const Extension* extension = delegate_->extension_host()->extension(); ExtensionResource icon_resource = extension->GetIconResource( Extension::EXTENSION_ICON_BITTY, ExtensionIconSet::MATCH_EXACTLY); if (!icon_resource.relative_path().empty()) { diff --git a/chrome/browser/views/location_bar/content_setting_image_view.cc b/chrome/browser/views/location_bar/content_setting_image_view.cc index baeed23..2d5af65 100644 --- a/chrome/browser/views/location_bar/content_setting_image_view.cc +++ b/chrome/browser/views/location_bar/content_setting_image_view.cc @@ -70,9 +70,8 @@ void ContentSettingImageView::OnMouseReleased(const views::MouseEvent& event, tab_contents, profile_, content_setting_image_model_->get_content_settings_type()), profile_, tab_contents); - info_bubble_ = - InfoBubble::Show(GetWidget(), screen_bounds, BubbleBorder::TOP_LEFT, - bubble_contents, this); + info_bubble_ = InfoBubble::Show(GetWidget(), screen_bounds, + BubbleBorder::TOP_RIGHT, bubble_contents, this); bubble_contents->set_info_bubble(info_bubble_); } diff --git a/chrome/browser/views/location_bar/keyword_hint_view.cc b/chrome/browser/views/location_bar/keyword_hint_view.cc index 14974c8..eec0dd1 100644 --- a/chrome/browser/views/location_bar/keyword_hint_view.cc +++ b/chrome/browser/views/location_bar/keyword_hint_view.cc @@ -7,7 +7,7 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/logging.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/profile.h" #include "chrome/browser/search_engines/template_url_model.h" #include "gfx/canvas.h" diff --git a/chrome/browser/views/location_bar/location_bar_view.cc b/chrome/browser/views/location_bar/location_bar_view.cc index 8d43f78..09c18a9 100644 --- a/chrome/browser/views/location_bar/location_bar_view.cc +++ b/chrome/browser/views/location_bar/location_bar_view.cc @@ -14,7 +14,7 @@ #include "app/theme_provider.h" #include "base/stl_util-inl.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/alternate_nav_url_fetcher.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/defaults.h" @@ -736,25 +736,8 @@ void LocationBarView::OnAutocompleteLosingFocus( SetSuggestedText(string16()); InstantController* instant = delegate_->GetInstant(); - if (!instant) - return; - - if (!instant->is_active() || !instant->GetPreviewContents()) - return; - - switch (GetCommitType(view_gaining_focus)) { - case COMMIT_INSTANT_IMMEDIATELY: - instant->CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST); - break; - case COMMIT_INSTANT_ON_MOUSE_UP: - instant->SetCommitOnMouseUp(); - break; - case REVERT_INSTANT: - instant->DestroyPreviewContents(); - break; - default: - NOTREACHED(); - } + if (instant) + instant->OnAutocompleteLostFocus(view_gaining_focus); } void LocationBarView::OnAutocompleteWillAccept() { @@ -774,6 +757,10 @@ bool LocationBarView::OnCommitSuggestedText(const std::wstring& typed_text) { return true; } +void LocationBarView::OnSetSuggestedSearchText(const string16& suggested_text) { + SetSuggestedText(suggested_text); +} + void LocationBarView::OnPopupBoundsChanged(const gfx::Rect& bounds) { InstantController* instant = delegate_->GetInstant(); if (instant) @@ -1206,45 +1193,3 @@ void LocationBarView::OnTemplateURLModelChanged() { template_url_model_ = NULL; ShowFirstRunBubble(bubble_type_); } - -LocationBarView::InstantCommitType LocationBarView::GetCommitType( - gfx::NativeView view_gaining_focus) { - // The InstantController is active. Destroy it if the user didn't click on the - // TabContents (or one of its children). -#if defined(OS_WIN) - InstantController* instant = delegate_->GetInstant(); - RenderWidgetHostView* rwhv = - instant->GetPreviewContents()->GetRenderWidgetHostView(); - if (!view_gaining_focus || !rwhv) - return REVERT_INSTANT; - - gfx::NativeView tab_view = instant->GetPreviewContents()->GetNativeView(); - if (rwhv->GetNativeView() == view_gaining_focus || - tab_view == view_gaining_focus) { - // Focus is going to the renderer. Only commit instant if the mouse is - // down. If the mouse isn't down it means someone else moved focus and we - // shouldn't commit. - if (instant->IsMouseDownFromActivate()) { - if (instant->IsShowingInstant()) { - // We're showing instant results. As instant results may shift when - // committing we commit on the mouse up. This way a slow click still - // works fine. - return COMMIT_INSTANT_ON_MOUSE_UP; - } - return COMMIT_INSTANT_IMMEDIATELY; - } - return REVERT_INSTANT; - } - gfx::NativeView view_gaining_focus_ancestor = view_gaining_focus; - while (view_gaining_focus_ancestor && - view_gaining_focus_ancestor != tab_view) { - view_gaining_focus_ancestor = ::GetParent(view_gaining_focus_ancestor); - } - return view_gaining_focus_ancestor != NULL ? - COMMIT_INSTANT_IMMEDIATELY : REVERT_INSTANT; -#else - // TODO: implement me. - NOTIMPLEMENTED(); - return REVERT_INSTANT; -#endif -} diff --git a/chrome/browser/views/location_bar/location_bar_view.h b/chrome/browser/views/location_bar/location_bar_view.h index 2895450..019e7a1 100644 --- a/chrome/browser/views/location_bar/location_bar_view.h +++ b/chrome/browser/views/location_bar/location_bar_view.h @@ -183,6 +183,7 @@ class LocationBarView : public LocationBar, virtual void OnAutocompleteLosingFocus(gfx::NativeView view_gaining_focus); virtual void OnAutocompleteWillAccept(); virtual bool OnCommitSuggestedText(const std::wstring& typed_text); + virtual void OnSetSuggestedSearchText(const string16& suggested_text); virtual void OnPopupBoundsChanged(const gfx::Rect& bounds); virtual void OnAutocompleteAccept(const GURL& url, WindowOpenDisposition disposition, @@ -261,18 +262,6 @@ class LocationBarView : public LocationBar, private: typedef std::vector<ContentSettingImageView*> ContentSettingViews; - // Enumeration of what should happen to instant on focus lost. - enum InstantCommitType { - // The instant preview should be committed immediately. - COMMIT_INSTANT_IMMEDIATELY, - - // The instant preview should be committed on mouse up. - COMMIT_INSTANT_ON_MOUSE_UP, - - // The instant preview should be reverted. - REVERT_INSTANT - }; - friend class PageActionImageView; friend class PageActionWithBadgeView; typedef std::vector<PageActionWithBadgeView*> PageActionViews; @@ -315,10 +304,6 @@ class LocationBarView : public LocationBar, // Helper to show the first run info bubble. void ShowFirstRunBubbleInternal(FirstRun::BubbleType bubble_type); - // Returns what should happen to the InstantController as a result of focus - // being lost. - InstantCommitType GetCommitType(gfx::NativeView view_gaining_focus); - // Current profile. Not owned by us. Profile* profile_; diff --git a/chrome/browser/views/location_bar/page_action_image_view.cc b/chrome/browser/views/location_bar/page_action_image_view.cc index aa739c0..c7155bf 100644 --- a/chrome/browser/views/location_bar/page_action_image_view.cc +++ b/chrome/browser/views/location_bar/page_action_image_view.cc @@ -26,8 +26,8 @@ PageActionImageView::PageActionImageView(LocationBarView* owner, current_tab_id_(-1), preview_enabled_(false), popup_(NULL) { - Extension* extension = profile->GetExtensionsService()->GetExtensionById( - page_action->extension_id(), false); + const Extension* extension = profile->GetExtensionsService()-> + GetExtensionById(page_action->extension_id(), false); DCHECK(extension); // Load all the icons declared in the manifest. This is the contents of the @@ -146,8 +146,8 @@ bool PageActionImageView::OnKeyPressed(const views::KeyEvent& e) { void PageActionImageView::ShowContextMenu(const gfx::Point& p, bool is_mouse_gesture) { - Extension* extension = profile_->GetExtensionsService()->GetExtensionById( - page_action()->extension_id(), false); + const Extension* extension = profile_->GetExtensionsService()-> + GetExtensionById(page_action()->extension_id(), false); Browser* browser = BrowserView::GetBrowserViewForNativeWindow( platform_util::GetTopLevel(GetWidget()->GetNativeView()))->browser(); context_menu_contents_ = diff --git a/chrome/browser/views/location_bar/star_view.cc b/chrome/browser/views/location_bar/star_view.cc index 88360ec..7d22441 100644 --- a/chrome/browser/views/location_bar/star_view.cc +++ b/chrome/browser/views/location_bar/star_view.cc @@ -6,7 +6,7 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/command_updater.h" #include "chrome/browser/view_ids.h" #include "chrome/browser/views/browser_dialogs.h" diff --git a/chrome/browser/views/options/advanced_contents_view.cc b/chrome/browser/views/options/advanced_contents_view.cc index ba3f426..b8513b6 100644 --- a/chrome/browser/views/options/advanced_contents_view.cc +++ b/chrome/browser/views/options/advanced_contents_view.cc @@ -1674,7 +1674,8 @@ void AdvancedContentsView::InitControlLayout() { layout->StartRow(0, single_column_view_set_id); layout->AddView(new SecuritySection(profile())); if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableCloudPrintProxy)) { + switches::kEnableCloudPrintProxy) && + profile()->GetCloudPrintProxyService()) { layout->StartRow(0, single_column_view_set_id); layout->AddView(new CloudPrintProxySection(profile())); } diff --git a/chrome/browser/views/options/fonts_languages_window_view.cc b/chrome/browser/views/options/fonts_languages_window_view.cc index ff6a998..cab82c7 100644 --- a/chrome/browser/views/options/fonts_languages_window_view.cc +++ b/chrome/browser/views/options/fonts_languages_window_view.cc @@ -49,8 +49,7 @@ bool FontsLanguagesWindowView::Accept() { // FontsLanguagesWindowView, views::WindowDelegate implementation: std::wstring FontsLanguagesWindowView::GetWindowTitle() const { - return l10n_util::GetStringF(IDS_FONT_LANGUAGE_SETTING_WINDOWS_TITLE, - l10n_util::GetString(IDS_PRODUCT_NAME)); + return l10n_util::GetString(IDS_FONT_LANGUAGE_SETTING_WINDOWS_TITLE); } void FontsLanguagesWindowView::WindowClosing() { diff --git a/chrome/browser/views/page_info_bubble_view.cc b/chrome/browser/views/page_info_bubble_view.cc index 0aa1d71..90fa667 100644 --- a/chrome/browser/views/page_info_bubble_view.cc +++ b/chrome/browser/views/page_info_bubble_view.cc @@ -140,8 +140,6 @@ void PageInfoBubbleView::LayoutSections() { for (int i = 0; i < count; ++i) { PageInfoModel::SectionInfo info = model_.GetSectionInfo(i); layout->StartRow(0, 0); - // TODO(finnur): Remove title from the info struct, since it is - // not used anymore. const SkBitmap* icon = model_.GetIconImage(info.icon_id); layout->AddView(new Section(this, info, icon, cert_id_ > 0)); diff --git a/chrome/browser/views/reload_button.cc b/chrome/browser/views/reload_button.cc index 71dc5b1..43015e7 100644 --- a/chrome/browser/views/reload_button.cc +++ b/chrome/browser/views/reload_button.cc @@ -5,7 +5,7 @@ #include "chrome/browser/views/reload_button.h" #include "app/l10n_util.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/views/event_utils.h" #include "chrome/browser/views/location_bar/location_bar_view.h" @@ -19,8 +19,12 @@ ReloadButton::ReloadButton(LocationBarView* location_bar, Browser* browser) location_bar_(location_bar), browser_(browser), intended_mode_(MODE_RELOAD), - visible_mode_(MODE_RELOAD) { - DCHECK(location_bar_); + visible_mode_(MODE_RELOAD), + double_click_timer_delay_( + base::TimeDelta::FromMilliseconds(GetDoubleClickTimeMS())), + stop_to_reload_timer_delay_(base::TimeDelta::FromMilliseconds(1350)), + testing_mouse_hovered_(false), + testing_reload_count_(0) { } ReloadButton::~ReloadButton() { @@ -32,32 +36,43 @@ void ReloadButton::ChangeMode(Mode mode, bool force) { // If the change is forced, or the user isn't hovering the icon, or it's safe // to change it to the other image type, make the change immediately; // otherwise we'll let it happen later. - if (force || !IsMouseHovered() || ((mode == MODE_STOP) ? - !timer_.IsRunning() : (visible_mode_ != MODE_STOP))) { - timer_.Stop(); + if (force || (!IsMouseHovered() && !testing_mouse_hovered_) || + ((mode == MODE_STOP) ? + !double_click_timer_.IsRunning() : (visible_mode_ != MODE_STOP))) { + double_click_timer_.Stop(); + stop_to_reload_timer_.Stop(); SetToggled(mode == MODE_STOP); visible_mode_ = mode; SetEnabled(true); // We want to disable the button if we're preventing a change from stop to // reload due to hovering, but not if we're preventing a change from reload to - // stop due to the timer running. (There is no disabled reload state.) + // stop due to the double-click timer running. (There is no disabled reload + // state.) } else if (visible_mode_ != MODE_RELOAD) { SetEnabled(false); + + // Go ahead and change to reload after a bit, which allows repeated reloads + // without moving the mouse. + if (!stop_to_reload_timer_.IsRunning()) { + stop_to_reload_timer_.Start(stop_to_reload_timer_delay_, this, + &ReloadButton::OnStopToReloadTimer); + } } } //////////////////////////////////////////////////////////////////////////////// // ReloadButton, views::ButtonListener implementation: -void ReloadButton::ButtonPressed(views::Button* button, +void ReloadButton::ButtonPressed(views::Button* /* button */, const views::Event& event) { if (visible_mode_ == MODE_STOP) { - browser_->Stop(); + if (browser_) + browser_->Stop(); // The user has clicked, so we can feel free to update the button, // even if the mouse is still hovering. ChangeMode(MODE_RELOAD, true); - } else if (!timer_.IsRunning()) { + } else if (!double_click_timer_.IsRunning()) { // Shift-clicking or ctrl-clicking the reload button means we should ignore // any cached content. // TODO(avayvod): eliminate duplication of this logic in @@ -74,7 +89,7 @@ void ReloadButton::ButtonPressed(views::Button* button, WindowOpenDisposition disposition = event_utils::DispositionFromEventFlags(flags); - if (disposition == CURRENT_TAB) { + if ((disposition == CURRENT_TAB) && location_bar_) { // Forcibly reset the location bar, since otherwise it won't discard any // ongoing user edits, since it doesn't realize this is a user-initiated // action. @@ -86,11 +101,12 @@ void ReloadButton::ButtonPressed(views::Button* button, // here as the browser will do that when it actually starts loading (which // may happen synchronously, thus the need to do this before telling the // browser to execute the reload command). - timer_.Stop(); - timer_.Start(base::TimeDelta::FromMilliseconds(GetDoubleClickTimeMS()), - this, &ReloadButton::OnButtonTimer); + double_click_timer_.Start(double_click_timer_delay_, this, + &ReloadButton::OnDoubleClickTimer); - browser_->ExecuteCommandWithDisposition(command, disposition); + if (browser_) + browser_->ExecuteCommandWithDisposition(command, disposition); + ++testing_reload_count_; } } @@ -112,6 +128,10 @@ bool ReloadButton::GetTooltipText(const gfx::Point& p, std::wstring* tooltip) { //////////////////////////////////////////////////////////////////////////////// // ReloadButton, private: -void ReloadButton::OnButtonTimer() { +void ReloadButton::OnDoubleClickTimer() { ChangeMode(intended_mode_, false); } + +void ReloadButton::OnStopToReloadTimer() { + ChangeMode(intended_mode_, true); +} diff --git a/chrome/browser/views/reload_button.h b/chrome/browser/views/reload_button.h index 6a8c29c..0c253bf 100644 --- a/chrome/browser/views/reload_button.h +++ b/chrome/browser/views/reload_button.h @@ -7,6 +7,7 @@ #pragma once #include "base/basictypes.h" +#include "base/gtest_prod_util.h" #include "base/timer.h" #include "views/controls/button/image_button.h" @@ -37,26 +38,44 @@ class ReloadButton : public views::ToggleImageButton, void ChangeMode(Mode mode, bool force); // Overridden from views::ButtonListener: - virtual void ButtonPressed(views::Button* button, const views::Event& event); + virtual void ButtonPressed(views::Button* /* button */, + const views::Event& event); // Overridden from views::View: virtual void OnMouseExited(const views::MouseEvent& e); virtual bool GetTooltipText(const gfx::Point& p, std::wstring* tooltip); private: - void OnButtonTimer(); + friend class ReloadButtonTest; - base::OneShotTimer<ReloadButton> timer_; + void OnDoubleClickTimer(); + void OnStopToReloadTimer(); + base::OneShotTimer<ReloadButton> double_click_timer_; + base::OneShotTimer<ReloadButton> stop_to_reload_timer_; + + // These may be NULL when testing. LocationBarView* location_bar_; Browser* browser_; - // The mode we should be in + // The mode we should be in assuming no timers are running. Mode intended_mode_; - // The currently-visible mode - this may different from the intended mode + // The currently-visible mode - this may differ from the intended mode. Mode visible_mode_; + // The delay times for the timers. These are members so that tests can modify + // them. + base::TimeDelta double_click_timer_delay_; + base::TimeDelta stop_to_reload_timer_delay_; + + // TESTING ONLY + // True if we should pretend the button is hovered. + bool testing_mouse_hovered_; + // Increments when we would tell the browser to "reload", so + // test code can tell whether we did so (as there may be no |browser_|). + int testing_reload_count_; + DISALLOW_IMPLICIT_CONSTRUCTORS(ReloadButton); }; diff --git a/chrome/browser/views/reload_button_unittest.cc b/chrome/browser/views/reload_button_unittest.cc new file mode 100644 index 0000000..5f8255b --- /dev/null +++ b/chrome/browser/views/reload_button_unittest.cc @@ -0,0 +1,150 @@ +// 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 "base/message_loop.h" +#include "chrome/browser/views/reload_button.h" +#include "testing/gtest/include/gtest/gtest.h" + +class ReloadButtonTest : public testing::Test { + public: + ReloadButtonTest(); + + void CheckState(bool enabled, + ReloadButton::Mode intended_mode, + ReloadButton::Mode visible_mode, + bool double_click_timer_running, + bool stop_to_reload_timer_running); + + // These accessors eliminate the need to declare each testcase as a friend. + void set_mouse_hovered(bool hovered) { + reload_.testing_mouse_hovered_ = hovered; + } + int reload_count() { return reload_.testing_reload_count_; } + + protected: + // We need a message loop for the timers to post events. + MessageLoop loop_; + + ReloadButton reload_; +}; + +ReloadButtonTest::ReloadButtonTest() : reload_(NULL, NULL) { + // Set the timer delays to 0 so that timers will fire as soon as we tell the + // message loop to run pending tasks. + reload_.double_click_timer_delay_ = base::TimeDelta(); + reload_.stop_to_reload_timer_delay_ = base::TimeDelta(); +} + +void ReloadButtonTest::CheckState(bool enabled, + ReloadButton::Mode intended_mode, + ReloadButton::Mode visible_mode, + bool double_click_timer_running, + bool stop_to_reload_timer_running) { + EXPECT_EQ(enabled, reload_.IsEnabled()); + EXPECT_EQ(intended_mode, reload_.intended_mode_); + EXPECT_EQ(visible_mode, reload_.visible_mode_); + EXPECT_EQ(double_click_timer_running, + reload_.double_click_timer_.IsRunning()); + EXPECT_EQ(stop_to_reload_timer_running, + reload_.stop_to_reload_timer_.IsRunning()); +} + +TEST_F(ReloadButtonTest, Basic) { + // The stop/reload button starts in the "enabled reload" state with no timers + // running. + CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, false, + false); + + // Press the button. This should start the double-click timer. + views::MouseEvent e(views::Event::ET_MOUSE_PRESSED, 0, 0, 0); + reload_.ButtonPressed(&reload_, e); + CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, true, + false); + + // Now change the mode (as if the browser had started loading the page). This + // should cancel the double-click timer since the button is not hovered. + reload_.ChangeMode(ReloadButton::MODE_STOP, false); + CheckState(true, ReloadButton::MODE_STOP, ReloadButton::MODE_STOP, false, + false); + + // Press the button again. This should change back to reload. + reload_.ButtonPressed(&reload_, e); + CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, false, + false); +} + +TEST_F(ReloadButtonTest, DoubleClickTimer) { + // Start by pressing the button. + views::MouseEvent e(views::Event::ET_MOUSE_PRESSED, 0, 0, 0); + reload_.ButtonPressed(&reload_, e); + + // Try to press the button again. This should do nothing because the timer is + // running. + int original_reload_count = reload_count(); + reload_.ButtonPressed(&reload_, e); + CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, true, + false); + EXPECT_EQ(original_reload_count, reload_count()); + + // Hover the button, and change mode. The visible mode should not change, + // again because the timer is running. + set_mouse_hovered(true); + reload_.ChangeMode(ReloadButton::MODE_STOP, false); + CheckState(true, ReloadButton::MODE_STOP, ReloadButton::MODE_RELOAD, true, + false); + + // Now fire the timer. This should complete the mode change. + loop_.RunAllPending(); + CheckState(true, ReloadButton::MODE_STOP, ReloadButton::MODE_STOP, false, + false); +} + +TEST_F(ReloadButtonTest, DisableOnHover) { + // Change to stop and hover. + views::MouseEvent e(views::Event::ET_MOUSE_PRESSED, 0, 0, 0); + reload_.ButtonPressed(&reload_, e); + reload_.ChangeMode(ReloadButton::MODE_STOP, false); + set_mouse_hovered(true); + + // Now change back to reload. This should result in a disabled stop button + // due to the hover. + reload_.ChangeMode(ReloadButton::MODE_RELOAD, false); + CheckState(false, ReloadButton::MODE_RELOAD, ReloadButton::MODE_STOP, false, + true); + + // Un-hover the button, which should allow it to reset. + set_mouse_hovered(false); + views::MouseEvent e2(views::Event::ET_MOUSE_MOVED, 0, 0, 0); + reload_.OnMouseExited(e2); + CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, false, + false); +} + +TEST_F(ReloadButtonTest, ResetOnClick) { + // Change to stop and hover. + views::MouseEvent e(views::Event::ET_MOUSE_PRESSED, 0, 0, 0); + reload_.ButtonPressed(&reload_, e); + reload_.ChangeMode(ReloadButton::MODE_STOP, false); + set_mouse_hovered(true); + + // Press the button. This should change back to reload despite the hover, + // because it's a direct user action. + reload_.ButtonPressed(&reload_, e); + CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, false, + false); +} + +TEST_F(ReloadButtonTest, ResetOnTimer) { + // Change to stop, hover, and change back to reload. + views::MouseEvent e(views::Event::ET_MOUSE_PRESSED, 0, 0, 0); + reload_.ButtonPressed(&reload_, e); + reload_.ChangeMode(ReloadButton::MODE_STOP, false); + set_mouse_hovered(true); + reload_.ChangeMode(ReloadButton::MODE_RELOAD, false); + + // Now fire the stop-to-reload timer. This should reset the button. + loop_.RunAllPending(); + CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, false, + false); +} diff --git a/chrome/browser/views/select_file_dialog.cc b/chrome/browser/views/select_file_dialog.cc index 9b9c2ef..da8427c 100644 --- a/chrome/browser/views/select_file_dialog.cc +++ b/chrome/browser/views/select_file_dialog.cc @@ -115,6 +115,7 @@ class SelectFileDialogImpl : public SelectFileDialog { virtual void OnDialogClosed(const std::string& json_retval); virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { } + virtual bool ShouldShowDialogTitle() const { return true; } DISALLOW_COPY_AND_ASSIGN(FileBrowseDelegate); }; diff --git a/chrome/browser/views/shell_dialogs_win.cc b/chrome/browser/views/shell_dialogs_win.cc index d3fe7ae..bc5f92b 100644 --- a/chrome/browser/views/shell_dialogs_win.cc +++ b/chrome/browser/views/shell_dialogs_win.cc @@ -891,10 +891,11 @@ bool SelectFileDialogImpl::RunOpenMultiFileDialog( ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = owner; - wchar_t filename[MAX_PATH] = L""; + scoped_array<wchar_t> filename(new wchar_t[UNICODE_STRING_MAX_CHARS]); + filename[0] = 0; - ofn.lpstrFile = filename; - ofn.nMaxFile = MAX_PATH; + ofn.lpstrFile = filename.get(); + ofn.nMaxFile = UNICODE_STRING_MAX_CHARS; // We use OFN_NOCHANGEDIR so that the user can rename or delete the directory // without having to close Chrome first. ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER diff --git a/chrome/browser/views/status_bubble_views.cc b/chrome/browser/views/status_bubble_views.cc index 2f92daf..234b6f0 100644 --- a/chrome/browser/views/status_bubble_views.cc +++ b/chrome/browser/views/status_bubble_views.cc @@ -646,8 +646,8 @@ void StatusBubbleViews::SetURL(const GURL& url, const string16& languages) { popup_->GetBounds(&popup_bounds, true); int text_width = static_cast<int>(popup_bounds.width() - (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1); - url_text_ = WideToUTF16(gfx::ElideUrl(url, view_->Label::font(), - text_width, UTF16ToWide(languages))); + url_text_ = gfx::ElideUrl(url, view_->Label::font(), + text_width, UTF16ToWideHack(languages)); std::wstring original_url_text = UTF16ToWideHack(net::FormatUrl(url, UTF16ToUTF8(languages))); @@ -801,8 +801,8 @@ void StatusBubbleViews::ExpandBubble() { gfx::Rect popup_bounds; popup_->GetBounds(&popup_bounds, true); int max_status_bubble_width = GetMaxStatusBubbleWidth(); - url_text_ = WideToUTF16(gfx::ElideUrl(url_, view_->Label::font(), - max_status_bubble_width, UTF16ToWideHack(languages_))); + url_text_ = gfx::ElideUrl(url_, view_->Label::font(), + max_status_bubble_width, UTF16ToWideHack(languages_)); int expanded_bubble_width =std::max(GetStandardStatusBubbleWidth(), std::min(view_->Label::font().GetStringWidth(UTF16ToWide(url_text_)) + (kShadowThickness * 2) + kTextPositionX + diff --git a/chrome/browser/views/tab_contents/render_view_context_menu_views.cc b/chrome/browser/views/tab_contents/render_view_context_menu_views.cc index 8efb30b..e7ac1a6 100644 --- a/chrome/browser/views/tab_contents/render_view_context_menu_views.cc +++ b/chrome/browser/views/tab_contents/render_view_context_menu_views.cc @@ -6,7 +6,7 @@ #include "app/keyboard_codes.h" #include "base/compiler_specific.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/profile.h" #include "grit/generated_resources.h" #include "views/accelerator.h" diff --git a/chrome/browser/views/tab_contents/tab_contents_view_gtk.cc b/chrome/browser/views/tab_contents/tab_contents_view_gtk.cc index e13d441..0a31684 100644 --- a/chrome/browser/views/tab_contents/tab_contents_view_gtk.cc +++ b/chrome/browser/views/tab_contents/tab_contents_view_gtk.cc @@ -393,6 +393,16 @@ void TabContentsViewGtk::ShowContextMenu(const ContextMenuParams& params) { MessageLoop::current()->SetNestableTasksAllowed(old_state); } +void TabContentsViewGtk::ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned) { + // External popup menus are only used on Mac. + NOTREACHED(); +} + gboolean TabContentsViewGtk::OnButtonPress(GtkWidget* widget, GdkEventButton* event) { last_mouse_down_ = *event; @@ -426,10 +436,13 @@ gboolean TabContentsViewGtk::OnPaint(GtkWidget* widget, GdkEventExpose* event) { // we need to pass on the message to paint the page gfx::Rect bounds; GetBounds(&bounds, true); - views::View *view = reinterpret_cast<RenderWidgetHostViewViews *>(tab_contents()->render_view_host()->view()); - view->SetBounds(gfx::Rect(0, 0, bounds.width(), bounds.height())); - gfx::CanvasSkiaPaint canvas(event); - view->ProcessPaint(&canvas); + views::View *view = reinterpret_cast<RenderWidgetHostViewViews *> + (tab_contents()->render_view_host()->view()); + if (view) { + view->SetBounds(gfx::Rect(0, 0, bounds.width(), bounds.height())); + gfx::CanvasSkiaPaint canvas(event); + view->ProcessPaint(&canvas); + } #endif } return false; // False indicates other widgets should get the event as well. @@ -485,7 +498,7 @@ void TabContentsViewGtk::SetFloatingPosition(const gfx::Size& size) { gtk_widget_size_request(widget, &requisition); int child_x = std::max(half_view_width - (requisition.width / 2), 0); - PositionChild(widget, child_x, 0, requisition.width, requisition.height); + PositionChild(widget, child_x, 0, 0, 0); } } diff --git a/chrome/browser/views/tab_contents/tab_contents_view_gtk.h b/chrome/browser/views/tab_contents/tab_contents_view_gtk.h index a46c4a3..890c6b1 100644 --- a/chrome/browser/views/tab_contents/tab_contents_view_gtk.h +++ b/chrome/browser/views/tab_contents/tab_contents_view_gtk.h @@ -65,6 +65,12 @@ class TabContentsViewGtk : public TabContentsView, // Backend implementation of RenderViewHostDelegate::View. virtual void ShowContextMenu(const ContextMenuParams& params); + virtual void ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned); virtual void StartDragging(const WebDropData& drop_data, WebKit::WebDragOperationsMask ops_allowed, const SkBitmap& image, diff --git a/chrome/browser/views/tab_contents/tab_contents_view_win.cc b/chrome/browser/views/tab_contents/tab_contents_view_win.cc index 91e2036..f3126d1 100644 --- a/chrome/browser/views/tab_contents/tab_contents_view_win.cc +++ b/chrome/browser/views/tab_contents/tab_contents_view_win.cc @@ -145,6 +145,8 @@ void TabContentsViewWin::OnDestroy() { RevokeDragDrop(GetNativeView()); drop_target_ = NULL; } + + WidgetWin::OnDestroy(); } void TabContentsViewWin::SetPageTitle(const std::wstring& title) { @@ -343,6 +345,16 @@ void TabContentsViewWin::ShowContextMenu(const ContextMenuParams& params) { MessageLoop::current()->SetNestableTasksAllowed(old_state); } +void TabContentsViewWin::ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned) { + // External popup menus are only used on Mac. + NOTREACHED(); +} + void TabContentsViewWin::OnHScroll(int scroll_type, short position, HWND scrollbar) { ScrollCommon(WM_HSCROLL, scroll_type, position, scrollbar); diff --git a/chrome/browser/views/tab_contents/tab_contents_view_win.h b/chrome/browser/views/tab_contents/tab_contents_view_win.h index 471869c..a42ffd7 100644 --- a/chrome/browser/views/tab_contents/tab_contents_view_win.h +++ b/chrome/browser/views/tab_contents/tab_contents_view_win.h @@ -59,6 +59,12 @@ class TabContentsViewWin : public TabContentsView, // Backend implementation of RenderViewHostDelegate::View. virtual void ShowContextMenu(const ContextMenuParams& params); + virtual void ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned); virtual void StartDragging(const WebDropData& drop_data, WebKit::WebDragOperationsMask operations, const SkBitmap& image, diff --git a/chrome/browser/views/tab_icon_view.cc b/chrome/browser/views/tab_icon_view.cc index db8a8e8..547b039 100644 --- a/chrome/browser/views/tab_icon_view.cc +++ b/chrome/browser/views/tab_icon_view.cc @@ -13,7 +13,7 @@ #include "app/theme_provider.h" #include "base/file_util.h" #include "base/logging.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "gfx/canvas.h" #include "gfx/favicon_size.h" diff --git a/chrome/browser/views/tabs/tab_dragging_test.cc b/chrome/browser/views/tabs/tab_dragging_test.cc index 91286ec..2cb0171 100644 --- a/chrome/browser/views/tabs/tab_dragging_test.cc +++ b/chrome/browser/views/tabs/tab_dragging_test.cc @@ -4,7 +4,7 @@ #include "base/command_line.h" #include "base/file_util.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/view_ids.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" @@ -18,12 +18,13 @@ #include "net/base/net_util.h" #include "views/event.h" -#if defined(OS_CHROMEOS) +#if defined(OS_LINUX) // This test doesn't make sense on chromeos as chromeos doesn't allow dragging // tabs out. #define MAYBE_Tab2OutOfTabStrip DISABLED_Tab2OutOfTabStrip #else -#define MAYBE_Tab2OutOfTabStrip Tab2OutOfTabStrip +// Flaky, http://crbug.com/62311. +#define MAYBE_Tab2OutOfTabStrip FLAKY_Tab2OutOfTabStrip #endif #if defined(OS_LINUX) @@ -42,8 +43,9 @@ #define MAYBE_Tab1Tab2 DISABLED_Tab1Tab2 #define MAYBE_Tab1Tab3 DISABLED_Tab1Tab3 #else -#define MAYBE_Tab1Tab2 Tab1Tab2 -#define MAYBE_Tab1Tab3 Tab1Tab3 +// Flaky, http://crbug.com/62311. +#define MAYBE_Tab1Tab2 FLAKY_Tab1Tab2 +#define MAYBE_Tab1Tab3 FLAKY_Tab1Tab3 #endif class TabDraggingTest : public UITest { diff --git a/chrome/browser/views/task_manager_view.cc b/chrome/browser/views/task_manager_view.cc index a4d74c2..f8432d4 100644 --- a/chrome/browser/views/task_manager_view.cc +++ b/chrome/browser/views/task_manager_view.cc @@ -9,7 +9,7 @@ #include "base/command_line.h" #include "base/metrics/stats_table.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_window.h" diff --git a/chrome/browser/views/textfield_views.cc b/chrome/browser/views/textfield_views.cc new file mode 100644 index 0000000..f15be68 --- /dev/null +++ b/chrome/browser/views/textfield_views.cc @@ -0,0 +1,34 @@ +// 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/views/textfield_views.h" + +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/dom_ui/textfields_ui.h" +#include "chrome/browser/tab_contents/tab_contents.h" + +TextfieldViews::TextfieldViews() : DOMView() {} + +std::wstring TextfieldViews::GetText() { + TextfieldsUI* textfields_ui = dom_ui(); + return (textfields_ui) ? textfields_ui->text() : std::wstring(); +} + +void TextfieldViews::SetText(const std::wstring& text) { + TextfieldsUI* textfields_ui = dom_ui(); + if (textfields_ui) { + StringValue text_value(WideToUTF16(text)); + textfields_ui->CallJavascriptFunction(L"setTextfieldValue", text_value); + } + SchedulePaint(); +} + +TextfieldsUI* TextfieldViews::dom_ui() { + TextfieldsUI* dom_ui = NULL; + if (tab_contents_.get() && tab_contents_->dom_ui()) { + dom_ui = static_cast<TextfieldsUI*>(tab_contents_->dom_ui()); + } + return dom_ui; +} diff --git a/chrome/browser/views/textfield_views.h b/chrome/browser/views/textfield_views.h new file mode 100644 index 0000000..506a2e6 --- /dev/null +++ b/chrome/browser/views/textfield_views.h @@ -0,0 +1,27 @@ +// 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_VIEWS_TEXTFIELD_VIEWS_H_ +#define CHROME_BROWSER_VIEWS_TEXTFIELD_VIEWS_H_ +#pragma once + +#include <string> + +#include "chrome/browser/views/dom_view.h" + +class TextfieldsUI; + +class TextfieldViews : public DOMView { + public: + TextfieldViews(); + std::wstring GetText(); + void SetText(const std::wstring& text); + + private: + TextfieldsUI* dom_ui(); + + DISALLOW_COPY_AND_ASSIGN(TextfieldViews); +}; + +#endif // CHROME_BROWSER_VIEWS_TEXTFIELD_VIEWS_H_ diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc index ded8a63..cc82cdc 100644 --- a/chrome/browser/views/toolbar_view.cc +++ b/chrome/browser/views/toolbar_view.cc @@ -6,7 +6,7 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/accessibility/browser_accessibility_state.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_window.h" @@ -41,6 +41,10 @@ #endif #include "chrome/browser/views/wrench_menu.h" +#if defined(OS_WIN) +#include "chrome/browser/enumerate_modules_model_win.h" +#endif + // The space between items is 4 px in general. const int ToolbarView::kStandardSpacing = 4; // The top of the toolbar has an edge we have to skip over in addition to the 4 @@ -102,6 +106,8 @@ ToolbarView::ToolbarView(Browser* browser) registrar_.Add(this, NotificationType::UPGRADE_RECOMMENDED, NotificationService::AllSources()); } + registrar_.Add(this, NotificationType::MODULE_INCOMPATIBILITY_DETECTED, + NotificationService::AllSources()); } ToolbarView::~ToolbarView() { @@ -175,8 +181,8 @@ void ToolbarView::Init(Profile* profile) { app_menu_->SetID(VIEW_ID_APP_MENU); // Catch the case where the window is created after we detect a new version. - if (IsUpgradeRecommended()) - ShowUpgradeReminder(); + if (IsUpgradeRecommended() || ShouldShowIncompatibilityWarning()) + ShowNotificationDot(); LoadImages(); @@ -317,8 +323,8 @@ cleanup: return; destroyed_flag_ = NULL; - // Stop pulsating the upgrade reminder on the app menu, if active. - upgrade_reminder_pulse_timer_.Stop(); + // Stop pulsating the notification dot on the app menu (if active). + notification_dot_pulse_timer_.Stop(); } //////////////////////////////////////////////////////////////////////////////// @@ -401,7 +407,11 @@ void ToolbarView::Observe(NotificationType type, SchedulePaint(); } } else if (type == NotificationType::UPGRADE_RECOMMENDED) { - ShowUpgradeReminder(); + ShowNotificationDot(); + } else if (type == NotificationType::MODULE_INCOMPATIBILITY_DETECTED) { + bool confirmed_bad = *Details<bool>(details).ptr(); + if (confirmed_bad) + ShowNotificationDot(); } } @@ -580,6 +590,15 @@ bool ToolbarView::IsUpgradeRecommended() { #endif } +bool ToolbarView::ShouldShowIncompatibilityWarning() { +#if defined(OS_WIN) + EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetSingleton(); + return loaded_modules->confirmed_bad_modules_detected() > 0; +#else + return false; +#endif +} + int ToolbarView::PopupTopSpacing() const { return GetWindow()->GetNonClientView()->UseNativeFrame() ? 0 : kPopupTopSpacingNonGlass; @@ -629,20 +648,21 @@ void ToolbarView::LoadImages() { app_menu_->SetPushedIcon(GetAppMenuIcon(views::CustomButton::BS_PUSHED)); } -void ToolbarView::ShowUpgradeReminder() { - update_reminder_animation_.reset(new SlideAnimation(this)); - update_reminder_animation_->SetSlideDuration(kPulseDuration); +void ToolbarView::ShowNotificationDot() { + notification_dot_animation_.reset(new SlideAnimation(this)); + notification_dot_animation_->SetSlideDuration(kPulseDuration); // Then start the recurring timer for pulsating it. - upgrade_reminder_pulse_timer_.Start( + notification_dot_pulse_timer_.Stop(); + notification_dot_pulse_timer_.Start( base::TimeDelta::FromMilliseconds(kPulsateEveryMs), - this, &ToolbarView::PulsateUpgradeNotifier); + this, &ToolbarView::PulsateNotificationDot); } -void ToolbarView::PulsateUpgradeNotifier() { +void ToolbarView::PulsateNotificationDot() { // Start the pulsating animation. - update_reminder_animation_->Reset(0.0); - update_reminder_animation_->Show(); + notification_dot_animation_->Reset(0.0); + notification_dot_animation_->Show(); } SkBitmap ToolbarView::GetAppMenuIcon(views::CustomButton::ButtonState state) { @@ -657,7 +677,8 @@ SkBitmap ToolbarView::GetAppMenuIcon(views::CustomButton::ButtonState state) { } SkBitmap icon = *tp->GetBitmapNamed(id); - if (!IsUpgradeRecommended()) + bool add_badge = IsUpgradeRecommended() || ShouldShowIncompatibilityWarning(); + if (!add_badge) return icon; // Draw the chrome app menu icon onto the canvas. @@ -668,27 +689,48 @@ SkBitmap ToolbarView::GetAppMenuIcon(views::CustomButton::ButtonState state) { SkBitmap badge; static bool has_faded_in = false; if (!has_faded_in) { - SkBitmap* dot = tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE); + SkBitmap* dot = NULL; + if (ShouldShowIncompatibilityWarning()) { +#if defined(OS_WIN) + dot = tp->GetBitmapNamed(IDR_INCOMPATIBILITY_DOT_INACTIVE); +#else + NOTREACHED(); +#endif + } else { + dot = tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE); + } SkBitmap transparent; transparent.setConfig(dot->getConfig(), dot->width(), dot->height()); transparent.allocPixels(); transparent.eraseARGB(0, 0, 0, 0); badge = SkBitmapOperations::CreateBlendedBitmap( - *dot, transparent, 1.0 - update_reminder_animation_->GetCurrentValue()); - if (update_reminder_animation_->GetCurrentValue() == 1.0) + *dot, transparent, + 1.0 - notification_dot_animation_->GetCurrentValue()); + if (notification_dot_animation_->GetCurrentValue() == 1.0) has_faded_in = true; } else { // Convert animation values that start from 0.0 and incrementally go // up to 1.0 into values that start in 0.0, go to 1.0 and then back // to 0.0 (to create a pulsing effect). double value = - 1.0 - abs(2.0 * update_reminder_animation_->GetCurrentValue() - 1.0); + 1.0 - abs(2.0 * notification_dot_animation_->GetCurrentValue() - 1.0); // Add the badge to it. - badge = SkBitmapOperations::CreateBlendedBitmap( - *tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE), - *tp->GetBitmapNamed(IDR_UPGRADE_DOT_ACTIVE), - value); + if (ShouldShowIncompatibilityWarning()) { +#if defined(OS_WIN) + badge = SkBitmapOperations::CreateBlendedBitmap( + *tp->GetBitmapNamed(IDR_INCOMPATIBILITY_DOT_INACTIVE), + *tp->GetBitmapNamed(IDR_INCOMPATIBILITY_DOT_ACTIVE), + value); +#else + NOTREACHED(); +#endif + } else { + badge = SkBitmapOperations::CreateBlendedBitmap( + *tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE), + *tp->GetBitmapNamed(IDR_UPGRADE_DOT_ACTIVE), + value); + } } static const int kBadgeLeftSpacing = 8; diff --git a/chrome/browser/views/toolbar_view.h b/chrome/browser/views/toolbar_view.h index 8715dc5..c9b92bd 100644 --- a/chrome/browser/views/toolbar_view.h +++ b/chrome/browser/views/toolbar_view.h @@ -142,6 +142,9 @@ class ToolbarView : public AccessiblePaneView, // Returns true if we should show the upgrade recommended dot. bool IsUpgradeRecommended(); + // Returns true if we should show the warning for incompatible software. + bool ShouldShowIncompatibilityWarning(); + // Returns the number of pixels above the location bar in non-normal display. int PopupTopSpacing() const; @@ -158,12 +161,12 @@ class ToolbarView : public AccessiblePaneView, return display_mode_ == DISPLAYMODE_NORMAL; } - // Starts the recurring timer that periodically asks the upgrade notifier + // Starts the recurring timer that periodically asks the notification dot // to pulsate. - void ShowUpgradeReminder(); + void ShowNotificationDot(); - // Show the reminder, tempting the user to upgrade by pulsating. - void PulsateUpgradeNotifier(); + // Show the reminder, tempting the user to take a look. + void PulsateNotificationDot(); // Gets a canvas with the icon for the app menu. It will possibly contain // an overlaid badge if an update is recommended. @@ -210,12 +213,12 @@ class ToolbarView : public AccessiblePaneView, // Vector of listeners to receive callbacks when the menu opens. std::vector<views::MenuListener*> menu_listeners_; - // The animation that makes the update reminder pulse. - scoped_ptr<SlideAnimation> update_reminder_animation_; + // The animation that makes the notification dot pulse. + scoped_ptr<SlideAnimation> notification_dot_animation_; // We periodically restart the animation after it has been showed // once, to create a pulsating effect. - base::RepeatingTimer<ToolbarView> upgrade_reminder_pulse_timer_; + base::RepeatingTimer<ToolbarView> notification_dot_pulse_timer_; // Used to post tasks to switch to the next/previous menu. ScopedRunnableMethodFactory<ToolbarView> method_factory_; diff --git a/chrome/browser/views/update_recommended_message_box.cc b/chrome/browser/views/update_recommended_message_box.cc index 99ecbbd..baf69ab 100644 --- a/chrome/browser/views/update_recommended_message_box.cc +++ b/chrome/browser/views/update_recommended_message_box.cc @@ -9,12 +9,18 @@ #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/views/window.h" #include "chrome/common/pref_names.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "views/controls/message_box_view.h" #include "views/window/window.h" +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/power_library.h" +#endif + //////////////////////////////////////////////////////////////////////////////// // UpdateRecommendedMessageBox, public: @@ -30,6 +36,10 @@ bool UpdateRecommendedMessageBox::Accept() { PrefService* pref_service = g_browser_process->local_state(); pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, true); +#if defined(OS_CHROMEOS) + chromeos::CrosLibrary::Get()->GetPowerLibrary()->RequestRestart(); + // If running the Chrome OS build, but we're not on the device, fall through +#endif BrowserList::CloseAllBrowsersAndExit(); return true; @@ -71,14 +81,18 @@ views::View* UpdateRecommendedMessageBox::GetContentsView() { UpdateRecommendedMessageBox::UpdateRecommendedMessageBox( gfx::NativeWindow parent_window) { const int kDialogWidth = 400; +#if defined(OS_CHROMEOS) + const std::wstring product_name = l10n_util::GetString(IDS_PRODUCT_OS_NAME); +#else + const std::wstring product_name = l10n_util::GetString(IDS_PRODUCT_NAME); +#endif // Also deleted when the window closes. message_box_view_ = new MessageBoxView( MessageBoxFlags::kFlagHasMessage | MessageBoxFlags::kFlagHasOKButton, - l10n_util::GetStringF(IDS_UPDATE_RECOMMENDED, - l10n_util::GetString(IDS_PRODUCT_NAME)), + l10n_util::GetStringF(IDS_UPDATE_RECOMMENDED, product_name), std::wstring(), kDialogWidth); - views::Window::CreateChromeWindow(parent_window, gfx::Rect(), this)->Show(); + browser::CreateViewsWindow(parent_window, gfx::Rect(), this)->Show(); } UpdateRecommendedMessageBox::~UpdateRecommendedMessageBox() { diff --git a/chrome/browser/views/window.cc b/chrome/browser/views/window.cc index bd8a304..ae28ce4 100644 --- a/chrome/browser/views/window.cc +++ b/chrome/browser/views/window.cc @@ -17,7 +17,10 @@ views::Window* CreateViewsWindow(gfx::NativeWindow parent, const gfx::Rect& bounds, views::WindowDelegate* delegate) { #if defined(OS_CHROMEOS) - return chromeos::BubbleWindow::Create(parent, gfx::Rect(), delegate); + return chromeos::BubbleWindow::Create(parent, + gfx::Rect(), + chromeos::BubbleWindow::STYLE_GENERIC, + delegate); #else return views::Window::CreateChromeWindow(parent, gfx::Rect(), delegate); #endif diff --git a/chrome/browser/views/wrench_menu.cc b/chrome/browser/views/wrench_menu.cc index 0c51c68..4ad20f0 100644 --- a/chrome/browser/views/wrench_menu.cc +++ b/chrome/browser/views/wrench_menu.cc @@ -10,7 +10,7 @@ #include "app/resource_bundle.h" #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" diff --git a/chrome/browser/visitedlink_master.cc b/chrome/browser/visitedlink_master.cc index cf1445c..6fdb461 100644 --- a/chrome/browser/visitedlink_master.cc +++ b/chrome/browser/visitedlink_master.cc @@ -21,6 +21,7 @@ #include "base/rand_util.h" #include "base/stack_container.h" #include "base/string_util.h" +#include "base/thread_restrictions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/history/history.h" @@ -251,6 +252,10 @@ void VisitedLinkMaster::InitMembers(Listener* listener, Profile* profile) { } bool VisitedLinkMaster::Init() { + // We probably shouldn't be loading this from the UI thread, + // but it does need to happen early on in startup. + // http://code.google.com/p/chromium/issues/detail?id=24163 + base::ThreadRestrictions::ScopedAllowIO allow_io; if (!InitFromFile()) return InitFromScratch(suppress_rebuild_); return true; @@ -670,14 +675,7 @@ bool VisitedLinkMaster::CreateURLTable(int32 num_entries, bool init_to_empty) { if (!shared_memory_) return false; - if (!shared_memory_->Create(std::string() /* anonymous */, - false /* read-write */, false /* create */, - alloc_size)) { - return false; - } - - // Map into our process. - if (!shared_memory_->Map(alloc_size)) { + if (!shared_memory_->CreateAndMapAnonymous(alloc_size)) { delete shared_memory_; shared_memory_ = NULL; return false; @@ -889,6 +887,9 @@ void VisitedLinkMaster::OnTableRebuildComplete( // Send an update notification to all child processes. listener_->NewTable(shared_memory_); + // We shouldn't be writing the table from the main thread! + // http://code.google.com/p/chromium/issues/detail?id=24163 + base::ThreadRestrictions::ScopedAllowIO allow_io; WriteFullTable(); } } diff --git a/chrome/browser/webdata/autofill_change.cc b/chrome/browser/webdata/autofill_change.cc new file mode 100644 index 0000000..fd0cfbf --- /dev/null +++ b/chrome/browser/webdata/autofill_change.cc @@ -0,0 +1,90 @@ +// 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/webdata/autofill_change.h" + +#include "chrome/browser/autofill/autofill_profile.h" +#include "chrome/browser/autofill/credit_card.h" + +AutofillChange::AutofillChange(Type type, const AutofillKey& key) + : GenericAutofillChange<AutofillKey>(type, key) { +} + +AutofillChange::~AutofillChange() { +} + +AutofillProfileChange::AutofillProfileChange(Type type, + string16 key, + const AutoFillProfile* profile, + const string16& pre_update_label) + : GenericAutofillChange<string16>(type, key), + profile_(profile), + pre_update_label_(pre_update_label) { +} + +AutofillProfileChange::~AutofillProfileChange() { +} + +bool AutofillProfileChange::operator==( + const AutofillProfileChange& change) const { + if (type() != change.type() || key() != change.key()) + return false; + if (type() == REMOVE) + return true; + if (*profile() != *change.profile()) + return false; + return type() == ADD || pre_update_label_ == change.pre_update_label(); +} + +AutofillCreditCardChange::AutofillCreditCardChange( + Type type, string16 key, const CreditCard* credit_card) + : GenericAutofillChange<string16>(type, key), credit_card_(credit_card) { +} + +AutofillCreditCardChange::~AutofillCreditCardChange() { +} + +bool AutofillCreditCardChange::operator==( + const AutofillCreditCardChange& change) const { + return type() == change.type() && + key() == change.key() && + (type() != REMOVE) ? *credit_card() == *change.credit_card() : true; +} + +AutofillProfileChangeGUID::AutofillProfileChangeGUID( + Type type, std::string key, const AutoFillProfile* profile) + : GenericAutofillChange<std::string>(type, key), + profile_(profile) { + DCHECK(type == ADD ? (profile && profile->guid() == key) : true); + DCHECK(type == UPDATE ? (profile && profile->guid() == key) : true); + DCHECK(type == REMOVE ? !profile : true); +} + +AutofillProfileChangeGUID::~AutofillProfileChangeGUID() { +} + +bool AutofillProfileChangeGUID::operator==( + const AutofillProfileChangeGUID& change) const { + return type() == change.type() && + key() == change.key() && + (type() != REMOVE) ? *profile() == *change.profile() : true; +} + +AutofillCreditCardChangeGUID::AutofillCreditCardChangeGUID( + Type type, std::string key, const CreditCard* credit_card) + : GenericAutofillChange<std::string>(type, key), credit_card_(credit_card) { + DCHECK(type == ADD ? (credit_card && credit_card->guid() == key) : true); + DCHECK(type == UPDATE ? (credit_card && credit_card->guid() == key) : true); + DCHECK(type == REMOVE ? !credit_card : true); +} + +AutofillCreditCardChangeGUID::~AutofillCreditCardChangeGUID() { +} + +bool AutofillCreditCardChangeGUID::operator==( + const AutofillCreditCardChangeGUID& change) const { + return type() == change.type() && + key() == change.key() && + (type() != REMOVE) ? *credit_card() == *change.credit_card() : true; +} diff --git a/chrome/browser/webdata/autofill_change.h b/chrome/browser/webdata/autofill_change.h index 11723d3..2a46399 100644 --- a/chrome/browser/webdata/autofill_change.h +++ b/chrome/browser/webdata/autofill_change.h @@ -6,8 +6,6 @@ #define CHROME_BROWSER_WEBDATA_AUTOFILL_CHANGE_H__ #pragma once -#include "chrome/browser/autofill/autofill_profile.h" -#include "chrome/browser/autofill/credit_card.h" #include "chrome/browser/webdata/autofill_entry.h" class AutoFillProfile; @@ -40,53 +38,102 @@ class GenericAutofillChange { class AutofillChange : public GenericAutofillChange<AutofillKey> { public: - AutofillChange(Type t, const AutofillKey& k) - : GenericAutofillChange<AutofillKey>(t, k) {} + AutofillChange(Type type, const AutofillKey& key); + virtual ~AutofillChange(); bool operator==(const AutofillChange& change) const { return type() == change.type() && key() == change.key(); } }; +// DEPRECATED +// TODO(dhollowa): Remove use of labels for sync. http://crbug.com/58813 class AutofillProfileChange : public GenericAutofillChange<string16> { public: - // If t == REMOVE, |p| should be NULL. |pre_update_label| only applies to - // UPDATE changes. - AutofillProfileChange(Type t, string16 k, const AutoFillProfile* p, - const string16& pre_update_label) - : GenericAutofillChange<string16>(t, k), profile_(p), - pre_update_label_(pre_update_label) {} + // The |type| input specifies the change type. The |key| input is the key, + // which is expected to be the label identifying the |profile|. + // When |type| == ADD, |profile| should be non-NULL. + // When |type| == UPDATE, |profile| should be non-NULL. + // When |type| == REMOVE, |profile| should be NULL. + // The |pre_update_label| input specifies the label as it was prior to the + // change (applicable only for UPDATE). + AutofillProfileChange(Type type, + string16 key, + const AutoFillProfile* profile, + const string16& pre_update_label); + virtual ~AutofillProfileChange(); const AutoFillProfile* profile() const { return profile_; } const string16& pre_update_label() const { return pre_update_label_; } - bool operator==(const AutofillProfileChange& change) const { - if (type() != change.type() || key() != change.key()) - return false; - if (type() == REMOVE) - return true; - // TODO(dhollowa): Replace with |AutoFillProfile::Compare|. - // http://crbug.com/58813 - if (*profile() != *change.profile()) - return false; - return type() == ADD || pre_update_label_ == change.pre_update_label(); - } + bool operator==(const AutofillProfileChange& change) const; + private: - const AutoFillProfile* profile_; // Unowned pointer, can be NULL. + // Weak reference, can be NULL. + const AutoFillProfile* profile_; const string16 pre_update_label_; }; +// DEPRECATED +// TODO(dhollowa): Remove use of labels for sync. http://crbug.com/58813 class AutofillCreditCardChange : public GenericAutofillChange<string16> { public: - // If t == REMOVE, |card| should be NULL. - AutofillCreditCardChange(Type t, string16 k, const CreditCard* card) - : GenericAutofillChange<string16>(t, k), credit_card_(card) {} + // The |type| input specifies the change type. The |key| input is the key, + // which is expected to be the label identifying the |credit_card|. + // When |type| == ADD, |credit_card| should be non-NULL. + // When |type| == UPDATE, |credit_card| should be non-NULL. + // When |type| == REMOVE, |credit_card| should be NULL. + AutofillCreditCardChange(Type type, + string16 key, + const CreditCard* credit_card); + virtual ~AutofillCreditCardChange(); const CreditCard* credit_card() const { return credit_card_; } - bool operator==(const AutofillCreditCardChange& change) const { - return type() == change.type() && key() == change.key() && - (type() != REMOVE) ? *credit_card() == *change.credit_card() : true; - } + bool operator==(const AutofillCreditCardChange& change) const; + + private: + // Weak reference, can be NULL. + const CreditCard* credit_card_; +}; + +// Change notification details for AutoFill profile changes. +class AutofillProfileChangeGUID : public GenericAutofillChange<std::string> { + public: + // The |type| input specifies the change type. The |key| input is the key, + // which is expected to be the GUID identifying the |profile|. + // When |type| == ADD, |profile| should be non-NULL. + // When |type| == UPDATE, |profile| should be non-NULL. + // When |type| == REMOVE, |profile| should be NULL. + AutofillProfileChangeGUID(Type type, + std::string key, + const AutoFillProfile* profile); + virtual ~AutofillProfileChangeGUID(); + + const AutoFillProfile* profile() const { return profile_; } + bool operator==(const AutofillProfileChangeGUID& change) const; + + private: + // Weak reference, can be NULL. + const AutoFillProfile* profile_; +}; + +// Change notification details for AutoFill credit card changes. +class AutofillCreditCardChangeGUID : public GenericAutofillChange<std::string> { + public: + // The |type| input specifies the change type. The |key| input is the key, + // which is expected to be the GUID identifying the |credit_card|. + // When |type| == ADD, |credit_card| should be non-NULL. + // When |type| == UPDATE, |credit_card| should be non-NULL. + // When |type| == REMOVE, |credit_card| should be NULL. + AutofillCreditCardChangeGUID(Type type, + std::string key, + const CreditCard* credit_card); + virtual ~AutofillCreditCardChangeGUID(); + + const CreditCard* credit_card() const { return credit_card_; } + bool operator==(const AutofillCreditCardChangeGUID& change) const; + private: - const CreditCard* credit_card_; // Unowned pointer, can be NULL. + // Weak reference, can be NULL. + const CreditCard* credit_card_; }; #endif // CHROME_BROWSER_WEBDATA_AUTOFILL_CHANGE_H__ diff --git a/chrome/browser/webdata/autofill_entry.cc b/chrome/browser/webdata/autofill_entry.cc index d0f8913..31bc406 100644 --- a/chrome/browser/webdata/autofill_entry.cc +++ b/chrome/browser/webdata/autofill_entry.cc @@ -4,6 +4,26 @@ #include <set> #include "chrome/browser/webdata/autofill_entry.h" +#include "base/utf_string_conversions.h" + +AutofillKey::AutofillKey() {} + +AutofillKey::AutofillKey(const string16& name, const string16& value) + : name_(name), + value_(value) { +} + +AutofillKey::AutofillKey(const char* name, const char* value) + : name_(UTF8ToUTF16(name)), + value_(UTF8ToUTF16(value)) { +} + +AutofillKey::AutofillKey(const AutofillKey& key) + : name_(key.name()), + value_(key.value()) { +} + +AutofillKey::~AutofillKey() {} bool AutofillKey::operator==(const AutofillKey& key) const { return name_ == key.name() && value_ == key.value(); @@ -20,6 +40,14 @@ bool AutofillKey::operator<(const AutofillKey& key) const { } } +AutofillEntry::AutofillEntry(const AutofillKey& key, + const std::vector<base::Time>& timestamps) + : key_(key), + timestamps_(timestamps) { +} + +AutofillEntry::~AutofillEntry() {} + bool AutofillEntry::operator==(const AutofillEntry& entry) const { if (!(key_ == entry.key())) return false; diff --git a/chrome/browser/webdata/autofill_entry.h b/chrome/browser/webdata/autofill_entry.h index 4917b29..7aa9139 100644 --- a/chrome/browser/webdata/autofill_entry.h +++ b/chrome/browser/webdata/autofill_entry.h @@ -9,21 +9,15 @@ #include <vector> #include "base/string16.h" #include "base/time.h" -#include "base/utf_string_conversions.h" class AutofillKey { public: - AutofillKey() {} - AutofillKey(const string16& name, const string16& value) - : name_(name), - value_(value) {} - AutofillKey(const char* name, const char* value) - : name_(UTF8ToUTF16(name)), - value_(UTF8ToUTF16(value)) {} - AutofillKey(const AutofillKey& key) - : name_(key.name()), - value_(key.value()) {} - virtual ~AutofillKey() {} + AutofillKey(); + AutofillKey(const string16& name, const string16& value); + AutofillKey(const char* name, const char* value); + AutofillKey(const AutofillKey& key); + virtual ~AutofillKey(); + const string16& name() const { return name_; } const string16& value() const { return value_; } @@ -38,9 +32,8 @@ class AutofillKey { class AutofillEntry { public: AutofillEntry(const AutofillKey& key, - const std::vector<base::Time>& timestamps) - : key_(key), - timestamps_(timestamps) {} + const std::vector<base::Time>& timestamps); + ~AutofillEntry(); const AutofillKey& key() const { return key_; } const std::vector<base::Time>& timestamps() const { return timestamps_; } diff --git a/chrome/browser/webdata/web_data_service.cc b/chrome/browser/webdata/web_data_service.cc index 0664bbf..d5ecd0f 100644 --- a/chrome/browser/webdata/web_data_service.cc +++ b/chrome/browser/webdata/web_data_service.cc @@ -391,37 +391,27 @@ void WebDataService::RemoveFormValueForElementName( request)); } -void WebDataService::AddAutoFillProfile(const AutoFillProfile& profile) { +void WebDataService::AddAutoFillProfileGUID(const AutoFillProfile& profile) { GenericRequest<AutoFillProfile>* request = new GenericRequest<AutoFillProfile>( this, GetNextRequestHandle(), NULL, profile); RegisterRequest(request); ScheduleTask(NewRunnableMethod(this, - &WebDataService::AddAutoFillProfileImpl, + &WebDataService::AddAutoFillProfileGUIDImpl, request)); } -void WebDataService::UpdateAutoFillProfile(const AutoFillProfile& profile) { +void WebDataService::UpdateAutoFillProfileGUID(const AutoFillProfile& profile) { GenericRequest<AutoFillProfile>* request = new GenericRequest<AutoFillProfile>( this, GetNextRequestHandle(), NULL, profile); RegisterRequest(request); ScheduleTask(NewRunnableMethod(this, - &WebDataService::UpdateAutoFillProfileImpl, + &WebDataService::UpdateAutoFillProfileGUIDImpl, request)); } -void WebDataService::RemoveAutoFillProfile(int profile_id) { - GenericRequest<int>* request = - new GenericRequest<int>( - this, GetNextRequestHandle(), NULL, profile_id); - RegisterRequest(request); - ScheduleTask(NewRunnableMethod(this, - &WebDataService::RemoveAutoFillProfileImpl, - request)); -} - -void WebDataService::RemoveAutoFillProfile(const std::string& guid) { +void WebDataService::RemoveAutoFillProfileGUID(const std::string& guid) { GenericRequest<std::string>* request = new GenericRequest<std::string>( this, GetNextRequestHandle(), NULL, guid); @@ -443,37 +433,27 @@ WebDataService::Handle WebDataService::GetAutoFillProfiles( return request->GetHandle(); } -void WebDataService::AddCreditCard(const CreditCard& creditcard) { +void WebDataService::AddCreditCardGUID(const CreditCard& credit_card) { GenericRequest<CreditCard>* request = new GenericRequest<CreditCard>( - this, GetNextRequestHandle(), NULL, creditcard); + this, GetNextRequestHandle(), NULL, credit_card); RegisterRequest(request); ScheduleTask(NewRunnableMethod(this, - &WebDataService::AddCreditCardImpl, + &WebDataService::AddCreditCardGUIDImpl, request)); } -void WebDataService::UpdateCreditCard(const CreditCard& creditcard) { +void WebDataService::UpdateCreditCardGUID(const CreditCard& credit_card) { GenericRequest<CreditCard>* request = new GenericRequest<CreditCard>( - this, GetNextRequestHandle(), NULL, creditcard); + this, GetNextRequestHandle(), NULL, credit_card); RegisterRequest(request); ScheduleTask(NewRunnableMethod(this, - &WebDataService::UpdateCreditCardImpl, + &WebDataService::UpdateCreditCardGUIDImpl, request)); } -void WebDataService::RemoveCreditCard(int creditcard_id) { - GenericRequest<int>* request = - new GenericRequest<int>( - this, GetNextRequestHandle(), NULL, creditcard_id); - RegisterRequest(request); - ScheduleTask(NewRunnableMethod(this, - &WebDataService::RemoveCreditCardImpl, - request)); -} - -void WebDataService::RemoveCreditCard(const std::string& guid) { +void WebDataService::RemoveCreditCardGUID(const std::string& guid) { GenericRequest<std::string>* request = new GenericRequest<std::string>( this, GetNextRequestHandle(), NULL, guid); @@ -499,11 +479,11 @@ void WebDataService::RemoveAutoFillProfilesAndCreditCardsModifiedBetween( const Time& delete_begin, const Time& delete_end) { GenericRequest2<Time, Time>* request = - new GenericRequest2<Time, Time>(this, - GetNextRequestHandle(), - NULL, - delete_begin, - delete_end); + new GenericRequest2<Time, Time>(this, + GetNextRequestHandle(), + NULL, + delete_begin, + delete_end); RegisterRequest(request); ScheduleTask(NewRunnableMethod( this, @@ -690,8 +670,10 @@ void WebDataService::RemoveKeywordImpl( void WebDataService::UpdateKeywordImpl(GenericRequest<TemplateURL>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - if (!db_->UpdateKeyword(request->GetArgument())) + if (!db_->UpdateKeyword(request->GetArgument())) { NOTREACHED(); + return; + } ScheduleCommit(); } request->RequestComplete(); @@ -714,8 +696,10 @@ void WebDataService::SetDefaultSearchProviderImpl( GenericRequest<TemplateURLID>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - if (!db_->SetDefaultSearchProviderID(request->GetArgument())) + if (!db_->SetDefaultSearchProviderID(request->GetArgument())) { NOTREACHED(); + return; + } ScheduleCommit(); } request->RequestComplete(); @@ -725,8 +709,10 @@ void WebDataService::SetBuiltinKeywordVersionImpl( GenericRequest<int>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - if (!db_->SetBuitinKeywordVersion(request->GetArgument())) + if (!db_->SetBuitinKeywordVersion(request->GetArgument())) { NOTREACHED(); + return; + } ScheduleCommit(); } request->RequestComplete(); @@ -922,8 +908,10 @@ void WebDataService::AddFormElementsImpl( const std::vector<FormField>& form_fields = request->GetArgument(); if (db_ && !request->IsCancelled()) { AutofillChangeList changes; - if (!db_->AddFormFieldValues(form_fields, &changes)) + if (!db_->AddFormFieldValues(form_fields, &changes)) { NOTREACHED(); + return; + } request->SetResult( new WDResult<AutofillChangeList>(AUTOFILL_CHANGES, changes)); ScheduleCommit(); @@ -1003,7 +991,7 @@ void WebDataService::RemoveFormValueForElementNameImpl( request->RequestComplete(); } -void WebDataService::AddAutoFillProfileImpl( +void WebDataService::AddAutoFillProfileGUIDImpl( GenericRequest<AutoFillProfile>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { @@ -1014,68 +1002,71 @@ void WebDataService::AddAutoFillProfileImpl( } ScheduleCommit(); - AutofillProfileChange change(AutofillProfileChange::ADD, - profile.Label(), &profile, string16()); + // Send GUID-based notification. + AutofillProfileChangeGUID change(AutofillProfileChangeGUID::ADD, + profile.guid(), &profile); + NotificationService::current()->Notify( + NotificationType::AUTOFILL_PROFILE_CHANGED_GUID, + Source<WebDataService>(this), + Details<AutofillProfileChangeGUID>(&change)); + + // TODO(dhollowa): Remove labels. http://crbug.com/58813 + // Send out old Label-based notification until sync can switch over to + // GUID-based notifications. + AutofillProfileChange deprecated_change(AutofillProfileChange::ADD, + profile.Label(), + &profile, + string16()); NotificationService::current()->Notify( NotificationType::AUTOFILL_PROFILE_CHANGED, Source<WebDataService>(this), - Details<AutofillProfileChange>(&change)); + Details<AutofillProfileChange>(&deprecated_change)); } request->RequestComplete(); } -void WebDataService::UpdateAutoFillProfileImpl( +void WebDataService::UpdateAutoFillProfileGUIDImpl( GenericRequest<AutoFillProfile>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { const AutoFillProfile& profile = request->GetArgument(); - // The AUTOFILL_PROFILE_CHANGED contract for an update requires that we - // send along the label of the un-updated profile, to detect label - // changes separately. So first, we query for the existing profile. - AutoFillProfile* old_profile_ptr = NULL; - if (!db_->GetAutoFillProfileForID(profile.unique_id(), &old_profile_ptr)) - NOTREACHED(); - if (old_profile_ptr) { - scoped_ptr<AutoFillProfile> old_profile(old_profile_ptr); - if (!db_->UpdateAutoFillProfile(profile)) - NOTREACHED(); - ScheduleCommit(); - AutofillProfileChange change(AutofillProfileChange::UPDATE, - profile.Label(), &profile, - old_profile->Label()); - NotificationService::current()->Notify( - NotificationType::AUTOFILL_PROFILE_CHANGED, - Source<WebDataService>(this), - Details<AutofillProfileChange>(&change)); + // TODO(dhollowa): Remove labels. http://crbug.com/58813 + // Send out old Label-based notification until sync can switch over to + // GUID-based notifications. + // Only perform the update if the profile exists. It is currently + // valid to try to update a missing profile. We simply drop the write and + // the caller will detect this on the next refresh. + AutoFillProfile* original_profile = NULL; + if (!db_->GetAutoFillProfileForGUID(profile.guid(), &original_profile)) { + request->RequestComplete(); + return; } - } - request->RequestComplete(); -} + scoped_ptr<AutoFillProfile> scoped_profile(original_profile); -void WebDataService::RemoveAutoFillProfileImpl( - GenericRequest<int>* request) { - InitializeDatabaseIfNecessary(); - if (db_ && !request->IsCancelled()) { - int profile_id = request->GetArgument(); - AutoFillProfile* profile = NULL; - if (!db_->GetAutoFillProfileForID(profile_id, &profile)) + if (!db_->UpdateAutoFillProfile(profile)) { NOTREACHED(); + return; + } + ScheduleCommit(); - if (profile) { - scoped_ptr<AutoFillProfile> dead_profile(profile); - if (!db_->RemoveAutoFillProfile(profile_id)) - NOTREACHED(); - ScheduleCommit(); + // Send GUID-based notification. + AutofillProfileChangeGUID change(AutofillProfileChangeGUID::UPDATE, + profile.guid(), &profile); + NotificationService::current()->Notify( + NotificationType::AUTOFILL_PROFILE_CHANGED_GUID, + Source<WebDataService>(this), + Details<AutofillProfileChangeGUID>(&change)); - AutofillProfileChange change(AutofillProfileChange::REMOVE, - dead_profile->Label(), - NULL, string16()); - NotificationService::current()->Notify( - NotificationType::AUTOFILL_PROFILE_CHANGED, - Source<WebDataService>(this), - Details<AutofillProfileChange>(&change)); - } + // TODO(dhollowa): Remove labels. http://crbug.com/58813 + // Send old Label-based notification. + AutofillProfileChange deprecated_change(AutofillProfileChange::UPDATE, + profile.Label(), &profile, + original_profile->Label()); + NotificationService::current()->Notify( + NotificationType::AUTOFILL_PROFILE_CHANGED, + Source<WebDataService>(this), + Details<AutofillProfileChange>(&deprecated_change)); } request->RequestComplete(); } @@ -1085,25 +1076,40 @@ void WebDataService::RemoveAutoFillProfileGUIDImpl( InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { std::string guid = request->GetArgument(); + + // TODO(dhollowa): Remove labels. http://crbug.com/58813 + // Send out old Label-based notification until sync can switch over to + // GUID-based notifications. AutoFillProfile* profile = NULL; - if (!db_->GetAutoFillProfileForGUID(guid, &profile)) + if (!db_->GetAutoFillProfileForGUID(guid, &profile)) { NOTREACHED(); + return; + } + scoped_ptr<AutoFillProfile> scoped_profile(profile); - if (profile) { - scoped_ptr<AutoFillProfile> dead_profile(profile); - if (!db_->RemoveAutoFillProfile(guid)) - NOTREACHED(); - ScheduleCommit(); - - // TODO(dhollowa): Deprecate and label. http://crbug.com/58813 - AutofillProfileChange change(AutofillProfileChange::REMOVE, - dead_profile->Label(), - NULL, string16()); - NotificationService::current()->Notify( - NotificationType::AUTOFILL_PROFILE_CHANGED, - Source<WebDataService>(this), - Details<AutofillProfileChange>(&change)); + if (!db_->RemoveAutoFillProfile(guid)) { + NOTREACHED(); + return; } + ScheduleCommit(); + + // Send GUID-based notification. + AutofillProfileChangeGUID change(AutofillProfileChangeGUID::REMOVE, + guid, NULL); + NotificationService::current()->Notify( + NotificationType::AUTOFILL_PROFILE_CHANGED_GUID, + Source<WebDataService>(this), + Details<AutofillProfileChangeGUID>(&change)); + + // TODO(dhollowa): Remove labels. http://crbug.com/58813 + // Send old Label-based notification. + AutofillProfileChange deprecated_change(AutofillProfileChange::REMOVE, + scoped_profile->Label(), + NULL, string16()); + NotificationService::current()->Notify( + NotificationType::AUTOFILL_PROFILE_CHANGED, + Source<WebDataService>(this), + Details<AutofillProfileChange>(&deprecated_change)); } request->RequestComplete(); } @@ -1120,67 +1126,56 @@ void WebDataService::GetAutoFillProfilesImpl(WebDataRequest* request) { request->RequestComplete(); } -void WebDataService::AddCreditCardImpl( +void WebDataService::AddCreditCardGUIDImpl( GenericRequest<CreditCard>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - const CreditCard& creditcard = request->GetArgument(); - if (!db_->AddCreditCard(creditcard)) + const CreditCard& credit_card = request->GetArgument(); + if (!db_->AddCreditCard(credit_card)) { NOTREACHED(); + return; + } ScheduleCommit(); - AutofillCreditCardChange change(AutofillCreditCardChange::ADD, - creditcard.Label(), &creditcard); + // Send GUID-based notification. + AutofillCreditCardChangeGUID change(AutofillCreditCardChangeGUID::ADD, + credit_card.guid(), &credit_card); NotificationService::current()->Notify( - NotificationType::AUTOFILL_CREDIT_CARD_CHANGED, + NotificationType::AUTOFILL_CREDIT_CARD_CHANGED_GUID, Source<WebDataService>(this), - Details<AutofillCreditCardChange>(&change)); + Details<AutofillCreditCardChangeGUID>(&change)); } request->RequestComplete(); } -void WebDataService::UpdateCreditCardImpl( +void WebDataService::UpdateCreditCardGUIDImpl( GenericRequest<CreditCard>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - const CreditCard& creditcard = request->GetArgument(); - if (!db_->UpdateCreditCard(creditcard)) + const CreditCard& credit_card = request->GetArgument(); + + // It is currently valid to try to update a missing profile. We simply drop + // the write and the caller will detect this on the next refresh. + CreditCard* original_credit_card = NULL; + if (!db_->GetCreditCardForGUID(credit_card.guid(), &original_credit_card)) { + request->RequestComplete(); + return; + } + scoped_ptr<CreditCard> scoped_credit_card(original_credit_card); + + if (!db_->UpdateCreditCard(credit_card)) { NOTREACHED(); + return; + } ScheduleCommit(); - AutofillCreditCardChange change(AutofillCreditCardChange::UPDATE, - creditcard.Label(), &creditcard); + // Send GUID-based notification. + AutofillCreditCardChangeGUID change(AutofillCreditCardChangeGUID::UPDATE, + credit_card.guid(), &credit_card); NotificationService::current()->Notify( - NotificationType::AUTOFILL_CREDIT_CARD_CHANGED, + NotificationType::AUTOFILL_CREDIT_CARD_CHANGED_GUID, Source<WebDataService>(this), - Details<AutofillCreditCardChange>(&change)); - } - request->RequestComplete(); -} - -void WebDataService::RemoveCreditCardImpl( - GenericRequest<int>* request) { - InitializeDatabaseIfNecessary(); - if (db_ && !request->IsCancelled()) { - int creditcard_id = request->GetArgument(); - CreditCard* credit_card = NULL; - if (!db_->GetCreditCardForID(creditcard_id, &credit_card)) - NOTREACHED(); - - if (credit_card) { - scoped_ptr<CreditCard> dead_credit_card(credit_card); - if (!db_->RemoveCreditCard(creditcard_id)) - NOTREACHED(); - - ScheduleCommit(); - - AutofillCreditCardChange change(AutofillCreditCardChange::REMOVE, - dead_credit_card->Label(), NULL); - NotificationService::current()->Notify( - NotificationType::AUTOFILL_CREDIT_CARD_CHANGED, - Source<WebDataService>(this), - Details<AutofillCreditCardChange>(&change)); - } + Details<AutofillCreditCardChangeGUID>(&change)); } request->RequestComplete(); } @@ -1190,25 +1185,19 @@ void WebDataService::RemoveCreditCardGUIDImpl( InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { std::string guid = request->GetArgument(); - CreditCard* credit_card = NULL; - if (!db_->GetCreditCardForGUID(guid, &credit_card)) + if (!db_->RemoveCreditCard(guid)) { NOTREACHED(); - - if (credit_card) { - scoped_ptr<CreditCard> dead_credit_card(credit_card); - if (!db_->RemoveCreditCard(guid)) - NOTREACHED(); - - ScheduleCommit(); - - // TODO(dhollowa): Deprecate and label. http://crbug.com/58813 - AutofillCreditCardChange change(AutofillCreditCardChange::REMOVE, - dead_credit_card->Label(), NULL); - NotificationService::current()->Notify( - NotificationType::AUTOFILL_CREDIT_CARD_CHANGED, - Source<WebDataService>(this), - Details<AutofillCreditCardChange>(&change)); + return; } + ScheduleCommit(); + + // Send GUID-based notification. + AutofillCreditCardChangeGUID change(AutofillCreditCardChangeGUID::REMOVE, + guid, NULL); + NotificationService::current()->Notify( + NotificationType::AUTOFILL_CREDIT_CARD_CHANGED_GUID, + Source<WebDataService>(this), + Details<AutofillCreditCardChangeGUID>(&change)); } request->RequestComplete(); } @@ -1216,11 +1205,11 @@ void WebDataService::RemoveCreditCardGUIDImpl( void WebDataService::GetCreditCardsImpl(WebDataRequest* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - std::vector<CreditCard*> creditcards; - db_->GetCreditCards(&creditcards); + std::vector<CreditCard*> credit_cards; + db_->GetCreditCards(&credit_cards); request->SetResult( new WDResult<std::vector<CreditCard*> >(AUTOFILL_CREDITCARDS_RESULT, - creditcards)); + credit_cards)); } request->RequestComplete(); } diff --git a/chrome/browser/webdata/web_data_service.h b/chrome/browser/webdata/web_data_service.h index 48a41cc..b1fbfb3 100644 --- a/chrome/browser/webdata/web_data_service.h +++ b/chrome/browser/webdata/web_data_service.h @@ -131,7 +131,7 @@ class WDTypedResult { template <class T> class WDResult : public WDTypedResult { public: - WDResult(WDResultType type, T v) : WDTypedResult(type), value_(v) { + WDResult(WDResultType type, const T& v) : WDTypedResult(type), value_(v) { } virtual ~WDResult() { @@ -225,8 +225,9 @@ class WebDataService GenericRequest(WebDataService* service, Handle handle, WebDataServiceConsumer* consumer, - T arg) : WebDataRequest(service, handle, consumer), - arg_(arg) { + const T& arg) + : WebDataRequest(service, handle, consumer), + arg_(arg) { } virtual ~GenericRequest() { @@ -246,8 +247,8 @@ class WebDataService GenericRequest2(WebDataService* service, Handle handle, WebDataServiceConsumer* consumer, - T arg1, - U arg2) + const T& arg1, + const U& arg2) : WebDataRequest(service, handle, consumer), arg1_(arg1), arg2_(arg2) { @@ -442,20 +443,14 @@ class WebDataService const string16& value); // Schedules a task to add an AutoFill profile to the web database. - void AddAutoFillProfile(const AutoFillProfile& profile); + void AddAutoFillProfileGUID(const AutoFillProfile& profile); // Schedules a task to update an AutoFill profile in the web database. - void UpdateAutoFillProfile(const AutoFillProfile& profile); - - // Schedules a task to remove an AutoFill profile from the web database. - // |profile_id| is the unique ID of the profile to remove. - // DEPRECATED - // TODO(dhollowa): Remove unique IDs. http://crbug.com/58813 - void RemoveAutoFillProfile(int profile_id); + void UpdateAutoFillProfileGUID(const AutoFillProfile& profile); // Schedules a task to remove an AutoFill profile from the web database. // |guid| is the identifer of the profile to remove. - void RemoveAutoFillProfile(const std::string& guid); + void RemoveAutoFillProfileGUID(const std::string& guid); // Initiates the request for all AutoFill profiles. The method // OnWebDataServiceRequestDone of |consumer| gets called when the request is @@ -464,20 +459,14 @@ class WebDataService Handle GetAutoFillProfiles(WebDataServiceConsumer* consumer); // Schedules a task to add credit card to the web database. - void AddCreditCard(const CreditCard& creditcard); + void AddCreditCardGUID(const CreditCard& credit_card); // Schedules a task to update credit card in the web database. - void UpdateCreditCard(const CreditCard& creditcard); - - // Schedules a task to remove a credit card from the web database. - // |creditcard_id| is the unique ID of the credit card to remove. - // DEPRECATED - // TODO(dhollowa): Remove unique IDs. http://crbug.com/58813 - void RemoveCreditCard(int creditcard_id); + void UpdateCreditCardGUID(const CreditCard& credit_card); // Schedules a task to remove a credit card from the web database. // |guid| is identifer of the credit card to remove. - void RemoveCreditCard(const std::string& guid); + void RemoveCreditCardGUID(const std::string& guid); // Initiates the request for all credit cards. The method // OnWebDataServiceRequestDone of |consumer| gets called when the request is @@ -615,16 +604,12 @@ class WebDataService GenericRequest2<base::Time, base::Time>* request); void RemoveFormValueForElementNameImpl( GenericRequest2<string16, string16>* request); - void AddAutoFillProfileImpl(GenericRequest<AutoFillProfile>* request); - void UpdateAutoFillProfileImpl(GenericRequest<AutoFillProfile>* request); - // TODO(dhollowa): Remove unique IDs. http://crbug.com/58813 - void RemoveAutoFillProfileImpl(GenericRequest<int>* request); + void AddAutoFillProfileGUIDImpl(GenericRequest<AutoFillProfile>* request); + void UpdateAutoFillProfileGUIDImpl(GenericRequest<AutoFillProfile>* request); void RemoveAutoFillProfileGUIDImpl(GenericRequest<std::string>* request); void GetAutoFillProfilesImpl(WebDataRequest* request); - void AddCreditCardImpl(GenericRequest<CreditCard>* request); - void UpdateCreditCardImpl(GenericRequest<CreditCard>* request); - // TODO(dhollowa): Remove unique IDs. http://crbug.com/58813 - void RemoveCreditCardImpl(GenericRequest<int>* request); + void AddCreditCardGUIDImpl(GenericRequest<CreditCard>* request); + void UpdateCreditCardGUIDImpl(GenericRequest<CreditCard>* request); void RemoveCreditCardGUIDImpl(GenericRequest<std::string>* request); void GetCreditCardsImpl(WebDataRequest* request); void RemoveAutoFillProfilesAndCreditCardsModifiedBetweenImpl( diff --git a/chrome/browser/webdata/web_data_service_unittest.cc b/chrome/browser/webdata/web_data_service_unittest.cc index 81234a0..2b4401c 100644 --- a/chrome/browser/webdata/web_data_service_unittest.cc +++ b/chrome/browser/webdata/web_data_service_unittest.cc @@ -62,8 +62,14 @@ class AutofillDBThreadObserverHelper : public DBThreadObserverHelper { NotificationType::AUTOFILL_PROFILE_CHANGED, NotificationService::AllSources()); registrar_.Add(&observer_, + NotificationType::AUTOFILL_PROFILE_CHANGED_GUID, + NotificationService::AllSources()); + registrar_.Add(&observer_, NotificationType::AUTOFILL_CREDIT_CARD_CHANGED, NotificationService::AllSources()); + registrar_.Add(&observer_, + NotificationType::AUTOFILL_CREDIT_CARD_CHANGED_GUID, + NotificationService::AllSources()); } }; @@ -250,52 +256,57 @@ TEST_F(WebDataServiceAutofillTest, FormFillRemoveMany) { done_event_.TimedWait(test_timeout_); } -TEST_F(WebDataServiceAutofillTest, ProfileAdd) { - AutoFillProfile profile(name1_, unique_id1_); - const AutofillProfileChange expected_change( - AutofillProfileChange::ADD, name1_, &profile, string16()); +TEST_F(WebDataServiceAutofillTest, ProfileAddGUID) { + AutoFillProfile profile; + // TODO(dhollowa): Remove this notification. http://crbug.com/58813 + // Old Label-based notifications will be sent out until Sync can switch over + // to GUID-based notifications. + profile.set_label(name1_); + const AutofillProfileChange deprecated_expected_change( + AutofillProfileChange::ADD, name1_, &profile, string16()); EXPECT_CALL( *observer_helper_->observer(), Observe(NotificationType(NotificationType::AUTOFILL_PROFILE_CHANGED), Source<WebDataService>(wds_.get()), Property(&Details<const AutofillProfileChange>::ptr, - Pointee(expected_change)))). + Pointee(deprecated_expected_change)))). WillOnce(SignalEvent(&done_event_)); - wds_->AddAutoFillProfile(profile); - done_event_.TimedWait(test_timeout_); -} - -TEST_F(WebDataServiceAutofillTest, ProfileRemove) { - AutoFillProfile profile(name1_, unique_id1_); - - EXPECT_CALL(*observer_helper_->observer(), Observe(_, _, _)). - WillOnce(SignalEvent(&done_event_)); - wds_->AddAutoFillProfile(profile); - done_event_.TimedWait(test_timeout_); - - const AutofillProfileChange expected_change( - AutofillProfileChange::REMOVE, name1_, NULL, string16()); + // Check that GUID-based notification was sent. + const AutofillProfileChangeGUID expected_change( + AutofillProfileChangeGUID::ADD, profile.guid(), &profile); EXPECT_CALL( *observer_helper_->observer(), - Observe(NotificationType(NotificationType::AUTOFILL_PROFILE_CHANGED), + Observe(NotificationType(NotificationType::AUTOFILL_PROFILE_CHANGED_GUID), Source<WebDataService>(wds_.get()), - Property(&Details<const AutofillProfileChange>::ptr, + Property(&Details<const AutofillProfileChangeGUID>::ptr, Pointee(expected_change)))). - WillOnce(SignalEvent(&done_event_)); + WillOnce(DoDefault()); - wds_->RemoveAutoFillProfile(profile.unique_id()); + wds_->AddAutoFillProfileGUID(profile); done_event_.TimedWait(test_timeout_); + + // Check that it was added. + AutofillWebDataServiceConsumer<std::vector<AutoFillProfile*> > consumer; + WebDataService::Handle handle = wds_->GetAutoFillProfiles(&consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, consumer.handle()); + ASSERT_EQ(1U, consumer.result().size()); + EXPECT_EQ(profile, *consumer.result()[0]); + STLDeleteElements(&consumer.result()); } TEST_F(WebDataServiceAutofillTest, ProfileRemoveGUID) { - AutoFillProfile profile(name1_, unique_id1_); + AutoFillProfile profile; + profile.set_label(name1_); // Add a profile. EXPECT_CALL(*observer_helper_->observer(), Observe(_, _, _)). + Times(2). + WillOnce(DoDefault()). WillOnce(SignalEvent(&done_event_)); - wds_->AddAutoFillProfile(profile); + wds_->AddAutoFillProfileGUID(profile); done_event_.TimedWait(test_timeout_); // Check that it was added. @@ -307,17 +318,32 @@ TEST_F(WebDataServiceAutofillTest, ProfileRemoveGUID) { EXPECT_EQ(profile, *consumer.result()[0]); STLDeleteElements(&consumer.result()); - // Remove the profile. - const AutofillProfileChange expected_change( + // TODO(dhollowa): Remove this notification. http://crbug.com/58813 + // Old Label-based notifications will be sent out until Sync can switch over + // to GUID-based notifications. + const AutofillProfileChange deprecated_expected_change( AutofillProfileChange::REMOVE, name1_, NULL, string16()); EXPECT_CALL( *observer_helper_->observer(), Observe(NotificationType(NotificationType::AUTOFILL_PROFILE_CHANGED), Source<WebDataService>(wds_.get()), Property(&Details<const AutofillProfileChange>::ptr, - Pointee(expected_change)))). + Pointee(deprecated_expected_change)))). WillOnce(SignalEvent(&done_event_)); - wds_->RemoveAutoFillProfile(profile.guid()); + + // Check that GUID-based notification was sent. + const AutofillProfileChangeGUID expected_change( + AutofillProfileChangeGUID::REMOVE, profile.guid(), NULL); + EXPECT_CALL( + *observer_helper_->observer(), + Observe(NotificationType(NotificationType::AUTOFILL_PROFILE_CHANGED_GUID), + Source<WebDataService>(wds_.get()), + Property(&Details<const AutofillProfileChangeGUID>::ptr, + Pointee(expected_change)))). + WillOnce(DoDefault()); + + // Remove the profile. + wds_->RemoveAutoFillProfileGUID(profile.guid()); done_event_.TimedWait(test_timeout_); // Check that it was removed. @@ -328,83 +354,111 @@ TEST_F(WebDataServiceAutofillTest, ProfileRemoveGUID) { ASSERT_EQ(0U, consumer2.result().size()); } -TEST_F(WebDataServiceAutofillTest, ProfileUpdate) { - AutoFillProfile profile1(name1_, unique_id1_); - AutoFillProfile profile2(name2_, unique_id2_); +TEST_F(WebDataServiceAutofillTest, ProfileUpdateGUID) { + AutoFillProfile profile1; + profile1.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Abe")); + AutoFillProfile profile2; + profile2.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Alice")); EXPECT_CALL(*observer_helper_->observer(), Observe(_, _, _)). - Times(2). + WillOnce(DoDefault()). + WillOnce(DoDefault()). WillOnce(DoDefault()). WillOnce(SignalEvent(&done_event_)); - wds_->AddAutoFillProfile(profile1); - wds_->AddAutoFillProfile(profile2); - + wds_->AddAutoFillProfileGUID(profile1); + wds_->AddAutoFillProfileGUID(profile2); done_event_.TimedWait(test_timeout_); - AutoFillProfile profile1_delta(profile1); - string16 new_label(ASCIIToUTF16("new_label!")); - profile1_delta.set_label(new_label); - const AutofillProfileChange expected_change( - AutofillProfileChange::UPDATE, new_label, &profile1_delta, name1_); + // Check that they were added. + AutofillWebDataServiceConsumer<std::vector<AutoFillProfile*> > consumer; + WebDataService::Handle handle = wds_->GetAutoFillProfiles(&consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, consumer.handle()); + ASSERT_EQ(2U, consumer.result().size()); + EXPECT_EQ(profile1, *consumer.result()[0]); + EXPECT_EQ(profile2, *consumer.result()[1]); + STLDeleteElements(&consumer.result()); + // TODO(dhollowa): Remove this notification. http://crbug.com/58813 + // Old Label-based notifications will be sent out until Sync can switch over + // to GUID-based notifications. + AutoFillProfile deprecated_profile1_changed(profile1); + deprecated_profile1_changed.SetInfo(AutoFillType(NAME_FIRST), + ASCIIToUTF16("Bill")); + const AutofillProfileChangeGUID deprecated_expected_change( + AutofillProfileChangeGUID::UPDATE, profile1.guid(), + &deprecated_profile1_changed); EXPECT_CALL( *observer_helper_->observer(), Observe(NotificationType(NotificationType::AUTOFILL_PROFILE_CHANGED), Source<WebDataService>(wds_.get()), - Property(&Details<const AutofillProfileChange>::ptr, - Pointee(expected_change)))). + Property(&Details<const AutofillProfileChangeGUID>::ptr, + Pointee(deprecated_expected_change)))). WillOnce(SignalEvent(&done_event_)); - wds_->UpdateAutoFillProfile(profile1_delta); - done_event_.TimedWait(test_timeout_); -} - -TEST_F(WebDataServiceAutofillTest, CreditAdd) { - CreditCard card(name1_, unique_id1_); - const AutofillCreditCardChange expected_change( - AutofillCreditCardChange::ADD, name1_, &card); + AutoFillProfile profile1_changed(profile1); + profile1_changed.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("Bill")); + const AutofillProfileChangeGUID expected_change( + AutofillProfileChangeGUID::UPDATE, profile1.guid(), &profile1_changed); EXPECT_CALL( *observer_helper_->observer(), - Observe(NotificationType(NotificationType::AUTOFILL_CREDIT_CARD_CHANGED), + Observe(NotificationType(NotificationType::AUTOFILL_PROFILE_CHANGED_GUID), Source<WebDataService>(wds_.get()), - Property(&Details<const AutofillCreditCardChange>::ptr, + Property(&Details<const AutofillProfileChangeGUID>::ptr, Pointee(expected_change)))). - WillOnce(SignalEvent(&done_event_)); + WillOnce(DoDefault()); - wds_->AddCreditCard(card); + // Update the profile. + wds_->UpdateAutoFillProfileGUID(profile1_changed); done_event_.TimedWait(test_timeout_); -} -TEST_F(WebDataServiceAutofillTest, CreditRemove) { - CreditCard card(name1_, unique_id1_); - EXPECT_CALL(*observer_helper_->observer(), Observe(_, _, _)). - WillOnce(SignalEvent(&done_event_)); - wds_->AddCreditCard(card); - done_event_.TimedWait(test_timeout_); + // Check that the updates were made. + AutofillWebDataServiceConsumer<std::vector<AutoFillProfile*> > consumer2; + WebDataService::Handle handle2 = wds_->GetAutoFillProfiles(&consumer2); + MessageLoop::current()->Run(); + EXPECT_EQ(handle2, consumer2.handle()); + ASSERT_EQ(2U, consumer2.result().size()); + EXPECT_NE(profile1, *consumer2.result()[0]); + EXPECT_EQ(profile1_changed, *consumer2.result()[0]); + EXPECT_EQ(profile2, *consumer2.result()[1]); + STLDeleteElements(&consumer2.result()); +} - const AutofillCreditCardChange expected_change( - AutofillCreditCardChange::REMOVE, name1_, NULL); +TEST_F(WebDataServiceAutofillTest, CreditAddGUID) { + CreditCard card; + const AutofillCreditCardChangeGUID expected_change( + AutofillCreditCardChangeGUID::ADD, card.guid(), &card); EXPECT_CALL( *observer_helper_->observer(), - Observe(NotificationType(NotificationType::AUTOFILL_CREDIT_CARD_CHANGED), + Observe( + NotificationType(NotificationType::AUTOFILL_CREDIT_CARD_CHANGED_GUID), Source<WebDataService>(wds_.get()), - Property(&Details<const AutofillCreditCardChange>::ptr, + Property(&Details<const AutofillCreditCardChangeGUID>::ptr, Pointee(expected_change)))). WillOnce(SignalEvent(&done_event_)); - wds_->RemoveCreditCard(card.unique_id()); + wds_->AddCreditCardGUID(card); done_event_.TimedWait(test_timeout_); + + // Check that it was added. + AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer; + WebDataService::Handle handle = wds_->GetCreditCards(&consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, consumer.handle()); + ASSERT_EQ(1U, consumer.result().size()); + EXPECT_EQ(card, *consumer.result()[0]); + STLDeleteElements(&consumer.result()); } TEST_F(WebDataServiceAutofillTest, CreditCardRemoveGUID) { - CreditCard card(name1_, unique_id1_); + CreditCard credit_card; // Add a credit card. EXPECT_CALL(*observer_helper_->observer(), Observe(_, _, _)). WillOnce(SignalEvent(&done_event_)); - wds_->AddCreditCard(card); + wds_->AddCreditCardGUID(credit_card); done_event_.TimedWait(test_timeout_); // Check that it was added. @@ -413,20 +467,21 @@ TEST_F(WebDataServiceAutofillTest, CreditCardRemoveGUID) { MessageLoop::current()->Run(); EXPECT_EQ(handle, consumer.handle()); ASSERT_EQ(1U, consumer.result().size()); - EXPECT_EQ(card, *consumer.result()[0]); + EXPECT_EQ(credit_card, *consumer.result()[0]); STLDeleteElements(&consumer.result()); // Remove the credit card. - const AutofillCreditCardChange expected_change( - AutofillCreditCardChange::REMOVE, name1_, NULL); + const AutofillCreditCardChangeGUID expected_change( + AutofillCreditCardChangeGUID::REMOVE, credit_card.guid(), NULL); EXPECT_CALL( *observer_helper_->observer(), - Observe(NotificationType(NotificationType::AUTOFILL_CREDIT_CARD_CHANGED), + Observe( + NotificationType(NotificationType::AUTOFILL_CREDIT_CARD_CHANGED_GUID), Source<WebDataService>(wds_.get()), - Property(&Details<const AutofillCreditCardChange>::ptr, + Property(&Details<const AutofillCreditCardChangeGUID>::ptr, Pointee(expected_change)))). WillOnce(SignalEvent(&done_event_)); - wds_->RemoveCreditCard(card.guid()); + wds_->RemoveCreditCardGUID(credit_card.guid()); done_event_.TimedWait(test_timeout_); // Check that it was removed. @@ -437,31 +492,55 @@ TEST_F(WebDataServiceAutofillTest, CreditCardRemoveGUID) { ASSERT_EQ(0U, consumer2.result().size()); } -TEST_F(WebDataServiceAutofillTest, CreditUpdate) { - CreditCard card1(name1_, unique_id1_); - CreditCard card2(name2_, unique_id2_); +TEST_F(WebDataServiceAutofillTest, CreditUpdateGUID) { + CreditCard card1; + card1.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Abe")); + CreditCard card2; + card2.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Alice")); EXPECT_CALL(*observer_helper_->observer(), Observe(_, _, _)). Times(2). WillOnce(DoDefault()). WillOnce(SignalEvent(&done_event_)); - wds_->AddCreditCard(card1); - wds_->AddCreditCard(card2); + wds_->AddCreditCardGUID(card1); + wds_->AddCreditCardGUID(card2); done_event_.TimedWait(test_timeout_); - CreditCard card1_delta(card1); - card1_delta.set_label(ASCIIToUTF16("new_label!")); - const AutofillCreditCardChange expected_change( - AutofillCreditCardChange::UPDATE, name1_, &card1_delta); + // Check that they got added. + AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer; + WebDataService::Handle handle = wds_->GetCreditCards(&consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, consumer.handle()); + ASSERT_EQ(2U, consumer.result().size()); + EXPECT_EQ(card1, *consumer.result()[0]); + EXPECT_EQ(card2, *consumer.result()[1]); + STLDeleteElements(&consumer.result()); + + CreditCard card1_changed(card1); + card1_changed.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Bill")); + const AutofillCreditCardChangeGUID expected_change( + AutofillCreditCardChangeGUID::UPDATE, card1.guid(), &card1_changed); EXPECT_CALL( *observer_helper_->observer(), - Observe(NotificationType(NotificationType::AUTOFILL_CREDIT_CARD_CHANGED), + Observe( + NotificationType(NotificationType::AUTOFILL_CREDIT_CARD_CHANGED_GUID), Source<WebDataService>(wds_.get()), - Property(&Details<const AutofillCreditCardChange>::ptr, + Property(&Details<const AutofillCreditCardChangeGUID>::ptr, Pointee(expected_change)))). WillOnce(SignalEvent(&done_event_)); - wds_->UpdateCreditCard(card1_delta); + wds_->UpdateCreditCardGUID(card1_changed); done_event_.TimedWait(test_timeout_); + + // Check that the updates were made. + AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer2; + WebDataService::Handle handle2 = wds_->GetCreditCards(&consumer2); + MessageLoop::current()->Run(); + EXPECT_EQ(handle2, consumer2.handle()); + ASSERT_EQ(2U, consumer2.result().size()); + EXPECT_NE(card1, *consumer2.result()[0]); + EXPECT_EQ(card1_changed, *consumer2.result()[0]); + EXPECT_EQ(card2, *consumer2.result()[1]); + STLDeleteElements(&consumer2.result()); } diff --git a/chrome/browser/webdata/web_database.cc b/chrome/browser/webdata/web_database.cc index 138fc7c..2b9c029 100644 --- a/chrome/browser/webdata/web_database.cc +++ b/chrome/browser/webdata/web_database.cc @@ -109,9 +109,10 @@ using webkit_glue::PasswordForm; // user with the AutoFill dialog. Most of the columns are // standard entries in a contact information form. // +// guid A guid string to uniquely identify the profile. +// Added in version 31. // label The label of the profile. Presented to the user when // selecting profiles. -// unique_id The unique ID of this profile. // first_name // middle_name // last_name @@ -127,45 +128,21 @@ using webkit_glue::PasswordForm; // fax // date_modified The date on which this profile was last modified. // Added in version 30. -// guid A guid string to uniquely identify the profile. This -// will eventually replace the unique_id above. We need -// to keep both during the transition. -// Added in version 31. -// TODO(dhollowa): Deprecate and remove unique_id. -// http://crbug.com/58813 // // credit_cards This table contains credit card data added by the user // with the AutoFill dialog. Most of the columns are // standard entries in a credit card form. // +// guid A guid string to uniquely identify the profile. +// Added in version 31. // label The label of the credit card. Presented to the user // when selecting credit cards. -// unique_id The unique ID of this credit card. // name_on_card -// type -// card_number Before version 23 stores credit card number, 23 and -// after stores empty string. // expiration_month // expiration_year -// verification_code Before version 23 stores the CVC/CVV/CVV2 card security -// code. After that stores the empty string. -// billing_address A foreign key into the autofill_profiles table. -// shipping_address A foreign key into the autofill_profiles table. -// For the following two fields encryption is used. Currently it uses -// Encryptor, that does encryption on windows only. As on the other -// systems this file is readable by owner only, it is good for now. -// For potentially going over the wire other encryption is used, see -// chrome/browser/sync/protocol/autofill_specifics.proto // card_number_encrypted Stores encrypted credit card number. -// verification_code_encrypted The CVC/CVV/CVV2 card security code. // date_modified The date on which this entry was last modified. // Added in version 30. -// guid A guid string to uniquely identify the profile. This -// will eventually replace the unique_id above. We need -// to keep both during the transition. -// Added in version 31. -// TODO(dhollowa): Deprecate and remove unique_id. -// http://crbug.com/58813 // // web_app_icons // url URL of the web app. @@ -188,8 +165,8 @@ typedef std::vector<Tuple3<int64, string16, string16> > AutofillElementList; // Current version number. Note: when changing the current version number, // corresponding changes must happen in the unit tests, and new migration test // added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|. -const int kCurrentVersionNumber = 31; -const int kCompatibleVersionNumber = 31; +const int kCurrentVersionNumber = 32; +const int kCompatibleVersionNumber = 32; // ID of the url column in keywords. const int kUrlIdPosition = 16; @@ -278,8 +255,9 @@ string16 LimitDataSize(const string16& data) { void BindAutoFillProfileToStatement(const AutoFillProfile& profile, sql::Statement* s) { - s->BindString16(0, profile.Label()); - s->BindInt(1, profile.unique_id()); + DCHECK(guid::IsValidGUID(profile.guid())); + s->BindString(0, profile.guid()); + s->BindString16(1, profile.Label()); string16 text = profile.GetFieldText(AutoFillType(NAME_FIRST)); s->BindString16(2, LimitDataSize(text)); @@ -308,13 +286,14 @@ void BindAutoFillProfileToStatement(const AutoFillProfile& profile, text = profile.GetFieldText(AutoFillType(PHONE_FAX_WHOLE_NUMBER)); s->BindString16(14, LimitDataSize(text)); s->BindInt64(15, Time::Now().ToTimeT()); - DCHECK(guid::IsValidGUID(profile.guid())); - s->BindString(16, profile.guid()); } AutoFillProfile* AutoFillProfileFromStatement(const sql::Statement& s) { - AutoFillProfile* profile = new AutoFillProfile(s.ColumnString16(0), - s.ColumnInt(1)); + AutoFillProfile* profile = new AutoFillProfile; + profile->set_guid(s.ColumnString(0)); + DCHECK(guid::IsValidGUID(profile->guid())); + profile->set_label(s.ColumnString16(1)); + profile->SetInfo(AutoFillType(NAME_FIRST), s.ColumnString16(2)); profile->SetInfo(AutoFillType(NAME_MIDDLE), @@ -342,79 +321,53 @@ AutoFillProfile* AutoFillProfileFromStatement(const sql::Statement& s) { profile->SetInfo(AutoFillType(PHONE_FAX_WHOLE_NUMBER), s.ColumnString16(14)); // Intentionally skip column 15, which stores the profile's modification date. - profile->set_guid(s.ColumnString(16)); - DCHECK(guid::IsValidGUID(profile->guid())); return profile; } void BindCreditCardToStatement(const CreditCard& credit_card, sql::Statement* s) { - s->BindString16(0, credit_card.Label()); - s->BindInt(1, credit_card.unique_id()); + DCHECK(guid::IsValidGUID(credit_card.guid())); + s->BindString(0, credit_card.guid()); + s->BindString16(1, credit_card.Label()); string16 text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NAME)); s->BindString16(2, LimitDataSize(text)); - text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_TYPE)); - s->BindString16(3, LimitDataSize(text)); - text.clear(); // No unencrypted cc info. - s->BindString16(4, LimitDataSize(text)); text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_EXP_MONTH)); - s->BindString16(5, LimitDataSize(text)); + s->BindString16(3, LimitDataSize(text)); text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR)); - s->BindString16(6, LimitDataSize(text)); - text.clear(); - s->BindString16(7, LimitDataSize(text)); - s->BindInt(8, credit_card.billing_address_id()); - // We don't store the shipping address anymore. - text.clear(); - s->BindString16(9, LimitDataSize(text)); + s->BindString16(4, LimitDataSize(text)); text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NUMBER)); std::string encrypted_data; Encryptor::EncryptString16(text, &encrypted_data); - s->BindBlob(10, encrypted_data.data(), + s->BindBlob(5, encrypted_data.data(), static_cast<int>(encrypted_data.length())); - // We don't store the CVV anymore. - text.clear(); - s->BindBlob(11, text.data(), static_cast<int>(text.length())); - s->BindInt64(12, Time::Now().ToTimeT()); - DCHECK(guid::IsValidGUID(credit_card.guid())); - s->BindString(13, credit_card.guid()); + s->BindInt64(6, Time::Now().ToTimeT()); } CreditCard* CreditCardFromStatement(const sql::Statement& s) { - CreditCard* credit_card = new CreditCard(s.ColumnString16(0), s.ColumnInt(1)); + CreditCard* credit_card = new CreditCard; + + credit_card->set_guid(s.ColumnString(0)); + DCHECK(guid::IsValidGUID(credit_card->guid())); + credit_card->set_label(s.ColumnString16(1)); + credit_card->SetInfo(AutoFillType(CREDIT_CARD_NAME), s.ColumnString16(2)); - credit_card->SetInfo(AutoFillType(CREDIT_CARD_TYPE), + credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), s.ColumnString16(3)); - string16 credit_card_number = s.ColumnString16(4); - // It could be non-empty prior to version 23. After that it encrypted in - // the column 10. - if (credit_card_number.empty()) { - int encrypted_cc_len = s.ColumnByteLength(10); - std::string encrypted_cc; - if (encrypted_cc_len) { - encrypted_cc.resize(encrypted_cc_len); - memcpy(&encrypted_cc[0], s.ColumnBlob(10), encrypted_cc_len); - Encryptor::DecryptString16(encrypted_cc, &credit_card_number); - } + credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), + s.ColumnString16(4)); + int encrypted_number_len = s.ColumnByteLength(5); + string16 credit_card_number; + if (encrypted_number_len) { + std::string encrypted_number; + encrypted_number.resize(encrypted_number_len); + memcpy(&encrypted_number[0], s.ColumnBlob(5), encrypted_number_len); + Encryptor::DecryptString16(encrypted_number, &credit_card_number); } credit_card->SetInfo(AutoFillType(CREDIT_CARD_NUMBER), credit_card_number); - credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), - s.ColumnString16(5)); - credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), - s.ColumnString16(6)); - - string16 credit_card_verification_code = s.ColumnString16(7); - // We don't store the CVV anymore. - credit_card->set_billing_address_id(s.ColumnInt(8)); - // We don't store the shipping address anymore. - // Column 10 is processed above. - // We don't store the encrypted CVV anymore. - // Intentionally skip column 12, which stores the modification date. - credit_card->set_guid(s.ColumnString(13)); - DCHECK(guid::IsValidGUID(credit_card->guid())); + // Intentionally skip column 6, which stores the modification date. return credit_card; } @@ -766,8 +719,8 @@ bool WebDatabase::InitAutofillDatesTable() { bool WebDatabase::InitAutoFillProfilesTable() { if (!db_.DoesTableExist("autofill_profiles")) { if (!db_.Execute("CREATE TABLE autofill_profiles ( " + "guid VARCHAR PRIMARY KEY, " "label VARCHAR, " - "unique_id INTEGER PRIMARY KEY, " "first_name VARCHAR, " "middle_name VARCHAR, " "last_name VARCHAR, " @@ -781,8 +734,7 @@ bool WebDatabase::InitAutoFillProfilesTable() { "country VARCHAR, " "phone VARCHAR, " "fax VARCHAR, " - "date_modified INTEGER NOT NULL DEFAULT 0, " - "guid VARCHAR NOT NULL DEFAULT \"\")")) { + "date_modified INTEGER NOT NULL DEFAULT 0)")) { NOTREACHED(); return false; } @@ -798,20 +750,13 @@ bool WebDatabase::InitAutoFillProfilesTable() { bool WebDatabase::InitCreditCardsTable() { if (!db_.DoesTableExist("credit_cards")) { if (!db_.Execute("CREATE TABLE credit_cards ( " + "guid VARCHAR PRIMARY KEY, " "label VARCHAR, " - "unique_id INTEGER PRIMARY KEY, " "name_on_card VARCHAR, " - "type VARCHAR, " - "card_number VARCHAR, " "expiration_month INTEGER, " "expiration_year INTEGER, " - "verification_code VARCHAR, " - "billing_address VARCHAR, " - "shipping_address VARCHAR, " "card_number_encrypted BLOB, " - "verification_code_encrypted BLOB, " - "date_modified INTEGER NOT NULL DEFAULT 0, " - "guid VARCHAR NOT NULL DEFAULT \"\")")) { + "date_modified INTEGER NOT NULL DEFAULT 0)")) { NOTREACHED(); return false; } @@ -1659,10 +1604,10 @@ bool WebDatabase::RemoveFormElement(const string16& name, bool WebDatabase::AddAutoFillProfile(const AutoFillProfile& profile) { sql::Statement s(db_.GetUniqueStatement( "INSERT INTO autofill_profiles" - "(label, unique_id, first_name, middle_name, last_name, email," + "(guid, label, first_name, middle_name, last_name, email," " company_name, address_line_1, address_line_2, city, state, zipcode," - " country, phone, fax, date_modified, guid)" - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); + " country, phone, fax, date_modified)" + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; @@ -1682,9 +1627,9 @@ bool WebDatabase::GetAutoFillProfileForLabel(const string16& label, AutoFillProfile** profile) { DCHECK(profile); sql::Statement s(db_.GetUniqueStatement( - "SELECT label, unique_id, first_name, middle_name, last_name, email, " + "SELECT guid, label, first_name, middle_name, last_name, email, " "company_name, address_line_1, address_line_2, city, state, zipcode, " - "country, phone, fax, date_modified, guid " + "country, phone, fax, date_modified " "FROM autofill_profiles " "WHERE label = ?")); if (!s) { @@ -1706,9 +1651,9 @@ bool WebDatabase::GetAutoFillProfileForGUID(const std::string& guid, DCHECK(guid::IsValidGUID(guid)); DCHECK(profile); sql::Statement s(db_.GetUniqueStatement( - "SELECT label, unique_id, first_name, middle_name, last_name, email, " + "SELECT guid, label, first_name, middle_name, last_name, email, " "company_name, address_line_1, address_line_2, city, state, zipcode, " - "country, phone, fax, date_modified, guid " + "country, phone, fax, date_modified " "FROM autofill_profiles " "WHERE guid = ?")); if (!s) { @@ -1731,9 +1676,9 @@ bool WebDatabase::GetAutoFillProfiles( profiles->clear(); sql::Statement s(db_.GetUniqueStatement( - "SELECT label, unique_id, first_name, middle_name, last_name, email, " + "SELECT guid, label, first_name, middle_name, last_name, email, " "company_name, address_line_1, address_line_2, city, state, zipcode, " - "country, phone, fax, date_modified, guid " + "country, phone, fax, date_modified " "FROM autofill_profiles")); if (!s) { NOTREACHED() << "Statement prepare failed"; @@ -1747,39 +1692,26 @@ bool WebDatabase::GetAutoFillProfiles( } bool WebDatabase::UpdateAutoFillProfile(const AutoFillProfile& profile) { - DCHECK(profile.unique_id()); + DCHECK(guid::IsValidGUID(profile.guid())); sql::Statement s(db_.GetUniqueStatement( "UPDATE autofill_profiles " - "SET label=?, unique_id=?, first_name=?, middle_name=?, last_name=?, " + "SET guid=?, label=?, first_name=?, middle_name=?, last_name=?, " " email=?, company_name=?, address_line_1=?, address_line_2=?, " " city=?, state=?, zipcode=?, country=?, phone=?, fax=?, " - " date_modified=?, guid=? " - "WHERE unique_id=?")); + " date_modified=? " + "WHERE guid=?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } BindAutoFillProfileToStatement(profile, &s); - s.BindInt(17, profile.unique_id()); + s.BindString(16, profile.guid()); bool result = s.Run(); DCHECK_GT(db_.GetLastChangeCount(), 0); return result; } -bool WebDatabase::RemoveAutoFillProfile(int profile_id) { - DCHECK_NE(0, profile_id); - sql::Statement s(db_.GetUniqueStatement( - "DELETE FROM autofill_profiles WHERE unique_id = ?")); - if (!s) { - NOTREACHED() << "Statement prepare failed"; - return false; - } - - s.BindInt(0, profile_id); - return s.Run(); -} - bool WebDatabase::RemoveAutoFillProfile(const std::string& guid) { DCHECK(guid::IsValidGUID(guid)); sql::Statement s(db_.GetUniqueStatement( @@ -1793,34 +1725,12 @@ bool WebDatabase::RemoveAutoFillProfile(const std::string& guid) { return s.Run(); } -bool WebDatabase::GetAutoFillProfileForID(int profile_id, - AutoFillProfile** profile) { - sql::Statement s(db_.GetUniqueStatement( - "SELECT label, unique_id, first_name, middle_name, last_name, email, " - "company_name, address_line_1, address_line_2, city, state, zipcode, " - "country, phone, fax, date_modified, guid " - "FROM autofill_profiles " - "WHERE unique_id = ?")); - if (!s) { - NOTREACHED() << "Statement prepare failed"; - return false; - } - - s.BindInt(0, profile_id); - if (s.Step()) - *profile = AutoFillProfileFromStatement(s); - - return s.Succeeded(); -} - bool WebDatabase::AddCreditCard(const CreditCard& credit_card) { sql::Statement s(db_.GetUniqueStatement( "INSERT INTO credit_cards" - "(label, unique_id, name_on_card, type, card_number, expiration_month," - " expiration_year, verification_code, billing_address, shipping_address," - " card_number_encrypted, verification_code_encrypted, date_modified," - " guid)" - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); + "(guid, label, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified)" + "VALUES (?,?,?,?,?,?,?)")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; @@ -1841,10 +1751,8 @@ bool WebDatabase::GetCreditCardForLabel(const string16& label, CreditCard** credit_card) { DCHECK(credit_card); sql::Statement s(db_.GetUniqueStatement( - "SELECT label, unique_id, name_on_card, type, card_number, " - "expiration_month, expiration_year, verification_code, billing_address, " - "shipping_address, card_number_encrypted, verification_code_encrypted, " - "date_modified, guid " + "SELECT guid, label, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " "FROM credit_cards " "WHERE label = ?")); if (!s) { @@ -1861,37 +1769,12 @@ bool WebDatabase::GetCreditCardForLabel(const string16& label, return s.Succeeded(); } -bool WebDatabase::GetCreditCardForID(int credit_card_id, - CreditCard** credit_card) { - sql::Statement s(db_.GetUniqueStatement( - "SELECT label, unique_id, name_on_card, type, card_number, " - "expiration_month, expiration_year, verification_code, billing_address, " - "shipping_address, card_number_encrypted, verification_code_encrypted, " - "date_modified, guid " - "FROM credit_cards " - "WHERE unique_id = ?")); - if (!s) { - NOTREACHED() << "Statement prepare failed"; - return false; - } - - s.BindInt(0, credit_card_id); - if (!s.Step()) - return false; - - *credit_card = CreditCardFromStatement(s); - - return s.Succeeded(); -} - bool WebDatabase::GetCreditCardForGUID(const std::string& guid, CreditCard** credit_card) { DCHECK(guid::IsValidGUID(guid)); sql::Statement s(db_.GetUniqueStatement( - "SELECT label, unique_id, name_on_card, type, card_number, " - "expiration_month, expiration_year, verification_code, billing_address, " - "shipping_address, card_number_encrypted, verification_code_encrypted, " - "date_modified, guid " + "SELECT guid, label, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " "FROM credit_cards " "WHERE guid = ?")); if (!s) { @@ -1914,10 +1797,8 @@ bool WebDatabase::GetCreditCards( credit_cards->clear(); sql::Statement s(db_.GetUniqueStatement( - "SELECT label, unique_id, name_on_card, type, card_number, " - "expiration_month, expiration_year, verification_code, billing_address, " - "shipping_address, card_number_encrypted, verification_code_encrypted, " - "date_modified, guid " + "SELECT guid, label, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " "FROM credit_cards")); if (!s) { NOTREACHED() << "Statement prepare failed"; @@ -1931,39 +1812,24 @@ bool WebDatabase::GetCreditCards( } bool WebDatabase::UpdateCreditCard(const CreditCard& credit_card) { - DCHECK(credit_card.unique_id()); + DCHECK(guid::IsValidGUID(credit_card.guid())); sql::Statement s(db_.GetUniqueStatement( "UPDATE credit_cards " - "SET label=?, unique_id=?, name_on_card=?, type=?, card_number=?, " - " expiration_month=?, expiration_year=?, verification_code=?, " - " billing_address=?, shipping_address=?, card_number_encrypted=?, " - " verification_code_encrypted=?, date_modified=?, guid=?" - "WHERE unique_id=?")); + "SET guid=?, label=?, name_on_card=?, expiration_month=?, " + " expiration_year=?, card_number_encrypted=?, date_modified=? " + "WHERE guid=?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } BindCreditCardToStatement(credit_card, &s); - s.BindInt(14, credit_card.unique_id()); + s.BindString(7, credit_card.guid()); bool result = s.Run(); DCHECK_GT(db_.GetLastChangeCount(), 0); return result; } -bool WebDatabase::RemoveCreditCard(int credit_card_id) { - DCHECK_NE(0, credit_card_id); - sql::Statement s(db_.GetUniqueStatement( - "DELETE FROM credit_cards WHERE unique_id = ?")); - if (!s) { - NOTREACHED() << "Statement prepare failed"; - return false; - } - - s.BindInt(0, credit_card_id); - return s.Run(); -} - bool WebDatabase::RemoveCreditCard(const std::string& guid) { DCHECK(guid::IsValidGUID(guid)); sql::Statement s(db_.GetUniqueStatement( @@ -2193,13 +2059,23 @@ sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded(){ NOTREACHED(); return sql::INIT_FAILURE; } - query = "DELETE FROM credit_cards WHERE (" + credit_cards_is_too_big + - ") OR label IN (SELECT label FROM autofill_profiles WHERE " + - autofill_profiles_is_too_big + ")"; - if (!db_.Execute(query.c_str())) { - LOG(WARNING) << "Unable to update web database to version 24."; - NOTREACHED(); - return sql::INIT_FAILURE; + // Only delete from legacy credit card tables where specific columns + // exist. + if (db_.DoesColumnExist("credit_cards", "label") && + db_.DoesColumnExist("credit_cards", "name_on_card") && + db_.DoesColumnExist("credit_cards", "type") && + db_.DoesColumnExist("credit_cards", "expiration_month") && + db_.DoesColumnExist("credit_cards", "expiration_year") && + db_.DoesColumnExist("credit_cards", "billing_address") && + db_.DoesColumnExist("credit_cards", "shipping_address")) { + query = "DELETE FROM credit_cards WHERE (" + credit_cards_is_too_big + + ") OR label IN (SELECT label FROM autofill_profiles WHERE " + + autofill_profiles_is_too_big + ")"; + if (!db_.Execute(query.c_str())) { + LOG(WARNING) << "Unable to update web database to version 24."; + NOTREACHED(); + return sql::INIT_FAILURE; + } } query = "DELETE FROM autofill_profiles WHERE " + autofill_profiles_is_too_big; @@ -2242,30 +2118,19 @@ sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded(){ // FALL THROUGH case 26: { - // Change the credit_cards.billing_address column from a string to an int. - // The stored string is the label of an address, so we have to select the - // unique ID of this address using the label as a foreign key into the - // |autofill_profiles| table. - std::string stmt = - "SELECT credit_cards.unique_id, autofill_profiles.unique_id " - "FROM autofill_profiles, credit_cards " - "WHERE credit_cards.billing_address = autofill_profiles.label"; - sql::Statement s(db_.GetUniqueStatement(stmt.c_str())); - if (!s) { - LOG(WARNING) << "Statement prepare failed"; - NOTREACHED(); - return sql::INIT_FAILURE; - } - - std::map<int, int> cc_billing_map; - while (s.Step()) - cc_billing_map[s.ColumnInt(0)] = s.ColumnInt(1); - - // Windows already stores the IDs as strings in |billing_address|. Try to - // convert those. - if (cc_billing_map.empty()) { + // Only migrate from legacy credit card tables where specific columns + // exist. + if (db_.DoesColumnExist("credit_cards", "unique_id") && + db_.DoesColumnExist("credit_cards", "billing_address") && + db_.DoesColumnExist("autofill_profiles", "unique_id")) { + // Change the credit_cards.billing_address column from a string to an + // int. The stored string is the label of an address, so we have to + // select the unique ID of this address using the label as a foreign + // key into the |autofill_profiles| table. std::string stmt = - "SELECT unique_id,billing_address FROM credit_cards"; + "SELECT credit_cards.unique_id, autofill_profiles.unique_id " + "FROM autofill_profiles, credit_cards " + "WHERE credit_cards.billing_address = autofill_profiles.label"; sql::Statement s(db_.GetUniqueStatement(stmt.c_str())); if (!s) { LOG(WARNING) << "Statement prepare failed"; @@ -2273,74 +2138,91 @@ sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded(){ return sql::INIT_FAILURE; } - while (s.Step()) { - int id = 0; - if (base::StringToInt(s.ColumnString(1), &id)) - cc_billing_map[s.ColumnInt(0)] = id; - } - } - - if (!db_.Execute("CREATE TABLE credit_cards_temp ( " - "label VARCHAR, " - "unique_id INTEGER PRIMARY KEY, " - "name_on_card VARCHAR, " - "type VARCHAR, " - "card_number VARCHAR, " - "expiration_month INTEGER, " - "expiration_year INTEGER, " - "verification_code VARCHAR, " - "billing_address INTEGER, " - "shipping_address VARCHAR, " - "card_number_encrypted BLOB, " - "verification_code_encrypted BLOB)")) { - LOG(WARNING) << "Unable to update web database to version 27."; - NOTREACHED(); - return sql::INIT_FAILURE; - } - - if (!db_.Execute( - "INSERT INTO credit_cards_temp " - "SELECT label,unique_id,name_on_card,type,card_number," - "expiration_month,expiration_year,verification_code,0," - "shipping_address,card_number_encrypted,verification_code_encrypted " - "FROM credit_cards")) { - LOG(WARNING) << "Unable to update web database to version 27."; - NOTREACHED(); - return sql::INIT_FAILURE; - } + std::map<int, int> cc_billing_map; + while (s.Step()) + cc_billing_map[s.ColumnInt(0)] = s.ColumnInt(1); + + // Windows already stores the IDs as strings in |billing_address|. Try + // to convert those. + if (cc_billing_map.empty()) { + std::string stmt = + "SELECT unique_id,billing_address FROM credit_cards"; + sql::Statement s(db_.GetUniqueStatement(stmt.c_str())); + if (!s) { + LOG(WARNING) << "Statement prepare failed"; + NOTREACHED(); + return sql::INIT_FAILURE; + } - if (!db_.Execute("DROP TABLE credit_cards")) { - LOG(WARNING) << "Unable to update web database to version 27."; - NOTREACHED(); - return sql::INIT_FAILURE; - } + while (s.Step()) { + int id = 0; + if (base::StringToInt(s.ColumnString(1), &id)) + cc_billing_map[s.ColumnInt(0)] = id; + } + } - if (!db_.Execute( - "ALTER TABLE credit_cards_temp RENAME TO credit_cards")) { - LOG(WARNING) << "Unable to update web database to version 27."; - NOTREACHED(); - return sql::INIT_FAILURE; - } + if (!db_.Execute("CREATE TABLE credit_cards_temp ( " + "label VARCHAR, " + "unique_id INTEGER PRIMARY KEY, " + "name_on_card VARCHAR, " + "type VARCHAR, " + "card_number VARCHAR, " + "expiration_month INTEGER, " + "expiration_year INTEGER, " + "verification_code VARCHAR, " + "billing_address INTEGER, " + "shipping_address VARCHAR, " + "card_number_encrypted BLOB, " + "verification_code_encrypted BLOB)")) { + LOG(WARNING) << "Unable to update web database to version 27."; + NOTREACHED(); + return sql::INIT_FAILURE; + } - for (std::map<int, int>::const_iterator iter = cc_billing_map.begin(); - iter != cc_billing_map.end(); ++iter) { - sql::Statement s(db_.GetCachedStatement( - SQL_FROM_HERE, - "UPDATE credit_cards SET billing_address=? WHERE unique_id=?")); - if (!s) { - LOG(WARNING) << "Statement prepare failed"; + if (!db_.Execute( + "INSERT INTO credit_cards_temp " + "SELECT label,unique_id,name_on_card,type,card_number," + "expiration_month,expiration_year,verification_code,0," + "shipping_address,card_number_encrypted,verification_code_encrypted " + "FROM credit_cards")) { + LOG(WARNING) << "Unable to update web database to version 27."; NOTREACHED(); return sql::INIT_FAILURE; } - s.BindInt(0, (*iter).second); - s.BindInt(1, (*iter).first); + if (!db_.Execute("DROP TABLE credit_cards")) { + LOG(WARNING) << "Unable to update web database to version 27."; + NOTREACHED(); + return sql::INIT_FAILURE; + } - if (!s.Run()) { + if (!db_.Execute( + "ALTER TABLE credit_cards_temp RENAME TO credit_cards")) { LOG(WARNING) << "Unable to update web database to version 27."; NOTREACHED(); return sql::INIT_FAILURE; } + + for (std::map<int, int>::const_iterator iter = cc_billing_map.begin(); + iter != cc_billing_map.end(); ++iter) { + sql::Statement s(db_.GetCachedStatement( + SQL_FROM_HERE, + "UPDATE credit_cards SET billing_address=? WHERE unique_id=?")); + if (!s) { + LOG(WARNING) << "Statement prepare failed"; + NOTREACHED(); + return sql::INIT_FAILURE; + } + + s.BindInt(0, (*iter).second); + s.BindInt(1, (*iter).first); + + if (!s.Run()) { + LOG(WARNING) << "Unable to update web database to version 27."; + NOTREACHED(); + return sql::INIT_FAILURE; + } + } } meta_table_.SetVersionNumber(27); @@ -2498,35 +2380,35 @@ sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded(){ NOTREACHED(); return sql::INIT_FAILURE; } - } - - // Set all the |guid| fields to valid values. - { - sql::Statement s(db_.GetUniqueStatement("SELECT unique_id " - "FROM autofill_profiles")); - if (!s) { - LOG(WARNING) << "Unable to update web database to version 30."; - NOTREACHED(); - return sql::INIT_FAILURE; - } + // Set all the |guid| fields to valid values. + { + sql::Statement s(db_.GetUniqueStatement("SELECT unique_id " + "FROM autofill_profiles")); - while (s.Step()) { - sql::Statement update_s( - db_.GetUniqueStatement("UPDATE autofill_profiles " - "SET guid=? WHERE unique_id=?")); - if (!update_s) { + if (!s) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } - update_s.BindString(0, guid::GenerateGUID()); - update_s.BindInt(1, s.ColumnInt(0)); - if (!update_s.Run()) { - LOG(WARNING) << "Unable to update web database to version 30."; - NOTREACHED(); - return sql::INIT_FAILURE; + while (s.Step()) { + sql::Statement update_s( + db_.GetUniqueStatement("UPDATE autofill_profiles " + "SET guid=? WHERE unique_id=?")); + if (!update_s) { + LOG(WARNING) << "Unable to update web database to version 30."; + NOTREACHED(); + return sql::INIT_FAILURE; + } + update_s.BindString(0, guid::GenerateGUID()); + update_s.BindInt(1, s.ColumnInt(0)); + + if (!update_s.Run()) { + LOG(WARNING) << "Unable to update web database to version 30."; + NOTREACHED(); + return sql::INIT_FAILURE; + } } } } @@ -2542,34 +2424,34 @@ sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded(){ NOTREACHED(); return sql::INIT_FAILURE; } - } - // Set all the |guid| fields to valid values. - { - sql::Statement s(db_.GetUniqueStatement("SELECT unique_id " - "FROM credit_cards")); - if (!s) { - LOG(WARNING) << "Unable to update web database to version 30."; - NOTREACHED(); - return sql::INIT_FAILURE; - } - - while (s.Step()) { - sql::Statement update_s( - db_.GetUniqueStatement("UPDATE credit_cards " - "set guid=? WHERE unique_id=?")); - if (!update_s) { + // Set all the |guid| fields to valid values. + { + sql::Statement s(db_.GetUniqueStatement("SELECT unique_id " + "FROM credit_cards")); + if (!s) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } - update_s.BindString(0, guid::GenerateGUID()); - update_s.BindInt(1, s.ColumnInt(0)); - if (!update_s.Run()) { - LOG(WARNING) << "Unable to update web database to version 30."; - NOTREACHED(); - return sql::INIT_FAILURE; + while (s.Step()) { + sql::Statement update_s( + db_.GetUniqueStatement("UPDATE credit_cards " + "set guid=? WHERE unique_id=?")); + if (!update_s) { + LOG(WARNING) << "Unable to update web database to version 30."; + NOTREACHED(); + return sql::INIT_FAILURE; + } + update_s.BindString(0, guid::GenerateGUID()); + update_s.BindInt(1, s.ColumnInt(0)); + + if (!update_s.Run()) { + LOG(WARNING) << "Unable to update web database to version 30."; + NOTREACHED(); + return sql::INIT_FAILURE; + } } } } @@ -2580,6 +2462,99 @@ sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded(){ // FALL THROUGH + case 31: + if (db_.DoesColumnExist("autofill_profiles", "unique_id")) { + if (!db_.Execute("CREATE TABLE autofill_profiles_temp ( " + "guid VARCHAR PRIMARY KEY, " + "label VARCHAR, " + "first_name VARCHAR, " + "middle_name VARCHAR, " + "last_name VARCHAR, " + "email VARCHAR, " + "company_name VARCHAR, " + "address_line_1 VARCHAR, " + "address_line_2 VARCHAR, " + "city VARCHAR, " + "state VARCHAR, " + "zipcode VARCHAR, " + "country VARCHAR, " + "phone VARCHAR, " + "fax VARCHAR, " + "date_modified INTEGER NOT NULL DEFAULT 0)")) { + LOG(WARNING) << "Unable to update web database to version 32."; + NOTREACHED(); + return sql::INIT_FAILURE; + } + + if (!db_.Execute( + "INSERT INTO autofill_profiles_temp " + "SELECT guid, label, first_name, middle_name, last_name, email, " + "company_name, address_line_1, address_line_2, city, state, zipcode, " + "country, phone, fax, date_modified " + "FROM autofill_profiles")) { + LOG(WARNING) << "Unable to update web database to version 32."; + NOTREACHED(); + return sql::INIT_FAILURE; + } + + if (!db_.Execute("DROP TABLE autofill_profiles")) { + LOG(WARNING) << "Unable to update web database to version 32."; + NOTREACHED(); + return sql::INIT_FAILURE; + } + + if (!db_.Execute( + "ALTER TABLE autofill_profiles_temp RENAME TO autofill_profiles")) { + LOG(WARNING) << "Unable to update web database to version 32."; + NOTREACHED(); + return sql::INIT_FAILURE; + } + } + + if (db_.DoesColumnExist("credit_cards", "unique_id")) { + if (!db_.Execute("CREATE TABLE credit_cards_temp ( " + "guid VARCHAR PRIMARY KEY, " + "label VARCHAR, " + "name_on_card VARCHAR, " + "expiration_month INTEGER, " + "expiration_year INTEGER, " + "card_number_encrypted BLOB, " + "date_modified INTEGER NOT NULL DEFAULT 0)")) { + LOG(WARNING) << "Unable to update web database to version 32."; + NOTREACHED(); + return sql::INIT_FAILURE; + } + + if (!db_.Execute( + "INSERT INTO credit_cards_temp " + "SELECT guid, label, name_on_card, expiration_month, " + "expiration_year, card_number_encrypted, date_modified " + "FROM credit_cards")) { + LOG(WARNING) << "Unable to update web database to version 32."; + NOTREACHED(); + return sql::INIT_FAILURE; + } + + if (!db_.Execute("DROP TABLE credit_cards")) { + LOG(WARNING) << "Unable to update web database to version 32."; + NOTREACHED(); + return sql::INIT_FAILURE; + } + + if (!db_.Execute( + "ALTER TABLE credit_cards_temp RENAME TO credit_cards")) { + LOG(WARNING) << "Unable to update web database to version 32."; + NOTREACHED(); + return sql::INIT_FAILURE; + } + } + + meta_table_.SetVersionNumber(32); + meta_table_.SetCompatibleVersionNumber( + std::min(32, kCompatibleVersionNumber)); + + // FALL THROUGH + // Add successive versions here. Each should set the version number and // compatible version number as appropriate, then fall through to the next // case. diff --git a/chrome/browser/webdata/web_database.h b/chrome/browser/webdata/web_database.h index e7fd295..2d80437 100644 --- a/chrome/browser/webdata/web_database.h +++ b/chrome/browser/webdata/web_database.h @@ -226,21 +226,10 @@ class WebDatabase { // Updates the database values for the specified profile. virtual bool UpdateAutoFillProfile(const AutoFillProfile& profile); - // Removes a row from the autofill_profiles table. |profile_id| is the - // unique ID of the profile to remove. - // DEPRECATED: In favor of |RemoveAutoFillProfile(const std::string& guid)|. - // TODO(dhollowa): Remove unique IDs. http://crbug.com/58813 - virtual bool RemoveAutoFillProfile(int profile_id); - // Removes a row from the autofill_profiles table. |guid| is the identifier // of the profile to remove. virtual bool RemoveAutoFillProfile(const std::string& guid); - // Retrieves profile for unique id |profile_id|, owned by caller. - // DEPRECATED: In favor of |GetAutoFillProfileForGUID(...)|. - // TODO(dhollowa): Remove unique IDs. http://crbug.com/58813 - bool GetAutoFillProfileForID(int profile_id, AutoFillProfile** profile); - // Retrieves a profile with label |label|. The caller owns |profile|. // DEPRECATED: In favor of |GetAutoFillProfileForGUID(...)|. // TODO(dhollowa): Remove labels. http://crbug.com/58813 @@ -260,12 +249,6 @@ class WebDatabase { // Updates the database values for the specified credit card. bool UpdateCreditCard(const CreditCard& credit_card); - // Removes a row from the credit_cards table. |credit_card_id| is the - // unique ID of the credit card to remove. - // DEPRECATED: In favor of |RemoveCreditCard(const std::string& guid)|. - // TODO(dhollowa): Remove unique IDs. http://crbug.com/58813 - bool RemoveCreditCard(int credit_card_id); - // Removes a row from the credit_cards table. |guid| is the identifer of the // credit card to remove. bool RemoveCreditCard(const std::string& guid); @@ -277,11 +260,6 @@ class WebDatabase { bool GetCreditCardForLabel(const string16& label, CreditCard** credit_card); - // Retrieves credit card for a card with unique id |credit_card_id|. - // DEPRECATED: In favor of |GetCreditCardForGUID()|. - // TODO(dhollowa): Remove unique IDs. http://crbug.com/58813 - bool GetCreditCardForID(int credit_card_id, CreditCard** credit_card); - // Retrieves a credit card with guid |guid|. The caller owns // |credit_card_id|. bool GetCreditCardForGUID(const std::string& guid, CreditCard** credit_card); diff --git a/chrome/browser/webdata/web_database_unittest.cc b/chrome/browser/webdata/web_database_unittest.cc index f9df633..a49b7ee 100644 --- a/chrome/browser/webdata/web_database_unittest.cc +++ b/chrome/browser/webdata/web_database_unittest.cc @@ -94,6 +94,115 @@ bool CompareAutofillEntries(const AutofillEntry& a, const AutofillEntry& b) { return timestamps2.size() != 0U; } +void AutoFillProfile31FromStatement(const sql::Statement& s, + AutoFillProfile* profile, + string16* label, + int* unique_id, + int64* date_modified) { + DCHECK(profile); + DCHECK(label); + DCHECK(unique_id); + DCHECK(date_modified); + *label = s.ColumnString16(0); + *unique_id = s.ColumnInt(1); + profile->SetInfo(AutoFillType(NAME_FIRST), s.ColumnString16(2)); + profile->SetInfo(AutoFillType(NAME_MIDDLE), s.ColumnString16(3)); + profile->SetInfo(AutoFillType(NAME_LAST),s.ColumnString16(4)); + profile->SetInfo(AutoFillType(EMAIL_ADDRESS), s.ColumnString16(5)); + profile->SetInfo(AutoFillType(COMPANY_NAME), s.ColumnString16(6)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE1), s.ColumnString16(7)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE2), s.ColumnString16(8)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_CITY), s.ColumnString16(9)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_STATE), s.ColumnString16(10)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_ZIP), s.ColumnString16(11)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY), s.ColumnString16(12)); + profile->SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER), s.ColumnString16(13)); + profile->SetInfo(AutoFillType(PHONE_FAX_WHOLE_NUMBER), s.ColumnString16(14)); + *date_modified = s.ColumnInt64(15); + profile->set_guid(s.ColumnString(16)); + EXPECT_TRUE(guid::IsValidGUID(profile->guid())); +} + +void AutoFillProfile32FromStatement(const sql::Statement& s, + AutoFillProfile* profile, + string16* label, + int64* date_modified) { + DCHECK(profile); + DCHECK(label); + DCHECK(date_modified); + profile->set_guid(s.ColumnString(0)); + EXPECT_TRUE(guid::IsValidGUID(profile->guid())); + *label = s.ColumnString16(1); + profile->SetInfo(AutoFillType(NAME_FIRST), s.ColumnString16(2)); + profile->SetInfo(AutoFillType(NAME_MIDDLE), s.ColumnString16(3)); + profile->SetInfo(AutoFillType(NAME_LAST),s.ColumnString16(4)); + profile->SetInfo(AutoFillType(EMAIL_ADDRESS), s.ColumnString16(5)); + profile->SetInfo(AutoFillType(COMPANY_NAME), s.ColumnString16(6)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE1), s.ColumnString16(7)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE2), s.ColumnString16(8)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_CITY), s.ColumnString16(9)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_STATE), s.ColumnString16(10)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_ZIP), s.ColumnString16(11)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY), s.ColumnString16(12)); + profile->SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER), s.ColumnString16(13)); + profile->SetInfo(AutoFillType(PHONE_FAX_WHOLE_NUMBER), s.ColumnString16(14)); + *date_modified = s.ColumnInt64(15); +} + +void CreditCard31FromStatement(const sql::Statement& s, + CreditCard* credit_card, + string16* label, + int* unique_id, + std::string* encrypted_number, + int64* date_modified) { + DCHECK(credit_card); + DCHECK(label); + DCHECK(unique_id); + DCHECK(encrypted_number); + DCHECK(date_modified); + *label = s.ColumnString16(0); + *unique_id = s.ColumnInt(1); + credit_card->SetInfo(AutoFillType(CREDIT_CARD_NAME), s.ColumnString16(2)); + credit_card->SetInfo(AutoFillType(CREDIT_CARD_TYPE), s.ColumnString16(3)); + credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), + s.ColumnString16(5)); + credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), + s.ColumnString16(6)); + int encrypted_number_len = s.ColumnByteLength(10); + if (encrypted_number_len) { + encrypted_number->resize(encrypted_number_len); + memcpy(&(*encrypted_number)[0], s.ColumnBlob(10), encrypted_number_len); + } + *date_modified = s.ColumnInt64(12); + credit_card->set_guid(s.ColumnString(13)); + EXPECT_TRUE(guid::IsValidGUID(credit_card->guid())); +} + +void CreditCard32FromStatement(const sql::Statement& s, + CreditCard* credit_card, + string16* label, + std::string* encrypted_number, + int64* date_modified) { + DCHECK(credit_card); + DCHECK(label); + DCHECK(encrypted_number); + DCHECK(date_modified); + credit_card->set_guid(s.ColumnString(0)); + EXPECT_TRUE(guid::IsValidGUID(credit_card->guid())); + *label = s.ColumnString16(1); + credit_card->SetInfo(AutoFillType(CREDIT_CARD_NAME), s.ColumnString16(2)); + credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), + s.ColumnString16(3)); + credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), + s.ColumnString16(4)); + int encrypted_number_len = s.ColumnByteLength(5); + if (encrypted_number_len) { + encrypted_number->resize(encrypted_number_len); + memcpy(&(*encrypted_number)[0], s.ColumnBlob(5), encrypted_number_len); + } + *date_modified = s.ColumnInt64(6); +} + } // namespace class WebDatabaseTest : public testing::Test { @@ -1258,7 +1367,8 @@ TEST_F(WebDatabaseTest, AutoFillProfile) { ASSERT_EQ(sql::INIT_OK, db.Init(file_)); // Add a 'Home' profile. - AutoFillProfile home_profile(ASCIIToUTF16("Home"), 17); + AutoFillProfile home_profile; + home_profile.set_label(ASCIIToUTF16("Home")); home_profile.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("John")); home_profile.SetInfo(AutoFillType(NAME_MIDDLE), ASCIIToUTF16("Q.")); home_profile.SetInfo(AutoFillType(NAME_LAST), ASCIIToUTF16("Smith")); @@ -1288,18 +1398,18 @@ TEST_F(WebDatabaseTest, AutoFillProfile) { ASSERT_TRUE(db.GetAutoFillProfileForLabel(ASCIIToUTF16("Home"), &db_profile)); EXPECT_EQ(home_profile, *db_profile); sql::Statement s_home(db.db_.GetUniqueStatement( - "SELECT * FROM autofill_profiles WHERE label='Home'")); + "SELECT date_modified " + "FROM autofill_profiles WHERE label='Home'")); ASSERT_TRUE(s_home); ASSERT_TRUE(s_home.Step()); - EXPECT_GE(s_home.ColumnInt64(15), pre_creation_time.ToTimeT()); - EXPECT_LE(s_home.ColumnInt64(15), post_creation_time.ToTimeT()); + EXPECT_GE(s_home.ColumnInt64(0), pre_creation_time.ToTimeT()); + EXPECT_LE(s_home.ColumnInt64(0), post_creation_time.ToTimeT()); EXPECT_FALSE(s_home.Step()); delete db_profile; // Add a 'Billing' profile. AutoFillProfile billing_profile = home_profile; billing_profile.set_label(ASCIIToUTF16("Billing")); - billing_profile.set_unique_id(13); billing_profile.SetInfo(AutoFillType(ADDRESS_HOME_LINE1), ASCIIToUTF16("5678 Bottom Street")); billing_profile.SetInfo(AutoFillType(ADDRESS_HOME_LINE2), @@ -1315,11 +1425,11 @@ TEST_F(WebDatabaseTest, AutoFillProfile) { &db_profile)); EXPECT_EQ(billing_profile, *db_profile); sql::Statement s_billing(db.db_.GetUniqueStatement( - "SELECT * FROM autofill_profiles WHERE label='Billing'")); + "SELECT date_modified FROM autofill_profiles WHERE label='Billing'")); ASSERT_TRUE(s_billing); ASSERT_TRUE(s_billing.Step()); - EXPECT_GE(s_billing.ColumnInt64(15), pre_creation_time.ToTimeT()); - EXPECT_LE(s_billing.ColumnInt64(15), post_creation_time.ToTimeT()); + EXPECT_GE(s_billing.ColumnInt64(0), pre_creation_time.ToTimeT()); + EXPECT_LE(s_billing.ColumnInt64(0), post_creation_time.ToTimeT()); EXPECT_FALSE(s_billing.Step()); delete db_profile; @@ -1332,25 +1442,24 @@ TEST_F(WebDatabaseTest, AutoFillProfile) { &db_profile)); EXPECT_EQ(billing_profile, *db_profile); sql::Statement s_billing_updated(db.db_.GetUniqueStatement( - "SELECT * FROM autofill_profiles WHERE label='Billing'")); + "SELECT date_modified FROM autofill_profiles WHERE label='Billing'")); ASSERT_TRUE(s_billing_updated); ASSERT_TRUE(s_billing_updated.Step()); - EXPECT_GE(s_billing_updated.ColumnInt64(15), + EXPECT_GE(s_billing_updated.ColumnInt64(0), pre_modification_time.ToTimeT()); - EXPECT_LE(s_billing_updated.ColumnInt64(15), + EXPECT_LE(s_billing_updated.ColumnInt64(0), post_modification_time.ToTimeT()); EXPECT_FALSE(s_billing_updated.Step()); delete db_profile; // Remove the 'Billing' profile. - EXPECT_TRUE(db.RemoveAutoFillProfile(billing_profile.unique_id())); + EXPECT_TRUE(db.RemoveAutoFillProfile(billing_profile.guid())); EXPECT_FALSE(db.GetAutoFillProfileForLabel(ASCIIToUTF16("Billing"), &db_profile)); // Add a 'GUID' profile. AutoFillProfile guid_profile = home_profile; guid_profile.set_label(ASCIIToUTF16("GUID")); - guid_profile.set_unique_id(14); guid_profile.SetInfo(AutoFillType(ADDRESS_HOME_LINE1), ASCIIToUTF16("5678 Top Street")); guid_profile.SetInfo(AutoFillType(ADDRESS_HOME_LINE2), @@ -1383,18 +1492,16 @@ TEST_F(WebDatabaseTest, CreditCard) { ASSERT_EQ(sql::INIT_OK, db.Init(file_)); // Add a 'Work' credit card. - CreditCard work_creditcard(ASCIIToUTF16("Work"), 13); + CreditCard work_creditcard; + work_creditcard.set_label(ASCIIToUTF16("Work")); work_creditcard.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Jack Torrance")); - work_creditcard.SetInfo(AutoFillType(CREDIT_CARD_TYPE), - ASCIIToUTF16("Visa")); work_creditcard.SetInfo(AutoFillType(CREDIT_CARD_NUMBER), ASCIIToUTF16("1234567890123456")); work_creditcard.SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), ASCIIToUTF16("04")); work_creditcard.SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), ASCIIToUTF16("2013")); - work_creditcard.set_billing_address_id(1); Time pre_creation_time = Time::Now(); EXPECT_TRUE(db.AddCreditCard(work_creditcard)); @@ -1405,27 +1512,27 @@ TEST_F(WebDatabaseTest, CreditCard) { ASSERT_TRUE(db.GetCreditCardForLabel(ASCIIToUTF16("Work"), &db_creditcard)); EXPECT_EQ(work_creditcard, *db_creditcard); sql::Statement s_work(db.db_.GetUniqueStatement( - "SELECT * FROM credit_cards WHERE label='Work'")); + "SELECT guid, label, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " + "FROM credit_cards WHERE label='Work'")); ASSERT_TRUE(s_work); ASSERT_TRUE(s_work.Step()); - EXPECT_GE(s_work.ColumnInt64(12), pre_creation_time.ToTimeT()); - EXPECT_LE(s_work.ColumnInt64(12), post_creation_time.ToTimeT()); + EXPECT_GE(s_work.ColumnInt64(6), pre_creation_time.ToTimeT()); + EXPECT_LE(s_work.ColumnInt64(6), post_creation_time.ToTimeT()); EXPECT_FALSE(s_work.Step()); delete db_creditcard; // Add a 'Target' credit card. - CreditCard target_creditcard(ASCIIToUTF16("Target"), 7); + CreditCard target_creditcard; + target_creditcard.set_label(ASCIIToUTF16("Target")); target_creditcard.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Jack Torrance")); - target_creditcard.SetInfo(AutoFillType(CREDIT_CARD_TYPE), - ASCIIToUTF16("Mastercard")); target_creditcard.SetInfo(AutoFillType(CREDIT_CARD_NUMBER), ASCIIToUTF16("1111222233334444")); target_creditcard.SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), ASCIIToUTF16("06")); target_creditcard.SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), ASCIIToUTF16("2012")); - target_creditcard.set_billing_address_id(1); pre_creation_time = Time::Now(); EXPECT_TRUE(db.AddCreditCard(target_creditcard)); @@ -1434,11 +1541,13 @@ TEST_F(WebDatabaseTest, CreditCard) { &db_creditcard)); EXPECT_EQ(target_creditcard, *db_creditcard); sql::Statement s_target(db.db_.GetUniqueStatement( - "SELECT * FROM credit_cards WHERE label='Target'")); + "SELECT guid, label, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " + "FROM credit_cards WHERE label='Target'")); ASSERT_TRUE(s_target); ASSERT_TRUE(s_target.Step()); - EXPECT_GE(s_target.ColumnInt64(12), pre_creation_time.ToTimeT()); - EXPECT_LE(s_target.ColumnInt64(12), post_creation_time.ToTimeT()); + EXPECT_GE(s_target.ColumnInt64(6), pre_creation_time.ToTimeT()); + EXPECT_LE(s_target.ColumnInt64(6), post_creation_time.ToTimeT()); EXPECT_FALSE(s_target.Step()); delete db_creditcard; @@ -1451,34 +1560,34 @@ TEST_F(WebDatabaseTest, CreditCard) { ASSERT_TRUE(db.GetCreditCardForLabel(ASCIIToUTF16("Target"), &db_creditcard)); EXPECT_EQ(target_creditcard, *db_creditcard); sql::Statement s_target_updated(db.db_.GetUniqueStatement( - "SELECT * FROM credit_cards WHERE label='Target'")); + "SELECT guid, label, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " + "FROM credit_cards WHERE label='Target'")); ASSERT_TRUE(s_target_updated); ASSERT_TRUE(s_target_updated.Step()); - EXPECT_GE(s_target_updated.ColumnInt64(12), + EXPECT_GE(s_target_updated.ColumnInt64(6), pre_modification_time.ToTimeT()); - EXPECT_LE(s_target_updated.ColumnInt64(12), + EXPECT_LE(s_target_updated.ColumnInt64(6), post_modification_time.ToTimeT()); EXPECT_FALSE(s_target_updated.Step()); delete db_creditcard; // Remove the 'Target' credit card. - EXPECT_TRUE(db.RemoveCreditCard(target_creditcard.unique_id())); + EXPECT_TRUE(db.RemoveCreditCard(target_creditcard.guid())); EXPECT_FALSE(db.GetCreditCardForLabel(ASCIIToUTF16("Target"), &db_creditcard)); // Add a 'GUID' profile. - CreditCard guid_creditcard(ASCIIToUTF16("GUID"), 7); + CreditCard guid_creditcard; + guid_creditcard.set_label(ASCIIToUTF16("GUID")); guid_creditcard.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("Jimmy Jones")); - guid_creditcard.SetInfo(AutoFillType(CREDIT_CARD_TYPE), - ASCIIToUTF16("Amex")); guid_creditcard.SetInfo(AutoFillType(CREDIT_CARD_NUMBER), ASCIIToUTF16("9999222233334444")); guid_creditcard.SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), ASCIIToUTF16("07")); guid_creditcard.SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), ASCIIToUTF16("2013")); - guid_creditcard.set_billing_address_id(1); EXPECT_TRUE(db.AddCreditCard(guid_creditcard)); ASSERT_TRUE(db.GetCreditCardForGUID(guid_creditcard.guid(), @@ -1506,84 +1615,84 @@ TEST_F(WebDatabaseTest, RemoveAutoFillProfilesAndCreditCardsModifiedBetween) { // Populate the autofill_profiles and credit_cards tables. ASSERT_TRUE(db.db_.Execute( - "INSERT INTO \"autofill_profiles\" VALUES('P1',1,'','','','','','','',''," - "'','','','','',11,'00000000-0000-0000-0000-000000000000');" - "INSERT INTO \"autofill_profiles\" VALUES('P2',2,'','','','','','','',''," - "'','','','','',21,'00000000-0000-0000-0000-000000000001');" - "INSERT INTO \"autofill_profiles\" VALUES('P3',3,'','','','','','','',''," - "'','','','','',31,'00000000-0000-0000-0000-000000000002');" - "INSERT INTO \"autofill_profiles\" VALUES('P4',4,'','','','','','','',''," - "'','','','','',41,'00000000-0000-0000-0000-000000000003');" - "INSERT INTO \"autofill_profiles\" VALUES('P5',5,'','','','','','','',''," - "'','','','','',51,'00000000-0000-0000-0000-000000000004');" - "INSERT INTO \"autofill_profiles\" VALUES('P6',6,'','','','','','','',''," - "'','','','','',61,'00000000-0000-0000-0000-000000000005');" - "INSERT INTO \"credit_cards\" VALUES('C10',10,'','','',10,2010,'','',''," - "X'',X'',17,'00000000-0000-0000-0000-000000000006');" - "INSERT INTO \"credit_cards\" VALUES('C20',20,'','','',10,2010,'','',''," - "X'',X'',27,'00000000-0000-0000-0000-000000000007');" - "INSERT INTO \"credit_cards\" VALUES('C30',30,'','','',10,2010,'','',''," - "X'',X'',37,'00000000-0000-0000-0000-000000000008');" - "INSERT INTO \"credit_cards\" VALUES('C40',40,'','','',10,2010,'','',''," - "X'',X'',47,'00000000-0000-0000-0000-000000000009');" - "INSERT INTO \"credit_cards\" VALUES('C50',50,'','','',10,2010,'','',''," - "X'',X'',57,'00000000-0000-0000-0000-000000000010');" - "INSERT INTO \"credit_cards\" VALUES('C60',60,'','','',10,2010,'','',''," - "X'',X'',67,'00000000-0000-0000-0000-000000000011');")); + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000000', 11);" + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000001', 21);" + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000002', 31);" + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000003', 41);" + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000004', 51);" + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000005', 61);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000006', 17);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000007', 27);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000008', 37);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000009', 47);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000010', 57);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000011', 67);")); // Remove all entries modified in the bounded time range [17,41). db.RemoveAutoFillProfilesAndCreditCardsModifiedBetween( base::Time::FromTimeT(17), base::Time::FromTimeT(41)); sql::Statement s_autofill_profiles_bounded(db.db_.GetUniqueStatement( - "SELECT * FROM autofill_profiles")); + "SELECT date_modified FROM autofill_profiles")); ASSERT_TRUE(s_autofill_profiles_bounded); ASSERT_TRUE(s_autofill_profiles_bounded.Step()); - EXPECT_EQ(11, s_autofill_profiles_bounded.ColumnInt64(15)); + EXPECT_EQ(11, s_autofill_profiles_bounded.ColumnInt64(0)); ASSERT_TRUE(s_autofill_profiles_bounded.Step()); - EXPECT_EQ(41, s_autofill_profiles_bounded.ColumnInt64(15)); + EXPECT_EQ(41, s_autofill_profiles_bounded.ColumnInt64(0)); ASSERT_TRUE(s_autofill_profiles_bounded.Step()); - EXPECT_EQ(51, s_autofill_profiles_bounded.ColumnInt64(15)); + EXPECT_EQ(51, s_autofill_profiles_bounded.ColumnInt64(0)); ASSERT_TRUE(s_autofill_profiles_bounded.Step()); - EXPECT_EQ(61, s_autofill_profiles_bounded.ColumnInt64(15)); + EXPECT_EQ(61, s_autofill_profiles_bounded.ColumnInt64(0)); EXPECT_FALSE(s_autofill_profiles_bounded.Step()); sql::Statement s_credit_cards_bounded(db.db_.GetUniqueStatement( - "SELECT * FROM credit_cards")); + "SELECT date_modified FROM credit_cards")); ASSERT_TRUE(s_credit_cards_bounded); ASSERT_TRUE(s_credit_cards_bounded.Step()); - EXPECT_EQ(47, s_credit_cards_bounded.ColumnInt64(12)); + EXPECT_EQ(47, s_credit_cards_bounded.ColumnInt64(0)); ASSERT_TRUE(s_credit_cards_bounded.Step()); - EXPECT_EQ(57, s_credit_cards_bounded.ColumnInt64(12)); + EXPECT_EQ(57, s_credit_cards_bounded.ColumnInt64(0)); ASSERT_TRUE(s_credit_cards_bounded.Step()); - EXPECT_EQ(67, s_credit_cards_bounded.ColumnInt64(12)); + EXPECT_EQ(67, s_credit_cards_bounded.ColumnInt64(0)); EXPECT_FALSE(s_credit_cards_bounded.Step()); // Remove all entries modified on or after time 51 (unbounded range). db.RemoveAutoFillProfilesAndCreditCardsModifiedBetween( base::Time::FromTimeT(51), base::Time()); sql::Statement s_autofill_profiles_unbounded(db.db_.GetUniqueStatement( - "SELECT * FROM autofill_profiles")); + "SELECT date_modified FROM autofill_profiles")); ASSERT_TRUE(s_autofill_profiles_unbounded); ASSERT_TRUE(s_autofill_profiles_unbounded.Step()); - EXPECT_EQ(11, s_autofill_profiles_unbounded.ColumnInt64(15)); + EXPECT_EQ(11, s_autofill_profiles_unbounded.ColumnInt64(0)); ASSERT_TRUE(s_autofill_profiles_unbounded.Step()); - EXPECT_EQ(41, s_autofill_profiles_unbounded.ColumnInt64(15)); + EXPECT_EQ(41, s_autofill_profiles_unbounded.ColumnInt64(0)); EXPECT_FALSE(s_autofill_profiles_unbounded.Step()); sql::Statement s_credit_cards_unbounded(db.db_.GetUniqueStatement( - "SELECT * FROM credit_cards")); + "SELECT date_modified FROM credit_cards")); ASSERT_TRUE(s_credit_cards_unbounded); ASSERT_TRUE(s_credit_cards_unbounded.Step()); - EXPECT_EQ(47, s_credit_cards_unbounded.ColumnInt64(12)); + EXPECT_EQ(47, s_credit_cards_unbounded.ColumnInt64(0)); EXPECT_FALSE(s_credit_cards_unbounded.Step()); // Remove all remaining entries. db.RemoveAutoFillProfilesAndCreditCardsModifiedBetween(base::Time(), base::Time()); sql::Statement s_autofill_profiles_empty(db.db_.GetUniqueStatement( - "SELECT * FROM autofill_profiles")); + "SELECT date_modified FROM autofill_profiles")); ASSERT_TRUE(s_autofill_profiles_empty); EXPECT_FALSE(s_autofill_profiles_empty.Step()); sql::Statement s_credit_cards_empty(db.db_.GetUniqueStatement( - "SELECT * FROM credit_cards")); + "SELECT date_modified FROM credit_cards")); ASSERT_TRUE(s_credit_cards_empty); EXPECT_FALSE(s_credit_cards_empty.Step()); } @@ -1816,7 +1925,7 @@ class WebDatabaseMigrationTest : public testing::Test { DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest); }; -const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 31; +const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 32; void WebDatabaseMigrationTest::LoadDatabase(const FilePath& file) { std::string contents; @@ -1934,7 +2043,7 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion22ToCurrent) { ASSERT_TRUE(connection.Open(GetDatabasePath())); // No |credit_card| table prior to version 23. - ASSERT_FALSE(connection.DoesColumnExist("credit_cards", "unique_id")); + ASSERT_FALSE(connection.DoesColumnExist("credit_cards", "guid")); ASSERT_FALSE( connection.DoesColumnExist("credit_cards", "card_number_encrypted")); } @@ -1956,7 +2065,7 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion22ToCurrent) { EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); // |credit_card| table now exists. - EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "unique_id")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid")); EXPECT_TRUE( connection.DoesColumnExist("credit_cards", "card_number_encrypted")); } @@ -2006,7 +2115,8 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion22CorruptedToCurrent) { // Columns existing and not existing before version 25. - EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "unique_id")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "unique_id")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid")); EXPECT_TRUE( connection.DoesColumnExist("credit_cards", "card_number_encrypted")); EXPECT_TRUE(connection.DoesColumnExist("keywords", "id")); @@ -2149,24 +2259,20 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion26ToCurrentStringLabels) { // Check version. EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); - EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "billing_address")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "billing_address")); - // |billing_address| is an integer. Also Verify the credit card data is - // converted. - std::string stmt = "SELECT * FROM credit_cards"; - sql::Statement s(connection.GetUniqueStatement(stmt.c_str())); + // Verify the credit card data is converted. + sql::Statement s(connection.GetUniqueStatement( + "SELECT guid, label, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " + "FROM credit_cards")); ASSERT_TRUE(s.Step()); - EXPECT_EQ(s.ColumnType(8), sql::COLUMN_TYPE_INTEGER); - EXPECT_EQ("label", s.ColumnString(0)); - EXPECT_EQ(2, s.ColumnInt(1)); + EXPECT_EQ("label", s.ColumnString(1)); EXPECT_EQ("Jack", s.ColumnString(2)); - EXPECT_EQ("Visa", s.ColumnString(3)); - EXPECT_EQ("1234", s.ColumnString(4)); - EXPECT_EQ(2, s.ColumnInt(5)); - EXPECT_EQ(2012, s.ColumnInt(6)); - EXPECT_EQ(std::string(), s.ColumnString(7)); - EXPECT_EQ(1, s.ColumnInt(8)); - // The remaining columns are unused or blobs. + EXPECT_EQ(2, s.ColumnInt(3)); + EXPECT_EQ(2012, s.ColumnInt(4)); + // Column 5 is encrypted number blob. + // Column 6 is date_modified. } } @@ -2229,24 +2335,20 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion26ToCurrentStringIDs) { // |keywords| |logo_id| column should have been added. EXPECT_TRUE(connection.DoesColumnExist("keywords", "id")); EXPECT_TRUE(connection.DoesColumnExist("keywords", "created_by_policy")); - EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "billing_address")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "billing_address")); - // |billing_address| is an integer. Also Verify the credit card data is - // converted. - std::string stmt = "SELECT * FROM credit_cards"; - sql::Statement s(connection.GetUniqueStatement(stmt.c_str())); + // Verify the credit card data is converted. + sql::Statement s(connection.GetUniqueStatement( + "SELECT guid, label, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " + "FROM credit_cards")); ASSERT_TRUE(s.Step()); - EXPECT_EQ(s.ColumnType(8), sql::COLUMN_TYPE_INTEGER); - EXPECT_EQ("label", s.ColumnString(0)); - EXPECT_EQ(2, s.ColumnInt(1)); + EXPECT_EQ("label", s.ColumnString(1)); EXPECT_EQ("Jack", s.ColumnString(2)); - EXPECT_EQ("Visa", s.ColumnString(3)); - EXPECT_EQ("1234", s.ColumnString(4)); - EXPECT_EQ(2, s.ColumnInt(5)); - EXPECT_EQ(2012, s.ColumnInt(6)); - EXPECT_EQ(std::string(), s.ColumnString(7)); - EXPECT_EQ(1, s.ColumnInt(8)); - // The remaining columns are unused or blobs. + EXPECT_EQ(2, s.ColumnInt(3)); + EXPECT_EQ(2012, s.ColumnInt(4)); + // Column 5 is encrypted credit card number blob. + // Column 6 is date_modified. } } @@ -2333,23 +2435,23 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion29ToCurrent) { "date_modified")); sql::Statement s_profiles(connection.GetUniqueStatement( - "SELECT * FROM autofill_profiles ")); + "SELECT date_modified FROM autofill_profiles ")); ASSERT_TRUE(s_profiles); while (s_profiles.Step()) { - EXPECT_GE(s_profiles.ColumnInt64(15), + EXPECT_GE(s_profiles.ColumnInt64(0), pre_creation_time.ToTimeT()); - EXPECT_LE(s_profiles.ColumnInt64(15), + EXPECT_LE(s_profiles.ColumnInt64(0), post_creation_time.ToTimeT()); } EXPECT_TRUE(s_profiles.Succeeded()); sql::Statement s_credit_cards(connection.GetUniqueStatement( - "SELECT * FROM credit_cards ")); + "SELECT date_modified FROM credit_cards ")); ASSERT_TRUE(s_credit_cards); while (s_credit_cards.Step()) { - EXPECT_GE(s_credit_cards.ColumnInt64(12), + EXPECT_GE(s_credit_cards.ColumnInt64(0), pre_creation_time.ToTimeT()); - EXPECT_LE(s_credit_cards.ColumnInt64(12), + EXPECT_LE(s_credit_cards.ColumnInt64(0), post_creation_time.ToTimeT()); } EXPECT_TRUE(s_credit_cards.Succeeded()); @@ -2407,3 +2509,142 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion30ToCurrent) { EXPECT_NE(guid1, guid2); } } + +// Removes unique IDs and make GUIDs the primary key. Also removes unused +// columns. +TEST_F(WebDatabaseMigrationTest, MigrateVersion31ToCurrent) { + // Initialize the database. + ASSERT_NO_FATAL_FAILURE( + LoadDatabase(FilePath(FILE_PATH_LITERAL("version_31.sql")))); + + // Verify pre-conditions. These are expectations for version 30 of the + // database. + AutoFillProfile profile; + string16 profile_label; + int profile_unique_id = 0; + int64 profile_date_modified = 0; + CreditCard credit_card; + string16 cc_label; + int cc_unique_id = 0; + std::string cc_number_encrypted; + int64 cc_date_modified = 0; + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Verify existence of columns we'll be changing. + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "unique_id")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "unique_id")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "type")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "card_number")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", + "verification_code")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "billing_address")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "shipping_address")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", + "verification_code_encrypted")); + + // Fetch data in the database prior to migration. + sql::Statement s1( + connection.GetUniqueStatement( + "SELECT label, unique_id, first_name, middle_name, last_name, " + "email, company_name, address_line_1, address_line_2, city, state, " + "zipcode, country, phone, fax, date_modified, guid " + "FROM autofill_profiles")); + ASSERT_TRUE(s1.Step()); + EXPECT_NO_FATAL_FAILURE(AutoFillProfile31FromStatement( + s1, &profile, &profile_label, &profile_unique_id, + &profile_date_modified)); + + sql::Statement s2( + connection.GetUniqueStatement( + "SELECT label, unique_id, name_on_card, type, card_number, " + "expiration_month, expiration_year, verification_code, " + "billing_address, shipping_address, card_number_encrypted, " + "verification_code_encrypted, date_modified, guid " + "FROM credit_cards")); + ASSERT_TRUE(s2.Step()); + EXPECT_NO_FATAL_FAILURE(CreditCard31FromStatement(s2, + &credit_card, + &cc_label, + &cc_unique_id, + &cc_number_encrypted, + &cc_date_modified)); + + EXPECT_NE(profile_unique_id, cc_unique_id); + EXPECT_NE(profile.guid(), credit_card.guid()); + } + + // Load the database via the WebDatabase class and migrate the database to + // the current version. + { + WebDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(GetDatabasePath())); + } + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // Verify existence of columns we'll be changing. + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid")); + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "unique_id")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "unique_id")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "type")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "card_number")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", + "verification_code")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "billing_address")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", + "shipping_address")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", + "verification_code_encrypted")); + + // Verify data in the database after the migration. + sql::Statement s1( + connection.GetUniqueStatement( + "SELECT guid, label, first_name, middle_name, last_name, " + "email, company_name, address_line_1, address_line_2, city, state, " + "zipcode, country, phone, fax, date_modified " + "FROM autofill_profiles")); + ASSERT_TRUE(s1.Step()); + + AutoFillProfile profile_a; + string16 profile_label_a; + int64 profile_date_modified_a = 0; + EXPECT_NO_FATAL_FAILURE(AutoFillProfile32FromStatement( + s1, &profile_a, &profile_label_a, &profile_date_modified_a)); + EXPECT_EQ(profile, profile_a); + EXPECT_EQ(profile_label, profile_label_a); + EXPECT_EQ(profile_date_modified, profile_date_modified_a); + + sql::Statement s2( + connection.GetUniqueStatement( + "SELECT guid, label, name_on_card, expiration_month, " + "expiration_year, card_number_encrypted, date_modified " + "FROM credit_cards")); + ASSERT_TRUE(s2.Step()); + + CreditCard credit_card_a; + string16 cc_label_a; + std::string cc_number_encrypted_a; + int64 cc_date_modified_a = 0; + EXPECT_NO_FATAL_FAILURE(CreditCard32FromStatement(s2, + &credit_card_a, + &cc_label_a, + &cc_number_encrypted_a, + &cc_date_modified_a)); + EXPECT_EQ(credit_card, credit_card_a); + EXPECT_EQ(cc_label, cc_label_a); + EXPECT_EQ(cc_number_encrypted, cc_number_encrypted_a); + EXPECT_EQ(cc_date_modified, cc_date_modified_a); + } +} diff --git a/chrome/browser/worker_host/message_port_dispatcher.cc b/chrome/browser/worker_host/message_port_dispatcher.cc index edd4a94..49c87a0 100644 --- a/chrome/browser/worker_host/message_port_dispatcher.cc +++ b/chrome/browser/worker_host/message_port_dispatcher.cc @@ -12,6 +12,21 @@ #include "chrome/common/notification_service.h" #include "chrome/common/worker_messages.h" +struct MessagePortDispatcher::MessagePort { + // sender and route_id are what we need to send messages to the port. + IPC::Message::Sender* sender; + int route_id; + // A function pointer to generate a new route id for the sender above. + // Owned by "sender" above, so don't delete. + CallbackWithReturnValue<int>::Type* next_routing_id; + // A globally unique id for this message port. + int message_port_id; + // The globally unique id of the entangled message port. + int entangled_message_port_id; + // If true, all messages to this message port are queued and not delivered. + bool queue_messages; + QueuedMessages queued_messages; +}; MessagePortDispatcher* MessagePortDispatcher::GetInstance() { return Singleton<MessagePortDispatcher>::get(); diff --git a/chrome/browser/worker_host/message_port_dispatcher.h b/chrome/browser/worker_host/message_port_dispatcher.h index b8f7021..566416b 100644 --- a/chrome/browser/worker_host/message_port_dispatcher.h +++ b/chrome/browser/worker_host/message_port_dispatcher.h @@ -74,22 +74,7 @@ class MessagePortDispatcher : public NotificationObserver { // verify that the message port id exists. void Erase(int message_port_id); - struct MessagePort { - // sender and route_id are what we need to send messages to the port. - IPC::Message::Sender* sender; - int route_id; - // A function pointer to generate a new route id for the sender above. - // Owned by "sender" above, so don't delete. - CallbackWithReturnValue<int>::Type* next_routing_id; - // A globally unique id for this message port. - int message_port_id; - // The globally unique id of the entangled message port. - int entangled_message_port_id; - // If true, all messages to this message port are queued and not delivered. - bool queue_messages; - QueuedMessages queued_messages; - }; - + struct MessagePort; typedef std::map<int, MessagePort> MessagePorts; MessagePorts message_ports_; diff --git a/chrome/browser/worker_host/worker_document_set.h b/chrome/browser/worker_host/worker_document_set.h index a73a9c9..7894fc4 100644 --- a/chrome/browser/worker_host/worker_document_set.h +++ b/chrome/browser/worker_host/worker_document_set.h @@ -32,7 +32,7 @@ class WorkerDocumentSet : public base::RefCounted<WorkerDocumentSet> { // Define operator "<", which is used to determine uniqueness within // the set. - bool operator <(DocumentInfo other) const { + bool operator <(const DocumentInfo& other) const { // Items are identical if the sender and document_id are identical, // otherwise create an arbitrary stable ordering based on the document // id/sender. diff --git a/chrome/browser/worker_host/worker_process_host.cc b/chrome/browser/worker_host/worker_process_host.cc index c8df23d..713e6db 100644 --- a/chrome/browser/worker_host/worker_process_host.cc +++ b/chrome/browser/worker_host/worker_process_host.cc @@ -9,7 +9,6 @@ #include "base/callback.h" #include "base/command_line.h" -#include "base/debug_util.h" #include "base/message_loop.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" @@ -37,6 +36,7 @@ #include "net/base/mime_util.h" #include "ipc/ipc_switches.h" #include "net/base/registry_controlled_domain.h" +#include "webkit/fileapi/file_system_path_manager.h" // Notifies RenderViewHost that one or more worker objects crashed. class WorkerCrashTask : public Task { @@ -184,6 +184,28 @@ bool WorkerProcessHost::Init() { cmd_line); ChildProcessSecurityPolicy::GetInstance()->Add(id()); + if (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableFileSystem)) { + // Grant most file permissions to this worker. + // PLATFORM_FILE_TEMPORARY, PLATFORM_FILE_HIDDEN and + // PLATFORM_FILE_DELETE_ON_CLOSE are not granted, because no existing API + // requests them. + ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile( + id(), + request_context_->file_system_host_context()-> + path_manager()->base_path(), + base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_CREATE | + base::PLATFORM_FILE_OPEN_ALWAYS | + base::PLATFORM_FILE_CREATE_ALWAYS | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_EXCLUSIVE_READ | + base::PLATFORM_FILE_EXCLUSIVE_WRITE | + base::PLATFORM_FILE_ASYNC | + base::PLATFORM_FILE_TRUNCATE | + base::PLATFORM_FILE_WRITE_ATTRIBUTES); + } return true; } diff --git a/chrome/browser/wrench_menu_model.cc b/chrome/browser/wrench_menu_model.cc index d308a4d..cb7bace 100644 --- a/chrome/browser/wrench_menu_model.cc +++ b/chrome/browser/wrench_menu_model.cc @@ -14,7 +14,7 @@ #include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/defaults.h" @@ -44,6 +44,15 @@ #include "chrome/browser/browser_window.h" #endif +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/update_library.h" +#endif + +#if defined(OS_WIN) +#include "chrome/browser/enumerate_modules_model_win.h" +#endif + //////////////////////////////////////////////////////////////////////////////// // EncodingMenuModel @@ -258,8 +267,23 @@ bool WrenchMenuModel::IsCommandIdEnabled(int command_id) const { } bool WrenchMenuModel::IsCommandIdVisible(int command_id) const { - if (command_id == IDC_UPGRADE_DIALOG) + if (command_id == IDC_UPGRADE_DIALOG) { +#if defined(OS_CHROMEOS) + return (chromeos::CrosLibrary::Get()->GetUpdateLibrary()->status().status + == chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT); +#else return Singleton<UpgradeDetector>::get()->notify_upgrade(); +#endif + } else if (command_id == IDC_VIEW_INCOMPATIBILITIES) { +#if defined(OS_WIN) + EnumerateModulesModel* loaded_modules = + EnumerateModulesModel::GetSingleton(); + return loaded_modules->confirmed_bad_modules_detected() > 0; +#else + return false; +#endif + } + return true; } @@ -383,14 +407,22 @@ void WrenchMenuModel::Build() { #endif } +#if defined(OS_CHROMEOS) + const string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME); +#else + const string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME); +#endif // On Mac, there is no About item. if (browser_defaults::kShowAboutMenuItem) { AddItem(IDC_ABOUT, l10n_util::GetStringFUTF16( - IDS_ABOUT, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); + IDS_ABOUT, product_name)); } AddItem(IDC_UPGRADE_DIALOG, l10n_util::GetStringFUTF16( - IDS_UPDATE_NOW, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); + IDS_UPDATE_NOW, product_name)); + AddItem(IDC_VIEW_INCOMPATIBILITIES, l10n_util::GetStringUTF16( + IDS_VIEW_INCOMPATIBILITIES)); + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); SetIcon(GetIndexOfCommandId(IDC_UPGRADE_DIALOG), *rb.GetBitmapNamed(IDR_UPDATE_AVAILABLE)); diff --git a/chrome/browser/wrench_menu_model_unittest.cc b/chrome/browser/wrench_menu_model_unittest.cc index c539cee..188abf4 100644 --- a/chrome/browser/wrench_menu_model_unittest.cc +++ b/chrome/browser/wrench_menu_model_unittest.cc @@ -4,7 +4,7 @@ #include "chrome/browser/wrench_menu_model.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/test/browser_with_test_window_test.h" #include "chrome/test/menu_model_test.h" #include "chrome/test/testing_profile.h" diff --git a/chrome/common/DEPS b/chrome/common/DEPS index 93d6fc0..18175ac 100644 --- a/chrome/common/DEPS +++ b/chrome/common/DEPS @@ -7,7 +7,7 @@ include_rules = [ "+media/base", "+remoting/client/plugin", "+sandbox/src", - "+skia/include", + "+skia", "+webkit/glue", # Other libraries. diff --git a/chrome/common/child_process_info.cc b/chrome/common/child_process_info.cc index 41d066b..bd94ff2 100644 --- a/chrome/common/child_process_info.cc +++ b/chrome/common/child_process_info.cc @@ -135,9 +135,9 @@ std::string ChildProcessInfo::GenerateRandomChannelID(void* instance) { // parent browser process, an identifier for the child instance, and a random // component. We use a random component so that a hacked child process can't // cause denial of service by causing future named pipe creation to fail. - return StringPrintf("%d.%p.%d", - base::GetCurrentProcId(), instance, - base::RandInt(0, std::numeric_limits<int>::max())); + return base::StringPrintf("%d.%p.%d", + base::GetCurrentProcId(), instance, + base::RandInt(0, std::numeric_limits<int>::max())); } // static diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc index 6f77510..181cf0a 100644 --- a/chrome/common/chrome_paths.cc +++ b/chrome/common/chrome_paths.cc @@ -295,6 +295,20 @@ bool PathProvider(int key, FilePath* result) { break; } #endif +#if defined(OS_MACOSX) + case chrome::DIR_MANAGED_PREFS: { + if (!GetLocalLibraryDirectory(&cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("Managed Preferences")); + char* login = getlogin(); + if (!login) + return false; + cur = cur.AppendASCII(login); + if (!file_util::PathExists(cur)) // we don't want to create this + return false; + break; + } +#endif default: return false; } diff --git a/chrome/common/chrome_paths.h b/chrome/common/chrome_paths.h index 65538a7..63037c6 100644 --- a/chrome/common/chrome_paths.h +++ b/chrome/common/chrome_paths.h @@ -42,6 +42,10 @@ enum { // to set policies for chrome. This directory // contains subdirectories. #endif +#if defined(OS_MACOSX) + DIR_MANAGED_PREFS, // Directory that stores the managed prefs plist + // files for the current user. +#endif FILE_RESOURCE_MODULE, // Full path and filename of the module that // contains embedded resources (version, // strings, images, etc.). diff --git a/chrome/common/chrome_paths_internal.h b/chrome/common/chrome_paths_internal.h index 38fbac2..6671e25 100644 --- a/chrome/common/chrome_paths_internal.h +++ b/chrome/common/chrome_paths_internal.h @@ -55,6 +55,10 @@ void SetOverrideVersionedDirectory(const FilePath* path); // framework are things that also depend on the framework, such as the helper // app bundle. FilePath GetFrameworkBundlePath(); + +// Get the local library directory. +bool GetLocalLibraryDirectory(FilePath* result); + #endif // OS_MACOSX } // namespace chrome diff --git a/chrome/common/chrome_paths_mac.mm b/chrome/common/chrome_paths_mac.mm index c7db483..b87b397 100644 --- a/chrome/common/chrome_paths_mac.mm +++ b/chrome/common/chrome_paths_mac.mm @@ -105,4 +105,8 @@ FilePath GetFrameworkBundlePath() { return GetVersionedDirectory().Append(kFrameworkName); } +bool GetLocalLibraryDirectory(FilePath* result) { + return mac_util::GetLocalDirectory(NSLibraryDirectory, result); +} + } // namespace chrome diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 9847a38..ff007df 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -100,6 +100,10 @@ const char kCloudPrintProxyId[] = "cloud-print-proxy-id"; // print service has been enabled (see enable-cloud-print). const char kCloudPrintServiceURL[] = "cloud-print-service"; +// Causes the browser process to inspect loaded and registered DLLs for +// known conflicts and warn the user. +const char kConflictingModulesCheck[] = "conflicting-modules-check"; + // The Country we should use. This is normally obtained from the operating // system during first run and cached in the preferences afterwards. This is a // string value, the 2 letter code from ISO 3166-1. @@ -144,9 +148,6 @@ const char kDisableBackgroundNetworking[] = "disable-background-networking"; // users with many windows/tabs and lots of memory. const char kDisableBackingStoreLimit[] = "disable-backing-store-limit"; -// Disable support for cached byte-ranges. -const char kDisableByteRangeSupport[] = "disable-byte-range-support"; - // Disable click-to-play for blocked plug-ins. const char kDisableClickToPlay[] = "disable-click-to-play"; @@ -194,6 +195,10 @@ const char kDisableGeolocation[] = "disable-geolocation"; // Disable the GLSL translator. const char kDisableGLSLTranslator[] = "disable-glsl-translator"; +// Disable the thread that crashes the GPU process if it stops responding to +// messages. +const char kDisableGpuWatchdog[] = "disable-gpu-watchdog"; + // Suppresses hang monitor dialogs in renderer processes. This may allow slow // unload handlers on a page to prevent the tab from closing, but the Task // Manager can be used to terminate the offending process in this case. @@ -293,9 +298,6 @@ const char kDisableSyncBookmarks[] = "disable-sync-bookmarks"; // Disable syncing of extensions. const char kDisableSyncExtensions[] = "disable-sync-extensions"; -// Disable syncing browser passwords. -const char kDisableSyncPasswords[] = "disable-sync-passwords"; - // Disable syncing of preferences. const char kDisableSyncPreferences[] = "disable-sync-preferences"; @@ -387,6 +389,9 @@ const char kEnableCloudPrintProxy[] = "enable-cloud-print-proxy"; // Enables the Cloud Print dialog hosting code. const char kEnableCloudPrint[] = "enable-cloud-print"; +// Enable the Confirm to Quit experiment. +const char kEnableConfirmToQuit[] = "enable-confirm-to-quit"; + // Enables establishing a backup TCP connection if a specified timeout is // exceeded. const char kEnableConnectBackupJobs[] = "enable-connect-backup-jobs"; @@ -403,6 +408,11 @@ const char kEnableDefaultApps[] = "enable-default-apps"; // Enables device motion events. const char kEnableDeviceMotion[] = "enable-device-motion"; +// Enable DNS side checking of certificates. Still experimental, should only +// be used by developers at the current time. +const char kEnableDNSCertProvenanceChecking[] = + "enable-dns-cert-provenance-checking"; + const char kEnableDNSSECCerts[] = "enable-dnssec-certs"; // Enables extension APIs that are in development. @@ -428,16 +438,10 @@ const char kEnableIPv6[] = "enable-ipv6"; // Enable the GPU plugin and Pepper 3D rendering. const char kEnableGPUPlugin[] = "enable-gpu-plugin"; -// Enable experimental GPU rendering for backing store and video. -const char kEnableGPURendering[] = "enable-gpu-rendering"; - // Force logging to be enabled. Logging is disabled by default in release // builds. const char kEnableLogging[] = "enable-logging"; -// Is match preview enabled? -const char kEnableMatchPreview[] = "enable-match-preview"; - // Allows reporting memory info (JS heap size) to page. const char kEnableMemoryInfo[] = "enable-memory-info"; @@ -456,6 +460,9 @@ const char kEnableNaClDebug[] = "enable-nacl-debug"; // Enable Native Web Worker support. const char kEnableNativeWebWorkers[] = "enable-native-web-workers"; +// Is the predictive varition of instant enabled? +const char kEnablePredictiveInstant[] = "enable-predictive-instant"; + // This applies only when the process type is "service". Enables the // Chromoting Host Process within the service process. const char kEnableRemoting[] = "enable-remoting"; @@ -464,6 +471,9 @@ const char kEnableRemoting[] = "enable-remoting"; const char kEnableResourceContentSettings[] = "enable-resource-content-settings"; +// Enable speculative prerendering of pages. +const char kEnablePagePrerender[] = "enable-page-prerender"; + // Enable speculative TCP/IP preconnection. const char kEnablePreconnect[] = "enable-preconnect"; @@ -477,9 +487,6 @@ const char kEnablePrintPreview[] = "enable-print-preview"; // parameter to indicate if the provider should be the default. const char kEnableSearchProviderApiV2[] = "enable-search-provider-api-v2"; -// Enables the secure infobars. Non-default until the kinks are worked out. -const char kEnableSecureInfoBars[] = "enable-secure-infobars"; - // Enables 0-RTT HTTPS handshakes. const char kEnableSnapStart[] = "enable-snap-start"; @@ -492,6 +499,9 @@ const char kEnableSync[] = "enable-sync"; // Enable syncing browser autofill. const char kEnableSyncAutofill[] = "enable-sync-autofill"; +// Enable syncing browser passwords. +const char kEnableSyncPasswords[] = "enable-sync-passwords"; + // Enable syncing browser sessions. const char kEnableSyncSessions[] = "enable-sync-sessions"; @@ -501,12 +511,19 @@ const char kEnableSyncTypedUrls[] = "enable-sync-typed-urls"; // Enable tabbed options, ie: dom-ui version of options window. const char kEnableTabbedOptions[] = "enable-tabbed-options"; +// Enable use of experimental TCP sockets API for sending data in the +// SYN packet. +const char kEnableTcpFastOpen[] = "enable-tcp-fastopen"; + // Enables TopSites. const char kEnableTopSites[] = "enable-top-sites"; // Whether or not the touch events API is exposed. const char kEnableTouch[] = "enable-touch"; +// Is verbatim instant enabled? +const char kEnableVerbatimInstant[] = "enable-verbatim-instant"; + // Enables the option to show tabs as a vertical stack down the side of the // browser window. const char kEnableVerticalTabs[] = "enable-vertical-tabs"; @@ -515,10 +532,6 @@ const char kEnableVerticalTabs[] = "enable-vertical-tabs"; // incomplete and this flag is used for development and testing. const char kEnableVideoFullscreen[] = "enable-video-fullscreen"; -// Enables video layering where video is rendered as a separate layer outside -// of the backing store. -const char kEnableVideoLayering[] = "enable-video-layering"; - // Enables video logging where video elements log playback performance data to // the debug log. const char kEnableVideoLogging[] = "enable-video-logging"; @@ -593,6 +606,10 @@ const char kForceAppsPromoVisible[] = "force-apps-promo-visible"; // current details. const char kForceFieldTestNameAndValue[] = "force-fieldtest"; +// Forces the internal PDF plugin to be used for this run, even if it's disabled +// by default. Used for testing. +const char kForceInternalPDFPlugin[] = "force-internal-pdf"; + // Force renderer accessibility to be on instead of enabling it on demand when // a screen reader is detected. The disable-renderer-accessibility switch // overrides this if present. @@ -664,6 +681,10 @@ const char kInProcessWebGL[] = "in-process-webgl"; // Causes the browser to launch directly in incognito mode. const char kIncognito[] = "incognito"; +// URL to use for instant. If specified this overrides the url from the +// TemplateURL. +const char kInstantURL[] = "instant-url"; + // Runs the Native Client inside the renderer process. const char kInternalNaCl[] = "internal-nacl"; @@ -980,14 +1001,12 @@ const char kSingleProcess[] = "single-process"; // Start the browser maximized, regardless of any previous settings. const char kStartMaximized[] = "start-maximized"; -// Control Sync XMPP client settings. -const char kSyncAllowPlain[] = "allow-plain"; - -// Control Sync XMPP client settings. -const char kSyncDisableTls[] = "disable-tls"; +// Allow insecure XMPP connections for sync (for testing). +const char kSyncAllowInsecureXmppConnection[] = + "sync-allow-insecure-xmpp-connection"; -// Email used for sync. -const char kSyncEmail[] = "email"; +// Invalidate any login info passed into sync's XMPP connection. +const char kSyncInvalidateXmppLogin[] = "sync-invalidate-xmpp-login"; // Use the SyncerThread implementation that matches up with the old pthread // impl semantics, but using Chrome synchronization primitives. The only @@ -998,26 +1017,15 @@ const char kSyncerThreadTimedStop[] = "syncer-thread-timed-stop"; // Override the default notification method for sync. const char kSyncNotificationMethod[] = "sync-notification-method"; -// Override the default host used for sync notifications. +// Override the default host used for sync notifications. Can be either +// "host" or "host:port". const char kSyncNotificationHost[] = "sync-notification-host"; -// Password used for sync. -const char kSyncPassword[] = "password"; - -// Port used for sync. -const char kSyncPort[] = "port"; - -// Server used for sync. -const char kSyncServer[] = "server"; - // Override the default server used for profile sync. const char kSyncServiceURL[] = "sync-url"; -// Control Sync XMPP client settings. -const char kSyncUseSslTcp[] = "use-ssl-tcp"; - -// Control Sync XMPP client settings. -const char kSyncUseCacheInvalidation[] = "use-cache-invalidation"; +// Try to connect to XMPP using SSLTCP first (for testing). +const char kSyncTrySsltcpFirstForXmpp[] = "sync-try-ssltcp-first-for-xmpp"; // Pass the name of the current running automated test to Chrome. const char kTestName[] = "test-name"; @@ -1056,12 +1064,20 @@ const char kIgnoreCertificateErrors[] = "ignore-certificate-errors"; // Set the maximum SPDY sessions per domain. const char kMaxSpdySessionsPerDomain[] = "max-spdy-sessions-per-domain"; +// Set the maximum concurrent streams over a SPDY session. +const char kMaxSpdyConcurrentStreams[] = "max-spdy-concurrent-streams"; + // Grant unlimited quota to store files to this process. // Used for testing Pepper's FileRef/FileIO/FileSystem implementations. // DO NOT USE FOR OTHER PURPOSES. // TODO(dumi): remove the switch when we have a real quota implementation. const char kUnlimitedQuotaForFiles[] = "unlimited-quota-for-files"; +// This is for testing IndexedDB and will give any website you visit unlimited +// quota in IndexedDB. This should only be used for development and not general +// browsing. It is ignored in single process mode. +const char kUnlimitedQuotaForIndexedDB[] = "unlimited-quota-for-indexeddb"; + // Use the low fragmentation heap for the CRT. const char kUseLowFragHeapCrt[] = "use-lf-heap"; @@ -1175,12 +1191,9 @@ const char kCompressSystemFeedback[] = "compress-sys-feedback"; // switch separates chrome code from the rest of ChromeOS. const char kForceStubLibcros[] = "force-stub-libcros"; -// Disables DOMUI menu and use gtk menu instead. -const char kDisableDOMUIMenu[] = "disable-domui-menu"; +// Enables DOMUI menu. +const char kEnableDOMUIMenu[] = "enable-domui-menu"; -#ifndef NDEBUG -extern const char kDOMUIMenuUrl[] = "domui-menu-url"; -#endif #endif #if defined(OS_LINUX) @@ -1199,8 +1212,6 @@ const char kUseSystemSSL[] = "use-system-ssl"; // command lines on Linux and Mac. It tells the helper process to enable crash // dumping and reporting, because helpers cannot access the profile or other // files needed to make this decision. -// If passed to the browser, it'll be passed on to all the helper processes -// as well, thereby force-enabling the crash reporter. const char kEnableCrashReporter[] = "enable-crash-reporter"; // Bypass the error dialog when the profile lock couldn't be attained. @@ -1225,6 +1236,13 @@ const char kEnableExposeForTabs[] = "enable-expose-for-tabs"; // Cause the OS X sandbox write to syslog every time an access to a resource // is denied by the sandbox. const char kEnableSandboxLogging[] = "enable-sandbox-logging"; + + +// Temporary flag to revert to the old WorkerPool implementation. +// This will be removed once we either fix the Mac WorkerPool +// implementation, or completely switch to the shared (with Linux) +// implementation. +const char kDisableLinuxWorkerPool[] = "disable-linux-worker-pool"; #else // Enable Kiosk mode. const char kKioskMode[] = "kiosk"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 14e3ae3..49c3e49 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -42,6 +42,7 @@ extern const char kCheckForUpdateIntervalSec[]; extern const char kChromeFrame[]; extern const char kCloudPrintProxyId[]; extern const char kCloudPrintServiceURL[]; +extern const char kConflictingModulesCheck[]; extern const char kCountry[]; extern const char kDebugPrint[]; extern const char kDiagnostics[]; @@ -52,7 +53,6 @@ extern const char kDisableAudio[]; extern const char kDisableAuthNegotiateCnameLookup[]; extern const char kDisableBackgroundNetworking[]; extern const char kDisableBackingStoreLimit[]; -extern const char kDisableByteRangeSupport[]; extern const char kDisableClickToPlay[]; extern const char kDisableConnectBackupJobs[]; extern const char kDisableContentPrefetch[]; @@ -67,6 +67,7 @@ extern const char kDisableExtensions[]; extern const char kDisableFileSystem[]; extern const char kDisableGLSLTranslator[]; extern const char kDisableGeolocation[]; +extern const char kDisableGpuWatchdog[]; extern const char kDisableHangMonitor[]; extern const char kDisableHistoryQuickProvider[]; extern const char kDisableHistoryURLProvider[]; @@ -96,7 +97,6 @@ extern const char kDisableSyncApps[]; extern const char kDisableSyncAutofill[]; extern const char kDisableSyncBookmarks[]; extern const char kDisableSyncExtensions[]; -extern const char kDisableSyncPasswords[]; extern const char kDisableSyncPreferences[]; extern const char kDisableSyncThemes[]; extern const char kDisableTabCloseableStateWatcher[]; @@ -121,45 +121,48 @@ extern const char kEnableBenchmarking[]; extern const char kEnableClearServerData[]; extern const char kEnableCloudPrintProxy[]; extern const char kEnableCloudPrint[]; +extern const char kEnableConfirmToQuit[]; extern const char kEnableConnectBackupJobs[]; extern const char kEnableContentPrefetch[]; extern const char kEnableDefaultApps[]; extern const char kEnableDeviceMotion[]; +extern const char kEnableDNSCertProvenanceChecking[]; extern const char kEnableDNSSECCerts[]; extern const char kEnableExperimentalExtensionApis[]; extern const char kEnableExtensionTimelineApi[]; extern const char kEnableFastback[]; extern const char kEnableFileCookies[]; extern const char kEnableGPUPlugin[]; -extern const char kEnableGPURendering[]; extern const char kEnableIPv6[]; extern const char kEnableLogging[]; -extern const char kEnableMatchPreview[]; extern const char kEnableMemoryInfo[]; extern const char kEnableMonitorProfile[]; extern const char kEnableNaCl[]; extern const char kEnableNaClDebug[]; extern const char kEnableNativeWebWorkers[]; +extern const char kEnablePagePrerender[]; extern const char kEnablePreconnect[]; +extern const char kEnablePredictiveInstant[]; extern const char kEnablePreparsedJsCaching[]; extern const char kEnablePrintPreview[]; extern const char kEnableRemoting[]; extern const char kEnableResourceContentSettings[]; extern const char kEnableSearchProviderApiV2[]; -extern const char kEnableSecureInfoBars[]; extern const char kEnableSnapStart[]; extern const char kEnableStatsTable[]; extern const char kEnableSync[]; extern const char kEnableSyncAutofill[]; +extern const char kEnableSyncPasswords[]; extern const char kEnableSyncPreferences[]; extern const char kEnableSyncSessions[]; extern const char kEnableSyncTypedUrls[]; extern const char kEnableTabbedOptions[]; +extern const char kEnableTcpFastOpen[]; extern const char kEnableTopSites[]; extern const char kEnableTouch[]; +extern const char kEnableVerbatimInstant[]; extern const char kEnableVerticalTabs[]; extern const char kEnableVideoFullscreen[]; -extern const char kEnableVideoLayering[]; extern const char kEnableVideoLogging[]; extern const char kEnableWatchdog[]; extern const char kEnableXSSAuditor[]; @@ -177,6 +180,7 @@ extern const char kFileDescriptorLimit[]; extern const char kFirstRun[]; extern const char kForceAppsPromoVisible[]; extern const char kForceFieldTestNameAndValue[]; +extern const char kForceInternalPDFPlugin[]; extern const char kForceRendererAccessibility[]; extern const char kGpuLauncher[]; extern const char kGpuProcess[]; @@ -193,6 +197,7 @@ extern const char kImportFromFile[]; extern const char kInProcessPlugins[]; extern const char kInProcessWebGL[]; extern const char kIncognito[]; +extern const char kInstantURL[]; extern const char kInternalNaCl[]; extern const char kInternalPepper[]; extern const char kJavaScriptFlags[]; @@ -275,18 +280,13 @@ extern const char kSilentDumpOnDCHECK[]; extern const char kSimpleDataSource[]; extern const char kSingleProcess[]; extern const char kStartMaximized[]; -extern const char kSyncAllowPlain[]; -extern const char kSyncDisableTls[]; -extern const char kSyncEmail[]; +extern const char kSyncAllowInsecureXmppConnection[]; +extern const char kSyncInvalidateXmppLogin[]; extern const char kSyncerThreadTimedStop[]; extern const char kSyncNotificationMethod[]; extern const char kSyncNotificationHost[]; -extern const char kSyncPassword[]; -extern const char kSyncPort[]; -extern const char kSyncServer[]; extern const char kSyncServiceURL[]; -extern const char kSyncUseSslTcp[]; -extern const char kSyncUseCacheInvalidation[]; +extern const char kSyncTrySsltcpFirstForXmpp[]; extern const char kTestNaClSandbox[]; extern const char kTestName[]; extern const char kTestSandbox[]; @@ -298,7 +298,9 @@ extern const char kUninstall[]; extern const char kUseSpdy[]; extern const char kIgnoreCertificateErrors[]; extern const char kMaxSpdySessionsPerDomain[]; +extern const char kMaxSpdyConcurrentStreams[]; extern const char kUnlimitedQuotaForFiles[]; +extern const char kUnlimitedQuotaForIndexedDB[]; extern const char kUseLowFragHeapCrt[]; extern const char kUserAgent[]; extern const char kUserDataDir[]; @@ -335,10 +337,7 @@ extern const char kStubCros[]; extern const char kScreenSaverUrl[]; extern const char kCompressSystemFeedback[]; extern const char kForceStubLibcros[]; -extern const char kDisableDOMUIMenu[]; -#ifndef NDEBUG -extern const char kDOMUIMenuUrl[]; -#endif +extern const char kEnableDOMUIMenu[]; #endif #if defined(OS_LINUX) @@ -361,6 +360,7 @@ extern const char kPasswordStore[]; extern const char kDisableHolePunching[]; extern const char kEnableExposeForTabs[]; extern const char kEnableSandboxLogging[]; +extern const char kDisableLinuxWorkerPool[]; #else extern const char kKioskMode[]; #endif diff --git a/chrome/common/chrome_version_info.cc b/chrome/common/chrome_version_info.cc index a26f81f..5157c4a 100644 --- a/chrome/common/chrome_version_info.cc +++ b/chrome/common/chrome_version_info.cc @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "base/file_version_info.h" #include "base/string_util.h" +#include "base/thread_restrictions.h" #include "build/build_config.h" namespace chrome { @@ -16,6 +17,8 @@ namespace chrome { // FileVersionInfo for the current module. VersionInfo::VersionInfo() { + // The current module is already loaded in memory, so this will be cheap. + base::ThreadRestrictions::ScopedAllowIO allow_io; version_info_.reset(FileVersionInfo::CreateFileVersionInfoForCurrentModule()); } diff --git a/chrome/common/common.sb b/chrome/common/common.sb index f8d4f1c..4b5c4f5 100644 --- a/chrome/common/common.sb +++ b/chrome/common/common.sb @@ -11,7 +11,7 @@ ; printing on sandbox exceptions; this functionality only exists on 10.6. The ; --enable-sandbox-logging flag or system versions <10.6 cause this flag to ; expand to an empty string. http://crbug.com/26621 -(deny default DISABLE_SANDBOX_DENIAL_LOGGING) +(deny default @DISABLE_SANDBOX_DENIAL_LOGGING@) ; Support for programmatically enabling verbose debugging. ;ENABLE_LOGGING (debug deny) @@ -26,13 +26,11 @@ ; This profile is tested with the following system versions: ; 10.5.6, 10.6 -; Allow following symlinks -(allow file-read-metadata) ; 10.5.6 - ; Loading System Libraries. -(allow file-read-data (regex #"^/System/Library/Frameworks($|/)")) ; 10.5.6 -(allow file-read-data (regex #"^/System/Library/PrivateFrameworks($|/)")) ; 10.5.6 -(allow file-read-data (regex #"^/System/Library/CoreServices($|/)")) ; 10.5.6 +(allow file-read* + (regex #"^/System/Library/Frameworks($|/)") + (regex #"^/System/Library/PrivateFrameworks($|/)") + (regex #"^/System/Library/CoreServices($|/)")) ; 10.5.6 ; Needed for IPC on 10.6 ;10.6_ONLY (allow ipc-posix-shm)
\ No newline at end of file diff --git a/chrome/common/common_param_traits.cc b/chrome/common/common_param_traits.cc index d18c6c5..2fd8bdd 100644 --- a/chrome/common/common_param_traits.cc +++ b/chrome/common/common_param_traits.cc @@ -138,7 +138,7 @@ bool ParamTraits<gfx::Point>::Read(const Message* m, void** iter, } void ParamTraits<gfx::Point>::Log(const gfx::Point& p, std::string* l) { - l->append(StringPrintf("(%d, %d)", p.x(), p.y())); + l->append(base::StringPrintf("(%d, %d)", p.x(), p.y())); } @@ -164,8 +164,8 @@ bool ParamTraits<gfx::Rect>::Read(const Message* m, void** iter, gfx::Rect* r) { } void ParamTraits<gfx::Rect>::Log(const gfx::Rect& p, std::string* l) { - l->append(StringPrintf("(%d, %d, %d, %d)", p.x(), p.y(), - p.width(), p.height())); + l->append(base::StringPrintf("(%d, %d, %d, %d)", p.x(), p.y(), + p.width(), p.height())); } @@ -185,7 +185,7 @@ bool ParamTraits<gfx::Size>::Read(const Message* m, void** iter, gfx::Size* r) { } void ParamTraits<gfx::Size>::Log(const gfx::Size& p, std::string* l) { - l->append(StringPrintf("(%d, %d)", p.width(), p.height())); + l->append(base::StringPrintf("(%d, %d)", p.width(), p.height())); } void ParamTraits<ContentSetting>::Write(Message* m, const param_type& p) { @@ -435,8 +435,8 @@ bool ParamTraits<ThumbnailScore>::Read(const Message* m, void** iter, } void ParamTraits<ThumbnailScore>::Log(const param_type& p, std::string* l) { - l->append(StringPrintf("(%f, %d, %d)", - p.boring_score, p.good_clipping, p.at_top)); + l->append(base::StringPrintf("(%f, %d, %d)", + p.boring_score, p.good_clipping, p.at_top)); } template <> @@ -454,7 +454,7 @@ struct ParamTraits<Geoposition::ErrorCode> { } static void Log(const param_type& p, std::string* l) { int error_code = p; - l->append(StringPrintf("<Geoposition::ErrorCode>%d", error_code)); + l->append(base::StringPrintf("<Geoposition::ErrorCode>%d", error_code)); } }; @@ -488,7 +488,7 @@ bool ParamTraits<Geoposition>::Read( void ParamTraits<Geoposition>::Log(const Geoposition& p, std::string* l) { l->append( - StringPrintf( + base::StringPrintf( "<Geoposition>" "%.6f %.6f %.6f %.6f " "%.6f %.6f %.6f ", diff --git a/chrome/common/database_util.cc b/chrome/common/database_util.cc index 412debc..ea4e2cc 100644 --- a/chrome/common/database_util.cc +++ b/chrome/common/database_util.cc @@ -18,8 +18,8 @@ WebKitClient::FileHandle DatabaseUtil::databaseOpenFile( IPC::PlatformFileForTransit file_handle = IPC::InvalidPlatformFileForTransit(); - scoped_refptr<IPC::SyncMessageFilter> filter = - ChildThread::current()->sync_message_filter(); + scoped_refptr<IPC::SyncMessageFilter> filter( + ChildThread::current()->sync_message_filter()); filter->Send(new ViewHostMsg_DatabaseOpenFile( vfs_file_name, desired_flags, &file_handle)); @@ -29,8 +29,8 @@ WebKitClient::FileHandle DatabaseUtil::databaseOpenFile( int DatabaseUtil::databaseDeleteFile( const WebString& vfs_file_name, bool sync_dir) { int rv = SQLITE_IOERR_DELETE; - scoped_refptr<IPC::SyncMessageFilter> filter = - ChildThread::current()->sync_message_filter(); + scoped_refptr<IPC::SyncMessageFilter> filter( + ChildThread::current()->sync_message_filter()); filter->Send(new ViewHostMsg_DatabaseDeleteFile( vfs_file_name, sync_dir, &rv)); return rv; @@ -38,16 +38,16 @@ int DatabaseUtil::databaseDeleteFile( long DatabaseUtil::databaseGetFileAttributes(const WebString& vfs_file_name) { int32 rv = -1; - scoped_refptr<IPC::SyncMessageFilter> filter = - ChildThread::current()->sync_message_filter(); + scoped_refptr<IPC::SyncMessageFilter> filter( + ChildThread::current()->sync_message_filter()); filter->Send(new ViewHostMsg_DatabaseGetFileAttributes(vfs_file_name, &rv)); return rv; } long long DatabaseUtil::databaseGetFileSize(const WebString& vfs_file_name) { int64 rv = 0LL; - scoped_refptr<IPC::SyncMessageFilter> filter = - ChildThread::current()->sync_message_filter(); + scoped_refptr<IPC::SyncMessageFilter> filter( + ChildThread::current()->sync_message_filter()); filter->Send(new ViewHostMsg_DatabaseGetFileSize(vfs_file_name, &rv)); return rv; } diff --git a/chrome/common/dx_diag_node.cc b/chrome/common/dx_diag_node.cc new file mode 100644 index 0000000..85beb5b --- /dev/null +++ b/chrome/common/dx_diag_node.cc @@ -0,0 +1,9 @@ +// 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/common/dx_diag_node.h" + +DxDiagNode::DxDiagNode() {} + +DxDiagNode::~DxDiagNode() {} diff --git a/chrome/common/dx_diag_node.h b/chrome/common/dx_diag_node.h index 9f20d92..6522213 100644 --- a/chrome/common/dx_diag_node.h +++ b/chrome/common/dx_diag_node.h @@ -12,6 +12,9 @@ #include <string> struct DxDiagNode { + DxDiagNode(); + ~DxDiagNode(); + std::map<std::string, std::string> values; std::map<std::string, DxDiagNode> children; }; diff --git a/chrome/common/env_vars.cc b/chrome/common/env_vars.cc index dc2648c..f63d3e4 100644 --- a/chrome/common/env_vars.cc +++ b/chrome/common/env_vars.cc @@ -14,6 +14,9 @@ const char kHeadless[] = "CHROME_HEADLESS"; // The name of the log file. const char kLogFileName[] = "CHROME_LOG_FILE"; +// The name of the session log directory when logged in to ChromeOS. +const char kSessionLogDir[] = "CHROMEOS_SESSION_LOG_DIR"; + // If this environment variable is set, Chrome on Windows will log // to Event Tracing for Windows. const char kEtwLogging[] = "CHROME_ETW_LOGGING"; diff --git a/chrome/common/env_vars.h b/chrome/common/env_vars.h index e9984d2..320a0b4 100644 --- a/chrome/common/env_vars.h +++ b/chrome/common/env_vars.h @@ -12,6 +12,7 @@ namespace env_vars { extern const char kHeadless[]; extern const char kLogFileName[]; +extern const char kSessionLogDir[]; extern const char kEtwLogging[]; extern const char kShowRestart[]; extern const char kRestartInfo[]; diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index 3fdd1ce..45d9409 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -503,7 +503,12 @@ "optional": true, "description": "The speak options. This parameter is currently ignored.", "properties": { - "languageName": { + "enqueue": { + "type": "boolean", + "optional": true, + "description": "If true, enqueues this utterance if TTS is already in progress. If false (the default), interrupts any current speech and flushes the speech queue before speaking this new utterance." + }, + "languageName": { "type": "string", "optional": true, "description": "The language name for synthesis specified in the form <language>-<locale>, e.g. en-US, en-GB, fr-CA, zh-CN, etc." @@ -804,6 +809,7 @@ "index": {"type": "integer", "minimum": 0, "description": "The zero-based index of the tab within its window."}, "windowId": {"type": "integer", "minimum": 0, "description": "The ID of the window the tab is contained within."}, "selected": {"type": "boolean", "description": "Whether the tab is selected."}, + "pinned": {"type": "boolean", "description": "Whether the tab is pinned."}, "url": {"type": "string", "description": "The URL the tab is displaying."}, "title": {"type": "string", "optional": true, "description": "The title of the tab. This may not be available if the tab is loading."}, "favIconUrl": {"type": "string", "optional": true, "description": "The URL of the tab's favicon. This may not be available if the tab is loading."}, @@ -871,7 +877,7 @@ ], "returns": { "$ref": "Port", - "description": "A port that can be used to communicate with the content scripts running in the specified tab." + "description": "A port that can be used to communicate with the content scripts running in the specified tab. The port's <a href='extension.html#type-Port'>onDisconnect</a> event is fired if the tab closes or does not exist. " } }, { @@ -896,7 +902,7 @@ { "name": "response", "type": "any", - "description": "The JSON response object sent by the handler of the request." + "description": "The JSON response object sent by the handler of the request. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and <a href='extension.html#property-lastError'>chrome.extension.lastError</a> will be set to the error message." } ] } @@ -974,6 +980,11 @@ "type": "boolean", "optional": true, "description": "Whether the tab should become the selected tab in the window. Defaults to <var>true</var>" + }, + "pinned": { + "type": "boolean", + "optional": true, + "description": "Whether the tab should be pinned. Defaults to <var>false</var>" } } }, @@ -1001,8 +1012,20 @@ "type": "object", "name": "updateProperties", "properties": { - "url": {"type": "string", "optional": true}, - "selected": {"type": "boolean", "optional": true} + "url": { + "optional": true, + "description": "A URL to navigate the tab to." + }, + "selected": { + "type": "boolean", + "optional": true, + "description": "Whether the tab should be selected." + }, + "pinned": { + "type": "boolean", + "optional": true, + "description": "Whether the tab should be pinned." + } } }, { @@ -1209,12 +1232,18 @@ "properties": { "status": { "type": "string", + "optional": true, "description": "The status of the tab. Can be either <em>loading</em> or <em>complete</em>." }, "url": { "type": "string", "optional": true, - "description": "Only specified if the tab's URL changed." + "description": "The tab's URL if it has changed." + }, + "pinned": { + "type": "boolean", + "optional": true, + "description": "The tab's new pinned state." } } }, @@ -2665,12 +2694,12 @@ "properties": { "menuItemId": { "type": "integer", - "description": "The id of the menu item that was clicked." + "description": "The ID of the menu item that was clicked." }, "parentMenuItemId": { "type": "integer", "optional": true, - "description": "The parent id, if any, for the item clicked." + "description": "The parent ID, if any, for the item clicked." }, "mediaType": { "type": "string", @@ -2680,21 +2709,21 @@ "linkUrl": { "type": "string", "optional": true, - "description": "If the element is a link, the url it points to." + "description": "If the element is a link, the URL it points to." }, "srcUrl": { "type": "string", "optional": true, - "description": "Will be present for elements with a 'src' url." + "description": "Will be present for elements with a 'src' URL." }, "pageUrl": { "type": "string", - "description": "The url of the page where the menu item was clicked." + "description": "The URL of the page where the menu item was clicked." }, "frameUrl": { "type": "string", "optional": true, - "description": " The url of the frame of the element where the context menu was clicked, if it was in a frame." + "description": " The URL of the frame of the element where the context menu was clicked, if it was in a frame." }, "selectionText": { "type": "string", @@ -2715,7 +2744,7 @@ "description": "Creates a new context menu item. Note that if an error occurs during creation, you may not find out until the creation callback fires (the details will be in chrome.extension.lastError).", "returns": { "type": "integer", - "description": "The id of the newly created item." + "description": "The ID of the newly created item." }, "parameters": [ { @@ -2730,12 +2759,12 @@ "title": { "type": "string", "optional": "true", - "description": "This must be specified unless type is 'separator'." + "description": "The text to be displayed in the item; this is <em>required</em> unless <em>type</em> is 'separator'. When the context is 'selection', you can use <code>%s</code> within the string to show the selected text. For example, if this parameter's value is \"Translate '%s' to Pig Latin\" and the user selects the word \"cool\", the context menu item for the selection is \"Translate 'cool' to Pig Latin\"." }, "checked": { "type": "boolean", "optional": true, - "description": "This only applies to the initial state of a checkbox or radio item. It indicates if the item should initially be checked/selected. Only one radio item can be selected at a time in a given group of radio items." + "description": "The initial state of a checkbox or radio item: true for selected and false for unselected. Only one radio item can be selected at a time in a given group of radio items." }, "contexts": { "type": "array", @@ -2764,19 +2793,19 @@ "parentId": { "type": "integer", "optional": true, - "description": "The id of a parent menu item - this makes the item a child of a previously added item." + "description": "The ID of a parent menu item; this makes the item a child of a previously added item." }, "documentUrlPatterns": { "type": "array", "items": {"type": "string"}, "optional": true, - "description": "Lets you restrict the item to only apply to documents whose URL matches one of the given patterns. (This applies to frames as well). For details on the format of a pattern, see <a href='match_patterns.html'>Match Patterns</a>." + "description": "Lets you restrict the item to apply only to documents whose URL matches one of the given patterns. (This applies to frames as well.) For details on the format of a pattern, see <a href='match_patterns.html'>Match Patterns</a>." }, "targetUrlPatterns": { "type": "array", "items": {"type": "string"}, "optional": true, - "description": "Similar to documentUrlPatterns, but this lets you filter based on the src attribute of img/audio/video tags and the href of anchor tags." + "description": "Similar to documentUrlPatterns, but lets you filter based on the src attribute of img/audio/video tags and the href of anchor tags." } } }, @@ -2784,7 +2813,7 @@ "type": "function", "name": "callback", "optional": true, - "description": "Called when the item has been created in the browser. If there were any problems creating the item, details will be avilable in chrome.extension.lastError.", + "description": "Called when the item has been created in the browser. If there were any problems creating the item, details will be available in chrome.extension.lastError.", "parameters": [] } ] @@ -2797,7 +2826,7 @@ { "type": "integer", "name": "id", - "description": "The id of the item to update." + "description": "The ID of the item to update." }, { "type": "object", @@ -2860,7 +2889,7 @@ { "type": "integer", "name": "menuItemId", - "description": "The id of the context menu item to remove." + "description": "The ID of the context menu item to remove." }, { "type": "function", @@ -4029,10 +4058,6 @@ "description": "Whether it is currently enabled or disabled.", "type": "boolean" }, - "enabled": { - "description": "Whether it is currently enabled or disabled.", - "type": "boolean" - }, "isApp": { "description": "True if this is an app.", "type": "boolean" @@ -4080,6 +4105,28 @@ ] }, { + "name": "get", + "description": "Return information about the installed extension with the given ID.", + "parameters": [ + { + "name": "id", + "type": "string", + "description": "The ID from an item of $ref:ExtensionInfo." + }, + { + "type": "function", + "name": "callback", + "optional": "true", + "parameters": [ + { + "name": "result", + "$ref": "ExtensionInfo" + } + ] + } + ] + }, + { "name": "setEnabled", "description": "Enable or disable an app or extension.", "parameters": [ diff --git a/chrome/common/extensions/docs/a11y.html b/chrome/common/extensions/docs/a11y.html index c8e2646..5c34cff 100644 --- a/chrome/common/extensions/docs/a11y.html +++ b/chrome/common/extensions/docs/a11y.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Accessibility (a11y) - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/api_index.html b/chrome/common/extensions/docs/api_index.html index 46ddd6e..299f041 100644 --- a/chrome/common/extensions/docs/api_index.html +++ b/chrome/common/extensions/docs/api_index.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.* APIs - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/api_other.html b/chrome/common/extensions/docs/api_other.html index c1ef26b..a4c11c6 100644 --- a/chrome/common/extensions/docs/api_other.html +++ b/chrome/common/extensions/docs/api_other.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Other APIs - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -327,7 +331,7 @@ E.g. window.open(someUrl). --></dd> <ul> <li> audio (<a href="http://www.html5rocks.com/tutorials/audio/quick/">tutorial</a>) </li> - <li> app cache + <li> application cache (<a href="http://www.html5rocks.com/tutorials/appcache/beginner/">tutorial</a>) </li> <li> canvas </li> <li> geolocation diff --git a/chrome/common/extensions/docs/autoupdate.html b/chrome/common/extensions/docs/autoupdate.html index f474abb..b8c8ea5 100644 --- a/chrome/common/extensions/docs/autoupdate.html +++ b/chrome/common/extensions/docs/autoupdate.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Autoupdating - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/background_pages.html b/chrome/common/extensions/docs/background_pages.html index 5227b51..e0760ef 100644 --- a/chrome/common/extensions/docs/background_pages.html +++ b/chrome/common/extensions/docs/background_pages.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Background Pages - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/bookmarks.html b/chrome/common/extensions/docs/bookmarks.html index 8283626..b906190 100644 --- a/chrome/common/extensions/docs/bookmarks.html +++ b/chrome/common/extensions/docs/bookmarks.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Bookmarks - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/browserAction.html b/chrome/common/extensions/docs/browserAction.html index 77c2b6c..4e00a55 100644 --- a/chrome/common/extensions/docs/browserAction.html +++ b/chrome/common/extensions/docs/browserAction.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Browser Actions - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -473,7 +477,7 @@ in the <a href="#manifest">manifest</a>, or call the Browser action icons should seem a little bigger and heavier than page action icons. </li><li><b>Don't</b> attempt to mimic - Google Chrome's monochrome "wrench" and "page" icons. + Google Chrome's monochrome "wrench" icon. That doesn't work well with themes, and anyway, extensions should stand out a little. </li><li><b>Do</b> use alpha transparency diff --git a/chrome/common/extensions/docs/build/build.py b/chrome/common/extensions/docs/build/build.py index 524e989..1ff2e0f 100755 --- a/chrome/common/extensions/docs/build/build.py +++ b/chrome/common/extensions/docs/build/build.py @@ -162,8 +162,14 @@ def main(): "build.sh script instead, which uses depot_tools python.") parser = OptionParser() - parser.add_option("--test-shell-path", dest="test_shell_path") - parser.add_option("--page-name", dest="page_name") + parser.add_option("--test-shell-path", dest="test_shell_path", + metavar="PATH", + help="path to test_shell executable") + parser.add_option("--page-name", dest="page_name", metavar="PAGE", + help="only generate docs for PAGE.html") + parser.add_option("--nozip", dest="zips", action="store_false", + help="do not generate zip files for samples", + default=True) (options, args) = parser.parse_args() if (options.test_shell_path and os.path.isfile(options.test_shell_path)): @@ -195,6 +201,11 @@ def main(): samples_manifest = SamplesManifest(_samples_dir, _base_dir, api_manifest) samples_manifest.writeToFile(_samples_json) + # Write zipped versions of the samples listed in the manifest to the + # filesystem, unless the user has disabled it + if options.zips: + samples_manifest.writeZippedSamples() + modified_files = RenderPages(page_names, test_shell) if (len(modified_files) == 0): diff --git a/chrome/common/extensions/docs/build/directory.py b/chrome/common/extensions/docs/build/directory.py index 312e5ac..79f5dd7 100644 --- a/chrome/common/extensions/docs/build/directory.py +++ b/chrome/common/extensions/docs/build/directory.py @@ -221,16 +221,11 @@ class SamplesManifest(object): def writeToFile(self, path): """ Writes the contents of this manifest file as a JSON-encoded text file. - For each sample written to the manifest, create a zip file with the sample - contents in the sample's parent directory. Args: path: The path to write the samples manifest file to. """ - for sample in self._manifest_data['samples']: - sample.write_zip() - manifest_text = json.dumps(self._manifest_data, indent=2) output_path = os.path.realpath(path) try: @@ -241,6 +236,13 @@ class SamplesManifest(object): output_file.write(manifest_text) output_file.close() + def writeZippedSamples(self): + """ For each sample in the current manifest, create a zip file with the + sample contents in the sample's parent directory. """ + + for sample in self._manifest_data['samples']: + sample.write_zip() + class Sample(dict): """ Represents metadata about a Chrome extension sample. @@ -275,6 +277,7 @@ class Sample(dict): self['search_string'] = self._get_search_string() self['source_files'] = self._parse_source_files() self['id'] = hashlib.sha1(self['path']).hexdigest() + self['zip_path'] = self._get_relative_zip_path() _FEATURE_ATTRIBUTES = ( 'browser_action', @@ -371,6 +374,19 @@ class Sample(dict): return real_manifest_path.replace(real_base_path, '')\ .replace('manifest.json', '')[1:] + def _get_relative_zip_path(self): + """ Returns a relative path from the base dir to the sample's zip file. + + Intended for locating the zip file for the sample in the samples manifest. + + Returns: + A relative directory path form the sample manifest's directory to this + sample's zip file. + """ + zip_filename = self._get_zip_filename() + zip_relpath = os.path.dirname(os.path.dirname(self._get_relative_path())) + return os.path.join(zip_relpath, zip_filename) + def _get_search_string(self): """ Constructs a string to be used when searching the samples list. @@ -394,6 +410,17 @@ class Sample(dict): .upper() return search_string + def _get_zip_filename(self): + """ Returns the filename to be used for a generated zip of the sample. + + Returns: + A string in the form of "<dirname>.zip" where <dirname> is the name + of the directory containing this sample's manifest.json. + """ + sample_path = os.path.realpath(os.path.dirname(self._manifest_path)) + sample_dirname = os.path.basename(sample_path) + return "%s.zip" % sample_dirname + def _parse_api_calls(self, api_methods): """ Returns a list of Chrome extension API calls the sample makes. @@ -589,7 +616,7 @@ class Sample(dict): sample_dirname = os.path.basename(sample_path) sample_parentpath = os.path.dirname(sample_path) - zip_filename = "%s.zip" % sample_dirname + zip_filename = self._get_zip_filename() zip_path = os.path.join(sample_parentpath, zip_filename) zip_file = zipfile.ZipFile(zip_path, 'w') @@ -603,11 +630,6 @@ class Sample(dict): # Relative path to store the file in under the zip. relpath = sample_dirname + abspath.replace(sample_path, "") zip_file.write(abspath, relpath) - - self['zip_path'] = os.path.join( - os.path.dirname(os.path.dirname(self._get_relative_path())), - zip_filename) - except RuntimeError, msg: raise Exception("Could not write zip at " % zip_path) finally: diff --git a/chrome/common/extensions/docs/content_scripts.html b/chrome/common/extensions/docs/content_scripts.html index 4a5352e..c2845b8 100644 --- a/chrome/common/extensions/docs/content_scripts.html +++ b/chrome/common/extensions/docs/content_scripts.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Content Scripts - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/contextMenus.html b/chrome/common/extensions/docs/contextMenus.html index a7b2cdb..6f055fd 100644 --- a/chrome/common/extensions/docs/contextMenus.html +++ b/chrome/common/extensions/docs/contextMenus.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Context Menus - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -537,7 +541,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>This must be specified unless type is 'separator'.</dd> + <dd>The text to be displayed in the item; this is <em>required</em> unless <em>type</em> is 'separator'. When the context is 'selection', you can use <code>%s</code> within the string to show the selected text. For example, if this parameter's value is "Translate '%s' to Pig Latin" and the user selects the word "cool", the context menu item for the selection is "Translate 'cool' to Pig Latin".</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -595,7 +599,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>This only applies to the initial state of a checkbox or radio item. It indicates if the item should initially be checked/selected. Only one radio item can be selected at a time in a given group of radio items.</dd> + <dd>The initial state of a checkbox or radio item: true for selected and false for unselected. Only one radio item can be selected at a time in a given group of radio items.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -865,7 +869,7 @@ You can find samples of this API on the </div> </div> - </dl> + </dl> </div> </dd> @@ -901,7 +905,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The id of a parent menu item - this makes the item a child of a previously added item.</dd> + <dd>The ID of a parent menu item; this makes the item a child of a previously added item.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -970,7 +974,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Lets you restrict the item to only apply to documents whose URL matches one of the given patterns. (This applies to frames as well). For details on the format of a pattern, see <a href="match_patterns.html">Match Patterns</a>.</dd> + <dd>Lets you restrict the item to apply only to documents whose URL matches one of the given patterns. (This applies to frames as well.) For details on the format of a pattern, see <a href="match_patterns.html">Match Patterns</a>.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1039,7 +1043,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Similar to documentUrlPatterns, but this lets you filter based on the src attribute of img/audio/video tags and the href of anchor tags.</dd> + <dd>Similar to documentUrlPatterns, but lets you filter based on the src attribute of img/audio/video tags and the href of anchor tags.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1107,7 +1111,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Called when the item has been created in the browser. If there were any problems creating the item, details will be avilable in chrome.extension.lastError.</dd> + <dd>Called when the item has been created in the browser. If there were any problems creating the item, details will be available in chrome.extension.lastError.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1171,7 +1175,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The id of the newly created item.</dd> + <dd>The ID of the newly created item.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1282,7 +1286,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The id of the context menu item to remove.</dd> + <dd>The ID of the context menu item to remove.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1580,7 +1584,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The id of the item to update.</dd> + <dd>The ID of the item to update.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2401,7 +2405,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The id of the menu item that was clicked.</dd> + <dd>The ID of the menu item that was clicked.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2459,7 +2463,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The parent id, if any, for the item clicked.</dd> + <dd>The parent ID, if any, for the item clicked.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2575,7 +2579,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>If the element is a link, the url it points to.</dd> + <dd>If the element is a link, the URL it points to.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2633,7 +2637,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Will be present for elements with a 'src' url.</dd> + <dd>Will be present for elements with a 'src' URL.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2691,7 +2695,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The url of the page where the menu item was clicked.</dd> + <dd>The URL of the page where the menu item was clicked.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2749,7 +2753,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd> The url of the frame of the element where the context menu was clicked, if it was in a frame.</dd> + <dd> The URL of the frame of the element where the context menu was clicked, if it was in a frame.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. diff --git a/chrome/common/extensions/docs/cookies.html b/chrome/common/extensions/docs/cookies.html index ea79e4c..482a342 100644 --- a/chrome/common/extensions/docs/cookies.html +++ b/chrome/common/extensions/docs/cookies.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Cookies - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/crx.html b/chrome/common/extensions/docs/crx.html index 9cd28ff..22ad2b5 100644 --- a/chrome/common/extensions/docs/crx.html +++ b/chrome/common/extensions/docs/crx.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>CRX Package Format - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/css/ApiRefStyles.css b/chrome/common/extensions/docs/css/ApiRefStyles.css index b3b86f3..ebc8669 100644 --- a/chrome/common/extensions/docs/css/ApiRefStyles.css +++ b/chrome/common/extensions/docs/css/ApiRefStyles.css @@ -800,6 +800,29 @@ a.leftNavSelected,.leftNavSelected a,a.leftNavSelected:visited,.leftNavSelected background:#fff } +#gc-toc .toggle { + background: url('../images/toggle_sprite.png') no-repeat 0px 0px; + width: 9px; + height: 9px; + overflow: hidden; + float: left; + text-decoration: none; + margin: 4px 1px 4px -13px; + cursor: pointer; +} + +#gc-toc .toggle.selected { + background-position: 0 -9px; +} + +#gc-toc .toggle:hover { + background-position: 0 -18px; +} + +#gc-toc .toggle.selected:hover { + background-position: 0 -27px; +} + #toc h2 { font-weight:bold; font-size:100%; diff --git a/chrome/common/extensions/docs/devguide.html b/chrome/common/extensions/docs/devguide.html index 298996e..2669a04 100644 --- a/chrome/common/extensions/docs/devguide.html +++ b/chrome/common/extensions/docs/devguide.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Developer's Guide - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/docs.html b/chrome/common/extensions/docs/docs.html index 2a81648..685f6aa 100644 --- a/chrome/common/extensions/docs/docs.html +++ b/chrome/common/extensions/docs/docs.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Hello There! - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/events.html b/chrome/common/extensions/docs/events.html index 7fbf71e..be42e7b 100644 --- a/chrome/common/extensions/docs/events.html +++ b/chrome/common/extensions/docs/events.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Events - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/examples/api/omnibox/extension-docs.zip b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs.zip Binary files differnew file mode 100644 index 0000000..7e81cea --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs.zip diff --git a/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/background.html b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/background.html new file mode 100644 index 0000000..19fcc45 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/background.html @@ -0,0 +1,466 @@ +<!DOCTYPE html> +<!-- + * 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. +--> +<html> + <head> + </head> + <body> + <script> + /** + * Allows for binding callbacks to a specific scope. + * @param {Object} scope Scope to bind to. + * @returns {Function} A wrapped call to this function. + */ + Function.prototype.bind = function(scope) { + var func = this; + return function() { + return func.apply(scope, arguments); + }; + }; + + ////////////////////////////////////////////////////////////////////////// + + /** + * Holds the search index and exposes operations to search the API docs. + * @constructor + */ + function APISearchCorpus() { + this.corpus_ = []; + }; + + /** + * Adds an entry to the index. + * @param {String} name Name of the function (e.g. chrome.tabs.get). + * @param {String} url Url to the documentation. + * @param {String} desc Description (optional). + * @param {String} type The type of entry (e.g. method, event). + */ + APISearchCorpus.prototype.addEntry = function(name, url, desc, type) { + this.corpus_.push({ + 'name' : name, + 'url' : url, + 'ranges' : [], + 'description' : desc, + 'type' : type + }); + }; + + /** + * Locates a match from the supplied keywords against text. + * + * Keywords are matched in the order supplied, and a non-overlapping + * search is used. The ranges are returned in a way that is easily + * converted to the style array required by the omnibox API. + * + * @param {Array.<String>} keywords A list of keywords to check. + * @param {String} name The name to search against. + * @returns {Array.<Array.<number>>|null} A list of indexes corresponding + * to matches, or null if no match was found. + */ + APISearchCorpus.prototype.findMatch_ = function(keywords, name) { + var ranges = []; + var indexFrom = 0; + for (var i = 0; i < keywords.length; i++) { + var keyword = keywords[i].toLowerCase(); + var start = name.indexOf(keyword, indexFrom); + if (start == -1) { + return null; + } + var end = start + keyword.length; + ranges.push([start, end]); + indexFrom = end + 1; + } + return ranges; + }; + + /** + * Searches this corpus for the supplied text. + * @param {String} text Query text. + * @param {Number} limit Max results to return. + * @returns {Array.<Object>} A list of entries corresponding with + * matches (@see APISearchCorpus.findMatch_ for keyword search + * algorithm. Results are returned in a sorted order, first by + * length, then alphabetically by name. An exact match will be + * returned first. + */ + APISearchCorpus.prototype.search = function(text, limit) { + var results = []; + var match = null; + if (!text || text.length == 0) { + return this.corpus_.slice(0, limit); // No text, start listing APIs. + } + var searchText = text.toLowerCase(); + var keywords = searchText.split(' '); + for (var i = 0; i < this.corpus_.length; i++) { + var name = this.corpus_[i]['name'].toLowerCase(); + if (results.length < limit) { + var result = this.findMatch_(keywords, name); + if (result) { + this.corpus_[i]['ranges'] = result; + results.push(this.corpus_[i]); + } + } + if (!match && searchText == name) { + match = this.corpus_[i]; // An exact match. + } + if (match && results.length >= limit) { + break; // Have an exact match and have reached the search limit. + } + } + if (match) { + results.unshift(match); // Add any exact match to the front. + } + return results; + }; + + /** + * Sorts the corpus according to name length, then name alphabetically. + */ + APISearchCorpus.prototype.sort = function() { + function compareLength(a, b) { + return a['name'].length - b['name'].length; + }; + + function compareAlpha(a, b) { + if (a['name'] < b['name']) return -1; + if (a['name'] > b['name']) return 1; + return 0; + }; + + function compare(a, b) { + var result = compareLength(a, b); + if (result == 0) result = compareAlpha(a, b); + return result; + }; + + this.corpus_.sort(compare); + }; + + ////////////////////////////////////////////////////////////////////////// + + /** + * Provides an interface to the Chrome Extensions documentation site. + * @param {APISearchCorpus} corpus The search corpus to populate. + * @constructor + */ + function DocsManager(corpus) { + this.CODE_URL_PREFIX = 'http://code.google.com/chrome/extensions/'; + this.API_MANIFEST_URL = [ + 'http://src.chromium.org/viewvc/chrome/trunk/src/', + 'chrome/common/extensions/api/extension_api.json' + ].join(''); + this.corpus_ = corpus; + }; + + /** + * Initiates a fetch of the docs and populates the corpus. + */ + DocsManager.prototype.fetch = function() { + this.fetchApi_(this.onApi_.bind(this)); + }; + + /** + * Retrieves the API manifest from cache or fetches a new one if none. + * @param {Function} callback The function to pass the parsed manifest + * data to. + */ + DocsManager.prototype.fetchApi_ = function(callback) { + var currentCacheTime = this.getCacheTime_(); + if (localStorage['cache-time'] && localStorage['cache']) { + var cacheTime = JSON.parse(localStorage['cache-time']); + if (cacheTime < currentCacheTime) { + callback(JSON.parse(localStorage['cache'])); + return; + } + } + var xhr = new XMLHttpRequest(); + xhr.addEventListener('readystatechange', function(evt) { + if (xhr.readyState == 4 && xhr.responseText) { + localStorage['cache-time'] = JSON.stringify(currentCacheTime); + localStorage['cache'] = xhr.responseText; + var json = JSON.parse(xhr.responseText); + callback(json); + } + }); + xhr.open('GET', this.API_MANIFEST_URL, true); + xhr.send(); + }; + + /** + * Returns a time which can be used to cache a manifest response. + * @returns {Number} A timestamp divided by the number of ms in a day, + * rounded to the nearest integer. This means the number should + * change only once per day, invalidating the cache that often. + */ + DocsManager.prototype.getCacheTime_ = function() { + var time = new Date().getTime(); + time = Math.round(time / (1000 * 60 * 60 * 24)); + return time; + }; + + /** + * Returns an URL for the documentation given an API element. + * @param {String} namespace The namespace (e.g. tabs, windows). + * @param {String} type The type of element (e.g. event, method, type). + * @param {String} name The name of the element (e.g. onRemoved). + * @returns {String} An URL corresponding with the documentation for the + * given element. + */ + DocsManager.prototype.getDocLink_ = function(namespace, type, name) { + var linkparts = [ this.CODE_URL_PREFIX, namespace, '.html' ]; + if (type && name) { + linkparts.push('#', type, '-', name); + } + return linkparts.join(''); + }; + + /** + * Returns a qualified name for an API element. + * @param {String} namespace The namespace (e.g. tabs, windows). + * @param {String} name The name of the element (e.g. onRemoved). + * @returns {String} A qualified API name (e.g. chrome.tabs.onRemoved). + */ + DocsManager.prototype.getName_ = function(namespace, name) { + var nameparts = [ 'chrome', namespace ]; + if (name) { + nameparts.push(name); + } + return nameparts.join('.'); + }; + + /** + * Parses an API manifest data structure and populates the search index. + * @param {Object} api The api manifest, as a JSON-parsed object. + */ + DocsManager.prototype.onApi_ = function(api) { + for (var i = 0; i < api.length; i++) { + var module = api[i]; + if (module.nodoc) { + continue; + } + var ns = module.namespace; + var nsName = this.getName_(ns); + var nsUrl = this.getDocLink_(ns); + this.corpus_.addEntry(nsName, nsUrl, null, 'namespace'); + this.parseAPIArray_('method', ns, module.functions); + this.parseAPIArray_('event', ns, module.events); + this.parseAPIArray_('type', ns, module.types); + this.parseAPIObject_('property', ns, module.properties); + this.corpus_.sort(); + } + }; + + /** + * Parses an API manifest subsection which is formatted as an Array. + * @param {String} type The type of data (e.g. method, event, type). + * @param {String} ns The namespace (e.g. tabs, windows). + * @param {Array} list The list of API elements. + */ + DocsManager.prototype.parseAPIArray_ = function(type, ns, list) { + if (!list) return; + for (var j = 0; j < list.length; j++) { + var item = list[j]; + if (item.nodoc) continue; + var name = item.name || item.id; + var fullname = this.getName_(ns, name); + var url = this.getDocLink_(ns, type, name); + var description = item.description; + this.corpus_.addEntry(fullname, url, description, type); + } + }; + + /** + * Parses an API manifest subsection which is formatted as an Object. + * @param {String} type The type of data (e.g. property). + * @param {String} ns The namespace (e.g. tabs, windows). + * @param {Object} list The object containing API elements. + */ + DocsManager.prototype.parseAPIObject_ = function(type, ns, list) { + for (var prop in list) { + if (list.hasOwnProperty(prop)) { + var name = this.getName_(ns, prop); + var url = this.getDocLink_(ns, type, prop); + var description = list[prop].description; + this.corpus_.addEntry(name, url, description, type); + } + } + }; + + ////////////////////////////////////////////////////////////////////////// + + /** + * Manages text input into the omnibox and returns search results. + * @param {APISearchCorpus} Populated search corpus. + * @param {TabManager} Manager to use to open tabs. + * @constructor + */ + function OmniboxManager(corpus, tabManager) { + this.SEPARATOR = ' - '; + this.corpus_ = corpus; + this.tabManager_ = tabManager; + chrome.experimental.omnibox.onInputChanged.addListener( + this.onChanged_.bind(this)); + chrome.experimental.omnibox.onInputEntered.addListener( + this.onEntered_.bind(this)); + }; + + /** + * Converts a corpus match to an object suitable for the omnibox API. + * @param {Object} match The match to convert. + * @returns {Object} A suggestion object formatted for the omnibox API. + */ + OmniboxManager.prototype.matchToSuggestion_ = function(match) { + var styles = [ ]; + var ranges = match['ranges']; + var desc = match['name']; + var name = match['name']; + var lastIndex = 0; + for (var i = 0; i < ranges.length; i++) { + styles.push(chrome.experimental.omnibox.styleMatch(ranges[i][0])); + styles.push(chrome.experimental.omnibox.styleNone(ranges[i][1])); + lastIndex = ranges[i][1]; + } + + if (match['type']) { + // Abusing the URL style a little, but want this to stand out. + desc = this.pushStyle_(styles, 'Url', desc, match['type']); + lastIndex = desc.length; + } + if (match['description']) { + desc = this.pushStyle_(styles, 'Dim', desc, match['description']); + lastIndex = desc.length; + } + + if (lastIndex == desc.length) styles.pop(); + + return { + 'content' : name, + 'description' : desc, + 'descriptionStyles' : styles + }; + }; + + /** + * Suggests a list of possible matches when omnibox text changes. + * @param {String} text Text input from the omnibox. + * @param {Function} suggest Callback to execute with a list of + * suggestion objects, if any matches were found. + */ + OmniboxManager.prototype.onChanged_ = function(text, suggest) { + var matches = this.corpus_.search(text, 10); + var suggestions = []; + for (var i = 0; i < matches.length; i++) { + var suggestion = this.matchToSuggestion_(matches[i]); + suggestions.push(suggestion); + } + suggest(suggestions); + }; + + /** + * Opens the most appropriate URL when enter is pressed in the omnibox. + * + * Note that the entered text does not have to be exact - the first + * search result is automatically opened when enter is pressed. + * + * @param {String} text The text entered. + */ + OmniboxManager.prototype.onEntered_ = function(text) { + var matches = this.corpus_.search(text, 1); + if (matches.length > 0) { + this.tabManager_.open(matches[0]['url']); + } + }; + + /** + * Helper function for constructing a suggestion style list. + * + * Adds a separator and text to a description, and modifies an array + * of styles so that the separator is not styled and the additional text + * obtains the requested style type. + * + * This method expects the list of styles to end with a styleNone style. + * It will modify the list so that the last element is a styleNone style. + * The last element will always be at the end of the returned string, + * which will throw an error unless it is removed before being passed + * to the API (this method is intended to be called a few times in a row). + * @see OmniboxManager.matchToSuggestion_ for code that removes this last + * entry. + * + * @param {Array.<Object>} styles An array of styles, in the format + * expected by the omnibox API. + * @param {String} type The style type to apply - must be one of + * "Dim", "Match", "None", and "Url". + * @param {String} desc The description text to append to and style. + * @param {String} text The text to append to the description. + * @returns {String} The description plus a separator and the supplied + * text, intended to overwrite the variable which was passed into + * this function as "desc". + */ + OmniboxManager.prototype.pushStyle_ = function(styles, type, desc, text) { + desc += this.SEPARATOR; + styles.push(chrome.experimental.omnibox['style' + type](desc.length)); + desc += text; + styles.push(chrome.experimental.omnibox.styleNone(desc.length)); + return desc; + }; + + ////////////////////////////////////////////////////////////////////////// + + /** + * Manages opening urls in tabs. + * @constructor + */ + function TabManager() { + this.tab_ = null; + chrome.tabs.onRemoved.addListener(this.onRemoved_.bind(this)); + }; + + /** + * When a tab is removed, see if it was opened by us and null out if yes. + * @param {Number} tabid ID of the removed tab. + */ + TabManager.prototype.onRemoved_ = function(tabid) { + if (this.tab_ && tabid == this.tab_.id) this.tab_ = null; + }; + + /** + * When a tab opened by us is created, store it for future updates. + * @param {Tab} tab The tab which was just opened. + */ + TabManager.prototype.onTab_ = function(tab) { + this.tab_ = tab; + }; + + /** + * Opens the supplied URL. + * + * The first time this method is called a new tab is created. Subsequent + * times this is called, the opened tab will be updated. If that tab + * is ever closed, then a new tab will be created for the next call. + * + * @param {String} url The URL to open. + */ + TabManager.prototype.open = function(url) { + if (url) { + var args = { 'url' : url, 'selected' : true }; + if (this.tab_) { + chrome.tabs.update(this.tab_.id, args); + } else { + chrome.tabs.create(args, this.onTab_.bind(this)); + } + } + }; + + ////////////////////////////////////////////////////////////////////////// + + var corpus = new APISearchCorpus(); + var docsManager = new DocsManager(corpus); + docsManager.fetch(); + var tabManager = new TabManager(); + var omnibox = new OmniboxManager(corpus, tabManager); + </script> + </body> +</html> diff --git a/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-128.png b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-128.png Binary files differnew file mode 100644 index 0000000..c7e114f --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-128.png diff --git a/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-16.png b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-16.png Binary files differnew file mode 100644 index 0000000..23b34a0 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-16.png diff --git a/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/manifest.json b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/manifest.json new file mode 100644 index 0000000..8e6b3b9 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/manifest.json @@ -0,0 +1,16 @@ +{ + "name" : "Extension Docs Search", + "description" : "Search the Chrome Extensions documentation. To use, type 'crdoc' plus a search term into the Omnibox.", + "version" : "1.0.0", + "permissions" : [ + "experimental", + "http://src.chromium.org/viewvc/*", + "tabs" + ], + "icons" : { + "16" : "icon-16.png", + "128" : "icon-128.png" + }, + "omnibox_keyword" : "crdoc", + "background_page" : "background.html" +} diff --git a/chrome/common/extensions/docs/examples/api/omnibox.zip b/chrome/common/extensions/docs/examples/api/omnibox/simple-example.zip Binary files differindex 58ba199..532eb00 100644 --- a/chrome/common/extensions/docs/examples/api/omnibox.zip +++ b/chrome/common/extensions/docs/examples/api/omnibox/simple-example.zip diff --git a/chrome/common/extensions/docs/examples/api/omnibox/background.html b/chrome/common/extensions/docs/examples/api/omnibox/simple-example/background.html index ff83ff2..ff83ff2 100644 --- a/chrome/common/extensions/docs/examples/api/omnibox/background.html +++ b/chrome/common/extensions/docs/examples/api/omnibox/simple-example/background.html diff --git a/chrome/common/extensions/docs/examples/api/omnibox/manifest.json b/chrome/common/extensions/docs/examples/api/omnibox/simple-example/manifest.json index 2b63ca2..c48a345 100644 --- a/chrome/common/extensions/docs/examples/api/omnibox/manifest.json +++ b/chrome/common/extensions/docs/examples/api/omnibox/simple-example/manifest.json @@ -1,5 +1,6 @@ { "name": "Omnibox Example", + "description" : "To use, type 'omnix' plus a search term into the Omnibox.", "version": "1.0", "permissions": [ "experimental" ], "background_page": "background.html", diff --git a/chrome/common/extensions/docs/examples/api/processes/process_monitor.zip b/chrome/common/extensions/docs/examples/api/processes/process_monitor.zip Binary files differnew file mode 100644 index 0000000..60d5641 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/processes/process_monitor.zip diff --git a/chrome/common/extensions/docs/examples/api/processes/show_tabs.zip b/chrome/common/extensions/docs/examples/api/processes/show_tabs.zip Binary files differindex 770b435..ba3c761 100644 --- a/chrome/common/extensions/docs/examples/api/processes/show_tabs.zip +++ b/chrome/common/extensions/docs/examples/api/processes/show_tabs.zip diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark.zip b/chrome/common/extensions/docs/examples/extensions/benchmark.zip Binary files differindex af258d4..7f43bdb 100644 --- a/chrome/common/extensions/docs/examples/extensions/benchmark.zip +++ b/chrome/common/extensions/docs/examples/extensions/benchmark.zip diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/README.txt b/chrome/common/extensions/docs/examples/extensions/benchmark/README.txt index 8136ab3..2267580 100644 --- a/chrome/common/extensions/docs/examples/extensions/benchmark/README.txt +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/README.txt @@ -14,5 +14,14 @@ To use this benchmark, you'll need to run chrome with the the the benchmark can clear idle connections and the cache. The code found in the jst/ subdirectory is JSTemplate code from -http://code.google.com/p/google-jstemplate/ +http://code.google.com/p/google-jstemplate/. + +In jquery/, jquery-1.4.2.min.js is from http://jquery.com/. jquery.flot.min.js +is a plotting library and from http://code.google.com/p/flot/. +jquery.flot.dashes.js is an enhancement of Flot for dashed lines and from +http://code.google.com/p/flot/issues/detail?id=61. + +In util/, sortable.js serves for sorting table content and is from +http://www.kryogenix.org/code/browser/sorttable/. table2CSV.js is for exporting +table data to .csv and from http://www.kunalbabre.com/projects/table2CSV.php. diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/background.html b/chrome/common/extensions/docs/examples/extensions/benchmark/background.html index ca712ff..36e4c31 100644 --- a/chrome/common/extensions/docs/examples/extensions/benchmark/background.html +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/background.html @@ -163,19 +163,22 @@ function Benchmark() { current_ = {}; current_.url = url; + current_.timestamp = new Date(); current_.viaSpdy = false; current_.startLoadResults = new Array(); // times to start current_.commitLoadResults = new Array(); // times to commit current_.docLoadResults = new Array(); // times to docload current_.paintResults = new Array(); // times to paint current_.totalResults = new Array(); // times to complete load - current_.bytesRead = 0; - current_.bytesWritten = 0; + current_.KbytesRead = new Array(); + current_.KbytesWritten = new Array(); + current_.readbpsResults = new Array(); + current_.writebpsResults = new Array(); current_.totalTime = 0; current_.iterations = 0; - current_.requests = 0; - current_.connects = 0; - current_.spdySessions = 0; + current_.requests = new Array(); + current_.connects = new Array(); + current_.spdySessions = new Array(); current_.domNum = 0; current_.maxDepth = 0; current_.minDepth = 0; @@ -237,7 +240,7 @@ function Benchmark() { this.openNextPage = function() { var benchmark = nextBenchmark(); benchmark.pageStart(); - chrome.tabs.create({"url": benchmark.url(),"selected": false}, + chrome.tabs.create({"url": benchmark.url(),"selected": true}, function(tab) { benchmarkWindow = tab; // script.js only executes on tested pages @@ -297,6 +300,43 @@ function Benchmark() { // Called when a page finishes loading. this.pageFinished = function(load_times, domNum, depths) { + + // Make sure the content can be fetched via spdy if it is enabled. + if (window.enableSpdy && !load_times.wasFetchedViaSpdy) { + alert("Can not fetch current url via spdy.\n" + + "Ending current test."); + me_.finish(); + // Move on to next benchmarked pages. + if (benchmarks.length > 0) { + if (window.clearConnections) { + chrome.benchmarking.closeConnections(); + } + setTimeout(me_.runPage, 100); + } + return; + } + + // If last fetch was via spdy, current fetch should use spdy too. Same + // for vise versa. + if (current_.iterations > 0 && + current_.viaSpdy != load_times.wasFetchedViaSpdy) { + alert("Error: viaSpdy for current fetch is different from last fetch!\n" + + "Ending current test."); + // Current data set is invalid: remove from the result array. + var currIndex; + currIndex = window.results.data.indexOf(current_, 0); + window.results.data.splice(currIndex, 1); + me_.displayResults(); + me_.finish(); + if (benchmarks.length > 0) { + if (window.clearConnections) { + chrome.benchmarking.closeConnections(); + } + setTimeout(me_.runPage, 100); + } + return; + } + var requested = load_times.requestTime; var started = load_times.startLoadTime; var startLoadTime = @@ -326,22 +366,6 @@ function Benchmark() { totalTime_ += totalTime; count_++; - // Make sure the content can be fetched via spdy if it is enabled. - if (window.enableSpdy && !load_times.wasFetchedViaSpdy) { - alert("Can not fetch current url via spdy.\n" + - "Ending current test."); - runCount_ = 1; - } - - // If last fetch was via spdy, current fetch should use spdy too. Same - // for vise versa. - if (current_.iterations > 0 && - current_.viaSpdy != load_times.wasFetchedViaSpdy) { - alert("Error: viaSpdy for current fetch is different from last fetch!\n" + - "Ending current test."); - runCount_ = 1; - } - // Get the index of current benchmarked page in the result array. var currIndex; currIndex = window.results.data.indexOf(current_, 0); @@ -354,16 +378,20 @@ function Benchmark() { current_.docLoadResults.push(docLoadTime); current_.paintResults.push(paintTime); current_.totalResults.push(totalTime); - current_.bytesRead += chrome.benchmarking.counter(kTCPReadBytes) - - initialReadBytes_; - current_.bytesWritten += chrome.benchmarking.counter(kTCPWriteBytes) - - initialWriteBytes_; - current_.requests += chrome.benchmarking.counter(kRequestCount) - - initialRequestCount_; - current_.connects += chrome.benchmarking.counter(kConnectCount) - - initialConnectCount_; - current_.spdySessions += chrome.benchmarking.counter(kSpdySessionCount) - - initialSpdySessionCount_; + var bytesRead = chrome.benchmarking.counter(kTCPReadBytes) - + initialReadBytes_; + var bytesWrite = chrome.benchmarking.counter(kTCPWriteBytes) - + initialWriteBytes_; + current_.KbytesRead.push(bytesRead / 1024); + current_.KbytesWritten.push(bytesWrite / 1024); + current_.readbpsResults.push(bytesRead * 8 / totalTime); + current_.writebpsResults.push(bytesWrite * 8 / totalTime); + current_.requests.push(chrome.benchmarking.counter(kRequestCount) - + initialRequestCount_); + current_.connects.push(chrome.benchmarking.counter(kConnectCount) - + initialConnectCount_); + current_.spdySessions.push(chrome.benchmarking.counter(kSpdySessionCount) - + initialSpdySessionCount_); current_.totalTime += totalTime; current_.domNum = domNum; current_.maxDepth = depths[0]; @@ -374,7 +402,7 @@ function Benchmark() { if (currIndex == -1) { window.results.data.push(current_); } else { - window.results.data[currIndex]=current_; + window.results.data[currIndex] = current_; } if (--runCount_ == 0) { @@ -386,7 +414,6 @@ function Benchmark() { if (window.clearConnections) { chrome.benchmarking.closeConnections(); } - setTimeout(me_.runPage, 100); } diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-1.4.2.min.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-1.4.2.min.js new file mode 100644 index 0000000..7c24308 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-1.4.2.min.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i? +e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r= +j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g, +"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e= +true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)|| +c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded", +L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype, +"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+ +a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f], +d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]=== +a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&& +!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari= +true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ", +i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ", +" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className= +this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i= +e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!= +null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), +fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop|| +d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this, +"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent= +a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y, +isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit= +{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}}; +if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&& +!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}}, +toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector, +u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "), +function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q]; +if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[]; +for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length- +1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, +CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}}, +relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]= +l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[]; +h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m= +m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition|| +!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m= +h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>"; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/, +gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length; +c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)? +a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&& +this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]|| +u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length=== +1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay"); +this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a], +"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)}, +animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing= +j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]); +this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length|| +c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement? +function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b= +this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle; +k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&& +f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>"; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-ui-1.8.4.custom.min.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-ui-1.8.4.custom.min.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-ui-1.8.4.custom.min.js diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.client.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.client.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.client.js diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.dashes.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.dashes.js new file mode 100644 index 0000000..7cebc92 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.dashes.js @@ -0,0 +1,237 @@ +/* + * jQuery.flot.dashes + * + * options = { + * series: { + * dashes: { + * + * // show + * // default: false + * // Whether to show dashes for the series. + * show: <boolean>, + * + * // lineWidth + * // default: 2 + * // The width of the dashed line in pixels. + * lineWidth: <number>, + * + * // dashLength + * // default: 10 + * // Controls the length of the individual dashes and the amount of + * // space between them. + * // If this is a number, the dashes and spaces will have that length. + * // If this is an array, it is read as [ dashLength, spaceLength ] + * dashLength: <number> or <array[2]> + * } + * } + * } + */ +(function($){ + + function init(plot) { + + plot.hooks.processDatapoints.push(function(plot, series, datapoints) { + + if (!series.dashes.show) return; + + plot.hooks.draw.push(function(plot, ctx) { + + var plotOffset = plot.getPlotOffset(), + axisx = series.xaxis, + axisy = series.yaxis; + + function plotDashes(xoffset, yoffset) { + + var points = datapoints.points, + ps = datapoints.pointsize, + prevx = null, + prevy = null, + dashRemainder = 0, + dashOn = true, + dashOnLength, + dashOffLength; + + if (series.dashes.dashLength[0]) { + dashOnLength = series.dashes.dashLength[0]; + if (series.dashes.dashLength[1]) { + dashOffLength = series.dashes.dashLength[1]; + } else { + dashOffLength = dashOnLength; + } + } else { + dashOffLength = dashOnLength = series.dashes.dashLength; + } + + ctx.beginPath(); + + for (var i = ps; i < points.length; i += ps) { + + var x1 = points[i - ps], + y1 = points[i - ps + 1], + x2 = points[i], + y2 = points[i + 1]; + + if (x1 == null || x2 == null) continue; + + // clip with ymin + if (y1 <= y2 && y1 < axisy.min) { + if (y2 < axisy.min) continue; // line segment is outside + // compute new intersection point + x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.min; + } else if (y2 <= y1 && y2 < axisy.min) { + if (y1 < axisy.min) continue; + x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.min; + } + + // clip with ymax + if (y1 >= y2 && y1 > axisy.max) { + if (y2 > axisy.max) continue; + x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.max; + } else if (y2 >= y1 && y2 > axisy.max) { + if (y1 > axisy.max) continue; + x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.max; + } + + // clip with xmin + if (x1 <= x2 && x1 < axisx.min) { + if (x2 < axisx.min) continue; + y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.min; + } else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) continue; + y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.min; + } + + // clip with xmax + if (x1 >= x2 && x1 > axisx.max) { + if (x2 > axisx.max) continue; + y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.max; + } else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) continue; + y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.max; + } + + if (x1 != prevx || y1 != prevy) { + ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); + } + + var ax1 = axisx.p2c(x1) + xoffset, + ay1 = axisy.p2c(y1) + yoffset, + ax2 = axisx.p2c(x2) + xoffset, + ay2 = axisy.p2c(y2) + yoffset, + dashOffset; + + function lineSegmentOffset(segmentLength) { + + var c = Math.sqrt(Math.pow(ax2 - ax1, 2) + Math.pow(ay2 - ay1, 2)); + + if (c <= segmentLength) { + return { + deltaX: ax2 - ax1, + deltaY: ay2 - ay1, + distance: c, + remainder: segmentLength - c + } + } else { + var xsign = ax2 > ax1 ? 1 : -1, + ysign = ay2 > ay1 ? 1 : -1; + return { + deltaX: xsign * Math.sqrt(Math.pow(segmentLength, 2) / (1 + Math.pow((ay2 - ay1)/(ax2 - ax1), 2))), + deltaY: ysign * Math.sqrt(Math.pow(segmentLength, 2) - Math.pow(segmentLength, 2) / (1 + Math.pow((ay2 - ay1)/(ax2 - ax1), 2))), + distance: segmentLength, + remainder: 0 + }; + } + } + //-end lineSegmentOffset + + do { + + dashOffset = lineSegmentOffset( + dashRemainder > 0 ? dashRemainder : + dashOn ? dashOnLength : dashOffLength); + + if (dashOffset.deltaX != 0 || dashOffset.deltaY != 0) { + if (dashOn) { + ctx.lineTo(ax1 + dashOffset.deltaX, ay1 + dashOffset.deltaY); + } else { + ctx.moveTo(ax1 + dashOffset.deltaX, ay1 + dashOffset.deltaY); + } + } + + dashOn = !dashOn; + dashRemainder = dashOffset.remainder; + ax1 += dashOffset.deltaX; + ay1 += dashOffset.deltaY; + + } while (dashOffset.distance > 0); + + prevx = x2; + prevy = y2; + } + + ctx.stroke(); + } + //-end plotDashes + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + ctx.lineJoin = 'round'; + + var lw = series.dashes.lineWidth, + sw = series.shadowSize; + + // FIXME: consider another form of shadow when filling is turned on + if (lw > 0 && sw > 0) { + // draw shadow as a thick and thin line with transparency + ctx.lineWidth = sw; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + // position shadow at angle from the mid of line + var angle = Math.PI/18; + plotDashes(Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2)); + ctx.lineWidth = sw/2; + plotDashes(Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4)); + } + + ctx.lineWidth = lw; + ctx.strokeStyle = series.color; + + if (lw > 0) { + plotDashes(0, 0); + } + + ctx.restore(); + + }); + //-end draw hook + + }); + //-end processDatapoints hook + + } + //-end init + + $.plot.plugins.push({ + init: init, + options: { + series: { + dashes: { + show: false, + lineWidth: 2, + dashLength: 10 + } + } + }, + name: 'dashes', + version: '0.1' + }); + +})(jQuery) + diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.js diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.min.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.min.js new file mode 100644 index 0000000..31f465b --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.min.js @@ -0,0 +1 @@ +(function(){jQuery.color={};jQuery.color.make=function(G,H,J,I){var A={};A.r=G||0;A.g=H||0;A.b=J||0;A.a=I!=null?I:1;A.add=function(C,D){for(var E=0;E<C.length;++E){A[C.charAt(E)]+=D}return A.normalize()};A.scale=function(C,D){for(var E=0;E<C.length;++E){A[C.charAt(E)]*=D}return A.normalize()};A.toString=function(){if(A.a>=1){return"rgb("+[A.r,A.g,A.b].join(",")+")"}else{return"rgba("+[A.r,A.g,A.b,A.a].join(",")+")"}};A.normalize=function(){function C(E,D,F){return D<E?E:(D>F?F:D)}A.r=C(0,parseInt(A.r),255);A.g=C(0,parseInt(A.g),255);A.b=C(0,parseInt(A.b),255);A.a=C(0,A.a,1);return A};A.clone=function(){return jQuery.color.make(A.r,A.b,A.g,A.a)};return A.normalize()};jQuery.color.extract=function(E,F){var A;do{A=E.css(F).toLowerCase();if(A!=""&&A!="transparent"){break}E=E.parent()}while(!jQuery.nodeName(E.get(0),"body"));if(A=="rgba(0, 0, 0, 0)"){A="transparent"}return jQuery.color.parse(A)};jQuery.color.parse=function(A){var F,H=jQuery.color.make;if(F=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(A)){return H(parseInt(F[1],10),parseInt(F[2],10),parseInt(F[3],10))}if(F=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(A)){return H(parseInt(F[1],10),parseInt(F[2],10),parseInt(F[3],10),parseFloat(F[4]))}if(F=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(A)){return H(parseFloat(F[1])*2.55,parseFloat(F[2])*2.55,parseFloat(F[3])*2.55)}if(F=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(A)){return H(parseFloat(F[1])*2.55,parseFloat(F[2])*2.55,parseFloat(F[3])*2.55,parseFloat(F[4]))}if(F=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(A)){return H(parseInt(F[1],16),parseInt(F[2],16),parseInt(F[3],16))}if(F=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(A)){return H(parseInt(F[1]+F[1],16),parseInt(F[2]+F[2],16),parseInt(F[3]+F[3],16))}var G=jQuery.trim(A).toLowerCase();if(G=="transparent"){return H(255,255,255,0)}else{F=B[G];return H(F[0],F[1],F[2])}};var B={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})();(function(C){function B(l,W,X,E){var O=[],g={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:0.85},xaxis:{mode:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,tickDecimals:null,tickSize:null,minTickSize:null,monthNames:null,timeformat:null,twelveHourClock:false},yaxis:{autoscaleMargin:0.02},x2axis:{autoscaleMargin:null},y2axis:{autoscaleMargin:0.02},series:{points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff"},lines:{lineWidth:2,fill:false,fillColor:null,steps:false},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,align:"left",horizontal:false},shadowSize:3},grid:{show:true,aboveData:false,color:"#545454",backgroundColor:null,tickColor:"rgba(0,0,0,0.15)",labelMargin:5,borderWidth:2,borderColor:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:false,hoverable:false,autoHighlight:true,mouseActiveRadius:10},hooks:{}},P=null,AC=null,AD=null,Y=null,AJ=null,s={xaxis:{},yaxis:{},x2axis:{},y2axis:{}},e={left:0,right:0,top:0,bottom:0},y=0,Q=0,I=0,t=0,L={processOptions:[],processRawData:[],processDatapoints:[],draw:[],bindEvents:[],drawOverlay:[]},G=this;G.setData=f;G.setupGrid=k;G.draw=AH;G.getPlaceholder=function(){return l};G.getCanvas=function(){return P};G.getPlotOffset=function(){return e};G.width=function(){return I};G.height=function(){return t};G.offset=function(){var AK=AD.offset();AK.left+=e.left;AK.top+=e.top;return AK};G.getData=function(){return O};G.getAxes=function(){return s};G.getOptions=function(){return g};G.highlight=AE;G.unhighlight=x;G.triggerRedrawOverlay=q;G.pointOffset=function(AK){return{left:parseInt(T(AK,"xaxis").p2c(+AK.x)+e.left),top:parseInt(T(AK,"yaxis").p2c(+AK.y)+e.top)}};G.hooks=L;b(G);r(X);c();f(W);k();AH();AG();function Z(AM,AK){AK=[G].concat(AK);for(var AL=0;AL<AM.length;++AL){AM[AL].apply(this,AK)}}function b(){for(var AK=0;AK<E.length;++AK){var AL=E[AK];AL.init(G);if(AL.options){C.extend(true,g,AL.options)}}}function r(AK){C.extend(true,g,AK);if(g.grid.borderColor==null){g.grid.borderColor=g.grid.color}if(g.xaxis.noTicks&&g.xaxis.ticks==null){g.xaxis.ticks=g.xaxis.noTicks}if(g.yaxis.noTicks&&g.yaxis.ticks==null){g.yaxis.ticks=g.yaxis.noTicks}if(g.grid.coloredAreas){g.grid.markings=g.grid.coloredAreas}if(g.grid.coloredAreasColor){g.grid.markingsColor=g.grid.coloredAreasColor}if(g.lines){C.extend(true,g.series.lines,g.lines)}if(g.points){C.extend(true,g.series.points,g.points)}if(g.bars){C.extend(true,g.series.bars,g.bars)}if(g.shadowSize){g.series.shadowSize=g.shadowSize}for(var AL in L){if(g.hooks[AL]&&g.hooks[AL].length){L[AL]=L[AL].concat(g.hooks[AL])}}Z(L.processOptions,[g])}function f(AK){O=M(AK);U();m()}function M(AN){var AL=[];for(var AK=0;AK<AN.length;++AK){var AM=C.extend(true,{},g.series);if(AN[AK].data){AM.data=AN[AK].data;delete AN[AK].data;C.extend(true,AM,AN[AK]);AN[AK].data=AM.data}else{AM.data=AN[AK]}AL.push(AM)}return AL}function T(AM,AK){var AL=AM[AK];if(!AL||AL==1){return s[AK]}if(typeof AL=="number"){return s[AK.charAt(0)+AL+AK.slice(1)]}return AL}function U(){var AP;var AV=O.length,AK=[],AN=[];for(AP=0;AP<O.length;++AP){var AS=O[AP].color;if(AS!=null){--AV;if(typeof AS=="number"){AN.push(AS)}else{AK.push(C.color.parse(O[AP].color))}}}for(AP=0;AP<AN.length;++AP){AV=Math.max(AV,AN[AP]+1)}var AL=[],AO=0;AP=0;while(AL.length<AV){var AR;if(g.colors.length==AP){AR=C.color.make(100,100,100)}else{AR=C.color.parse(g.colors[AP])}var AM=AO%2==1?-1:1;AR.scale("rgb",1+AM*Math.ceil(AO/2)*0.2);AL.push(AR);++AP;if(AP>=g.colors.length){AP=0;++AO}}var AQ=0,AW;for(AP=0;AP<O.length;++AP){AW=O[AP];if(AW.color==null){AW.color=AL[AQ].toString();++AQ}else{if(typeof AW.color=="number"){AW.color=AL[AW.color].toString()}}if(AW.lines.show==null){var AU,AT=true;for(AU in AW){if(AW[AU].show){AT=false;break}}if(AT){AW.lines.show=true}}AW.xaxis=T(AW,"xaxis");AW.yaxis=T(AW,"yaxis")}}function m(){var AW=Number.POSITIVE_INFINITY,AQ=Number.NEGATIVE_INFINITY,Ac,Aa,AZ,AV,AL,AR,Ab,AX,AP,AO,AK,Ai,Af,AT;for(AK in s){s[AK].datamin=AW;s[AK].datamax=AQ;s[AK].used=false}function AN(Al,Ak,Aj){if(Ak<Al.datamin){Al.datamin=Ak}if(Aj>Al.datamax){Al.datamax=Aj}}for(Ac=0;Ac<O.length;++Ac){AR=O[Ac];AR.datapoints={points:[]};Z(L.processRawData,[AR,AR.data,AR.datapoints])}for(Ac=0;Ac<O.length;++Ac){AR=O[Ac];var Ah=AR.data,Ae=AR.datapoints.format;if(!Ae){Ae=[];Ae.push({x:true,number:true,required:true});Ae.push({y:true,number:true,required:true});if(AR.bars.show){Ae.push({y:true,number:true,required:false,defaultValue:0})}AR.datapoints.format=Ae}if(AR.datapoints.pointsize!=null){continue}if(AR.datapoints.pointsize==null){AR.datapoints.pointsize=Ae.length}AX=AR.datapoints.pointsize;Ab=AR.datapoints.points;insertSteps=AR.lines.show&&AR.lines.steps;AR.xaxis.used=AR.yaxis.used=true;for(Aa=AZ=0;Aa<Ah.length;++Aa,AZ+=AX){AT=Ah[Aa];var AM=AT==null;if(!AM){for(AV=0;AV<AX;++AV){Ai=AT[AV];Af=Ae[AV];if(Af){if(Af.number&&Ai!=null){Ai=+Ai;if(isNaN(Ai)){Ai=null}}if(Ai==null){if(Af.required){AM=true}if(Af.defaultValue!=null){Ai=Af.defaultValue}}}Ab[AZ+AV]=Ai}}if(AM){for(AV=0;AV<AX;++AV){Ai=Ab[AZ+AV];if(Ai!=null){Af=Ae[AV];if(Af.x){AN(AR.xaxis,Ai,Ai)}if(Af.y){AN(AR.yaxis,Ai,Ai)}}Ab[AZ+AV]=null}}else{if(insertSteps&&AZ>0&&Ab[AZ-AX]!=null&&Ab[AZ-AX]!=Ab[AZ]&&Ab[AZ-AX+1]!=Ab[AZ+1]){for(AV=0;AV<AX;++AV){Ab[AZ+AX+AV]=Ab[AZ+AV]}Ab[AZ+1]=Ab[AZ-AX+1];AZ+=AX}}}}for(Ac=0;Ac<O.length;++Ac){AR=O[Ac];Z(L.processDatapoints,[AR,AR.datapoints])}for(Ac=0;Ac<O.length;++Ac){AR=O[Ac];Ab=AR.datapoints.points,AX=AR.datapoints.pointsize;var AS=AW,AY=AW,AU=AQ,Ad=AQ;for(Aa=0;Aa<Ab.length;Aa+=AX){if(Ab[Aa]==null){continue}for(AV=0;AV<AX;++AV){Ai=Ab[Aa+AV];Af=Ae[AV];if(!Af){continue}if(Af.x){if(Ai<AS){AS=Ai}if(Ai>AU){AU=Ai}}if(Af.y){if(Ai<AY){AY=Ai}if(Ai>Ad){Ad=Ai}}}}if(AR.bars.show){var Ag=AR.bars.align=="left"?0:-AR.bars.barWidth/2;if(AR.bars.horizontal){AY+=Ag;Ad+=Ag+AR.bars.barWidth}else{AS+=Ag;AU+=Ag+AR.bars.barWidth}}AN(AR.xaxis,AS,AU);AN(AR.yaxis,AY,Ad)}for(AK in s){if(s[AK].datamin==AW){s[AK].datamin=null}if(s[AK].datamax==AQ){s[AK].datamax=null}}}function c(){function AK(AM,AL){var AN=document.createElement("canvas");AN.width=AM;AN.height=AL;if(C.browser.msie){AN=window.G_vmlCanvasManager.initElement(AN)}return AN}y=l.width();Q=l.height();l.html("");if(l.css("position")=="static"){l.css("position","relative")}if(y<=0||Q<=0){throw"Invalid dimensions for plot, width = "+y+", height = "+Q}if(C.browser.msie){window.G_vmlCanvasManager.init_(document)}P=C(AK(y,Q)).appendTo(l).get(0);Y=P.getContext("2d");AC=C(AK(y,Q)).css({position:"absolute",left:0,top:0}).appendTo(l).get(0);AJ=AC.getContext("2d");AJ.stroke()}function AG(){AD=C([AC,P]);if(g.grid.hoverable){AD.mousemove(D)}if(g.grid.clickable){AD.click(d)}Z(L.bindEvents,[AD])}function k(){function AL(AT,AU){function AP(AV){return AV}var AS,AO,AQ=AU.transform||AP,AR=AU.inverseTransform;if(AT==s.xaxis||AT==s.x2axis){AS=AT.scale=I/(AQ(AT.max)-AQ(AT.min));AO=AQ(AT.min);if(AQ==AP){AT.p2c=function(AV){return(AV-AO)*AS}}else{AT.p2c=function(AV){return(AQ(AV)-AO)*AS}}if(!AR){AT.c2p=function(AV){return AO+AV/AS}}else{AT.c2p=function(AV){return AR(AO+AV/AS)}}}else{AS=AT.scale=t/(AQ(AT.max)-AQ(AT.min));AO=AQ(AT.max);if(AQ==AP){AT.p2c=function(AV){return(AO-AV)*AS}}else{AT.p2c=function(AV){return(AO-AQ(AV))*AS}}if(!AR){AT.c2p=function(AV){return AO-AV/AS}}else{AT.c2p=function(AV){return AR(AO-AV/AS)}}}}function AN(AR,AT){var AQ,AS=[],AP;AR.labelWidth=AT.labelWidth;AR.labelHeight=AT.labelHeight;if(AR==s.xaxis||AR==s.x2axis){if(AR.labelWidth==null){AR.labelWidth=y/(AR.ticks.length>0?AR.ticks.length:1)}if(AR.labelHeight==null){AS=[];for(AQ=0;AQ<AR.ticks.length;++AQ){AP=AR.ticks[AQ].label;if(AP){AS.push('<div class="tickLabel" style="float:left;width:'+AR.labelWidth+'px">'+AP+"</div>")}}if(AS.length>0){var AO=C('<div style="position:absolute;top:-10000px;width:10000px;font-size:smaller">'+AS.join("")+'<div style="clear:left"></div></div>').appendTo(l);AR.labelHeight=AO.height();AO.remove()}}}else{if(AR.labelWidth==null||AR.labelHeight==null){for(AQ=0;AQ<AR.ticks.length;++AQ){AP=AR.ticks[AQ].label;if(AP){AS.push('<div class="tickLabel">'+AP+"</div>")}}if(AS.length>0){var AO=C('<div style="position:absolute;top:-10000px;font-size:smaller">'+AS.join("")+"</div>").appendTo(l);if(AR.labelWidth==null){AR.labelWidth=AO.width()}if(AR.labelHeight==null){AR.labelHeight=AO.find("div").height()}AO.remove()}}}if(AR.labelWidth==null){AR.labelWidth=0}if(AR.labelHeight==null){AR.labelHeight=0}}function AM(){var AP=g.grid.borderWidth;for(i=0;i<O.length;++i){AP=Math.max(AP,2*(O[i].points.radius+O[i].points.lineWidth/2))}e.left=e.right=e.top=e.bottom=AP;var AO=g.grid.labelMargin+g.grid.borderWidth;if(s.xaxis.labelHeight>0){e.bottom=Math.max(AP,s.xaxis.labelHeight+AO)}if(s.yaxis.labelWidth>0){e.left=Math.max(AP,s.yaxis.labelWidth+AO)}if(s.x2axis.labelHeight>0){e.top=Math.max(AP,s.x2axis.labelHeight+AO)}if(s.y2axis.labelWidth>0){e.right=Math.max(AP,s.y2axis.labelWidth+AO)}I=y-e.left-e.right;t=Q-e.bottom-e.top}var AK;for(AK in s){K(s[AK],g[AK])}if(g.grid.show){for(AK in s){F(s[AK],g[AK]);p(s[AK],g[AK]);AN(s[AK],g[AK])}AM()}else{e.left=e.right=e.top=e.bottom=0;I=y;t=Q}for(AK in s){AL(s[AK],g[AK])}if(g.grid.show){h()}AI()}function K(AN,AQ){var AM=+(AQ.min!=null?AQ.min:AN.datamin),AK=+(AQ.max!=null?AQ.max:AN.datamax),AP=AK-AM;if(AP==0){var AL=AK==0?1:0.01;if(AQ.min==null){AM-=AL}if(AQ.max==null||AQ.min!=null){AK+=AL}}else{var AO=AQ.autoscaleMargin;if(AO!=null){if(AQ.min==null){AM-=AP*AO;if(AM<0&&AN.datamin!=null&&AN.datamin>=0){AM=0}}if(AQ.max==null){AK+=AP*AO;if(AK>0&&AN.datamax!=null&&AN.datamax<=0){AK=0}}}}AN.min=AM;AN.max=AK}function F(AP,AS){var AO;if(typeof AS.ticks=="number"&&AS.ticks>0){AO=AS.ticks}else{if(AP==s.xaxis||AP==s.x2axis){AO=0.3*Math.sqrt(y)}else{AO=0.3*Math.sqrt(Q)}}var AX=(AP.max-AP.min)/AO,AZ,AT,AV,AW,AR,AM,AL;if(AS.mode=="time"){var AU={second:1000,minute:60*1000,hour:60*60*1000,day:24*60*60*1000,month:30*24*60*60*1000,year:365.2425*24*60*60*1000};var AY=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[0.25,"month"],[0.5,"month"],[1,"month"],[2,"month"],[3,"month"],[6,"month"],[1,"year"]];var AN=0;if(AS.minTickSize!=null){if(typeof AS.tickSize=="number"){AN=AS.tickSize}else{AN=AS.minTickSize[0]*AU[AS.minTickSize[1]]}}for(AR=0;AR<AY.length-1;++AR){if(AX<(AY[AR][0]*AU[AY[AR][1]]+AY[AR+1][0]*AU[AY[AR+1][1]])/2&&AY[AR][0]*AU[AY[AR][1]]>=AN){break}}AZ=AY[AR][0];AV=AY[AR][1];if(AV=="year"){AM=Math.pow(10,Math.floor(Math.log(AX/AU.year)/Math.LN10));AL=(AX/AU.year)/AM;if(AL<1.5){AZ=1}else{if(AL<3){AZ=2}else{if(AL<7.5){AZ=5}else{AZ=10}}}AZ*=AM}if(AS.tickSize){AZ=AS.tickSize[0];AV=AS.tickSize[1]}AT=function(Ac){var Ah=[],Af=Ac.tickSize[0],Ai=Ac.tickSize[1],Ag=new Date(Ac.min);var Ab=Af*AU[Ai];if(Ai=="second"){Ag.setUTCSeconds(A(Ag.getUTCSeconds(),Af))}if(Ai=="minute"){Ag.setUTCMinutes(A(Ag.getUTCMinutes(),Af))}if(Ai=="hour"){Ag.setUTCHours(A(Ag.getUTCHours(),Af))}if(Ai=="month"){Ag.setUTCMonth(A(Ag.getUTCMonth(),Af))}if(Ai=="year"){Ag.setUTCFullYear(A(Ag.getUTCFullYear(),Af))}Ag.setUTCMilliseconds(0);if(Ab>=AU.minute){Ag.setUTCSeconds(0)}if(Ab>=AU.hour){Ag.setUTCMinutes(0)}if(Ab>=AU.day){Ag.setUTCHours(0)}if(Ab>=AU.day*4){Ag.setUTCDate(1)}if(Ab>=AU.year){Ag.setUTCMonth(0)}var Ak=0,Aj=Number.NaN,Ad;do{Ad=Aj;Aj=Ag.getTime();Ah.push({v:Aj,label:Ac.tickFormatter(Aj,Ac)});if(Ai=="month"){if(Af<1){Ag.setUTCDate(1);var Aa=Ag.getTime();Ag.setUTCMonth(Ag.getUTCMonth()+1);var Ae=Ag.getTime();Ag.setTime(Aj+Ak*AU.hour+(Ae-Aa)*Af);Ak=Ag.getUTCHours();Ag.setUTCHours(0)}else{Ag.setUTCMonth(Ag.getUTCMonth()+Af)}}else{if(Ai=="year"){Ag.setUTCFullYear(Ag.getUTCFullYear()+Af)}else{Ag.setTime(Aj+Ab)}}}while(Aj<Ac.max&&Aj!=Ad);return Ah};AW=function(Aa,Ad){var Af=new Date(Aa);if(AS.timeformat!=null){return C.plot.formatDate(Af,AS.timeformat,AS.monthNames)}var Ab=Ad.tickSize[0]*AU[Ad.tickSize[1]];var Ac=Ad.max-Ad.min;var Ae=(AS.twelveHourClock)?" %p":"";if(Ab<AU.minute){fmt="%h:%M:%S"+Ae}else{if(Ab<AU.day){if(Ac<2*AU.day){fmt="%h:%M"+Ae}else{fmt="%b %d %h:%M"+Ae}}else{if(Ab<AU.month){fmt="%b %d"}else{if(Ab<AU.year){if(Ac<AU.year){fmt="%b"}else{fmt="%b %y"}}else{fmt="%y"}}}}return C.plot.formatDate(Af,fmt,AS.monthNames)}}else{var AK=AS.tickDecimals;var AQ=-Math.floor(Math.log(AX)/Math.LN10);if(AK!=null&&AQ>AK){AQ=AK}AM=Math.pow(10,-AQ);AL=AX/AM;if(AL<1.5){AZ=1}else{if(AL<3){AZ=2;if(AL>2.25&&(AK==null||AQ+1<=AK)){AZ=2.5;++AQ}}else{if(AL<7.5){AZ=5}else{AZ=10}}}AZ*=AM;if(AS.minTickSize!=null&&AZ<AS.minTickSize){AZ=AS.minTickSize}if(AS.tickSize!=null){AZ=AS.tickSize}AP.tickDecimals=Math.max(0,(AK!=null)?AK:AQ);AT=function(Ac){var Ae=[];var Af=A(Ac.min,Ac.tickSize),Ab=0,Aa=Number.NaN,Ad;do{Ad=Aa;Aa=Af+Ab*Ac.tickSize;Ae.push({v:Aa,label:Ac.tickFormatter(Aa,Ac)});++Ab}while(Aa<Ac.max&&Aa!=Ad);return Ae};AW=function(Aa,Ab){return Aa.toFixed(Ab.tickDecimals)}}AP.tickSize=AV?[AZ,AV]:AZ;AP.tickGenerator=AT;if(C.isFunction(AS.tickFormatter)){AP.tickFormatter=function(Aa,Ab){return""+AS.tickFormatter(Aa,Ab)}}else{AP.tickFormatter=AW}}function p(AO,AQ){AO.ticks=[];if(!AO.used){return }if(AQ.ticks==null){AO.ticks=AO.tickGenerator(AO)}else{if(typeof AQ.ticks=="number"){if(AQ.ticks>0){AO.ticks=AO.tickGenerator(AO)}}else{if(AQ.ticks){var AP=AQ.ticks;if(C.isFunction(AP)){AP=AP({min:AO.min,max:AO.max})}var AN,AK;for(AN=0;AN<AP.length;++AN){var AL=null;var AM=AP[AN];if(typeof AM=="object"){AK=AM[0];if(AM.length>1){AL=AM[1]}}else{AK=AM}if(AL==null){AL=AO.tickFormatter(AK,AO)}AO.ticks[AN]={v:AK,label:AL}}}}}if(AQ.autoscaleMargin!=null&&AO.ticks.length>0){if(AQ.min==null){AO.min=Math.min(AO.min,AO.ticks[0].v)}if(AQ.max==null&&AO.ticks.length>1){AO.max=Math.max(AO.max,AO.ticks[AO.ticks.length-1].v)}}}function AH(){Y.clearRect(0,0,y,Q);var AL=g.grid;if(AL.show&&!AL.aboveData){S()}for(var AK=0;AK<O.length;++AK){AA(O[AK])}Z(L.draw,[Y]);if(AL.show&&AL.aboveData){S()}}function N(AL,AR){var AO=AR+"axis",AK=AR+"2axis",AN,AQ,AP,AM;if(AL[AO]){AN=s[AO];AQ=AL[AO].from;AP=AL[AO].to}else{if(AL[AK]){AN=s[AK];AQ=AL[AK].from;AP=AL[AK].to}else{AN=s[AO];AQ=AL[AR+"1"];AP=AL[AR+"2"]}}if(AQ!=null&&AP!=null&&AQ>AP){return{from:AP,to:AQ,axis:AN}}return{from:AQ,to:AP,axis:AN}}function S(){var AO;Y.save();Y.translate(e.left,e.top);if(g.grid.backgroundColor){Y.fillStyle=R(g.grid.backgroundColor,t,0,"rgba(255, 255, 255, 0)");Y.fillRect(0,0,I,t)}var AL=g.grid.markings;if(AL){if(C.isFunction(AL)){AL=AL({xmin:s.xaxis.min,xmax:s.xaxis.max,ymin:s.yaxis.min,ymax:s.yaxis.max,xaxis:s.xaxis,yaxis:s.yaxis,x2axis:s.x2axis,y2axis:s.y2axis})}for(AO=0;AO<AL.length;++AO){var AK=AL[AO],AQ=N(AK,"x"),AN=N(AK,"y");if(AQ.from==null){AQ.from=AQ.axis.min}if(AQ.to==null){AQ.to=AQ.axis.max}if(AN.from==null){AN.from=AN.axis.min}if(AN.to==null){AN.to=AN.axis.max}if(AQ.to<AQ.axis.min||AQ.from>AQ.axis.max||AN.to<AN.axis.min||AN.from>AN.axis.max){continue}AQ.from=Math.max(AQ.from,AQ.axis.min);AQ.to=Math.min(AQ.to,AQ.axis.max);AN.from=Math.max(AN.from,AN.axis.min);AN.to=Math.min(AN.to,AN.axis.max);if(AQ.from==AQ.to&&AN.from==AN.to){continue}AQ.from=AQ.axis.p2c(AQ.from);AQ.to=AQ.axis.p2c(AQ.to);AN.from=AN.axis.p2c(AN.from);AN.to=AN.axis.p2c(AN.to);if(AQ.from==AQ.to||AN.from==AN.to){Y.beginPath();Y.strokeStyle=AK.color||g.grid.markingsColor;Y.lineWidth=AK.lineWidth||g.grid.markingsLineWidth;Y.moveTo(AQ.from,AN.from);Y.lineTo(AQ.to,AN.to);Y.stroke()}else{Y.fillStyle=AK.color||g.grid.markingsColor;Y.fillRect(AQ.from,AN.to,AQ.to-AQ.from,AN.from-AN.to)}}}Y.lineWidth=1;Y.strokeStyle=g.grid.tickColor;Y.beginPath();var AM,AP=s.xaxis;for(AO=0;AO<AP.ticks.length;++AO){AM=AP.ticks[AO].v;if(AM<=AP.min||AM>=s.xaxis.max){continue}Y.moveTo(Math.floor(AP.p2c(AM))+Y.lineWidth/2,0);Y.lineTo(Math.floor(AP.p2c(AM))+Y.lineWidth/2,t)}AP=s.yaxis;for(AO=0;AO<AP.ticks.length;++AO){AM=AP.ticks[AO].v;if(AM<=AP.min||AM>=AP.max){continue}Y.moveTo(0,Math.floor(AP.p2c(AM))+Y.lineWidth/2);Y.lineTo(I,Math.floor(AP.p2c(AM))+Y.lineWidth/2)}AP=s.x2axis;for(AO=0;AO<AP.ticks.length;++AO){AM=AP.ticks[AO].v;if(AM<=AP.min||AM>=AP.max){continue}Y.moveTo(Math.floor(AP.p2c(AM))+Y.lineWidth/2,-5);Y.lineTo(Math.floor(AP.p2c(AM))+Y.lineWidth/2,5)}AP=s.y2axis;for(AO=0;AO<AP.ticks.length;++AO){AM=AP.ticks[AO].v;if(AM<=AP.min||AM>=AP.max){continue}Y.moveTo(I-5,Math.floor(AP.p2c(AM))+Y.lineWidth/2);Y.lineTo(I+5,Math.floor(AP.p2c(AM))+Y.lineWidth/2)}Y.stroke();if(g.grid.borderWidth){var AR=g.grid.borderWidth;Y.lineWidth=AR;Y.strokeStyle=g.grid.borderColor;Y.strokeRect(-AR/2,-AR/2,I+AR,t+AR)}Y.restore()}function h(){l.find(".tickLabels").remove();var AK=['<div class="tickLabels" style="font-size:smaller;color:'+g.grid.color+'">'];function AM(AP,AQ){for(var AO=0;AO<AP.ticks.length;++AO){var AN=AP.ticks[AO];if(!AN.label||AN.v<AP.min||AN.v>AP.max){continue}AK.push(AQ(AN,AP))}}var AL=g.grid.labelMargin+g.grid.borderWidth;AM(s.xaxis,function(AN,AO){return'<div style="position:absolute;top:'+(e.top+t+AL)+"px;left:"+Math.round(e.left+AO.p2c(AN.v)-AO.labelWidth/2)+"px;width:"+AO.labelWidth+'px;text-align:center" class="tickLabel">'+AN.label+"</div>"});AM(s.yaxis,function(AN,AO){return'<div style="position:absolute;top:'+Math.round(e.top+AO.p2c(AN.v)-AO.labelHeight/2)+"px;right:"+(e.right+I+AL)+"px;width:"+AO.labelWidth+'px;text-align:right" class="tickLabel">'+AN.label+"</div>"});AM(s.x2axis,function(AN,AO){return'<div style="position:absolute;bottom:'+(e.bottom+t+AL)+"px;left:"+Math.round(e.left+AO.p2c(AN.v)-AO.labelWidth/2)+"px;width:"+AO.labelWidth+'px;text-align:center" class="tickLabel">'+AN.label+"</div>"});AM(s.y2axis,function(AN,AO){return'<div style="position:absolute;top:'+Math.round(e.top+AO.p2c(AN.v)-AO.labelHeight/2)+"px;left:"+(e.left+I+AL)+"px;width:"+AO.labelWidth+'px;text-align:left" class="tickLabel">'+AN.label+"</div>"});AK.push("</div>");l.append(AK.join(""))}function AA(AK){if(AK.lines.show){a(AK)}if(AK.bars.show){n(AK)}if(AK.points.show){o(AK)}}function a(AN){function AM(AY,AZ,AR,Ad,Ac){var Ae=AY.points,AS=AY.pointsize,AW=null,AV=null;Y.beginPath();for(var AX=AS;AX<Ae.length;AX+=AS){var AU=Ae[AX-AS],Ab=Ae[AX-AS+1],AT=Ae[AX],Aa=Ae[AX+1];if(AU==null||AT==null){continue}if(Ab<=Aa&&Ab<Ac.min){if(Aa<Ac.min){continue}AU=(Ac.min-Ab)/(Aa-Ab)*(AT-AU)+AU;Ab=Ac.min}else{if(Aa<=Ab&&Aa<Ac.min){if(Ab<Ac.min){continue}AT=(Ac.min-Ab)/(Aa-Ab)*(AT-AU)+AU;Aa=Ac.min}}if(Ab>=Aa&&Ab>Ac.max){if(Aa>Ac.max){continue}AU=(Ac.max-Ab)/(Aa-Ab)*(AT-AU)+AU;Ab=Ac.max}else{if(Aa>=Ab&&Aa>Ac.max){if(Ab>Ac.max){continue}AT=(Ac.max-Ab)/(Aa-Ab)*(AT-AU)+AU;Aa=Ac.max}}if(AU<=AT&&AU<Ad.min){if(AT<Ad.min){continue}Ab=(Ad.min-AU)/(AT-AU)*(Aa-Ab)+Ab;AU=Ad.min}else{if(AT<=AU&&AT<Ad.min){if(AU<Ad.min){continue}Aa=(Ad.min-AU)/(AT-AU)*(Aa-Ab)+Ab;AT=Ad.min}}if(AU>=AT&&AU>Ad.max){if(AT>Ad.max){continue}Ab=(Ad.max-AU)/(AT-AU)*(Aa-Ab)+Ab;AU=Ad.max}else{if(AT>=AU&&AT>Ad.max){if(AU>Ad.max){continue}Aa=(Ad.max-AU)/(AT-AU)*(Aa-Ab)+Ab;AT=Ad.max}}if(AU!=AW||Ab!=AV){Y.moveTo(Ad.p2c(AU)+AZ,Ac.p2c(Ab)+AR)}AW=AT;AV=Aa;Y.lineTo(Ad.p2c(AT)+AZ,Ac.p2c(Aa)+AR)}Y.stroke()}function AO(AX,Ae,Ac){var Af=AX.points,AR=AX.pointsize,AS=Math.min(Math.max(0,Ac.min),Ac.max),Aa,AV=0,Ad=false;for(var AW=AR;AW<Af.length;AW+=AR){var AU=Af[AW-AR],Ab=Af[AW-AR+1],AT=Af[AW],AZ=Af[AW+1];if(Ad&&AU!=null&&AT==null){Y.lineTo(Ae.p2c(AV),Ac.p2c(AS));Y.fill();Ad=false;continue}if(AU==null||AT==null){continue}if(AU<=AT&&AU<Ae.min){if(AT<Ae.min){continue}Ab=(Ae.min-AU)/(AT-AU)*(AZ-Ab)+Ab;AU=Ae.min}else{if(AT<=AU&&AT<Ae.min){if(AU<Ae.min){continue}AZ=(Ae.min-AU)/(AT-AU)*(AZ-Ab)+Ab;AT=Ae.min}}if(AU>=AT&&AU>Ae.max){if(AT>Ae.max){continue}Ab=(Ae.max-AU)/(AT-AU)*(AZ-Ab)+Ab;AU=Ae.max}else{if(AT>=AU&&AT>Ae.max){if(AU>Ae.max){continue}AZ=(Ae.max-AU)/(AT-AU)*(AZ-Ab)+Ab;AT=Ae.max}}if(!Ad){Y.beginPath();Y.moveTo(Ae.p2c(AU),Ac.p2c(AS));Ad=true}if(Ab>=Ac.max&&AZ>=Ac.max){Y.lineTo(Ae.p2c(AU),Ac.p2c(Ac.max));Y.lineTo(Ae.p2c(AT),Ac.p2c(Ac.max));AV=AT;continue}else{if(Ab<=Ac.min&&AZ<=Ac.min){Y.lineTo(Ae.p2c(AU),Ac.p2c(Ac.min));Y.lineTo(Ae.p2c(AT),Ac.p2c(Ac.min));AV=AT;continue}}var Ag=AU,AY=AT;if(Ab<=AZ&&Ab<Ac.min&&AZ>=Ac.min){AU=(Ac.min-Ab)/(AZ-Ab)*(AT-AU)+AU;Ab=Ac.min}else{if(AZ<=Ab&&AZ<Ac.min&&Ab>=Ac.min){AT=(Ac.min-Ab)/(AZ-Ab)*(AT-AU)+AU;AZ=Ac.min}}if(Ab>=AZ&&Ab>Ac.max&&AZ<=Ac.max){AU=(Ac.max-Ab)/(AZ-Ab)*(AT-AU)+AU;Ab=Ac.max}else{if(AZ>=Ab&&AZ>Ac.max&&Ab<=Ac.max){AT=(Ac.max-Ab)/(AZ-Ab)*(AT-AU)+AU;AZ=Ac.max}}if(AU!=Ag){if(Ab<=Ac.min){Aa=Ac.min}else{Aa=Ac.max}Y.lineTo(Ae.p2c(Ag),Ac.p2c(Aa));Y.lineTo(Ae.p2c(AU),Ac.p2c(Aa))}Y.lineTo(Ae.p2c(AU),Ac.p2c(Ab));Y.lineTo(Ae.p2c(AT),Ac.p2c(AZ));if(AT!=AY){if(AZ<=Ac.min){Aa=Ac.min}else{Aa=Ac.max}Y.lineTo(Ae.p2c(AT),Ac.p2c(Aa));Y.lineTo(Ae.p2c(AY),Ac.p2c(Aa))}AV=Math.max(AT,AY)}if(Ad){Y.lineTo(Ae.p2c(AV),Ac.p2c(AS));Y.fill()}}Y.save();Y.translate(e.left,e.top);Y.lineJoin="round";var AP=AN.lines.lineWidth,AK=AN.shadowSize;if(AP>0&&AK>0){Y.lineWidth=AK;Y.strokeStyle="rgba(0,0,0,0.1)";var AQ=Math.PI/18;AM(AN.datapoints,Math.sin(AQ)*(AP/2+AK/2),Math.cos(AQ)*(AP/2+AK/2),AN.xaxis,AN.yaxis);Y.lineWidth=AK/2;AM(AN.datapoints,Math.sin(AQ)*(AP/2+AK/4),Math.cos(AQ)*(AP/2+AK/4),AN.xaxis,AN.yaxis)}Y.lineWidth=AP;Y.strokeStyle=AN.color;var AL=V(AN.lines,AN.color,0,t);if(AL){Y.fillStyle=AL;AO(AN.datapoints,AN.xaxis,AN.yaxis)}if(AP>0){AM(AN.datapoints,0,0,AN.xaxis,AN.yaxis)}Y.restore()}function o(AN){function AP(AU,AT,Ab,AR,AV,AZ,AY){var Aa=AU.points,AQ=AU.pointsize;for(var AS=0;AS<Aa.length;AS+=AQ){var AX=Aa[AS],AW=Aa[AS+1];if(AX==null||AX<AZ.min||AX>AZ.max||AW<AY.min||AW>AY.max){continue}Y.beginPath();Y.arc(AZ.p2c(AX),AY.p2c(AW)+AR,AT,0,AV,false);if(Ab){Y.fillStyle=Ab;Y.fill()}Y.stroke()}}Y.save();Y.translate(e.left,e.top);var AO=AN.lines.lineWidth,AL=AN.shadowSize,AK=AN.points.radius;if(AO>0&&AL>0){var AM=AL/2;Y.lineWidth=AM;Y.strokeStyle="rgba(0,0,0,0.1)";AP(AN.datapoints,AK,null,AM+AM/2,Math.PI,AN.xaxis,AN.yaxis);Y.strokeStyle="rgba(0,0,0,0.2)";AP(AN.datapoints,AK,null,AM/2,Math.PI,AN.xaxis,AN.yaxis)}Y.lineWidth=AO;Y.strokeStyle=AN.color;AP(AN.datapoints,AK,V(AN.points,AN.color),0,2*Math.PI,AN.xaxis,AN.yaxis);Y.restore()}function AB(AV,AU,Ad,AQ,AY,AN,AL,AT,AS,Ac,AZ){var AM,Ab,AR,AX,AO,AK,AW,AP,Aa;if(AZ){AP=AK=AW=true;AO=false;AM=Ad;Ab=AV;AX=AU+AQ;AR=AU+AY;if(Ab<AM){Aa=Ab;Ab=AM;AM=Aa;AO=true;AK=false}}else{AO=AK=AW=true;AP=false;AM=AV+AQ;Ab=AV+AY;AR=Ad;AX=AU;if(AX<AR){Aa=AX;AX=AR;AR=Aa;AP=true;AW=false}}if(Ab<AT.min||AM>AT.max||AX<AS.min||AR>AS.max){return }if(AM<AT.min){AM=AT.min;AO=false}if(Ab>AT.max){Ab=AT.max;AK=false}if(AR<AS.min){AR=AS.min;AP=false}if(AX>AS.max){AX=AS.max;AW=false}AM=AT.p2c(AM);AR=AS.p2c(AR);Ab=AT.p2c(Ab);AX=AS.p2c(AX);if(AL){Ac.beginPath();Ac.moveTo(AM,AR);Ac.lineTo(AM,AX);Ac.lineTo(Ab,AX);Ac.lineTo(Ab,AR);Ac.fillStyle=AL(AR,AX);Ac.fill()}if(AO||AK||AW||AP){Ac.beginPath();Ac.moveTo(AM,AR+AN);if(AO){Ac.lineTo(AM,AX+AN)}else{Ac.moveTo(AM,AX+AN)}if(AW){Ac.lineTo(Ab,AX+AN)}else{Ac.moveTo(Ab,AX+AN)}if(AK){Ac.lineTo(Ab,AR+AN)}else{Ac.moveTo(Ab,AR+AN)}if(AP){Ac.lineTo(AM,AR+AN)}else{Ac.moveTo(AM,AR+AN)}Ac.stroke()}}function n(AM){function AL(AS,AR,AU,AP,AT,AW,AV){var AX=AS.points,AO=AS.pointsize;for(var AQ=0;AQ<AX.length;AQ+=AO){if(AX[AQ]==null){continue}AB(AX[AQ],AX[AQ+1],AX[AQ+2],AR,AU,AP,AT,AW,AV,Y,AM.bars.horizontal)}}Y.save();Y.translate(e.left,e.top);Y.lineWidth=AM.bars.lineWidth;Y.strokeStyle=AM.color;var AK=AM.bars.align=="left"?0:-AM.bars.barWidth/2;var AN=AM.bars.fill?function(AO,AP){return V(AM.bars,AM.color,AO,AP)}:null;AL(AM.datapoints,AK,AK+AM.bars.barWidth,0,AN,AM.xaxis,AM.yaxis);Y.restore()}function V(AM,AK,AL,AO){var AN=AM.fill;if(!AN){return null}if(AM.fillColor){return R(AM.fillColor,AL,AO,AK)}var AP=C.color.parse(AK);AP.a=typeof AN=="number"?AN:0.4;AP.normalize();return AP.toString()}function AI(){l.find(".legend").remove();if(!g.legend.show){return }var AP=[],AN=false,AV=g.legend.labelFormatter,AU,AR;for(i=0;i<O.length;++i){AU=O[i];AR=AU.label;if(!AR){continue}if(i%g.legend.noColumns==0){if(AN){AP.push("</tr>")}AP.push("<tr>");AN=true}if(AV){AR=AV(AR,AU)}AP.push('<td class="legendColorBox"><div style="border:1px solid '+g.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+AU.color+';overflow:hidden"></div></div></td><td class="legendLabel">'+AR+"</td>")}if(AN){AP.push("</tr>")}if(AP.length==0){return }var AT='<table style="font-size:smaller;color:'+g.grid.color+'">'+AP.join("")+"</table>";if(g.legend.container!=null){C(g.legend.container).html(AT)}else{var AQ="",AL=g.legend.position,AM=g.legend.margin;if(AM[0]==null){AM=[AM,AM]}if(AL.charAt(0)=="n"){AQ+="top:"+(AM[1]+e.top)+"px;"}else{if(AL.charAt(0)=="s"){AQ+="bottom:"+(AM[1]+e.bottom)+"px;"}}if(AL.charAt(1)=="e"){AQ+="right:"+(AM[0]+e.right)+"px;"}else{if(AL.charAt(1)=="w"){AQ+="left:"+(AM[0]+e.left)+"px;"}}var AS=C('<div class="legend">'+AT.replace('style="','style="position:absolute;'+AQ+";")+"</div>").appendTo(l);if(g.legend.backgroundOpacity!=0){var AO=g.legend.backgroundColor;if(AO==null){AO=g.grid.backgroundColor;if(AO&&typeof AO=="string"){AO=C.color.parse(AO)}else{AO=C.color.extract(AS,"background-color")}AO.a=1;AO=AO.toString()}var AK=AS.children();C('<div style="position:absolute;width:'+AK.width()+"px;height:"+AK.height()+"px;"+AQ+"background-color:"+AO+';"> </div>').prependTo(AS).css("opacity",g.legend.backgroundOpacity)}}}var w=[],J=null;function AF(AR,AP,AM){var AX=g.grid.mouseActiveRadius,Aj=AX*AX+1,Ah=null,Aa=false,Af,Ad;for(Af=0;Af<O.length;++Af){if(!AM(O[Af])){continue}var AY=O[Af],AQ=AY.xaxis,AO=AY.yaxis,Ae=AY.datapoints.points,Ac=AY.datapoints.pointsize,AZ=AQ.c2p(AR),AW=AO.c2p(AP),AL=AX/AQ.scale,AK=AX/AO.scale;if(AY.lines.show||AY.points.show){for(Ad=0;Ad<Ae.length;Ad+=Ac){var AT=Ae[Ad],AS=Ae[Ad+1];if(AT==null){continue}if(AT-AZ>AL||AT-AZ<-AL||AS-AW>AK||AS-AW<-AK){continue}var AV=Math.abs(AQ.p2c(AT)-AR),AU=Math.abs(AO.p2c(AS)-AP),Ab=AV*AV+AU*AU;if(Ab<=Aj){Aj=Ab;Ah=[Af,Ad/Ac]}}}if(AY.bars.show&&!Ah){var AN=AY.bars.align=="left"?0:-AY.bars.barWidth/2,Ag=AN+AY.bars.barWidth;for(Ad=0;Ad<Ae.length;Ad+=Ac){var AT=Ae[Ad],AS=Ae[Ad+1],Ai=Ae[Ad+2];if(AT==null){continue}if(O[Af].bars.horizontal?(AZ<=Math.max(Ai,AT)&&AZ>=Math.min(Ai,AT)&&AW>=AS+AN&&AW<=AS+Ag):(AZ>=AT+AN&&AZ<=AT+Ag&&AW>=Math.min(Ai,AS)&&AW<=Math.max(Ai,AS))){Ah=[Af,Ad/Ac]}}}}if(Ah){Af=Ah[0];Ad=Ah[1];Ac=O[Af].datapoints.pointsize;return{datapoint:O[Af].datapoints.points.slice(Ad*Ac,(Ad+1)*Ac),dataIndex:Ad,series:O[Af],seriesIndex:Af}}return null}function D(AK){if(g.grid.hoverable){H("plothover",AK,function(AL){return AL.hoverable!=false})}}function d(AK){H("plotclick",AK,function(AL){return AL.clickable!=false})}function H(AL,AK,AM){var AN=AD.offset(),AS={pageX:AK.pageX,pageY:AK.pageY},AQ=AK.pageX-AN.left-e.left,AO=AK.pageY-AN.top-e.top;if(s.xaxis.used){AS.x=s.xaxis.c2p(AQ)}if(s.yaxis.used){AS.y=s.yaxis.c2p(AO)}if(s.x2axis.used){AS.x2=s.x2axis.c2p(AQ)}if(s.y2axis.used){AS.y2=s.y2axis.c2p(AO)}var AT=AF(AQ,AO,AM);if(AT){AT.pageX=parseInt(AT.series.xaxis.p2c(AT.datapoint[0])+AN.left+e.left);AT.pageY=parseInt(AT.series.yaxis.p2c(AT.datapoint[1])+AN.top+e.top)}if(g.grid.autoHighlight){for(var AP=0;AP<w.length;++AP){var AR=w[AP];if(AR.auto==AL&&!(AT&&AR.series==AT.series&&AR.point==AT.datapoint)){x(AR.series,AR.point)}}if(AT){AE(AT.series,AT.datapoint,AL)}}l.trigger(AL,[AS,AT])}function q(){if(!J){J=setTimeout(v,30)}}function v(){J=null;AJ.save();AJ.clearRect(0,0,y,Q);AJ.translate(e.left,e.top);var AL,AK;for(AL=0;AL<w.length;++AL){AK=w[AL];if(AK.series.bars.show){z(AK.series,AK.point)}else{u(AK.series,AK.point)}}AJ.restore();Z(L.drawOverlay,[AJ])}function AE(AM,AK,AN){if(typeof AM=="number"){AM=O[AM]}if(typeof AK=="number"){AK=AM.data[AK]}var AL=j(AM,AK);if(AL==-1){w.push({series:AM,point:AK,auto:AN});q()}else{if(!AN){w[AL].auto=false}}}function x(AM,AK){if(AM==null&&AK==null){w=[];q()}if(typeof AM=="number"){AM=O[AM]}if(typeof AK=="number"){AK=AM.data[AK]}var AL=j(AM,AK);if(AL!=-1){w.splice(AL,1);q()}}function j(AM,AN){for(var AK=0;AK<w.length;++AK){var AL=w[AK];if(AL.series==AM&&AL.point[0]==AN[0]&&AL.point[1]==AN[1]){return AK}}return -1}function u(AN,AM){var AL=AM[0],AR=AM[1],AQ=AN.xaxis,AP=AN.yaxis;if(AL<AQ.min||AL>AQ.max||AR<AP.min||AR>AP.max){return }var AO=AN.points.radius+AN.points.lineWidth/2;AJ.lineWidth=AO;AJ.strokeStyle=C.color.parse(AN.color).scale("a",0.5).toString();var AK=1.5*AO;AJ.beginPath();AJ.arc(AQ.p2c(AL),AP.p2c(AR),AK,0,2*Math.PI,false);AJ.stroke()}function z(AN,AK){AJ.lineWidth=AN.bars.lineWidth;AJ.strokeStyle=C.color.parse(AN.color).scale("a",0.5).toString();var AM=C.color.parse(AN.color).scale("a",0.5).toString();var AL=AN.bars.align=="left"?0:-AN.bars.barWidth/2;AB(AK[0],AK[1],AK[2]||0,AL,AL+AN.bars.barWidth,0,function(){return AM},AN.xaxis,AN.yaxis,AJ,AN.bars.horizontal)}function R(AM,AL,AQ,AO){if(typeof AM=="string"){return AM}else{var AP=Y.createLinearGradient(0,AQ,0,AL);for(var AN=0,AK=AM.colors.length;AN<AK;++AN){var AR=AM.colors[AN];if(typeof AR!="string"){AR=C.color.parse(AO).scale("rgb",AR.brightness);AR.a*=AR.opacity;AR=AR.toString()}AP.addColorStop(AN/(AK-1),AR)}return AP}}}C.plot=function(G,E,D){var F=new B(C(G),E,D,C.plot.plugins);return F};C.plot.plugins=[];C.plot.formatDate=function(H,E,G){var L=function(N){N=""+N;return N.length==1?"0"+N:N};var D=[];var M=false;var K=H.getUTCHours();var I=K<12;if(G==null){G=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}if(E.search(/%p|%P/)!=-1){if(K>12){K=K-12}else{if(K==0){K=12}}}for(var F=0;F<E.length;++F){var J=E.charAt(F);if(M){switch(J){case"h":J=""+K;break;case"H":J=L(K);break;case"M":J=L(H.getUTCMinutes());break;case"S":J=L(H.getUTCSeconds());break;case"d":J=""+H.getUTCDate();break;case"m":J=""+(H.getUTCMonth()+1);break;case"y":J=""+H.getUTCFullYear();break;case"b":J=""+G[H.getUTCMonth()];break;case"p":J=(I)?("am"):("pm");break;case"P":J=(I)?("AM"):("PM");break}D.push(J);M=false}else{if(J=="%"){M=true}else{D.push(J)}}}return D.join("")};function A(E,D){return D*Math.floor(E/D)}})(jQuery);
\ No newline at end of file diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.navigate.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.navigate.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.navigate.js diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.valuelabels.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.valuelabels.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.valuelabels.js diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/options.html b/chrome/common/extensions/docs/examples/extensions/benchmark/options.html index 4a09047..24adc77 100644 --- a/chrome/common/extensions/docs/examples/extensions/benchmark/options.html +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/options.html @@ -4,7 +4,11 @@ <script src="jst/util.js" type="text/javascript"></script> <script src="jst/jsevalcontext.js" type="text/javascript"></script> <script src="jst/jstemplate.js" type="text/javascript"></script> - <script src="jst/jstemplate_example.js" type="text/javascript"></script> + <script src="jquery/jquery-1.4.2.min.js" type="text/javascript"></script> + <script src="jquery/jquery.flot.min.js" type="text/javascript"></script> + <script src="jquery/jquery.flot.dashes.js" type="text/javascript"></script> + <script src="util/table2CSV.js" type="text/javascript"></script> + <script src="util/sorttable.js" type="text/javascript"></script> <style> body { @@ -54,16 +58,16 @@ div#header p { display: inline; } -table.list { +table.sortable { font-size: 84%; table-layout: fixed; } -table.list:not([class*='filtered']) tr:nth-child(odd) td:not([class*='filtered']) { +table.sortable:not([class*='filtered']) tr:nth-child(even) td:not([class*='filtered']) { background: #eff3ff; } -table.list th { +.nobg { padding: 0 0.5em; vertical-align: bottom; font-weight: bold; @@ -72,6 +76,20 @@ table.list th { text-align: center; } +.bg{ + padding: 0 0.5em; + vertical-align: bottom; + font-weight: bold; + color: #315d94; + color: black; + text-align: center; + cursor: pointer; +} + +.bg:hover { + background: #eff3aa; +} + .avg { font-weight: bold; text-align: center; @@ -83,14 +101,35 @@ table.list th { } .bggraph { - background-color: #faa; white-space: nowrap; } +.file_input +{ + position: absolute; + width: 140px; + height: 26px; + overflow: hidden; +} + +.file_input_button +{ + width: 140px; + position: absolute; + top: 0px; +} + +.file_input_hidden +{ + font-size: 25px; + position: absolute; + right: 0px; + top: 0px; + opacity: 0; +} </style> <script> - var max_sample = 0; Array.max = function(array) { @@ -222,6 +261,293 @@ function restoreTable() { document.getElementById("expand").value = FULL_VIEW; } } + +// A class to store the data to plot. +function PData() { + this.xAxis = "Iteration(s)"; + this.yAxis = ""; + this.A = []; // Two data sets for comparison. + this.B = []; + this.avgA = []; // Avg value is plotted as a line. + this.avgB = []; + this.maxA = 0; + this.maxB = 0; + this.countA = 0; // Size of the data sets. + this.countB = 0; + + this.setYAxis = function (str) { + this.yAxis = str; + } + + this.setAvg = function (arr, cha) { + if (cha == 'A') { + var avgA = Array.avg(arr); + for (var i = 1; i <= this.countA; i++) { + this.avgA.push([i, avgA]); + } + } else if (cha == 'B') { + var avgB = Array.avg(arr); + for (var i = 1; i <= this.countB; i++) { + this.avgB.push([i, avgB]); + } + } + } + + this.setMax = function (arr, cha) { + if (cha == 'A') { + this.maxA = Array.max(arr); + } else if (cha == 'B') { + this.maxB = Array.max(arr); + } + } + + // Add an entry to the array. + this.addArr = function (val, cha) { + if (cha == 'A') { + this.countA++; + this.A.push([this.countA, val]); + } else if (cha == 'B') { + this.countB++; + this.B.push([this.countB, val]); + } + } + + // Plot the graph at the specified place. + this.plot = function (placeholder) { + $.plot(placeholder, + [// Line A + { + data: this.A, + label: "A's " + this.yAxis + " in " + this.countA + " " + this.xAxis, + points: { + show: true + }, + lines: { + show: true + } + }, + + // Line B + { + data: this.B, + label: "B's " + this.yAxis + " in " + this.countB + " " + this.xAxis, + points: { + show: true + }, + lines: { + show: true + } + }, + + // Line avgA + { + data: this.avgA, + label: "A's avg " + this.yAxis, + dashes: { + show: true + } + }, + + // Line avgB + { + data: this.avgB, + label: "B's avg " + this.yAxis, + dashes: { + show: true + } + }], + + // Axis and legend setup. + { xaxis: { + max: this.countA > this.countB ? this.countA : this.countB, + tickSize: 1, + tickDecimals: 0 + }, + yaxis: { + // Leave some space for legend. + max: this.maxA > this.maxB ? this.maxA * 1.5 : this.maxB * 1.5 + }, + legend: { + backgroundOpacity: 0 + } + }); + } +} + +// Compare the selected metric of the two selected data sets. +function compare() { + var checkboxArr = document.getElementsByName("checkboxArr"); + var radioArr = document.getElementsByName("radioArr"); + + if (checkAmount(checkboxArr) != 2) { + alert("please select two rows to compare"); + return; + } + + var rowIndexArr = getSelectedIndex(checkboxArr); + var colIndexArr = getSelectedIndex(radioArr); + // To this point, it is for sure that rowIndexArr has two elements + // while colIndexArr has one. + var selectedRowA = rowIndexArr[0]; + var selectedRowB = rowIndexArr[1]; + var selectedCol = colIndexArr[0]; + + var extension = chrome.extension.getBackgroundPage(); + var data = extension.results.data; + var selectedA = getSelectedResults(data,selectedRowA,selectedCol); + var selectedB = getSelectedResults(data,selectedRowB,selectedCol); + var yAxis = getMetricName(selectedCol); + + // Indicate A and B on selected rows. + checkboxArr[selectedRowA].parentElement.firstChild.data = "A"; + checkboxArr[selectedRowB].parentElement.firstChild.data = "B"; + + plot(selectedA, selectedB, yAxis); +} + +// Show the comparison graph. +function plot(A, B, axis) { + var plotData = new PData(); + + plotData.setYAxis(axis); + for (var i = 0; i < A.length; i++) { + plotData.addArr(A[i],'A'); + } + for (var i = 0; i < B.length; i++) { + plotData.addArr(B[i],'B'); + } + plotData.setAvg(A,'A'); + plotData.setAvg(B,'B'); + plotData.setMax(A,'A'); + plotData.setMax(B,'B'); + + var placeholder = document.getElementById("placeholder"); + placeholder.style.display = ""; + plotData.plot(placeholder); +} + +var METRIC = {"STARTLOAD": 0, "COMMITLOAD": 1, "DOCLOAD": 2, "PAINT": 3, + "TOTAL": 4, "REQUESTS": 5, "CONNECTS": 6, "READKB": 7, + "WRITEKB": 8, "READKBPS": 9, "WRITEKBPS": 10}; + +// Retrieve the metric name from index. +function getMetricName (index) { + switch (index) { + case METRIC.STARTLOAD: + return "Start Load Time"; + case METRIC.COMMITLOAD: + return "Commit Load Time"; + case METRIC.DOCLOAD: + return "Doc Load Time"; + case METRIC.PAINT: + return "Paint Time"; + case METRIC.TOTAL: + return "Total Load Time"; + case METRIC.REQUESTS: + return "# Requests"; + case METRIC.CONNECTS: + return "# Connects"; + case METRIC.READKB: + return "Read KB"; + case METRIC.WRITEKB: + return "Write KB"; + case METRIC.READKBPS: + return "Read KBps"; + case METRIC.WRITEKBPS: + return "Write KBps"; + default: + return ""; + } +} + +// Get the results with a specific row (data set) and column (metric). +function getSelectedResults(arr, rowIndex, colIndex) { + switch (colIndex) { + case METRIC.STARTLOAD: + return arr[rowIndex].startLoadResults; + case METRIC.COMMITLOAD: + return arr[rowIndex].commitLoadResults; + case METRIC.DOCLOAD: + return arr[rowIndex].docLoadResults; + case METRIC.PAINT: + return arr[rowIndex].paintResults; + case METRIC.TOTAL: + return arr[rowIndex].totalResults; + case METRIC.REQUESTS: + return arr[rowIndex].requests; + case METRIC.CONNECTS: + return arr[rowIndex].connects; + case METRIC.READKB: + return arr[rowIndex].KbytesRead; + case METRIC.WRITEKB: + return arr[rowIndex].KbytesWritten; + case METRIC.READKBPS: + return arr[rowIndex].readbpsResults; + case METRIC.WRITEKBPS: + return arr[rowIndex].writebpsResults; + default: + return undefined; + } +} + +// Ensure only two data sets (rows) are selected. +function checkAmount(arr) { + var amount = 0; + for (var i = 0; i < arr.length; i++) { + if (arr[i].checked) { + amount++; + } + } + return amount; +} + +// Get the index of selected row or column. +function getSelectedIndex(arr) { + var selectedArr = new Array(); + for (var i = 0; i < arr.length; i++) { + if(arr[i].checked) { + selectedArr.push(i); + } + } + return selectedArr; +} + +// Repaint or hide the chart. +function updateChart(caller) { + var placeholder = document.getElementById("placeholder"); + if (caller.type == "radio") { + // Other radio button is clicked. + if (placeholder.style.display == "") { + compare(); + } + } else { + // Other checkbox or clearing results is clicked. + if (placeholder.style.display == "") { + placeholder.style.display = "none"; + } + } +} + +// Clear indicators besides checkbox. +function clearIndicator () { + var checkboxArr = document.getElementsByName("checkboxArr"); + for (var i = 0; i < checkboxArr.length; i++) { + checkboxArr[i].parentElement.firstChild.data = ""; + } +} + +// Enable/Disable buttons according to checkbox change. +function checkSelected () { + var checkboxArr = document.getElementsByName("checkboxArr"); + if (checkAmount(checkboxArr) !=0) { + document.getElementById("clearSelected").disabled = false; + document.getElementById("compare").disabled = false; + } else { + document.getElementById("clearSelected").disabled = true; + document.getElementById("compare").disabled = true; + } +} + // Object to summarize everything var totals = {}; @@ -230,7 +556,8 @@ function computeDisplayResults(data) { var count = data.data.length; for (var i = 0; i < count; i++) { var obj = data.data[i]; - var resultList = obj.totalResults; + obj.displayTime = setDisplayTime(obj.timestamp); + var resultList = obj.totalResults; obj.mean = Array.avg(resultList); obj.stddev = Array.stddev(resultList); obj.stderr = obj.stddev / Math.sqrt(obj.iterations); @@ -239,18 +566,18 @@ function computeDisplayResults(data) { obj.cilow = obj.mean - ci; obj.min = Array.min(resultList); obj.max = Array.max(resultList); - obj.readbps = obj.bytesRead * 8 / obj.totalTime; - obj.writebps = obj.bytesWritten * 8 / obj.totalTime; - obj.readKB = obj.bytesRead / 1024; - obj.writeKB = obj.bytesWritten / 1024; + obj.readbps = Array.avg(obj.readbpsResults); + obj.writebps = Array.avg(obj.writebpsResults); + obj.readKB = Array.avg(obj.KbytesRead); + obj.writeKB = Array.avg(obj.KbytesWritten); obj.paintMean = Array.avg(obj.paintResults); obj.startLoadMean = Array.avg(obj.startLoadResults); obj.commitLoadMean = Array.avg(obj.commitLoadResults); obj.docLoadMean = Array.avg(obj.docLoadResults); - obj.displayRequests = obj.requests; - obj.displayConnects = obj.connects; - obj.displaySpdySessions = obj.spdySessions; + obj.displayRequests = Array.avg(obj.requests); + obj.displayConnects = Array.avg(obj.connects); + obj.displaySpdySessions = Array.avg(obj.spdySessions); obj.displayDomNum = obj.domNum; obj.displayMaxDepth = obj.maxDepth; @@ -260,6 +587,23 @@ function computeDisplayResults(data) { return count; } +// Convert timestamp to readable string. +function setDisplayTime(ts) { + var year = ts.getFullYear(); + var mon = ts.getMonth()+1; + var date = ts.getDate(); + var hrs = ts.getHours(); + var mins = ts.getMinutes(); + var secs = ts.getSeconds(); + + mon = ( mon < 10 ? "0" : "" ) + mon; + date = ( date < 10 ? "0" : "" ) + date; + mins = ( mins < 10 ? "0" : "" ) + mins; + secs = ( secs < 10 ? "0" : "" ) + secs; + + return (year + "/" + mon + "/" + date + " " + hrs + ":" + mins + ":" + secs); +} + // Subtract the results from two data sets. // This function could be smarter about what it subtracts, // for now it just subtracts everything. @@ -442,20 +786,51 @@ function run() { function showConfirm() { var r = confirm("Are you sure to clear results?"); if (r) { - clearResults(); + // Find out the event source element. + var evtSrc = window.event.srcElement; + if (evtSrc.value == "Clear Selected") { + clearSelected(); + } else if (evtSrc.value == "Clear All") { + clearResults(); + } + } +} + +// Clear the selected results +function clearSelected() { + var extension = chrome.extension.getBackgroundPage(); + var checkboxArr = document.getElementsByName("checkboxArr"); + var rowIndexArr = getSelectedIndex(checkboxArr); + var currIndex; + for (var i = 0; i < rowIndexArr.length; i++) { + currIndex = rowIndexArr[i]; + // Update the index of the original row in the modified array. + currIndex -= i; + extension.results.data.splice(currIndex, 1); + document.location.reload(true); + updateChart(this); + jsinit(); } } -// Clear the results +// Clear all the results function clearResults() { var extension = chrome.extension.getBackgroundPage(); extension.results = {}; extension.results.data = new Array(); document.getElementById("json").value = ""; document.getElementById("baseline").value = ""; + updateChart(this); jsinit(); } +// Export html table into CSV format. +function export() { + var checkboxArr = document.getElementsByName("checkboxArr"); + var rowNum = checkboxArr.length + 1; // # of data rows plus total-stats row. + $('#t').table2CSV(rowNum); +} + // Toggle display of an element function toggle(id) { var elt = document.getElementById(id); @@ -481,52 +856,58 @@ Clear Connections?<input id="clearconns" type="checkbox"> Clear Cache?<input id="clearcache" type="checkbox"> Enable Spdy?<input id="enablespdy" type="checkbox"> <br> -<span>URLs to load</span> <input type="text" id="testurl" size="100"> -<br> -Load URLs from file<input type="file" id="files" name="files[]" multiple /> -<br> -<form onsubmit="config();run()" style="display:inline"> +<span>URLs to load</span> <input type="text" id="testurl" size="80"> +<span class="file_input"> +<input class="file_input_button" type="button" value="Load URLs From File" /> +<input class="file_input_hidden" type="file" id="files" name="files[]" multiple /> +</span> +<form onsubmit="config();run()"> <input type="submit" value="Run"> </form> -<input type="button" value="Clear Results" onclick="showConfirm()"> <p> <h1>Results</h1> <input id="expand" type="button" value="Show More Details" onclick="expand()"> - -<table id="t" class="list" width="100%"> +<input id="clearSelected" type="button" value="Clear Selected" disabled="true" onclick="showConfirm()"> +<input id="clearAll" type="button" value="Clear All" onclick="showConfirm()"> +<input type="button" value="Export As .csv" onclick="export()"> +<table id="t" class="sortable" width="100%"> <tr> - <th width=150>url</th> - <th width=50>iterations</th> - <th width=50 >via spdy</th> - <th width=50 style="display:none">start load mean</th> - <th width=50 style="display:none">commit load mean</th> - <th width=50>doc load mean</th> - <th width=50>paint mean</th> - <th width=50>total load mean</th> - <th width=50>stddev</th> - <th width=50 style="display:none">stderr</th> - <th width=50 style="display:none">95% CI-low</th> - <th width=50 style="display:none">95% CI-high</th> - <th width=50 style="display:none">min</th> - <th width=50 style="display:none">max</th> - <th width=50 style="display:none"># Requests</th> - <th width=50 style="display:none"># Connects</th> - <th width=50 style="display:none"># SPDY Sessions</th> - <th width=50 style="display:none">Read KB</th> - <th width=50 style="display:none">Write KB</th> - <th width=50>Read KBps</th> - <th width=50>Write KBps</th> - <th width=50># DOM</th> - <th width=70 style="display:none">max DOM depth</th> - <th width=30 style="display:none">min</th> - <th width=30 style="display:none">avg</th> - <th samples style="display:none"></th> + <th width=35 class="nobg"></th> + <th width=215 class="nobg">url</th> + <th width=110 class="nobg" style="display:none">timestamp</th> + <th width=50 class="nobg">iterations</th> + <th width=50 class="nobg">via spdy</th> + <th width=50 class="bg" style="display:none">start load mean</th> + <th width=50 class="bg" style="display:none">commit load mean</th> + <th width=50 class="bg">doc load mean</th> + <th width=50 class="bg">paint mean</th> + <th width=50 class="bg">total load mean</th> + <th width=50 class="bg">stddev</th> + <th width=50 class="bg" style="display:none">stderr</th> + <th width=50 class="bg" style="display:none">95% CI-low</th> + <th width=50 class="bg" style="display:none">95% CI-high</th> + <th width=50 class="bg" style="display:none">min</th> + <th width=50 class="bg" style="display:none">max</th> + <th width=60 class="bg" style="display:none"># Requests</th> + <th width=60 class="bg" style="display:none"># Connects</th> + <th width=50 class="bg" style="display:none"># SPDY Sessions</th> + <th width=50 class="bg" style="display:none">Read KB</th> + <th width=50 class="bg" style="display:none">Write KB</th> + <th width=50 class="bg">Read KBps</th> + <th width=50 class="bg">Write KBps</th> + <th width=50 class="bg"># DOM</th> + <th width=70 class="bg" style="display:none">max DOM depth</th> + <th width=30 class="bg" style="display:none">min</th> + <th width=30 class="bg" style="display:none">avg</th> + <th samples class="nobg" style="display:none">total loan time samples</th> </tr> <tr id="t.total" jsselect="totals"> + <td class="avg" jseval="1"></td> <td class="url">TOTALS <span jscontent="url"></span></td> + <td class="avg" style="display:none"></td> <td class="avg" jseval="1"></td> <td class="avg" jseval="1"></td> <td class="avg" style="display:none"><span jseval="val = startLoadMean.toFixed(1)" jscontent="val"></span></td> @@ -544,11 +925,20 @@ Load URLs from file<input type="file" id="files" name="files[]" multiple /> <td class="avg" jseval="1"></td> <td class="avg" jseval="1"></td> <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> <td class="data"></td> </tr> <tr jsselect="data"> + <td align=right> <input type="checkbox" name="checkboxArr" onclick="updateChart(this);clearIndicator();checkSelected()"></td> <td class="url" jseval="$width = getWidth($this.mean, this); url.length > 40 ? $suburl = url.substring(0,27) + '...' + url.substring(url.length-10, url.length) : $suburl=url"><div jsvalues=".style.width:$width" class="bggraph"><a jsvalues="href:$this.url" jscontent="$suburl"></a></div></td> + <td class="avg" style="display:none" jseval="val = displayTime" jscontent="val"></td> <td class="avg" jseval="val = iterations" jscontent="val"></td> <td class="avg" jseval="val = viaSpdy" jscontent="val"></td> <td class="avg" style="display:none" jseval="val = startLoadMean.toFixed(1)" jscontent="val"></td> @@ -562,9 +952,9 @@ Load URLs from file<input type="file" id="files" name="files[]" multiple /> <td class="avg" style="display:none" jseval="val = cihigh.toFixed(1)" jscontent="val"></td> <td class="avg" style="display:none" jseval="val = min.toFixed(1)" jscontent="val"></td> <td class="avg" style="display:none" jseval="val = max.toFixed(1)" jscontent="val"></td> - <td class="avg" style="display:none" jseval="val = displayRequests" jscontent="val"></td> - <td class="avg" style="display:none" jseval="val = displayConnects" jscontent="val"></td> - <td class="avg" style="display:none" jseval="val = displaySpdySessions" jscontent="val"></td> + <td class="avg" style="display:none" jseval="val = displayRequests.toFixed(1)" jscontent="val"></td> + <td class="avg" style="display:none" jseval="val = displayConnects.toFixed(1)" jscontent="val"></td> + <td class="avg" style="display:none" jseval="val = displaySpdySessions.toFixed(1)" jscontent="val"></td> <td class="avg" style="display:none" jseval="val = readKB.toFixed(1)" jscontent="val"></td> <td class="avg" style="display:none" jseval="val = writeKB.toFixed(1)" jscontent="val"></td> <td class="avg" jseval="val = readbps.toFixed(1)" jscontent="val"></td> @@ -576,14 +966,45 @@ Load URLs from file<input type="file" id="files" name="files[]" multiple /> <td class="data" style="display:none"><span jsselect="totalResults"><span jscontent="$this"></span>,</span> </td> </tr> <tr jsdisplay="data.length == 0"> - <td colspan=11>No tests have been run yet.</td> + <td colspan=2>No tests have been run yet.</td> + </tr> + <tr jsdisplay="data.length > 1"> + <td width=25 jseval="1"></td> + <td class="url" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"> </td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)" checked></td> + <td class="avg" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + </tr> + <tr jsdisplay="data.length > 1"> + <td> <input id="compare" type="button" value="Compare" disabled="true" onclick="compare()"></td> </tr> </table> <hr> +<center> +<div id="placeholder" style="width:430px;height:230px;display:none">graph place</div> +</center> <span onclick="toggle('json')">JSON data</span><br> -<textarea style="display: none" type=text id=json rows=10 cols=50></textarea><p> +<textarea style="display:none" type=text id=json rows=10 cols=50></textarea><p> <span onclick="toggle('baseline')">COMPARE to</span><br> -<textarea style="display: none" type=text id=baseline rows=10 cols=50 +<textarea style="display:none" type=text id=baseline rows=10 cols=50 onchange="jsinit()"></textarea><p> </body> diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/util/sorttable.js b/chrome/common/extensions/docs/examples/extensions/benchmark/util/sorttable.js new file mode 100644 index 0000000..5583053 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/util/sorttable.js @@ -0,0 +1,489 @@ +/* + SortTable + version 2 + 7th April 2007 + Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ + + Instructions: + Download this file + Add <script src="sorttable.js"></script> to your HTML + Add class="sortable" to any table you'd like to make sortable + Click on the headers to sort + + Thanks to many, many people for contributions and suggestions. + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + This basically means: do what you want with it. +*/ + +/* +Changes by jning: +-Specify which coloumns to sort or not by changing index in makeSortable(). +-To avoid including the non-data rows (e.g., radio buttions) into sorting, + add index and row handling (deletion and appending) in makeSortable() and + reverse(). +-Remove the init parts for other browsers. +*/ + +var stIsIE = /*@cc_on!@*/false; + +sorttable = { + init: function() { + // quit if this function has already been called + if (arguments.callee.done) return; + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (_timer) clearInterval(_timer); + + if (!document.createElement || !document.getElementsByTagName) return; + + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; + + forEach(document.getElementsByTagName('table'), function(table) { + if (table.className.search(/\bsortable\b/) != -1) { + sorttable.makeSortable(table); + } + }); + + }, + + makeSortable: function(table) { + if (table.getElementsByTagName('thead').length == 0) { + // table doesn't have a tHead. Since it should have, create one and + // put the first table row in it. + the = document.createElement('thead'); + the.appendChild(table.rows[0]); + table.insertBefore(the,table.firstChild); + } + // Safari doesn't support table.tHead, sigh + if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; + + if (table.tHead.rows.length != 1) return; // can't cope with two header rows + + // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as + // "total" rows, for example). This is B&R, since what you're supposed + // to do is put them in a tfoot. So, if there are sortbottom rows, + // for backwards compatibility, move them to tfoot (creating it if needed). + sortbottomrows = []; + for (var i=0; i<table.rows.length; i++) { + if (table.rows[i].className.search(/\bsortbottom\b/) != -1) { + sortbottomrows[sortbottomrows.length] = table.rows[i]; + } + } + if (sortbottomrows) { + if (table.tFoot == null) { + // table doesn't have a tfoot. Create one. + tfo = document.createElement('tfoot'); + table.appendChild(tfo); + } + for (var i=0; i<sortbottomrows.length; i++) { + tfo.appendChild(sortbottomrows[i]); + } + delete sortbottomrows; + } + + // work through each column and calculate its type + headrow = table.tHead.rows[0].cells; + for (var i=5; i<headrow.length-1; i++) { + // manually override the type with a sorttable_type attribute + if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col + mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); + if (mtch) { override = mtch[1]; } + if (mtch && typeof sorttable["sort_"+override] == 'function') { + headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; + } else { + headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); + } + // make it clickable to sort + headrow[i].sorttable_columnindex = i; + headrow[i].sorttable_tbody = table.tBodies[0]; + dean_addEvent(headrow[i],"click", function(e) { + + if (this.className.search(/\bsorttable_sorted\b/) != -1) { + // if we're already sorted by this column, just + // reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted', + 'sorttable_sorted_reverse'); + this.removeChild(document.getElementById('sorttable_sortfwdind')); + sortrevind = document.createElement('span'); + sortrevind.id = "sorttable_sortrevind"; + sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴'; + this.appendChild(sortrevind); + return; + } + if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { + // if we're already sorted by this column in reverse, just + // re-reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted_reverse', + 'sorttable_sorted'); + this.removeChild(document.getElementById('sorttable_sortrevind')); + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + return; + } + + // remove sorttable_sorted classes + theadrow = this.parentNode; + forEach(theadrow.childNodes, function(cell) { + if (cell.nodeType == 1) { // an element + cell.className = cell.className.replace('sorttable_sorted_reverse',''); + cell.className = cell.className.replace('sorttable_sorted',''); + } + }); + sortfwdind = document.getElementById('sorttable_sortfwdind'); + if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } + sortrevind = document.getElementById('sorttable_sortrevind'); + if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } + + this.className += ' sorttable_sorted'; + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + + // build an array to sort. This is a Schwartzian transform thing, + // i.e., we "decorate" each row with the actual sort key, + // sort based on the sort keys, and then put the rows back in order + // which is a lot faster because you only do getInnerText once per row + row_array = []; + col = this.sorttable_columnindex; + rows = this.sorttable_tbody.rows; + for (var j=1; j<rows.length-3; j++) { + row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]]; + } + /* If you want a stable sort, uncomment the following line */ + sorttable.shaker_sort(row_array, this.sorttable_sortfunction); + /* and comment out this one */ + //row_array.sort(this.sorttable_sortfunction); + + tb = this.sorttable_tbody; + var noTestRow = tb.rows[tb.rows.length-3]; + var radioRow = tb.rows[tb.rows.length-2]; + var compareRow = tb.rows[tb.rows.length-1]; + tb.deleteRow(tb.rows.length-1); + tb.deleteRow(tb.rows.length-1); + tb.deleteRow(tb.rows.length-1); + for (var j=0; j<row_array.length; j++) { + tb.appendChild(row_array[j][1]); + } + tb.appendChild(noTestRow); + tb.appendChild(radioRow); + tb.appendChild(compareRow); + delete row_array; + delete noTestRow; + delete radioRow; + delete compareRow; + }); + } + } + }, + + guessType: function(table, column) { + // guess the type of a column based on its first non-blank row + sortfn = sorttable.sort_numeric; + for (var i=0; i<table.tBodies[0].rows.length-3; i++) { + text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]); + if (text != '') { + if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) { + return sorttable.sort_numeric; + } + // check for a date: dd/mm/yyyy or dd/mm/yy + // can have / or . or - as separator + // can be mm/dd as well + possdate = text.match(sorttable.DATE_RE) + if (possdate) { + // looks like a date + first = parseInt(possdate[1]); + second = parseInt(possdate[2]); + if (first > 12) { + // definitely dd/mm + return sorttable.sort_ddmm; + } else if (second > 12) { + return sorttable.sort_mmdd; + } else { + // looks like a date, but we can't tell which, so assume + // that it's dd/mm (English imperialism!) and keep looking + sortfn = sorttable.sort_ddmm; + } + } + } + } + return sortfn; + }, + + getInnerText: function(node) { + // gets the text we want to use for sorting for a cell. + // strips leading and trailing whitespace. + // this is *not* a generic getInnerText function; it's special to sorttable. + // for example, you can override the cell text with a customkey attribute. + // it also gets .value for <input> fields. + + hasInputs = (typeof node.getElementsByTagName == 'function') && + node.getElementsByTagName('input').length; + + if (node.getAttribute("sorttable_customkey") != null) { + return node.getAttribute("sorttable_customkey"); + } + else if (typeof node.textContent != 'undefined' && !hasInputs) { + return node.textContent.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.innerText != 'undefined' && !hasInputs) { + return node.innerText.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.text != 'undefined' && !hasInputs) { + return node.text.replace(/^\s+|\s+$/g, ''); + } + else { + switch (node.nodeType) { + case 3: + if (node.nodeName.toLowerCase() == 'input') { + return node.value.replace(/^\s+|\s+$/g, ''); + } + case 4: + return node.nodeValue.replace(/^\s+|\s+$/g, ''); + break; + case 1: + case 11: + var innerText = ''; + for (var i = 0; i < node.childNodes.length; i++) { + innerText += sorttable.getInnerText(node.childNodes[i]); + } + return innerText.replace(/^\s+|\s+$/g, ''); + break; + default: + return ''; + } + } + }, + + reverse: function(tbody) { + // reverse the rows in a tbody + newrows = []; + for (var i=0; i<tbody.rows.length; i++) { + newrows[newrows.length] = tbody.rows[i]; + } + tbody.appendChild(newrows[0]); + for (var i=newrows.length-4; i>0; i--) { + tbody.appendChild(newrows[i]); + } + tbody.appendChild(newrows[newrows.length-3]); + tbody.appendChild(newrows[newrows.length-2]); + tbody.appendChild(newrows[newrows.length-1]); + delete newrows; + }, + + /* sort functions + each sort function takes two parameters, a and b + you are comparing a[0] and b[0] */ + sort_numeric: function(a,b) { + aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); + if (isNaN(aa)) aa = 0; + bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); + if (isNaN(bb)) bb = 0; + return aa-bb; + }, + sort_alpha: function(a,b) { + if (a[0]==b[0]) return 0; + if (a[0]<b[0]) return -1; + return 1; + }, + sort_ddmm: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + sort_mmdd: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + + shaker_sort: function(list, comp_func) { + // A stable sort function to allow multi-level sorting of data + // see: http://en.wikipedia.org/wiki/Cocktail_sort + // thanks to Joseph Nahmias + var b = 0; + var t = list.length - 1; + var swap = true; + + while(swap) { + swap = false; + for(var i = b; i < t; ++i) { + if ( comp_func(list[i], list[i+1]) > 0 ) { + var q = list[i]; list[i] = list[i+1]; list[i+1] = q; + swap = true; + } + } // for + t--; + + if (!swap) break; + + for(var i = t; i > b; --i) { + if ( comp_func(list[i], list[i-1]) < 0 ) { + var q = list[i]; list[i] = list[i-1]; list[i-1] = q; + swap = true; + } + } // for + b++; + + } // while(swap) + } +} + +/* ****************************************************************** + Supporting functions: bundled here to avoid depending on a library + ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for other browsers */ +window.onload = sorttable.init; + +// written by Dean Edwards, 2005 +// with input from Tino Zijdel, Matthias Miller, Diego Perini + +// http://dean.edwards.name/weblog/2005/10/add-event/ + +function dean_addEvent(element, type, handler) { + if (element.addEventListener) { + element.addEventListener(type, handler, false); + } else { + // assign each event handler a unique ID + if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++; + // create a hash table of event types for the element + if (!element.events) element.events = {}; + // create a hash table of event handlers for each element/event pair + var handlers = element.events[type]; + if (!handlers) { + handlers = element.events[type] = {}; + // store the existing event handler (if there is one) + if (element["on" + type]) { + handlers[0] = element["on" + type]; + } + } + // store the event handler in the hash table + handlers[handler.$$guid] = handler; + // assign a global event handler to do all the work + element["on" + type] = handleEvent; + } +}; +// a counter used to create unique IDs +dean_addEvent.guid = 1; + +function removeEvent(element, type, handler) { + if (element.removeEventListener) { + element.removeEventListener(type, handler, false); + } else { + // delete the event handler from the hash table + if (element.events && element.events[type]) { + delete element.events[type][handler.$$guid]; + } + } +}; + +function handleEvent(event) { + var returnValue = true; + // grab the event object (IE uses a global event object) + event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); + // get a reference to the hash table of event handlers + var handlers = this.events[event.type]; + // execute each event handler + for (var i in handlers) { + this.$$handleEvent = handlers[i]; + if (this.$$handleEvent(event) === false) { + returnValue = false; + } + } + return returnValue; +}; + +function fixEvent(event) { + // add W3C standard event methods + event.preventDefault = fixEvent.preventDefault; + event.stopPropagation = fixEvent.stopPropagation; + return event; +}; +fixEvent.preventDefault = function() { + this.returnValue = false; +}; +fixEvent.stopPropagation = function() { + this.cancelBubble = true; +} + +// Dean's forEach: http://dean.edwards.name/base/forEach.js +/* + forEach, version 1.0 + Copyright 2006, Dean Edwards + License: http://www.opensource.org/licenses/mit-license.php +*/ + +// array-like enumeration +if (!Array.forEach) { // mozilla already supports this + Array.forEach = function(array, block, context) { + for (var i = 0; i < array.length; i++) { + block.call(context, array[i], i, array); + } + }; +} + +// generic enumeration +Function.prototype.forEach = function(object, block, context) { + for (var key in object) { + if (typeof this.prototype[key] == "undefined") { + block.call(context, object[key], key, object); + } + } +}; + +// character enumeration +String.forEach = function(string, block, context) { + Array.forEach(string.split(""), function(chr, index) { + block.call(context, chr, index, string); + }); +}; + +// globally resolve forEach enumeration +var forEach = function(object, block, context) { + if (object) { + var resolve = Object; // default + if (object instanceof Function) { + // functions have a "length" property + resolve = Function; + } else if (object.forEach instanceof Function) { + // the object implements a custom forEach method so use that + object.forEach(block, context); + return; + } else if (typeof object == "string") { + // the object is a string + resolve = String; + } else if (typeof object.length == "number") { + // the object is array-like + resolve = Array; + } + resolve.forEach(object, block, context); + } +}; diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/util/table2CSV.js b/chrome/common/extensions/docs/examples/extensions/benchmark/util/table2CSV.js new file mode 100644 index 0000000..04a5166 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/util/table2CSV.js @@ -0,0 +1,90 @@ +/* +This is a small JQuery utility to export HTML table as CSV file. + +The author is Kunal Babre and the original script can be found in +http://www.kunalbabre.com/projects/table2CSV.php. Permissions are +granted by the author to make changes and redistribute. + +Changes made by jning: To avoid exporting the textbox, radio buttons and etc. +in the table, the parameters rowNum and index in $().find().each() or +$().filter().find.each() help to ignore non-data cells. +*/ + +jQuery.fn.table2CSV = function(rowNum, options) { + var options = jQuery.extend({ + separator: ',', + header: [], + delivery: 'popup' // popup, value + }, + options); + + var csvData = []; + var headerArr = []; + var el = this; + + //header + var numCols = options.header.length; + var tmpRow = []; // construct header avalible array + + if (numCols > 0) { + for (var i = 0; i < numCols; i++) { + tmpRow[tmpRow.length] = formatData(options.header[i]); + } + } else { + $(el).filter(':visible').find('th').each(function(index) { + if (index > 0 && $(this).css('display') != 'none') + tmpRow[tmpRow.length] = formatData($(this).html()); + }); + } + + row2CSV(tmpRow); + + // actual data + $(el).find('tr').each(function(index) { + if (index < rowNum + 1) { + var tmpRow = []; + $(this).filter(':visible').find('td').each(function(index) { + if (index > 0 && $(this).css('display') != 'none') + tmpRow[tmpRow.length] = formatData($(this).html()); + }); + row2CSV(tmpRow); + } + }); + if (options.delivery == 'popup') { + var mydata = csvData.join('\n'); + return popup(mydata); + } else { + var mydata = csvData.join('\n'); + return mydata; + } + + function row2CSV(tmpRow) { + var tmp = tmpRow.join('') // to remove any blank rows + // alert(tmp); + if (tmpRow.length > 0 && tmp != '') { + var mystr = tmpRow.join(options.separator); + csvData[csvData.length] = mystr; + } + } + function formatData(input) { + // replace " with “ + var regexp = new RegExp(/["]/g); + var output = input.replace(regexp, "“"); + //HTML + var regexp = new RegExp(/\<[^\<]+\>/g); + var output = output.replace(regexp, ""); + if (output == "") return ''; + return '"' + output + '"'; + } + function popup(data) { + var generator = window.open('', 'csv', 'height=400,width=600'); + generator.document.write('<html><head><title>CSV</title>'); + generator.document.write('</head><body >'); + generator.document.write('<textArea cols=70 rows=15 wrap="off" >'); + generator.document.write(data); + generator.document.write('</textArea>'); + generator.document.write('</body></html>'); + generator.document.close(); + return true; + } +}; diff --git a/chrome/common/extensions/docs/experimental.clipboard.html b/chrome/common/extensions/docs/experimental.clipboard.html index 0bf145d..dc9d20b 100644 --- a/chrome/common/extensions/docs/experimental.clipboard.html +++ b/chrome/common/extensions/docs/experimental.clipboard.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.clipboard - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.contextMenus.html b/chrome/common/extensions/docs/experimental.contextMenus.html index db22bb4..5c11ce2 100644 --- a/chrome/common/extensions/docs/experimental.contextMenus.html +++ b/chrome/common/extensions/docs/experimental.contextMenus.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>experimental.chrome.contextMenus - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.cookies.html b/chrome/common/extensions/docs/experimental.cookies.html index 1eaeee9..b57ea7f 100644 --- a/chrome/common/extensions/docs/experimental.cookies.html +++ b/chrome/common/extensions/docs/experimental.cookies.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>experimental.chrome.cookies - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.history.html b/chrome/common/extensions/docs/experimental.history.html index 5b1b412..7a2deda 100644 --- a/chrome/common/extensions/docs/experimental.history.html +++ b/chrome/common/extensions/docs/experimental.history.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>experimental.chrome.history - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.html b/chrome/common/extensions/docs/experimental.html index 08480af..3fe2e15 100644 --- a/chrome/common/extensions/docs/experimental.html +++ b/chrome/common/extensions/docs/experimental.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.* APIs - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.idle.html b/chrome/common/extensions/docs/experimental.idle.html index d7b2d80..7712706 100644 --- a/chrome/common/extensions/docs/experimental.idle.html +++ b/chrome/common/extensions/docs/experimental.idle.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>experimental.chrome.idle - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.infobars.html b/chrome/common/extensions/docs/experimental.infobars.html index 97d859d..0c9295d 100644 --- a/chrome/common/extensions/docs/experimental.infobars.html +++ b/chrome/common/extensions/docs/experimental.infobars.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.infobars - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.omnibox.html b/chrome/common/extensions/docs/experimental.omnibox.html index 995f838..1a668ed 100644 --- a/chrome/common/extensions/docs/experimental.omnibox.html +++ b/chrome/common/extensions/docs/experimental.omnibox.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.omnibox - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -1259,7 +1263,7 @@ You can find samples of this API on the </div> </div> - </dl> + </dl> </div> </dd> diff --git a/chrome/common/extensions/docs/experimental.processes.html b/chrome/common/extensions/docs/experimental.processes.html index 801ff1e..a5e3b05 100644 --- a/chrome/common/extensions/docs/experimental.processes.html +++ b/chrome/common/extensions/docs/experimental.processes.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.processes - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.proxy.html b/chrome/common/extensions/docs/experimental.proxy.html index 57d48f5..3d27695 100644 --- a/chrome/common/extensions/docs/experimental.proxy.html +++ b/chrome/common/extensions/docs/experimental.proxy.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.proxy - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.sidebar.html b/chrome/common/extensions/docs/experimental.sidebar.html index eb9493b..35b757b 100644 --- a/chrome/common/extensions/docs/experimental.sidebar.html +++ b/chrome/common/extensions/docs/experimental.sidebar.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.sidebar - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.webNavigation.html b/chrome/common/extensions/docs/experimental.webNavigation.html index 8ab76fc..aebd5ca 100644 --- a/chrome/common/extensions/docs/experimental.webNavigation.html +++ b/chrome/common/extensions/docs/experimental.webNavigation.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.webNavigation - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.webRequest.html b/chrome/common/extensions/docs/experimental.webRequest.html index 9615bcc..0541584 100644 --- a/chrome/common/extensions/docs/experimental.webRequest.html +++ b/chrome/common/extensions/docs/experimental.webRequest.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.webRequest - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/extension.html b/chrome/common/extensions/docs/extension.html index b361568..36c768f 100644 --- a/chrome/common/extensions/docs/extension.html +++ b/chrome/common/extensions/docs/extension.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.extension - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -1891,7 +1895,7 @@ For details, see </div> </div> - </dl> + </dl> </div> </dd> diff --git a/chrome/common/extensions/docs/external_extensions.html b/chrome/common/extensions/docs/external_extensions.html index 6bf9e4f..d3f18ad 100644 --- a/chrome/common/extensions/docs/external_extensions.html +++ b/chrome/common/extensions/docs/external_extensions.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Other Deployment Options - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/faq.html b/chrome/common/extensions/docs/faq.html index 94ec93b..ea4af3a 100644 --- a/chrome/common/extensions/docs/faq.html +++ b/chrome/common/extensions/docs/faq.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Frequently Asked Questions - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -376,12 +380,21 @@ try the <p> As long as you are using a version of Google Chrome that supports extensions, you already have everything you need to start writing an - extension of your own. Select <b>Extensions</b> from the Tools menu - <img style="vertical-align: middle; margin:0; padding:0" src="images/toolsmenu.gif"> (or from - the Window menu on Mac) and click <b>Developer Mode</b>. From there, you can load - an unpacked directory of files as if it were a packaged extension, reload - extensions, and more. For a complete tutorial, please view - <a href="http://code.google.com/chrome/extensions/getstarted.html">this getting started guide</a>. + extension of your own. + You can start by turning on Developer mode. + </p> + + <p> + Click the wrench icon + <img src="images/toolsmenu.gif" height="29" width="29" alt="" class="nomargin"> + and select <b>Extensions</b> from the <b>Tools</b> menu + (or from the <b>Window</b> menu on Mac). + If there's a "+" next to "Developer mode", + click the "+" so it turns into a "-". + Now you can reload extensions, + load an unpacked directory of files as if it were a packaged extension, + and more. For a complete tutorial, see + <a href="http://code.google.com/chrome/extensions/getstarted.html">Getting Started</a>. </p> <h3 id="faq-dev-02">Can I make cross-domain Ajax requests in an extension?</h3> @@ -679,7 +692,9 @@ win,stable,#.#.###.#,#.#.###.#</pre> to the ticket you starred or opened. This will make it easier for others with the same request to find the correct ticket. </li> -</ol></div> +</ol> + +</div> <!-- API PAGE --> <div class="apiPage" style="display: none; "> diff --git a/chrome/common/extensions/docs/getstarted.html b/chrome/common/extensions/docs/getstarted.html index a3a10fc..5590e32 100644 --- a/chrome/common/extensions/docs/getstarted.html +++ b/chrome/common/extensions/docs/getstarted.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tutorial: Getting Started (Hello, World!) - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -358,14 +362,12 @@ to the toolbar of Google Chrome. </li> <li id="load-ext"> Load the extension. <ol type="a"> - <li> + <li style="margin-top:0"> Bring up the extensions management page - by clicking the Tools menu - <img src="images/toolsmenu.gif" width="43" height="34" alt="" align="absmiddle" style="margin:0; padding:0"> - (or the Window menu on Mac) - and choosing <b>Extensions</b>. - (If you're using version 6 or later, - choose <b>Tools > Extensions</b>.) + by clicking the wrench icon + <img src="images/toolsmenu.gif" width="29" height="29" alt="" style="margin-top:0"> + and choosing <b>Tools > Extensions</b>. + (On Mac, use <b>Window > Extensions</b>.) </li> <li> diff --git a/chrome/common/extensions/docs/history.html b/chrome/common/extensions/docs/history.html index 41b93ab..5fed0af 100644 --- a/chrome/common/extensions/docs/history.html +++ b/chrome/common/extensions/docs/history.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.history - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/hosting.html b/chrome/common/extensions/docs/hosting.html index 20267e7..55c0178 100644 --- a/chrome/common/extensions/docs/hosting.html +++ b/chrome/common/extensions/docs/hosting.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Hosting - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/i18n-messages.html b/chrome/common/extensions/docs/i18n-messages.html index 7ff4c33..823bfc3 100644 --- a/chrome/common/extensions/docs/i18n-messages.html +++ b/chrome/common/extensions/docs/i18n-messages.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Formats: Locale-Specific Messages - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/i18n.html b/chrome/common/extensions/docs/i18n.html index 6809923..b33c856 100644 --- a/chrome/common/extensions/docs/i18n.html +++ b/chrome/common/extensions/docs/i18n.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Internationalization (i18n) - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -767,7 +771,7 @@ Here's how to change the locale using the UI on Google Chrome for Windows: </p> <ol> - <li> Tools menu (wrench) > <b>Options</b> </li> + <li> Wrench icon > <b>Options</b> </li> <li> Choose the <b>Under the Hood</b> tab </li> <li> Scroll down to <b>Web Content</b> </li> <li> Click <b>Change font and language settings</b> </li> diff --git a/chrome/common/extensions/docs/idle.html b/chrome/common/extensions/docs/idle.html index 2fd5dd8..0d6988c 100644 --- a/chrome/common/extensions/docs/idle.html +++ b/chrome/common/extensions/docs/idle.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Idle - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/images/toggle_sprite.png b/chrome/common/extensions/docs/images/toggle_sprite.png Binary files differnew file mode 100644 index 0000000..a91c2d4 --- /dev/null +++ b/chrome/common/extensions/docs/images/toggle_sprite.png diff --git a/chrome/common/extensions/docs/images/toolsmenu.gif b/chrome/common/extensions/docs/images/toolsmenu.gif Binary files differindex d6afcab..1b0b1e5 100644 --- a/chrome/common/extensions/docs/images/toolsmenu.gif +++ b/chrome/common/extensions/docs/images/toolsmenu.gif diff --git a/chrome/common/extensions/docs/index.html b/chrome/common/extensions/docs/index.html index 826d74b..12e3114 100644 --- a/chrome/common/extensions/docs/index.html +++ b/chrome/common/extensions/docs/index.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/js/sidebar.js b/chrome/common/extensions/docs/js/sidebar.js new file mode 100644 index 0000000..51d9292 --- /dev/null +++ b/chrome/common/extensions/docs/js/sidebar.js @@ -0,0 +1,84 @@ +/** + * 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. + */ + +var TEXT_NODE = 3; // Text nodes have nodeType of 3. + +/** + * Toggles the display of nodes given the status of their associated controls. + * + * For each node passed to this function, check to see if a toggle has been + * inserted into the node's parent. If yes, change the state of the toggle and + * hide/reveal the node as needed. + * + * @param {NodeList|Node|Array.<Node>} Nodes to operate on. + */ +function toggleList(list) { + if (typeof list.length != 'number') { + list = Array(list); + } + + for (var i = 0; i < list.length; i++) { + var toggle = list[i].parentNode && + list[i].parentNode.firstChild; + if (toggle && toggle.className.substring(0, 6) == 'toggle') { + var visible = toggle.className == 'toggle'; + list[i].style.display = visible ? 'block' : 'none'; + toggle.className = visible ? 'toggle selected' : 'toggle'; + } + } +}; + +/** + * Reveals the hidden ancestor of the passed node, adjusts toggles as needed. + * + * @param {Node} node The node whose ancestor is a hidden toggleable element. + */ +function revealAncestor(node) { + while (node.parentNode) { + if (node.style.display == 'none') { + toggleList(node); + break; + } + node = node.parentNode; + } +}; + +/** + * Adds toggle controls to the sidebar list. + * + * Controls are inserted as the first children of list items in the sidebar + * which contain only text (not a link). Handlers are set up so that when a + * toggle control is clicked, any <ul> elements who are siblings of the control + * are hidden/revealed as appropriate given the control's state. + * + * If a list item possesses the class "leftNavSelected" its ancestor <ul> is + * revealed by default (it represents the current page). + */ +function initToggles() { + var toc = document.getElementById('gc-toc'); + var items = toc.getElementsByTagName('li'); + var selectedNode = null; + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (item.className == 'leftNavSelected') { + selectedNode = item; + } else if (item.firstChild && + item.firstChild.nodeType == TEXT_NODE) { + // Only assign toggles to text nodes in the sidebar. + var a = document.createElement('a'); + a.className = 'toggle selected'; + a.appendChild(document.createTextNode(' ')); + a.onclick = function() { + toggleList(this.parentNode.getElementsByTagName('ul')); + }; + item.insertBefore(a, item.firstChild); + toggleList(item.getElementsByTagName('ul')); + } + } + if (selectedNode) { + revealAncestor(selectedNode); + } +}; diff --git a/chrome/common/extensions/docs/management.html b/chrome/common/extensions/docs/management.html index 8fb7add..2792019 100644 --- a/chrome/common/extensions/docs/management.html +++ b/chrome/common/extensions/docs/management.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Management - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -234,10 +238,10 @@ <div id="toc"> <h2>Contents</h2> <ol> - <li style="display: none; "> - <a>h2Name</a> + <li> + <a href="#manifest">Manifest</a> <ol> - <li> + <li style="display: none; "> <a>h3Name</a> </li> </ol> @@ -257,6 +261,8 @@ <a href="#methods">Methods</a> <ol> <li> + <a href="#method-get">get</a> + </li><li> <a href="#method-getAll">getAll</a> </li><li> <a href="#method-launchApp">launchApp</a> @@ -304,10 +310,27 @@ <!-- STATIC CONTENT PLACEHOLDER --> <div id="static"><div id="pageData-name" class="pageData">Management</div> + <!-- BEGIN AUTHORED CONTENT --> <p id="classSummary"> The <code>chrome.management</code> module provides ways to manage the list of extensions/apps that are installed and running. It is particularly useful for extensions that <a href="override.html">override</a> the built-in New Tab page. </p> + +<h2 id="manifest">Manifest</h2> + +<p>You must declare the "management" permission +in the <a href="manifest.html">extension manifest</a> +to use the management API. +For example:</p> +<pre>{ + "name": "My extension", + ... + <b>"permissions": [ + "management" + ]</b>, + ... +}</pre> + <!-- END AUTHORED CONTENT --> </div> @@ -341,6 +364,243 @@ The <code>chrome.management</code> module provides ways to manage the list of ex <!-- iterates over all functions --> <div class="apiItem"> + <a name="method-get"></a> <!-- method-anchor --> + <h4>get</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.management.get</span>(<span class="null"><span style="display: none; ">, </span><span>string</span> + <var><span>id</span></var></span><span class="optional"><span>, </span><span>function</span> + <var><span>callback</span></var></span>)</div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Return information about the installed extension with the given ID.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>id</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The ID from an item of <a href="management.html#type-ExtensionInfo">ExtensionInfo</a>.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>callback</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>function</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + + <!-- RETURNS --> + <h4 style="display: none; ">Returns</h4> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div> + <div> + <h4>Callback function</h4> + <p style="display: none; "> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span>ExtensionInfo result</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + <dt> + <var>result</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span> + <a href="management.html#type-ExtensionInfo">ExtensionInfo</a> + </span> + <span style="display: none; "> + <span> + array of <span><span></span></span> + </span> + <span>paramType</span> + <span></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p style="display: none; "> + This function was added in version <b><span></span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + + </div><div class="apiItem"> <a name="method-getAll"></a> <!-- method-anchor --> <h4>getAll</h4> diff --git a/chrome/common/extensions/docs/manifest.html b/chrome/common/extensions/docs/manifest.html index d8bd4f4..b593f31 100644 --- a/chrome/common/extensions/docs/manifest.html +++ b/chrome/common/extensions/docs/manifest.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Formats: Manifest Files - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -245,11 +249,15 @@ <a href="#H2-1">Field details</a> <ol> <li> + <a href="#default_locale">default_locale</a> + </li><li> <a href="#description">description</a> </li><li> + <a href="#homepage_url">homepage_url</a> + </li><li> <a href="#icons">icons</a> </li><li> - <a href="#default_locale">default_locale</a> + <a href="#incognito">incognito</a> </li><li> <a href="#key">key</a> </li><li> @@ -260,8 +268,6 @@ <a href="#permissions">permissions</a> </li><li> <a href="#version">version</a> - </li><li> - <a href="#incognito">incognito</a> </li> </ol> </li> @@ -350,13 +356,14 @@ are <b>name</b> and <b>version</b>. "<a href="background_pages.html">background_page</a>": "<em>aFile</em>.html", "<a href="override.html">chrome_url_overrides</a>": {...}, "<a href="content_scripts.html">content_scripts</a>": [...], + "<a href="#homepage_url">homepage_url</a>": "http://<em>path/to/homepage</em>", + "<a href="#incognito">incognito</a>": "spanning" <em>or</em> "split", "<a href="#key">key</a>": "<em>publicKey</em>", "<a href="#minimum_chrome_version">minimum_chrome_version</a>": "<em>versionString</em>", "<a href="options.html">options_page</a>": "<em>aFile</em>.html", "<a href="#permissions">permissions</a>": [...], "<a href="npapi.html">plugins</a>": [...], "<a href="autoupdate.html">update_url</a>": "http://<em>path/to/updateInfo</em>.xml" - "<a href="#incognito">incognito</a>": "<em>split</em> or <em>spanning</em>", } </pre> @@ -370,6 +377,19 @@ with links to where they're described in detail, see the <a href="#overview">Field summary</a>. </p> +<h3 id="default_locale">default_locale</h3> + +<p> +Specifies the subdirectory of <code>_locales</code> +that contains the default strings for this extension. +This field is <b>required</b> in extensions +that have a <code>_locales</code> directory; +it <b>must be absent</b> in extensions +that have no <code>_locales</code> directory. +For details, see +<a href="i18n.html">Internationalization</a>. +</p> + <h3 id="description">description</h3> <p> @@ -384,6 +404,15 @@ You can specify locale-specific strings for this field; see <a href="i18n.html">Internationalization</a> for details. </p> +<h3 id="homepage_url">homepage_url</h3> + +<p> +The URL of the homepage for this extension. The extensions management page (chrome://extensions) +will contain a link to this URL. This field is particularly useful if you +<a href="hosting.html">host the extension on your own site</a>. If you distribute your +extension using the <a href="http://chrome.google.com/extensions">gallery</a>, +the homepage URL defaults to the extension's own gallery page. +</p> <h3 id="icons">icons</h3> @@ -428,8 +457,6 @@ extension developers are using the documented sizes. If you use other sizes, your icon may look bad in future versions of the browser. </p> - - <p> If you submit your extension to the <a href="https://chrome.google.com/extensions">gallery</a>, @@ -441,17 +468,35 @@ see the <a href="http://www.google.com/support/chrome/bin/answer.py?answer=113909">gallery help</a>. </p> -<h3 id="default_locale">default_locale</h3> +<h3 id="incognito">incognito</h3> <p> -Specifies the subdirectory of <code>_locales</code> -that contains the default strings for this extension. -This field is <b>required</b> in extensions -that have a <code>_locales</code> directory; -it <b>must be absent</b> in extensions -that have no <code>_locales</code> directory. -For details, see -<a href="i18n.html">Internationalization</a>. +Either "spanning" or "split", to specify how this extension will +behave if allowed to run in incognito mode. +</p> + +<p> +The default for extensions is "spanning", which means that the extension +will run in a single shared process. Any events or messages from an incognito +tab will be sent to the shared process, with an <em>incognito</em> flag +indicating where it came from. +</p> + +<p> +The default for installable web apps is "split", +which means that all app pages in +an incognito window will run in their own incognito process. If the app or extension contains a background page, that will also run in the incognito process. +This incognito process runs along side the regular process, but has a separate +memory-only cookie store. Each process sees events and messages only from its +own context (for example, the incognito process will see only incognito tab updates). +The processes are unable to communicate with each other. +</p> + +<p> +As a rule of thumb, if your extension or app needs to load a tab in an incognito browser, use +<em>split</em> incognito behavior. If your extension or app needs to be logged +into a remote server or persist settings locally, use <em>spanning</em> +incognito behavior. </p> <h3 id="key">key</h3> @@ -607,6 +652,11 @@ The following table lists the permissions an extension can use. <a href="idle.html">chrome.idle</a> module. </td> </tr> <tr> + <td> "management" </td> + <td> Required if the extension uses the + <a href="management.html">chrome.management</a> module. </td> +</tr> +<tr> <td> "notifications" </td> <td> Allows the extension to use the proposed HTML5 <a href="http://www.chromium.org/developers/design-documents/desktop-notifications/api-specification">notification API</a> @@ -626,7 +676,16 @@ The following table lists the permissions an extension can use. <td> Provides an unlimited quota for storing HTML5 client-side data, such as databases and local storage files. Without this permission, the extension is limited to - 5 MB of local storage. </td> + 5 MB of local storage. + + <p class="note"> + <b>Note:</b> + This permission applies only to Web SQL Database and application cache + (see issue <a href="http://crbug.com/58985">58985</a>). + Also, it doesn't currently work with wildcard subdomains such as + <code>http://*.example.com</code>. + </p> + </td> </tr> </tbody></table> @@ -680,36 +739,6 @@ For more information, see <a href="autoupdate.html">Autoupdating</a>. </p> -<h3 id="incognito">incognito</h3> - -<p> -Either <em>split</em> or <em>spanning</em>, to specify how this extension will -behave if allowed to run in incognito. -</p> - -<p> -<em>spanning</em> is the default for extensions, and means that the extension -will run in a single shared process. Any events or messages from an incognito -tab will be sent to the shared process, with an <em>incognito</em> flag -indicating where it came from. -</p> - -<p> -<em>split</em> is the default for apps, and it means that all app pages in -an incognito window will run in their own incognito process. If the app or extension contains a background page, that will also run in the incognito process. -This incognito process runs along side the regular process, but has a separate -memory-only cookie store. Each process sees events and messages only from its -own context (e.g. the incognito process will only see incognito tab updates). -The processes are unable to communicate with each other. -</p> - -<p> -As a rule of thumb, if your extension or app needs to load a tab in an incognito browser, use -<em>split</em> incognito behavior. If your extension or app needs to be logged -into a remote server or persist settings locally, use <em>spanning</em> -incognito behavior. -</p> - <!-- [PENDING: Possibly: point to the gallery and make a big deal of the fact that autoupdating is free if you use the gallery.] --> </div> diff --git a/chrome/common/extensions/docs/match_patterns.html b/chrome/common/extensions/docs/match_patterns.html index 5e511b3..9a58c28 100644 --- a/chrome/common/extensions/docs/match_patterns.html +++ b/chrome/common/extensions/docs/match_patterns.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Match Patterns - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/messaging.html b/chrome/common/extensions/docs/messaging.html index 120a03c..343f987 100644 --- a/chrome/common/extensions/docs/messaging.html +++ b/chrome/common/extensions/docs/messaging.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Message Passing - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/notifications.html b/chrome/common/extensions/docs/notifications.html index fd0567a..6732897 100644 --- a/chrome/common/extensions/docs/notifications.html +++ b/chrome/common/extensions/docs/notifications.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Desktop Notifications - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/npapi.html b/chrome/common/extensions/docs/npapi.html index 5190257..c622beb 100644 --- a/chrome/common/extensions/docs/npapi.html +++ b/chrome/common/extensions/docs/npapi.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>NPAPI Plugins - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/options.html b/chrome/common/extensions/docs/options.html index 94dbfcf..9b469ff 100644 --- a/chrome/common/extensions/docs/options.html +++ b/chrome/common/extensions/docs/options.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Options - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/override.html b/chrome/common/extensions/docs/override.html index d1236c9..0701ea0 100644 --- a/chrome/common/extensions/docs/override.html +++ b/chrome/common/extensions/docs/override.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Override - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -339,7 +343,7 @@ An extension can replace any one of the following pages: <li> <b>Bookmark Manager:</b> The page that appears when the user chooses the Bookmark Manager menu item - from the Tools (wrench) menu or, on Mac, + from the wrench menu or, on Mac, the Bookmark Manager item from the Bookmarks menu. You can also get to this page by entering the URL <b>chrome://bookmarks</b>. diff --git a/chrome/common/extensions/docs/overview.html b/chrome/common/extensions/docs/overview.html index 2d9f9a1..714aa26 100644 --- a/chrome/common/extensions/docs/overview.html +++ b/chrome/common/extensions/docs/overview.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Overview - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/packaging.html b/chrome/common/extensions/docs/packaging.html index 8637ec4..6c20134 100644 --- a/chrome/common/extensions/docs/packaging.html +++ b/chrome/common/extensions/docs/packaging.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Packaging - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/pageAction.html b/chrome/common/extensions/docs/pageAction.html index 9bd9388..302279c 100644 --- a/chrome/common/extensions/docs/pageAction.html +++ b/chrome/common/extensions/docs/pageAction.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Page Actions - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/permission_warnings.html b/chrome/common/extensions/docs/permission_warnings.html index ce53446..4363047 100644 --- a/chrome/common/extensions/docs/permission_warnings.html +++ b/chrome/common/extensions/docs/permission_warnings.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Permission Warnings - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,11 @@ <div> </div> </div> +<<<<<<< HEAD + </dl> +======= </dl> +>>>>>>> All communication with the cloud print proxy service from the browser now happens in the CloudPrintProxyService class. Added code to start the service process if the cloud print proxy was enabled. Also, when detect an auto-update, we send an IPC to the service process. The service process then shuts down when the browser disconnects. </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +230,12 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> +<<<<<<< HEAD + <script> + initToggles(); + </script> +======= +>>>>>>> All communication with the cloud print proxy service from the browser now happens in the CloudPrintProxyService class. Added code to start the service process if the cloud print proxy was enabled. Also, when detect an auto-update, we send an IPC to the service process. The service process then shuts down when the browser disconnects. <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/samples.html b/chrome/common/extensions/docs/samples.html index 1593349..21ff76e 100644 --- a/chrome/common/extensions/docs/samples.html +++ b/chrome/common/extensions/docs/samples.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Samples - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -290,7 +294,7 @@ <!-- STATIC CONTENT PLACEHOLDER --> <div id="static"><link rel="stylesheet" href="css/samples.css"> -<script>var search_data = {"0262260daf0c8f7b28feff2ef23b05e7abf9d1e0":"A BROWSER ACTION WHICH CHANGES ITS ICON WHEN CLICKED. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON","ea2894c41cb8e80a4433a3e6c5772dadce9be90d":"A BROWSER ACTION WITH A POPUP THAT CHANGES THE PAGE COLOR. BROWSER_ACTION POPUP TABS CHROME.TABS.EXECUTESCRIPT","ede3c47b7757245be42ec33fd5ca63df4b490066":"A BROWSER ACTION WITH NO ICON THAT MAKES THE PAGE RED BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.EXECUTESCRIPT","fbf0aa1a09a15ff8cc4fc7de4fd176d6c663d07a":"ACCEPTLANGUAGE RETURNS ACCEPT LANGUAGES OF THE BROWSER BROWSER_ACTION POPUP CHROME.I18N.GETACCEPTLANGUAGES CHROME.I18N.GETMESSAGE","9a6e4ec46997fb92b324974afa08a3d007e2537f":"ANIMATED PAGE ACTION THIS EXTENSION ADDS AN ANIMATED BROWSER ACTION TO THE TOOLBAR. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.ONCLICKED CHROME.PAGEACTION.SETICON CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED","9747e3d6a3eab39bc7c17f11a80573c62d44c7e5":"BLANK NEW TAB PAGE CHROME_URL_OVERRIDES","903e7277139e1e6caec123d3319cab295d8d1b3a":"CHROME SOUNDS ENJOY A MORE MAGICAL AND IMMERSIVE EXPERIENCE WHEN BROWSING THE WEB USING THE POWER OF SOUND. BACKGROUND_PAGE BOOKMARKS OPTIONS_PAGE TABS CHROME.BOOKMARKS.ONCREATED CHROME.BOOKMARKS.ONMOVED CHROME.BOOKMARKS.ONREMOVED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.TABS.GET CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED","0e790e035a4a00b6f1def5ef9a7d7be1bce95ab5":"CHROMIUM BUILDBOT MONITOR DISPLAYS THE STATUS OF THE CHROMIUM BUILDBOT IN THE TOOLBAR. CLICK TO SEE MORE DETAILED STATUS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION NOTIFICATIONS OPTIONS_PAGE POPUP CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETURL","ac31228200b41a87982e386cc90d3a6eee4ad885":"CHROMIUM SEARCH ADD SUPPORT TO THE OMNIBOX TO SEARCH THE CHROMIUM SOURCE CODE. BACKGROUND_PAGE EXPERIMENTAL TABS CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED CHROME.EXPERIMENTAL.OMNIBOX.STYLEMATCH CHROME.EXPERIMENTAL.OMNIBOX.STYLENONE CHROME.EXPERIMENTAL.OMNIBOX.STYLEURL CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE","7d5d6cf195bc25480256618e360aa38c6e6fba82":"CLD DISPLAYS THE LANGUAGE OF A TAB BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.DETECTLANGUAGE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED","5d81304a17cf7ac2887484f730fbd2b01e51e166":"CONTEXT MENUS SAMPLE SHOWS SOME OF THE FEATURES OF THE CONTEXT MENUS API BACKGROUND_PAGE CONTEXTMENUS CHROME.CONTEXTMENUS.CREATE","4daa6becd0899a54776d9cf7f09613ed1a9f4d77":"COOKIE API TEST EXTENSION TESTING COOKIE API BACKGROUND_PAGE BROWSER_ACTION COOKIES TABS CHROME.BROWSERACTION.ONCLICKED CHROME.COOKIES.GET CHROME.COOKIES.GETALL CHROME.COOKIES.ONCHANGED CHROME.COOKIES.REMOVE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL","6871d09f4a96bf9d4b6cc724d00e909cee0f3902":"CROSS-DOMAIN XMLHTTPREQUEST FROM A CONTENT SCRIPT DEMONSTRATES A METHOD TO MAKE A CROSS-DOMAIN XMLHTTPREQUEST FETCH FROM A CONTENT SCRIPT. THIS EXTENSION FETCHES THE CURRENT TRENDING TOPICS FROM TWITTER AND INSERTS THEM IN AN OVERLAY AT THE TOP OF GOOGLE NEWS. VISIT HTTP://NEWS.GOOGLE.COM TO TEST THIS EXTENSION. BACKGROUND_PAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","028eb5364924344029bcbe1d527f132fc72b34e5":"EMAIL THIS PAGE (BY GOOGLE) THIS EXTENSION ADDS AN EMAIL BUTTON TO THE TOOLBAR WHICH ALLOWS YOU TO EMAIL THE PAGE LINK USING YOUR DEFAULT MAIL CLIENT OR GMAIL. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.CONNECT CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.UPDATE","763a08e9b06595d785568a8d392b95a2f3700258":"EVENT TRACKING WITH GOOGLE ANALYTICS A SAMPLE EXTENSION WHICH USES GOOGLE ANALYTICS TO TRACK USAGE. BACKGROUND_PAGE BROWSER_ACTION POPUP","4e35caa9742fb82dbd628892d23a781614f6eff6":"GOOGLE DOCUMENT LIST VIEWER DEMONSTRATES HOW TO USE OAUTH TO CONNECT THE GOOGLE DOCUMENTS LIST DATA API. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","bb57f7a0132cbeb36ad7e7bb0ab75c21704234ca":"GOOGLE MAIL CHECKER DISPLAYS THE NUMBER OF UNREAD MESSAGES IN YOUR GOOGLE MAIL INBOX. YOU CAN ALSO CLICK THE BUTTON TO OPEN YOUR INBOX. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE","1682e05ea9a1bde985123b04f6f8ac50a8a64033":"GOOGLE WAVE NOTIFIER FIND OUT WHEN YOU HAVE NEW WAVES AND PREVIEW THEM FAST. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","14b9651fda4e57b2a5914ba73a779812201b750a":"HELLO WORLD THE FIRST EXTENSION THAT I MADE. BROWSER_ACTION POPUP","2020d72f2577f53caf8e94e3dbac0fb849ceaa4d":"IDLE - SIMPLE EXAMPLE DEMONSTRATES THE IDLE API BACKGROUND_PAGE BROWSER_ACTION IDLE CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.IDLE.ONSTATECHANGED CHROME.IDLE.QUERYSTATE","0ea1588bd07b20338fc21f725de1542a5fdf9726":"IGOOGLE NEW TAB PAGE CHROME_URL_OVERRIDES","646325c25f572a1d15edc73d057f821d847a4fbe":"IMAGEINFO GET IMAGE INFO FOR IMAGES, INCLUDING EXIF DATA BACKGROUND_PAGE CONTEXTMENUS TABS CHROME.CONTEXTMENUS.CREATE CHROME.TABS.GET CHROME.TABS.GETCURRENT CHROME.WINDOWS.CREATE CHROME.WINDOWS.UPDATE","ec97ec20ca2f095d081e39f1565fc12af09ef067":"MAPPY FINDS ADDRESSES IN THE WEB PAGE YOURE ON AND POPS UP A MAP WINDOW. BACKGROUND_PAGE PAGE_ACTION POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.SENDREQUEST","b2f5f8a790e16f091a7e4e0a39b2d0a6d32e3a6d":"MERGE WINDOWS MERGES ALL OF THE BROWSERS WINDOWS INTO THE CURRENT WINDOW BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.MOVE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT","51a83d2ba3a32e3ff1bdb624d4e18ccec4c4038e":"MESSAGE TIMER TIMES HOW LONG IT TAKES TO SEND A MESSAGE TO A CONTENT SCRIPT AND BACK. BROWSER_ACTION POPUP TABS CHROME.EXTENSION.ONCONNECT CHROME.EXTENSION.ONREQUEST CHROME.TABS.CONNECT CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.SENDREQUEST","4f6785ec4f937add6728615682dd37c9a42d9548":"MY BOOKMARKS A BROWSER ACTION WITH A POPUP DUMP OF ALL BOOKMARKS, INCLUDING SEARCH, ADD, EDIT AND DELETE. BOOKMARKS BROWSER_ACTION POPUP TABS CHROME.BOOKMARKS.CREATE CHROME.BOOKMARKS.GET CHROME.BOOKMARKS.GETTREE CHROME.BOOKMARKS.REMOVE CHROME.BOOKMARKS.UPDATE CHROME.TABS.CREATE","3aea027164cb9b732ba4a8c51cb93708891726ef":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE","597015d3bcce3da693b02314afd607bec4f55291":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE","6444e5c8ae112a6a433909c5e770669cd16e2e5f":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE","f799e26ceef2367cf836f24bcb47df4398b0df58":"NOTIFICATION DEMO SHOWS OFF DESKTOP NOTIFICATIONS, WHICH ARE TOAST WINDOWS THAT POP UP ON THE DESKTOP. BACKGROUND_PAGE NOTIFICATIONS OPTIONS_PAGE TABS CHROME.TABS.CREATE","a88ab12b0241ee3dac6e74bb04da7964fab0f57d":"OMNIBOX EXAMPLE BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED","8d0a50b57c26bb498be592e871001ffed91541b4":"PAGE ACTION BY CONTENT SHOWS A PAGE ACTION FOR HTML PAGES CONTAINING THE WORD SANDWICH BACKGROUND_PAGE PAGE_ACTION CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.PAGEACTION.SHOW","80b86ccc6e8520660fa591caa565826f0ed1b12c":"PAGE ACTION BY URL SHOWS A PAGE ACTION FOR URLS WHICH HAVE THE LETTER G IN THEM. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.SHOW CHROME.TABS.ONUPDATED","d74c3c18a1c1dd18b035149105a306f837c8823e":"PAGE BENCHMARKER CHROMIUM PAGE BENCHMARKER. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.CONNECT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETEXTENSIONTABS CHROME.EXTENSION.GETURL CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETCURRENT","e6ae17ab4ccfd7e059c8c01f25760ca5d894c7fd":"PRINT THIS PAGE ADDS A PRINT BUTTON TO THE BROWSER. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.UPDATE","beff6ecd9677dea0a7c648c5042165b48bb66f09":"PROCESS MONITOR ADDS A BROWSER ACTION THAT MONITORS RESOURCE USAGE OF ALL BROWSER PROCESSES. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.ONUPDATED","56a8d2ac24ca7bba78fd88ad57f43fc13c784497":"SAMPLE - OAUTH CONTACTS USES OAUTH TO CONNECT TO GOOGLES CONTACTS SERVICE AND DISPLAY A LIST OF YOUR CONTACTS. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","38f6e1e17756ede38b1364c7114a738ca717dcbb":"SANDWICHBAR SHOWS AN INFOBAR ON PAGES WHICH CONTAIN THE WORD SANDWICH BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.INFOBARS.SHOW CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","fc89b35755483af30b66cd72cefa34a43a3e8312":"SHOW TABS IN PROCESS ADDS A BROWSER ACTION SHOWING WHICH TABS SHARE THE CURRENT TABS PROCESS. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.GETPROCESSIDFORTAB CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.UPDATE","230463f2d5c3d4d0ca13c230e1f00f2aae0a8a64":"TAB INSPECTOR UTILITY FOR WORKING WITH THE EXTENSION TABS API BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.MOVE CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.CREATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.GETLASTFOCUSED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED CHROME.WINDOWS.REMOVE CHROME.WINDOWS.UPDATE","e1697cacebad05218798bf3e8a0f724517f0e8c3":"TEST SCREENSHOT EXTENSION DEMONSTRATE SCREENSHOT FUNCTIONALITY IN THE CHROME.TABS API. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.EXTENSION.GETVIEWS CHROME.TABS.CAPTUREVISIBLETAB CHROME.TABS.CREATE CHROME.TABS.ONUPDATED","b3de91ab04b7d7a2670ca7ee9d740eb42cead0b6":"TYPED URL HISTORY READS YOUR HISTORY, AND SHOWS THE TOP TEN PAGES YOU GO TO BY TYPING THE URL. BROWSER_ACTION HISTORY TABS CHROME.HISTORY.GETVISITS CHROME.HISTORY.SEARCH CHROME.TABS.CREATE"}</script> +<script>var search_data = {"0262260daf0c8f7b28feff2ef23b05e7abf9d1e0":"A BROWSER ACTION WHICH CHANGES ITS ICON WHEN CLICKED. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON","ea2894c41cb8e80a4433a3e6c5772dadce9be90d":"A BROWSER ACTION WITH A POPUP THAT CHANGES THE PAGE COLOR. BROWSER_ACTION POPUP TABS CHROME.TABS.EXECUTESCRIPT","ede3c47b7757245be42ec33fd5ca63df4b490066":"A BROWSER ACTION WITH NO ICON THAT MAKES THE PAGE RED BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.EXECUTESCRIPT","fbf0aa1a09a15ff8cc4fc7de4fd176d6c663d07a":"ACCEPTLANGUAGE RETURNS ACCEPT LANGUAGES OF THE BROWSER BROWSER_ACTION POPUP CHROME.I18N.GETACCEPTLANGUAGES CHROME.I18N.GETMESSAGE","9a6e4ec46997fb92b324974afa08a3d007e2537f":"ANIMATED PAGE ACTION THIS EXTENSION ADDS AN ANIMATED BROWSER ACTION TO THE TOOLBAR. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.ONCLICKED CHROME.PAGEACTION.SETICON CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED","9747e3d6a3eab39bc7c17f11a80573c62d44c7e5":"BLANK NEW TAB PAGE CHROME_URL_OVERRIDES","903e7277139e1e6caec123d3319cab295d8d1b3a":"CHROME SOUNDS ENJOY A MORE MAGICAL AND IMMERSIVE EXPERIENCE WHEN BROWSING THE WEB USING THE POWER OF SOUND. BACKGROUND_PAGE BOOKMARKS OPTIONS_PAGE TABS CHROME.BOOKMARKS.ONCREATED CHROME.BOOKMARKS.ONMOVED CHROME.BOOKMARKS.ONREMOVED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.TABS.GET CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED","0e790e035a4a00b6f1def5ef9a7d7be1bce95ab5":"CHROMIUM BUILDBOT MONITOR DISPLAYS THE STATUS OF THE CHROMIUM BUILDBOT IN THE TOOLBAR. CLICK TO SEE MORE DETAILED STATUS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION NOTIFICATIONS OPTIONS_PAGE POPUP CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETURL","ac31228200b41a87982e386cc90d3a6eee4ad885":"CHROMIUM SEARCH ADD SUPPORT TO THE OMNIBOX TO SEARCH THE CHROMIUM SOURCE CODE. BACKGROUND_PAGE EXPERIMENTAL TABS CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED CHROME.EXPERIMENTAL.OMNIBOX.STYLEMATCH CHROME.EXPERIMENTAL.OMNIBOX.STYLENONE CHROME.EXPERIMENTAL.OMNIBOX.STYLEURL CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE","7d5d6cf195bc25480256618e360aa38c6e6fba82":"CLD DISPLAYS THE LANGUAGE OF A TAB BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.DETECTLANGUAGE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED","5d81304a17cf7ac2887484f730fbd2b01e51e166":"CONTEXT MENUS SAMPLE SHOWS SOME OF THE FEATURES OF THE CONTEXT MENUS API BACKGROUND_PAGE CONTEXTMENUS CHROME.CONTEXTMENUS.CREATE","4daa6becd0899a54776d9cf7f09613ed1a9f4d77":"COOKIE API TEST EXTENSION TESTING COOKIE API BACKGROUND_PAGE BROWSER_ACTION COOKIES TABS CHROME.BROWSERACTION.ONCLICKED CHROME.COOKIES.GET CHROME.COOKIES.GETALL CHROME.COOKIES.ONCHANGED CHROME.COOKIES.REMOVE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL","6871d09f4a96bf9d4b6cc724d00e909cee0f3902":"CROSS-DOMAIN XMLHTTPREQUEST FROM A CONTENT SCRIPT DEMONSTRATES A METHOD TO MAKE A CROSS-DOMAIN XMLHTTPREQUEST FETCH FROM A CONTENT SCRIPT. THIS EXTENSION FETCHES THE CURRENT TRENDING TOPICS FROM TWITTER AND INSERTS THEM IN AN OVERLAY AT THE TOP OF GOOGLE NEWS. VISIT HTTP://NEWS.GOOGLE.COM TO TEST THIS EXTENSION. BACKGROUND_PAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","028eb5364924344029bcbe1d527f132fc72b34e5":"EMAIL THIS PAGE (BY GOOGLE) THIS EXTENSION ADDS AN EMAIL BUTTON TO THE TOOLBAR WHICH ALLOWS YOU TO EMAIL THE PAGE LINK USING YOUR DEFAULT MAIL CLIENT OR GMAIL. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.CONNECT CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.UPDATE","763a08e9b06595d785568a8d392b95a2f3700258":"EVENT TRACKING WITH GOOGLE ANALYTICS A SAMPLE EXTENSION WHICH USES GOOGLE ANALYTICS TO TRACK USAGE. BACKGROUND_PAGE BROWSER_ACTION POPUP","e3df888a89e35bdeb9c8bc8d03be5e1851b97c68":"EXTENSION DOCS SEARCH SEARCH THE CHROME EXTENSIONS DOCUMENTATION. TO USE, TYPE CRDOC PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE EXPERIMENTAL TABS CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED CHROME.EXPERIMENTAL.OMNIBOX.STYLEMATCH CHROME.EXPERIMENTAL.OMNIBOX.STYLENONE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.ONREMOVED CHROME.TABS.UPDATE","4e35caa9742fb82dbd628892d23a781614f6eff6":"GOOGLE DOCUMENT LIST VIEWER DEMONSTRATES HOW TO USE OAUTH TO CONNECT THE GOOGLE DOCUMENTS LIST DATA API. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","bb57f7a0132cbeb36ad7e7bb0ab75c21704234ca":"GOOGLE MAIL CHECKER DISPLAYS THE NUMBER OF UNREAD MESSAGES IN YOUR GOOGLE MAIL INBOX. YOU CAN ALSO CLICK THE BUTTON TO OPEN YOUR INBOX. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE","1682e05ea9a1bde985123b04f6f8ac50a8a64033":"GOOGLE WAVE NOTIFIER FIND OUT WHEN YOU HAVE NEW WAVES AND PREVIEW THEM FAST. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","14b9651fda4e57b2a5914ba73a779812201b750a":"HELLO WORLD THE FIRST EXTENSION THAT I MADE. BROWSER_ACTION POPUP","2020d72f2577f53caf8e94e3dbac0fb849ceaa4d":"IDLE - SIMPLE EXAMPLE DEMONSTRATES THE IDLE API BACKGROUND_PAGE BROWSER_ACTION IDLE CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.IDLE.ONSTATECHANGED CHROME.IDLE.QUERYSTATE","0ea1588bd07b20338fc21f725de1542a5fdf9726":"IGOOGLE NEW TAB PAGE CHROME_URL_OVERRIDES","646325c25f572a1d15edc73d057f821d847a4fbe":"IMAGEINFO GET IMAGE INFO FOR IMAGES, INCLUDING EXIF DATA BACKGROUND_PAGE CONTEXTMENUS TABS CHROME.CONTEXTMENUS.CREATE CHROME.TABS.GET CHROME.TABS.GETCURRENT CHROME.WINDOWS.CREATE CHROME.WINDOWS.UPDATE","ec97ec20ca2f095d081e39f1565fc12af09ef067":"MAPPY FINDS ADDRESSES IN THE WEB PAGE YOURE ON AND POPS UP A MAP WINDOW. BACKGROUND_PAGE PAGE_ACTION POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.SENDREQUEST","b2f5f8a790e16f091a7e4e0a39b2d0a6d32e3a6d":"MERGE WINDOWS MERGES ALL OF THE BROWSERS WINDOWS INTO THE CURRENT WINDOW BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.MOVE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT","51a83d2ba3a32e3ff1bdb624d4e18ccec4c4038e":"MESSAGE TIMER TIMES HOW LONG IT TAKES TO SEND A MESSAGE TO A CONTENT SCRIPT AND BACK. BROWSER_ACTION POPUP TABS CHROME.EXTENSION.ONCONNECT CHROME.EXTENSION.ONREQUEST CHROME.TABS.CONNECT CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.SENDREQUEST","4f6785ec4f937add6728615682dd37c9a42d9548":"MY BOOKMARKS A BROWSER ACTION WITH A POPUP DUMP OF ALL BOOKMARKS, INCLUDING SEARCH, ADD, EDIT AND DELETE. BOOKMARKS BROWSER_ACTION POPUP TABS CHROME.BOOKMARKS.CREATE CHROME.BOOKMARKS.GET CHROME.BOOKMARKS.GETTREE CHROME.BOOKMARKS.REMOVE CHROME.BOOKMARKS.UPDATE CHROME.TABS.CREATE","3aea027164cb9b732ba4a8c51cb93708891726ef":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE","597015d3bcce3da693b02314afd607bec4f55291":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE","6444e5c8ae112a6a433909c5e770669cd16e2e5f":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE","f799e26ceef2367cf836f24bcb47df4398b0df58":"NOTIFICATION DEMO SHOWS OFF DESKTOP NOTIFICATIONS, WHICH ARE TOAST WINDOWS THAT POP UP ON THE DESKTOP. BACKGROUND_PAGE NOTIFICATIONS OPTIONS_PAGE TABS CHROME.TABS.CREATE","e787b322bddbc6289bb31b7d7550b1bf6456a80b":"OMNIBOX EXAMPLE TO USE, TYPE OMNIX PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED","8d0a50b57c26bb498be592e871001ffed91541b4":"PAGE ACTION BY CONTENT SHOWS A PAGE ACTION FOR HTML PAGES CONTAINING THE WORD SANDWICH BACKGROUND_PAGE PAGE_ACTION CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.PAGEACTION.SHOW","80b86ccc6e8520660fa591caa565826f0ed1b12c":"PAGE ACTION BY URL SHOWS A PAGE ACTION FOR URLS WHICH HAVE THE LETTER G IN THEM. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.SHOW CHROME.TABS.ONUPDATED","d74c3c18a1c1dd18b035149105a306f837c8823e":"PAGE BENCHMARKER CHROMIUM PAGE BENCHMARKER. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.CONNECT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETEXTENSIONTABS CHROME.EXTENSION.GETURL CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETCURRENT","e6ae17ab4ccfd7e059c8c01f25760ca5d894c7fd":"PRINT THIS PAGE ADDS A PRINT BUTTON TO THE BROWSER. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.UPDATE","beff6ecd9677dea0a7c648c5042165b48bb66f09":"PROCESS MONITOR ADDS A BROWSER ACTION THAT MONITORS RESOURCE USAGE OF ALL BROWSER PROCESSES. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.ONUPDATED","56a8d2ac24ca7bba78fd88ad57f43fc13c784497":"SAMPLE - OAUTH CONTACTS USES OAUTH TO CONNECT TO GOOGLES CONTACTS SERVICE AND DISPLAY A LIST OF YOUR CONTACTS. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","38f6e1e17756ede38b1364c7114a738ca717dcbb":"SANDWICHBAR SHOWS AN INFOBAR ON PAGES WHICH CONTAIN THE WORD SANDWICH BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.INFOBARS.SHOW CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","fc89b35755483af30b66cd72cefa34a43a3e8312":"SHOW TABS IN PROCESS ADDS A BROWSER ACTION SHOWING WHICH TABS SHARE THE CURRENT TABS PROCESS. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.GETPROCESSIDFORTAB CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.UPDATE","230463f2d5c3d4d0ca13c230e1f00f2aae0a8a64":"TAB INSPECTOR UTILITY FOR WORKING WITH THE EXTENSION TABS API BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.MOVE CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.CREATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.GETLASTFOCUSED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED CHROME.WINDOWS.REMOVE CHROME.WINDOWS.UPDATE","e1697cacebad05218798bf3e8a0f724517f0e8c3":"TEST SCREENSHOT EXTENSION DEMONSTRATE SCREENSHOT FUNCTIONALITY IN THE CHROME.TABS API. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.EXTENSION.GETVIEWS CHROME.TABS.CAPTUREVISIBLETAB CHROME.TABS.CREATE CHROME.TABS.ONUPDATED","b3de91ab04b7d7a2670ca7ee9d740eb42cead0b6":"TYPED URL HISTORY READS YOUR HISTORY, AND SHOWS THE TOP TEN PAGES YOU GO TO BY TYPING THE URL. BROWSER_ACTION HISTORY TABS CHROME.HISTORY.GETVISITS CHROME.HISTORY.SEARCH CHROME.TABS.CREATE"}</script> <script src="js/sample_search.js"></script> @@ -1019,6 +1023,56 @@ </ul> </div> <div><a href="examples/tutorials/analytics.zip">Download .zip</a></div> +</div><div class="sample" id="e3df888a89e35bdeb9c8bc8d03be5e1851b97c68"> + <img class="icon" src="examples/api/omnibox/extension-docs/icon-128.png"> + <img class="icon" src="images/sample-default-icon.png" style="display: none; "> + <h2 class="name"> + <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/">Extension Docs Search</a> + </h2> + <p class="metadata features">Uses + <span> + <strong>background_page</strong><span>, </span> + <span style="display: none; "> and</span> + </span><span> + <strong>experimental</strong><span style="display: none; ">, </span> + <span> and</span> + </span><span> + <strong>tabs</strong><span style="display: none; ">, </span> + <span style="display: none; "> and</span> + </span> + </p> + <p>Search the Chrome Extensions documentation. To use, type 'crdoc' plus a search term into the Omnibox.</p> + <div class="apicalls"><strong>Calls:</strong> + <ul> + <li> + <code><a href="experimental.omnibox.html#event-onInputChanged">chrome.experimental.omnibox.onInputChanged</a></code> + </li><li> + <code><a href="experimental.omnibox.html#event-onInputEntered">chrome.experimental.omnibox.onInputEntered</a></code> + </li><li> + <code><a href="experimental.omnibox.html#method-styleMatch">chrome.experimental.omnibox.styleMatch</a></code> + </li><li> + <code><a href="experimental.omnibox.html#method-styleNone">chrome.experimental.omnibox.styleNone</a></code> + </li><li> + <code><a href="tabs.html#method-create">chrome.tabs.create</a></code> + </li><li> + <code><a href="tabs.html#method-get">chrome.tabs.get</a></code> + </li><li> + <code><a href="tabs.html#event-onRemoved">chrome.tabs.onRemoved</a></code> + </li><li> + <code><a href="tabs.html#method-update">chrome.tabs.update</a></code> + </li> + </ul> + </div> + <div class="sourcefiles"><strong>Source files:</strong> + <ul> + <li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/background.html?content-type=text/plain">background.html</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/manifest.json?content-type=text/plain">manifest.json</a></code> + </li> + </ul> + </div> + <div><a href="examples/api/omnibox/extension-docs.zip">Download .zip</a></div> </div><div class="sample" id="4e35caa9742fb82dbd628892d23a781614f6eff6"> <img class="icon" src="examples/extensions/gdocs/img/docs_spreadsheets-128.gif"> <img class="icon" src="images/sample-default-icon.png" style="display: none; "> @@ -1822,11 +1876,11 @@ </ul> </div> <div><a href="examples/api/notifications.zip">Download .zip</a></div> -</div><div class="sample" id="a88ab12b0241ee3dac6e74bb04da7964fab0f57d"> +</div><div class="sample" id="e787b322bddbc6289bb31b7d7550b1bf6456a80b"> <img class="icon" style="display: none; "> <img class="icon" src="images/sample-default-icon.png"> <h2 class="name"> - <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/">Omnibox Example</a> + <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/simple-example/">Omnibox Example</a> </h2> <p class="metadata features">Uses <span> @@ -1837,7 +1891,7 @@ <span style="display: none; "> and</span> </span> </p> - <p></p> + <p>To use, type 'omnix' plus a search term into the Omnibox.</p> <div class="apicalls"><strong>Calls:</strong> <ul> <li> @@ -1850,13 +1904,13 @@ <div class="sourcefiles"><strong>Source files:</strong> <ul> <li> - <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/background.html?content-type=text/plain">background.html</a></code> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/simple-example/background.html?content-type=text/plain">background.html</a></code> </li><li> - <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/manifest.json?content-type=text/plain">manifest.json</a></code> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/simple-example/manifest.json?content-type=text/plain">manifest.json</a></code> </li> </ul> </div> - <div><a href="examples/api/omnibox.zip">Download .zip</a></div> + <div><a href="examples/api/omnibox/simple-example.zip">Download .zip</a></div> </div><div class="sample" id="8d0a50b57c26bb498be592e871001ffed91541b4"> <img class="icon" src="examples/api/pageAction/pageaction_by_content/sandwich-128.png"> <img class="icon" src="images/sample-default-icon.png" style="display: none; "> @@ -2002,6 +2056,22 @@ <li> <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/background.html?content-type=text/plain">background.html</a></code> </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-1.4.2.min.js?content-type=text/plain">jquery/jquery-1.4.2.min.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-ui-1.8.4.custom.min.js?content-type=text/plain">jquery/jquery-ui-1.8.4.custom.min.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.client.js?content-type=text/plain">jquery/jquery.client.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.dashes.js?content-type=text/plain">jquery/jquery.flot.dashes.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.js?content-type=text/plain">jquery/jquery.flot.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.min.js?content-type=text/plain">jquery/jquery.flot.min.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.navigate.js?content-type=text/plain">jquery/jquery.flot.navigate.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.valuelabels.js?content-type=text/plain">jquery/jquery.flot.valuelabels.js</a></code> + </li><li> <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jst/jsevalcontext.js?content-type=text/plain">jst/jsevalcontext.js</a></code> </li><li> <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jst/jstemplate.js?content-type=text/plain">jst/jstemplate.js</a></code> @@ -2015,6 +2085,10 @@ <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/options.html?content-type=text/plain">options.html</a></code> </li><li> <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/script.js?content-type=text/plain">script.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/util/sorttable.js?content-type=text/plain">util/sorttable.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/util/table2CSV.js?content-type=text/plain">util/table2CSV.js</a></code> </li> </ul> </div> diff --git a/chrome/common/extensions/docs/samples.json b/chrome/common/extensions/docs/samples.json index 30a3b02..bc67080 100644 --- a/chrome/common/extensions/docs/samples.json +++ b/chrome/common/extensions/docs/samples.json @@ -51,6 +51,7 @@ "chrome.experimental.clipboard.executeCopy": "experimental.clipboard.html#method-executeCopy", "chrome.bookmarks.move": "bookmarks.html#method-move", "chrome.experimental.webRequest.onBeforeRedirect": "experimental.webRequest.html#event-onBeforeRedirect", + "chrome.management.get": "management.html#method-get", "chrome.tabs.getCurrent": "tabs.html#method-getCurrent", "chrome.experimental.sidebar.getState": "experimental.sidebar.html#method-getState", "chrome.management.launchApp": "management.html#method-launchApp", @@ -567,6 +568,37 @@ { "features": [ "background_page", + "experimental", + "tabs" + ], + "icon": "icon-128.png", + "description": "Search the Chrome Extensions documentation. To use, type 'crdoc' plus a search term into the Omnibox.", + "search_string": "EXTENSION DOCS SEARCH SEARCH THE CHROME EXTENSIONS DOCUMENTATION. TO USE, TYPE CRDOC PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE EXPERIMENTAL TABS CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED CHROME.EXPERIMENTAL.OMNIBOX.STYLEMATCH CHROME.EXPERIMENTAL.OMNIBOX.STYLENONE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.ONREMOVED CHROME.TABS.UPDATE", + "source_files": [ + "background.html", + "manifest.json" + ], + "zip_path": "examples/api/omnibox/extension-docs.zip", + "path": "examples/api/omnibox/extension-docs/", + "api_calls": [ + "chrome.experimental.omnibox.onInputChanged", + "chrome.experimental.omnibox.onInputEntered", + "chrome.experimental.omnibox.styleMatch", + "chrome.experimental.omnibox.styleNone", + "chrome.tabs.create", + "chrome.tabs.get", + "chrome.tabs.onRemoved", + "chrome.tabs.update" + ], + "id": "e3df888a89e35bdeb9c8bc8d03be5e1851b97c68", + "protocols": [ + "http://" + ], + "name": "Extension Docs Search" + }, + { + "features": [ + "background_page", "browser_action", "options_page", "popup", @@ -1050,19 +1082,19 @@ "experimental" ], "icon": null, - "description": "", - "search_string": "OMNIBOX EXAMPLE BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED", + "description": "To use, type 'omnix' plus a search term into the Omnibox.", + "search_string": "OMNIBOX EXAMPLE TO USE, TYPE OMNIX PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED", "source_files": [ "background.html", "manifest.json" ], - "zip_path": "examples/api/omnibox.zip", - "path": "examples/api/omnibox/", + "zip_path": "examples/api/omnibox/simple-example.zip", + "path": "examples/api/omnibox/simple-example/", "api_calls": [ "chrome.experimental.omnibox.onInputChanged", "chrome.experimental.omnibox.onInputEntered" ], - "id": "a88ab12b0241ee3dac6e74bb04da7964fab0f57d", + "id": "e787b322bddbc6289bb31b7d7550b1bf6456a80b", "protocols": [], "name": "Omnibox Example" }, @@ -1125,13 +1157,23 @@ "search_string": "PAGE BENCHMARKER CHROMIUM PAGE BENCHMARKER. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.CONNECT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETEXTENSIONTABS CHROME.EXTENSION.GETURL CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETCURRENT", "source_files": [ "background.html", + "jquery/jquery-1.4.2.min.js", + "jquery/jquery-ui-1.8.4.custom.min.js", + "jquery/jquery.client.js", + "jquery/jquery.flot.dashes.js", + "jquery/jquery.flot.js", + "jquery/jquery.flot.min.js", + "jquery/jquery.flot.navigate.js", + "jquery/jquery.flot.valuelabels.js", "jst/jsevalcontext.js", "jst/jstemplate.js", "jst/jstemplate_test.js", "jst/util.js", "manifest.json", "options.html", - "script.js" + "script.js", + "util/sorttable.js", + "util/table2CSV.js" ], "zip_path": "examples/extensions/benchmark.zip", "path": "examples/extensions/benchmark/", @@ -1409,4 +1451,4 @@ "name": "Typed URL History" } ] -}
\ No newline at end of file +} diff --git a/chrome/common/extensions/docs/static/api_other.html b/chrome/common/extensions/docs/static/api_other.html index 95bf464..e9ba86f 100644 --- a/chrome/common/extensions/docs/static/api_other.html +++ b/chrome/common/extensions/docs/static/api_other.html @@ -36,7 +36,7 @@ E.g. window.open(someUrl). --></dd> <ul> <li> audio (<a href="http://www.html5rocks.com/tutorials/audio/quick/">tutorial</a>) </li> - <li> app cache + <li> application cache (<a href="http://www.html5rocks.com/tutorials/appcache/beginner/">tutorial</a>) </li> <li> canvas </li> <li> geolocation diff --git a/chrome/common/extensions/docs/static/browserAction.html b/chrome/common/extensions/docs/static/browserAction.html index 224c247..2a35b5d 100644 --- a/chrome/common/extensions/docs/static/browserAction.html +++ b/chrome/common/extensions/docs/static/browserAction.html @@ -148,7 +148,7 @@ in the <a href="#manifest">manifest</a>, or call the Browser action icons should seem a little bigger and heavier than page action icons. <li><b>Don't</b> attempt to mimic - Google Chrome's monochrome "wrench" and "page" icons. + Google Chrome's monochrome "wrench" icon. That doesn't work well with themes, and anyway, extensions should stand out a little. <li><b>Do</b> use alpha transparency diff --git a/chrome/common/extensions/docs/static/content_scripts.html b/chrome/common/extensions/docs/static/content_scripts.html index c11a247..9cbf0c3 100644 --- a/chrome/common/extensions/docs/static/content_scripts.html +++ b/chrome/common/extensions/docs/static/content_scripts.html @@ -173,7 +173,7 @@ can have the following properties:</p> </tr> </table> -<h3 id="include-exclude-globs">Include and exclude globs</h2> +<h3 id="include-exclude-globs">Include and exclude globs</h3> <p> The content script will be injected into a page if its URL matches any <code>matches</code> pattern and any <code>include_globs</code> pattern, as long as the URL doesn't also match an <code>exclude_globs</code> pattern. Because the <code>matches</code> property is required, <code>include_globs</code> and <code>exclude_globs</code> can only be used to limit which pages will be affected. diff --git a/chrome/common/extensions/docs/static/crx.html b/chrome/common/extensions/docs/static/crx.html index 69f5a3d..422148b 100644 --- a/chrome/common/extensions/docs/static/crx.html +++ b/chrome/common/extensions/docs/static/crx.html @@ -6,7 +6,7 @@ CRX files are ZIP files with a special header and the <code>.crx</code> file extension. </p> -<h2>Package header</h3> +<h2>Package header</h2> <p> The header contains the author's public key and the extension's signature. @@ -63,7 +63,7 @@ the <code>.crx</code> header in order: </tr> </table> -<h2>Extension contents</h3> +<h2>Extension contents</h2> <p> The extension's ZIP file is appended to the <code>*.crx</code> package after the @@ -71,7 +71,7 @@ header. This should be the same ZIP file that the signature in the header was generated from. </p> -<h2>Example</h3> +<h2>Example</h2> <p> The following is an example hex dump from the beginning of a <code>.crx</code> diff --git a/chrome/common/extensions/docs/static/faq.html b/chrome/common/extensions/docs/static/faq.html index 0c94d9d..e9c6908 100644 --- a/chrome/common/extensions/docs/static/faq.html +++ b/chrome/common/extensions/docs/static/faq.html @@ -85,12 +85,22 @@ try the <p> As long as you are using a version of Google Chrome that supports extensions, you already have everything you need to start writing an - extension of your own. Select <b>Extensions</b> from the Tools menu - <img style="vertical-align: middle; margin:0; padding:0" src="images/toolsmenu.gif" /> (or from - the Window menu on Mac) and click <b>Developer Mode</b>. From there, you can load - an unpacked directory of files as if it were a packaged extension, reload - extensions, and more. For a complete tutorial, please view - <a href="http://code.google.com/chrome/extensions/getstarted.html">this getting started guide</a>. + extension of your own. + You can start by turning on Developer mode. + </p> + + <p> + Click the wrench icon + <img src="images/toolsmenu.gif" height="29" width="29" alt="" + class="nomargin" /> + and select <b>Extensions</b> from the <b>Tools</b> menu + (or from the <b>Window</b> menu on Mac). + If there's a "+" next to "Developer mode", + click the "+" so it turns into a "-". + Now you can reload extensions, + load an unpacked directory of files as if it were a packaged extension, + and more. For a complete tutorial, see + <a href="http://code.google.com/chrome/extensions/getstarted.html">Getting Started</a>. </p> <h3 id="faq-dev-02">Can I make cross-domain Ajax requests in an extension?</h3> @@ -388,4 +398,5 @@ win,stable,#.#.###.#,#.#.###.#</pre> to the ticket you starred or opened. This will make it easier for others with the same request to find the correct ticket. </li> -</ol>
\ No newline at end of file +</ol> + diff --git a/chrome/common/extensions/docs/static/getstarted.html b/chrome/common/extensions/docs/static/getstarted.html index 497652c..e1b907e 100644 --- a/chrome/common/extensions/docs/static/getstarted.html +++ b/chrome/common/extensions/docs/static/getstarted.html @@ -59,15 +59,13 @@ to the toolbar of Google Chrome. </li> <li id="load-ext"> Load the extension. <ol type="a"> - <li> + <li style="margin-top:0" /> Bring up the extensions management page - by clicking the Tools menu - <img src="images/toolsmenu.gif" width="43" height="34" alt="" - align="absmiddle" style="margin:0; padding:0"> - (or the Window menu on Mac) - and choosing <b>Extensions</b>. - (If you're using version 6 or later, - choose <b>Tools > Extensions</b>.) + by clicking the wrench icon + <img src="images/toolsmenu.gif" width="29" height="29" alt="" + style="margin-top:0" /> + and choosing <b>Tools > Extensions</b>. + (On Mac, use <b>Window > Extensions</b>.) </li> <li> diff --git a/chrome/common/extensions/docs/static/i18n.html b/chrome/common/extensions/docs/static/i18n.html index 207ab3e..c56abc6 100644 --- a/chrome/common/extensions/docs/static/i18n.html +++ b/chrome/common/extensions/docs/static/i18n.html @@ -462,7 +462,7 @@ Here's how to change the locale using the UI on Google Chrome for Windows: </p> <ol> - <li> Tools menu (wrench) > <b>Options</b> </li> + <li> Wrench icon > <b>Options</b> </li> <li> Choose the <b>Under the Hood</b> tab </li> <li> Scroll down to <b>Web Content</b> </li> <li> Click <b>Change font and language settings</b> </li> diff --git a/chrome/common/extensions/docs/static/management.html b/chrome/common/extensions/docs/static/management.html index 11bdc96..f9ee486 100644 --- a/chrome/common/extensions/docs/static/management.html +++ b/chrome/common/extensions/docs/static/management.html @@ -1,6 +1,23 @@ <div id="pageData-name" class="pageData">Management</div> + <!-- BEGIN AUTHORED CONTENT --> <p id="classSummary"> The <code>chrome.management</code> module provides ways to manage the list of extensions/apps that are installed and running. It is particularly useful for extensions that <a href="override.html">override</a> the built-in New Tab page. </p> + +<h2 id="manifest">Manifest</h2> + +<p>You must declare the "management" permission +in the <a href="manifest.html">extension manifest</a> +to use the management API. +For example:</p> +<pre>{ + "name": "My extension", + ... + <b>"permissions": [ + "management" + ]</b>, + ... +}</pre> + <!-- END AUTHORED CONTENT --> diff --git a/chrome/common/extensions/docs/static/manifest.html b/chrome/common/extensions/docs/static/manifest.html index 5e1758c..bbefe61 100644 --- a/chrome/common/extensions/docs/static/manifest.html +++ b/chrome/common/extensions/docs/static/manifest.html @@ -37,13 +37,14 @@ are <b>name</b> and <b>version</b>. "<a href="background_pages.html">background_page</a>": "<em>aFile</em>.html", "<a href="override.html">chrome_url_overrides</a>": {...}, "<a href="content_scripts.html">content_scripts</a>": [...], + "<a href="#homepage_url">homepage_url</a>": "http://<em>path/to/homepage</em>", + "<a href="#incognito">incognito</a>": "spanning" <em>or</em> "split", "<a href="#key">key</a>": "<em>publicKey</em>", "<a href="#minimum_chrome_version">minimum_chrome_version</a>": "<em>versionString</em>", "<a href="options.html">options_page</a>": "<em>aFile</em>.html", "<a href="#permissions">permissions</a>": [...], "<a href="npapi.html">plugins</a>": [...], "<a href="autoupdate.html">update_url</a>": "http://<em>path/to/updateInfo</em>.xml" - "<a href="#incognito">incognito</a>": "<em>split</em> or <em>spanning</em>", } </pre> @@ -57,6 +58,19 @@ with links to where they're described in detail, see the <a href="#overview">Field summary</a>. </p> +<h3 id="default_locale">default_locale</h3> + +<p> +Specifies the subdirectory of <code>_locales</code> +that contains the default strings for this extension. +This field is <b>required</b> in extensions +that have a <code>_locales</code> directory; +it <b>must be absent</b> in extensions +that have no <code>_locales</code> directory. +For details, see +<a href="i18n.html">Internationalization</a>. +</p> + <h3 id="description">description</h3> <p> @@ -71,6 +85,15 @@ You can specify locale-specific strings for this field; see <a href="i18n.html">Internationalization</a> for details. </p> +<h3 id="homepage_url">homepage_url</h3> + +<p> +The URL of the homepage for this extension. The extensions management page (chrome://extensions) +will contain a link to this URL. This field is particularly useful if you +<a href="hosting.html">host the extension on your own site</a>. If you distribute your +extension using the <a href="http://chrome.google.com/extensions">gallery</a>, +the homepage URL defaults to the extension's own gallery page. +</p> <h3 id="icons">icons</h3> @@ -116,8 +139,6 @@ extension developers are using the documented sizes. If you use other sizes, your icon may look bad in future versions of the browser. </p> - - <p> If you submit your extension to the <a href="https://chrome.google.com/extensions">gallery</a>, @@ -129,17 +150,35 @@ see the <a href="http://www.google.com/support/chrome/bin/answer.py?answer=113909">gallery help</a>. </p> -<h3 id="default_locale">default_locale</h3> +<h3 id="incognito">incognito</h3> <p> -Specifies the subdirectory of <code>_locales</code> -that contains the default strings for this extension. -This field is <b>required</b> in extensions -that have a <code>_locales</code> directory; -it <b>must be absent</b> in extensions -that have no <code>_locales</code> directory. -For details, see -<a href="i18n.html">Internationalization</a>. +Either "spanning" or "split", to specify how this extension will +behave if allowed to run in incognito mode. +</p> + +<p> +The default for extensions is "spanning", which means that the extension +will run in a single shared process. Any events or messages from an incognito +tab will be sent to the shared process, with an <em>incognito</em> flag +indicating where it came from. +</p> + +<p> +The default for installable web apps is "split", +which means that all app pages in +an incognito window will run in their own incognito process. If the app or extension contains a background page, that will also run in the incognito process. +This incognito process runs along side the regular process, but has a separate +memory-only cookie store. Each process sees events and messages only from its +own context (for example, the incognito process will see only incognito tab updates). +The processes are unable to communicate with each other. +</p> + +<p> +As a rule of thumb, if your extension or app needs to load a tab in an incognito browser, use +<em>split</em> incognito behavior. If your extension or app needs to be logged +into a remote server or persist settings locally, use <em>spanning</em> +incognito behavior. </p> <h3 id="key">key</h3> @@ -296,6 +335,11 @@ The following table lists the permissions an extension can use. <a href="idle.html">chrome.idle</a> module. </td> </tr> <tr> + <td> "management" </td> + <td> Required if the extension uses the + <a href="management.html">chrome.management</a> module. </td> +</tr> +<tr> <td> "notifications" </td> <td> Allows the extension to use the proposed HTML5 <a href="http://www.chromium.org/developers/design-documents/desktop-notifications/api-specification">notification API</a> @@ -315,7 +359,16 @@ The following table lists the permissions an extension can use. <td> Provides an unlimited quota for storing HTML5 client-side data, such as databases and local storage files. Without this permission, the extension is limited to - 5 MB of local storage. </td> + 5 MB of local storage. + + <p class="note"> + <b>Note:</b> + This permission applies only to Web SQL Database and application cache + (see issue <a href="http://crbug.com/58985">58985</a>). + Also, it doesn't currently work with wildcard subdomains such as + <code>http://*.example.com</code>. + </p> + </td> </tr> </table> @@ -369,34 +422,4 @@ For more information, see <a href="autoupdate.html">Autoupdating</a>. </p> -<h3 id="incognito">incognito</h3> - -<p> -Either <em>split</em> or <em>spanning</em>, to specify how this extension will -behave if allowed to run in incognito. -</p> - -<p> -<em>spanning</em> is the default for extensions, and means that the extension -will run in a single shared process. Any events or messages from an incognito -tab will be sent to the shared process, with an <em>incognito</em> flag -indicating where it came from. -</p> - -<p> -<em>split</em> is the default for apps, and it means that all app pages in -an incognito window will run in their own incognito process. If the app or extension contains a background page, that will also run in the incognito process. -This incognito process runs along side the regular process, but has a separate -memory-only cookie store. Each process sees events and messages only from its -own context (e.g. the incognito process will only see incognito tab updates). -The processes are unable to communicate with each other. -</p> - -<p> -As a rule of thumb, if your extension or app needs to load a tab in an incognito browser, use -<em>split</em> incognito behavior. If your extension or app needs to be logged -into a remote server or persist settings locally, use <em>spanning</em> -incognito behavior. -</p> - <!-- [PENDING: Possibly: point to the gallery and make a big deal of the fact that autoupdating is free if you use the gallery.] --> diff --git a/chrome/common/extensions/docs/static/override.html b/chrome/common/extensions/docs/static/override.html index a04b5de..6a4848d 100644 --- a/chrome/common/extensions/docs/static/override.html +++ b/chrome/common/extensions/docs/static/override.html @@ -34,7 +34,7 @@ An extension can replace any one of the following pages: <li> <b>Bookmark Manager:</b> The page that appears when the user chooses the Bookmark Manager menu item - from the Tools (wrench) menu or, on Mac, + from the wrench menu or, on Mac, the Bookmark Manager item from the Bookmarks menu. You can also get to this page by entering the URL <b>chrome://bookmarks</b>. diff --git a/chrome/common/extensions/docs/tabs.html b/chrome/common/extensions/docs/tabs.html index 4660e79..9aa03b4 100644 --- a/chrome/common/extensions/docs/tabs.html +++ b/chrome/common/extensions/docs/tabs.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tabs - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -1039,7 +1043,7 @@ For other examples and for help in viewing the source code, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>A port that can be used to communicate with the content scripts running in the specified tab.</dd> + <dd>A port that can be used to communicate with the content scripts running in the specified tab. The port's <a href="extension.html#type-Port">onDisconnect</a> event is fired if the tab closes or does not exist. </dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1398,6 +1402,64 @@ For other examples and for help in viewing the source code, see </dd> </div> + </div><div> + <div> + <dt> + <var>pinned</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Whether the tab should be pinned. Defaults to <var>false</var></dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> </div> </dl> </dd> @@ -4342,7 +4404,7 @@ For other examples and for help in viewing the source code, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The JSON response object sent by the handler of the request.</dd> + <dd>The JSON response object sent by the handler of the request. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and <a href="extension.html#property-lastError">chrome.extension.lastError</a> will be set to the error message.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -4370,7 +4432,7 @@ For other examples and for help in viewing the source code, see </div> </div> - </dl> + </dl> </div> </dd> @@ -4434,7 +4496,7 @@ For other examples and for help in viewing the source code, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The JSON response object sent by the handler of the request.</dd> + <dd>The JSON response object sent by the handler of the request. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and <a href="extension.html#property-lastError">chrome.extension.lastError</a> will be set to the error message.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -4619,7 +4681,7 @@ For other examples and for help in viewing the source code, see <span style="display: none; "> array of <span><span></span></span> </span> - <span>string</span> + <span>undefined</span> <span style="display: none; "></span> </span> </span> @@ -4628,12 +4690,10 @@ For other examples and for help in viewing the source code, see </em> </dt> - <dd class="todo"> + <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd style="display: none; "> - Description of this parameter from the json schema. - </dd> + <dd>A URL to navigate the tab to.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -4688,12 +4748,68 @@ For other examples and for help in viewing the source code, see </em> </dt> - <dd class="todo"> + <dd class="todo" style="display: none; "> Undocumented. </dd> + <dd>Whether the tab should be selected.</dd> <dd style="display: none; "> - Description of this parameter from the json schema. + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>pinned</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Whether the tab should be pinned.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -6333,7 +6449,7 @@ For other examples and for help in viewing the source code, see <!-- TYPE --> <div style="display:inline"> ( - <span class="optional" style="display: none; ">optional</span> + <span class="optional">optional</span> <span class="enum" style="display: none; ">enumerated</span> <span id="typeTemplate"> <span style="display: none; "> @@ -6413,7 +6529,65 @@ For other examples and for help in viewing the source code, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Only specified if the tab's URL changed.</dd> + <dd>The tab's URL if it has changed.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>pinned</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The tab's new pinned state.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -6808,6 +6982,64 @@ For other examples and for help in viewing the source code, see </div><div> <div> <dt> + <var>pinned</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Whether the tab is pinned.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> <var>url</var> <em> diff --git a/chrome/common/extensions/docs/template/api_template.html b/chrome/common/extensions/docs/template/api_template.html index 75f089d..dd4f074 100644 --- a/chrome/common/extensions/docs/template/api_template.html +++ b/chrome/common/extensions/docs/template/api_template.html @@ -64,7 +64,7 @@ </dd> <!-- FUNCTION PARAMETERS --> - <dd jsdisplay="isFunction($this) && $this.parameters && + <dd jsdisplay="isFunction($this) && $this.parameters && $this.name != 'callback'"> <div transclude="functionParametersTemplate"></div> </dd> @@ -78,7 +78,7 @@ <div transclude="valueTemplate"> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -212,6 +212,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/template/page_shell.html b/chrome/common/extensions/docs/template/page_shell.html index 9980469..fdca7c6 100644 --- a/chrome/common/extensions/docs/template/page_shell.html +++ b/chrome/common/extensions/docs/template/page_shell.html @@ -19,6 +19,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> </head> <body> </body> diff --git a/chrome/common/extensions/docs/themes.html b/chrome/common/extensions/docs/themes.html index 0f4912d..9c46460 100644 --- a/chrome/common/extensions/docs/themes.html +++ b/chrome/common/extensions/docs/themes.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Themes - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/tut_analytics.html b/chrome/common/extensions/docs/tut_analytics.html index 6f6dafd..4a09e0e 100644 --- a/chrome/common/extensions/docs/tut_analytics.html +++ b/chrome/common/extensions/docs/tut_analytics.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tutorial: Google Analytics - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/tut_debugging.html b/chrome/common/extensions/docs/tut_debugging.html index 9d62494..dad2b44 100644 --- a/chrome/common/extensions/docs/tut_debugging.html +++ b/chrome/common/extensions/docs/tut_debugging.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tutorial: Debugging - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/tut_oauth.html b/chrome/common/extensions/docs/tut_oauth.html index bab5072..a7b4cb5 100644 --- a/chrome/common/extensions/docs/tut_oauth.html +++ b/chrome/common/extensions/docs/tut_oauth.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tutorial: OAuth - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/tutorials.html b/chrome/common/extensions/docs/tutorials.html index c7bc92d..4d270ac 100644 --- a/chrome/common/extensions/docs/tutorials.html +++ b/chrome/common/extensions/docs/tutorials.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tutorials - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/whats_new.html b/chrome/common/extensions/docs/whats_new.html index 61192cb..86f79b9 100644 --- a/chrome/common/extensions/docs/whats_new.html +++ b/chrome/common/extensions/docs/whats_new.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>What's New in Extensions? - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/windows.html b/chrome/common/extensions/docs/windows.html index 97518ea..209fd58 100644 --- a/chrome/common/extensions/docs/windows.html +++ b/chrome/common/extensions/docs/windows.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Windows - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/xhr.html b/chrome/common/extensions/docs/xhr.html index 5c0176f..8987f40 100644 --- a/chrome/common/extensions/docs/xhr.html +++ b/chrome/common/extensions/docs/xhr.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Cross-Origin XMLHttpRequest - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index fbb262f..2bbf99b 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -64,7 +64,7 @@ const int kRSAKeySize = 1024; static void ConvertHexadecimalToIDAlphabet(std::string* id) { for (size_t i = 0; i < id->size(); ++i) { int val; - if (base::HexStringToInt(id->substr(i, 1), &val)) + if (base::HexStringToInt(id->begin() + i, id->begin() + i + 1, &val)) (*id)[i] = val + 'a'; else (*id)[i] = 'a'; @@ -249,45 +249,27 @@ const size_t Extension::kNumHostedAppPermissions = const char Extension::kOldUnlimitedStoragePermission[] = "unlimited_storage"; // -// Extension::StaticData -// - -Extension::StaticData::StaticData() - : incognito_split_mode(false), - location(INVALID), - converted_from_user_script(false), - is_theme(false), - is_app(false), - launch_container(extension_misc::LAUNCH_TAB), - launch_width(0), - launch_height(0) { -} - -Extension::StaticData::~StaticData() { -} - -// -// Extension::RuntimeData +// Extension // -Extension::RuntimeData::RuntimeData() - : background_page_ready(false), - being_upgraded(false) { -} - -Extension::RuntimeData::~RuntimeData() { +// static +scoped_refptr<Extension> Extension::Create(const FilePath& path, + Location location, + const DictionaryValue& value, + bool require_key, + std::string* error) { + scoped_refptr<Extension> extension = new Extension(path, location); + if (!extension->InitFromValue(value, require_key, error)) + return NULL; + return extension; } -// -// Extension -// - // static int Extension::GetPermissionMessageId(const std::string& permission) { return ExtensionConfig::GetSingleton()->GetPermissionMessageId(permission); } -std::vector<string16> Extension::GetPermissionMessages() { +std::vector<string16> Extension::GetPermissionMessages() const { std::vector<string16> messages; if (!plugins().empty()) { messages.push_back( @@ -305,7 +287,7 @@ std::vector<string16> Extension::GetPermissionMessages() { return messages; } -std::set<string16> Extension::GetSimplePermissionMessages() { +std::set<string16> Extension::GetSimplePermissionMessages() const { std::set<string16> messages; std::set<std::string>::const_iterator i; for (i = api_permissions().begin(); i != api_permissions().end(); ++i) { @@ -316,7 +298,7 @@ std::set<string16> Extension::GetSimplePermissionMessages() { return messages; } -std::vector<std::string> Extension::GetDistinctHosts() { +std::vector<std::string> Extension::GetDistinctHosts() const { return GetDistinctHosts(GetEffectiveHostPermissions().patterns()); } @@ -347,7 +329,7 @@ std::vector<std::string> Extension::GetDistinctHosts( return distinct_hosts; } -string16 Extension::GetHostPermissionMessage() { +string16 Extension::GetHostPermissionMessage() const { if (HasEffectiveAccessToAllHosts()) return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT2_WARNING_ALL_HOSTS); @@ -401,9 +383,6 @@ bool Extension::IsHostedAppPermission(const std::string& str) { return false; } -Extension::~Extension() { -} - const std::string Extension::VersionString() const { return version()->GetString(); } @@ -438,7 +417,7 @@ std::string Extension::GenerateIdForPath(const FilePath& path) { return id; } -Extension::HistogramType Extension::GetHistogramType() { +Extension::HistogramType Extension::GetHistogramType() const { if (is_theme()) return TYPE_THEME; if (converted_from_user_script()) @@ -534,12 +513,11 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, return false; } - URLPattern pattern; + URLPattern pattern(UserScript::kValidUserScriptSchemes); if (CanExecuteScriptEverywhere()) - pattern = URLPattern(URLPattern::SCHEME_ALL); - else - pattern = URLPattern(UserScript::kValidUserScriptSchemes); - if (!pattern.Parse(match_str)) { + pattern.set_valid_schemes(URLPattern::SCHEME_ALL); + + if (URLPattern::PARSE_SUCCESS != pattern.Parse(match_str)) { *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidMatch, base::IntToString(definition_index), base::IntToString(j)); return false; @@ -774,7 +752,7 @@ ExtensionAction* Extension::LoadExtensionActionHelper( return result.release(); } -bool Extension::ContainsNonThemeKeys(const DictionaryValue& source) { +bool Extension::ContainsNonThemeKeys(const DictionaryValue& source) const { for (DictionaryValue::key_iterator key = source.begin_keys(); key != source.end_keys(); ++key) { if (!IsBaseCrxKey(*key) && *key != keys::kTheme) @@ -786,7 +764,7 @@ bool Extension::ContainsNonThemeKeys(const DictionaryValue& source) { bool Extension::LoadIsApp(const DictionaryValue* manifest, std::string* error) { if (manifest->HasKey(keys::kApp)) - mutable_static_data_->is_app = true; + is_app_ = true; return true; } @@ -816,7 +794,12 @@ bool Extension::LoadExtent(const DictionaryValue* manifest, } URLPattern pattern(kValidWebExtentSchemes); - if (!pattern.Parse(pattern_string)) { + URLPattern::ParseResult result = pattern.Parse(pattern_string); + if (result == URLPattern::PARSE_ERROR_EMPTY_PATH) { + pattern_string += "/"; + result = pattern.Parse(pattern_string); + } + if (URLPattern::PARSE_SUCCESS != result) { *error = ExtensionErrorUtils::FormatErrorMessage(value_error, base::UintToString(i)); return false; @@ -869,7 +852,7 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest, return false; } - mutable_static_data_->launch_local_path = launch_path; + launch_local_path_ = launch_path; } else if (manifest->Get(keys::kLaunchWebURL, &temp)) { std::string launch_url; if (!temp->GetAsString(&launch_url)) { @@ -883,7 +866,7 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest, return false; } - mutable_static_data_->launch_web_url = launch_url; + launch_web_url_ = launch_url; } else if (is_app()) { *error = errors::kLaunchURLRequired; return false; @@ -899,7 +882,7 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest, } pattern.set_host(launch_url.host()); pattern.set_path("/*"); - mutable_static_data_->extent.AddPattern(pattern); + extent_.AddPattern(pattern); } // In order for the --apps-gallery-url switch to work with the gallery @@ -909,12 +892,12 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest, GURL gallery_url(CommandLine::ForCurrentProcess()-> GetSwitchValueASCII(switches::kAppsGalleryURL)); if (gallery_url.is_valid()) { - mutable_static_data_->launch_web_url = gallery_url.spec(); + launch_web_url_ = gallery_url.spec(); URLPattern pattern(URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS); pattern.Parse(gallery_url.spec()); pattern.set_path(pattern.path() + '*'); - mutable_static_data_->extent.AddPattern(pattern); + extent_.AddPattern(pattern); } } @@ -934,9 +917,9 @@ bool Extension::LoadLaunchContainer(const DictionaryValue* manifest, } if (launch_container_string == values::kLaunchContainerPanel) { - mutable_static_data_->launch_container = extension_misc::LAUNCH_PANEL; + launch_container_ = extension_misc::LAUNCH_PANEL; } else if (launch_container_string == values::kLaunchContainerTab) { - mutable_static_data_->launch_container = extension_misc::LAUNCH_TAB; + launch_container_ = extension_misc::LAUNCH_TAB; } else { *error = errors::kInvalidLaunchContainer; return false; @@ -949,9 +932,9 @@ bool Extension::LoadLaunchContainer(const DictionaryValue* manifest, *error = errors::kInvalidLaunchWidthContainer; return false; } - if (!temp->GetAsInteger(&mutable_static_data_->launch_width) || - mutable_static_data_->launch_width < 0) { - mutable_static_data_->launch_width = 0; + if (!temp->GetAsInteger(&launch_width_) || + launch_width_ < 0) { + launch_width_ = 0; *error = errors::kInvalidLaunchWidth; return false; } @@ -964,9 +947,8 @@ bool Extension::LoadLaunchContainer(const DictionaryValue* manifest, *error = errors::kInvalidLaunchHeightContainer; return false; } - if (!temp->GetAsInteger(&mutable_static_data_->launch_height) || - mutable_static_data_->launch_height < 0) { - mutable_static_data_->launch_height = 0; + if (!temp->GetAsInteger(&launch_height_) || launch_height_ < 0) { + launch_height_ = 0; *error = errors::kInvalidLaunchHeight; return false; } @@ -994,18 +976,22 @@ bool Extension::EnsureNotHybridApp(const DictionaryValue* manifest, return true; } -Extension::Extension(const FilePath& path) - : mutable_static_data_(new StaticData), - runtime_data_(new RuntimeData) { +Extension::Extension(const FilePath& path, Location location) + : incognito_split_mode_(false), + location_(location), + converted_from_user_script_(false), + is_theme_(false), + is_app_(false), + launch_container_(extension_misc::LAUNCH_TAB), + launch_width_(0), + launch_height_(0) { DCHECK(path.IsAbsolute()); - - static_data_ = mutable_static_data_; - mutable_static_data_->location = INVALID; - - mutable_static_data_->path = MaybeNormalizePath(path); + path_ = MaybeNormalizePath(path); } - -ExtensionResource Extension::GetResource(const std::string& relative_path) { +Extension::~Extension() { +} +ExtensionResource Extension::GetResource( + const std::string& relative_path) const { #if defined(OS_POSIX) FilePath relative_file_path(relative_path); #elif defined(OS_WIN) @@ -1014,7 +1000,8 @@ ExtensionResource Extension::GetResource(const std::string& relative_path) { return ExtensionResource(id(), path(), relative_file_path); } -ExtensionResource Extension::GetResource(const FilePath& relative_file_path) { +ExtensionResource Extension::GetResource( + const FilePath& relative_file_path) const { return ExtensionResource(id(), path(), relative_file_path); } @@ -1094,8 +1081,8 @@ bool Extension::FormatPEMForFileOutput(const std::string input, // extensions that require less permissions than the current version, but then // we don't silently allow them to go back. In order to fix this, we would need // to remember the max set of permissions we ever granted a single extension. -bool Extension::IsPrivilegeIncrease(Extension* old_extension, - Extension* new_extension) { +bool Extension::IsPrivilegeIncrease(const Extension* old_extension, + const Extension* new_extension) { // If the old extension had native code access, we don't need to go any // further. Things can't get any worse. if (old_extension->plugins().size() > 0) @@ -1142,7 +1129,7 @@ bool Extension::IsPrivilegeIncrease(Extension* old_extension, } // static -void Extension::DecodeIcon(Extension* extension, +void Extension::DecodeIcon(const Extension* extension, Icons icon_size, scoped_ptr<SkBitmap>* result) { FilePath icon_path = extension->GetIconResource( @@ -1195,17 +1182,13 @@ GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, std::string* error) { - // Unit tests reuse Extension objects, so we need to reset mutable_static_data - // when we re-initialize. - mutable_static_data_ = const_cast<StaticData*>(static_data_.get()); - if (source.HasKey(keys::kPublicKey)) { std::string public_key_bytes; if (!source.GetString(keys::kPublicKey, - &mutable_static_data_->public_key) || - !ParsePEMKeyBytes(mutable_static_data_->public_key, + &public_key_) || + !ParsePEMKeyBytes(public_key_, &public_key_bytes) || - !GenerateId(public_key_bytes, &mutable_static_data_->id)) { + !GenerateId(public_key_bytes, &id_)) { *error = errors::kInvalidKey; return false; } @@ -1216,19 +1199,19 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // If there is a path, we generate the ID from it. This is useful for // development mode, because it keeps the ID stable across restarts and // reloading the extension. - mutable_static_data_->id = Extension::GenerateIdForPath(path()); - if (mutable_static_data_->id.empty()) { + id_ = Extension::GenerateIdForPath(path()); + if (id_.empty()) { NOTREACHED() << "Could not create ID from path."; return false; } } // Make a copy of the manifest so we can store it in prefs. - mutable_static_data_->manifest_value.reset( + manifest_value_.reset( static_cast<DictionaryValue*>(source.DeepCopy())); // Initialize the URL. - mutable_static_data_->extension_url = + extension_url_ = Extension::GetBaseURLFromExtensionId(id()); // Initialize version. @@ -1237,10 +1220,10 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kInvalidVersion; return false; } - mutable_static_data_->version.reset( + version_.reset( Version::GetVersionFromString(version_str)); - if (!mutable_static_data_->version.get() || - mutable_static_data_->version->components().size() > 4) { + if (!version_.get() || + version_->components().size() > 4) { *error = errors::kInvalidVersion; return false; } @@ -1252,17 +1235,33 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } base::i18n::AdjustStringForLocaleDirection(localized_name, &localized_name); - mutable_static_data_->name = UTF16ToUTF8(localized_name); + name_ = UTF16ToUTF8(localized_name); // Initialize description (if present). if (source.HasKey(keys::kDescription)) { if (!source.GetString(keys::kDescription, - &mutable_static_data_->description)) { + &description_)) { *error = errors::kInvalidDescription; return false; } } + // Initialize homepage url (if present). + if (source.HasKey(keys::kHomepageURL)) { + std::string tmp; + if (!source.GetString(keys::kHomepageURL, &tmp)) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidHomepageURL, ""); + return false; + } + homepage_url_ = GURL(tmp); + if (!homepage_url_.is_valid()) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidHomepageURL, tmp); + return false; + } + } + // Initialize update url (if present). if (source.HasKey(keys::kUpdateURL)) { std::string tmp; @@ -1271,9 +1270,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, errors::kInvalidUpdateURL, ""); return false; } - mutable_static_data_->update_url = GURL(tmp); - if (!mutable_static_data_->update_url.is_valid() || - mutable_static_data_->update_url.has_ref()) { + update_url_ = GURL(tmp); + if (!update_url_.is_valid() || + update_url_.has_ref()) { *error = ExtensionErrorUtils::FormatErrorMessage( errors::kInvalidUpdateURL, tmp); return false; @@ -1321,7 +1320,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // Initialize converted_from_user_script (if present) source.GetBoolean(keys::kConvertedFromUserScript, - &mutable_static_data_->converted_from_user_script); + &converted_from_user_script_); // Initialize icons (if present). if (source.HasKey(keys::kIcons)) { @@ -1350,13 +1349,13 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } - mutable_static_data_->icons.Add(kIconSizes[i], icon_path); + icons_.Add(kIconSizes[i], icon_path); } } } // Initialize themes (if present). - mutable_static_data_->is_theme = false; + is_theme_ = false; if (source.HasKey(keys::kTheme)) { // Themes cannot contain extension keys. if (ContainsNonThemeKeys(source)) { @@ -1369,7 +1368,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kInvalidTheme; return false; } - mutable_static_data_->is_theme = true; + is_theme_ = true; DictionaryValue* images_value; if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) { @@ -1382,7 +1381,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } } - mutable_static_data_->theme_images.reset( + theme_images_.reset( static_cast<DictionaryValue*>(images_value->DeepCopy())); } @@ -1411,7 +1410,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } } - mutable_static_data_->theme_colors.reset( + theme_colors_.reset( static_cast<DictionaryValue*>(colors_value->DeepCopy())); } @@ -1432,14 +1431,14 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } } - mutable_static_data_->theme_tints.reset( + theme_tints_.reset( static_cast<DictionaryValue*>(tints_value->DeepCopy())); } DictionaryValue* display_properties_value; if (theme_value->GetDictionary(keys::kThemeDisplayProperties, &display_properties_value)) { - mutable_static_data_->theme_display_properties.reset( + theme_display_properties_.reset( static_cast<DictionaryValue*>(display_properties_value->DeepCopy())); } @@ -1487,9 +1486,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, } } - mutable_static_data_->plugins.push_back(PluginInfo()); - mutable_static_data_->plugins.back().path = path().AppendASCII(path_str); - mutable_static_data_->plugins.back().is_public = is_public; + plugins_.push_back(PluginInfo()); + plugins_.back().path = path().AppendASCII(path_str); + plugins_.back().is_public = is_public; } } @@ -1500,7 +1499,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kInvalidBackground; return false; } - mutable_static_data_->background_url = GetResourceURL(background_str); + background_url_ = GetResourceURL(background_str); } // Initialize toolstrips. This is deprecated for public use. @@ -1535,7 +1534,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, errors::kInvalidToolstrip, base::IntToString(i)); return false; } - mutable_static_data_->toolstrips.push_back(toolstrip); + toolstrips_.push_back(toolstrip); } } @@ -1559,11 +1558,11 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, if (!LoadUserScriptHelper(content_script, i, error, &script)) return false; // Failed to parse script context definition. script.set_extension_id(id()); - if (mutable_static_data_->converted_from_user_script) { + if (converted_from_user_script_) { script.set_emulate_greasemonkey(true); script.set_match_all_frames(true); // Greasemonkey matches all frames. } - mutable_static_data_->content_scripts.push_back(script); + content_scripts_.push_back(script); } } @@ -1600,9 +1599,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // If page_action_value is not NULL, then there was a valid page action. if (page_action_value) { - mutable_static_data_->page_action.reset( + page_action_.reset( LoadExtensionActionHelper(page_action_value, error)); - if (!mutable_static_data_->page_action.get()) + if (!page_action_.get()) return false; // Failed to parse page action definition. } @@ -1614,25 +1613,25 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } - mutable_static_data_->browser_action.reset( + browser_action_.reset( LoadExtensionActionHelper(browser_action_value, error)); - if (!mutable_static_data_->browser_action.get()) + if (!browser_action_.get()) return false; // Failed to parse browser action definition. } // Load App settings. - if (!LoadIsApp(mutable_static_data_->manifest_value.get(), error) || - !LoadExtent(mutable_static_data_->manifest_value.get(), keys::kWebURLs, - &mutable_static_data_->extent, + if (!LoadIsApp(manifest_value_.get(), error) || + !LoadExtent(manifest_value_.get(), keys::kWebURLs, + &extent_, errors::kInvalidWebURLs, errors::kInvalidWebURL, error) || - !EnsureNotHybridApp(mutable_static_data_->manifest_value.get(), error) || - !LoadLaunchURL(mutable_static_data_->manifest_value.get(), error) || - !LoadLaunchContainer(mutable_static_data_->manifest_value.get(), error)) { + !EnsureNotHybridApp(manifest_value_.get(), error) || + !LoadLaunchURL(manifest_value_.get(), error) || + !LoadLaunchContainer(manifest_value_.get(), error)) { return false; } // Initialize options page url (optional). - // Funtion LoadIsApp() set mutable_static_data_->is_app above. + // Funtion LoadIsApp() set is_app_ above. if (source.HasKey(keys::kOptionsPage)) { std::string options_str; if (!source.GetString(keys::kOptionsPage, &options_str)) { @@ -1648,7 +1647,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kInvalidOptionsPageInHostedApp; return false; } - mutable_static_data_->options_url = options_url; + options_url_ = options_url; } else { GURL absolute(options_str); @@ -1656,8 +1655,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kInvalidOptionsPageExpectUrlInPackage; return false; } - mutable_static_data_->options_url = GetResourceURL(options_str); - if (!mutable_static_data_->options_url.is_valid()) { + options_url_ = GetResourceURL(options_str); + if (!options_url_.is_valid()) { *error = errors::kInvalidOptionsPage; return false; } @@ -1685,7 +1684,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // TODO(asargent) - We want a more general purpose mechanism for this, // and better error messages. (http://crbug.com/54013) if (permission_str == kWebstorePrivatePermission && - mutable_static_data_->location != Extension::COMPONENT) { + location_ != Extension::COMPONENT) { continue; } @@ -1696,13 +1695,13 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, if (web_extent().is_empty() || location() == Extension::COMPONENT) { // Check if it's a module permission. If so, enable that permission. if (IsAPIPermission(permission_str)) { - mutable_static_data_->api_permissions.insert(permission_str); + api_permissions_.insert(permission_str); continue; } } else { // Hosted apps only get access to a subset of the valid permissions. if (IsHostedAppPermission(permission_str)) { - mutable_static_data_->api_permissions.insert(permission_str); + api_permissions_.insert(permission_str); continue; } } @@ -1713,7 +1712,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, (UserScript::kValidUserScriptSchemes | URLPattern::SCHEME_CHROMEUI) & ~URLPattern::SCHEME_FILE); - if (!pattern.Parse(permission_str)) { + if (URLPattern::PARSE_SUCCESS != pattern.Parse(permission_str)) { *error = ExtensionErrorUtils::FormatErrorMessage( errors::kInvalidPermission, base::IntToString(i)); return false; @@ -1729,14 +1728,14 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // match all paths. pattern.set_path("/*"); - mutable_static_data_->host_permissions.push_back(pattern); + host_permissions_.push_back(pattern); } } if (source.HasKey(keys::kDefaultLocale)) { if (!source.GetString(keys::kDefaultLocale, - &mutable_static_data_->default_locale) || - mutable_static_data_->default_locale.empty()) { + &default_locale_) || + default_locale_.empty()) { *error = errors::kInvalidDefaultLocale; return false; } @@ -1767,7 +1766,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } // Replace the entry with a fully qualified chrome-extension:// URL. - mutable_static_data_->chrome_url_overrides[page] = GetResourceURL(val); + chrome_url_overrides_[page] = GetResourceURL(val); } // An extension may override at most one page. @@ -1779,8 +1778,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, if (source.HasKey(keys::kOmniboxKeyword)) { if (!source.GetString(keys::kOmniboxKeyword, - &mutable_static_data_->omnibox_keyword) || - mutable_static_data_->omnibox_keyword.empty()) { + &omnibox_keyword_) || + omnibox_keyword_.empty()) { *error = errors::kInvalidOmniboxKeyword; return false; } @@ -1801,12 +1800,12 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kDevToolsExperimental; return false; } - mutable_static_data_->devtools_url = GetResourceURL(devtools_str); + devtools_url_ = GetResourceURL(devtools_str); } // Initialize incognito behavior. Apps default to split mode, extensions // default to spanning. - mutable_static_data_->incognito_split_mode = is_app(); + incognito_split_mode_ = is_app(); if (source.HasKey(keys::kIncognito)) { std::string value; if (!source.GetString(keys::kIncognito, &value)) { @@ -1814,9 +1813,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } if (value == values::kIncognitoSpanning) { - mutable_static_data_->incognito_split_mode = false; + incognito_split_mode_ = false; } else if (value == values::kIncognitoSplit) { - mutable_static_data_->incognito_split_mode = true; + incognito_split_mode_ = true; } else { *error = errors::kInvalidIncognitoBehavior; return false; @@ -1835,10 +1834,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // it calls InitFromValue, passing it up to the browser process which calls // InitFromValue again. As a result, we need to make sure that nobody // accidentally modifies it. - DCHECK(source.Equals(mutable_static_data_->manifest_value.get())); - - // Ensure we can't modify our static data anymore. - mutable_static_data_ = NULL; + DCHECK(source.Equals(manifest_value_.get())); return true; } @@ -1854,19 +1850,22 @@ std::string Extension::ChromeStoreLaunchURL() { return gallery_prefix; } -GURL Extension::GalleryUrl() const { - if (!update_url().DomainIs("google.com")) +GURL Extension::GetHomepageURL() const { + if (homepage_url_.is_valid()) + return homepage_url_; + + if (update_url()!= GURL(extension_urls::kGalleryUpdateHttpsUrl) && + update_url()!= GURL(extension_urls::kGalleryUpdateHttpUrl)) return GURL(); // TODO(erikkay): This may not be entirely correct with the webstore. // I think it will have a mixture of /extensions/detail and /webstore/detail // URLs. Perhaps they'll handle this nicely with redirects? GURL url(ChromeStoreLaunchURL() + std::string("/detail/") + id()); - return url; } -std::set<FilePath> Extension::GetBrowserImages() { +std::set<FilePath> Extension::GetBrowserImages() const { std::set<FilePath> image_paths; // TODO(viettrungluu): These |FilePath::FromWStringHack(UTF8ToWide())| // indicate that we're doing something wrong. @@ -1916,20 +1915,6 @@ GURL Extension::GetFullLaunchURL() const { return GURL(launch_web_url()); } -bool Extension::GetBackgroundPageReady() { - return (GetRuntimeData()->background_page_ready || - background_url().is_empty()); -} - -void Extension::SetBackgroundPageReady() { - DCHECK(!background_url().is_empty()); - GetRuntimeData()->background_page_ready = true; - NotificationService::current()->Notify( - NotificationType::EXTENSION_BACKGROUND_PAGE_READY, - Source<Extension>(this), - NotificationService::NoDetails()); -} - static std::string SizeToString(const gfx::Size& max_size) { return base::IntToString(max_size.width()) + "x" + base::IntToString(max_size.height()); @@ -1949,29 +1934,27 @@ void Extension::SetScriptingWhitelist( void Extension::SetCachedImage(const ExtensionResource& source, const SkBitmap& image, - const gfx::Size& original_size) { + const gfx::Size& original_size) const { DCHECK(source.extension_root() == path()); // The resource must come from // this extension. const FilePath& path = source.relative_path(); gfx::Size actual_size(image.width(), image.height()); if (actual_size == original_size) { - GetRuntimeData()->image_cache_[ - RuntimeData::ImageCacheKey(path, std::string())] = image; + image_cache_[ImageCacheKey(path, std::string())] = image; } else { - GetRuntimeData()->image_cache_[ - RuntimeData::ImageCacheKey(path, SizeToString(actual_size))] = image; + image_cache_[ImageCacheKey(path, SizeToString(actual_size))] = image; } } bool Extension::HasCachedImage(const ExtensionResource& source, - const gfx::Size& max_size) { + const gfx::Size& max_size) const { DCHECK(source.extension_root() == path()); // The resource must come from // this extension. return GetCachedImageImpl(source, max_size) != NULL; } SkBitmap Extension::GetCachedImage(const ExtensionResource& source, - const gfx::Size& max_size) { + const gfx::Size& max_size) const { DCHECK(source.extension_root() == path()); // The resource must come from // this extension. SkBitmap* image = GetCachedImageImpl(source, max_size); @@ -1979,20 +1962,19 @@ SkBitmap Extension::GetCachedImage(const ExtensionResource& source, } SkBitmap* Extension::GetCachedImageImpl(const ExtensionResource& source, - const gfx::Size& max_size) { + const gfx::Size& max_size) const { const FilePath& path = source.relative_path(); // Look for exact size match. - RuntimeData::ImageCache::iterator i = GetRuntimeData()->image_cache_.find( - RuntimeData::ImageCacheKey(path, SizeToString(max_size))); - if (i != GetRuntimeData()->image_cache_.end()) + ImageCache::iterator i = image_cache_.find( + ImageCacheKey(path, SizeToString(max_size))); + if (i != image_cache_.end()) return &(i->second); // If we have the original size version cached, return that if it's small // enough. - i = GetRuntimeData()->image_cache_.find( - RuntimeData::ImageCacheKey(path, std::string())); - if (i != GetRuntimeData()->image_cache_.end()) { + i = image_cache_.find(ImageCacheKey(path, std::string())); + if (i != image_cache_.end()) { SkBitmap& image = i->second; if (image.width() <= max_size.width() && image.height() <= max_size.height()) @@ -2003,14 +1985,15 @@ SkBitmap* Extension::GetCachedImageImpl(const ExtensionResource& source, } ExtensionResource Extension::GetIconResource( - int size, ExtensionIconSet::MatchType match_type) { + int size, ExtensionIconSet::MatchType match_type) const { std::string path = icons().Get(size, match_type); if (path.empty()) return ExtensionResource(); return GetResource(path); } -GURL Extension::GetIconURL(int size, ExtensionIconSet::MatchType match_type) { +GURL Extension::GetIconURL(int size, + ExtensionIconSet::MatchType match_type) const { std::string path = icons().Get(size, match_type); if (path.empty()) return GURL(); @@ -2018,7 +2001,7 @@ GURL Extension::GetIconURL(int size, ExtensionIconSet::MatchType match_type) { return GetResourceURL(path); } -bool Extension::CanSpecifyHostPermission(const URLPattern pattern) const { +bool Extension::CanSpecifyHostPermission(const URLPattern& pattern) const { if (!pattern.match_all_urls() && pattern.MatchesScheme(chrome::kChromeUIScheme)) { // Only allow access to chrome://favicon to regular extensions. Component @@ -2031,7 +2014,7 @@ bool Extension::CanSpecifyHostPermission(const URLPattern pattern) const { return true; } -// static. +// static bool Extension::HasApiPermission( const std::set<std::string>& api_permissions, const std::string& function_name) { @@ -2077,7 +2060,7 @@ bool Extension::HasHostPermission(const GURL& url) const { void Extension::InitEffectiveHostPermissions() { for (URLPatternList::const_iterator host = host_permissions().begin(); host != host_permissions().end(); ++host) - mutable_static_data_->effective_host_permissions.AddPattern(*host); + effective_host_permissions_.AddPattern(*host); for (UserScriptList::const_iterator content_script = content_scripts().begin(); @@ -2085,7 +2068,7 @@ void Extension::InitEffectiveHostPermissions() { UserScript::PatternList::const_iterator pattern = content_script->url_patterns().begin(); for (; pattern != content_script->url_patterns().end(); ++pattern) - mutable_static_data_->effective_host_permissions.AddPattern(*pattern); + effective_host_permissions_.AddPattern(*pattern); } } @@ -2171,7 +2154,7 @@ bool Extension::HasEffectiveAccessToAllHosts() const { return false; } -bool Extension::IsAPIPermission(const std::string& str) { +bool Extension::IsAPIPermission(const std::string& str) const { for (size_t i = 0; i < Extension::kNumPermissions; ++i) { if (str == Extension::kPermissions[i].name) { // Only allow the experimental API permission if the command line @@ -2210,12 +2193,6 @@ bool Extension::CanExecuteScriptEverywhere() const { return false; } -Extension::RuntimeData* Extension::GetRuntimeData() const { - // TODO(mpcomplete): it would be nice if I could verify we were on the UI - // thread, but we're in common and don't have access to BrowserThread. - return const_cast<Extension::RuntimeData*>(runtime_data_.get()); -} - ExtensionInfo::ExtensionInfo(const DictionaryValue* manifest, const std::string& id, const FilePath& path, diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index db1c0c3..ce7e628 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -30,7 +30,7 @@ class SkBitmap; class Version; // Represents a Chrome extension. -class Extension { +class Extension : public base::RefCountedThreadSafe<Extension> { public: typedef std::map<const std::string, GURL> URLOverrideMap; typedef std::vector<std::string> ScriptingWhitelist; @@ -49,8 +49,12 @@ class Extension { COMPONENT, // An integral component of Chrome itself, which // happens to be implemented as an extension. We don't // show these in the management UI. - EXTERNAL_PREF_DOWNLOAD // A crx file from an external directory (via + EXTERNAL_PREF_DOWNLOAD, // A crx file from an external directory (via // prefs), installed from an update URL. + EXTERNAL_POLICY_DOWNLOAD, // A crx file from an external directory (via + // admin policies), installed from an update URL. + + NUM_LOCATIONS }; enum State { @@ -96,181 +100,6 @@ class Extension { bool is_public; // False if only this extension can load this plugin. }; - // Contains a subset of the extension's data that doesn't change once - // initialized, and therefore shareable across threads without locking. - struct StaticData : public base::RefCountedThreadSafe<StaticData> { - StaticData(); - - // TODO(mpcomplete): RefCountedThreadSafe does not allow AddRef/Release on - // const objects. I think that is a mistake. Until we can fix that, here's - // a workaround. - void AddRef() const { - const_cast<StaticData*>(this)-> - base::RefCountedThreadSafe<StaticData>::AddRef(); - } - void Release() const { - const_cast<StaticData*>(this)-> - base::RefCountedThreadSafe<StaticData>::Release(); - } - - // A persistent, globally unique ID. An extension's ID is used in things - // like directory structures and URLs, and is expected to not change across - // versions. It is generated as a SHA-256 hash of the extension's public - // key, or as a hash of the path in the case of unpacked extensions. - std::string id; - - // The extension's human-readable name. Name is used for display purpose. It - // might be wrapped with unicode bidi control characters so that it is - // displayed correctly in RTL context. - // NOTE: Name is UTF-8 and may contain non-ascii characters. - std::string name; - - // The absolute path to the directory the extension is stored in. - FilePath path; - - // Default locale for fall back. Can be empty if extension is not localized. - std::string default_locale; - - // If true, a separate process will be used for the extension in incognito - // mode. - bool incognito_split_mode; - - // Defines the set of URLs in the extension's web content. - ExtensionExtent extent; - - // The set of host permissions that the extension effectively has access to, - // which is a merge of host_permissions_ and all of the match patterns in - // any content scripts the extension has. This is used to determine which - // URLs have the ability to load an extension's resources via embedded - // chrome-extension: URLs (see extension_protocols.cc). - ExtensionExtent effective_host_permissions; - - // The set of module-level APIs this extension can use. - std::set<std::string> api_permissions; - - // The icons for the extension. - ExtensionIconSet icons; - - // The base extension url for the extension. - GURL extension_url; - - // The location the extension was loaded from. - Location location; - - // The extension's version. - scoped_ptr<Version> version; - - // An optional longer description of the extension. - std::string description; - - // True if the extension was generated from a user script. (We show slightly - // different UI if so). - bool converted_from_user_script; - - // Paths to the content scripts the extension contains. - UserScriptList content_scripts; - - // The extension's page action, if any. - scoped_ptr<ExtensionAction> page_action; - - // The extension's browser action, if any. - scoped_ptr<ExtensionAction> browser_action; - - // Optional list of NPAPI plugins and associated properties. - std::vector<PluginInfo> plugins; - - // Optional URL to a master page of which a single instance should be always - // loaded in the background. - GURL background_url; - - // Optional URL to a page for setting options/preferences. - GURL options_url; - - // Optional URL to a devtools extension page. - GURL devtools_url; - - // Optional list of toolstrips and associated properties. - std::vector<GURL> toolstrips; - - // The public key used to sign the contents of the crx package. - std::string public_key; - - // A map of resource id's to relative file paths. - scoped_ptr<DictionaryValue> theme_images; - - // A map of color names to colors. - scoped_ptr<DictionaryValue> theme_colors; - - // A map of color names to colors. - scoped_ptr<DictionaryValue> theme_tints; - - // A map of display properties. - scoped_ptr<DictionaryValue> theme_display_properties; - - // Whether the extension is a theme. - bool is_theme; - - // The sites this extension has permission to talk to (using XHR, etc). - URLPatternList host_permissions; - - // URL for fetching an update manifest - GURL update_url; - - // A copy of the manifest that this extension was created from. - scoped_ptr<DictionaryValue> manifest_value; - - // A map of chrome:// hostnames (newtab, downloads, etc.) to Extension URLs - // which override the handling of those URLs. - URLOverrideMap chrome_url_overrides; - - // Whether this extension uses app features. - bool is_app; - - // The local path inside the extension to use with the launcher. - std::string launch_local_path; - - // A web url to use with the launcher. Note that this might be relative or - // absolute. If relative, it is relative to web_origin. - std::string launch_web_url; - - // The type of container to launch into. - extension_misc::LaunchContainer launch_container; - - // The default size of the container when launching. Only respected for - // containers like panels and windows. - int launch_width; - int launch_height; - - // The Omnibox keyword for this extension, or empty if there is none. - std::string omnibox_keyword; - - protected: - friend class base::RefCountedThreadSafe<StaticData>; - ~StaticData(); - }; - - // Contains the subset of the extension's (private) data that can be modified - // after initialization. This class should only be accessed on the UI thread. - struct RuntimeData { - // We keep a cache of images loaded from extension resources based on their - // path and a string representation of a size that may have been used to - // scale it (or the empty string if the image is at its original size). - typedef std::pair<FilePath, std::string> ImageCacheKey; - typedef std::map<ImageCacheKey, SkBitmap> ImageCache; - - RuntimeData(); - ~RuntimeData(); - - // True if the background page is ready. - bool background_page_ready; - - // True while the extension is being upgraded. - bool being_upgraded; - - // Cached images for this extension. - ImageCache image_cache_; - }; - // A permission is defined by its |name| (what is used in the manifest), // and the |message_id| that's used by install/update UI. struct Permission { @@ -278,12 +107,18 @@ class Extension { const int message_id; }; + static scoped_refptr<Extension> Create(const FilePath& path, + Location location, + const DictionaryValue& value, + bool require_key, + std::string* error); + // The install message id for |permission|. Returns 0 if none exists. static int GetPermissionMessageId(const std::string& permission); // Returns the full list of permission messages that this extension // should display at install time. - std::vector<string16> GetPermissionMessages(); + std::vector<string16> GetPermissionMessages() const; // Returns the distinct hosts that should be displayed in the install UI. This // discards some of the detail that is present in the manifest to make it as @@ -291,7 +126,7 @@ class Extension { // and path components of URLPatterns and de-dupe the result. static std::vector<std::string> GetDistinctHosts( const URLPatternList& host_patterns); - std::vector<std::string> GetDistinctHosts(); + std::vector<std::string> GetDistinctHosts() const; // Icon sizes used by the extension system. static const int kIconSizes[]; @@ -351,9 +186,6 @@ class Extension { // The mimetype used for extensions. static const char kMimeType[]; - explicit Extension(const FilePath& path); - virtual ~Extension(); - // Checks to see if the extension has a valid ID. static bool IdIsValid(const std::string& id); @@ -367,11 +199,19 @@ class Extension { static inline bool IsExternalLocation(Location location) { return location == Extension::EXTERNAL_PREF || location == Extension::EXTERNAL_REGISTRY || - location == Extension::EXTERNAL_PREF_DOWNLOAD; + location == Extension::EXTERNAL_PREF_DOWNLOAD || + location == Extension::EXTERNAL_POLICY_DOWNLOAD; + } + + // Whether extensions with |location| are auto-updatable or not. + static inline bool IsAutoUpdateableLocation(Location location) { + // Only internal and external extensions can be autoupdated. + return location == Extension::INTERNAL || + IsExternalLocation(location); } // See HistogramType definition above. - HistogramType GetHistogramType(); + HistogramType GetHistogramType() const; // Returns an absolute url to a resource inside of an extension. The // |extension_url| argument should be the url() from an Extension object. The @@ -386,10 +226,10 @@ class Extension { // Returns an extension resource object. |relative_path| should be UTF8 // encoded. - ExtensionResource GetResource(const std::string& relative_path); + ExtensionResource GetResource(const std::string& relative_path) const; // As above, but with |relative_path| following the file system's encoding. - ExtensionResource GetResource(const FilePath& relative_path); + ExtensionResource GetResource(const FilePath& relative_path) const; // |input| is expected to be the text of an rsa public or private key. It // tolerates the presence or absence of bracking header/footer like this: @@ -411,14 +251,14 @@ class Extension { // Determine whether |new_extension| has increased privileges compared to // |old_extension|. - static bool IsPrivilegeIncrease(Extension* old_extension, - Extension* new_extension); + static bool IsPrivilegeIncrease(const Extension* old_extension, + const Extension* new_extension); // Given an extension and icon size, read it if present and decode it into // result. In the browser process, this will DCHECK if not called on the // file thread. To easily load extension images on the UI thread, see // ImageLoadingTracker. - static void DecodeIcon(Extension* extension, + static void DecodeIcon(const Extension* extension, Icons icon_size, scoped_ptr<SkBitmap>* result); @@ -457,57 +297,6 @@ class Extension { // Adds an extension to the scripting whitelist. Used for testing only. static void SetScriptingWhitelist(const ScriptingWhitelist& whitelist); - // Initialize the extension from a parsed manifest. - // Usually, the id of an extension is generated by the "key" property of - // its manifest, but if |require_key| is |false|, a temporary ID will be - // generated based on the path. - bool InitFromValue(const DictionaryValue& value, bool require_key, - std::string* error); - - const StaticData* static_data() const { return static_data_; } - - const FilePath& path() const { return static_data_->path; } - const GURL& url() const { return static_data_->extension_url; } - Location location() const { return static_data_->location; } - void set_location(Location location) { - mutable_static_data_->location = location; - } - - const std::string& id() const { return static_data_->id; } - const Version* version() const { return static_data_->version.get(); } - // String representation of the version number. - const std::string VersionString() const; - const std::string& name() const { return static_data_->name; } - const std::string& public_key() const { return static_data_->public_key; } - const std::string& description() const { return static_data_->description; } - bool converted_from_user_script() const { - return static_data_->converted_from_user_script; - } - const UserScriptList& content_scripts() const { - return static_data_->content_scripts; - } - ExtensionAction* page_action() const { - return static_data_->page_action.get(); - } - ExtensionAction* browser_action() const { - return static_data_->browser_action.get(); - } - const std::vector<PluginInfo>& plugins() const { - return static_data_->plugins; - } - const GURL& background_url() const { return static_data_->background_url; } - const GURL& options_url() const { return static_data_->options_url; } - const GURL& devtools_url() const { return static_data_->devtools_url; } - const std::vector<GURL>& toolstrips() const { - return static_data_->toolstrips; - } - const std::set<std::string>& api_permissions() const { - return static_data_->api_permissions; - } - const URLPatternList& host_permissions() const { - return static_data_->host_permissions; - } - // Returns true if the extension has the specified API permission. static bool HasApiPermission(const std::set<std::string>& api_permissions, const std::string& function_name); @@ -517,14 +306,14 @@ class Extension { } const ExtensionExtent& GetEffectiveHostPermissions() const { - return static_data_->effective_host_permissions; + return effective_host_permissions_; } // Whether or not the extension is allowed permission for a URL pattern from // the manifest. http, https, and chrome://favicon/ is allowed for all // extensions, while component extensions are allowed access to // chrome://resources. - bool CanSpecifyHostPermission(const URLPattern pattern) const; + bool CanSpecifyHostPermission(const URLPattern& pattern) const; // Whether the extension has access to the given URL. bool HasHostPermission(const GURL& url) const; @@ -536,120 +325,125 @@ class Extension { // network, etc.) bool HasEffectiveAccessToAllHosts() const; - const GURL& update_url() const { return static_data_->update_url; } - - const ExtensionIconSet& icons() const { - return static_data_->icons; - } - - // Returns the Google Gallery URL for this extension, if one exists. For + // Returns the Homepage URL for this extension. If homepage_url was not + // specified in the manifest, this returns the Google Gallery URL. For // third-party extensions, this returns a blank GURL. - GURL GalleryUrl() const; - - // Theme-related. - DictionaryValue* GetThemeImages() const { - return static_data_->theme_images.get(); - } - DictionaryValue* GetThemeColors() const { - return static_data_->theme_colors.get(); - } - DictionaryValue* GetThemeTints() const { - return static_data_->theme_tints.get(); - } - DictionaryValue* GetThemeDisplayProperties() const { - return static_data_->theme_display_properties.get(); - } - bool is_theme() const { return static_data_->is_theme; } + GURL GetHomepageURL() const; // Returns a list of paths (relative to the extension dir) for images that // the browser might load (like themes and page action icons). - std::set<FilePath> GetBrowserImages(); + std::set<FilePath> GetBrowserImages() const; // Get an extension icon as a resource or URL. - ExtensionResource GetIconResource(int size, - ExtensionIconSet::MatchType match_type); - GURL GetIconURL(int size, ExtensionIconSet::MatchType match_type); - - const DictionaryValue* manifest_value() const { - return static_data_->manifest_value.get(); - } - - const std::string default_locale() const { - return static_data_->default_locale; - } - - // Chrome URL overrides (see ExtensionOverrideUI). - const URLOverrideMap& GetChromeURLOverrides() const { - return static_data_->chrome_url_overrides; - } - - const std::string omnibox_keyword() const { - return static_data_->omnibox_keyword; - } - - bool is_app() const { return static_data_->is_app; } - const ExtensionExtent& web_extent() const { - return static_data_->extent; - } - const std::string& launch_local_path() const { - return static_data_->launch_local_path; - } - const std::string& launch_web_url() const { - return static_data_->launch_web_url; - } - extension_misc::LaunchContainer launch_container() const { - return static_data_->launch_container; - } - int launch_width() const { return static_data_->launch_width; } - int launch_height() const { return static_data_->launch_height; } - bool incognito_split_mode() const { - return static_data_->incognito_split_mode; - } + ExtensionResource GetIconResource( + int size, ExtensionIconSet::MatchType match_type) const; + GURL GetIconURL(int size, ExtensionIconSet::MatchType match_type) const; // Gets the fully resolved absolute launch URL. GURL GetFullLaunchURL() const; - - // Whether the background page, if any, is ready. We don't load other - // components until then. If there is no background page, we consider it to - // be ready. - bool GetBackgroundPageReady(); - void SetBackgroundPageReady(); - - // Getter and setter for the flag that specifies whether the extension is - // being upgraded. - bool being_upgraded() const { return GetRuntimeData()->being_upgraded; } - void set_being_upgraded(bool value) { - GetRuntimeData()->being_upgraded = value; - } - // Image cache related methods. These are only valid on the UI thread and // not maintained by this class. See ImageLoadingTracker for usage. The // |original_size| parameter should be the size of the image at |source| // before any scaling may have been done to produce the pixels in |image|. void SetCachedImage(const ExtensionResource& source, const SkBitmap& image, - const gfx::Size& original_size); + const gfx::Size& original_size) const; bool HasCachedImage(const ExtensionResource& source, - const gfx::Size& max_size); + const gfx::Size& max_size) const; SkBitmap GetCachedImage(const ExtensionResource& source, - const gfx::Size& max_size); - bool is_hosted_app() const { return is_app() && !web_extent().is_empty(); } - bool is_packaged_app() const { return is_app() && web_extent().is_empty(); } - + const gfx::Size& max_size) const; // Returns true if this extension is a COMPONENT extension, or if it is // on the whitelist of extensions that can script all pages. bool CanExecuteScriptEverywhere() const; + // Accessors: + + const FilePath& path() const { return path_; } + const GURL& url() const { return extension_url_; } + Location location() const { return location_; } + const std::string& id() const { return id_; } + const Version* version() const { return version_.get(); } + const std::string VersionString() const; + const std::string& name() const { return name_; } + const std::string& public_key() const { return public_key_; } + const std::string& description() const { return description_; } + bool converted_from_user_script() const { + return converted_from_user_script_; + } + const UserScriptList& content_scripts() const { return content_scripts_; } + ExtensionAction* page_action() const { return page_action_.get(); } + ExtensionAction* browser_action() const { return browser_action_.get(); } + const std::vector<PluginInfo>& plugins() const { return plugins_; } + const GURL& background_url() const { return background_url_; } + const GURL& options_url() const { return options_url_; } + const GURL& devtools_url() const { return devtools_url_; } + const std::vector<GURL>& toolstrips() const { return toolstrips_; } + const std::set<std::string>& api_permissions() const { + return api_permissions_; + } + const URLPatternList& host_permissions() const { return host_permissions_; } + const GURL& update_url() const { return update_url_; } + const ExtensionIconSet& icons() const { return icons_; } + const DictionaryValue* manifest_value() const { + return manifest_value_.get(); + } + const std::string default_locale() const { return default_locale_; } + const URLOverrideMap& GetChromeURLOverrides() const { + return chrome_url_overrides_; + } + const std::string omnibox_keyword() const { return omnibox_keyword_; } + bool incognito_split_mode() const { return incognito_split_mode_; } + + // App-related. + bool is_app() const { return is_app_; } + bool is_hosted_app() const { return is_app() && !web_extent().is_empty(); } + bool is_packaged_app() const { return is_app() && web_extent().is_empty(); } + const ExtensionExtent& web_extent() const { return extent_; } + const std::string& launch_local_path() const { return launch_local_path_; } + const std::string& launch_web_url() const { return launch_web_url_; } + extension_misc::LaunchContainer launch_container() const { + return launch_container_; + } + int launch_width() const { return launch_width_; } + int launch_height() const { return launch_height_; } + + // Theme-related. + bool is_theme() const { return is_theme_; } + DictionaryValue* GetThemeImages() const { return theme_images_.get(); } + DictionaryValue* GetThemeColors() const {return theme_colors_.get(); } + DictionaryValue* GetThemeTints() const { return theme_tints_.get(); } + DictionaryValue* GetThemeDisplayProperties() const { + return theme_display_properties_.get(); + } + private: + friend class base::RefCountedThreadSafe<Extension>; + + // We keep a cache of images loaded from extension resources based on their + // path and a string representation of a size that may have been used to + // scale it (or the empty string if the image is at its original size). + typedef std::pair<FilePath, std::string> ImageCacheKey; + typedef std::map<ImageCacheKey, SkBitmap> ImageCache; + // Normalize the path for use by the extension. On Windows, this will make // sure the drive letter is uppercase. static FilePath MaybeNormalizePath(const FilePath& path); + Extension(const FilePath& path, Location location); + ~Extension(); + + // Initialize the extension from a parsed manifest. + // Usually, the id of an extension is generated by the "key" property of + // its manifest, but if |require_key| is |false|, a temporary ID will be + // generated based on the path. + bool InitFromValue(const DictionaryValue& value, bool require_key, + std::string* error); + // Helper function for implementing HasCachedImage/GetCachedImage. A return // value of NULL means there is no matching image cached (we allow caching an // empty SkBitmap). SkBitmap* GetCachedImageImpl(const ExtensionResource& source, - const gfx::Size& max_size); + const gfx::Size& max_size) const; // Helper method that loads a UserScript object from a // dictionary in the content_script list of the manifest. @@ -691,43 +485,171 @@ class Extension { // Figures out if a source contains keys not associated with themes - we // don't want to allow scripts and such to be bundled with themes. - bool ContainsNonThemeKeys(const DictionaryValue& source); + bool ContainsNonThemeKeys(const DictionaryValue& source) const; // Returns true if the string is one of the known api permissions (see // kPermissions). - bool IsAPIPermission(const std::string& permission); + bool IsAPIPermission(const std::string& permission) const; // The set of unique API install messages that the extension has. // NOTE: This only includes messages related to permissions declared in the // "permissions" key in the manifest. Permissions implied from other features // of the manifest, like plugins and content scripts are not included. - std::set<string16> GetSimplePermissionMessages(); + std::set<string16> GetSimplePermissionMessages() const; // The permission message displayed related to the host permissions for // this extension. - string16 GetHostPermissionMessage(); + string16 GetHostPermissionMessage() const; + + // Cached images for this extension. This should only be touched on the UI + // thread. + mutable ImageCache image_cache_; + + // A persistent, globally unique ID. An extension's ID is used in things + // like directory structures and URLs, and is expected to not change across + // versions. It is generated as a SHA-256 hash of the extension's public + // key, or as a hash of the path in the case of unpacked extensions. + std::string id_; + + // The extension's human-readable name. Name is used for display purpose. It + // might be wrapped with unicode bidi control characters so that it is + // displayed correctly in RTL context. + // NOTE: Name is UTF-8 and may contain non-ascii characters. + std::string name_; + + // The absolute path to the directory the extension is stored in. + FilePath path_; + + // Default locale for fall back. Can be empty if extension is not localized. + std::string default_locale_; + + // If true, a separate process will be used for the extension in incognito + // mode. + bool incognito_split_mode_; + + // Defines the set of URLs in the extension's web content. + ExtensionExtent extent_; + + // The set of host permissions that the extension effectively has access to, + // which is a merge of host_permissions_ and all of the match patterns in + // any content scripts the extension has. This is used to determine which + // URLs have the ability to load an extension's resources via embedded + // chrome-extension: URLs (see extension_protocols.cc). + ExtensionExtent effective_host_permissions_; + + // The set of module-level APIs this extension can use. + std::set<std::string> api_permissions_; + + // The icons for the extension. + ExtensionIconSet icons_; + + // The base extension url for the extension. + GURL extension_url_; + + // The location the extension was loaded from. + Location location_; + + // The extension's version. + scoped_ptr<Version> version_; + + // An optional longer description of the extension. + std::string description_; + + // True if the extension was generated from a user script. (We show slightly + // different UI if so). + bool converted_from_user_script_; + + // Paths to the content scripts the extension contains. + UserScriptList content_scripts_; + + // The extension's page action, if any. + scoped_ptr<ExtensionAction> page_action_; + + // The extension's browser action, if any. + scoped_ptr<ExtensionAction> browser_action_; + + // Optional list of NPAPI plugins and associated properties. + std::vector<PluginInfo> plugins_; + + // Optional URL to a master page of which a single instance should be always + // loaded in the background. + GURL background_url_; + + // Optional URL to a page for setting options/preferences. + GURL options_url_; + + // Optional URL to a devtools extension page. + GURL devtools_url_; + + // Optional list of toolstrips and associated properties. + std::vector<GURL> toolstrips_; + + // The public key used to sign the contents of the crx package. + std::string public_key_; + + // A map of resource id's to relative file paths. + scoped_ptr<DictionaryValue> theme_images_; + + // A map of color names to colors. + scoped_ptr<DictionaryValue> theme_colors_; + + // A map of color names to colors. + scoped_ptr<DictionaryValue> theme_tints_; + + // A map of display properties. + scoped_ptr<DictionaryValue> theme_display_properties_; + + // Whether the extension is a theme. + bool is_theme_; + + // The sites this extension has permission to talk to (using XHR, etc). + URLPatternList host_permissions_; + + // The homepage for this extension. Useful if it is not hosted by Google and + // therefore does not have a Gallery URL. + GURL homepage_url_; + + // URL for fetching an update manifest + GURL update_url_; + + // A copy of the manifest that this extension was created from. + scoped_ptr<DictionaryValue> manifest_value_; + + // A map of chrome:// hostnames (newtab, downloads, etc.) to Extension URLs + // which override the handling of those URLs. (see ExtensionOverrideUI). + URLOverrideMap chrome_url_overrides_; + + // Whether this extension uses app features. + bool is_app_; + + // The local path inside the extension to use with the launcher. + std::string launch_local_path_; + + // A web url to use with the launcher. Note that this might be relative or + // absolute. If relative, it is relative to web_origin. + std::string launch_web_url_; - // Returns a mutable pointer to our runtime data. Can only be called on - // the UI thread. - RuntimeData* GetRuntimeData() const; + // The type of container to launch into. + extension_misc::LaunchContainer launch_container_; - // Collection of extension data that doesn't change doesn't change once an - // Extension object has been initialized. The mutable version is valid only - // until InitFromValue finishes, to ensure we don't accidentally modify it - // post-initialization. - StaticData* mutable_static_data_; - scoped_refptr<const StaticData> static_data_; + // The default size of the container when launching. Only respected for + // containers like panels and windows. + int launch_width_; + int launch_height_; - // Runtime data. - scoped_ptr<const RuntimeData> runtime_data_; + // The Omnibox keyword for this extension, or empty if there is none. + std::string omnibox_keyword_; FRIEND_TEST_ALL_PREFIXES(ExtensionTest, LoadPageActionHelper); + FRIEND_TEST_ALL_PREFIXES(ExtensionTest, InitFromValueInvalid); + FRIEND_TEST_ALL_PREFIXES(ExtensionTest, InitFromValueValid); + FRIEND_TEST_ALL_PREFIXES(ExtensionTest, InitFromValueValidNameInRTL); FRIEND_TEST_ALL_PREFIXES(TabStripModelTest, Apps); DISALLOW_COPY_AND_ASSIGN(Extension); }; -typedef std::vector<Extension*> ExtensionList; +typedef std::vector< scoped_refptr<const Extension> > ExtensionList; typedef std::set<std::string> ExtensionIdSet; // Handy struct to pass core extension info around. diff --git a/chrome/common/extensions/extension_action.h b/chrome/common/extensions/extension_action.h index a50ebf3..a1e5bc4 100644 --- a/chrome/common/extensions/extension_action.h +++ b/chrome/common/extensions/extension_action.h @@ -151,7 +151,7 @@ class ExtensionAction { }; template<class T> - void SetValue(std::map<int, T>* map, int tab_id, T val) { + void SetValue(std::map<int, T>* map, int tab_id, const T& val) { (*map)[tab_id] = val; } diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index 983fda4..5e418b5 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -19,6 +19,7 @@ const char* kDefaultLocale = "default_locale"; const char* kDescription = "description"; const char* kDevToolsPage = "devtools_page"; const char* kExcludeGlobs = "exclude_globs"; +const char* kHomepageURL = "homepage_url"; const char* kIcons = "icons"; const char* kIncognito = "incognito"; const char* kIncludeGlobs = "include_globs"; @@ -124,6 +125,8 @@ const char* kInvalidGlob = "Invalid value for 'content_scripts[*].*[*]'."; const char* kInvalidGlobList = "Invalid value for 'content_scripts[*].*'."; +const char* kInvalidHomepageURL = + "Invalid value for homepage url: '[*]'."; const char* kInvalidIconPath = "Invalid value for 'icons[\"*\"]'."; const char* kInvalidIcons = diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index e76ce24..332f386 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -24,6 +24,7 @@ namespace extension_manifest_keys { extern const char* kDescription; extern const char* kDevToolsPage; extern const char* kExcludeGlobs; + extern const char* kHomepageURL; extern const char* kIcons; extern const char* kIncognito; extern const char* kIncludeGlobs; @@ -108,6 +109,7 @@ namespace extension_manifest_errors { extern const char* kInvalidDevToolsPage; extern const char* kInvalidGlob; extern const char* kInvalidGlobList; + extern const char* kInvalidHomepageURL; extern const char* kInvalidIconPath; extern const char* kInvalidIcons; extern const char* kInvalidIncognitoBehavior; diff --git a/chrome/common/extensions/extension_file_util.cc b/chrome/common/extensions/extension_file_util.cc index d530357..f2350cc 100644 --- a/chrome/common/extensions/extension_file_util.cc +++ b/chrome/common/extensions/extension_file_util.cc @@ -54,7 +54,7 @@ FilePath InstallExtension(const FilePath& unpacked_source_dir, const int kMaxAttempts = 100; for (int i = 0; i < kMaxAttempts; ++i) { FilePath candidate = extension_dir.AppendASCII( - StringPrintf("%s_%u", version.c_str(), i)); + base::StringPrintf("%s_%u", version.c_str(), i)); if (!file_util::PathExists(candidate)) { version_dir = candidate; break; @@ -81,15 +81,14 @@ void UninstallExtension(const FilePath& extensions_dir, file_util::Delete(extensions_dir.AppendASCII(id), true); // recursive. } -Extension* LoadExtension(const FilePath& extension_path, - Extension::Location location, - bool require_key, - std::string* error) { +scoped_refptr<Extension> LoadExtension(const FilePath& extension_path, + Extension::Location location, + bool require_key, + std::string* error) { FilePath manifest_path = extension_path.Append(Extension::kManifestFilename); if (!file_util::PathExists(manifest_path)) { - *error = - l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); + *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); return NULL; } @@ -101,37 +100,33 @@ Extension* LoadExtension(const FilePath& extension_path, // It would be cleaner to have the JSON reader give a specific error // in this case, but other code tests for a file error with // error->empty(). For now, be consistent. - *error = - l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); + *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); } else { - *error = StringPrintf("%s %s", - errors::kManifestParseError, - error->c_str()); + *error = base::StringPrintf("%s %s", + errors::kManifestParseError, + error->c_str()); } return NULL; } if (!root->IsType(Value::TYPE_DICTIONARY)) { - *error = - l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_INVALID); + *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_INVALID); return NULL; } DictionaryValue* manifest = static_cast<DictionaryValue*>(root.get()); - - scoped_ptr<Extension> extension(new Extension(extension_path)); - extension->set_location(location); - - if (!extension_l10n_util::LocalizeExtension(extension.get(), manifest, error)) + if (!extension_l10n_util::LocalizeExtension(extension_path, manifest, error)) return NULL; - if (!extension->InitFromValue(*manifest, require_key, error)) + scoped_refptr<Extension> extension(Extension::Create( + extension_path, location, *manifest, require_key, error)); + if (!extension.get()) return NULL; if (!ValidateExtension(extension.get(), error)) return NULL; - return extension.release(); + return extension; } bool ValidateExtension(Extension* extension, std::string* error) { @@ -411,7 +406,7 @@ static bool ValidateLocaleInfo(const Extension& extension, std::string* error) { locale_path.Append(Extension::kMessagesFilename); if (!file_util::PathExists(messages_path)) { - *error = StringPrintf( + *error = base::StringPrintf( "%s %s", errors::kLocalesMessagesFileMissing, WideToUTF8(messages_path.ToWStringHack()).c_str()); return false; @@ -480,10 +475,10 @@ bool CheckForIllegalFilenames(const FilePath& extension_path, if (filename.find_first_of(FILE_PATH_LITERAL("_")) != 0) continue; if (reserved_underscore_names.find(filename) == reserved_underscore_names.end()) { - *error = StringPrintf( - "Cannot load extension with file or directory name %s. " - "Filenames starting with \"_\" are reserved for use by the system.", - filename.c_str()); + *error = base::StringPrintf( + "Cannot load extension with file or directory name %s. " + "Filenames starting with \"_\" are reserved for use by the system.", + filename.c_str()); return false; } } diff --git a/chrome/common/extensions/extension_file_util.h b/chrome/common/extensions/extension_file_util.h index 1b8fb76..f6a72bc 100644 --- a/chrome/common/extensions/extension_file_util.h +++ b/chrome/common/extensions/extension_file_util.h @@ -37,10 +37,10 @@ void UninstallExtension(const FilePath& extensions_dir, // Loads and validates an extension from the specified directory. Returns NULL // on failure, with a description of the error in |error|. -Extension* LoadExtension(const FilePath& extension_root, - Extension::Location location, - bool require_key, - std::string* error); +scoped_refptr<Extension> LoadExtension(const FilePath& extension_root, + Extension::Location location, + bool require_key, + std::string* error); // Returns true if the given extension object is valid and consistent. // Otherwise, a description of the error is returned in |error|. diff --git a/chrome/common/extensions/extension_file_util_unittest.cc b/chrome/common/extensions/extension_file_util_unittest.cc index 634df05..230712d 100644 --- a/chrome/common/extensions/extension_file_util_unittest.cc +++ b/chrome/common/extensions/extension_file_util_unittest.cc @@ -78,7 +78,7 @@ TEST(ExtensionFileUtil, LoadExtensionWithValidLocales) { .AppendASCII("1.0.0.0"); std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<Extension> extension(extension_file_util::LoadExtension( install_dir, Extension::LOAD, false, &error)); ASSERT_TRUE(extension != NULL); EXPECT_EQ("The first extension that I made.", extension->description()); @@ -94,7 +94,7 @@ TEST(ExtensionFileUtil, LoadExtensionWithoutLocalesFolder) { .AppendASCII("1.0"); std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<Extension> extension(extension_file_util::LoadExtension( install_dir, Extension::LOAD, false, &error)); ASSERT_FALSE(extension == NULL); EXPECT_TRUE(error.empty()); @@ -152,7 +152,7 @@ TEST(ExtensionFileUtil, LoadExtensionGivesHelpfullErrorOnMissingManifest) { .AppendASCII("1.0"); std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<Extension> extension(extension_file_util::LoadExtension( install_dir, Extension::LOAD, false, &error)); ASSERT_TRUE(extension == NULL); ASSERT_FALSE(error.empty()); @@ -169,7 +169,7 @@ TEST(ExtensionFileUtil, LoadExtensionGivesHelpfullErrorOnBadManifest) { .AppendASCII("1.0"); std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<Extension> extension(extension_file_util::LoadExtension( install_dir, Extension::LOAD, false, &error)); ASSERT_TRUE(extension == NULL); ASSERT_FALSE(error.empty()); @@ -185,7 +185,7 @@ TEST(ExtensionFileUtil, FailLoadingNonUTF8Scripts) { .AppendASCII("bad_encoding"); std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<Extension> extension(extension_file_util::LoadExtension( install_dir, Extension::LOAD, false, &error)); ASSERT_TRUE(extension == NULL); ASSERT_STREQ("Could not load file 'bad_encoding.js' for content script. " diff --git a/chrome/common/extensions/extension_l10n_util.cc b/chrome/common/extensions/extension_l10n_util.cc index 4187689..8c7826c 100644 --- a/chrome/common/extensions/extension_l10n_util.cc +++ b/chrome/common/extensions/extension_l10n_util.cc @@ -113,7 +113,7 @@ bool LocalizeManifest(const ExtensionMessageBundle& messages, return true; } -bool LocalizeExtension(Extension* extension, +bool LocalizeExtension(const FilePath& extension_path, DictionaryValue* manifest, std::string* error) { DCHECK(manifest); @@ -122,7 +122,7 @@ bool LocalizeExtension(Extension* extension, scoped_ptr<ExtensionMessageBundle> message_bundle( extension_file_util::LoadExtensionMessageBundle( - extension->path(), default_locale, error)); + extension_path, default_locale, error)); if (!message_bundle.get() && !error->empty()) return false; @@ -146,8 +146,8 @@ bool AddLocale(const std::set<std::string>& chrome_locales, if (chrome_locales.find(locale_name) == chrome_locales.end()) { // Warn if there is an extension locale that's not in the Chrome list, // but don't fail. - LOG(WARNING) << StringPrintf("Supplied locale %s is not supported.", - locale_name.c_str()); + LOG(WARNING) << base::StringPrintf("Supplied locale %s is not supported.", + locale_name.c_str()); return true; } // Check if messages file is actually present (but don't check content). @@ -155,8 +155,8 @@ bool AddLocale(const std::set<std::string>& chrome_locales, locale_folder.Append(Extension::kMessagesFilename))) { valid_locales->insert(locale_name); } else { - *error = StringPrintf("Catalog file is missing for locale %s.", - locale_name.c_str()); + *error = base::StringPrintf("Catalog file is missing for locale %s.", + locale_name.c_str()); return false; } @@ -250,8 +250,8 @@ static DictionaryValue* LoadMessageFile(const FilePath& locale_path, if (!dictionary && error->empty()) { // JSONFileValueSerializer just returns NULL if file cannot be found. It // doesn't set the error, so we have to do it. - *error = StringPrintf("Catalog file is missing for locale %s.", - extension_locale.c_str()); + *error = base::StringPrintf("Catalog file is missing for locale %s.", + extension_locale.c_str()); } return static_cast<DictionaryValue*>(dictionary); diff --git a/chrome/common/extensions/extension_l10n_util.h b/chrome/common/extensions/extension_l10n_util.h index 48dc901..e1fdfa0 100644 --- a/chrome/common/extensions/extension_l10n_util.h +++ b/chrome/common/extensions/extension_l10n_util.h @@ -44,7 +44,7 @@ bool LocalizeManifest(const ExtensionMessageBundle& messages, // Load message catalogs, localize manifest and attach message bundle to the // extension. -bool LocalizeExtension(Extension* extension, +bool LocalizeExtension(const FilePath& extension_path, DictionaryValue* manifest, std::string* error); diff --git a/chrome/common/extensions/extension_localization_peer.cc b/chrome/common/extensions/extension_localization_peer.cc index 06e0317..e04bd0b 100644 --- a/chrome/common/extensions/extension_localization_peer.cc +++ b/chrome/common/extensions/extension_localization_peer.cc @@ -90,10 +90,6 @@ void ExtensionLocalizationPeer::OnCompletedRequest( original_peer_->OnCompletedRequest(status, security_info, completion_time); } -GURL ExtensionLocalizationPeer::GetURLForDebugging() const { - return original_peer_->GetURLForDebugging(); -} - void ExtensionLocalizationPeer::ReplaceMessages() { if (!message_sender_ || data_.empty()) return; diff --git a/chrome/common/extensions/extension_localization_peer.h b/chrome/common/extensions/extension_localization_peer.h index b2b7c7a..1274421 100644 --- a/chrome/common/extensions/extension_localization_peer.h +++ b/chrome/common/extensions/extension_localization_peer.h @@ -45,7 +45,6 @@ class ExtensionLocalizationPeer virtual void OnCompletedRequest(const URLRequestStatus& status, const std::string& security_info, const base::Time& completion_time); - virtual GURL GetURLForDebugging() const; private: friend class ExtensionLocalizationPeerTest; diff --git a/chrome/common/extensions/extension_localization_peer_unittest.cc b/chrome/common/extensions/extension_localization_peer_unittest.cc index bc6f73f..dc67d5b 100644 --- a/chrome/common/extensions/extension_localization_peer_unittest.cc +++ b/chrome/common/extensions/extension_localization_peer_unittest.cc @@ -72,7 +72,6 @@ class MockResourceLoaderBridgePeer const URLRequestStatus& status, const std::string& security_info, const base::Time& completion_time)); - MOCK_CONST_METHOD0(GetURLForDebugging, GURL()); private: DISALLOW_COPY_AND_ASSIGN(MockResourceLoaderBridgePeer); @@ -153,7 +152,6 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestEmptyData) { // It will self-delete once it exits OnCompletedRequest. ExtensionLocalizationPeer* filter_peer = filter_peer_.release(); - EXPECT_CALL(*original_peer_, GetURLForDebugging()).Times(0); EXPECT_CALL(*original_peer_, OnReceivedData(_, _)).Times(0); EXPECT_CALL(*sender_, Send(_)).Times(0); @@ -172,7 +170,6 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestNoCatalogs) { SetData(filter_peer, "some text"); - EXPECT_CALL(*original_peer_, GetURLForDebugging()).Times(0); EXPECT_CALL(*sender_, Send(_)); std::string data = GetData(filter_peer); @@ -208,7 +205,6 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestWithCatalogs) { SetData(filter_peer, "some __MSG_text__"); - EXPECT_CALL(*original_peer_, GetURLForDebugging()).Times(0); // We already have messages in memory, Send will be skipped. EXPECT_CALL(*sender_, Send(_)).Times(0); @@ -240,7 +236,6 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestReplaceMessagesFails) { std::string message("some __MSG_missing_message__"); SetData(filter_peer, message); - EXPECT_CALL(*original_peer_, GetURLForDebugging()).Times(0); // We already have messages in memory, Send will be skipped. EXPECT_CALL(*sender_, Send(_)).Times(0); diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc index ac56f70..3a89166 100644 --- a/chrome/common/extensions/extension_manifests_unittest.cc +++ b/chrome/common/extensions/extension_manifests_unittest.cc @@ -37,53 +37,48 @@ class ExtensionManifestTest : public testing::Test { return static_cast<DictionaryValue*>(serializer.Deserialize(NULL, error)); } - Extension* LoadExtensionWithLocation(DictionaryValue* value, - Extension::Location location, - std::string* error) { + scoped_refptr<Extension> LoadExtensionWithLocation( + DictionaryValue* value, + Extension::Location location, + std::string* error) { FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); path = path.AppendASCII("extensions").AppendASCII("manifest_tests"); - - scoped_ptr<Extension> extension(new Extension(path.DirName())); - extension->set_location(location); - - if (!extension->InitFromValue(*value, false, error)) - return NULL; - - return extension.release(); + return Extension::Create(path.DirName(), location, *value, false, error); } - Extension* LoadExtension(const std::string& name, - std::string* error) { + scoped_refptr<Extension> LoadExtension(const std::string& name, + std::string* error) { return LoadExtensionWithLocation(name, Extension::INTERNAL, error); } - Extension* LoadExtension(DictionaryValue* value, - std::string* error) { + scoped_refptr<Extension> LoadExtension(DictionaryValue* value, + std::string* error) { return LoadExtensionWithLocation(value, Extension::INTERNAL, error); } - Extension* LoadExtensionWithLocation(const std::string& name, - Extension::Location location, - std::string* error) { + scoped_refptr<Extension> LoadExtensionWithLocation( + const std::string& name, + Extension::Location location, + std::string* error) { scoped_ptr<DictionaryValue> value(LoadManifestFile(name, error)); if (!value.get()) return NULL; return LoadExtensionWithLocation(value.get(), location, error); } - Extension* LoadAndExpectSuccess(const std::string& name) { + scoped_refptr<Extension> LoadAndExpectSuccess(const std::string& name) { std::string error; - Extension* extension = LoadExtension(name, &error); + scoped_refptr<Extension> extension = LoadExtension(name, &error); EXPECT_TRUE(extension) << name; EXPECT_EQ("", error) << name; return extension; } - Extension* LoadAndExpectSuccess(DictionaryValue* manifest, - const std::string& name) { + scoped_refptr<Extension> LoadAndExpectSuccess(DictionaryValue* manifest, + const std::string& name) { std::string error; - Extension* extension = LoadExtension(manifest, &error); + scoped_refptr<Extension> extension = LoadExtension(manifest, &error); EXPECT_TRUE(extension) << "Unexpected success for " << name; EXPECT_EQ("", error) << "Unexpected no error for " << name; return extension; @@ -103,7 +98,7 @@ class ExtensionManifestTest : public testing::Test { void LoadAndExpectError(const std::string& name, const std::string& expected_error) { std::string error; - scoped_ptr<Extension> extension(LoadExtension(name, &error)); + scoped_refptr<Extension> extension(LoadExtension(name, &error)); VerifyExpectedError(extension.get(), name, error, expected_error); } @@ -111,7 +106,7 @@ class ExtensionManifestTest : public testing::Test { const std::string& name, const std::string& expected_error) { std::string error; - scoped_ptr<Extension> extension(LoadExtension(manifest, &error)); + scoped_refptr<Extension> extension(LoadExtension(manifest, &error)); VerifyExpectedError(extension.get(), name, error, expected_error); } @@ -119,7 +114,7 @@ class ExtensionManifestTest : public testing::Test { }; TEST_F(ExtensionManifestTest, ValidApp) { - scoped_ptr<Extension> extension(LoadAndExpectSuccess("valid_app.json")); + scoped_refptr<Extension> extension(LoadAndExpectSuccess("valid_app.json")); ASSERT_EQ(2u, extension->web_extent().patterns().size()); EXPECT_EQ("http://www.google.com/mail/*", extension->web_extent().patterns()[0].GetAsString()); @@ -145,7 +140,7 @@ TEST_F(ExtensionManifestTest, AppWebUrls) { ExtensionErrorUtils::FormatErrorMessage( errors::kInvalidWebURL, "0")); - scoped_ptr<Extension> extension( + scoped_refptr<Extension> extension( LoadAndExpectSuccess("web_urls_default.json")); ASSERT_EQ(1u, extension->web_extent().patterns().size()); EXPECT_EQ("*://www.google.com/*", @@ -153,21 +148,21 @@ TEST_F(ExtensionManifestTest, AppWebUrls) { } TEST_F(ExtensionManifestTest, AppLaunchContainer) { - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; - extension.reset(LoadAndExpectSuccess("launch_tab.json")); + extension = LoadAndExpectSuccess("launch_tab.json"); EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container()); - extension.reset(LoadAndExpectSuccess("launch_panel.json")); + extension = LoadAndExpectSuccess("launch_panel.json"); EXPECT_EQ(extension_misc::LAUNCH_PANEL, extension->launch_container()); - extension.reset(LoadAndExpectSuccess("launch_default.json")); + extension = LoadAndExpectSuccess("launch_default.json"); EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container()); - extension.reset(LoadAndExpectSuccess("launch_width.json")); + extension = LoadAndExpectSuccess("launch_width.json"); EXPECT_EQ(640, extension->launch_width()); - extension.reset(LoadAndExpectSuccess("launch_height.json")); + extension = LoadAndExpectSuccess("launch_height.json"); EXPECT_EQ(480, extension->launch_height()); LoadAndExpectError("launch_window.json", @@ -198,15 +193,15 @@ TEST_F(ExtensionManifestTest, AppLaunchURL) { LoadAndExpectError("launch_url_invalid_type.json", errors::kInvalidLaunchWebURL); - scoped_ptr<Extension> extension; - extension.reset(LoadAndExpectSuccess("launch_local_path.json")); + scoped_refptr<Extension> extension; + extension = LoadAndExpectSuccess("launch_local_path.json"); EXPECT_EQ(extension->url().spec() + "launch.html", extension->GetFullLaunchURL().spec()); LoadAndExpectError("launch_web_url_relative.json", errors::kInvalidLaunchWebURL); - extension.reset(LoadAndExpectSuccess("launch_web_url_absolute.json")); + extension = LoadAndExpectSuccess("launch_web_url_absolute.json"); EXPECT_EQ(GURL("http://www.google.com/launch.html"), extension->GetFullLaunchURL()); } @@ -217,13 +212,13 @@ TEST_F(ExtensionManifestTest, Override) { LoadAndExpectError("override_invalid_page.json", errors::kInvalidChromeURLOverrides); - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; - extension.reset(LoadAndExpectSuccess("override_new_tab.json")); + extension = LoadAndExpectSuccess("override_new_tab.json"); EXPECT_EQ(extension->url().spec() + "newtab.html", extension->GetChromeURLOverrides().find("newtab")->second.spec()); - extension.reset(LoadAndExpectSuccess("override_history.json")); + extension = LoadAndExpectSuccess("override_history.json"); EXPECT_EQ(extension->url().spec() + "history.html", extension->GetChromeURLOverrides().find("history")->second.spec()); } @@ -237,11 +232,11 @@ TEST_F(ExtensionManifestTest, ChromeResourcesPermissionValidOnlyForComponents) { LoadAndExpectError("permission_chrome_resources_url.json", errors::kInvalidPermissionScheme); std::string error; - scoped_ptr<Extension> extension; - extension.reset(LoadExtensionWithLocation( + scoped_refptr<Extension> extension; + extension = LoadExtensionWithLocation( "permission_chrome_resources_url.json", Extension::COMPONENT, - &error)); + &error); EXPECT_EQ("", error); } @@ -260,8 +255,8 @@ TEST_F(ExtensionManifestTest, DevToolsExtensions) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); - scoped_ptr<Extension> extension; - extension.reset(LoadAndExpectSuccess("devtools_extension.json")); + scoped_refptr<Extension> extension; + extension = LoadAndExpectSuccess("devtools_extension.json"); EXPECT_EQ(extension->url().spec() + "devtools.html", extension->devtools_url().spec()); *CommandLine::ForCurrentProcess() = old_command_line; @@ -275,11 +270,10 @@ TEST_F(ExtensionManifestTest, DisallowHybridApps) { } TEST_F(ExtensionManifestTest, OptionsPageInApps) { - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; // Allow options page with absolute URL in hosed apps. - extension.reset( - LoadAndExpectSuccess("hosted_app_absolute_options.json")); + extension = LoadAndExpectSuccess("hosted_app_absolute_options.json"); EXPECT_STREQ("http", extension->options_url().scheme().c_str()); EXPECT_STREQ("example.com", @@ -313,10 +307,10 @@ TEST_F(ExtensionManifestTest, DisallowExtensionPermissions) { StringValue* p = new StringValue(name); permissions->Clear(); permissions->Append(p); - std::string message_name = StringPrintf("permission-%s", name); + std::string message_name = base::StringPrintf("permission-%s", name); if (Extension::IsHostedAppPermission(name)) { - scoped_ptr<Extension> extension; - extension.reset(LoadAndExpectSuccess(manifest.get(), message_name)); + scoped_refptr<Extension> extension; + extension = LoadAndExpectSuccess(manifest.get(), message_name); } else { LoadAndExpectError(manifest.get(), message_name, errors::kInvalidPermission); @@ -325,7 +319,7 @@ TEST_F(ExtensionManifestTest, DisallowExtensionPermissions) { } TEST_F(ExtensionManifestTest, NormalizeIconPaths) { - scoped_ptr<Extension> extension( + scoped_refptr<Extension> extension( LoadAndExpectSuccess("normalize_icon_paths.json")); EXPECT_EQ("16.png", extension->icons().Get(16, ExtensionIconSet::MATCH_EXACTLY)); @@ -338,3 +332,38 @@ TEST_F(ExtensionManifestTest, DisallowMultipleUISurfaces) { LoadAndExpectError("multiple_ui_surfaces_2.json", errors::kOneUISurfaceOnly); LoadAndExpectError("multiple_ui_surfaces_3.json", errors::kOneUISurfaceOnly); } + +TEST_F(ExtensionManifestTest, ParseHomepageURLs) { + scoped_refptr<Extension> extension( + LoadAndExpectSuccess("homepage_valid.json")); + LoadAndExpectError("homepage_empty.json", + extension_manifest_errors::kInvalidHomepageURL); + LoadAndExpectError("homepage_invalid.json", + extension_manifest_errors::kInvalidHomepageURL); +} + +TEST_F(ExtensionManifestTest, GetHomepageURL) { + scoped_refptr<Extension> extension( + LoadAndExpectSuccess("homepage_valid.json")); + EXPECT_EQ(GURL("http://foo.com#bar"), extension->GetHomepageURL()); + + // The Google Gallery URL ends with the id, which depends on the path, which + // can be different in testing, so we just check the part before id. + extension = LoadAndExpectSuccess("homepage_google_hosted.json"); + EXPECT_TRUE(StartsWithASCII(extension->GetHomepageURL().spec(), + "https://chrome.google.com/extensions/detail/", + false)); + + extension = LoadAndExpectSuccess("homepage_externally_hosted.json"); + EXPECT_EQ(GURL(), extension->GetHomepageURL()); +} + +TEST_F(ExtensionManifestTest, DefaultPathForExtent) { + scoped_refptr<Extension> extension( + LoadAndExpectSuccess("default_path_for_extent.json")); + + ASSERT_EQ(1u, extension->web_extent().patterns().size()); + EXPECT_EQ("/*", extension->web_extent().patterns()[0].path()); + EXPECT_TRUE(extension->web_extent().ContainsURL( + GURL("http://www.google.com/monkey"))); +} diff --git a/chrome/common/extensions/extension_message_bundle.cc b/chrome/common/extensions/extension_message_bundle.cc index 4de6b45..3f7c152 100644 --- a/chrome/common/extensions/extension_message_bundle.cc +++ b/chrome/common/extensions/extension_message_bundle.cc @@ -48,8 +48,10 @@ const char* ExtensionMessageBundle::kBidiRightEdgeValue = "right"; // Formats message in case we encounter a bad formed key in the JSON object. // Returns false and sets |error| to actual error message. static bool BadKeyMessage(const std::string& name, std::string* error) { - *error = StringPrintf("Name of a key \"%s\" is invalid. Only ASCII [a-z], " - "[A-Z], [0-9] and \"_\" are allowed.", name.c_str()); + *error = base::StringPrintf( + "Name of a key \"%s\" is invalid. Only ASCII [a-z], " + "[A-Z], [0-9] and \"_\" are allowed.", + name.c_str()); return false; } @@ -134,13 +136,13 @@ bool ExtensionMessageBundle::GetMessageValue(const std::string& key, // Get the top level tree for given key (name part). DictionaryValue* name_tree; if (!catalog.GetDictionaryWithoutPathExpansion(key, &name_tree)) { - *error = StringPrintf("Not a valid tree for key %s.", key.c_str()); + *error = base::StringPrintf("Not a valid tree for key %s.", key.c_str()); return false; } // Extract message from it. if (!name_tree->GetString(kMessageKey, value)) { - *error = StringPrintf("There is no \"%s\" element for key %s.", kMessageKey, - key.c_str()); + *error = base::StringPrintf( + "There is no \"%s\" element for key %s.", kMessageKey, key.c_str()); return false; } @@ -166,8 +168,8 @@ bool ExtensionMessageBundle::GetPlaceholders(const DictionaryValue& name_tree, DictionaryValue* placeholders_tree; if (!name_tree.GetDictionary(kPlaceholdersKey, &placeholders_tree)) { - *error = StringPrintf("Not a valid \"%s\" element for key %s.", - kPlaceholdersKey, name_key.c_str()); + *error = base::StringPrintf("Not a valid \"%s\" element for key %s.", + kPlaceholdersKey, name_key.c_str()); return false; } @@ -179,15 +181,15 @@ bool ExtensionMessageBundle::GetPlaceholders(const DictionaryValue& name_tree, return BadKeyMessage(content_key, error); if (!placeholders_tree->GetDictionaryWithoutPathExpansion(content_key, &placeholder)) { - *error = StringPrintf("Invalid placeholder %s for key %s", - content_key.c_str(), - name_key.c_str()); + *error = base::StringPrintf("Invalid placeholder %s for key %s", + content_key.c_str(), + name_key.c_str()); return false; } std::string content; if (!placeholder->GetString(kContentKey, &content)) { - *error = StringPrintf("Invalid \"%s\" element for key %s.", - kContentKey, name_key.c_str()); + *error = base::StringPrintf("Invalid \"%s\" element for key %s.", + kContentKey, name_key.c_str()); return false; } (*placeholders)[StringToLowerASCII(content_key)] = content; @@ -253,10 +255,10 @@ bool ExtensionMessageBundle::ReplaceVariables( SubstitutionMap::const_iterator it = variables.find(StringToLowerASCII(var_name)); if (it == variables.end()) { - *error = StringPrintf("Variable %s%s%s used but not defined.", - var_begin_delimiter.c_str(), - var_name.c_str(), - var_end_delimiter.c_str()); + *error = base::StringPrintf("Variable %s%s%s used but not defined.", + var_begin_delimiter.c_str(), + var_name.c_str(), + var_end_delimiter.c_str()); return false; } diff --git a/chrome/common/extensions/extension_resource.cc b/chrome/common/extensions/extension_resource.cc index d3c5898..be22ec9 100644 --- a/chrome/common/extensions/extension_resource.cc +++ b/chrome/common/extensions/extension_resource.cc @@ -6,6 +6,7 @@ #include "base/file_util.h" #include "base/logging.h" +#include "base/thread_restrictions.h" PlatformThreadId ExtensionResource::file_thread_id_ = 0; @@ -45,6 +46,12 @@ const FilePath& ExtensionResource::GetFilePath() const { // static FilePath ExtensionResource::GetFilePathOnAnyThreadHack( const FilePath& extension_root, const FilePath& relative_path) { + // This function is a hack, and causes us to block the IO thread. + // Fixing + // http://code.google.com/p/chromium/issues/detail?id=59849 + // would also fix this. Suppress the error for now. + base::ThreadRestrictions::ScopedAllowIO allow_io; + // We need to resolve the parent references in the extension_root // path on its own because IsParent doesn't like parent references. FilePath clean_extension_root(extension_root); diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index d2a7049..3f86bbd 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -77,7 +77,9 @@ TEST(ExtensionTest, InitFromValueInvalid) { #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - Extension extension(path); + scoped_refptr<Extension> extension_ptr(new Extension(path, + Extension::INVALID)); + Extension& extension = *extension_ptr; int error_code = 0; std::string error; @@ -303,7 +305,9 @@ TEST(ExtensionTest, InitFromValueValid) { #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - Extension extension(path); + scoped_refptr<Extension> extension_ptr(new Extension(path, + Extension::INVALID)); + Extension& extension = *extension_ptr; std::string error; DictionaryValue input_value; @@ -366,7 +370,9 @@ TEST(ExtensionTest, InitFromValueValidNameInRTL) { #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - Extension extension(path); + scoped_refptr<Extension> extension_ptr(new Extension(path, + Extension::INVALID)); + Extension& extension = *extension_ptr; std::string error; DictionaryValue input_value; @@ -403,27 +409,31 @@ TEST(ExtensionTest, GetResourceURLAndPath) { #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - Extension extension(path); DictionaryValue input_value; input_value.SetString(keys::kVersion, "1.0.0.0"); input_value.SetString(keys::kName, "my extension"); - EXPECT_TRUE(extension.InitFromValue(input_value, false, NULL)); - - EXPECT_EQ(extension.url().spec() + "bar/baz.js", - Extension::GetResourceURL(extension.url(), "bar/baz.js").spec()); - EXPECT_EQ(extension.url().spec() + "baz.js", - Extension::GetResourceURL(extension.url(), "bar/../baz.js").spec()); - EXPECT_EQ(extension.url().spec() + "baz.js", - Extension::GetResourceURL(extension.url(), "../baz.js").spec()); + scoped_refptr<Extension> extension(Extension::Create( + path, Extension::INVALID, input_value, false, NULL)); + EXPECT_TRUE(extension.get()); + + EXPECT_EQ(extension->url().spec() + "bar/baz.js", + Extension::GetResourceURL(extension->url(), "bar/baz.js").spec()); + EXPECT_EQ(extension->url().spec() + "baz.js", + Extension::GetResourceURL(extension->url(), + "bar/../baz.js").spec()); + EXPECT_EQ(extension->url().spec() + "baz.js", + Extension::GetResourceURL(extension->url(), "../baz.js").spec()); } TEST(ExtensionTest, LoadPageActionHelper) { #if defined(OS_WIN) - FilePath path(StringPrintf(L"c:\\extension")); + FilePath path(base::StringPrintf(L"c:\\extension")); #else - FilePath path(StringPrintf("/extension")); + FilePath path(base::StringPrintf("/extension")); #endif - Extension extension(path); + scoped_refptr<Extension> extension_ptr(new Extension(path, + Extension::INVALID)); + Extension& extension = *extension_ptr; std::string error_msg; scoped_ptr<ExtensionAction> action; DictionaryValue input; @@ -539,7 +549,7 @@ TEST(ExtensionTest, LoadPageActionHelper) { // LoadExtensionActionHelper expects the extension member |extension_url| // to be set. - extension.mutable_static_data_->extension_url = + extension.extension_url_ = GURL(std::string(chrome::kExtensionScheme) + chrome::kStandardSchemeSeparator + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/"); @@ -651,18 +661,19 @@ TEST(ExtensionTest, UpdateUrls) { #if defined(OS_WIN) // (Why %Iu below? This is the single file in the whole code base that // might make use of a WidePRIuS; let's not encourage any more.) - FilePath path(StringPrintf(L"c:\\extension%Iu", i)); + FilePath path(base::StringPrintf(L"c:\\extension%Iu", i)); #else - FilePath path(StringPrintf("/extension%" PRIuS, i)); + FilePath path(base::StringPrintf("/extension%" PRIuS, i)); #endif - Extension extension(path); std::string error; input_value.SetString(keys::kVersion, "1.0"); input_value.SetString(keys::kName, "Test"); input_value.SetString(keys::kUpdateURL, url.spec()); - EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); + scoped_refptr<Extension> extension(Extension::Create( + path, Extension::INVALID, input_value, false, &error)); + EXPECT_TRUE(extension.get()) << error; } // Test some invalid update urls @@ -675,17 +686,18 @@ TEST(ExtensionTest, UpdateUrls) { #if defined(OS_WIN) // (Why %Iu below? This is the single file in the whole code base that // might make use of a WidePRIuS; let's not encourage any more.) - FilePath path(StringPrintf(L"c:\\extension%Iu", i)); + FilePath path(base::StringPrintf(L"c:\\extension%Iu", i)); #else - FilePath path(StringPrintf("/extension%" PRIuS, i)); + FilePath path(base::StringPrintf("/extension%" PRIuS, i)); #endif - Extension extension(path); std::string error; input_value.SetString(keys::kVersion, "1.0"); input_value.SetString(keys::kName, "Test"); input_value.SetString(keys::kUpdateURL, invalid[i]); - EXPECT_FALSE(extension.InitFromValue(input_value, false, &error)); + scoped_refptr<Extension> extension(Extension::Create( + path, Extension::INVALID, input_value, false, &error)); + EXPECT_FALSE(extension.get()); EXPECT_TRUE(MatchPattern(error, errors::kInvalidUpdateURL)); } } @@ -714,8 +726,8 @@ TEST(ExtensionTest, MimeTypeSniffing) { EXPECT_EQ("application/octet-stream", result); } -static Extension* LoadManifest(const std::string& dir, - const std::string& test_file) { +static scoped_refptr<Extension> LoadManifest(const std::string& dir, + const std::string& test_file) { FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); path = path.AppendASCII("extensions") @@ -730,74 +742,70 @@ static Extension* LoadManifest(const std::string& dir, return NULL; } - scoped_ptr<Extension> extension(new Extension(path.DirName())); - EXPECT_TRUE(extension->InitFromValue( - *static_cast<DictionaryValue*>(result.get()), false, &error)) << error; - - return extension.release(); + scoped_refptr<Extension> extension = Extension::Create( + path.DirName(), Extension::INVALID, + *static_cast<DictionaryValue*>(result.get()), false, &error); + EXPECT_TRUE(extension) << error; + return extension; } TEST(ExtensionTest, EffectiveHostPermissions) { - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; ExtensionExtent hosts; - extension.reset(LoadManifest("effective_host_permissions", "empty.json")); + extension = LoadManifest("effective_host_permissions", "empty.json"); EXPECT_EQ(0u, extension->GetEffectiveHostPermissions().patterns().size()); EXPECT_FALSE(hosts.ContainsURL(GURL("http://www.google.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", "one_host.json")); + extension = LoadManifest("effective_host_permissions", "one_host.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com"))); EXPECT_FALSE(hosts.ContainsURL(GURL("https://www.google.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "one_host_wildcard.json")); + extension = LoadManifest("effective_host_permissions", + "one_host_wildcard.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://foo.google.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "two_hosts.json")); + extension = LoadManifest("effective_host_permissions", "two_hosts.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.reddit.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "https_not_considered.json")); + extension = LoadManifest("effective_host_permissions", + "https_not_considered.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com"))); EXPECT_TRUE(hosts.ContainsURL(GURL("https://google.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "two_content_scripts.json")); + extension = LoadManifest("effective_host_permissions", + "two_content_scripts.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.reddit.com"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://news.ycombinator.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "all_hosts.json")); + extension = LoadManifest("effective_host_permissions", "all_hosts.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://test/"))); EXPECT_FALSE(hosts.ContainsURL(GURL("https://test/"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com"))); EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "all_hosts2.json")); + extension = LoadManifest("effective_host_permissions", "all_hosts2.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://test/"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com"))); EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "all_hosts3.json")); + extension = LoadManifest("effective_host_permissions", "all_hosts3.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_FALSE(hosts.ContainsURL(GURL("http://test/"))); EXPECT_TRUE(hosts.ContainsURL(GURL("https://test/"))); @@ -834,10 +842,10 @@ TEST(ExtensionTest, IsPrivilegeIncrease) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { - scoped_ptr<Extension> old_extension( + scoped_refptr<Extension> old_extension( LoadManifest("allow_silent_upgrade", std::string(kTests[i].base_name) + "_old.json")); - scoped_ptr<Extension> new_extension( + scoped_refptr<Extension> new_extension( LoadManifest("allow_silent_upgrade", std::string(kTests[i].base_name) + "_new.json")); @@ -912,11 +920,12 @@ TEST(ExtensionTest, ImageCaching) { // Initialize the Extension. std::string errors; - scoped_ptr<Extension> extension(new Extension(path)); DictionaryValue values; values.SetString(keys::kName, "test"); values.SetString(keys::kVersion, "0.1"); - ASSERT_TRUE(extension->InitFromValue(values, false, &errors)); + scoped_refptr<Extension> extension(Extension::Create( + path, Extension::INVALID, values, false, &errors)); + ASSERT_TRUE(extension.get()); // Create an ExtensionResource pointing at an icon. FilePath icon_relative_path(FILE_PATH_LITERAL("icon3.png")); @@ -997,10 +1006,11 @@ TEST(ExtensionTest, OldUnlimitedStoragePermission) { // Initialize the extension and make sure the permission for unlimited storage // is present. - Extension extension(extension_path); std::string errors; - EXPECT_TRUE(extension.InitFromValue(dictionary, false, &errors)); - EXPECT_TRUE(extension.HasApiPermission( + scoped_refptr<Extension> extension(Extension::Create( + extension_path, Extension::INVALID, dictionary, false, &errors)); + EXPECT_TRUE(extension.get()); + EXPECT_TRUE(extension->HasApiPermission( Extension::kUnlimitedStoragePermission)); } @@ -1036,8 +1046,8 @@ TEST(ExtensionTest, ApiPermissions) { { "tabs.getSelected", false}, }; - scoped_ptr<Extension> extension; - extension.reset(LoadManifest("empty_manifest", "empty.json")); + scoped_refptr<Extension> extension; + extension = LoadManifest("empty_manifest", "empty.json"); for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { EXPECT_EQ(kTests[i].expect_success, diff --git a/chrome/common/extensions/extension_unpacker.cc b/chrome/common/extensions/extension_unpacker.cc index d431bcb..e270870 100644 --- a/chrome/common/extensions/extension_unpacker.cc +++ b/chrome/common/extensions/extension_unpacker.cc @@ -175,20 +175,21 @@ bool ExtensionUnpacker::Run() { // InitFromValue is allowed to generate a temporary id for the extension. // ANY CODE THAT FOLLOWS SHOULD NOT DEPEND ON THE CORRECT ID OF THIS // EXTENSION. - Extension extension(temp_install_dir_); std::string error; - if (!extension.InitFromValue(*parsed_manifest_, false, &error)) { + scoped_refptr<Extension> extension(Extension::Create( + temp_install_dir_, Extension::INVALID, *parsed_manifest_, false, &error)); + if (!extension.get()) { SetError(error); return false; } - if (!extension_file_util::ValidateExtension(&extension, &error)) { + if (!extension_file_util::ValidateExtension(extension.get(), &error)) { SetError(error); return false; } // Decode any images that the browser needs to display. - std::set<FilePath> image_paths = extension.GetBrowserImages(); + std::set<FilePath> image_paths = extension->GetBrowserImages(); for (std::set<FilePath>::iterator it = image_paths.begin(); it != image_paths.end(); ++it) { if (!AddDecodedImage(*it)) @@ -197,8 +198,8 @@ bool ExtensionUnpacker::Run() { // Parse all message catalogs (if any). parsed_catalogs_.reset(new DictionaryValue); - if (!extension.default_locale().empty()) { - if (!ReadAllMessageCatalogs(extension.default_locale())) + if (!extension->default_locale().empty()) { + if (!ReadAllMessageCatalogs(extension->default_locale())) return false; // Error was already reported. } @@ -288,10 +289,11 @@ bool ExtensionUnpacker::ReadMessageCatalog(const FilePath& message_path) { std::string messages_file = WideToASCII(message_path.ToWStringHack()); if (error.empty()) { // If file is missing, Deserialize will fail with empty error. - SetError(StringPrintf("%s %s", errors::kLocalesMessagesFileMissing, - messages_file.c_str())); + SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing, + messages_file.c_str())); } else { - SetError(StringPrintf("%s: %s", messages_file.c_str(), error.c_str())); + SetError(base::StringPrintf("%s: %s", messages_file.c_str(), + error.c_str())); } return false; } diff --git a/chrome/common/extensions/url_pattern.cc b/chrome/common/extensions/url_pattern.cc index aa7e252..b6e09b7 100644 --- a/chrome/common/extensions/url_pattern.cc +++ b/chrome/common/extensions/url_pattern.cc @@ -58,14 +58,14 @@ URLPattern::URLPattern(int valid_schemes) URLPattern::URLPattern(int valid_schemes, const std::string& pattern) : valid_schemes_(valid_schemes), match_all_urls_(false), match_subdomains_(false) { - if (!Parse(pattern)) + if (PARSE_SUCCESS != Parse(pattern)) NOTREACHED() << "URLPattern is invalid: " << pattern; } URLPattern::~URLPattern() { } -bool URLPattern::Parse(const std::string& pattern) { +URLPattern::ParseResult URLPattern::Parse(const std::string& pattern) { // Special case pattern to match every valid URL. if (pattern == kAllUrlsPattern) { match_all_urls_ = true; @@ -73,40 +73,51 @@ bool URLPattern::Parse(const std::string& pattern) { scheme_ = "*"; host_.clear(); path_ = "/*"; - return true; + return PARSE_SUCCESS; + } + + // Parse out the scheme. + size_t scheme_end_pos = pattern.find(chrome::kStandardSchemeSeparator); + bool has_standard_scheme_separator = true; + + // Some urls also use ':' alone as the scheme separator. + if (scheme_end_pos == std::string::npos) { + scheme_end_pos = pattern.find(':'); + has_standard_scheme_separator = false; } - size_t scheme_end_pos = pattern.find(":"); if (scheme_end_pos == std::string::npos) - return false; + return PARSE_ERROR_MISSING_SCHEME_SEPARATOR; if (!SetScheme(pattern.substr(0, scheme_end_pos))) - return false; + return PARSE_ERROR_INVALID_SCHEME; - std::string separator = - pattern.substr(scheme_end_pos, strlen(chrome::kStandardSchemeSeparator)); - if (separator == chrome::kStandardSchemeSeparator) - scheme_end_pos += strlen(chrome::kStandardSchemeSeparator); - else - scheme_end_pos += 1; + bool standard_scheme = IsStandardScheme(scheme_); + if (standard_scheme != has_standard_scheme_separator) + return PARSE_ERROR_WRONG_SCHEME_SEPARATOR; // Advance past the scheme separator. - size_t host_start_pos = scheme_end_pos; - if (host_start_pos >= pattern.length()) - return false; + scheme_end_pos += + (standard_scheme ? strlen(chrome::kStandardSchemeSeparator) : 1); + if (scheme_end_pos >= pattern.size()) + return PARSE_ERROR_EMPTY_HOST; // Parse out the host and path. + size_t host_start_pos = scheme_end_pos; size_t path_start_pos = 0; - bool standard_scheme = IsStandardScheme(scheme_); - // File URLs are special because they have no host. if (scheme_ == chrome::kFileScheme || !standard_scheme) { path_start_pos = host_start_pos; } else { size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos); + + // Host is required. + if (host_start_pos == host_end_pos) + return PARSE_ERROR_EMPTY_HOST; + if (host_end_pos == std::string::npos) - return false; + return PARSE_ERROR_EMPTY_PATH; host_ = pattern.substr(host_start_pos, host_end_pos - host_start_pos); @@ -124,14 +135,14 @@ bool URLPattern::Parse(const std::string& pattern) { // done as a convenience to developers who might otherwise be confused and // think '*' works as a glob in the host. if (host_.find('*') != std::string::npos) - return false; + return PARSE_ERROR_INVALID_HOST_WILDCARD; path_start_pos = host_end_pos; } path_ = pattern.substr(path_start_pos); - return true; + return PARSE_SUCCESS; } bool URLPattern::SetScheme(const std::string& scheme) { diff --git a/chrome/common/extensions/url_pattern.h b/chrome/common/extensions/url_pattern.h index 832673c..72810ba 100644 --- a/chrome/common/extensions/url_pattern.h +++ b/chrome/common/extensions/url_pattern.h @@ -91,13 +91,20 @@ class URLPattern { SCHEME_ALL = -1, }; + // Error codes returned from Parse(). + enum ParseResult { + PARSE_SUCCESS, + PARSE_ERROR_MISSING_SCHEME_SEPARATOR, + PARSE_ERROR_INVALID_SCHEME, + PARSE_ERROR_WRONG_SCHEME_SEPARATOR, + PARSE_ERROR_EMPTY_HOST, + PARSE_ERROR_INVALID_HOST_WILDCARD, + PARSE_ERROR_EMPTY_PATH, + }; + // The <all_urls> string pattern. static const char kAllUrlsPattern[]; - // Note: don't use this directly. This exists so URLPattern can be used - // with STL containers. - URLPattern(); - // Construct an URLPattern with the given set of allowable schemes. See // valid_schemes_ for more info. explicit URLPattern(int valid_schemes); @@ -134,9 +141,11 @@ class URLPattern { bool match_all_urls() const { return match_all_urls_; } void set_match_all_urls(bool val) { match_all_urls_ = val; } - // Initializes this instance by parsing the provided string. On failure, the - // instance will have some intermediate values and is in an invalid state. - bool Parse(const std::string& pattern_str); + // Initializes this instance by parsing the provided string. Returns + // URLPattern::PARSE_SUCCESS on success, or an error code otherwise. On + // failure, this instance will have some intermediate values and is in an + // invalid state. + ParseResult Parse(const std::string& pattern_str); // Sets the scheme for pattern matches. This can be a single '*' if the // pattern matches all valid schemes (as defined by the valid_schemes_ @@ -192,6 +201,21 @@ class URLPattern { }; private: + friend class std::vector<URLPattern>; + +// See clang bug: http://llvm.org/bugs/show_bug.cgi?id=8479 +#if defined(__clang__) + public: +#endif + + // Note: don't use this directly. This exists so URLPattern can be used + // with STL containers. + URLPattern(); + +#if defined(__clang__) + private: +#endif + // A bitmask containing the schemes which are considered valid for this // pattern. Parse() uses this to decide whether a pattern contains a valid // scheme. MatchesScheme uses this to decide whether a wildcard scheme_ diff --git a/chrome/common/extensions/url_pattern_unittest.cc b/chrome/common/extensions/url_pattern_unittest.cc index a9ef21e..e0be087 100644 --- a/chrome/common/extensions/url_pattern_unittest.cc +++ b/chrome/common/extensions/url_pattern_unittest.cc @@ -17,29 +17,34 @@ static const int kAllSchemes = URLPattern::SCHEME_CHROMEUI; TEST(URLPatternTest, ParseInvalid) { - const char* kInvalidPatterns[] = { - "http", // no scheme - "http:", - "http:/", - "http://", // no path separator - "http://foo", // no path separator - "http://*foo/bar", // not allowed as substring of host component - "http://foo.*.bar/baz", // must be first component - "http:/bar", // scheme separator not found - "foo://*", // invalid scheme - "chrome-extension://*/*", // we don't support chrome extension URLs + const struct { + const char* pattern; + URLPattern::ParseResult expected_result; + } kInvalidPatterns[] = { + { "http", URLPattern::PARSE_ERROR_MISSING_SCHEME_SEPARATOR }, + { "http:", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR }, + { "http:/", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR }, + { "about://", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR }, + { "http://", URLPattern::PARSE_ERROR_EMPTY_HOST }, + { "http:///", URLPattern::PARSE_ERROR_EMPTY_HOST }, + { "http://*foo/bar", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD }, + { "http://foo.*.bar/baz", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD }, + { "http:/bar", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR }, + { "http://bar", URLPattern::PARSE_ERROR_EMPTY_PATH }, }; - for (size_t i = 0; i < arraysize(kInvalidPatterns); ++i) { - URLPattern pattern; - EXPECT_FALSE(pattern.Parse(kInvalidPatterns[i])) << kInvalidPatterns[i]; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInvalidPatterns); ++i) { + URLPattern pattern(URLPattern::SCHEME_ALL); + EXPECT_EQ(kInvalidPatterns[i].expected_result, + pattern.Parse(kInvalidPatterns[i].pattern)) + << kInvalidPatterns[i].pattern; } }; // all pages for a given scheme TEST(URLPatternTest, Match1) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("http://*/*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*/*")); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -55,7 +60,7 @@ TEST(URLPatternTest, Match1) { // all domains TEST(URLPatternTest, Match2) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("https://*/foo*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("https://*/foo*")); EXPECT_EQ("https", pattern.scheme()); EXPECT_EQ("", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -70,7 +75,8 @@ TEST(URLPatternTest, Match2) { // subdomains TEST(URLPatternTest, Match3) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("http://*.google.com/foo*bar")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://*.google.com/foo*bar")); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("google.com", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -86,7 +92,8 @@ TEST(URLPatternTest, Match3) { // glob escaping TEST(URLPatternTest, Match5) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("file:///foo?bar\\*baz")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("file:///foo?bar\\*baz")); EXPECT_EQ("file", pattern.scheme()); EXPECT_EQ("", pattern.host()); EXPECT_FALSE(pattern.match_subdomains()); @@ -99,7 +106,8 @@ TEST(URLPatternTest, Match5) { // ip addresses TEST(URLPatternTest, Match6) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("http://127.0.0.1/*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://127.0.0.1/*")); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("127.0.0.1", pattern.host()); EXPECT_FALSE(pattern.match_subdomains()); @@ -111,7 +119,8 @@ TEST(URLPatternTest, Match6) { // subdomain matching with ip addresses TEST(URLPatternTest, Match7) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("http://*.0.0.1/*")); // allowed, but useless + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://*.0.0.1/*")); // allowed, but useless EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("0.0.1", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -126,7 +135,8 @@ TEST(URLPatternTest, Match8) { URLPattern pattern(kAllSchemes); // The below is the ASCII encoding of the following URL: // http://*.\xe1\x80\xbf/a\xc2\x81\xe1* - EXPECT_TRUE(pattern.Parse("http://*.xn--gkd/a%C2%81%E1*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://*.xn--gkd/a%C2%81%E1*")); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("xn--gkd", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -141,7 +151,8 @@ TEST(URLPatternTest, Match8) { // chrome:// TEST(URLPatternTest, Match9) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("chrome://favicon/*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("chrome://favicon/*")); EXPECT_EQ("chrome", pattern.scheme()); EXPECT_EQ("favicon", pattern.host()); EXPECT_FALSE(pattern.match_subdomains()); @@ -155,7 +166,7 @@ TEST(URLPatternTest, Match9) { // *:// TEST(URLPatternTest, Match10) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("*://*/*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("*://*/*")); EXPECT_TRUE(pattern.MatchesScheme("http")); EXPECT_TRUE(pattern.MatchesScheme("https")); EXPECT_FALSE(pattern.MatchesScheme("chrome")); @@ -172,7 +183,7 @@ TEST(URLPatternTest, Match10) { // <all_urls> TEST(URLPatternTest, Match11) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("<all_urls>")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("<all_urls>")); EXPECT_TRUE(pattern.MatchesScheme("chrome")); EXPECT_TRUE(pattern.MatchesScheme("http")); EXPECT_TRUE(pattern.MatchesScheme("https")); @@ -188,7 +199,7 @@ TEST(URLPatternTest, Match11) { // SCHEME_ALL matches all schemes. TEST(URLPatternTest, Match12) { URLPattern pattern(URLPattern::SCHEME_ALL); - EXPECT_TRUE(pattern.Parse("<all_urls>")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("<all_urls>")); EXPECT_TRUE(pattern.MatchesScheme("chrome")); EXPECT_TRUE(pattern.MatchesScheme("http")); EXPECT_TRUE(pattern.MatchesScheme("https")); @@ -226,7 +237,8 @@ static const struct MatchPatterns { TEST(URLPatternTest, Match13) { for (size_t i = 0; i < arraysize(kMatch13UrlPatternTestCases); ++i) { URLPattern pattern(URLPattern::SCHEME_ALL); - EXPECT_TRUE(pattern.Parse(kMatch13UrlPatternTestCases[i].pattern)) + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse(kMatch13UrlPatternTestCases[i].pattern)) << " while parsing " << kMatch13UrlPatternTestCases[i].pattern; EXPECT_TRUE(pattern.MatchesUrl( GURL(kMatch13UrlPatternTestCases[i].matches))) @@ -235,7 +247,7 @@ TEST(URLPatternTest, Match13) { // Negative test. URLPattern pattern(URLPattern::SCHEME_ALL); - EXPECT_TRUE(pattern.Parse("data:*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("data:*")); EXPECT_FALSE(pattern.MatchesUrl(GURL("about:blank"))); }; @@ -259,7 +271,8 @@ static const struct GetAsStringPatterns { TEST(URLPatternTest, GetAsString) { for (size_t i = 0; i < arraysize(kGetAsStringTestCases); ++i) { URLPattern pattern(URLPattern::SCHEME_ALL); - EXPECT_TRUE(pattern.Parse(kGetAsStringTestCases[i].pattern)); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse(kGetAsStringTestCases[i].pattern)); EXPECT_STREQ(kGetAsStringTestCases[i].pattern, pattern.GetAsString().c_str()); } diff --git a/chrome/common/extensions/user_script.cc b/chrome/common/extensions/user_script.cc index cea7cf6..f71de5c 100644 --- a/chrome/common/extensions/user_script.cc +++ b/chrome/common/extensions/user_script.cc @@ -195,7 +195,7 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) { std::string pattern_str; URLPattern pattern(valid_schemes); CHECK(pickle.ReadString(iter, &pattern_str)); - CHECK(pattern.Parse(pattern_str)); + CHECK(URLPattern::PARSE_SUCCESS == pattern.Parse(pattern_str)); url_patterns_.push_back(pattern); } diff --git a/chrome/common/extensions/user_script_unittest.cc b/chrome/common/extensions/user_script_unittest.cc index ab3e218..949ad1a 100644 --- a/chrome/common/extensions/user_script_unittest.cc +++ b/chrome/common/extensions/user_script_unittest.cc @@ -71,7 +71,7 @@ TEST(UserScriptTest, Match5) { TEST(UserScriptTest, Match6) { URLPattern pattern(kAllSchemes); - ASSERT_TRUE(pattern.Parse("http://*/foo*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*/foo*")); UserScript script; script.add_url_pattern(pattern); @@ -86,7 +86,8 @@ TEST(UserScriptTest, UrlPatternGlobInteraction) { UserScript script; URLPattern pattern(kAllSchemes); - ASSERT_TRUE(pattern.Parse("http://www.google.com/*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://www.google.com/*")); script.add_url_pattern(pattern); script.add_glob("*bar*"); @@ -114,8 +115,8 @@ TEST(UserScriptTest, UrlPatternGlobInteraction) { TEST(UserScriptTest, Pickle) { URLPattern pattern1(kAllSchemes); URLPattern pattern2(kAllSchemes); - ASSERT_TRUE(pattern1.Parse("http://*/foo*")); - ASSERT_TRUE(pattern2.Parse("http://bar/baz*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern1.Parse("http://*/foo*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern2.Parse("http://bar/baz*")); UserScript script1; script1.js_scripts().push_back(UserScript::File( diff --git a/chrome/common/file_system/file_system_dispatcher.cc b/chrome/common/file_system/file_system_dispatcher.cc index dfe23bf..352deb3 100644 --- a/chrome/common/file_system/file_system_dispatcher.cc +++ b/chrome/common/file_system/file_system_dispatcher.cc @@ -41,10 +41,11 @@ bool FileSystemDispatcher::OnMessageReceived(const IPC::Message& msg) { bool FileSystemDispatcher::OpenFileSystem( const GURL& origin_url, fileapi::FileSystemType type, - long long size, fileapi::FileSystemCallbackDispatcher* dispatcher) { + long long size, bool create, + fileapi::FileSystemCallbackDispatcher* dispatcher) { int request_id = dispatchers_.Add(dispatcher); if (!ChildThread::current()->Send(new ViewHostMsg_OpenFileSystemRequest( - request_id, origin_url, type, size))) { + request_id, origin_url, type, size, create))) { dispatchers_.Remove(request_id); // destroys |dispatcher| return false; } @@ -247,7 +248,7 @@ void FileSystemDispatcher::DidReadMetadata( void FileSystemDispatcher::DidReadDirectory( int request_id, - const std::vector<base::file_util_proxy::Entry>& entries, + const std::vector<base::FileUtilProxy::Entry>& entries, bool has_more) { fileapi::FileSystemCallbackDispatcher* dispatcher = dispatchers_.Lookup(request_id); diff --git a/chrome/common/file_system/file_system_dispatcher.h b/chrome/common/file_system/file_system_dispatcher.h index f256189..b8ffb2c 100644 --- a/chrome/common/file_system/file_system_dispatcher.h +++ b/chrome/common/file_system/file_system_dispatcher.h @@ -35,6 +35,7 @@ class FileSystemDispatcher { bool OpenFileSystem(const GURL& origin_url, fileapi::FileSystemType type, long long size, + bool create, fileapi::FileSystemCallbackDispatcher* dispatcher); bool Move(const FilePath& src_path, const FilePath& dest_path, @@ -87,7 +88,7 @@ class FileSystemDispatcher { const base::PlatformFileInfo& file_info); void DidReadDirectory( int request_id, - const std::vector<base::file_util_proxy::Entry>& entries, + const std::vector<base::FileUtilProxy::Entry>& entries, bool has_more); void DidFail(int request_id, base::PlatformFileError error_code); void DidWrite(int request_id, int64 bytes, bool complete); diff --git a/chrome/common/file_system/webfilesystem_callback_dispatcher.cc b/chrome/common/file_system/webfilesystem_callback_dispatcher.cc index bf8d901..6d779a2 100644 --- a/chrome/common/file_system/webfilesystem_callback_dispatcher.cc +++ b/chrome/common/file_system/webfilesystem_callback_dispatcher.cc @@ -41,7 +41,7 @@ void WebFileSystemCallbackDispatcher::DidReadMetadata( } void WebFileSystemCallbackDispatcher::DidReadDirectory( - const std::vector<base::file_util_proxy::Entry>& entries, bool has_more) { + const std::vector<base::FileUtilProxy::Entry>& entries, bool has_more) { WebVector<WebFileSystemEntry> file_system_entries(entries.size()); for (size_t i = 0; i < entries.size(); i++) { file_system_entries[i].name = diff --git a/chrome/common/file_system/webfilesystem_callback_dispatcher.h b/chrome/common/file_system/webfilesystem_callback_dispatcher.h index 075d5bb..dc570fa 100644 --- a/chrome/common/file_system/webfilesystem_callback_dispatcher.h +++ b/chrome/common/file_system/webfilesystem_callback_dispatcher.h @@ -9,12 +9,6 @@ #include "base/platform_file.h" #include "webkit/fileapi/file_system_callback_dispatcher.h" -namespace base { -namespace file_util_proxy { -struct Entry; -} -} - namespace WebKit { class WebFileSystemCallbacks; } @@ -29,7 +23,7 @@ class WebFileSystemCallbackDispatcher virtual void DidSucceed(); virtual void DidReadMetadata(const base::PlatformFileInfo& file_info); virtual void DidReadDirectory( - const std::vector<base::file_util_proxy::Entry>& entries, + const std::vector<base::FileUtilProxy::Entry>& entries, bool has_more); virtual void DidOpenFileSystem(const std::string&, const FilePath&); diff --git a/chrome/common/file_system/webfilewriter_impl.cc b/chrome/common/file_system/webfilewriter_impl.cc index 2f3677b..7fc2656 100644 --- a/chrome/common/file_system/webfilewriter_impl.cc +++ b/chrome/common/file_system/webfilewriter_impl.cc @@ -12,7 +12,6 @@ namespace { inline FileSystemDispatcher* GetFileSystemDispatcher() { return ChildThread::current()->file_system_dispatcher(); } - } class WebFileWriterImpl::CallbackDispatcher @@ -28,7 +27,7 @@ class WebFileWriterImpl::CallbackDispatcher NOTREACHED(); } virtual void DidReadDirectory( - const std::vector<base::file_util_proxy::Entry>& entries, + const std::vector<base::FileUtilProxy::Entry>& entries, bool has_more) { NOTREACHED(); } @@ -78,4 +77,3 @@ void WebFileWriterImpl::DoCancel() { GetFileSystemDispatcher()->Cancel(request_id_, new CallbackDispatcher(AsWeakPtr())); } - diff --git a/chrome/common/font_loader_mac.mm b/chrome/common/font_loader_mac.mm index a5bb781..3ab57c4 100644 --- a/chrome/common/font_loader_mac.mm +++ b/chrome/common/font_loader_mac.mm @@ -65,16 +65,11 @@ bool FontLoader::LoadFontIntoBuffer(NSFont* font_to_encode, } int32 font_file_size_32 = static_cast<int32>(font_file_size_64); - if (!font_data->Create("", false, false, font_file_size_32)) { + if (!font_data->CreateAndMapAnonymous(font_file_size_32)) { LOG(ERROR) << "Failed to create shmem area for " << font_name; return false; } - if (!font_data->Map(font_file_size_32)) { - LOG(ERROR) << "Failed to map shmem area for " << font_name; - return false; - } - int32 amt_read = file_util::ReadFile(font_path, reinterpret_cast<char*>(font_data->memory()), font_file_size_32); diff --git a/chrome/common/gpu_create_command_buffer_config.cc b/chrome/common/gpu_create_command_buffer_config.cc new file mode 100644 index 0000000..e8ab9d7 --- /dev/null +++ b/chrome/common/gpu_create_command_buffer_config.cc @@ -0,0 +1,16 @@ +// 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/common/gpu_create_command_buffer_config.h" + +GPUCreateCommandBufferConfig::GPUCreateCommandBufferConfig() {} + +GPUCreateCommandBufferConfig::GPUCreateCommandBufferConfig( + const std::string& _allowed_extensions, + const std::vector<int>& _attribs) + : allowed_extensions(_allowed_extensions), + attribs(_attribs) { +} + +GPUCreateCommandBufferConfig::~GPUCreateCommandBufferConfig() {} diff --git a/chrome/common/gpu_create_command_buffer_config.h b/chrome/common/gpu_create_command_buffer_config.h index 8a2525f..0cb217a 100644 --- a/chrome/common/gpu_create_command_buffer_config.h +++ b/chrome/common/gpu_create_command_buffer_config.h @@ -11,14 +11,12 @@ // Parameters passed when initializing a GPU channel. struct GPUCreateCommandBufferConfig { - GPUCreateCommandBufferConfig() { } + GPUCreateCommandBufferConfig(); - GPUCreateCommandBufferConfig( - const std::string& _allowed_extensions, - const std::vector<int>& _attribs) - : allowed_extensions(_allowed_extensions), - attribs(_attribs) { - } + GPUCreateCommandBufferConfig(const std::string& _allowed_extensions, + const std::vector<int>& _attribs); + + ~GPUCreateCommandBufferConfig(); std::string allowed_extensions; std::vector<int> attribs; diff --git a/chrome/common/gpu_info.cc b/chrome/common/gpu_info.cc index 667c375..64f0498 100644 --- a/chrome/common/gpu_info.cc +++ b/chrome/common/gpu_info.cc @@ -16,6 +16,10 @@ bool GPUInfo::initialized() const { return initialized_; } +base::TimeDelta GPUInfo::initialization_time() const { + return initialization_time_; +} + uint32 GPUInfo::vendor_id() const { return vendor_id_; } @@ -45,6 +49,12 @@ bool GPUInfo::can_lose_context() const { return can_lose_context_; } +void GPUInfo::SetInitializationTime( + const base::TimeDelta& initialization_time) { + initialization_time_ = initialization_time; +} + + void GPUInfo::SetGraphicsInfo(uint32 vendor_id, uint32 device_id, const std::wstring& driver_version, uint32 pixel_shader_version, diff --git a/chrome/common/gpu_info.h b/chrome/common/gpu_info.h index 50728db..bdf8709 100644 --- a/chrome/common/gpu_info.h +++ b/chrome/common/gpu_info.h @@ -12,6 +12,7 @@ #include <string> #include "base/basictypes.h" +#include "base/time.h" #include "build/build_config.h" #include "chrome/common/dx_diag_node.h" @@ -23,6 +24,10 @@ class GPUInfo { // Returns whether this GPUInfo has been initialized with information bool initialized() const; + // The amount of time taken to get from the process starting to the message + // loop being pumped. + base::TimeDelta initialization_time() const; + // Return the DWORD (uint32) representing the graphics card vendor id. uint32 vendor_id() const; @@ -55,6 +60,8 @@ class GPUInfo { // semantics are available. bool can_lose_context() const; + void SetInitializationTime(const base::TimeDelta& initialization_time); + // Populate variables with passed in values void SetGraphicsInfo(uint32 vendor_id, uint32 device_id, const std::wstring& driver_version, @@ -72,6 +79,7 @@ class GPUInfo { private: bool initialized_; + base::TimeDelta initialization_time_; uint32 vendor_id_; uint32 device_id_; std::wstring driver_version_; diff --git a/chrome/common/gpu_messages.cc b/chrome/common/gpu_messages.cc index db7f597..671a6a6 100644 --- a/chrome/common/gpu_messages.cc +++ b/chrome/common/gpu_messages.cc @@ -10,6 +10,7 @@ #include "gfx/rect.h" #include "gfx/size.h" #include "ipc/ipc_channel_handle.h" +#include "ipc/ipc_message_utils.h" #define MESSAGES_INTERNAL_IMPL_FILE \ "chrome/common/gpu_messages_internal.h" @@ -80,6 +81,7 @@ void ParamTraits<GpuHostMsg_AcceleratedSurfaceSetIOSurface_Params> ::Log( #endif // if defined(OS_MACOSX) void ParamTraits<GPUInfo> ::Write(Message* m, const param_type& p) { + ParamTraits<base::TimeDelta> ::Write(m, p.initialization_time()); m->WriteUInt32(p.vendor_id()); m->WriteUInt32(p.device_id()); m->WriteWString(p.driver_version()); @@ -94,6 +96,7 @@ void ParamTraits<GPUInfo> ::Write(Message* m, const param_type& p) { } bool ParamTraits<GPUInfo> ::Read(const Message* m, void** iter, param_type* p) { + base::TimeDelta initialization_time; uint32 vendor_id; uint32 device_id; std::wstring driver_version; @@ -101,13 +104,15 @@ bool ParamTraits<GPUInfo> ::Read(const Message* m, void** iter, param_type* p) { uint32 vertex_shader_version; uint32 gl_version; bool can_lose_context; - bool ret = m->ReadUInt32(iter, &vendor_id); + bool ret = ParamTraits<base::TimeDelta> ::Read(m, iter, &initialization_time); + ret = ret && m->ReadUInt32(iter, &vendor_id); ret = ret && m->ReadUInt32(iter, &device_id); ret = ret && m->ReadWString(iter, &driver_version); ret = ret && m->ReadUInt32(iter, &pixel_shader_version); ret = ret && m->ReadUInt32(iter, &vertex_shader_version); ret = ret && m->ReadUInt32(iter, &gl_version); ret = ret && m->ReadBool(iter, &can_lose_context); + p->SetInitializationTime(initialization_time); p->SetGraphicsInfo(vendor_id, device_id, driver_version, @@ -126,11 +131,13 @@ bool ParamTraits<GPUInfo> ::Read(const Message* m, void** iter, param_type* p) { } void ParamTraits<GPUInfo> ::Log(const param_type& p, std::string* l) { - l->append(StringPrintf("<GPUInfo> %x %x %ls %d", - p.vendor_id(), - p.device_id(), - p.driver_version().c_str(), - p.can_lose_context())); + l->append(base::StringPrintf("<GPUInfo> %d %x %x %ls %d", + static_cast<int32>( + p.initialization_time().InMilliseconds()), + p.vendor_id(), + p.device_id(), + p.driver_version().c_str(), + p.can_lose_context())); } void ParamTraits<DxDiagNode> ::Write(Message* m, const param_type& p) { diff --git a/chrome/common/gpu_messages.h b/chrome/common/gpu_messages.h index 799bc98..f932c99 100644 --- a/chrome/common/gpu_messages.h +++ b/chrome/common/gpu_messages.h @@ -9,7 +9,6 @@ #include "base/basictypes.h" #include "base/process.h" #include "chrome/common/common_param_traits.h" -#include "chrome/common/gpu_native_window_handle.h" #include "chrome/common/gpu_param_traits.h" #include "gfx/native_widget_types.h" #include "gpu/command_buffer/common/command_buffer.h" diff --git a/chrome/common/gpu_messages_internal.h b/chrome/common/gpu_messages_internal.h index 184d8f4..1b51c03 100644 --- a/chrome/common/gpu_messages_internal.h +++ b/chrome/common/gpu_messages_internal.h @@ -47,10 +47,6 @@ IPC_BEGIN_MESSAGES(Gpu) // asynchronously.) Results in a GpuHostMsg_SynchronizeReply. IPC_MESSAGE_CONTROL0(GpuMsg_Synchronize) - IPC_MESSAGE_CONTROL2(GpuMsg_NewRenderWidgetHostView, - GpuNativeWindowHandle, /* parent window */ - int32 /* view_id */) - // Tells the GPU process to create a context for collecting graphics card // information. IPC_MESSAGE_CONTROL0(GpuMsg_CollectGraphicsInfo) @@ -61,45 +57,6 @@ IPC_BEGIN_MESSAGES(Gpu) // Tells the GPU process to hang. IPC_MESSAGE_CONTROL0(GpuMsg_Hang) - // Creates a new backing store. - IPC_MESSAGE_ROUTED2(GpuMsg_NewBackingStore, - int32, /* backing_store_routing_id */ - gfx::Size /* size */) - - // Creates a new video layer. - IPC_MESSAGE_ROUTED2(GpuMsg_NewVideoLayer, - int32, /* video_layer_routing_id */ - gfx::Size /* size */) - - // Updates the backing store with the given bitmap. The GPU process will send - // back a GpuHostMsg_PaintToBackingStore_ACK after the paint is complete to - // let the caller know the TransportDIB can be freed or reused. - IPC_MESSAGE_ROUTED4(GpuMsg_PaintToBackingStore, - base::ProcessId, /* process */ - TransportDIB::Id, /* bitmap */ - gfx::Rect, /* bitmap_rect */ - std::vector<gfx::Rect>) /* copy_rects */ - - - IPC_MESSAGE_ROUTED4(GpuMsg_ScrollBackingStore, - int, /* dx */ - int, /* dy */ - gfx::Rect, /* clip_rect */ - gfx::Size) /* view_size */ - - // Tells the GPU process that the RenderWidgetHost has painted the window. - // Depending on the platform, the accelerated content may need to be painted - // over the top. - IPC_MESSAGE_ROUTED0(GpuMsg_WindowPainted) - - // Updates the video layer with the given YUV data. The GPU process will send - // back a GpuHostMsg_PaintToVideoLayer_ACK after the paint is complete to - // let the caller know the TransportDIB can be freed or reused. - IPC_MESSAGE_ROUTED3(GpuMsg_PaintToVideoLayer, - base::ProcessId, /* process */ - TransportDIB::Id, /* bitmap */ - gfx::Rect) /* bitmap_rect */ - IPC_END_MESSAGES(Gpu) //------------------------------------------------------------------------------ @@ -107,12 +64,6 @@ IPC_END_MESSAGES(Gpu) // These are messages from the GPU process to the browser. IPC_BEGIN_MESSAGES(GpuHost) - // Sent in response to GpuMsg_PaintToBackingStore, see that for more. - IPC_MESSAGE_ROUTED0(GpuHostMsg_PaintToBackingStore_ACK) - - // Sent in response to GpuMsg_PaintToVideoLayer, see that for more. - IPC_MESSAGE_ROUTED0(GpuHostMsg_PaintToVideoLayer_ACK) - // Response to a GpuHostMsg_EstablishChannel message. IPC_MESSAGE_CONTROL2(GpuHostMsg_ChannelEstablished, IPC::ChannelHandle, /* channel_handle */ @@ -142,10 +93,11 @@ IPC_BEGIN_MESSAGES(GpuHost) // This message notifies the browser process that the renderer // swapped the buffers associated with the given "window", which // should cause the browser to redraw the compositor's contents. - IPC_MESSAGE_CONTROL3(GpuHostMsg_AcceleratedSurfaceBuffersSwapped, + IPC_MESSAGE_CONTROL4(GpuHostMsg_AcceleratedSurfaceBuffersSwapped, int32, /* renderer_id */ int32, /* render_view_id */ - gfx::PluginWindowHandle /* window */) + gfx::PluginWindowHandle /* window */, + uint64 /* surface_id */) #endif IPC_END_MESSAGES(GpuHost) diff --git a/chrome/common/gpu_messages_unittest.cc b/chrome/common/gpu_messages_unittest.cc index 455458f..59d42ed 100644 --- a/chrome/common/gpu_messages_unittest.cc +++ b/chrome/common/gpu_messages_unittest.cc @@ -13,6 +13,7 @@ TEST(GPUIPCMessageTest, GPUInfo) { GPUInfo input; // Test variables taken from Lenovo T61 + input.SetInitializationTime(base::TimeDelta::FromMilliseconds(100)); input.SetGraphicsInfo(0x10de, 0x429, L"6.14.11.7715", 0xffff0300, 0xfffe0300, @@ -25,6 +26,8 @@ TEST(GPUIPCMessageTest, GPUInfo) { GPUInfo output; void* iter = NULL; EXPECT_TRUE(IPC::ReadParam(&msg, &iter, &output)); + EXPECT_EQ(input.initialization_time().InMilliseconds(), + output.initialization_time().InMilliseconds()); EXPECT_EQ(input.vendor_id(), output.vendor_id()); EXPECT_EQ(input.device_id(), output.device_id()); EXPECT_EQ(input.driver_version(), output.driver_version()); @@ -35,5 +38,5 @@ TEST(GPUIPCMessageTest, GPUInfo) { std::string log_message; IPC::LogParam(output, &log_message); - EXPECT_STREQ("<GPUInfo> 10de 429 6.14.11.7715 1", log_message.c_str()); + EXPECT_STREQ("<GPUInfo> 100 10de 429 6.14.11.7715 1", log_message.c_str()); } diff --git a/chrome/common/gpu_native_window_handle.h b/chrome/common/gpu_native_window_handle.h deleted file mode 100644 index cdc89f7..0000000 --- a/chrome/common/gpu_native_window_handle.h +++ /dev/null @@ -1,42 +0,0 @@ -// 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_COMMON_GPU_NATIVE_WINDOW_HANDLE_H_ -#define CHROME_COMMON_GPU_NATIVE_WINDOW_HANDLE_H_ -#pragma once - -#include "build/build_config.h" - -// This file defines the window handle type used by the GPU process IPC layer. -// This is different than gfx::NativeWindow[Id] since on X, this is an XID. -// Normally, Chrome deals with either GTK window pointers, or magic window -// IDs that the app generates. The GPU process needs to know the real XID. - -#if defined(OS_WIN) - -#include <windows.h> - -typedef HWND GpuNativeWindowHandle; - -#elif defined(OS_MACOSX) - -// The GPU process isn't supported on Mac yet. Defining this arbitrarily allows -// us to not worry about the integration points not compiling. -typedef int GpuNativeWindowHandle; - -#elif defined(USE_X11) - -// Forward declar XID ourselves to avoid pulling in all of the X headers, which -// can cause compile problems for some parts of the project. -typedef unsigned long XID; - -typedef XID GpuNativeWindowHandle; - -#else - -#error define GpuNativeWindowHandle - -#endif - -#endif // CHROME_COMMON_GPU_NATIVE_WINDOW_HANDLE_H_ diff --git a/chrome/common/gpu_param_traits.h b/chrome/common/gpu_param_traits.h index 59885ea..2aac31a 100644 --- a/chrome/common/gpu_param_traits.h +++ b/chrome/common/gpu_param_traits.h @@ -12,7 +12,6 @@ #include "chrome/common/dx_diag_node.h" #include "chrome/common/gpu_create_command_buffer_config.h" #include "chrome/common/gpu_info.h" -#include "chrome/common/gpu_native_window_handle.h" #include "gfx/native_widget_types.h" #include "gfx/rect.h" #include "gfx/size.h" diff --git a/chrome/common/gpu_video_common.cc b/chrome/common/gpu_video_common.cc index 67466b9..9e25a5f 100644 --- a/chrome/common/gpu_video_common.cc +++ b/chrome/common/gpu_video_common.cc @@ -28,7 +28,7 @@ bool ParamTraits<GpuVideoDecoderInitParam>::Read( void ParamTraits<GpuVideoDecoderInitParam>::Log( const GpuVideoDecoderInitParam& p, std::string* l) { - l->append(StringPrintf("(%d, %d %d)", p.codec_id, p.width, p.height)); + l->append(base::StringPrintf("(%d, %d %d)", p.codec_id, p.width, p.height)); } /////////////////////////////////////////////////////////////////////////////// @@ -51,7 +51,7 @@ bool ParamTraits<GpuVideoDecoderInitDoneParam>::Read( void ParamTraits<GpuVideoDecoderInitDoneParam>::Log( const GpuVideoDecoderInitDoneParam& p, std::string* l) { - l->append(StringPrintf("(%d %d)", p.success, p.input_buffer_size)); + l->append(base::StringPrintf("(%d %d)", p.success, p.input_buffer_size)); } /////////////////////////////////////////////////////////////////////////////// @@ -74,9 +74,9 @@ bool ParamTraits<GpuVideoDecoderInputBufferParam>::Read( void ParamTraits<GpuVideoDecoderInputBufferParam>::Log( const GpuVideoDecoderInputBufferParam& p, std::string* l) { - l->append(StringPrintf("(%d %d %d)", - static_cast<int>(p.timestamp), - p.offset, p.size)); + l->append(base::StringPrintf("(%d %d %d)", + static_cast<int>(p.timestamp), + p.offset, p.size)); } /////////////////////////////////////////////////////////////////////////////// @@ -95,7 +95,7 @@ bool ParamTraits<GpuVideoDecoderErrorInfoParam>::Read( void ParamTraits<GpuVideoDecoderErrorInfoParam>::Log( const GpuVideoDecoderErrorInfoParam& p, std::string* l) { - l->append(StringPrintf("(%d)", p.error_id)); + l->append(base::StringPrintf("(%d)", p.error_id)); } /////////////////////////////////////////////////////////////////////////////// @@ -114,7 +114,7 @@ bool ParamTraits<GpuVideoDecoderFormatChangeParam>::Read( void ParamTraits<GpuVideoDecoderFormatChangeParam>::Log( const GpuVideoDecoderFormatChangeParam& p, std::string* l) { - l->append(StringPrintf("%d", p.input_buffer_size)); + l->append(base::StringPrintf("%d", p.input_buffer_size)); } /////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/common/important_file_writer.cc b/chrome/common/important_file_writer.cc index 2d9503d..94a8e57 100644 --- a/chrome/common/important_file_writer.cc +++ b/chrome/common/important_file_writer.cc @@ -6,7 +6,6 @@ #include <stdio.h> -#include <ostream> #include <string> #include "base/file_path.h" @@ -46,28 +45,28 @@ class WriteToDiskTask : public Task { size_t bytes_written = fwrite(data_.data(), 1, data_.length(), tmp_file); if (!file_util::CloseFile(tmp_file)) { - file_util::Delete(tmp_file_path, false); LogFailure("failed to close temporary file"); + file_util::Delete(tmp_file_path, false); return; } if (bytes_written < data_.length()) { - file_util::Delete(tmp_file_path, false); LogFailure("error writing, bytes_written=" + base::Uint64ToString(bytes_written)); + file_util::Delete(tmp_file_path, false); return; } if (!file_util::ReplaceFile(tmp_file_path, path_)) { - file_util::Delete(tmp_file_path, false); LogFailure("could not rename temporary file"); + file_util::Delete(tmp_file_path, false); return; } } private: void LogFailure(const std::string& message) { - LOG(WARNING) << "failed to write " << path_.value() - << ": " << message; + PLOG(WARNING) << "failed to write " << path_.value() + << ": " << message; } const FilePath path_; diff --git a/chrome/common/json_pref_store.h b/chrome/common/json_pref_store.h index 5211889..0407d1e 100644 --- a/chrome/common/json_pref_store.h +++ b/chrome/common/json_pref_store.h @@ -31,7 +31,7 @@ class JsonPrefStore : public PrefStore, // PrefStore methods: virtual bool ReadOnly() { return read_only_; } - virtual DictionaryValue* prefs() { return prefs_.get(); } + virtual DictionaryValue* prefs() const { return prefs_.get(); } virtual PrefReadError ReadPrefs(); diff --git a/chrome/common/libxml_utils.cc b/chrome/common/libxml_utils.cc index f06df72..009f0d3 100644 --- a/chrome/common/libxml_utils.cc +++ b/chrome/common/libxml_utils.cc @@ -6,7 +6,7 @@ #include "base/compiler_specific.h" #include "base/logging.h" -#include "base/string_util.h" +#include "base/stringprintf.h" #include "libxml/xmlreader.h" @@ -35,7 +35,7 @@ void XmlReader::GenericErrorCallback(void* context, const char* msg, ...) { va_start(args, msg); XmlReader* reader = static_cast<XmlReader*>(context); - reader->errors_.append(StringPrintV(msg, args)); + reader->errors_.append(base::StringPrintV(msg, args)); va_end(args); } diff --git a/chrome/common/logging_chrome.cc b/chrome/common/logging_chrome.cc index beab5b8..196c581 100644 --- a/chrome/common/logging_chrome.cc +++ b/chrome/common/logging_chrome.cc @@ -32,7 +32,7 @@ #include "base/command_line.h" #include "base/compiler_specific.h" -#include "base/debug_util.h" +#include "base/debug/debugger.h" #include "base/environment.h" #include "base/file_path.h" #include "base/file_util.h" @@ -64,8 +64,9 @@ static bool chrome_logging_redirected_ = false; #if defined(OS_WIN) // {7FE69228-633E-4f06-80C1-527FEA23E3A7} -DEFINE_GUID(kChromeTraceProviderName, - 0x7fe69228, 0x633e, 0x4f06, 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7); +static const GUID kChromeTraceProviderName = { + 0x7fe69228, 0x633e, 0x4f06, + { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } }; #endif // Assertion handler for logging errors that occur when dialogs are @@ -73,7 +74,7 @@ DEFINE_GUID(kChromeTraceProviderName, // with that error in the str parameter. MSVC_DISABLE_OPTIMIZE(); static void SilentRuntimeAssertHandler(const std::string& str) { - DebugUtil::BreakDebugger(); + base::debug::BreakDebugger(); } static void SilentRuntimeReportHandler(const std::string& str) { } @@ -135,47 +136,106 @@ LoggingDestination DetermineLogMode(const CommandLine& command_line) { } #if defined(OS_CHROMEOS) -void SetUpSymlink(const FilePath& symlink_path, const FilePath& new_log_path) { - // We don't care if the unlink fails; we're going to continue anyway. - if (unlink(symlink_path.value().c_str()) == -1) - PLOG(WARNING) << "Unable to unlink " << symlink_path.value(); - if (symlink(new_log_path.value().c_str(), - symlink_path.value().c_str()) == -1) { - PLOG(ERROR) << "Unable to create symlink " << symlink_path.value() - << " pointing at " << new_log_path.value(); +namespace { +FilePath GenerateTimestampedName(const FilePath& base_path, + base::Time timestamp) { + base::Time::Exploded time_deets; + timestamp.LocalExplode(&time_deets); + std::string suffix = base::StringPrintf("_%02d%02d%02d-%02d%02d%02d", + time_deets.year, + time_deets.month, + time_deets.day_of_month, + time_deets.hour, + time_deets.minute, + time_deets.second); + return base_path.InsertBeforeExtension(suffix); +} + +FilePath SetUpSymlinkIfNeeded(const FilePath& symlink_path, bool new_log) { + DCHECK(!symlink_path.empty()); + + // If not starting a new log, then just log through the existing + // symlink, but if the symlink doesn't exist, create it. If + // starting a new log, then delete the old symlink and make a new + // one to a fresh log file. + FilePath target_path; + bool symlink_exists = file_util::PathExists(symlink_path); + if (new_log || !symlink_exists) { + target_path = GenerateTimestampedName(symlink_path, base::Time::Now()); + + // We don't care if the unlink fails; we're going to continue anyway. + if (unlink(symlink_path.value().c_str()) == -1) { + if (symlink_exists) // only warn if we might expect it to succeed. + PLOG(WARNING) << "Unable to unlink " << symlink_path.value(); + } + if (symlink(target_path.value().c_str(), + symlink_path.value().c_str()) == -1) { + PLOG(ERROR) << "Unable to create symlink " << symlink_path.value() + << " pointing at " << target_path.value(); + } + } else { + char buf[PATH_MAX]; + ssize_t count = readlink(symlink_path.value().c_str(), buf, arraysize(buf)); + if (count > 0) { + target_path = FilePath(FilePath::StringType(buf, count)); + } else { + PLOG(ERROR) << "Unable to read symlink " << symlink_path.value(); + } } + return target_path; } -FilePath TimestampLog(const FilePath& new_log_file, base::Time timestamp) { - base::Time::Exploded time_deets; - timestamp.LocalExplode(&time_deets); - std::string suffix = StringPrintf("_%02d%02d%02d-%02d%02d%02d", - time_deets.year, - time_deets.month, - time_deets.day_of_month, - time_deets.hour, - time_deets.minute, - time_deets.second); - FilePath new_log_path = new_log_file.InsertBeforeExtension(suffix); - SetUpSymlink(new_log_file, new_log_path); - - return new_log_path; +void RemoveSymlinkAndLog(const FilePath& link_path, + const FilePath& target_path) { + if (::unlink(link_path.value().c_str()) == -1) + PLOG(WARNING) << "Unable to unlink symlink " << link_path.value(); + if (::unlink(target_path.value().c_str()) == -1) + PLOG(WARNING) << "Unable to unlink log file " << target_path.value(); } -void RedirectChromeLogging(const FilePath& new_log_dir, - const CommandLine& command_line, - OldFileDeletionState delete_old_log_file) { +} // anonymous namespace + +FilePath GetSessionLogFile(const CommandLine& command_line) { + FilePath log_dir; + std::string log_dir_str; + scoped_ptr<base::Environment> env(base::Environment::Create()); + if (env->GetVar(env_vars::kSessionLogDir, &log_dir_str) && + !log_dir_str.empty()) { + log_dir = FilePath(log_dir_str); + } else { + PathService::Get(chrome::DIR_USER_DATA, &log_dir); + FilePath login_profile = + command_line.GetSwitchValuePath(switches::kLoginProfile); + log_dir = log_dir.Append(login_profile); + } + return log_dir.Append(GetLogFileName().BaseName()); +} + +void RedirectChromeLogging(const CommandLine& command_line) { DCHECK(!chrome_logging_redirected_) << "Attempted to redirect logging when it was already initialized."; - FilePath log_file_name = GetLogFileName().BaseName(); - FilePath new_log_path = - TimestampLog(new_log_dir.Append(log_file_name), base::Time::Now()); - InitLogging(new_log_path.value().c_str(), - DetermineLogMode(command_line), - logging::LOCK_LOG_FILE, - delete_old_log_file); - chrome_logging_redirected_ = true; + + // Redirect logs to the session log directory, if set. Otherwise + // defaults to the profile dir. + FilePath log_path = GetSessionLogFile(command_line); + + // Always force a new symlink when redirecting. + FilePath target_path = SetUpSymlinkIfNeeded(log_path, true); + + // ChromeOS always logs through the symlink, so it shouldn't be + // deleted if it already exists. + if (!InitLogging(log_path.value().c_str(), + DetermineLogMode(command_line), + logging::LOCK_LOG_FILE, + logging::APPEND_TO_OLD_LOG_FILE)) { + LOG(ERROR) << "Unable to initialize logging to " << log_path.value(); + RemoveSymlinkAndLog(log_path, target_path); + } else { + chrome_logging_redirected_ = true; + } } + + #endif void InitChromeLogging(const CommandLine& command_line, @@ -188,21 +248,54 @@ void InitChromeLogging(const CommandLine& command_line, #endif FilePath log_path = GetLogFileName(); + #if defined(OS_CHROMEOS) - log_path = TimestampLog(log_path, base::Time::Now()); + // For BWSI (Incognito) logins, we want to put the logs in the user + // profile directory that is created for the temporary session instead + // of in the system log directory, for privacy reasons. + if (command_line.HasSwitch(switches::kGuestSession)) + log_path = GetSessionLogFile(command_line); + + // On ChromeOS we log to the symlink. We force creation of a new + // symlink if we've been asked to delete the old log, since that + // indicates the start of a new session. + FilePath target_path = SetUpSymlinkIfNeeded( + log_path, delete_old_log_file == logging::DELETE_OLD_LOG_FILE); + + // Because ChromeOS manages the move to a new session by redirecting + // the link, it shouldn't remove the old file in the logging code, + // since that will remove the newly created link instead. + delete_old_log_file = logging::APPEND_TO_OLD_LOG_FILE; #endif - logging::InitLogging(log_path.value().c_str(), - DetermineLogMode(command_line), - logging::LOCK_LOG_FILE, - delete_old_log_file); + bool success = InitLogging(log_path.value().c_str(), + DetermineLogMode(command_line), + logging::LOCK_LOG_FILE, + delete_old_log_file); + +#if defined(OS_CHROMEOS) + if (!success) { + PLOG(ERROR) << "Unable to initialize logging to " << log_path.value() + << " (which should be a link to " << target_path.value() << ")"; + RemoveSymlinkAndLog(log_path, target_path); + return; + } +#else + if (!success) { + PLOG(ERROR) << "Unable to initialize logging to " << log_path.value(); + return; + } +#endif // Default to showing error dialogs. if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoErrorDialogs)) logging::SetShowErrorDialogs(true); // we want process and thread IDs because we have a lot of things running - logging::SetLogItems(true, true, false, true); + logging::SetLogItems(true, // enable_process_id + true, // enable_thread_id + false, // enable_timestamp + true); // enable_tickcount // We call running in unattended mode "headless", and allow // headless mode to be configured either by the Environment diff --git a/chrome/common/logging_chrome.h b/chrome/common/logging_chrome.h index a2d62d5..6fc1389 100644 --- a/chrome/common/logging_chrome.h +++ b/chrome/common/logging_chrome.h @@ -37,9 +37,11 @@ void InitChromeLogging(const CommandLine& command_line, OldFileDeletionState delete_old_log_file); #if defined(OS_CHROMEOS) -void RedirectChromeLogging(const FilePath& new_log_dir, - const CommandLine& command_line, - OldFileDeletionState delete_old_log_file); +// Get the log file location. +FilePath GetSessionLogFile(const CommandLine& command_line); + +// Redirects chrome logging to the appropriate session log dir. +void RedirectChromeLogging(const CommandLine& command_line); #endif // Call when done using logging for Chrome. diff --git a/chrome/common/logging_chrome_uitest.cc b/chrome/common/logging_chrome_uitest.cc index 0dc44d9..96e3205 100644 --- a/chrome/common/logging_chrome_uitest.cc +++ b/chrome/common/logging_chrome_uitest.cc @@ -162,10 +162,7 @@ class RendererCrashTest : public UITest { #define EXPECTED_CRASH_CRASHES 1 #endif -#if defined(OS_WIN) -// http://crbug.com/32048 -#define Crash FLAKY_Crash -#elif defined(OS_CHROMEOS) +#if defined(OS_CHROMEOS) // http://crbug.com/43115 #define Crash DISABLED_Crash #elif defined(OS_MACOSX) diff --git a/chrome/common/metrics_helpers.cc b/chrome/common/metrics_helpers.cc index 6dd0a42..e25355c 100644 --- a/chrome/common/metrics_helpers.cc +++ b/chrome/common/metrics_helpers.cc @@ -484,9 +484,25 @@ void MetricsServiceBase::RecordHistogram(const Histogram& histogram) { // Get up-to-date snapshot of sample stats. Histogram::SampleSet snapshot; histogram.SnapshotSample(&snapshot); - const std::string& histogram_name = histogram.histogram_name(); + int corruption = histogram.FindCorruption(snapshot); + if (corruption) { + NOTREACHED(); + // Don't send corrupt data to metrics survices. + UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesBrowser", + corruption, Histogram::NEVER_EXCEEDED_VALUE); + typedef std::map<std::string, int> ProblemMap; + static ProblemMap* inconsistencies = new ProblemMap; + int old_corruption = (*inconsistencies)[histogram_name]; + if (old_corruption == (corruption | old_corruption)) + return; // We've already seen this corruption for this histogram. + (*inconsistencies)[histogram_name] |= corruption; + UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesBrowserUnique", + corruption, Histogram::NEVER_EXCEEDED_VALUE); + return; + } + // Find the already sent stats, or create an empty set. LoggedSampleMap::iterator it = logged_samples_.find(histogram_name); Histogram::SampleSet* already_logged; diff --git a/chrome/common/net/gaia/gaia_authenticator2.cc b/chrome/common/net/gaia/gaia_authenticator2.cc index 108d76c..21b8190 100644 --- a/chrome/common/net/gaia/gaia_authenticator2.cc +++ b/chrome/common/net/gaia/gaia_authenticator2.cc @@ -11,6 +11,7 @@ #include "base/string_split.h" #include "base/string_util.h" #include "chrome/common/net/gaia/gaia_auth_consumer.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/net/gaia/google_service_auth_error.h" #include "chrome/common/net/http_return.h" #include "chrome/common/net/url_request_context_getter.h" @@ -42,7 +43,7 @@ const char GaiaAuthenticator2::kIssueAuthTokenFormat[] = "SID=%s&" "LSID=%s&" "service=%s&" - "Session=true"; + "Session=%s"; // static const char GaiaAuthenticator2::kGetUserInfoFormat[] = "LSID=%s"; @@ -149,24 +150,24 @@ std::string GaiaAuthenticator2::MakeClientLoginBody( kAccountTypeGoogle; if (login_token.empty() || login_captcha.empty()) { - return StringPrintf(kClientLoginFormat, - encoded_username.c_str(), - encoded_password.c_str(), - kCookiePersistence, - account_type, - source.c_str(), - service); + return base::StringPrintf(kClientLoginFormat, + encoded_username.c_str(), + encoded_password.c_str(), + kCookiePersistence, + account_type, + source.c_str(), + service); } - return StringPrintf(kClientLoginCaptchaFormat, - encoded_username.c_str(), - encoded_password.c_str(), - kCookiePersistence, - account_type, - source.c_str(), - service, - encoded_login_token.c_str(), - encoded_login_captcha.c_str()); + return base::StringPrintf(kClientLoginCaptchaFormat, + encoded_username.c_str(), + encoded_password.c_str(), + kCookiePersistence, + account_type, + source.c_str(), + service, + encoded_login_token.c_str(), + encoded_login_captcha.c_str()); } @@ -178,16 +179,22 @@ std::string GaiaAuthenticator2::MakeIssueAuthTokenBody( std::string encoded_sid = UrlEncodeString(sid); std::string encoded_lsid = UrlEncodeString(lsid); - return StringPrintf(kIssueAuthTokenFormat, - encoded_sid.c_str(), - encoded_lsid.c_str(), - service); + // All tokens should be session tokens except the gaia auth token. + bool session = true; + if (!strcmp(service, GaiaConstants::kGaiaService)) + session = false; + + return base::StringPrintf(kIssueAuthTokenFormat, + encoded_sid.c_str(), + encoded_lsid.c_str(), + service, + session ? "true" : "false"); } // static std::string GaiaAuthenticator2::MakeGetUserInfoBody(const std::string& lsid) { std::string encoded_lsid = UrlEncodeString(lsid); - return StringPrintf(kGetUserInfoFormat, encoded_lsid.c_str()); + return base::StringPrintf(kGetUserInfoFormat, encoded_lsid.c_str()); } // Helper method that extracts tokens from a successful reply. diff --git a/chrome/common/net/gaia/gaia_authenticator2_unittest.cc b/chrome/common/net/gaia/gaia_authenticator2_unittest.cc index 44d1a17..6ef0a51 100644 --- a/chrome/common/net/gaia/gaia_authenticator2_unittest.cc +++ b/chrome/common/net/gaia/gaia_authenticator2_unittest.cc @@ -245,8 +245,8 @@ TEST_F(GaiaAuthenticator2Test, WorkingIssueAuthToken) { TEST_F(GaiaAuthenticator2Test, CheckTwoFactorResponse) { std::string response = - StringPrintf("Error=BadAuthentication\n%s\n", - GaiaAuthenticator2::kSecondFactor); + base::StringPrintf("Error=BadAuthentication\n%s\n", + GaiaAuthenticator2::kSecondFactor); EXPECT_TRUE(GaiaAuthenticator2::IsSecondFactorSuccess(response)); } @@ -256,7 +256,7 @@ TEST_F(GaiaAuthenticator2Test, CheckNormalErrorCode) { } TEST_F(GaiaAuthenticator2Test, TwoFactorLogin) { - std::string response = StringPrintf("Error=BadAuthentication\n%s\n", + std::string response = base::StringPrintf("Error=BadAuthentication\n%s\n", GaiaAuthenticator2::kSecondFactor); GoogleServiceAuthError error = diff --git a/chrome/common/net/gaia/gaia_constants.cc b/chrome/common/net/gaia/gaia_constants.cc index 810055e..addd468 100644 --- a/chrome/common/net/gaia/gaia_constants.cc +++ b/chrome/common/net/gaia/gaia_constants.cc @@ -12,6 +12,8 @@ namespace GaiaConstants { const char kChromeOSSource[] = "chromeos"; const char kChromeSource[] = "ChromiumBrowser"; +// Service name for Gaia. Used to convert to cookie auth. +const char kGaiaService[] = "gaia"; // Service name for Gaia Contacts API. API is used to get user's image. const char kContactsService[] = "cp"; // Service name for sync. @@ -22,5 +24,7 @@ const char kTalkService[] = "talk"; const char kRemotingService[] = "chromoting"; // Service name for cloud print. const char kCloudPrintService[] = "cloudprint"; +// Service name for device management (cloud-based policy) server. +const char kDeviceManagementService[] = "mobilesync"; } // namespace GaiaConstants diff --git a/chrome/common/net/gaia/gaia_constants.h b/chrome/common/net/gaia/gaia_constants.h index b1a2617..d0f3b10 100644 --- a/chrome/common/net/gaia/gaia_constants.h +++ b/chrome/common/net/gaia/gaia_constants.h @@ -14,11 +14,13 @@ extern const char kChromeOSSource[]; extern const char kChromeSource[]; // Gaia services for requesting +extern const char kGaiaService[]; // uber token extern const char kContactsService[]; extern const char kTalkService[]; extern const char kSyncService[]; extern const char kRemotingService[]; extern const char kCloudPrintService[]; +extern const char kDeviceManagementService[]; } // namespace GaiaConstants diff --git a/chrome/common/net/gaia/google_service_auth_error.h b/chrome/common/net/gaia/google_service_auth_error.h index fa6f76b..032f59f 100644 --- a/chrome/common/net/gaia/google_service_auth_error.h +++ b/chrome/common/net/gaia/google_service_auth_error.h @@ -71,6 +71,10 @@ class GoogleServiceAuthError { // The requestor of the authentication step cancelled the request // prior to completion. REQUEST_CANCELED = 9, + + // The user has provided a HOSTED account, when this service requires + // a GOOGLE account. + HOSTED_NOT_ALLOWED = 10, }; // Additional data for CAPTCHA_REQUIRED errors. diff --git a/chrome/common/net/raw_host_resolver_proc.cc b/chrome/common/net/raw_host_resolver_proc.cc new file mode 100644 index 0000000..474afa2 --- /dev/null +++ b/chrome/common/net/raw_host_resolver_proc.cc @@ -0,0 +1,28 @@ +// 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/common/net/raw_host_resolver_proc.h" + +#include "base/logging.h" +#include "net/base/net_errors.h" + +namespace chrome_common_net { + +RawHostResolverProc::RawHostResolverProc(const net::IPAddressNumber& dns_server, + net::HostResolverProc* previous) + : HostResolverProc(previous), dns_server_(dns_server) {} + +int RawHostResolverProc::Resolve(const std::string& host, + net::AddressFamily address_family, + net::HostResolverFlags host_resolver_flags, + net::AddressList* addrlist, + int* os_error) { + // TODO(agayev): Implement raw DNS resolution. + LOG(INFO) << "trying to resolve " << host; + return net::ERR_NAME_NOT_RESOLVED; +} + +RawHostResolverProc::~RawHostResolverProc() {} + +} // namespace chrome_common_net diff --git a/chrome/common/net/raw_host_resolver_proc.h b/chrome/common/net/raw_host_resolver_proc.h new file mode 100644 index 0000000..7ec751a --- /dev/null +++ b/chrome/common/net/raw_host_resolver_proc.h @@ -0,0 +1,40 @@ +// 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_COMMON_NET_RAW_HOST_RESOLVER_PROC_H_ +#define CHROME_COMMON_NET_RAW_HOST_RESOLVER_PROC_H_ +#pragma once + +// RawHostResolverProc will eventually be a getaddrinfo() replacement. It +// will construct and send DNS queries to the DNS server specified via +// --dns-server flag and will parse the responses and put it into a cache +// together with the TTL. Necessary amendments will be made to cache and +// HostResolverProc interface to accomodate these. + +#include <string> + +#include "net/base/host_resolver_proc.h" +#include "net/base/net_util.h" + +namespace chrome_common_net { + +class RawHostResolverProc : public net::HostResolverProc { + public: + RawHostResolverProc(const net::IPAddressNumber& dns_server, + net::HostResolverProc* previous); + + virtual int Resolve(const std::string& host, + net::AddressFamily address_family, + net::HostResolverFlags host_resolver_flags, + net::AddressList* addrlist, + int* os_error); + private: + virtual ~RawHostResolverProc(); + + net::IPAddressNumber dns_server_; +}; + +} // namespace chrome_common_net + +#endif // CHROME_COMMON_NET_RAW_HOST_RESOLVER_PROC_H_ diff --git a/chrome/common/net/test_url_fetcher_factory.cc b/chrome/common/net/test_url_fetcher_factory.cc index 6ab3a49..635319f 100644 --- a/chrome/common/net/test_url_fetcher_factory.cc +++ b/chrome/common/net/test_url_fetcher_factory.cc @@ -1,13 +1,15 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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/common/net/test_url_fetcher_factory.h" -TestURLFetcher::TestURLFetcher(const GURL& url, +TestURLFetcher::TestURLFetcher(int id, + const GURL& url, URLFetcher::RequestType request_type, URLFetcher::Delegate* d) : URLFetcher(url, request_type, d), + id_(id), original_url_(url) { } @@ -16,7 +18,7 @@ URLFetcher* TestURLFetcherFactory::CreateURLFetcher( const GURL& url, URLFetcher::RequestType request_type, URLFetcher::Delegate* d) { - TestURLFetcher* fetcher = new TestURLFetcher(url, request_type, d); + TestURLFetcher* fetcher = new TestURLFetcher(id, url, request_type, d); fetchers_[id] = fetcher; return fetcher; } @@ -25,3 +27,9 @@ TestURLFetcher* TestURLFetcherFactory::GetFetcherByID(int id) const { Fetchers::const_iterator i = fetchers_.find(id); return i == fetchers_.end() ? NULL : i->second; } + +void TestURLFetcherFactory::RemoveFetcherFromMap(int id) { + Fetchers::iterator i = fetchers_.find(id); + DCHECK(i != fetchers_.end()); + fetchers_.erase(i); +} diff --git a/chrome/common/net/test_url_fetcher_factory.h b/chrome/common/net/test_url_fetcher_factory.h index a831e3a..3afa19e 100644 --- a/chrome/common/net/test_url_fetcher_factory.h +++ b/chrome/common/net/test_url_fetcher_factory.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -39,14 +39,17 @@ class TestURLFetcher : public URLFetcher { public: - TestURLFetcher(const GURL& url, RequestType request_type, Delegate* d); - - // Returns the delegate installed on the URLFetcher. - Delegate* delegate() const { return URLFetcher::delegate(); } + TestURLFetcher(int id, + const GURL& url, + RequestType request_type, + Delegate* d); // Overriden to do nothing. It is assumed the caller will notify the delegate. virtual void Start() {} + // Unique ID in our factory. + int id() const { return id_; } + // URL we were created with. Because of how we're using URLFetcher url() // always returns an empty URL. Chances are you'll want to use original_url() // in your tests. @@ -55,7 +58,11 @@ class TestURLFetcher : public URLFetcher { // Returns the data uploaded on this URLFetcher. const std::string& upload_data() const { return URLFetcher::upload_data(); } + // Returns the delegate installed on the URLFetcher. + Delegate* delegate() const { return URLFetcher::delegate(); } + private: + const int id_; const GURL original_url_; DISALLOW_COPY_AND_ASSIGN(TestURLFetcher); @@ -71,8 +78,8 @@ class TestURLFetcherFactory : public URLFetcher::Factory { const GURL& url, URLFetcher::RequestType request_type, URLFetcher::Delegate* d); - TestURLFetcher* GetFetcherByID(int id) const; + void RemoveFetcherFromMap(int id); private: // Maps from id passed to create to the returned URLFetcher. diff --git a/chrome/common/net/url_fetcher.cc b/chrome/common/net/url_fetcher.cc index 4bfd8c7..9551255 100644 --- a/chrome/common/net/url_fetcher.cc +++ b/chrome/common/net/url_fetcher.cc @@ -34,7 +34,6 @@ bool URLFetcher::g_interception_enabled = false; class URLFetcher::Core : public base::RefCountedThreadSafe<URLFetcher::Core>, - public MessageLoop::DestructionObserver, public URLRequest::Delegate { public: // For POST requests, set |content_type| to the MIME type of the content @@ -58,10 +57,6 @@ class URLFetcher::Core // safe to call this multiple times. void Stop(); - // MessageLoop::DestructionObserver implementation. We are only registered as - // a DestructionObserver when |request_| exists. - virtual void WillDestroyCurrentMessageLoop(); - // URLRequest::Delegate implementation. virtual void OnResponseStarted(URLRequest* request); virtual void OnReadCompleted(URLRequest* request, int bytes_read); @@ -244,13 +239,6 @@ void URLFetcher::Core::Stop() { } } -void URLFetcher::Core::WillDestroyCurrentMessageLoop() { - CancelURLRequest(); - // Don't bother to try and notify the delegate thread portion of this object, - // since if the IO thread is shutting down, everything is shutting down, and - // we just want to avoid leaks. -} - void URLFetcher::Core::CancelAll() { g_registry.Get().CancelAll(); } @@ -308,7 +296,6 @@ void URLFetcher::Core::StartURLRequest() { CHECK(request_context_getter_); DCHECK(!request_.get()); - MessageLoop::current()->AddDestructionObserver(this); g_registry.Get().AddURLFetcherCore(this); request_.reset(new URLRequest(original_url_, this)); int flags = request_->load_flags() | load_flags_; @@ -401,7 +388,6 @@ void URLFetcher::Core::OnCompletedURLRequest(const URLRequestStatus& status) { void URLFetcher::Core::ReleaseRequest() { request_.reset(); g_registry.Get().RemoveURLFetcherCore(this); - MessageLoop::current()->RemoveDestructionObserver(this); } void URLFetcher::set_upload_data(const std::string& upload_content_type, diff --git a/chrome/common/net/url_fetcher_unittest.cc b/chrome/common/net/url_fetcher_unittest.cc index 3fc8469..466d8e6 100644 --- a/chrome/common/net/url_fetcher_unittest.cc +++ b/chrome/common/net/url_fetcher_unittest.cc @@ -5,6 +5,7 @@ #include "base/message_loop_proxy.h" #include "base/thread.h" #include "base/waitable_event.h" +#include "build/build_config.h" #include "chrome/common/chrome_plugin_lib.h" #include "chrome/common/net/url_fetcher.h" #include "chrome/common/net/url_fetcher_protect.h" @@ -14,6 +15,10 @@ #include "net/test/test_server.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_LINUX) +#include "net/ocsp/nss_ocsp.h" +#endif + using base::Time; using base::TimeDelta; @@ -34,7 +39,7 @@ class TestURLRequestContextGetter : public URLRequestContextGetter { context_ = new TestURLRequestContext(); return context_; } - virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() { + virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const { return io_message_loop_proxy_; } @@ -74,6 +79,15 @@ class URLFetcherTest : public testing::Test, public URLFetcher::Delegate { // Ensure that any plugin operations done by other tests are cleaned up. ChromePluginLib::UnloadAllPlugins(); +#if defined(OS_LINUX) + net::EnsureOCSPInit(); +#endif + } + + virtual void TearDown() { +#if defined(OS_LINUX) + net::ShutdownOCSP(); +#endif } // URLFetcher is designed to run on the main UI thread, but in our tests @@ -199,7 +213,7 @@ class CancelTestURLRequestContextGetter : public URLRequestContextGetter { } return context_; } - virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() { + virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const { return io_message_loop_proxy_; } void WaitForContextCreation() { @@ -540,8 +554,9 @@ TEST_F(URLFetcherProtectTestPassedThrough, ServerUnavailablePropagateResponse) { TEST_F(URLFetcherBadHTTPSTest, BadHTTPSTest) { - net::TestServer test_server(net::TestServer::TYPE_HTTPS_EXPIRED_CERTIFICATE, - FilePath(kDocRoot)); + net::TestServer::HTTPSOptions https_options( + net::TestServer::HTTPSOptions::CERT_EXPIRED); + net::TestServer test_server(https_options, FilePath(kDocRoot)); ASSERT_TRUE(test_server.Start()); CreateFetcher(test_server.GetURL("defaultresponse")); diff --git a/chrome/common/net/url_request_context_getter.cc b/chrome/common/net/url_request_context_getter.cc index 57feb0e..08b5368 100644 --- a/chrome/common/net/url_request_context_getter.cc +++ b/chrome/common/net/url_request_context_getter.cc @@ -14,7 +14,7 @@ URLRequestContextGetter::URLRequestContextGetter() : is_main_(false) {} URLRequestContextGetter::~URLRequestContextGetter() {} -void URLRequestContextGetter::OnDestruct() { +void URLRequestContextGetter::OnDestruct() const { scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy = GetIOMessageLoopProxy(); DCHECK(io_message_loop_proxy); diff --git a/chrome/common/net/url_request_context_getter.h b/chrome/common/net/url_request_context_getter.h index 2b6ea82..ade5e1a 100644 --- a/chrome/common/net/url_request_context_getter.h +++ b/chrome/common/net/url_request_context_getter.h @@ -33,7 +33,8 @@ class URLRequestContextGetter // Returns a MessageLoopProxy corresponding to the thread on which the // request IO happens (the thread on which the returned URLRequestContext // may be used). - virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() = 0; + virtual scoped_refptr<base::MessageLoopProxy> + GetIOMessageLoopProxy() const = 0; // Controls whether or not the URLRequestContextGetter considers itself to be // the the "main" URLRequestContextGetter. Note that each Profile will have a @@ -44,7 +45,7 @@ class URLRequestContextGetter void set_is_main(bool is_main) { is_main_ = is_main; } protected: - friend class DeleteTask<URLRequestContextGetter>; + friend class DeleteTask<const URLRequestContextGetter>; friend struct URLRequestContextGetterTraits; URLRequestContextGetter(); @@ -55,7 +56,7 @@ class URLRequestContextGetter private: // OnDestruct is meant to ensure deletion on the thread on which the request // IO happens. - void OnDestruct(); + void OnDestruct() const; // Indicates whether or not this is the default URLRequestContextGetter for // the main Profile. @@ -63,7 +64,7 @@ class URLRequestContextGetter }; struct URLRequestContextGetterTraits { - static void Destruct(URLRequestContextGetter* context_getter) { + static void Destruct(const URLRequestContextGetter* context_getter) { context_getter->OnDestruct(); } }; diff --git a/chrome/common/net/x509_certificate_model.cc b/chrome/common/net/x509_certificate_model.cc index 5f39685..0ec2bff 100644 --- a/chrome/common/net/x509_certificate_model.cc +++ b/chrome/common/net/x509_certificate_model.cc @@ -49,5 +49,41 @@ std::string ProcessIDN(const std::string& input) { input16, output16); } +std::string ProcessRawBytesWithSeparators(const unsigned char* data, + size_t data_length, + char hex_separator, + char line_separator) { + static const char kHexChars[] = "0123456789ABCDEF"; + + // Each input byte creates two output hex characters + a space or newline, + // except for the last byte. + std::string ret; + size_t kMin = 0U; + ret.reserve(std::max(kMin, data_length * 3 - 1)); + + for (size_t i = 0; i < data_length; ++i) { + unsigned char b = data[i]; + ret.push_back(kHexChars[(b >> 4) & 0xf]); + ret.push_back(kHexChars[b & 0xf]); + if (i + 1 < data_length) { + if ((i + 1) % 16 == 0) + ret.push_back(line_separator); + else + ret.push_back(hex_separator); + } + } + return ret; +} + +std::string ProcessRawBytes(const unsigned char* data, size_t data_length) { + return ProcessRawBytesWithSeparators(data, data_length, ' ', '\n'); +} + +#if defined(USE_NSS) +std::string ProcessRawBits(const unsigned char* data, size_t data_length) { + return ProcessRawBytes(data, (data_length + 7) / 8); +} +#endif // USE_NSS + } // x509_certificate_model diff --git a/chrome/common/net/x509_certificate_model.h b/chrome/common/net/x509_certificate_model.h index 351f489..3e4f14f 100644 --- a/chrome/common/net/x509_certificate_model.h +++ b/chrome/common/net/x509_certificate_model.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef NET_BASE_X509_CERTIFICATE_MODEL_H_ -#define NET_BASE_X509_CERTIFICATE_MODEL_H_ +#ifndef CHROME_COMMON_NET_X509_CERTIFICATE_MODEL_H_ +#define CHROME_COMMON_NET_X509_CERTIFICATE_MODEL_H_ #pragma once #include "net/base/cert_database.h" @@ -123,6 +123,24 @@ std::string ProcessRawBitsSignatureWrap( void RegisterDynamicOids(); +// Format a buffer as |hex_separator| separated string, with 16 bytes on each +// line separated using |line_separator|. +std::string ProcessRawBytesWithSeparators(const unsigned char* data, + size_t data_length, + char hex_separator, + char line_separator); + +// Format a buffer as a space separated string, with 16 bytes on each line. +std::string ProcessRawBytes(const unsigned char* data, + size_t data_length); + +#if defined(USE_NSS) +// Format a buffer as a space separated string, with 16 bytes on each line. +// |data_length| is the length in bits. +std::string ProcessRawBits(const unsigned char* data, + size_t data_length); +#endif // USE_NSS + } // namespace x509_certificate_model -#endif // NET_BASE_X509_CERTIFICATE_MODEL_H_ +#endif // CHROME_COMMON_NET_X509_CERTIFICATE_MODEL_H_ diff --git a/chrome/common/net/x509_certificate_model_nss.cc b/chrome/common/net/x509_certificate_model_nss.cc index da17a60..4d46174 100644 --- a/chrome/common/net/x509_certificate_model_nss.cc +++ b/chrome/common/net/x509_certificate_model_nss.cc @@ -42,7 +42,6 @@ std::string Stringize(char* nss_text, const std::string& alternative_text) { // algorithm, but given the limited uses, not worth fixing.) std::string HashCert(CERTCertificate* cert, HASH_HashType algorithm, int len) { unsigned char fingerprint[HASH_LENGTH_MAX]; - SECItem fingerprint_item; DCHECK(NULL != cert->derCert.data); DCHECK_NE(0U, cert->derCert.len); @@ -51,9 +50,7 @@ std::string HashCert(CERTCertificate* cert, HASH_HashType algorithm, int len) { SECStatus rv = HASH_HashBuf(algorithm, fingerprint, cert->derCert.data, cert->derCert.len); DCHECK_EQ(rv, SECSuccess); - fingerprint_item.data = fingerprint; - fingerprint_item.len = len; - return psm::ProcessRawBytes(&fingerprint_item); + return x509_certificate_model::ProcessRawBytes(fingerprint, len); } std::string ProcessSecAlgorithmInternal(SECAlgorithmID* algorithm_id) { @@ -293,6 +290,7 @@ void DestroyCertChain(X509Certificate::OSCertHandles* cert_handles) { for (X509Certificate::OSCertHandles::iterator i(cert_handles->begin()); i != cert_handles->end(); ++i) CERT_DestroyCertificate(*i); + cert_handles->clear(); } string GetDerString(X509Certificate::OSCertHandle cert_handle) { @@ -372,7 +370,8 @@ string ProcessSubjectPublicKeyInfo(X509Certificate::OSCertHandle cert_handle) { } string ProcessRawBitsSignatureWrap(X509Certificate::OSCertHandle cert_handle) { - return psm::ProcessRawBits(&cert_handle->signatureWrap.signature); + return ProcessRawBits(cert_handle->signatureWrap.signature.data, + cert_handle->signatureWrap.signature.len); } void RegisterDynamicOids() { diff --git a/chrome/common/net/x509_certificate_model_openssl.cc b/chrome/common/net/x509_certificate_model_openssl.cc index 57670f1..7c4836f 100644 --- a/chrome/common/net/x509_certificate_model_openssl.cc +++ b/chrome/common/net/x509_certificate_model_openssl.cc @@ -2,11 +2,42 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/common/net/x509_certificate_model.h" + +#include <openssl/obj_mac.h> +#include <openssl/sha.h> #include <openssl/x509v3.h> -#include "chrome/common/net/x509_certificate_model.h" +#include "base/logging.h" +#include "base/string_number_conversions.h" +#include "net/base/x509_openssl_util.h" + +namespace nxou = net::x509_openssl_util; + +namespace { + +std::string AlternativeWhenEmpty(const std::string& text, + const std::string& alternative) { + return text.empty() ? alternative : text; +} + +std::string GetKeyValuesFromName(X509_NAME* name) { + std::string ret; + int rdns = X509_NAME_entry_count(name) - 1; + for (int i = rdns; i >= 0; --i) { + std::string key; + std::string value; + if (!nxou::ParsePrincipalKeyAndValueByIndex(name, i, &key, &value)) + break; + ret += key; + ret += " = "; + ret += value; + ret += '\n'; + } + return ret; +} -#include "net/base/x509_certificate.h" +} // namepsace namespace x509_certificate_model { @@ -23,7 +54,9 @@ std::string GetTokenName(X509Certificate::OSCertHandle cert_handle) { } std::string GetVersion(net::X509Certificate::OSCertHandle cert_handle) { - // TODO(bulach): implement me. + unsigned long version = X509_get_version(cert_handle); + if (version != ULONG_MAX) + return base::UintToString(version + 1); return ""; } @@ -50,55 +83,70 @@ std::string GetKeyUsageString(X509Certificate::OSCertHandle cert_handle) { std::string GetSerialNumberHexified( X509Certificate::OSCertHandle cert_handle, const std::string& alternative_text) { - // TODO(bulach): implement me. - return ""; + ASN1_INTEGER* num = X509_get_serialNumber(cert_handle); + const char kSerialNumberSeparator = ':'; + std::string hex_string = ProcessRawBytesWithSeparators( + num->data, num->length, kSerialNumberSeparator, kSerialNumberSeparator); + return AlternativeWhenEmpty(hex_string, alternative_text); } std::string GetIssuerCommonName( X509Certificate::OSCertHandle cert_handle, const std::string& alternative_text) { - // TODO(bulach): implement me. - return ""; + std::string ret; + nxou::ParsePrincipalValueByNID(X509_get_issuer_name(cert_handle), + NID_commonName, &ret); + return AlternativeWhenEmpty(ret, alternative_text); } std::string GetIssuerOrgName( X509Certificate::OSCertHandle cert_handle, const std::string& alternative_text) { - // TODO(bulach): implement me. - return ""; + std::string ret; + nxou::ParsePrincipalValueByNID(X509_get_issuer_name(cert_handle), + NID_organizationName, &ret); + return AlternativeWhenEmpty(ret, alternative_text); } std::string GetIssuerOrgUnitName( X509Certificate::OSCertHandle cert_handle, const std::string& alternative_text) { - // TODO(bulach): implement me. - return ""; + std::string ret; + nxou::ParsePrincipalValueByNID(X509_get_issuer_name(cert_handle), + NID_organizationalUnitName, &ret); + return AlternativeWhenEmpty(ret, alternative_text); } std::string GetSubjectOrgName( X509Certificate::OSCertHandle cert_handle, const std::string& alternative_text) { - // TODO(bulach): implement me. - return ""; + std::string ret; + nxou::ParsePrincipalValueByNID(X509_get_subject_name(cert_handle), + NID_organizationName, &ret); + return AlternativeWhenEmpty(ret, alternative_text); } std::string GetSubjectOrgUnitName( X509Certificate::OSCertHandle cert_handle, const std::string& alternative_text) { - // TODO(bulach): implement me. - return ""; + std::string ret; + nxou::ParsePrincipalValueByNID(X509_get_subject_name(cert_handle), + NID_organizationalUnitName, &ret); + return AlternativeWhenEmpty(ret, alternative_text); } std::string GetSubjectCommonName(X509Certificate::OSCertHandle cert_handle, const std::string& alternative_text) { - // TODO(bulach): implement me. - return ""; + std::string ret; + nxou::ParsePrincipalValueByNID(X509_get_subject_name(cert_handle), + NID_commonName, &ret); + return AlternativeWhenEmpty(ret, alternative_text); } bool GetTimes(X509Certificate::OSCertHandle cert_handle, base::Time* issued, base::Time* expires) { - // TODO(bulach): implement me. - return false; + return nxou::ParseDate(X509_get_notBefore(cert_handle), issued) && + nxou::ParseDate(X509_get_notAfter(cert_handle), expires); } std::string GetTitle(net::X509Certificate::OSCertHandle cert_handle) { @@ -107,13 +155,11 @@ std::string GetTitle(net::X509Certificate::OSCertHandle cert_handle) { } std::string GetIssuerName(net::X509Certificate::OSCertHandle cert_handle) { - // TODO(bulach): implement me. - return ""; + return GetKeyValuesFromName(X509_get_issuer_name(cert_handle)); } std::string GetSubjectName(net::X509Certificate::OSCertHandle cert_handle) { - // TODO(bulach): implement me. - return ""; + return GetKeyValuesFromName(X509_get_subject_name(cert_handle)); } void GetEmailAddresses(net::X509Certificate::OSCertHandle cert_handle, @@ -138,22 +184,34 @@ void GetExtensions( } std::string HashCertSHA256(net::X509Certificate::OSCertHandle cert_handle) { - // TODO(bulach): implement me. - return ""; + unsigned char sha256_data[SHA256_DIGEST_LENGTH] = {0}; + unsigned int sha256_size = sizeof(sha256_data); + int ret = X509_digest(cert_handle, EVP_sha256(), sha256_data, &sha256_size); + CHECK(ret); + CHECK_EQ(sha256_size, sizeof(sha256_data)); + return ProcessRawBytes(sha256_data, sha256_size); } std::string HashCertSHA1(net::X509Certificate::OSCertHandle cert_handle) { - // TODO(bulach): implement me. - return ""; + unsigned char sha1_data[SHA_DIGEST_LENGTH] = {0}; + unsigned int sha1_size = sizeof(sha1_data); + int ret = X509_digest(cert_handle, EVP_sha1(), sha1_data, &sha1_size); + CHECK(ret); + CHECK_EQ(sha1_size, sizeof(sha1_data)); + return ProcessRawBytes(sha1_data, sha1_size); } void GetCertChainFromCert(net::X509Certificate::OSCertHandle cert_handle, net::X509Certificate::OSCertHandles* cert_handles) { - // TODO(bulach): implement me. + // TODO(bulach): how to get the chain out of a certificate? + cert_handles->push_back(net::X509Certificate::DupOSCertHandle(cert_handle)); } void DestroyCertChain(net::X509Certificate::OSCertHandles* cert_handles) { - // TODO(bulach): implement me. + for (net::X509Certificate::OSCertHandles::iterator i = cert_handles->begin(); + i != cert_handles->end(); ++i) + X509_free(*i); + cert_handles->clear(); } std::string GetDerString(net::X509Certificate::OSCertHandle cert_handle) { diff --git a/chrome/common/notification_registrar.cc b/chrome/common/notification_registrar.cc index 7ec5cda..d413585 100644 --- a/chrome/common/notification_registrar.cc +++ b/chrome/common/notification_registrar.cc @@ -47,10 +47,9 @@ NotificationRegistrar::~NotificationRegistrar() { void NotificationRegistrar::Add(NotificationObserver* observer, NotificationType type, const NotificationSource& source) { - Record record = { observer, type, source, PlatformThread::CurrentId() }; + DCHECK(!IsRegistered(observer, type, source)) << "Duplicate registration."; - DCHECK(std::find(registered_.begin(), registered_.end(), record) == - registered_.end()) << "Duplicate registration."; + Record record = { observer, type, source, PlatformThread::CurrentId() }; registered_.push_back(record); NotificationService::current()->AddObserver(observer, type, source); @@ -59,16 +58,16 @@ void NotificationRegistrar::Add(NotificationObserver* observer, void NotificationRegistrar::Remove(NotificationObserver* observer, NotificationType type, const NotificationSource& source) { - Record record = { observer, type, source }; - RecordVector::iterator found = std::find( - registered_.begin(), registered_.end(), record); - if (found == registered_.end()) { + if (!IsRegistered(observer, type, source)) { NOTREACHED() << "Trying to remove unregistered observer of type " << type.value << " from list of size " << registered_.size() << "."; return; } - CheckCalledOnValidThread(found->thread_id); + Record record = { observer, type, source }; + RecordVector::iterator found = std::find( + registered_.begin(), registered_.end(), record); + CheckCalledOnValidThread(found->thread_id); registered_.erase(found); // This can be NULL if our owner outlives the NotificationService, e.g. if our @@ -106,3 +105,11 @@ void NotificationRegistrar::RemoveAll() { bool NotificationRegistrar::IsEmpty() const { return registered_.empty(); } + +bool NotificationRegistrar::IsRegistered(NotificationObserver* observer, + NotificationType type, + const NotificationSource& source) { + Record record = { observer, type, source }; + return std::find(registered_.begin(), registered_.end(), record) != + registered_.end(); +} diff --git a/chrome/common/notification_registrar.h b/chrome/common/notification_registrar.h index 1207d99..9048560 100644 --- a/chrome/common/notification_registrar.h +++ b/chrome/common/notification_registrar.h @@ -42,6 +42,12 @@ class NotificationRegistrar { // Returns true if no notifications are registered. bool IsEmpty() const; + // Returns true if there is already a registered notification with the + // specified details. + bool IsRegistered(NotificationObserver* observer, + NotificationType type, + const NotificationSource& source); + private: struct Record; diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h index 4fa44f2..ba8280d 100644 --- a/chrome/common/notification_type.h +++ b/chrome/common/notification_type.h @@ -107,13 +107,24 @@ class NotificationType { FRAME_PROVISIONAL_LOAD_START, // The provisional load for a frame was committed. The source is a - // NavigationController corresponding to the tab in which the load occured. + // NavigationController corresponding to the tab in which the load occurred. // Details is a ProvisionalLoadDetails object. In contrast to // NAV_ENTRY_COMMITTED, this notification is sent when the load was // committed, even if no navigation entry was committed (such as // AUTO_SUBFRAME navigations). FRAME_PROVISIONAL_LOAD_COMMITTED, + // The DOM for a frame was fully constructed, but referenced resources + // might not be fully loaded yet. The source is a + // Source<NavigationController> corresponding to the tab in which the load + // occurred. Details are the long long frame ID. + FRAME_DOM_CONTENT_LOADED, + + // The frame finished loading. The source is a Source<NavigationController> + // corresponding to the tab in which the load occurred. Details are the + // long long frame ID. + FRAME_DID_FINISH_LOAD, + // Content was loaded from an in-memory cache. The source will be a // Source<NavigationController> corresponding to the tab in which the load // occurred. Details in the form of a LoadFromMemoryCacheDetails object @@ -631,6 +642,10 @@ class NotificationType { // the details is history::URLsDeletedDetails that lists the deleted URLs. HISTORY_URLS_DELETED, + // Sent when a keyword search term is updated. The source is the Profile and + // the details are history::KeywordSearchTermDetails + HISTORY_KEYWORD_SEARCH_TERM_UPDATED, + // Sent by history when the favicon of a URL changes. The source is the // profile, and the details is history::FavIconChangeDetails (see // history_notifications.h). @@ -649,6 +664,12 @@ class NotificationType { // none and the source is a Profile*. PROFILE_DESTROYED, + // TopSites ---------------------------------------------------------------- + + // Sent by TopSites when it finishes loading. The source is the profile the + // details the TopSites. + TOP_SITES_LOADED, + // Thumbnails--------------------------------------------------------------- // Sent by the ThumbnailGenerator whenever a render widget host @@ -695,6 +716,10 @@ class NotificationType { // NoDetails. TEMPLATE_URL_MODEL_LOADED, + // Sent when a TemplateURL is removed from the model. The source is the + // Profile, and the details the id of the TemplateURL being removed. + TEMPLATE_URL_REMOVED, + // Notification triggered when a web application has been installed or // uninstalled. Any application view should reload its data. The source is // the profile. No details are provided. @@ -946,14 +971,28 @@ class NotificationType { // key of the entry that was affected. AUTOFILL_ENTRIES_CHANGED, + // DEPRECATED + // TODO(dhollowa): Remove this once Sync has migrated to GUID-based + // notifications. http://crbug.com/58813 // Sent when an AutoFillProfile has been added/removed/updated in the // WebDatabase. The detail is an AutofillProfileChange. AUTOFILL_PROFILE_CHANGED, + // Sent when an AutoFillProfile has been added/removed/updated in the + // WebDatabase. The detail is an AutofillProfileChangeGUID. + AUTOFILL_PROFILE_CHANGED_GUID, + + // DEPRECATED + // TODO(dhollowa): Remove this once Sync has migrated to GUID-based + // notifications. http://crbug.com/58813 // Sent when an Autofill CreditCard has been added/removed/updated in the // WebDatabase. The detail is an AutofillCreditCardChange. AUTOFILL_CREDIT_CARD_CHANGED, + // Sent when an Autofill CreditCard has been added/removed/updated in the + // WebDatabase. The detail is an AutofillCreditCardChangeGUID. + AUTOFILL_CREDIT_CARD_CHANGED_GUID, + // This notification is sent whenever the web database service has finished // loading the web database. No details are expected. WEB_DATABASE_LOADED, @@ -978,6 +1017,17 @@ class NotificationType { // expected. UPGRADE_RECOMMENDED, + // Software incompatibility notifications ---------------------------------- + + // Sent when Chrome has finished compiling the list of loaded modules (and + // other modules of interest). No details are expected. + MODULE_LIST_ENUMERATED, + + // Sent when Chrome detects an incompatible module. Details is a boolean + // specifying true if one or more confirmed bad modules were found or false + // if only suspected bad modules were found. + MODULE_INCOMPATIBILITY_DETECTED, + // Accessibility Notifications --------------------------------------------- // Notification that a window in the browser UI (not the web content) @@ -1022,15 +1072,6 @@ class NotificationType { // TabSpecificContentSettings object, there are no details. COLLECTED_COOKIES_SHOWN, - // Sent when the default geolocation setting has changed. The source is the - // GeolocationContentSettingsMap, the details are None. - GEOLOCATION_DEFAULT_CHANGED, - - // Sent when a non-default setting in the the geolocation content settings - // map has changed. The source is the GeolocationContentSettingsMap, the - // details are None. - GEOLOCATION_SETTINGS_CHANGED, - // Sent when the default setting for desktop notifications has changed. // The source is the DesktopNotificationService, the details are None. DESKTOP_NOTIFICATION_DEFAULT_CHANGED, @@ -1123,13 +1164,12 @@ class NotificationType { TOKEN_UPDATED, // Sent when a user signs into Google services such as sync. - // The source is the SigninManager instance. The details are a - // GoogleServiceSignin object. + // The source is the Profile. The details are a GoogleServiceSignin object. GOOGLE_SIGNIN_SUCCESSFUL, // Sent when a user fails to sign into Google services such as sync. - // The source is the SigninManager instance. The details are a - // GoogleServiceAuthError object. + // The source is the Profile. The details are a GoogleServiceAuthError + // object. GOOGLE_SIGNIN_FAILED, // AutoFill Notifications -------------------------------------------------- @@ -1212,6 +1252,9 @@ class NotificationType { // Sent each time the InstantController is updated. INSTANT_CONTROLLER_UPDATED, + // Sent each time the InstantController shows the InstantLoader. + INSTANT_CONTROLLER_SHOWN, + // Password Store ---------------------------------------------------------- // This notification is sent whenenever login entries stored in the password // store are changed. The detail of this notification is a list of changes @@ -1225,6 +1268,10 @@ class NotificationType { // The detail of this notification is not used. POLICY_CHANGED, + // This notification is sent whenever the device token becomes available + // that the policy subsystem uses to fetch policy from the cloud. + DEVICE_TOKEN_AVAILABLE, + // Count (must be last) ---------------------------------------------------- // Used to determine the number of notification types. Not valid as // a type parameter when registering for or posting notifications. diff --git a/chrome/common/plugin_messages_internal.h b/chrome/common/plugin_messages_internal.h index a83b41a..6cee704 100644 --- a/chrome/common/plugin_messages_internal.h +++ b/chrome/common/plugin_messages_internal.h @@ -438,7 +438,7 @@ IPC_BEGIN_MESSAGES(PluginHost) gfx::PluginWindowHandle /* window */, int32 /* width */, int32 /* height */, - uint64 /* identifier for IOSurface */) + uint64 /* surface_id */) // On the Mac, shared memory can't be allocated in the sandbox, so @@ -458,8 +458,9 @@ IPC_BEGIN_MESSAGES(PluginHost) // browser process) that the plug-in swapped the buffers associated // with the given "window", which should cause the browser to redraw // the various plug-ins' contents. - IPC_MESSAGE_ROUTED1(PluginHostMsg_AcceleratedSurfaceBuffersSwapped, - gfx::PluginWindowHandle /* window */) + IPC_MESSAGE_ROUTED2(PluginHostMsg_AcceleratedSurfaceBuffersSwapped, + gfx::PluginWindowHandle /* window */, + uint64 /* surface_id */) #endif IPC_END_MESSAGES(PluginHost) diff --git a/chrome/common/policy_constants.cc b/chrome/common/policy_constants.cc index ffe93b4..f56f2b2 100644 --- a/chrome/common/policy_constants.cc +++ b/chrome/common/policy_constants.cc @@ -50,10 +50,13 @@ const char kApplicationLocaleValue[] = "ApplicationLocaleValue"; const char kSyncDisabled[] = "SyncDisabled"; const char kExtensionInstallAllowList[] = "ExtensionInstallWhitelist"; const char kExtensionInstallDenyList[] = "ExtensionInstallBlacklist"; +const char kExtensionInstallForceList[] = "ExtensionInstallForcelist"; const char kShowHomeButton[] = "ShowHomeButton"; const char kPrintingEnabled[] = "PrintingEnabled"; const char kJavascriptEnabled[] = "JavascriptEnabled"; const char kSavingBrowserHistoryDisabled[] = "SavingBrowserHistoryDisabled"; +const char kDeveloperToolsDisabled[] = "DeveloperToolsDisabled"; +const char kBlockThirdPartyCookies[] = "BlockThirdPartyCookies"; // Chrome Frame specific policy constants const char kChromeFrameRendererSettings[] = "ChromeFrameRendererSettings"; diff --git a/chrome/common/policy_constants.h b/chrome/common/policy_constants.h index cebf36b..1bff710 100644 --- a/chrome/common/policy_constants.h +++ b/chrome/common/policy_constants.h @@ -47,10 +47,13 @@ extern const char kApplicationLocaleValue[]; extern const char kSyncDisabled[]; extern const char kExtensionInstallAllowList[]; extern const char kExtensionInstallDenyList[]; +extern const char kExtensionInstallForceList[]; extern const char kShowHomeButton[]; extern const char kPrintingEnabled[]; extern const char kJavascriptEnabled[]; extern const char kSavingBrowserHistoryDisabled[]; +extern const char kDeveloperToolsDisabled[]; +extern const char kBlockThirdPartyCookies[]; // Chrome Frame specific policy constants extern const char kChromeFrameRendererSettings[]; diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 27bc3b7..9c91912 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -212,9 +212,9 @@ const char kInstantConfirmDialogShown[] = "instant.confirm_dialog_shown"; // Boolean pref indicating if instant is enabled. const char kInstantEnabled[] = "instant.enabled"; -#if defined(USE_NSS) +#if defined(USE_NSS) || defined(USE_OPENSSL) // Prefs for SSLConfigServicePref. Currently, these are only present on -// and used by NSS-using OSes. +// and used by NSS/OpenSSL using OSes. const char kCertRevocationCheckingEnabled[] = "ssl.rev_checking.enabled"; const char kSSL2Enabled[] = "ssl.ssl2.enabled"; const char kSSL3Enabled[] = "ssl.ssl3.enabled"; @@ -397,10 +397,6 @@ const char kLabsAdvancedFilesystemEnabled[] = // A boolean pref which turns on the mediaplayer. const char kLabsMediaplayerEnabled[] = "settings.labs.mediaplayer"; -// An integer pref which maps to the extension state for Talk. -const char kLabsTalkEnabled[] = - "extensions.settings.ggnioahjipcehijkhpdjekioddnjoben.state"; - // A boolean pref that turns on screen locker. const char kEnableScreenLock[] = "settings.enable_screen_lock"; @@ -484,7 +480,7 @@ const char kPluginsPluginsBlacklist[] = "plugins.plugins_blacklist"; // When first shipped, the pdf plugin will be disabled by default. When we // enable it by default, we'll want to do so only once. -const char kPluginsEnabledInternalPDF[] = "plugins.enabled_internal_pdf2"; +const char kPluginsEnabledInternalPDF[] = "plugins.enabled_internal_pdf3"; // Boolean that indicates whether we should check if we are the default browser // on start-up. @@ -541,6 +537,9 @@ const char kBlockNonsandboxedPlugins[] = "profile.block_nonsandboxed_plugins"; // storage, etc..) should be deleted on exit. const char kClearSiteDataOnExit[] = "profile.clear_site_data_on_exit"; +// Double that indicates the default zoom level. +const char kDefaultZoomLevel[] = "profile.default_zoom_level"; + // Dictionary that maps hostnames to zoom levels. Hosts not in this pref will // be displayed at the default zoom level. const char kPerHostZoomLevels[] = "profile.per_host_zoom_levels"; @@ -731,10 +730,6 @@ const char kBrowserWindowPlacement[] = "browser.window_placement"; // manager window to restore on startup. const char kTaskManagerWindowPlacement[] = "task_manager.window_placement"; -// A collection of position, size, and other data relating to the page info -// window to restore on startup. -const char kPageInfoWindowPlacement[] = "page_info.window_placement"; - // A collection of position, size, and other data relating to the keyword // editor window to restore on startup. const char kKeywordEditorWindowPlacement[] = "keyword_editor.window_placement"; @@ -768,7 +763,7 @@ const char kSaveFileDefaultDirectory[] = "savefile.default_directory"; // String which specifies the last directory that was chosen for uploading // or opening a file. -extern const char kSelectFileLastDirectory[] = "selectfile.last_directory"; +const char kSelectFileLastDirectory[] = "selectfile.last_directory"; // Extensions which should be opened upon completion. const char kDownloadExtensionsToOpen[] = "download.extensions_to_open"; @@ -897,6 +892,13 @@ const char kExtensionInstallAllowList[] = "extensions.install.allowlist"; // Google controlled. const char kExtensionInstallDenyList[] = "extensions.install.denylist"; +// A list containing extensions that Chrome will silently install +// at startup time. It is a list of strings, each string contains +// an extension ID and an update URL, delimited by a semicolon. +// This preference is set by an admin policy, and meant to be only +// accessed through ExternalPolicyExtensionProvider. +const char kExtensionInstallForceList[] = "extensions.install.forcelist"; + // Time of the last, and next scheduled, extensions auto-update checks. const char kLastExtensionsUpdateCheck[] = "extensions.autoupdate.last_check"; const char kNextExtensionsUpdateCheck[] = "extensions.autoupdate.next_check"; @@ -942,6 +944,8 @@ const char kNTPPrefVersion[] = "ntp.pref_version"; const char kNTPCustomLogoStart[] = "ntp.alt_logo_start"; const char kNTPCustomLogoEnd[] = "ntp.alt_logo_end"; +const char kDevToolsDisabled[] = "devtools.disabled"; + // A boolean specifying whether dev tools window should be opened docked. const char kDevToolsOpenDocked[] = "devtools.open_docked"; @@ -1011,12 +1015,22 @@ const char kLoginDatabaseMigrated[] = "login_database.migrated"; // The root URL of the cloud print service. const char kCloudPrintServiceURL[] = "cloud_print.service_url"; +// The last requested size of the dialog as it was closed. +const char kCloudPrintDialogWidth[] = "cloud_print.dialog_size.width"; +const char kCloudPrintDialogHeight[] = "cloud_print.dialog_size.height"; + const char kRemotingHasSetupCompleted[] = "remoting.has_setup_completed"; // The list of BackgroundContents that should be loaded when the browser // launches. const char kRegisteredBackgroundContents[] = "background_contents.registered"; +#if defined(OS_CHROMEOS) +// Dictionary for transient storage of settings that should go into signed +// settings storage before owner has been assigned. +const char kSignedSettingsTempStorage[] = "signed_settings_temp_storage"; +#endif + // *************** SERVICE PREFS *************** // These are attached to the service process. @@ -1030,9 +1044,9 @@ const char kCloudPrintAuthToken[] = "cloud_print.auth_token"; const char kCloudPrintXMPPAuthToken[] = "cloud_print.xmpp_auth_token"; // The email address of the account used to authenticate with the Cloud Print // server. -extern const char kCloudPrintEmail[] = "cloud_print.email"; +const char kCloudPrintEmail[] = "cloud_print.email"; // Settings specific to underlying print system. -extern const char kCloudPrintPrintSystemSettings[] = +const char kCloudPrintPrintSystemSettings[] = "cloud_print.print_system_settings"; // Used by the service process to determine if the remoting host is enabled. diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 7f7acdc..0d5275c 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -75,7 +75,7 @@ extern const char kDisableSpdy[]; extern const char kCookiePromptExpanded[]; extern const char kInstantConfirmDialogShown[]; extern const char kInstantEnabled[]; -#if defined(USE_NSS) +#if defined(USE_NSS) || defined(USE_OPENSSL) extern const char kCertRevocationCheckingEnabled[]; extern const char kSSL2Enabled[]; extern const char kSSL3Enabled[]; @@ -146,7 +146,6 @@ extern const char kLanguageXkbAutoRepeatInterval[]; extern const char kAccessibilityEnabled[]; extern const char kLabsAdvancedFilesystemEnabled[]; extern const char kLabsMediaplayerEnabled[]; -extern const char kLabsTalkEnabled[]; extern const char kEnableScreenLock[]; #endif extern const char kIpcDisabledMessages[]; @@ -202,6 +201,7 @@ extern const char kContentSettingsPatterns[]; extern const char kBlockThirdPartyCookies[]; extern const char kBlockNonsandboxedPlugins[]; extern const char kClearSiteDataOnExit[]; +extern const char kDefaultZoomLevel[]; extern const char kPerHostZoomLevels[]; extern const char kAutoFillEnabled[]; extern const char kAutoFillAuxiliaryProfilesEnabled[]; @@ -261,7 +261,6 @@ extern const char kUninstallLastObservedRunTimeSec[]; extern const char kBrowserWindowPlacement[]; extern const char kTaskManagerWindowPlacement[]; -extern const char kPageInfoWindowPlacement[]; extern const char kKeywordEditorWindowPlacement[]; extern const char kPreferencesWindowPlacement[]; extern const char kMemoryCacheSize[]; @@ -326,6 +325,8 @@ extern const char kNextExtensionsUpdateCheck[]; extern const char kExtensionInstallAllowList[]; extern const char kExtensionInstallDenyList[]; +extern const char kExtensionInstallForceList[]; + extern const char kExtensionBlacklistUpdateVersion[]; extern const char kExtensionSidebarWidth[]; @@ -341,6 +342,7 @@ extern const char kNTPPrefVersion[]; extern const char kNTPCustomLogoStart[]; extern const char kNTPCustomLogoEnd[]; +extern const char kDevToolsDisabled[]; extern const char kDevToolsOpenDocked[]; extern const char kDevToolsSplitLocation[]; extern const char kSyncSessions[]; @@ -373,8 +375,10 @@ extern const char kGeolocationContentSettings[]; extern const char kLoginDatabaseMigrated[]; -extern const char kCloudPrintProxyEnabled[]; extern const char kCloudPrintServiceURL[]; +extern const char kCloudPrintDialogWidth[]; +extern const char kCloudPrintDialogHeight[]; +extern const char kCloudPrintProxyEnabled[]; extern const char kCloudPrintProxyId[]; extern const char kCloudPrintAuthToken[]; extern const char kCloudPrintXMPPAuthToken[]; @@ -390,6 +394,10 @@ extern const char kProxyServer[]; extern const char kProxyPacUrl[]; extern const char kProxyBypassList[]; +#if defined(OS_CHROMEOS) +extern const char kSignedSettingsTempStorage[]; +#endif + extern const char kRegisteredBackgroundContents[]; } // namespace prefs diff --git a/chrome/common/pref_store.h b/chrome/common/pref_store.h index 192ce14..7011eba 100644 --- a/chrome/common/pref_store.h +++ b/chrome/common/pref_store.h @@ -55,7 +55,7 @@ class PrefStore { // DictionaryValue. Instead, it should have getters that return a // richer set of information for a pref, including if the store // wants to return the default value for a preference. - virtual DictionaryValue* prefs() = 0; + virtual DictionaryValue* prefs() const = 0; virtual PrefReadError ReadPrefs() = 0; diff --git a/chrome/common/render_messages.cc b/chrome/common/render_messages.cc index f437bf4..2a42c6c 100644 --- a/chrome/common/render_messages.cc +++ b/chrome/common/render_messages.cc @@ -14,6 +14,7 @@ #include "chrome/common/render_messages_params.h" #include "chrome/common/resource_response.h" #include "chrome/common/serialized_script_value.h" +#include "chrome/common/speech_input_result.h" #include "chrome/common/thumbnail_score.h" #include "gfx/rect.h" #include "ipc/ipc_channel_handle.h" @@ -863,7 +864,7 @@ bool ParamTraits<URLPattern>::Read(const Message* m, void** iter, return false; p->set_valid_schemes(valid_schemes); - return p->Parse(spec); + return URLPattern::PARSE_SUCCESS == p->Parse(spec); } void ParamTraits<URLPattern>::Log(const param_type& p, std::string* l) { @@ -1229,4 +1230,26 @@ void ParamTraits<PepperDirEntry>::Log(const param_type& p, std::string* l) { l->append(")"); } +void ParamTraits<speech_input::SpeechInputResultItem>::Write( + Message* m, const param_type& p) { + WriteParam(m, p.utterance); + WriteParam(m, p.confidence); +} + +bool ParamTraits<speech_input::SpeechInputResultItem>::Read(const Message* m, + void** iter, + param_type* p) { + return ReadParam(m, iter, &p->utterance) && + ReadParam(m, iter, &p->confidence); +} + +void ParamTraits<speech_input::SpeechInputResultItem>::Log(const param_type& p, + std::string* l) { + l->append("("); + LogParam(p.utterance, l); + l->append(":"); + LogParam(p.confidence, l); + l->append(")"); +} + } // namespace IPC diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index da54616..fb68afa 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -23,7 +23,6 @@ #include "chrome/common/translate_errors.h" #include "chrome/common/view_types.h" #include "chrome/common/webkit_param_traits.h" -#include "gfx/native_widget_types.h" #include "ipc/ipc_message_utils.h" #include "ipc/ipc_platform_file.h" // ifdefed typedef. #include "third_party/WebKit/WebKit/chromium/public/WebStorageArea.h" @@ -53,6 +52,10 @@ namespace webkit_blob { class BlobData; } +namespace speech_input { +struct SpeechInputResultItem; +} + namespace webkit_glue { struct FormData; class FormField; @@ -312,7 +315,7 @@ struct ParamTraits<gfx::NativeView> { } static void Log(const param_type& p, std::string* l) { - l->append(StringPrintf("<gfx::NativeView>")); + l->append(base::StringPrintf("<gfx::NativeView>")); } }; @@ -627,6 +630,14 @@ struct ParamTraits<PepperDirEntry> { static void Log(const param_type& p, std::string* l); }; +template <> +struct ParamTraits<speech_input::SpeechInputResultItem> { + typedef speech_input::SpeechInputResultItem param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* p); + static void Log(const param_type& p, std::string* l); +}; + } // namespace IPC #define MESSAGES_INTERNAL_FILE "chrome/common/render_messages_internal.h" diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index b8e9a70..f00c806 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -13,6 +13,7 @@ #include "build/build_config.h" #include "base/file_path.h" +#include "base/file_util_proxy.h" #include "base/nullable_string16.h" #include "base/platform_file.h" #include "base/sync_socket.h" @@ -21,6 +22,7 @@ #include "chrome/common/nacl_types.h" #include "chrome/common/notification_type.h" #include "chrome/common/page_zoom.h" +#include "chrome/common/speech_input_result.h" #include "chrome/common/translate_errors.h" #include "chrome/common/window_container_type.h" #include "ipc/ipc_message_macros.h" @@ -49,12 +51,6 @@ class SkBitmap; struct ThumbnailScore; class WebCursor; -namespace base { -namespace file_util_proxy { -struct Entry; -} -} - namespace gfx { class Rect; } @@ -165,6 +161,24 @@ IPC_BEGIN_MESSAGES(View) int /* document_cookie */, bool /* success */) + // Tells the render view to switch the CSS to print media type, renders every + // requested pages for print preview. + IPC_MESSAGE_ROUTED0(ViewMsg_PrintPreview) + +#if defined(OS_MACOSX) || defined(OS_WIN) + // Sends back to the browser the rendered "printed page" for preview that was + // requested by a ViewMsg_PrintPage message or from scripted printing. The + // memory handle in this message is already valid in the browser process. + IPC_MESSAGE_ROUTED1(ViewHostMsg_PageReadyForPreview, + ViewHostMsg_DidPrintPage_Params /* page content */) +#else + // Sends back to the browser the rendered "printed page" for preview that was + // requested by a ViewMsg_PrintPage message or from scripted printing. The + // memory handle in this message is already valid in the browser process. + IPC_MESSAGE_ROUTED1(ViewHostMsg_PagesReadyForPreview, + int /* fd in browser */) +#endif + // Tells the renderer to dump as much memory as it can, perhaps because we // have memory pressure or the renderer is (or will be) paged out. This // should only result in purging objects we can recalculate, e.g. caches or @@ -189,12 +203,6 @@ IPC_BEGIN_MESSAGES(View) // This signals the render view that it can send another UpdateRect message. IPC_MESSAGE_ROUTED0(ViewMsg_UpdateRect_ACK) - // Replies to creating and updating videos. - IPC_MESSAGE_ROUTED1(ViewMsg_CreateVideo_ACK, - int32 /* video_id */) - IPC_MESSAGE_ROUTED1(ViewMsg_UpdateVideo_ACK, - int32 /* video_id */) - // Message payload includes: // 1. A blob that should be cast to WebInputEvent // 2. An optional boolean value indicating if a RawKeyDown event is associated @@ -229,6 +237,11 @@ IPC_BEGIN_MESSAGES(View) // node. IPC_MESSAGE_ROUTED1(ViewMsg_SetInitialFocus, bool /* reverse */) + // Tells the renderer to scroll the currently focused node into view only if + // the currently focused node is a Text node (textfield, text area or content + // editable divs). + IPC_MESSAGE_ROUTED0(ViewMsg_ScrollFocusedEditableNodeIntoView) + // Tells the renderer to perform the specified navigation, interrupting any // existing navigation. IPC_MESSAGE_ROUTED1(ViewMsg_Navigate, ViewMsg_Navigate_Params) @@ -407,6 +420,12 @@ IPC_BEGIN_MESSAGES(View) IPC_MESSAGE_ROUTED1(ViewMsg_Zoom, PageZoom::Function /* function */) + // Set the zoom level for the current main frame. If the level actually + // changes, a ViewHostMsg_DidZoomURL message will be sent back to the browser + // telling it what url got zoomed and what its current zoom level is. + IPC_MESSAGE_ROUTED1(ViewMsg_SetZoomLevel, + double /* zoom_level */) + // Set the zoom level for a particular url that the renderer is in the // process of loading. This will be stored, to be used if the load commits // and ignored otherwise. @@ -803,6 +822,20 @@ IPC_BEGIN_MESSAGES(View) // Used to instruct the RenderView to send back updates to the preferred size. IPC_MESSAGE_ROUTED1(ViewMsg_EnablePreferredSizeChangedMode, int /*flags*/) + IPC_MESSAGE_ROUTED4(ViewMsg_SearchBoxChange, + string16 /*value*/, + bool /*verbatim*/, + int /*selection_start*/, + int /*selection_end*/) + IPC_MESSAGE_ROUTED2(ViewMsg_SearchBoxSubmit, + string16 /*value*/, + bool /*verbatim*/) + IPC_MESSAGE_ROUTED0(ViewMsg_SearchBoxCancel) + IPC_MESSAGE_ROUTED1(ViewMsg_SearchBoxResize, + gfx::Rect /*search_box_bounds*/) + IPC_MESSAGE_ROUTED1(ViewMsg_DetermineIfPageSupportsInstant, + string16 /*value*/) + // Used to tell the renderer not to add scrollbars with height and // width below a threshold. IPC_MESSAGE_ROUTED1(ViewMsg_DisableScrollbarsForSmallWindows, @@ -1033,7 +1066,7 @@ IPC_BEGIN_MESSAGES(View) // Relay a speech recognition result, either partial or final. IPC_MESSAGE_ROUTED2(ViewMsg_SpeechInput_SetRecognitionResult, int /* request id */, - string16 /* result */) + speech_input::SpeechInputResultArray /* result */) // Indicate that speech recognizer has stopped recording and started // recognition. @@ -1065,7 +1098,7 @@ IPC_BEGIN_MESSAGES(View) base::PlatformFileInfo) IPC_MESSAGE_CONTROL3(ViewMsg_FileSystem_DidReadDirectory, int /* request_id */, - std::vector<base::file_util_proxy::Entry> /* entries */, + std::vector<base::FileUtilProxy::Entry> /* entries */, bool /* has_more */) IPC_MESSAGE_CONTROL3(ViewMsg_FileSystem_DidWrite, @@ -1088,6 +1121,10 @@ IPC_BEGIN_MESSAGES(View) IPC_MESSAGE_CONTROL1(ViewMsg_SetPhishingModel, IPC::PlatformFileForTransit /* model_file */) + // External popup menus. + IPC_MESSAGE_ROUTED1(ViewMsg_SelectPopupMenuItem, + int /* selected index, -1 means no selection */) + IPC_END_MESSAGES(View) @@ -1185,7 +1222,12 @@ IPC_BEGIN_MESSAGES(ViewHost) std::string /* state */) // Notifies the browser that a document has been loaded in a frame. - IPC_MESSAGE_ROUTED0(ViewHostMsg_DocumentLoadedInFrame) + IPC_MESSAGE_ROUTED1(ViewHostMsg_DocumentLoadedInFrame, + long long /* frame_id */) + + // Notifies the browser that a frame finished loading. + IPC_MESSAGE_ROUTED1(ViewHostMsg_DidFinishLoad, + long long /* frame_id */) // Changes the title for the page in the UI when the page is navigated or the // title changes. @@ -1268,14 +1310,6 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC_MESSAGE_ROUTED1(ViewHostMsg_UpdateRect, ViewHostMsg_UpdateRect_Params) - // Sent to create, update and destroy video layers. - IPC_MESSAGE_ROUTED1(ViewHostMsg_CreateVideo, - gfx::Size /* size */) - IPC_MESSAGE_ROUTED2(ViewHostMsg_UpdateVideo, - TransportDIB::Id /* bitmap */, - gfx::Rect /* bitmap_rect */) - IPC_MESSAGE_ROUTED0(ViewHostMsg_DestroyVideo) - // Sent by the renderer when GPU compositing is enabled or disabled to notify // the browser whether or not is should do paiting. IPC_MESSAGE_ROUTED1(ViewHostMsg_GpuRenderingActivated, @@ -1289,7 +1323,12 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC_MESSAGE_ROUTED0(ViewHostMsg_Focus) IPC_MESSAGE_ROUTED0(ViewHostMsg_Blur) - IPC_MESSAGE_ROUTED0(ViewHostMsg_FocusedNodeChanged) + + // Message sent from renderer to the browser when focus changes inside the + // webpage. The parameter says whether the newly focused element needs + // keyboard input (true for textfields, text areas and content editable divs). + IPC_MESSAGE_ROUTED1(ViewHostMsg_FocusedNodeChanged, + bool /* is_editable_node */) // Returns the window location of the given window. // TODO(shess): Provide a mapping from reply_msg->routing_id() to @@ -1782,8 +1821,8 @@ IPC_BEGIN_MESSAGES(ViewHost) std::string /* value */) // Send back a string to be recorded by UserMetrics. - IPC_MESSAGE_ROUTED1(ViewHostMsg_UserMetricsRecordAction, - std::string /* action */) + IPC_MESSAGE_CONTROL1(ViewHostMsg_UserMetricsRecordAction, + std::string /* action */) // Send back histograms as vector of pickled-histogram strings. IPC_MESSAGE_CONTROL2(ViewHostMsg_RendererHistograms, @@ -1903,6 +1942,9 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC_MESSAGE_ROUTED1(ViewHostMsg_DataReceived_ACK, int /* request_id */) + IPC_MESSAGE_CONTROL1(ViewHostMsg_RevealFolderInOS, + FilePath /* path */) + // Sent when the renderer has processed a DataDownloaded message. IPC_MESSAGE_ROUTED1(ViewHostMsg_DataDownloaded_ACK, int /* request_id */) @@ -2199,14 +2241,15 @@ IPC_BEGIN_MESSAGES(ViewHost) gfx::PluginWindowHandle /* window */, int32 /* width */, int32 /* height */, - uint64 /* identifier for IOSurface */) + uint64 /* surface_id */) // This message notifies the browser process that the plug-in // swapped the buffers associated with the given "window", which // should cause the browser to redraw the various plug-ins' // contents. - IPC_MESSAGE_ROUTED1(ViewHostMsg_AcceleratedSurfaceBuffersSwapped, - gfx::PluginWindowHandle /* window */) + IPC_MESSAGE_ROUTED2(ViewHostMsg_AcceleratedSurfaceBuffersSwapped, + gfx::PluginWindowHandle /* window */, + uint64 /* surface_id */) #endif // A renderer sends this to the browser process when it wants to create a @@ -2833,10 +2876,12 @@ IPC_BEGIN_MESSAGES(ViewHost) // Requests the speech input service to start speech recognition on behalf of // the given |render_view_id|. - IPC_MESSAGE_CONTROL3(ViewHostMsg_SpeechInput_StartRecognition, + IPC_MESSAGE_CONTROL5(ViewHostMsg_SpeechInput_StartRecognition, int /* render_view_id */, - int /* request id */, - gfx::Rect /* element rect in render view coordinates */) + int /* request_id */, + gfx::Rect /* element_rect */, + std::string /* language */, + std::string /* grammar */) // Requests the speech input service to cancel speech recognition on behalf of // the given |render_view_id|. If speech recognition is not happening nor or @@ -2869,11 +2914,12 @@ IPC_BEGIN_MESSAGES(ViewHost) // These are messages sent from the renderer to the browser process. // WebFrameClient::openFileSystem() message. - IPC_MESSAGE_CONTROL4(ViewHostMsg_OpenFileSystemRequest, + IPC_MESSAGE_CONTROL5(ViewHostMsg_OpenFileSystemRequest, int /* request_id */, GURL /* origin_url */, fileapi::FileSystemType /* type */, - int64 /* requested_size */) + int64 /* requested_size */, + bool /* create */) // WebFileSystem::move() message. IPC_MESSAGE_CONTROL3(ViewHostMsg_FileSystem_Move, @@ -2961,9 +3007,13 @@ IPC_BEGIN_MESSAGES(ViewHost) // Suggest results ----------------------------------------------------------- - IPC_MESSAGE_ROUTED2(ViewHostMsg_SetSuggestResult, + IPC_MESSAGE_ROUTED2(ViewHostMsg_SetSuggestions, int32 /* page_id */, - std::string /* suggest */) + std::vector<std::string> /* suggestions */) + + IPC_MESSAGE_ROUTED2(ViewHostMsg_InstantSupportDetermined, + int32 /* page_id */, + bool /* result */) // Client-Side Phishing Detector --------------------------------------------- // Inform the browser that the current URL is phishing according to the diff --git a/chrome/common/render_messages_params.cc b/chrome/common/render_messages_params.cc index 59802b4..a57b076 100644 --- a/chrome/common/render_messages_params.cc +++ b/chrome/common/render_messages_params.cc @@ -522,7 +522,7 @@ struct ParamTraits<Extension::Location> { int val = 0; if (!ReadParam(m, iter, &val) || val < Extension::INVALID || - val > Extension::EXTERNAL_PREF_DOWNLOAD) + val >= Extension::NUM_LOCATIONS) return false; *p = static_cast<param_type>(val); return true; @@ -1873,14 +1873,14 @@ void ParamTraits<ViewHostMsg_DomMessage_Params>::Log(const param_type& p, l->append(")"); } -void ParamTraits<base::file_util_proxy::Entry>::Write( +void ParamTraits<base::FileUtilProxy::Entry>::Write( Message* m, const param_type& p) { WriteParam(m, p.name); WriteParam(m, p.is_directory); } -bool ParamTraits<base::file_util_proxy::Entry>::Read( +bool ParamTraits<base::FileUtilProxy::Entry>::Read( const Message* m, void** iter, param_type* p) { @@ -1889,7 +1889,7 @@ bool ParamTraits<base::file_util_proxy::Entry>::Read( ReadParam(m, iter, &p->is_directory); } -void ParamTraits<base::file_util_proxy::Entry>::Log( +void ParamTraits<base::FileUtilProxy::Entry>::Log( const param_type& p, std::string* l) { l->append("("); diff --git a/chrome/common/render_messages_params.h b/chrome/common/render_messages_params.h index 2eeff70..d92def3 100644 --- a/chrome/common/render_messages_params.h +++ b/chrome/common/render_messages_params.h @@ -1315,8 +1315,8 @@ struct ParamTraits<ViewHostMsg_DomMessage_Params> { }; template <> -struct ParamTraits<base::file_util_proxy::Entry> { - typedef base::file_util_proxy::Entry param_type; +struct ParamTraits<base::FileUtilProxy::Entry> { + typedef base::FileUtilProxy::Entry param_type; static void Write(Message* m, const param_type& p); static bool Read(const Message* m, void** iter, param_type* p); static void Log(const param_type& p, std::string* l); diff --git a/chrome/common/resource_dispatcher_unittest.cc b/chrome/common/resource_dispatcher_unittest.cc index 8e31ee9..f5828ff 100644 --- a/chrome/common/resource_dispatcher_unittest.cc +++ b/chrome/common/resource_dispatcher_unittest.cc @@ -69,10 +69,6 @@ class TestRequestCallback : public ResourceLoaderBridge::Peer { complete_ = true; } - virtual GURL GetURLForDebugging() const { - return GURL(); - } - const std::string& data() const { return data_; } @@ -121,9 +117,7 @@ class ResourceDispatcherTest : public testing::Test, // received data message with the test contents base::SharedMemory shared_mem; - EXPECT_TRUE(shared_mem.Create(std::string(), false, false, - test_page_contents_len)); - EXPECT_TRUE(shared_mem.Map(test_page_contents_len)); + EXPECT_TRUE(shared_mem.CreateAndMapAnonymous(test_page_contents_len)); char* put_data_here = static_cast<char*>(shared_mem.memory()); memcpy(put_data_here, test_page_contents, test_page_contents_len); base::SharedMemoryHandle dup_handle; @@ -293,14 +287,10 @@ class DeferredResourceLoadingTest : public ResourceDispatcherTest, const base::Time& completion_time) { } - virtual GURL GetURLForDebugging() const { - return GURL(); - } - protected: virtual void SetUp() { - EXPECT_EQ(true, shared_handle_.Create("DeferredResourceLoaderTest", false, - false, 100)); + EXPECT_EQ(true, shared_handle_.CreateNamed("DeferredResourceLoaderTest", + false, 100)); ResourceDispatcherTest::SetUp(); } diff --git a/chrome/common/sandbox_init_wrapper_mac.cc b/chrome/common/sandbox_init_wrapper_mac.cc index ca21255..d65ee70 100644 --- a/chrome/common/sandbox_init_wrapper_mac.cc +++ b/chrome/common/sandbox_init_wrapper_mac.cc @@ -12,10 +12,12 @@ bool SandboxInitWrapper::InitializeSandbox(const CommandLine& command_line, const std::string& process_type) { + using sandbox::Sandbox; + if (command_line.HasSwitch(switches::kNoSandbox)) return true; - sandbox::SandboxProcessType sandbox_process_type; + Sandbox::SandboxProcessType sandbox_process_type; FilePath allowed_dir; // Empty by default. if (process_type.empty()) { @@ -29,7 +31,7 @@ bool SandboxInitWrapper::InitializeSandbox(const CommandLine& command_line, // this once this flag is removed. return true; } else { - sandbox_process_type = sandbox::SANDBOX_TYPE_RENDERER; + sandbox_process_type = Sandbox::SANDBOX_TYPE_RENDERER; } } else if (process_type == switches::kExtensionProcess) { // Extension processes are just renderers [they use RenderMain()] with a @@ -42,15 +44,15 @@ bool SandboxInitWrapper::InitializeSandbox(const CommandLine& command_line, return true; } else if (process_type == switches::kUtilityProcess) { // Utility process sandbox. - sandbox_process_type = sandbox::SANDBOX_TYPE_UTILITY; + sandbox_process_type = Sandbox::SANDBOX_TYPE_UTILITY; allowed_dir = command_line.GetSwitchValuePath(switches::kUtilityProcessAllowedDir); } else if (process_type == switches::kWorkerProcess) { // Worker process sandbox. - sandbox_process_type = sandbox::SANDBOX_TYPE_WORKER; + sandbox_process_type = Sandbox::SANDBOX_TYPE_WORKER; } else if (process_type == switches::kNaClLoaderProcess) { // Native Client sel_ldr (user untrusted code) sandbox. - sandbox_process_type = sandbox::SANDBOX_TYPE_NACL_LOADER; + sandbox_process_type = Sandbox::SANDBOX_TYPE_NACL_LOADER; } else if ((process_type == switches::kPluginProcess) || (process_type == switches::kProfileImportProcess) || (process_type == switches::kGpuProcess) || @@ -64,8 +66,8 @@ bool SandboxInitWrapper::InitializeSandbox(const CommandLine& command_line, } // Warm up APIs before turning on the sandbox. - sandbox::SandboxWarmup(); + Sandbox::SandboxWarmup(); // Actually sandbox the process. - return sandbox::EnableSandbox(sandbox_process_type, allowed_dir); + return Sandbox::EnableSandbox(sandbox_process_type, allowed_dir); } diff --git a/chrome/common/sandbox_mac.h b/chrome/common/sandbox_mac.h index 61a02fc..f16fdb5 100644 --- a/chrome/common/sandbox_mac.h +++ b/chrome/common/sandbox_mac.h @@ -6,52 +6,171 @@ #define CHROME_COMMON_SANDBOX_MAC_H_ #pragma once +#include <string> + +#include "base/basictypes.h" +#include "base/hash_tables.h" +#include "base/gtest_prod_util.h" + class FilePath; +#if __OBJC__ +@class NSArray; +@class NSString; +#else +class NSArray; +class NSString; +#endif + namespace sandbox { -enum SandboxProcessType { - SANDBOX_TYPE_FIRST_TYPE, // Placeholder to ease iteration. +// Class representing a substring of the sandbox profile tagged with its type. +class SandboxSubstring { + public: + enum SandboxSubstringType { + PLAIN, // Just a plain string, no escaping necessary. + LITERAL, // Escape for use in (literal ...) expression. + REGEX, // Escape for use in (regex ...) expression. + }; - SANDBOX_TYPE_RENDERER = SANDBOX_TYPE_FIRST_TYPE, + SandboxSubstring() {} - // The worker processes uses the most restrictive sandbox which has almost - // *everything* locked down. Only a couple of /System/Library/ paths and - // some other very basic operations (e.g., reading metadata to allow - // following symlinks) are permitted. - SANDBOX_TYPE_WORKER, + explicit SandboxSubstring(const std::string& value) + : value_(value), + type_(PLAIN) {} - // Utility process is as restrictive as the worker process except full access - // is allowed to one configurable directory. - SANDBOX_TYPE_UTILITY, + SandboxSubstring(const std::string& value, SandboxSubstringType type) + : value_(value), + type_(type) {} - // Native Client sandbox for the user's untrusted code. - SANDBOX_TYPE_NACL_LOADER, + const std::string& value() { return value_; } + SandboxSubstringType type() { return type_; } - SANDBOX_AFTER_TYPE_LAST_TYPE, // Placeholder to ease iteration. + private: + std::string value_; + SandboxSubstringType type_; }; -// Warm up System APIs that empirically need to be accessed before the Sandbox -// is turned on. -void SandboxWarmup(); - -// Turns on the OS X sandbox for this process. -// |sandbox_type| - type of Sandbox to use. -// |allowed_dir| - directory to allow access to, currently the only sandbox -// profile that supports this is SANDBOX_TYPE_UTILITY . -// -// |allowed_dir| must be a "simple" string since it's placed as is in a regex -// i.e. it must not contain quotation characters, escaping or any characters -// that might have special meaning when blindly substituted into a regular -// expression - crbug.com/26492 . -// Returns true on success, false if an error occurred enabling the sandbox. -bool EnableSandbox(SandboxProcessType sandbox_type, - const FilePath& allowed_dir); - -// Convert provided path into a "canonical" path matching what the Sandbox -// expects i.e. one without symlinks. -// This path is not necessarily unique e.g. in the face of hardlinks. -void GetCanonicalSandboxPath(FilePath* path); +class Sandbox { + public: + // A map of variable name -> string to substitute in its place. + typedef base::hash_map<std::string, SandboxSubstring> + SandboxVariableSubstitions; + + enum SandboxProcessType { + SANDBOX_TYPE_FIRST_TYPE, // Placeholder to ease iteration. + + SANDBOX_TYPE_RENDERER = SANDBOX_TYPE_FIRST_TYPE, + + // The worker process uses the most restrictive sandbox which has almost + // *everything* locked down. Only a couple of /System/Library/ paths and + // some other very basic operations (e.g., reading metadata to allow + // following symlinks) are permitted. + SANDBOX_TYPE_WORKER, + + // Utility process is as restrictive as the worker process except full + // access is allowed to one configurable directory. + SANDBOX_TYPE_UTILITY, + + // Native Client sandbox for the user's untrusted code. + SANDBOX_TYPE_NACL_LOADER, + + SANDBOX_AFTER_TYPE_LAST_TYPE, // Placeholder to ease iteration. + }; + + // Warm up System APIs that empirically need to be accessed before the Sandbox + // is turned on. + static void SandboxWarmup(); + + // Turns on the OS X sandbox for this process. + // |sandbox_type| - type of Sandbox to use. + // |allowed_dir| - directory to allow access to, currently the only sandbox + // profile that supports this is SANDBOX_TYPE_UTILITY . + // + // Returns true on success, false if an error occurred enabling the sandbox. + static bool EnableSandbox(SandboxProcessType sandbox_type, + const FilePath& allowed_dir); + + + // Exposed for testing purposes, used by an accessory function of our tests + // so we can't use FRIEND_TEST. + + // Build the Sandbox command necessary to allow access to a named directory + // indicated by |allowed_dir|. + // Returns a string containing the sandbox profile commands necessary to allow + // access to that directory or nil if an error occured. + + // The header comment for PostProcessSandboxProfile() explains how variable + // substition works in sandbox templates. + // The returned string contains embedded variables. The function fills in + // |substitutions| to contain the values for these variables. + static NSString* BuildAllowDirectoryAccessSandboxString( + const FilePath& allowed_dir, + SandboxVariableSubstitions* substitutions); + + // Assemble the final sandbox profile from a template by removing comments + // and substituting variables. + // + // |sandbox_template| is a string which contains 2 entitites to operate on: + // + // - Comments - The sandbox comment syntax is used to make the OS sandbox + // optionally ignore commands it doesn't support. e.g. + // ;10.6_ONLY (foo) + // Where (foo) is some command that is only supported on OS X 10.6. + // The ;10.6_ONLY comment can then be removed from the template to enable + // (foo) as appropriate. + // + // - Variables - denoted by @variable_name@ . These are defined in the + // sandbox template in cases where another string needs to be substituted at + // runtime. e.g. @HOMEDIR_AS_LITERAL@ is substituted at runtime for the user's + // home directory escaped appropriately for a (literal ...) expression. + // + // |comments_to_remove| is a list of NSStrings containing the comments to + // remove. + // |substitutions| is a hash of "variable name" -> "string to substitute". + // Where the replacement string is tagged with information on how it is to be + // escaped e.g. used as part of a regex string or a literal. + // + // On output |final_sandbox_profile_str| contains the final sandbox profile. + // Returns true on success, false otherwise. + static bool PostProcessSandboxProfile( + NSString* in_sandbox_data, + NSArray* comments_to_remove, + SandboxVariableSubstitions& substitutions, + std::string *final_sandbox_profile_str); + + private: + // Escape |src_utf8| for use in a plain string variable in a sandbox + // configuraton file. On return |dst| is set to the quoted output. + // Returns: true on success, false otherwise. + static bool QuotePlainString(const std::string& src_utf8, std::string* dst); + + // Escape |str_utf8| for use in a regex literal in a sandbox + // configuraton file. On return |dst| is set to the utf-8 encoded quoted + // output. + // + // The implementation of this function is based on empirical testing of the + // OS X sandbox on 10.5.8 & 10.6.2 which is undocumented and subject to + // change. + // + // Note: If str_utf8 contains any characters < 32 || >125 then the function + // fails and false is returned. + // + // Returns: true on success, false otherwise. + static bool QuoteStringForRegex(const std::string& str_utf8, + std::string* dst); + + // Convert provided path into a "canonical" path matching what the Sandbox + // expects i.e. one without symlinks. + // This path is not necessarily unique e.g. in the face of hardlinks. + static void GetCanonicalSandboxPath(FilePath* path); + + FRIEND_TEST(MacDirAccessSandboxTest, StringEscape); + FRIEND_TEST(MacDirAccessSandboxTest, RegexEscape); + FRIEND_TEST(MacDirAccessSandboxTest, SandboxAccess); + + DISALLOW_IMPLICIT_CONSTRUCTORS(Sandbox); +}; } // namespace sandbox diff --git a/chrome/common/sandbox_mac.mm b/chrome/common/sandbox_mac.mm index 608ba27..6ede3bf 100644 --- a/chrome/common/sandbox_mac.mm +++ b/chrome/common/sandbox_mac.mm @@ -69,15 +69,13 @@ bool EscapeSingleChar(char c, std::string* dst) { namespace sandbox { -// Escape |str_utf8| for use in a plain string variable in a sandbox -// configuraton file. On return |dst| is set to the utf-8 encoded quoted -// output. -// Returns: true on success, false otherwise. -bool QuotePlainString(const std::string& str_utf8, std::string* dst) { + +// static +bool Sandbox::QuotePlainString(const std::string& src_utf8, std::string* dst) { dst->clear(); - const char* src = str_utf8.c_str(); - int32_t length = str_utf8.length(); + const char* src = src_utf8.c_str(); + int32_t length = src_utf8.length(); int32_t position = 0; while (position < length) { UChar32 c; @@ -108,18 +106,9 @@ bool QuotePlainString(const std::string& str_utf8, std::string* dst) { return true; } -// Escape |str_utf8| for use in a regex literal in a sandbox -// configuraton file. On return |dst| is set to the utf-8 encoded quoted -// output. -// -// The implementation of this function is based on empirical testing of the -// OS X sandbox on 10.5.8 & 10.6.2 which is undocumented and subject to change. -// -// Note: If str_utf8 contains any characters < 32 || >125 then the function -// fails and false is returned. -// -// Returns: true on success, false otherwise. -bool QuoteStringForRegex(const std::string& str_utf8, std::string* dst) { +// static +bool Sandbox::QuoteStringForRegex(const std::string& str_utf8, + std::string* dst) { // Characters with special meanings in sandbox profile syntax. const char regex_special_chars[] = { '\\', @@ -186,7 +175,9 @@ bool QuoteStringForRegex(const std::string& str_utf8, std::string* dst) { // enable the function is also noted. // This function is tested on the following OS versions: // 10.5.6, 10.6.0 -void SandboxWarmup() { + +// static +void Sandbox::SandboxWarmup() { base::mac::ScopedNSAutoreleasePool scoped_pool; { // CGColorSpaceCreateWithName(), CGBitmapContextCreate() - 10.5.6 @@ -242,38 +233,90 @@ void SandboxWarmup() { } } -// Turns on the OS X sandbox for this process. -bool EnableSandbox(SandboxProcessType sandbox_type, - const FilePath& allowed_dir) { - // Sanity - currently only SANDBOX_TYPE_UTILITY supports a directory being - // passed in. - if (sandbox_type != SANDBOX_TYPE_UTILITY) { - DCHECK(allowed_dir.empty()) - << "Only SANDBOX_TYPE_UTILITY allows a custom directory parameter."; +// static +NSString* Sandbox::BuildAllowDirectoryAccessSandboxString( + const FilePath& allowed_dir, + SandboxVariableSubstitions* substitutions) { + // A whitelist is used to determine which directories can be statted + // This means that in the case of an /a/b/c/d/ directory, we may be able to + // stat the leaf directory, but not it's parent. + // The extension code in Chrome calls realpath() which fails if it can't call + // stat() on one of the parent directories in the path. + // The solution to this is to allow statting the parent directories themselves + // but not their contents. We need to add a separate rule for each parent + // directory. + + // The sandbox only understands "real" paths. This resolving step is + // needed so the caller doesn't need to worry about things like /var + // being a link to /private/var (like in the paths CreateNewTempDirectory() + // returns). + FilePath allowed_dir_canonical(allowed_dir); + GetCanonicalSandboxPath(&allowed_dir_canonical); + + // Collect a list of all parent directories. + FilePath last_path = allowed_dir_canonical; + std::vector<FilePath> subpaths; + for (FilePath path = allowed_dir_canonical.DirName(); + path.value() != last_path.value(); + path = path.DirName()) { + subpaths.push_back(path); + last_path = path; } + + // Iterate through all parents and allow stat() on them explicitly. + NSString* sandbox_command = @"(allow file-read-metadata "; + for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin(); + i != subpaths.rend(); + ++i) { + std::string subdir_escaped; + if (!QuotePlainString(i->value(), &subdir_escaped)) { + LOG(FATAL) << "String quoting failed " << i->value(); + return nil; + } + + NSString* subdir_escaped_ns = + base::SysUTF8ToNSString(subdir_escaped.c_str()); + sandbox_command = + [sandbox_command stringByAppendingFormat:@"(literal \"%@\")", + subdir_escaped_ns]; + } + + // Finally append the leaf directory. Unlike it's parents (for which only + // stat() should be allowed), the leaf directory needs full access. + (*substitutions)["ALLOWED_DIR"] = + SandboxSubstring(allowed_dir_canonical.value(), + SandboxSubstring::REGEX); + sandbox_command = + [sandbox_command + stringByAppendingString:@") (allow file-read* file-write*" + " (regex #\"@ALLOWED_DIR@\") )"]; + return sandbox_command; +} + +// Load the appropriate template for the given sandbox type. +// Returns the template as an NSString or nil on error. +NSString* LoadSandboxTemplate(Sandbox::SandboxProcessType sandbox_type) { // We use a custom sandbox definition file to lock things down as // tightly as possible. - // TODO(jeremy): Look at using include syntax to unify common parts of sandbox - // definition files. NSString* sandbox_config_filename = nil; switch (sandbox_type) { - case SANDBOX_TYPE_RENDERER: + case Sandbox::SANDBOX_TYPE_RENDERER: sandbox_config_filename = @"renderer"; break; - case SANDBOX_TYPE_WORKER: + case Sandbox::SANDBOX_TYPE_WORKER: sandbox_config_filename = @"worker"; break; - case SANDBOX_TYPE_UTILITY: + case Sandbox::SANDBOX_TYPE_UTILITY: sandbox_config_filename = @"utility"; break; - case SANDBOX_TYPE_NACL_LOADER: + case Sandbox::SANDBOX_TYPE_NACL_LOADER: // The Native Client loader is used for safeguarding the user's // untrusted code within Native Client. sandbox_config_filename = @"nacl_loader"; break; default: NOTREACHED(); - return false; + return nil; } // Read in the sandbox profile and the common prefix file. @@ -288,7 +331,7 @@ bool EnableSandbox(SandboxProcessType sandbox_type, if (!common_sandbox_prefix_data) { LOG(FATAL) << "Failed to find the sandbox profile on disk " << [common_sandbox_prefix_path fileSystemRepresentation]; - return false; + return nil; } NSString* sandbox_profile_path = @@ -302,74 +345,149 @@ bool EnableSandbox(SandboxProcessType sandbox_type, if (!sandbox_data) { LOG(FATAL) << "Failed to find the sandbox profile on disk " << [sandbox_profile_path fileSystemRepresentation]; - return false; + return nil; } // Prefix sandbox_data with common_sandbox_prefix_data. - sandbox_data = - [common_sandbox_prefix_data stringByAppendingString:sandbox_data]; + return [common_sandbox_prefix_data stringByAppendingString:sandbox_data]; +} + +// Retrieve OS X version, output parameters are self explanatory. +void GetOSVersion(bool* snow_leopard_or_higher) { + int32 major_version, minor_version, bugfix_version; + base::SysInfo::OperatingSystemVersionNumbers(&major_version, + &minor_version, + &bugfix_version); + *snow_leopard_or_higher = + (major_version > 10 || (major_version == 10 && minor_version >= 6)); +} + +// static +bool Sandbox::PostProcessSandboxProfile( + NSString* sandbox_template, + NSArray* comments_to_remove, + SandboxVariableSubstitions& substitutions, + std::string *final_sandbox_profile_str) { + NSString* sandbox_data = [[sandbox_template copy] autorelease]; + + // Remove comments, e.g. ;10.6_ONLY . + for (NSString* to_remove in comments_to_remove) { + sandbox_data = [sandbox_data stringByReplacingOccurrencesOfString:to_remove + withString:@""]; + } + + // Split string on "@" characters. + std::vector<std::string> raw_sandbox_pieces; + if (Tokenize([sandbox_data UTF8String], "@", &raw_sandbox_pieces) == 0) { + LOG(FATAL) << "Bad Sandbox profile, should contain at least one token (" + << [sandbox_data UTF8String] + << ")"; + return false; + } + + // Iterate over string pieces and substitute variables, escaping as necessary. + size_t output_string_length = 0; + std::vector<std::string> processed_sandbox_pieces(raw_sandbox_pieces.size()); + for (std::vector<std::string>::iterator it = raw_sandbox_pieces.begin(); + it != raw_sandbox_pieces.end(); + ++it) { + std::string new_piece; + SandboxVariableSubstitions::iterator replacement_it = + substitutions.find(*it); + if (replacement_it == substitutions.end()) { + new_piece = *it; + } else { + // Found something to substitute. + SandboxSubstring& replacement = replacement_it->second; + switch (replacement.type()) { + case SandboxSubstring::PLAIN: + new_piece = replacement.value(); + break; + + case SandboxSubstring::LITERAL: + QuotePlainString(replacement.value(), &new_piece); + break; + + case SandboxSubstring::REGEX: + QuoteStringForRegex(replacement.value(), &new_piece); + break; + } + } + output_string_length += new_piece.size(); + processed_sandbox_pieces.push_back(new_piece); + } + + // Build final output string. + final_sandbox_profile_str->reserve(output_string_length); + + for (std::vector<std::string>::iterator it = processed_sandbox_pieces.begin(); + it != processed_sandbox_pieces.end(); + ++it) { + final_sandbox_profile_str->append(*it); + } + return true; +} + + +// Turns on the OS X sandbox for this process. + +// static +bool Sandbox::EnableSandbox(SandboxProcessType sandbox_type, + const FilePath& allowed_dir) { + // Sanity - currently only SANDBOX_TYPE_UTILITY supports a directory being + // passed in. + if (sandbox_type != SANDBOX_TYPE_UTILITY) { + DCHECK(allowed_dir.empty()) + << "Only SANDBOX_TYPE_UTILITY allows a custom directory parameter."; + } + + NSString* sandbox_data = LoadSandboxTemplate(sandbox_type); + if (!sandbox_data) { + return false; + } + + SandboxVariableSubstitions substitutions; + if (!allowed_dir.empty()) { + // Add the sandbox commands necessary to access the given directory. + // Note: this function must be called before PostProcessSandboxProfile() + // since the string it inserts contains variables that need substitution. + NSString* allowed_dir_sandbox_command = + BuildAllowDirectoryAccessSandboxString(allowed_dir, &substitutions); + + if (allowed_dir_sandbox_command) { // May be nil if function fails. + sandbox_data = [sandbox_data + stringByReplacingOccurrencesOfString:@";ENABLE_DIRECTORY_ACCESS" + withString:allowed_dir_sandbox_command]; + } + } + + NSMutableArray* tokens_to_remove = [NSMutableArray array]; // Enable verbose logging if enabled on the command line. (See common.sb // for details). const CommandLine *command_line = CommandLine::ForCurrentProcess(); bool enable_logging = - command_line->HasSwitch(switches::kEnableSandboxLogging); + command_line->HasSwitch(switches::kEnableSandboxLogging);; if (enable_logging) { - sandbox_data = [sandbox_data - stringByReplacingOccurrencesOfString:@";ENABLE_LOGGING" - withString:@""]; + [tokens_to_remove addObject:@";ENABLE_LOGGING"]; } - // Get the OS version. - int32 major_version, minor_version, bugfix_version; - base::SysInfo::OperatingSystemVersionNumbers(&major_version, - &minor_version, &bugfix_version); - bool snow_leopard_or_higher = - (major_version > 10 || (major_version == 10 && minor_version >= 6)); + bool snow_leopard_or_higher; + GetOSVersion(&snow_leopard_or_higher); // Without this, the sandbox will print a message to the system log every // time it denies a request. This floods the console with useless spew. The // (with no-log) syntax is only supported on 10.6+ if (snow_leopard_or_higher && !enable_logging) { - sandbox_data = [sandbox_data - stringByReplacingOccurrencesOfString:@"DISABLE_SANDBOX_DENIAL_LOGGING" - withString:@"(with no-log)"]; + substitutions["DISABLE_SANDBOX_DENIAL_LOGGING"] = + SandboxSubstring("(with no-log)"); } else { - sandbox_data = [sandbox_data - stringByReplacingOccurrencesOfString:@"DISABLE_SANDBOX_DENIAL_LOGGING" - withString:@""]; - } - - if (!allowed_dir.empty()) { - // The sandbox only understands "real" paths. This resolving step is - // needed so the caller doesn't need to worry about things like /var - // being a link to /private/var (like in the paths CreateNewTempDirectory() - // returns). - FilePath allowed_dir_canonical(allowed_dir); - GetCanonicalSandboxPath(&allowed_dir_canonical); - - std::string allowed_dir_escaped; - if (!QuoteStringForRegex(allowed_dir_canonical.value(), - &allowed_dir_escaped)) { - LOG(FATAL) << "Regex string quoting failed " << allowed_dir.value(); - return false; - } - NSString* allowed_dir_escaped_ns = base::SysUTF8ToNSString( - allowed_dir_escaped.c_str()); - sandbox_data = [sandbox_data - stringByReplacingOccurrencesOfString:@";ENABLE_DIRECTORY_ACCESS" - withString:@""]; - sandbox_data = [sandbox_data - stringByReplacingOccurrencesOfString:@"DIR_TO_ALLOW_ACCESS" - withString:allowed_dir_escaped_ns]; - + substitutions["DISABLE_SANDBOX_DENIAL_LOGGING"] = SandboxSubstring(""); } if (snow_leopard_or_higher) { // 10.6-only Sandbox rules. - sandbox_data = [sandbox_data - stringByReplacingOccurrencesOfString:@";10.6_ONLY" - withString:@""]; + [tokens_to_remove addObject:@";10.6_ONLY"]; // Splice the path of the user's home directory into the sandbox profile // (see renderer.sb for details). // This code is in the 10.6-only block because the sandbox syntax we use @@ -381,24 +499,25 @@ bool EnableSandbox(SandboxProcessType sandbox_type, FilePath home_dir_canonical(home_dir); GetCanonicalSandboxPath(&home_dir_canonical); - std::string home_dir_escaped; - if (!QuotePlainString(home_dir_canonical.value(), &home_dir_escaped)) { - LOG(FATAL) << "Sandbox string quoting failed"; - return false; - } - NSString* home_dir_escaped_ns = base::SysUTF8ToNSString(home_dir_escaped); - sandbox_data = [sandbox_data - stringByReplacingOccurrencesOfString:@"USER_HOMEDIR" - withString:home_dir_escaped_ns]; - } else if (major_version == 10 && minor_version < 6) { + substitutions["USER_HOMEDIR_AS_LITERAL"] = + SandboxSubstring(home_dir_canonical.value(), + SandboxSubstring::LITERAL); + } else { // Sandbox rules only for versions before 10.6. - sandbox_data = [sandbox_data - stringByReplacingOccurrencesOfString:@";BEFORE_10.6" - withString:@""]; + [tokens_to_remove addObject:@";BEFORE_10.6"]; + } + + // All information needed to assemble the final profile has been collected. + // Merge it all together. + std::string final_sandbox_profile_str; + if (!PostProcessSandboxProfile(sandbox_data, tokens_to_remove, substitutions, + &final_sandbox_profile_str)) { + return false; } + // Initialize sandbox. char* error_buff = NULL; - int error = sandbox_init([sandbox_data UTF8String], 0, &error_buff); + int error = sandbox_init(final_sandbox_profile_str.c_str(), 0, &error_buff); bool success = (error == 0 && error_buff == NULL); LOG_IF(FATAL, !success) << "Failed to initialize sandbox: " << error @@ -408,7 +527,8 @@ bool EnableSandbox(SandboxProcessType sandbox_type, return success; } -void GetCanonicalSandboxPath(FilePath* path) { +// static +void Sandbox::GetCanonicalSandboxPath(FilePath* path) { int fd = HANDLE_EINTR(open(path->value().c_str(), O_RDONLY)); if (fd < 0) { PLOG(FATAL) << "GetCanonicalSandboxPath() failed for: " diff --git a/chrome/common/sandbox_mac_diraccess_unittest.mm b/chrome/common/sandbox_mac_diraccess_unittest.mm index 5804361..fed540d 100644 --- a/chrome/common/sandbox_mac_diraccess_unittest.mm +++ b/chrome/common/sandbox_mac_diraccess_unittest.mm @@ -18,18 +18,16 @@ extern "C" { #include "testing/gtest/include/gtest/gtest.h" #include "testing/multiprocess_func_list.h" -// Tests to exercise directory-access-related restrictions of Mac sandbox. - -namespace sandbox { - -bool QuotePlainString(const std::string& str_utf8, std::string* dst); -bool QuoteStringForRegex(const std::string& str_utf8, std::string* dst); - -} // namespace sandbox - namespace { static const char* kSandboxAccessPathKey = "sandbox_dir"; +static const char* kDeniedSuffix = "_denied"; + +} // namespace + +// Tests need to be in the same namespace as the sandbox::Sandbox class to be +// useable with FRIEND_TEST() declaration. +namespace sandbox { class MacDirAccessSandboxTest : public base::MultiProcessTest { public: @@ -47,8 +45,6 @@ class MacDirAccessSandboxTest : public base::MultiProcessTest { }; TEST_F(MacDirAccessSandboxTest, StringEscape) { - using sandbox::QuotePlainString; - const struct string_escape_test_data { const char* to_escape; const char* escaped; @@ -64,14 +60,12 @@ TEST_F(MacDirAccessSandboxTest, StringEscape) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(string_escape_cases); ++i) { std::string out; std::string in(string_escape_cases[i].to_escape); - EXPECT_TRUE(QuotePlainString(in, &out)); + EXPECT_TRUE(Sandbox::QuotePlainString(in, &out)); EXPECT_EQ(string_escape_cases[i].escaped, out); } } TEST_F(MacDirAccessSandboxTest, RegexEscape) { - using sandbox::QuoteStringForRegex; - const std::string kSandboxEscapeSuffix("(/|$)"); const struct regex_test_data { const wchar_t *to_escape; @@ -89,24 +83,25 @@ TEST_F(MacDirAccessSandboxTest, RegexEscape) { std::string out; char fail_string[] = {31, 0}; char ok_string[] = {32, 0}; - EXPECT_FALSE(QuoteStringForRegex(fail_string, &out)); - EXPECT_TRUE(QuoteStringForRegex(ok_string, &out)); + EXPECT_FALSE(Sandbox::QuoteStringForRegex(fail_string, &out)); + EXPECT_TRUE(Sandbox::QuoteStringForRegex(ok_string, &out)); } // Check that all characters whose values are larger than 126 [7E] are // rejected by the regex escaping code. { std::string out; - EXPECT_TRUE(QuoteStringForRegex("}", &out)); // } == 0x7D == 125 - EXPECT_FALSE(QuoteStringForRegex("~", &out)); // ~ == 0x7E == 126 - EXPECT_FALSE(QuoteStringForRegex(WideToUTF8(L"^\u2135.\u2136$"), &out)); + EXPECT_TRUE(Sandbox::QuoteStringForRegex("}", &out)); // } == 0x7D == 125 + EXPECT_FALSE(Sandbox::QuoteStringForRegex("~", &out)); // ~ == 0x7E == 126 + EXPECT_FALSE( + Sandbox::QuoteStringForRegex(WideToUTF8(L"^\u2135.\u2136$"), &out)); } { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(regex_cases); ++i) { std::string out; std::string in = WideToUTF8(regex_cases[i].to_escape); - EXPECT_TRUE(QuoteStringForRegex(in, &out)); + EXPECT_TRUE(Sandbox::QuoteStringForRegex(in, &out)); std::string expected("^"); expected.append(regex_cases[i].escaped); expected.append(kSandboxEscapeSuffix); @@ -125,7 +120,7 @@ TEST_F(MacDirAccessSandboxTest, RegexEscape) { expected.append(kSandboxEscapeSuffix); std::string out; - EXPECT_TRUE(QuoteStringForRegex(in_utf8, &out)); + EXPECT_TRUE(Sandbox::QuoteStringForRegex(in_utf8, &out)); EXPECT_EQ(expected, out); } @@ -143,14 +138,15 @@ class ScopedDirectoryDelete { typedef scoped_ptr_malloc<FilePath, ScopedDirectoryDelete> ScopedDirectory; -// Crashy, http://crbug.com/56765. -TEST_F(MacDirAccessSandboxTest, DISABLED_SandboxAccess) { +TEST_F(MacDirAccessSandboxTest, SandboxAccess) { + using file_util::CreateDirectory; + FilePath tmp_dir; ASSERT_TRUE(file_util::CreateNewTempDirectory("", &tmp_dir)); // This step is important on OS X since the sandbox only understands "real" // paths and the paths CreateNewTempDirectory() returns are empirically in // /var which is a symlink to /private/var . - sandbox::GetCanonicalSandboxPath(&tmp_dir); + Sandbox::GetCanonicalSandboxPath(&tmp_dir); ScopedDirectory cleanup(&tmp_dir); const char* sandbox_dir_cases[] = { @@ -162,8 +158,18 @@ TEST_F(MacDirAccessSandboxTest, DISABLED_SandboxAccess) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(sandbox_dir_cases); ++i) { const char* sandbox_dir_name = sandbox_dir_cases[i]; FilePath sandbox_dir = tmp_dir.Append(sandbox_dir_name); - ASSERT_TRUE(file_util::CreateDirectory(sandbox_dir)); + ASSERT_TRUE(CreateDirectory(sandbox_dir)); ScopedDirectory cleanup_sandbox(&sandbox_dir); + + // Create a sibling directory of the sandbox dir, whose name has sandbox dir + // as a substring but to which access is denied. + std::string sibling_sandbox_dir_name_denied = + std::string(sandbox_dir_cases[i]) + kDeniedSuffix; + FilePath sibling_sandbox_dir = tmp_dir.Append( + sibling_sandbox_dir_name_denied.c_str()); + ASSERT_TRUE(CreateDirectory(sibling_sandbox_dir)); + ScopedDirectory cleanup_sandbox_sibling(&sibling_sandbox_dir); + EXPECT_TRUE(CheckSandbox(sandbox_dir.value())); } } @@ -173,29 +179,36 @@ MULTIPROCESS_TEST_MAIN(mac_sandbox_path_access) { if (!sandbox_allowed_dir) return -1; - // Build up a sandbox profile that only allows access to DIR_TO_ALLOW_ACCESS. + // Build up a sandbox profile that only allows access to a single directory. NSString *sandbox_profile = @"(version 1)" \ "(deny default)" \ "(allow signal (target self))" \ "(allow sysctl-read)" \ - "(allow file-read-metadata)" \ - "(allow file-read* file-write* (regex #\"DIR_TO_ALLOW_ACCESS\"))"; + ";ENABLE_DIRECTORY_ACCESS"; std::string allowed_dir(sandbox_allowed_dir); - std::string allowed_dir_escaped; - if (!sandbox::QuoteStringForRegex(allowed_dir, &allowed_dir_escaped)) { - LOG(ERROR) << "Regex string quoting failed " << allowed_dir; + Sandbox::SandboxVariableSubstitions substitutions; + NSString* allow_dir_sandbox_code = + Sandbox::BuildAllowDirectoryAccessSandboxString( + FilePath(sandbox_allowed_dir), + &substitutions); + sandbox_profile = [sandbox_profile + stringByReplacingOccurrencesOfString:@";ENABLE_DIRECTORY_ACCESS" + withString:allow_dir_sandbox_code]; + + std::string final_sandbox_profile_str; + if (!Sandbox::PostProcessSandboxProfile(sandbox_profile, + [NSArray array], + substitutions, + &final_sandbox_profile_str)) { + LOG(ERROR) << "Call to PostProcessSandboxProfile() failed"; return -1; } - NSString* allowed_dir_escaped_ns = base::SysUTF8ToNSString( - allowed_dir_escaped.c_str()); - sandbox_profile = [sandbox_profile - stringByReplacingOccurrencesOfString:@"DIR_TO_ALLOW_ACCESS" - withString:allowed_dir_escaped_ns]; + // Enable Sandbox. char* error_buff = NULL; - int error = sandbox_init([sandbox_profile UTF8String], 0, &error_buff); + int error = sandbox_init(final_sandbox_profile_str.c_str(), 0, &error_buff); if (error == -1) { LOG(ERROR) << "Failed to Initialize Sandbox: " << error_buff; return -1; @@ -223,8 +236,9 @@ MULTIPROCESS_TEST_MAIN(mac_sandbox_path_access) { // Try to write a file who's name has the same prefix as the directory we // allow access to. FilePath basename = allowed_dir_path.BaseName(); + FilePath allowed_parent_dir = allowed_dir_path.DirName(); std::string tricky_filename = basename.value() + "123"; - FilePath denied_file2 = allowed_dir_path.DirName().Append(tricky_filename); + FilePath denied_file2 = allowed_parent_dir.Append(tricky_filename); if (open(allowed_file.value().c_str(), O_WRONLY | O_CREAT) <= 0) { PLOG(ERROR) << "Sandbox overly restrictive: failed to write (" @@ -233,6 +247,43 @@ MULTIPROCESS_TEST_MAIN(mac_sandbox_path_access) { return -1; } + // Test that we deny access to a sibling of the sandboxed directory whose + // name has the sandboxed directory name as a substring. e.g. if the sandbox + // directory is /foo/baz then test /foo/baz_denied. + { + struct stat tmp_stat_info; + std::string denied_sibling = + std::string(sandbox_allowed_dir) + kDeniedSuffix; + if (stat(denied_sibling.c_str(), &tmp_stat_info) > 0) { + PLOG(ERROR) << "Sandbox breach: was able to stat (" + << denied_sibling.c_str() + << ")"; + return -1; + } + } + + // Test that we can stat parent directories of the "allowed" directory. + { + struct stat tmp_stat_info; + if (stat(allowed_parent_dir.value().c_str(), &tmp_stat_info) != 0) { + PLOG(ERROR) << "Sandbox overly restrictive: unable to stat (" + << allowed_parent_dir.value() + << ")"; + return -1; + } + } + + // Test that we can't stat files outside the "allowed" directory. + { + struct stat tmp_stat_info; + if (stat(denied_file1.value().c_str(), &tmp_stat_info) > 0) { + PLOG(ERROR) << "Sandbox breach: was able to stat (" + << denied_file1.value() + << ")"; + return -1; + } + } + if (open(denied_file1.value().c_str(), O_WRONLY | O_CREAT) > 0) { PLOG(ERROR) << "Sandbox breach: was able to write (" << denied_file1.value() @@ -250,4 +301,4 @@ MULTIPROCESS_TEST_MAIN(mac_sandbox_path_access) { return 0; } -} // namespace +} // namespace sandbox diff --git a/chrome/common/sandbox_mac_fontloading_unittest.mm b/chrome/common/sandbox_mac_fontloading_unittest.mm index 834fc0b..17f4a4a 100644 --- a/chrome/common/sandbox_mac_fontloading_unittest.mm +++ b/chrome/common/sandbox_mac_fontloading_unittest.mm @@ -16,6 +16,7 @@ namespace { using sandboxtest::MacSandboxTest; +using sandbox::Sandbox; bool CGFontFromFontContainer(ATSFontContainerRef container, CGFontRef* out) { // Count the number of fonts that were loaded. @@ -91,16 +92,11 @@ bool FontLoadingTestCase::BeforeSandboxInit() { return false; } - if (!font_shmem_->Create("", false, false, font_data_length_)) { + if (!font_shmem_->CreateAndMapAnonymous(font_data_length_)) { LOG(ERROR) << "SharedMemory::Create failed"; return false; } - if (!font_shmem_->Map(font_data_length_)) { - LOG(ERROR) << "SharedMemory::Map failed"; - return false; - } - memcpy(font_shmem_->memory(), font_data.c_str(), font_data_length_); if (!font_shmem_->Unmap()) { LOG(ERROR) << "SharedMemory::Unmap failed"; @@ -126,32 +122,32 @@ bool FontLoadingTestCase::SandboxedTest() { // Unload the font container when done. ScopedFontContainer scoped_unloader(font_container); - CGFontRef font_ref; - if (!CGFontFromFontContainer(font_container, &font_ref)) { + CGFontRef cg_font_ref; + if (!CGFontFromFontContainer(font_container, &cg_font_ref)) { LOG(ERROR) << "CGFontFromFontContainer failed"; return false; } - if (!font_ref) { + if (!cg_font_ref) { LOG(ERROR) << "Got NULL CGFontRef"; return false; } - base::mac::ScopedCFTypeRef<CGFontRef> cgfont; - cgfont.reset(font_ref); + base::mac::ScopedCFTypeRef<CGFontRef> cgfont(cg_font_ref); + + CTFontRef ct_font_ref = + CTFontCreateWithGraphicsFont(cgfont.get(), 16.0, NULL, NULL); + base::mac::ScopedCFTypeRef<CTFontRef> ctfont(ct_font_ref); - const NSFont* nsfont = reinterpret_cast<const NSFont*>( - CTFontCreateWithGraphicsFont(cgfont.get(), 16.0, - NULL, NULL)); - if (!nsfont) { + if (!ct_font_ref) { LOG(ERROR) << "CTFontCreateWithGraphicsFont() failed"; return false; } // Do something with the font to make sure it's loaded. - CGFloat cap_height = [nsfont capHeight]; + CGFloat cap_height = CTFontGetCapHeight(ct_font_ref); if (cap_height <= 0.0) { - LOG(ERROR) << "Got bad value for [NSFont capHeight] " << cap_height; + LOG(ERROR) << "Got bad value for CTFontGetCapHeight " << cap_height; return false; } @@ -174,7 +170,7 @@ TEST_F(MacSandboxTest, FontLoadingTest) { file_util::WriteFileDescriptor(fileno(temp_file), static_cast<const char *>(font_data.memory()), font_data_size); - ASSERT_TRUE(RunTestInSandbox(sandbox::SANDBOX_TYPE_RENDERER, + ASSERT_TRUE(RunTestInSandbox(Sandbox::SANDBOX_TYPE_RENDERER, "FontLoadingTestCase", temp_file_path.value().c_str())); temp_file_closer.reset(); ASSERT_TRUE(file_util::Delete(temp_file_path, false)); diff --git a/chrome/common/sandbox_mac_unittest_helper.h b/chrome/common/sandbox_mac_unittest_helper.h index 960a4cf..674b246 100644 --- a/chrome/common/sandbox_mac_unittest_helper.h +++ b/chrome/common/sandbox_mac_unittest_helper.h @@ -50,7 +50,7 @@ class MacSandboxTest : public base::MultiProcessTest { // to the child process runing in the sandbox. // Returns true if the test passes, false if either of the functions in // the corresponding MacSandboxTestCase return false. - bool RunTestInSandbox(sandbox::SandboxProcessType sandbox_type, + bool RunTestInSandbox(sandbox::Sandbox::SandboxProcessType sandbox_type, const char* test_name, const char* test_data); diff --git a/chrome/common/sandbox_mac_unittest_helper.mm b/chrome/common/sandbox_mac_unittest_helper.mm index 007530e..4e885b5 100644 --- a/chrome/common/sandbox_mac_unittest_helper.mm +++ b/chrome/common/sandbox_mac_unittest_helper.mm @@ -16,6 +16,8 @@ extern "C" { #include "chrome/common/sandbox_mac.h" #include "testing/multiprocess_func_list.h" +using sandbox::Sandbox; + namespace { const char* kSandboxTypeKey = "CHROMIUM_SANDBOX_SANDBOX_TYPE"; @@ -52,10 +54,10 @@ bool MacSandboxTest:: RunTestInAllSandboxTypes(const char* test_name, const char* test_data) { // Go through all the sandbox types, and run the test case in each of them // if one fails, abort. - for(int i = static_cast<int>(sandbox::SANDBOX_TYPE_FIRST_TYPE); - i < sandbox::SANDBOX_AFTER_TYPE_LAST_TYPE; + for(int i = static_cast<int>(Sandbox::SANDBOX_TYPE_FIRST_TYPE); + i < Sandbox::SANDBOX_AFTER_TYPE_LAST_TYPE; ++i) { - if (!RunTestInSandbox(static_cast<sandbox::SandboxProcessType>(i), + if (!RunTestInSandbox(static_cast<Sandbox::SandboxProcessType>(i), test_name, test_data)) { LOG(ERROR) << "Sandboxed test (" << test_name << ")" << "Failed in sandbox type " << i << @@ -66,7 +68,7 @@ bool MacSandboxTest:: RunTestInAllSandboxTypes(const char* test_name, return true; } -bool MacSandboxTest::RunTestInSandbox(sandbox::SandboxProcessType sandbox_type, +bool MacSandboxTest::RunTestInSandbox(Sandbox::SandboxProcessType sandbox_type, const char* test_name, const char* test_data) { std::stringstream s; @@ -116,8 +118,8 @@ MULTIPROCESS_TEST_MAIN(mac_sandbox_test_runner) { LOG(ERROR) << "Sandbox type not specified"; return -1; } - sandbox::SandboxProcessType sandbox_type = - static_cast<sandbox::SandboxProcessType>(atoi(sandbox_type_str)); + Sandbox::SandboxProcessType sandbox_type = + static_cast<Sandbox::SandboxProcessType>(atoi(sandbox_type_str)); char* sandbox_test_name = getenv(kSandboxTestNameKey); if (!sandbox_test_name) { LOG(ERROR) << "Sandbox test name not specified"; @@ -141,9 +143,9 @@ MULTIPROCESS_TEST_MAIN(mac_sandbox_test_runner) { return -1; } - sandbox::SandboxWarmup(); + Sandbox::SandboxWarmup(); - if (!sandbox::EnableSandbox(sandbox_type, FilePath())) { + if (!Sandbox::EnableSandbox(sandbox_type, FilePath())) { LOG(ERROR) << "Failed to initialize sandbox " << sandbox_type; return -1; } diff --git a/chrome/common/sandbox_policy.cc b/chrome/common/sandbox_policy.cc index f97da8a..0ab7563 100644 --- a/chrome/common/sandbox_policy.cc +++ b/chrome/common/sandbox_policy.cc @@ -8,7 +8,8 @@ #include "app/win_util.h" #include "base/command_line.h" -#include "base/debug_util.h" +#include "base/debug/debugger.h" +#include "base/debug/trace_event.h" #include "base/file_util.h" #include "base/logging.h" #include "base/path_service.h" @@ -16,7 +17,6 @@ #include "base/stringprintf.h" #include "base/string_number_conversions.h" #include "base/string_util.h" -#include "base/trace_event.h" #include "base/win/windows_version.h" #include "chrome/common/child_process_info.h" #include "chrome/common/chrome_constants.h" @@ -513,7 +513,7 @@ base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line, // Prefetch hints on windows: // Using a different prefetch profile per process type will allow Windows // to create separate pretetch settings for browser, renderer etc. - cmd_line->AppendArg(StringPrintf("/prefetch:%d", type)); + cmd_line->AppendArg(base::StringPrintf("/prefetch:%d", type)); if (!in_sandbox) { base::LaunchApp(*cmd_line, false, false, &process); @@ -579,7 +579,7 @@ base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line, // Help the process a little. It can't start the debugger by itself if // the process is in a sandbox. if (child_needs_help) - DebugUtil::SpawnDebuggerOnProcess(target.dwProcessId); + base::debug::SpawnDebuggerOnProcess(target.dwProcessId); return process; } diff --git a/chrome/common/security_filter_peer.cc b/chrome/common/security_filter_peer.cc index 9b0815b..3be8864 100644 --- a/chrome/common/security_filter_peer.cc +++ b/chrome/common/security_filter_peer.cc @@ -53,7 +53,7 @@ SecurityFilterPeer* SecurityFilterPeer::CreateSecurityFilterPeerForFrame( webkit_glue::ResourceLoaderBridge::Peer* peer, int os_error) { // TODO(jcampan): use a different message when getting a phishing/malware // error. - std::string html = StringPrintf( + std::string html = base::StringPrintf( "<html><meta charset='UTF-8'>" "<body style='background-color:#990000;color:white;'>" "%s</body></html>", @@ -90,10 +90,6 @@ void SecurityFilterPeer::OnCompletedRequest(const URLRequestStatus& status, NOTREACHED(); } -GURL SecurityFilterPeer::GetURLForDebugging() const { - return original_peer_->GetURLForDebugging(); -} - // static void ProcessResponseInfo( const webkit_glue::ResourceResponseInfo& info_in, diff --git a/chrome/common/security_filter_peer.h b/chrome/common/security_filter_peer.h index 8365ebd..2559bf3 100644 --- a/chrome/common/security_filter_peer.h +++ b/chrome/common/security_filter_peer.h @@ -44,7 +44,6 @@ class SecurityFilterPeer : public webkit_glue::ResourceLoaderBridge::Peer { virtual void OnCompletedRequest(const URLRequestStatus& status, const std::string& security_info, const base::Time& completion_time); - virtual GURL GetURLForDebugging() const; protected: SecurityFilterPeer(webkit_glue::ResourceLoaderBridge* resource_loader_bridge, diff --git a/chrome/common/service_process_util.cc b/chrome/common/service_process_util.cc index f636f2b..489610a 100644 --- a/chrome/common/service_process_util.cc +++ b/chrome/common/service_process_util.cc @@ -212,8 +212,8 @@ bool ServiceProcessState::CreateSharedData() { return false; uint32 alloc_size = sizeof(ServiceProcessSharedData); - if (!shared_mem_service_data->Create(GetServiceProcessSharedMemName(), false, - true, alloc_size)) + if (!shared_mem_service_data->CreateNamed(GetServiceProcessSharedMemName(), + true, alloc_size)) return false; if (!shared_mem_service_data->Map(alloc_size)) diff --git a/chrome/common/speech_input_result.h b/chrome/common/speech_input_result.h new file mode 100644 index 0000000..2807412 --- /dev/null +++ b/chrome/common/speech_input_result.h @@ -0,0 +1,33 @@ +// 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_COMMON_SPEECH_INPUT_RESULT_H_ +#define CHROME_COMMON_SPEECH_INPUT_RESULT_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/string16.h" + +namespace speech_input { + +struct SpeechInputResultItem { + string16 utterance; + double confidence; + + SpeechInputResultItem() + : confidence(0.0) { + } + + SpeechInputResultItem(const string16 utterance_value, double confidence_value) + : utterance(utterance_value), + confidence(confidence_value) { + } +}; + +typedef std::vector<SpeechInputResultItem> SpeechInputResultArray; + +} // namespace speech_input + +#endif // CHROME_COMMON_SPEECH_INPUT_RESULT_H_ diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc index 1c65a66..427c933 100644 --- a/chrome/common/url_constants.cc +++ b/chrome/common/url_constants.cc @@ -1,3 +1,4 @@ + // 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. @@ -11,6 +12,7 @@ namespace chrome { const char kAboutScheme[] = "about"; const char kBlobScheme[] = "blob"; +const char kChromeDevToolsScheme[] = "chrome-devtools"; const char kChromeInternalScheme[] = "chrome-internal"; const char kChromeUIScheme[] = "chrome"; const char kDataScheme[] = "data"; @@ -38,6 +40,7 @@ const char* kSavableSchemes[] = { kFileScheme, kFtpScheme, kExtensionScheme, + kChromeDevToolsScheme, kChromeUIScheme, NULL }; @@ -46,6 +49,7 @@ const char kAboutAboutURL[] = "about:about"; const char kAboutAppCacheInternalsURL[] = "about:appcache-internals"; const char kAboutBlankURL[] = "about:blank"; const char kAboutCacheURL[] = "about:cache"; +const char kAboutConflicts[] = "about:conflicts"; const char kAboutCrashURL[] = "about:crash"; const char kAboutCreditsURL[] = "about:credits"; const char kAboutDNSURL[] = "about:dns"; @@ -71,8 +75,9 @@ const char kChromeUIAboutURL[] = "chrome://settings/about"; const char kChromeUIAppLauncherURL[] = "chrome://newtab/#mode=app-launcher"; const char kChromeUIBookmarksURL[] = "chrome://bookmarks/"; const char kChromeUIBugReportURL[] = "chrome://bugreport/"; +const char kChromeUIConflictsURL[] = "chrome://conflicts/"; const char kChromeUIConstrainedHTMLTestURL[] = "chrome://constrained-test/"; -const char kChromeUIDevToolsURL[] = "chrome://devtools/"; +const char kChromeUIDevToolsURL[] = "chrome-devtools://devtools/"; const char kChromeUIDownloadsURL[] = "chrome://downloads/"; const char kChromeUIExtensionsURL[] = "chrome://extensions/"; const char kChromeUIFavIconURL[] = "chrome://favicon/"; @@ -85,10 +90,12 @@ const char kChromeUINewTabURL[] = "chrome://newtab"; const char kChromeUIPluginsURL[] = "chrome://plugins/"; const char kChromeUIPrintURL[] = "chrome://print/"; const char kChromeUISettingsURL[] = "chrome://settings/"; +const char kChromeUITextfieldsURL[] = "chrome://textfields/"; #if defined(OS_CHROMEOS) const char kChromeUIFileBrowseURL[] = "chrome://filebrowse/"; const char kChromeUIImageBurnerURL[] = "chrome://imageburner/"; +const char kChromeUIKeyboardOverlayURL[] = "chrome://keyboardoverlay/"; const char kChromeUIMediaplayerURL[] = "chrome://mediaplayer/"; const char kChromeUIMobileSetupURL[] = "chrome://mobilesetup/"; const char kChromeUIRegisterPageURL[] = "chrome://register/"; @@ -99,6 +106,7 @@ const char kChromeUISystemInfoURL[] = "chrome://system/"; // Keep this list sorted please. const char kChromeUIBookmarksHost[] = "bookmarks"; const char kChromeUIBugReportHost[] = "bugreport"; +const char kChromeUIConflictsHost[] = "conflicts"; const char kChromeUIDevToolsHost[] = "devtools"; const char kChromeUIDialogHost[] = "dialog"; const char kChromeUIDownloadsHost[] = "downloads"; @@ -119,12 +127,14 @@ const char kChromeUIResourcesHost[] = "resources"; const char kChromeUIScreenshotPath[] = "screenshots"; const char kChromeUISettingsHost[] = "settings"; const char kChromeUISyncResourcesHost[] = "syncresources"; +const char kChromeUITextfieldsHost[] = "textfields"; const char kChromeUIThemePath[] = "theme"; const char kChromeUIThumbnailPath[] = "thumb"; #if defined(OS_CHROMEOS) const char kChromeUIFileBrowseHost[] = "filebrowse"; const char kChromeUIImageBurnerHost[] = "imageburner"; +const char kChromeUIKeyboardOverlayHost[] = "keyboardoverlay"; const char kChromeUIMediaplayerHost[] = "mediaplayer"; const char kChromeUIMobileSetupHost[] = "mobilesetup"; const char kChromeUIRegisterPageHost[] = "register"; @@ -135,6 +145,8 @@ const char kChromeUIWrenchMenu[] = "wrench-menu"; const char kChromeUINetworkMenu[] = "network-menu"; #endif +const char kUnreachableWebDataURL[] = "chrome://chromewebdata/"; + const char kAppCacheViewInternalsURL[] = "chrome://appcache-internals/"; const char kBlobViewInternalsURL[] = "chrome://blob-internals/"; @@ -165,6 +177,7 @@ const char kSystemOptionsSubPage[] = "system"; void RegisterChromeSchemes() { // Don't need "chrome-internal" which was used in old versions of Chrome for // the new tab page. + url_util::AddStandardScheme(kChromeDevToolsScheme); url_util::AddStandardScheme(kChromeUIScheme); url_util::AddStandardScheme(kGearsScheme); url_util::AddStandardScheme(kExtensionScheme); diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h index e11e5b5..05e32e6 100644 --- a/chrome/common/url_constants.h +++ b/chrome/common/url_constants.h @@ -13,6 +13,7 @@ namespace chrome { // Canonical schemes you can use as input to GURL.SchemeIs(). extern const char kAboutScheme[]; extern const char kBlobScheme[]; +extern const char kChromeDevToolsScheme[]; extern const char kChromeInternalScheme[]; extern const char kChromeUIScheme[]; // The scheme used for DOMUIs. extern const char kCrosScheme[]; // The scheme used for ChromeOS. @@ -40,6 +41,7 @@ extern const char kAboutAboutURL[]; extern const char kAboutAppCacheInternalsURL[]; extern const char kAboutBlankURL[]; extern const char kAboutBrowserCrash[]; +extern const char kAboutConflicts[]; extern const char kAboutCacheURL[]; extern const char kAboutCrashURL[]; extern const char kAboutCreditsURL[]; @@ -64,6 +66,7 @@ extern const char kChromeUIAboutURL[]; extern const char kChromeUIAppLauncherURL[]; extern const char kChromeUIBookmarksURL[]; extern const char kChromeUIBugReportURL[]; +extern const char kChromeUIConflictsURL[]; extern const char kChromeUIConstrainedHTMLTestURL[]; extern const char kChromeUIDevToolsURL[]; extern const char kChromeUIDownloadsURL[]; @@ -78,10 +81,12 @@ extern const char kChromeUINewTabURL[]; extern const char kChromeUIPluginsURL[]; extern const char kChromeUIPrintURL[]; extern const char kChromeUISettingsURL[]; +extern const char kChromeUITextfieldsURL[]; #if defined(OS_CHROMEOS) extern const char kChromeUIFileBrowseURL[]; extern const char kChromeUIImageBurnerURL[]; +extern const char kChromeUIKeyboardOverlayURL[]; extern const char kChromeUIMediaplayerURL[]; extern const char kChromeUIMobileSetupURL[]; extern const char kChromeUIRegisterPageURL[]; @@ -93,6 +98,7 @@ extern const char kChromeUISystemInfoURL[]; // above. extern const char kChromeUIBookmarksHost[]; extern const char kChromeUIBugReportHost[]; +extern const char kChromeUIConflictsHost[]; extern const char kChromeUIDevToolsHost[]; extern const char kChromeUIDialogHost[]; extern const char kChromeUIDownloadsHost[]; @@ -101,7 +107,6 @@ extern const char kChromeUIFavIconHost[]; extern const char kChromeUIFlagsHost[]; extern const char kChromeUIHistory2Host[]; extern const char kChromeUIHistoryHost[]; -extern const char kChromeUIInspectorHost[]; extern const char kChromeUIKeyboardHost[]; extern const char kChromeUINetInternalsHost[]; extern const char kChromeUINewTabHost[]; @@ -113,12 +118,14 @@ extern const char kChromeUIResourcesHost[]; extern const char kChromeUIScreenshotPath[]; extern const char kChromeUISettingsHost[]; extern const char kChromeUISyncResourcesHost[]; +extern const char kChromeUITextfieldsHost[]; extern const char kChromeUIThemePath[]; extern const char kChromeUIThumbnailPath[]; #if defined(OS_CHROMEOS) extern const char kChromeUIFileBrowseHost[]; extern const char kChromeUIImageBurnerHost[]; +extern const char kChromeUIKeyboardOverlayHost[]; extern const char kChromeUIMediaplayerHost[]; extern const char kChromeUIMobileSetupHost[]; extern const char kChromeUIRegisterPageHost[]; @@ -129,6 +136,9 @@ extern const char kChromeUIWrenchMenu[]; extern const char kChromeUINetworkMenu[]; #endif +// Special URL used to start a navigation to an error page. +extern const char kUnreachableWebDataURL[]; + // AppCache related URL. extern const char kAppCacheViewInternalsURL[]; diff --git a/chrome/common/view_types.h b/chrome/common/view_types.h index 7a1caf0..2d9c539 100644 --- a/chrome/common/view_types.h +++ b/chrome/common/view_types.h @@ -21,7 +21,6 @@ class ViewType { DEV_TOOLS_UI, INTERSTITIAL_PAGE, NOTIFICATION, - HTML_DIALOG_UI, }; // Constant strings corresponding to the Type enumeration values. Used diff --git a/chrome/common/webkit_param_traits.cc b/chrome/common/webkit_param_traits.cc index 26465dd..909d7fd 100644 --- a/chrome/common/webkit_param_traits.cc +++ b/chrome/common/webkit_param_traits.cc @@ -40,7 +40,8 @@ void ParamTraits<WebKit::WebRect>::Log(const param_type& p, std::string* l) { l->append(")"); } -void ParamTraits<WebKit::WebScreenInfo>::Write(Message* m, const param_type& p) { +void ParamTraits<WebKit::WebScreenInfo>::Write(Message* m, + const param_type& p) { WriteParam(m, p.depth); WriteParam(m, p.depthPerComponent); WriteParam(m, p.isMonochrome); @@ -101,8 +102,8 @@ void ParamTraits<WebKit::WebFindOptions>::Log(const param_type& p, void ParamTraits<WebKit::WebCache::ResourceTypeStat>::Log( const param_type& p, std::string* l) { - l->append(StringPrintf("%" PRIuS " %" PRIuS " %" PRIuS " %" PRIuS, - p.count, p.size, p.liveSize, p.decodedSize)); + l->append(base::StringPrintf("%" PRIuS " %" PRIuS " %" PRIuS " %" PRIuS, + p.count, p.size, p.liveSize, p.decodedSize)); } void ParamTraits<WebKit::WebMediaPlayerAction>::Write(Message* m, diff --git a/chrome/common/webmessageportchannel_impl.cc b/chrome/common/webmessageportchannel_impl.cc index 888ab78..b7a9388 100644 --- a/chrome/common/webmessageportchannel_impl.cc +++ b/chrome/common/webmessageportchannel_impl.cc @@ -69,8 +69,8 @@ void WebMessagePortChannelImpl::entangle(WebMessagePortChannel* channel) { // The message port ids might not be set up yet, if this channel wasn't // created on the main thread. So need to wait until we're on the main thread // before getting the other message port id. - scoped_refptr<WebMessagePortChannelImpl> webchannel = - static_cast<WebMessagePortChannelImpl*>(channel); + scoped_refptr<WebMessagePortChannelImpl> webchannel( + static_cast<WebMessagePortChannelImpl*>(channel)); Entangle(webchannel); } |
