| // Copyright 2018 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/notifications/win/notification_launch_id.h" |
| |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/common/chrome_switches.h" |
| |
| namespace { |
| |
| enum LaunchIdComponents { |
| NORMAL = 0, |
| BUTTON_INDEX = 1, |
| CONTEXT_MENU = 2, |
| DISMISS_BUTTON = 3, |
| }; |
| |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. |
| enum class LaunchIdDecodeStatus { |
| kSuccess = 0, |
| kEmptyInput = 1, |
| kComponentIdInvalid = 2, |
| kComponentIdOutOfRange = 3, |
| kTokensInsufficient = 4, |
| kButtonIndexInvalid = 5, |
| kTypeInvalid = 6, |
| kTypeOutOfRange = 7, |
| kMaxValue = kTypeOutOfRange, |
| }; |
| |
| void LogLaunchIdDecodeStatus(LaunchIdDecodeStatus status) { |
| UMA_HISTOGRAM_ENUMERATION("Notifications.Windows.LaunchIdDecodeStatus", |
| status); |
| } |
| |
| } // namespace |
| |
| NotificationLaunchId::NotificationLaunchId() = default; |
| |
| NotificationLaunchId::NotificationLaunchId(const NotificationLaunchId& other) = |
| default; |
| |
| NotificationLaunchId::NotificationLaunchId( |
| NotificationHandler::Type notification_type, |
| const std::string& notification_id, |
| const std::string& profile_id, |
| bool incognito, |
| const GURL& origin_url) |
| : notification_type_(notification_type), |
| notification_id_(notification_id), |
| profile_id_(profile_id), |
| incognito_(incognito), |
| origin_url_(origin_url), |
| is_valid_(true) {} |
| |
| NotificationLaunchId::NotificationLaunchId(const std::string& input) { |
| if (input.empty()) { |
| LogLaunchIdDecodeStatus(LaunchIdDecodeStatus::kEmptyInput); |
| return; |
| } |
| |
| const char kDelimiter[] = "|"; |
| std::vector<std::string> tokens = base::SplitString( |
| input, kDelimiter, base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| |
| // Figure out what kind of input string it is. |
| int number; |
| if (!base::StringToInt(tokens[0], &number)) { |
| LogLaunchIdDecodeStatus(LaunchIdDecodeStatus::kComponentIdInvalid); |
| return; |
| } |
| LaunchIdComponents components = static_cast<LaunchIdComponents>(number); |
| |
| // The final token may contain the separation character. |
| size_t min_num_tokens; |
| switch (components) { |
| case NORMAL: |
| // type|notification_type|profile_id|incognito|origin|notification_id |
| min_num_tokens = 6; |
| break; |
| case BUTTON_INDEX: |
| // type|button_index|notification_type|profile_id|incognito|origin|notification_id |
| min_num_tokens = 7; |
| break; |
| case CONTEXT_MENU: |
| // type|notification_type|profile_id|incognito|origin|notification_id |
| min_num_tokens = 6; |
| is_for_context_menu_ = true; |
| break; |
| case DISMISS_BUTTON: |
| // type|notification_type|profile_id|incognito|origin|notification_id |
| min_num_tokens = 6; |
| is_for_dismiss_button_ = true; |
| break; |
| default: |
| // |components| has an invalid value. |
| LogLaunchIdDecodeStatus(LaunchIdDecodeStatus::kComponentIdOutOfRange); |
| return; |
| } |
| |
| if (tokens.size() < min_num_tokens) { |
| LogLaunchIdDecodeStatus(LaunchIdDecodeStatus::kTokensInsufficient); |
| return; |
| } |
| |
| if (components == BUTTON_INDEX) { |
| if (!base::StringToInt(tokens[1], &button_index_)) { |
| LogLaunchIdDecodeStatus(LaunchIdDecodeStatus::kButtonIndexInvalid); |
| return; |
| } |
| tokens.erase(tokens.begin()); |
| } |
| |
| int type = -1; |
| if (!base::StringToInt(tokens[1], &type)) { |
| LogLaunchIdDecodeStatus(LaunchIdDecodeStatus::kTypeInvalid); |
| return; |
| } |
| if (type < 0 || type > static_cast<int>(NotificationHandler::Type::MAX)) { |
| LogLaunchIdDecodeStatus(LaunchIdDecodeStatus::kTypeOutOfRange); |
| return; |
| } |
| notification_type_ = static_cast<NotificationHandler::Type>(type); |
| |
| profile_id_ = tokens[2]; |
| incognito_ = tokens[3] == "1" ? true : false; |
| origin_url_ = GURL(tokens[4]); |
| |
| notification_id_.clear(); |
| // Notification IDs is the rest of the string (delimiters not stripped off). |
| const size_t kMinVectorSize = 5; |
| for (size_t i = kMinVectorSize; i < tokens.size(); ++i) { |
| if (i > kMinVectorSize) |
| notification_id_ += kDelimiter; |
| notification_id_ += tokens[i]; |
| } |
| |
| is_valid_ = true; |
| LogLaunchIdDecodeStatus(LaunchIdDecodeStatus::kSuccess); |
| } |
| |
| std::string NotificationLaunchId::Serialize() const { |
| // The pipe was chosen as delimiter because it is invalid for directory paths |
| // and unsafe for origins -- and should therefore be encoded (as per |
| // http://www.ietf.org/rfc/rfc1738.txt). |
| std::string prefix; |
| LaunchIdComponents type; |
| if (is_for_context_menu_) |
| type = CONTEXT_MENU; |
| else if (is_for_dismiss_button_) |
| type = DISMISS_BUTTON; |
| else if (button_index_ > -1) |
| type = BUTTON_INDEX; |
| else |
| type = NORMAL; |
| |
| if (button_index_ > -1) |
| prefix = base::StringPrintf("|%d", button_index_); |
| return base::StringPrintf( |
| "%d%s|%d|%s|%d|%s|%s", type, prefix.c_str(), |
| static_cast<int>(notification_type_), profile_id_.c_str(), incognito_, |
| origin_url_.spec().c_str(), notification_id_.c_str()); |
| } |
| |
| // static |
| std::string NotificationLaunchId::GetProfileIdFromLaunchId( |
| const std::wstring& launch_id_str) { |
| NotificationLaunchId launch_id(base::WideToUTF8(launch_id_str)); |
| |
| // The launch_id_invalid failure is logged via HandleActivation(). We don't |
| // re-log it here, which would skew the UMA failure metrics. |
| return launch_id.is_valid() ? launch_id.profile_id() : std::string(); |
| } |
| |
| // static |
| std::string NotificationLaunchId::GetNotificationLaunchProfileId( |
| const base::CommandLine& command_line) { |
| if (command_line.HasSwitch(switches::kNotificationLaunchId)) { |
| return NotificationLaunchId::GetProfileIdFromLaunchId( |
| command_line.GetSwitchValueNative(switches::kNotificationLaunchId)); |
| } |
| return std::string(); |
| } |