| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/extensions/echo_private_api.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/files/file_util.h" |
| #include "base/location.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/post_task.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "chrome/browser/ash/notifications/echo_dialog_view.h" |
| #include "chrome/browser/ash/settings/cros_settings.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_navigator.h" |
| #include "chrome/browser/ui/browser_navigator_params.h" |
| #include "chrome/common/extensions/api/echo_private.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "chromeos/system/statistics_provider.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/prefs/scoped_user_pref_update.h" |
| #include "content/public/browser/web_contents.h" |
| #include "extensions/browser/extension_file_task_runner.h" |
| #include "extensions/browser/view_type_utils.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/mojom/view_type.mojom.h" |
| |
| namespace echo_api = extensions::api::echo_private; |
| |
| namespace chromeos { |
| |
| namespace echo_offer { |
| |
| void RegisterPrefs(PrefRegistrySimple* registry) { |
| registry->RegisterDictionaryPref(prefs::kEchoCheckedOffers); |
| } |
| |
| } // namespace echo_offer |
| |
| } // namespace chromeos |
| |
| EchoPrivateGetRegistrationCodeFunction:: |
| EchoPrivateGetRegistrationCodeFunction() {} |
| |
| EchoPrivateGetRegistrationCodeFunction:: |
| ~EchoPrivateGetRegistrationCodeFunction() {} |
| |
| ExtensionFunction::ResponseValue |
| EchoPrivateGetRegistrationCodeFunction::GetRegistrationCode( |
| const std::string& type) { |
| // Possible ECHO code type and corresponding key name in StatisticsProvider. |
| const std::string kCouponType = "COUPON_CODE"; |
| const std::string kGroupType = "GROUP_CODE"; |
| |
| chromeos::system::StatisticsProvider* provider = |
| chromeos::system::StatisticsProvider::GetInstance(); |
| std::string result; |
| if (type == kCouponType) { |
| provider->GetMachineStatistic(chromeos::system::kOffersCouponCodeKey, |
| &result); |
| } else if (type == kGroupType) { |
| provider->GetMachineStatistic(chromeos::system::kOffersGroupCodeKey, |
| &result); |
| } |
| |
| return ArgumentList(echo_api::GetRegistrationCode::Results::Create(result)); |
| } |
| |
| ExtensionFunction::ResponseAction |
| EchoPrivateGetRegistrationCodeFunction::Run() { |
| std::unique_ptr<echo_api::GetRegistrationCode::Params> params = |
| echo_api::GetRegistrationCode::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| return RespondNow(GetRegistrationCode(params->type)); |
| } |
| |
| EchoPrivateSetOfferInfoFunction::EchoPrivateSetOfferInfoFunction() {} |
| |
| EchoPrivateSetOfferInfoFunction::~EchoPrivateSetOfferInfoFunction() {} |
| |
| ExtensionFunction::ResponseAction EchoPrivateSetOfferInfoFunction::Run() { |
| std::unique_ptr<echo_api::SetOfferInfo::Params> params = |
| echo_api::SetOfferInfo::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| |
| const std::string& service_id = params->id; |
| std::unique_ptr<base::DictionaryValue> dict = |
| params->offer_info.additional_properties.DeepCopyWithoutEmptyChildren(); |
| |
| PrefService* local_state = g_browser_process->local_state(); |
| DictionaryPrefUpdateDeprecated offer_update(local_state, |
| prefs::kEchoCheckedOffers); |
| offer_update->SetKey("echo." + service_id, |
| base::Value::FromUniquePtrValue(std::move(dict))); |
| return RespondNow(NoArguments()); |
| } |
| |
| EchoPrivateGetOfferInfoFunction::EchoPrivateGetOfferInfoFunction() {} |
| |
| EchoPrivateGetOfferInfoFunction::~EchoPrivateGetOfferInfoFunction() {} |
| |
| ExtensionFunction::ResponseAction EchoPrivateGetOfferInfoFunction::Run() { |
| std::unique_ptr<echo_api::GetOfferInfo::Params> params = |
| echo_api::GetOfferInfo::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| |
| const std::string& service_id = params->id; |
| PrefService* local_state = g_browser_process->local_state(); |
| const base::Value* offer_infos = |
| local_state->GetDictionary(prefs::kEchoCheckedOffers); |
| |
| const base::Value* offer_info = |
| offer_infos->FindDictKey("echo." + service_id); |
| if (!offer_info) { |
| return RespondNow(Error("Not found")); |
| } |
| |
| echo_api::GetOfferInfo::Results::Result result; |
| result.additional_properties.MergeDictionary(offer_info); |
| return RespondNow( |
| ArgumentList(echo_api::GetOfferInfo::Results::Create(result))); |
| } |
| |
| EchoPrivateGetOobeTimestampFunction::EchoPrivateGetOobeTimestampFunction() { |
| } |
| |
| EchoPrivateGetOobeTimestampFunction::~EchoPrivateGetOobeTimestampFunction() { |
| } |
| |
| ExtensionFunction::ResponseAction EchoPrivateGetOobeTimestampFunction::Run() { |
| base::PostTaskAndReplyWithResult( |
| extensions::GetExtensionFileTaskRunner().get(), FROM_HERE, |
| base::BindOnce( |
| &EchoPrivateGetOobeTimestampFunction::GetOobeTimestampOnFileSequence, |
| this), |
| base::BindOnce(&EchoPrivateGetOobeTimestampFunction::RespondWithResult, |
| this)); |
| return RespondLater(); |
| } |
| |
| // Get the OOBE timestamp from file /home/chronos/.oobe_completed. |
| // The timestamp is used to determine when the user first activates the device. |
| // If we can get the timestamp info, return it as yyyy-mm-dd, otherwise, return |
| // an empty string. |
| std::unique_ptr<base::Value> |
| EchoPrivateGetOobeTimestampFunction::GetOobeTimestampOnFileSequence() { |
| DCHECK( |
| extensions::GetExtensionFileTaskRunner()->RunsTasksInCurrentSequence()); |
| |
| const char kOobeTimestampFile[] = "/home/chronos/.oobe_completed"; |
| std::string timestamp; |
| base::File::Info fileInfo; |
| if (base::GetFileInfo(base::FilePath(kOobeTimestampFile), &fileInfo)) { |
| base::Time::Exploded ctime; |
| fileInfo.creation_time.UTCExplode(&ctime); |
| timestamp += base::StringPrintf("%u-%u-%u", |
| ctime.year, |
| ctime.month, |
| ctime.day_of_month); |
| } |
| return std::make_unique<base::Value>(timestamp); |
| } |
| |
| void EchoPrivateGetOobeTimestampFunction::RespondWithResult( |
| std::unique_ptr<base::Value> result) { |
| Respond(OneArgument(base::Value::FromUniquePtrValue(std::move(result)))); |
| } |
| |
| EchoPrivateGetUserConsentFunction::EchoPrivateGetUserConsentFunction() |
| : redeem_offers_allowed_(false) { |
| } |
| |
| // static |
| scoped_refptr<EchoPrivateGetUserConsentFunction> |
| EchoPrivateGetUserConsentFunction::CreateForTest( |
| const DialogShownTestCallback& dialog_shown_callback) { |
| scoped_refptr<EchoPrivateGetUserConsentFunction> function( |
| new EchoPrivateGetUserConsentFunction()); |
| function->dialog_shown_callback_ = dialog_shown_callback; |
| return function; |
| } |
| |
| EchoPrivateGetUserConsentFunction::~EchoPrivateGetUserConsentFunction() {} |
| |
| ExtensionFunction::ResponseAction EchoPrivateGetUserConsentFunction::Run() { |
| CheckRedeemOffersAllowed(); |
| return RespondLater(); |
| } |
| |
| void EchoPrivateGetUserConsentFunction::OnAccept() { |
| Finalize(true); |
| } |
| |
| void EchoPrivateGetUserConsentFunction::OnCancel() { |
| Finalize(false); |
| } |
| |
| void EchoPrivateGetUserConsentFunction::OnMoreInfoLinkClicked() { |
| NavigateParams params(Profile::FromBrowserContext(browser_context()), |
| GURL(chrome::kEchoLearnMoreURL), |
| ui::PAGE_TRANSITION_LINK); |
| // Open the link in a new window. The echo dialog is modal, so the current |
| // window is useless until the dialog is closed. |
| params.disposition = WindowOpenDisposition::NEW_WINDOW; |
| Navigate(¶ms); |
| } |
| |
| void EchoPrivateGetUserConsentFunction::CheckRedeemOffersAllowed() { |
| ash::CrosSettingsProvider::TrustedStatus status = |
| ash::CrosSettings::Get()->PrepareTrustedValues(base::BindOnce( |
| &EchoPrivateGetUserConsentFunction::CheckRedeemOffersAllowed, this)); |
| if (status == ash::CrosSettingsProvider::TEMPORARILY_UNTRUSTED) |
| return; |
| |
| bool allow = true; |
| ash::CrosSettings::Get()->GetBoolean( |
| ash::kAllowRedeemChromeOsRegistrationOffers, &allow); |
| |
| OnRedeemOffersAllowedChecked(allow); |
| } |
| |
| void EchoPrivateGetUserConsentFunction::OnRedeemOffersAllowedChecked( |
| bool is_allowed) { |
| redeem_offers_allowed_ = is_allowed; |
| |
| std::unique_ptr<echo_api::GetUserConsent::Params> params = |
| echo_api::GetUserConsent::Params::Create(args()); |
| |
| // Verify that the passed origin URL is valid. |
| GURL service_origin = GURL(params->consent_requester.origin); |
| if (!service_origin.is_valid()) { |
| Respond(Error("Invalid origin.")); |
| return; |
| } |
| |
| content::WebContents* web_contents = nullptr; |
| if (!params->consent_requester.tab_id) { |
| web_contents = GetSenderWebContents(); |
| |
| if (!web_contents || extensions::GetViewType(web_contents) != |
| extensions::mojom::ViewType::kAppWindow) { |
| Respond(Error("Not called from an app window - the tabId is required.")); |
| return; |
| } |
| } else { |
| TabStripModel* tab_strip = nullptr; |
| int tab_index = -1; |
| if (!extensions::ExtensionTabUtil::GetTabById( |
| *params->consent_requester.tab_id, browser_context(), |
| false /*incognito_enabled*/, nullptr /*browser*/, &tab_strip, |
| &web_contents, &tab_index)) { |
| Respond(Error("Tab not found.")); |
| return; |
| } |
| |
| // Bail out if the requested tab is not active - the dialog is modal to the |
| // window, so showing it for a request from an inactive tab could be |
| // misleading/confusing to the user. |
| if (tab_index != tab_strip->active_index()) { |
| Respond(Error("Consent requested from an inactive tab.")); |
| return; |
| } |
| } |
| |
| DCHECK(web_contents); |
| |
| // Add ref to ensure the function stays around until the dialog listener is |
| // called. The reference is release in |Finalize|. |
| AddRef(); |
| |
| // Create and show the dialog. |
| ash::EchoDialogView::Params dialog_params; |
| dialog_params.echo_enabled = redeem_offers_allowed_; |
| if (dialog_params.echo_enabled) { |
| dialog_params.service_name = |
| base::UTF8ToUTF16(params->consent_requester.service_name); |
| dialog_params.origin = base::UTF8ToUTF16(params->consent_requester.origin); |
| } |
| |
| ash::EchoDialogView* dialog = new ash::EchoDialogView(this, dialog_params); |
| dialog->Show(web_contents->GetTopLevelNativeWindow()); |
| |
| // If there is a dialog_shown_callback_, invoke it with the created dialog. |
| if (!dialog_shown_callback_.is_null()) |
| dialog_shown_callback_.Run(dialog); |
| } |
| |
| void EchoPrivateGetUserConsentFunction::Finalize(bool consent) { |
| // Consent should not be true if offers redeeming is disabled. |
| CHECK(redeem_offers_allowed_ || !consent); |
| Respond(OneArgument(base::Value(consent))); |
| |
| // Release the reference added in |OnRedeemOffersAllowedChecked|, before |
| // showing the dialog. |
| Release(); |
| } |