Commit ce200585 authored by Malay Keshav's avatar Malay Keshav Committed by Commit Bot

Hats: Setup HatsDialog for Hats vNext

Modifies the HatsDialog to use trigger ID instead of site ID to load the
surveys. It also modifies loading the survey as a dialog instead of a
window with frame buttons, since the HTML survey already includes a
close button.

NOTE: This is a reupload of a previously abandoned CL. This approach may
have possible points of failure:
  - Survey may not load, leaving user with a blank window.
  - Allowed survey localizations may not match the device locale.
  - HaTS backend may choose to not load the survey.
Subsequent work will fix this issues. See go/cros-hats-next follow-up
work section.

Bug: 1087034
Test: Tested manually on linux chrome and atlas
Change-Id: I1453617284fcf1e30c8107643a763b7880fa3c73
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2386418
Commit-Queue: Malay Keshav <malaykeshav@chromium.org>
Reviewed-by: default avatarXiyuan Slow <xiyuan@chromium.org>
Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#803749}
parent 7d47bba1
...@@ -312,7 +312,6 @@ ...@@ -312,7 +312,6 @@
<include name="IDR_SLOW_CSS" file="resources\chromeos\slow.css" type="BINDATA" /> <include name="IDR_SLOW_CSS" file="resources\chromeos\slow.css" type="BINDATA" />
<include name="IDR_SLOW_HTML" file="resources\chromeos\slow.html" type="BINDATA" /> <include name="IDR_SLOW_HTML" file="resources\chromeos\slow.html" type="BINDATA" />
<include name="IDR_SLOW_JS" file="resources\chromeos\slow.js" type="BINDATA" /> <include name="IDR_SLOW_JS" file="resources\chromeos\slow.js" type="BINDATA" />
<include name="IDR_HATS_HTML" file="resources\chromeos\hats\hats.html" flattenhtml="false" type="BINDATA" />
<include name="IDR_DEMO_APP_MANIFEST" file="resources\chromeos\demo_app\manifest.json" type="BINDATA" /> <include name="IDR_DEMO_APP_MANIFEST" file="resources\chromeos\demo_app\manifest.json" type="BINDATA" />
<include name="IDR_WALLPAPERMANAGER_MANIFEST" file="resources\chromeos\wallpaper_manager\manifest.json" type="BINDATA" /> <include name="IDR_WALLPAPERMANAGER_MANIFEST" file="resources\chromeos\wallpaper_manager\manifest.json" type="BINDATA" />
<include name="IDR_FIRST_RUN_DIALOG_MANIFEST" file="resources\chromeos\first_run\app/manifest.json" type="BINDATA" /> <include name="IDR_FIRST_RUN_DIALOG_MANIFEST" file="resources\chromeos\first_run\app/manifest.json" type="BINDATA" />
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/task/thread_pool.h" #include "base/task/thread_pool.h"
#include "chrome/browser/chromeos/hats/hats_finch_helper.h"
#include "chrome/browser/profiles/profile_destroyer.h" #include "chrome/browser/profiles/profile_destroyer.h"
#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/browser_dialogs.h"
...@@ -33,22 +34,17 @@ namespace chromeos { ...@@ -33,22 +34,17 @@ namespace chromeos {
namespace { namespace {
// Default width/height ratio of screen size. // Default width/height ratio of screen size.
const int kDefaultWidth = 400; const int kDefaultWidth = 340;
const int kDefaultHeight = 420; const int kDefaultHeight = 260;
// Site ID for HaTS survey.
constexpr char kRegularSiteID[] = "cs5lsagwwbho7l5cbbdniso22e"; constexpr char kCrOSHaTSURL[] =
constexpr char kGooglerSiteID[] = "z56p2hjy7pegxh3gmmur4qlwha"; "https://storage.googleapis.com/chromeos-hats-web-stable/index.html";
constexpr char kScriptSrcReplacementToken[] = "$SCRIPT_SRC";
constexpr char kDoneButtonLabelReplacementToken[] = "$DONE_BUTTON_LABEL";
// Base URL to fetch the google consumer survey script.
constexpr char kBaseFormatUrl[] =
"https://www.google.com/insights/consumersurveys/"
"async_survey?site=%s&force_https=1&sc=%s";
// Keyword used to join the separate device info elements into a single string // Keyword used to join the separate device info elements into a single string
// to be used as site context. // to be used as site context.
const char kDeviceInfoStopKeyword[] = "STOP"; const char kDeviceInfoStopKeyword[] = "&";
const char kDefaultProfileLocale[] = "en-US"; const char kDefaultProfileLocale[] = "en-US";
enum class DeviceInfoKey : unsigned int { enum class DeviceInfoKey : unsigned int {
BROWSER = 0, BROWSER = 0,
PLATFORM, PLATFORM,
...@@ -56,25 +52,6 @@ enum class DeviceInfoKey : unsigned int { ...@@ -56,25 +52,6 @@ enum class DeviceInfoKey : unsigned int {
LOCALE, LOCALE,
}; };
// Returns the local HaTS HTML file as a string with the correct Hats script
// URL.
std::string LoadLocalHtmlAsString(const std::string& site_id,
const std::string& site_context) {
std::string html_data =
ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
IDR_HATS_HTML);
size_t pos = html_data.find(kScriptSrcReplacementToken);
html_data.replace(pos, strlen(kScriptSrcReplacementToken),
base::StringPrintf(kBaseFormatUrl, site_id.c_str(),
site_context.c_str()));
pos = html_data.find(kDoneButtonLabelReplacementToken);
html_data.replace(pos, strlen(kDoneButtonLabelReplacementToken),
l10n_util::GetStringUTF8(IDS_HATS_DONE_BUTTON_LABEL));
return html_data;
}
// Maps the given DeviceInfoKey |key| enum to the corresponding string value // Maps the given DeviceInfoKey |key| enum to the corresponding string value
// that can be used as a key when creating a URL parameter. // that can be used as a key when creating a URL parameter.
const std::string KeyEnumToString(DeviceInfoKey key) { const std::string KeyEnumToString(DeviceInfoKey key) {
...@@ -96,40 +73,28 @@ const std::string KeyEnumToString(DeviceInfoKey key) { ...@@ -96,40 +73,28 @@ const std::string KeyEnumToString(DeviceInfoKey key) {
// Must be run on a blocking thread pool. // Must be run on a blocking thread pool.
// Gathers the browser version info, firmware info and platform info and returns // Gathers the browser version info, firmware info and platform info and returns
// them in a single encoded string, the format of which is defined below. // them in a single encoded string, the format of which is defined below.
// Currently the format is "<key><value>STOP<key><value>STOP<key><value>" where // Currently the format is "<key>=<value>&<key>=<value>&<key>=<value>".
// 'STOP' is used as a token to identify the end of a key value pair. This is
// done since GCS only allows the use of alphanumeric characters to be passed as
// a site context.
std::string GetFormattedSiteContext(const std::string& user_locale, std::string GetFormattedSiteContext(const std::string& user_locale,
base::StringPiece join_keyword) { base::StringPiece join_keyword) {
std::vector<std::string> pairs; std::vector<std::string> pairs;
pairs.push_back(KeyEnumToString(DeviceInfoKey::BROWSER) + pairs.push_back(KeyEnumToString(DeviceInfoKey::BROWSER) + "=" +
version_info::GetVersionNumber()); version_info::GetVersionNumber());
pairs.push_back(KeyEnumToString(DeviceInfoKey::PLATFORM) + pairs.push_back(KeyEnumToString(DeviceInfoKey::PLATFORM) + "=" +
version_loader::GetVersion(version_loader::VERSION_FULL)); version_loader::GetVersion(version_loader::VERSION_FULL));
pairs.push_back(KeyEnumToString(DeviceInfoKey::FIRMWARE) + pairs.push_back(KeyEnumToString(DeviceInfoKey::FIRMWARE) + "=" +
version_loader::GetFirmware()); version_loader::GetFirmware());
pairs.push_back(KeyEnumToString(DeviceInfoKey::LOCALE) + user_locale); pairs.push_back(KeyEnumToString(DeviceInfoKey::LOCALE) + "=" + user_locale);
return base::JoinString(pairs, join_keyword); return base::JoinString(pairs, join_keyword);
} }
// Determine which HaTS survey to show the user.
const std::string GetSiteID(bool is_google_account) {
if (is_google_account) {
return kGooglerSiteID;
} else {
return kRegularSiteID;
}
}
} // namespace } // namespace
// static // static
void HatsDialog::CreateAndShow(bool is_google_account) { std::unique_ptr<HatsDialog> HatsDialog::CreateAndShow() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Profile* profile = ProfileManager::GetActiveUserProfile(); Profile* profile = ProfileManager::GetActiveUserProfile();
...@@ -139,44 +104,39 @@ void HatsDialog::CreateAndShow(bool is_google_account) { ...@@ -139,44 +104,39 @@ void HatsDialog::CreateAndShow(bool is_google_account) {
if (!user_locale.length()) if (!user_locale.length())
user_locale = kDefaultProfileLocale; user_locale = kDefaultProfileLocale;
std::unique_ptr<HatsDialog> hats_dialog(
new HatsDialog(HatsFinchHelper::GetTriggerID(), profile));
// Raw pointer is used here since the dialog is owned by the hats
// notification controller which lives until the end of the user session. The
// dialog will always be closed before that time instant.
base::ThreadPool::PostTaskAndReplyWithResult( base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&GetFormattedSiteContext, user_locale, base::BindOnce(&GetFormattedSiteContext, user_locale,
kDeviceInfoStopKeyword), kDeviceInfoStopKeyword),
base::BindOnce(&HatsDialog::Show, is_google_account)); base::BindOnce(&HatsDialog::Show, base::Unretained(hats_dialog.get())));
return hats_dialog;
} }
// static void HatsDialog::Show(const std::string& site_context) {
void HatsDialog::Show(bool is_google_account, const std::string& site_context) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Load and set the html data that needs to be displayed in the dialog. // Link the trigger ID to fetch the correct survey.
std::string site_id = GetSiteID(is_google_account); url_ = std::string(kCrOSHaTSURL) + "?" + site_context +
std::string html_data = LoadLocalHtmlAsString(site_id, site_context); "&trigger=" + trigger_id_;
Profile* active_profile = ProfileManager::GetActiveUserProfile(); chrome::ShowWebDialog(nullptr, ProfileManager::GetActiveUserProfile(), this);
Profile* profile_to_show = active_profile->GetOffTheRecordProfile(
Profile::OTRProfileID::CreateUnique("ChromeOS::HatsDialog"));
// Self deleting.
// Users of non-primary OTR profiles should destroy it when it's not needed
// any more.
auto* hats_dialog = new HatsDialog(html_data,
/* otr_profile= */ profile_to_show);
chrome::ShowWebDialog(nullptr, profile_to_show, hats_dialog);
} }
HatsDialog::HatsDialog(const std::string& html_data, Profile* otr_profile) HatsDialog::HatsDialog(const std::string& trigger_id, Profile* user_profile)
: html_data_(html_data), otr_profile_(otr_profile) { : trigger_id_(trigger_id), user_profile_(user_profile) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(otr_profile_->IsOffTheRecord());
DCHECK(!otr_profile_->IsPrimaryOTRProfile());
} }
HatsDialog::~HatsDialog() { HatsDialog::~HatsDialog() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(otr_profile_); DCHECK(user_profile_);
ProfileDestroyer::DestroyProfileWhenAppropriate(otr_profile_);
} }
ui::ModalType HatsDialog::GetDialogModalType() const { ui::ModalType HatsDialog::GetDialogModalType() const {
...@@ -188,7 +148,7 @@ base::string16 HatsDialog::GetDialogTitle() const { ...@@ -188,7 +148,7 @@ base::string16 HatsDialog::GetDialogTitle() const {
} }
GURL HatsDialog::GetDialogContentURL() const { GURL HatsDialog::GetDialogContentURL() const {
return GURL("data:text/html;charset=utf-8," + html_data_); return GURL(url_);
} }
void HatsDialog::GetWebUIMessageHandlers( void HatsDialog::GetWebUIMessageHandlers(
...@@ -206,9 +166,7 @@ std::string HatsDialog::GetDialogArgs() const { ...@@ -206,9 +166,7 @@ std::string HatsDialog::GetDialogArgs() const {
return std::string(); return std::string();
} }
void HatsDialog::OnDialogClosed(const std::string& json_retval) { void HatsDialog::OnDialogClosed(const std::string& json_retval) {}
delete this;
}
void HatsDialog::OnCloseContents(WebContents* source, bool* out_close_dialog) { void HatsDialog::OnCloseContents(WebContents* source, bool* out_close_dialog) {
*out_close_dialog = true; *out_close_dialog = true;
...@@ -218,10 +176,18 @@ bool HatsDialog::ShouldShowDialogTitle() const { ...@@ -218,10 +176,18 @@ bool HatsDialog::ShouldShowDialogTitle() const {
return false; return false;
} }
bool HatsDialog::ShouldShowCloseButton() const {
return false;
}
bool HatsDialog::HandleContextMenu(content::RenderFrameHost* render_frame_host, bool HatsDialog::HandleContextMenu(content::RenderFrameHost* render_frame_host,
const content::ContextMenuParams& params) { const content::ContextMenuParams& params) {
// Disable context menu. // Disable context menu
return true; return true;
} }
ui::WebDialogDelegate::FrameKind HatsDialog::GetWebDialogFrameKind() const {
return ui::WebDialogDelegate::FrameKind::kDialog;
}
} // namespace chromeos } // namespace chromeos
...@@ -16,19 +16,18 @@ namespace chromeos { ...@@ -16,19 +16,18 @@ namespace chromeos {
// Happiness tracking survey dialog. Sometimes appears after login to ask the // Happiness tracking survey dialog. Sometimes appears after login to ask the
// user how satisfied they are with their Chromebook. // user how satisfied they are with their Chromebook.
// This class lives on the UI thread and is self deleting. // This class lives on the UI thread.
class HatsDialog : public ui::WebDialogDelegate { class HatsDialog : public ui::WebDialogDelegate {
public: public:
// Creates an instance of HatsDialog and posts a task to load all the relevant // Creates an instance of HatsDialog and posts a task to load all the relevant
// device info before displaying the dialog. // device info before displaying the dialog.
static void CreateAndShow(bool is_google_account); static std::unique_ptr<HatsDialog> CreateAndShow();
~HatsDialog() override;
private: private:
static void Show(bool is_google_account, const std::string& site_context); void Show(const std::string& site_context);
// Use CreateAndShow() above. explicit HatsDialog(const std::string& trigger_id, Profile* user_profile);
explicit HatsDialog(const std::string& html_data, Profile* otr_profile);
~HatsDialog() override;
// ui::WebDialogDelegate implementation. // ui::WebDialogDelegate implementation.
ui::ModalType GetDialogModalType() const override; ui::ModalType GetDialogModalType() const override;
...@@ -39,16 +38,18 @@ class HatsDialog : public ui::WebDialogDelegate { ...@@ -39,16 +38,18 @@ class HatsDialog : public ui::WebDialogDelegate {
void GetDialogSize(gfx::Size* size) const override; void GetDialogSize(gfx::Size* size) const override;
bool CanResizeDialog() const override; bool CanResizeDialog() const override;
std::string GetDialogArgs() const override; std::string GetDialogArgs() const override;
// NOTE: This function deletes this object at the end.
void OnDialogClosed(const std::string& json_retval) override; void OnDialogClosed(const std::string& json_retval) override;
void OnCloseContents(content::WebContents* source, void OnCloseContents(content::WebContents* source,
bool* out_close_dialog) override; bool* out_close_dialog) override;
bool ShouldShowDialogTitle() const override; bool ShouldShowDialogTitle() const override;
bool ShouldShowCloseButton() const override;
bool HandleContextMenu(content::RenderFrameHost* render_frame_host, bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
const content::ContextMenuParams& params) override; const content::ContextMenuParams& params) override;
ui::WebDialogDelegate::FrameKind GetWebDialogFrameKind() const override;
const std::string html_data_; const std::string trigger_id_;
Profile* otr_profile_; std::string url_;
Profile* user_profile_;
DISALLOW_COPY_AND_ASSIGN(HatsDialog); DISALLOW_COPY_AND_ASSIGN(HatsDialog);
}; };
......
...@@ -25,6 +25,14 @@ const char HatsFinchHelper::kSurveyStartDateMsParam[] = "survey_start_date_ms"; ...@@ -25,6 +25,14 @@ const char HatsFinchHelper::kSurveyStartDateMsParam[] = "survey_start_date_ms";
const char HatsFinchHelper::kResetSurveyCycleParam[] = "reset_survey_cycle"; const char HatsFinchHelper::kResetSurveyCycleParam[] = "reset_survey_cycle";
// static // static
const char HatsFinchHelper::kResetAllParam[] = "reset_all"; const char HatsFinchHelper::kResetAllParam[] = "reset_all";
// static
const char HatsFinchHelper::kTriggerIdParam[] = "trigger_id";
std::string HatsFinchHelper::GetTriggerID() {
DCHECK(base::FeatureList::IsEnabled(features::kHappinessTrackingSystem));
return base::GetFieldTrialParamValueByFeature(
features::kHappinessTrackingSystem, kTriggerIdParam);
}
HatsFinchHelper::HatsFinchHelper(Profile* profile) : profile_(profile) { HatsFinchHelper::HatsFinchHelper(Profile* profile) : profile_(profile) {
LoadFinchParamValues(); LoadFinchParamValues();
...@@ -79,6 +87,8 @@ void HatsFinchHelper::LoadFinchParamValues() { ...@@ -79,6 +87,8 @@ void HatsFinchHelper::LoadFinchParamValues() {
first_survey_start_date_ = first_survey_start_date_ =
base::Time().FromJsTime(first_survey_start_date_ms); base::Time().FromJsTime(first_survey_start_date_ms);
trigger_id_ = GetTriggerID();
reset_survey_cycle_ = base::GetFieldTrialParamByFeatureAsBool( reset_survey_cycle_ = base::GetFieldTrialParamByFeatureAsBool(
feature, kResetSurveyCycleParam, false); feature, kResetSurveyCycleParam, false);
...@@ -137,6 +147,11 @@ void HatsFinchHelper::CheckForDeviceSelection() { ...@@ -137,6 +147,11 @@ void HatsFinchHelper::CheckForDeviceSelection() {
bool is_selected = false; bool is_selected = false;
if (rand_double < probability_of_pick_) if (rand_double < probability_of_pick_)
is_selected = true; is_selected = true;
// Check if the trigger id is a valid string. Trigger IDs are a hash strings
// of around 26 characters.
is_selected = is_selected && (trigger_id_.length() > 15);
pref_service->SetBoolean(prefs::kHatsDeviceIsSelected, is_selected); pref_service->SetBoolean(prefs::kHatsDeviceIsSelected, is_selected);
device_is_selected_for_cycle_ = is_selected; device_is_selected_for_cycle_ = is_selected;
} }
......
...@@ -19,6 +19,8 @@ namespace chromeos { ...@@ -19,6 +19,8 @@ namespace chromeos {
// information related to the hats finch experiment. // information related to the hats finch experiment.
class HatsFinchHelper { class HatsFinchHelper {
public: public:
static std::string GetTriggerID();
explicit HatsFinchHelper(Profile* profile); explicit HatsFinchHelper(Profile* profile);
~HatsFinchHelper(); ~HatsFinchHelper();
...@@ -39,6 +41,7 @@ class HatsFinchHelper { ...@@ -39,6 +41,7 @@ class HatsFinchHelper {
static const char kSurveyStartDateMsParam[]; static const char kSurveyStartDateMsParam[];
static const char kResetSurveyCycleParam[]; static const char kResetSurveyCycleParam[];
static const char kResetAllParam[]; static const char kResetAllParam[];
static const char kTriggerIdParam[];
// Loads all the param values from the finch seed and initializes the member // Loads all the param values from the finch seed and initializes the member
// variables. // variables.
...@@ -70,6 +73,12 @@ class HatsFinchHelper { ...@@ -70,6 +73,12 @@ class HatsFinchHelper {
// key "survey_start_date_ms". // key "survey_start_date_ms".
base::Time first_survey_start_date_; base::Time first_survey_start_date_;
// The survey's trigger id. This is used by the Hats server to identify its
// client, Chrome OS in this case. Different Chrome OS surveys can have
// different trigger ids. This is provided as a param via the finch seed under
// the key "trigger_id".
std::string trigger_id_;
// Indicates that the survey cycle needs to be reset if set to true. // Indicates that the survey cycle needs to be reset if set to true.
bool reset_survey_cycle_ = false; bool reset_survey_cycle_ = false;
......
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace chromeos { namespace chromeos {
namespace {
constexpr char kValidTriggerId[] = "1gksUIDXA0jBnuK8T6R0NfspWBvA";
} // namespace
class HatsFinchHelperTest : public testing::Test { class HatsFinchHelperTest : public testing::Test {
public: public:
...@@ -32,13 +35,15 @@ class HatsFinchHelperTest : public testing::Test { ...@@ -32,13 +35,15 @@ class HatsFinchHelperTest : public testing::Test {
std::string cycle_length, std::string cycle_length,
std::string start_date, std::string start_date,
std::string reset_survey, std::string reset_survey,
std::string reset) { std::string reset,
std::string trigger_id) {
base::FieldTrialParams params; base::FieldTrialParams params;
params[HatsFinchHelper::kProbabilityParam] = prob; params[HatsFinchHelper::kProbabilityParam] = prob;
params[HatsFinchHelper::kSurveyCycleLengthParam] = cycle_length; params[HatsFinchHelper::kSurveyCycleLengthParam] = cycle_length;
params[HatsFinchHelper::kSurveyStartDateMsParam] = start_date; params[HatsFinchHelper::kSurveyStartDateMsParam] = start_date;
params[HatsFinchHelper::kResetSurveyCycleParam] = reset_survey; params[HatsFinchHelper::kResetSurveyCycleParam] = reset_survey;
params[HatsFinchHelper::kResetAllParam] = reset; params[HatsFinchHelper::kResetAllParam] = reset;
params[HatsFinchHelper::kTriggerIdParam] = trigger_id;
return params; return params;
} }
...@@ -56,8 +61,8 @@ class HatsFinchHelperTest : public testing::Test { ...@@ -56,8 +61,8 @@ class HatsFinchHelperTest : public testing::Test {
}; };
TEST_F(HatsFinchHelperTest, InitFinchSeed_ValidValues) { TEST_F(HatsFinchHelperTest, InitFinchSeed_ValidValues) {
base::FieldTrialParams params = base::FieldTrialParams params = CreateParamMap(
CreateParamMap("1.0", "7", "1475613895337", "false", "false"); "1.0", "7", "1475613895337", "false", "false", kValidTriggerId);
SetFeatureParams(params); SetFeatureParams(params);
HatsFinchHelper hats_finch_helper(&profile_); HatsFinchHelper hats_finch_helper(&profile_);
...@@ -66,13 +71,14 @@ TEST_F(HatsFinchHelperTest, InitFinchSeed_ValidValues) { ...@@ -66,13 +71,14 @@ TEST_F(HatsFinchHelperTest, InitFinchSeed_ValidValues) {
EXPECT_EQ(hats_finch_helper.survey_cycle_length_, 7); EXPECT_EQ(hats_finch_helper.survey_cycle_length_, 7);
EXPECT_EQ(hats_finch_helper.first_survey_start_date_, EXPECT_EQ(hats_finch_helper.first_survey_start_date_,
base::Time().FromJsTime(1475613895337LL)); base::Time().FromJsTime(1475613895337LL));
EXPECT_EQ(hats_finch_helper.trigger_id_, kValidTriggerId);
EXPECT_FALSE(hats_finch_helper.reset_survey_cycle_); EXPECT_FALSE(hats_finch_helper.reset_survey_cycle_);
EXPECT_FALSE(hats_finch_helper.reset_hats_); EXPECT_FALSE(hats_finch_helper.reset_hats_);
} }
TEST_F(HatsFinchHelperTest, InitFinchSeed_Invalidalues) { TEST_F(HatsFinchHelperTest, InitFinchSeed_Invalidalues) {
base::FieldTrialParams params = base::FieldTrialParams params =
CreateParamMap("-0.1", "-1", "-1000", "false", "false"); CreateParamMap("-0.1", "-1", "-1000", "false", "false", "1A2B3C4D5");
SetFeatureParams(params); SetFeatureParams(params);
base::Time current_time = base::Time::Now(); base::Time current_time = base::Time::Now();
...@@ -88,7 +94,7 @@ TEST_F(HatsFinchHelperTest, TestComputeNextDate) { ...@@ -88,7 +94,7 @@ TEST_F(HatsFinchHelperTest, TestComputeNextDate) {
base::FieldTrialParams params = base::FieldTrialParams params =
CreateParamMap("0", CreateParamMap("0",
"7", // 7 Days survey cycle length "7", // 7 Days survey cycle length
"0", "false", "false"); "0", "false", "false", kValidTriggerId);
SetFeatureParams(params); SetFeatureParams(params);
...@@ -116,7 +122,7 @@ TEST_F(HatsFinchHelperTest, TestComputeNextDate) { ...@@ -116,7 +122,7 @@ TEST_F(HatsFinchHelperTest, TestComputeNextDate) {
TEST_F(HatsFinchHelperTest, ResetSurveyCycle) { TEST_F(HatsFinchHelperTest, ResetSurveyCycle) {
base::FieldTrialParams params = base::FieldTrialParams params =
CreateParamMap("0.5", "7", "1475613895337", "true", "0"); CreateParamMap("0.5", "7", "1475613895337", "true", "0", kValidTriggerId);
SetFeatureParams(params); SetFeatureParams(params);
int64_t initial_timestamp = base::Time::Now().ToInternalValue(); int64_t initial_timestamp = base::Time::Now().ToInternalValue();
...@@ -140,7 +146,7 @@ TEST_F(HatsFinchHelperTest, ResetSurveyCycle) { ...@@ -140,7 +146,7 @@ TEST_F(HatsFinchHelperTest, ResetSurveyCycle) {
TEST_F(HatsFinchHelperTest, ResetHats) { TEST_F(HatsFinchHelperTest, ResetHats) {
base::FieldTrialParams params = base::FieldTrialParams params =
CreateParamMap("0.5", "7", "1475613895337", "0", "true"); CreateParamMap("0.5", "7", "1475613895337", "0", "true", kValidTriggerId);
SetFeatureParams(params); SetFeatureParams(params);
int64_t initial_timestamp = base::Time::Now().ToInternalValue(); int64_t initial_timestamp = base::Time::Now().ToInternalValue();
......
...@@ -180,9 +180,7 @@ void HatsNotificationController::Click( ...@@ -180,9 +180,7 @@ void HatsNotificationController::Click(
UpdateLastInteractionTime(); UpdateLastInteractionTime();
// The dialog deletes itself on close. hats_dialog_ = HatsDialog::CreateAndShow();
HatsDialog::CreateAndShow(
gaia::IsGoogleInternalAccountEmail(profile_->GetProfileUserName()));
// Remove the notification. // Remove the notification.
network_portal_detector::GetInstance()->RemoveObserver(this); network_portal_detector::GetInstance()->RemoveObserver(this);
......
...@@ -20,6 +20,7 @@ class Profile; ...@@ -20,6 +20,7 @@ class Profile;
class NetworkState; class NetworkState;
namespace chromeos { namespace chromeos {
class HatsDialog;
// Happiness tracking survey (HaTS) notification controller is responsible for // Happiness tracking survey (HaTS) notification controller is responsible for
// managing the HaTS notification that is displayed to the user. // managing the HaTS notification that is displayed to the user.
...@@ -67,6 +68,7 @@ class HatsNotificationController : public message_center::NotificationDelegate, ...@@ -67,6 +68,7 @@ class HatsNotificationController : public message_center::NotificationDelegate,
Profile* const profile_; Profile* const profile_;
std::unique_ptr<message_center::Notification> notification_; std::unique_ptr<message_center::Notification> notification_;
std::unique_ptr<HatsDialog> hats_dialog_;
base::WeakPtrFactory<HatsNotificationController> weak_pointer_factory_{this}; base::WeakPtrFactory<HatsNotificationController> weak_pointer_factory_{this};
DISALLOW_COPY_AND_ASSIGN(HatsNotificationController); DISALLOW_COPY_AND_ASSIGN(HatsNotificationController);
......
<html>
<head>
<style>
.done-button {
bottom: 17px;
color: rgb(33, 150, 243);
display: none;
filter: none;
font: 400 16px Roboto,RobotoDraft,Helvetica,sans-serif;
position: fixed;
right: 25px;
text-shadow: none;
text-transform: uppercase;
z-index: 10;
}
</style>
<script>
/**
* Handles the callback when the survey is submitted or when the survey
* has already been submitted in the past.
* @param {boolean} isFirstRun Will be true when the user just earned
* access to the content and false if the user had already had access
* previously.
*/
var didFinishSurvey = function(isFirstRun) {
if (!isFirstRun) {
return;
}
/* Display the done button at the end of the survey. */
document.getElementById('id-done-button').style.display = 'block';
};
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('id-link-done-button').onclick = function() {
window.close();
}
})
</script>
<script src="$SCRIPT_SRC"></script>
</head>
<body>
<a id="id-link-done-button" href="#" role="button">
<div id="id-done-button" class="done-button">$DONE_BUTTON_LABEL</div>
</a>
</body>
</html>
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "ui/views/bubble/bubble_frame_view.h" #include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/webview/web_dialog_view.h" #include "ui/views/controls/webview/web_dialog_view.h"
#include "ui/views/layout/fill_layout.h" #include "ui/views/layout/fill_layout.h"
#include "ui/web_dialogs/web_dialog_delegate.h"
// A delegate used to intercept the creation of new WebContents by the HaTS // A delegate used to intercept the creation of new WebContents by the HaTS
// Next dialog. // Next dialog.
...@@ -166,6 +167,10 @@ bool HatsNextWebDialog::HandleContextMenu( ...@@ -166,6 +167,10 @@ bool HatsNextWebDialog::HandleContextMenu(
const content::ContextMenuParams& params) { const content::ContextMenuParams& params) {
return true; return true;
} }
ui::WebDialogDelegate::FrameKind HatsNextWebDialog::GetWebDialogFrameKind()
const {
return ui::WebDialogDelegate::FrameKind::kDialog;
}
gfx::Size HatsNextWebDialog::CalculatePreferredSize() const { gfx::Size HatsNextWebDialog::CalculatePreferredSize() const {
return size_; return size_;
...@@ -198,8 +203,7 @@ HatsNextWebDialog::HatsNextWebDialog(Browser* browser, ...@@ -198,8 +203,7 @@ HatsNextWebDialog::HatsNextWebDialog(Browser* browser,
SetLayoutManager(std::make_unique<views::FillLayout>()); SetLayoutManager(std::make_unique<views::FillLayout>());
web_view_ = AddChildView(std::make_unique<views::WebDialogView>( web_view_ = AddChildView(std::make_unique<views::WebDialogView>(
otr_profile_, this, std::make_unique<ChromeWebContentsHandler>(), otr_profile_, this, std::make_unique<ChromeWebContentsHandler>()));
/* use_dialog_frame */ true));
set_margins(gfx::Insets()); set_margins(gfx::Insets());
widget_ = views::BubbleDialogDelegateView::CreateBubble(this); widget_ = views::BubbleDialogDelegateView::CreateBubble(this);
......
...@@ -51,6 +51,7 @@ class HatsNextWebDialog : public ui::WebDialogDelegate, ...@@ -51,6 +51,7 @@ class HatsNextWebDialog : public ui::WebDialogDelegate,
bool ShouldShowDialogTitle() const override; bool ShouldShowDialogTitle() const override;
bool HandleContextMenu(content::RenderFrameHost* render_frame_host, bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
const content::ContextMenuParams& params) override; const content::ContextMenuParams& params) override;
ui::WebDialogDelegate::FrameKind GetWebDialogFrameKind() const override;
// BubbleDialogDelegateView: // BubbleDialogDelegateView:
gfx::Size CalculatePreferredSize() const override; gfx::Size CalculatePreferredSize() const override;
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
#include "ui/views/controls/webview/web_dialog_view.h" #include "ui/views/controls/webview/web_dialog_view.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
#include "ui/web_dialogs/web_dialog_delegate.h"
#include "url/url_canon.h" #include "url/url_canon.h"
#include "url/url_util.h" #include "url/url_util.h"
...@@ -196,6 +197,10 @@ void HatsWebDialog::OnMainFrameResourceLoadComplete( ...@@ -196,6 +197,10 @@ void HatsWebDialog::OnMainFrameResourceLoadComplete(
} }
} }
ui::WebDialogDelegate::FrameKind HatsWebDialog::GetWebDialogFrameKind() const {
return ui::WebDialogDelegate::FrameKind::kDialog;
}
views::View* HatsWebDialog::GetContentsView() { views::View* HatsWebDialog::GetContentsView() {
return webview_; return webview_;
} }
...@@ -231,8 +236,7 @@ void HatsWebDialog::CreateWebDialog(Browser* browser) { ...@@ -231,8 +236,7 @@ void HatsWebDialog::CreateWebDialog(Browser* browser) {
// Create a web dialog aligned to the bottom center of the location bar. // Create a web dialog aligned to the bottom center of the location bar.
SetButtons(ui::DIALOG_BUTTON_NONE); SetButtons(ui::DIALOG_BUTTON_NONE);
webview_ = new views::WebDialogView( webview_ = new views::WebDialogView(
otr_profile_, this, std::make_unique<ChromeWebContentsHandler>(), otr_profile_, this, std::make_unique<ChromeWebContentsHandler>());
/* use_dialog_frame= */ true);
webview_->SetPreferredSize( webview_->SetPreferredSize(
gfx::Size(kDefaultHatsDialogWidth, kDefaultHatsDialogHeight)); gfx::Size(kDefaultHatsDialogWidth, kDefaultHatsDialogHeight));
preloading_widget_ = constrained_window::CreateBrowserModalDialogViews( preloading_widget_ = constrained_window::CreateBrowserModalDialogViews(
......
...@@ -63,6 +63,7 @@ class HatsWebDialog : public ui::WebDialogDelegate, ...@@ -63,6 +63,7 @@ class HatsWebDialog : public ui::WebDialogDelegate,
void OnWebContentsFinishedLoad() override; void OnWebContentsFinishedLoad() override;
void OnMainFrameResourceLoadComplete( void OnMainFrameResourceLoadComplete(
const blink::mojom::ResourceLoadInfo& resource_load_info) override; const blink::mojom::ResourceLoadInfo& resource_load_info) override;
ui::WebDialogDelegate::FrameKind GetWebDialogFrameKind() const override;
// views::DialogDelegateView implementation. // views::DialogDelegateView implementation.
views::View* GetContentsView() override; views::View* GetContentsView() override;
......
...@@ -74,13 +74,11 @@ void ObservableWebView::ResetDelegate() { ...@@ -74,13 +74,11 @@ void ObservableWebView::ResetDelegate() {
WebDialogView::WebDialogView(content::BrowserContext* context, WebDialogView::WebDialogView(content::BrowserContext* context,
WebDialogDelegate* delegate, WebDialogDelegate* delegate,
std::unique_ptr<WebContentsHandler> handler, std::unique_ptr<WebContentsHandler> handler)
bool use_dialog_frame)
: ClientView(nullptr, nullptr), : ClientView(nullptr, nullptr),
WebDialogWebContentsDelegate(context, std::move(handler)), WebDialogWebContentsDelegate(context, std::move(handler)),
delegate_(delegate), delegate_(delegate),
web_view_(new ObservableWebView(context, delegate)), web_view_(new ObservableWebView(context, delegate)) {
use_dialog_frame_(use_dialog_frame) {
SetCanMinimize(!delegate_ || delegate_->can_minimize()); SetCanMinimize(!delegate_ || delegate_->can_minimize());
SetModalType(GetDialogModalType()); SetModalType(GetDialogModalType());
web_view_->set_allow_accelerators(true); web_view_->set_allow_accelerators(true);
...@@ -228,8 +226,18 @@ views::ClientView* WebDialogView::CreateClientView(views::Widget* widget) { ...@@ -228,8 +226,18 @@ views::ClientView* WebDialogView::CreateClientView(views::Widget* widget) {
std::unique_ptr<NonClientFrameView> WebDialogView::CreateNonClientFrameView( std::unique_ptr<NonClientFrameView> WebDialogView::CreateNonClientFrameView(
Widget* widget) { Widget* widget) {
return use_dialog_frame_ ? DialogDelegate::CreateDialogFrameView(widget) if (!delegate_)
: WidgetDelegate::CreateNonClientFrameView(widget); return WidgetDelegate::CreateNonClientFrameView(widget);
switch (delegate_->GetWebDialogFrameKind()) {
case WebDialogDelegate::FrameKind::kNonClient:
return WidgetDelegate::CreateNonClientFrameView(widget);
case WebDialogDelegate::FrameKind::kDialog:
return DialogDelegate::CreateDialogFrameView(widget);
default:
NOTREACHED() << "Unknown frame kind type enum specified.";
return std::unique_ptr<NonClientFrameView>{};
}
} }
views::View* WebDialogView::GetInitiallyFocusedView() { views::View* WebDialogView::GetInitiallyFocusedView() {
......
...@@ -78,8 +78,7 @@ class WEBVIEW_EXPORT WebDialogView : public ClientView, ...@@ -78,8 +78,7 @@ class WEBVIEW_EXPORT WebDialogView : public ClientView,
// client frame view. // client frame view.
WebDialogView(content::BrowserContext* context, WebDialogView(content::BrowserContext* context,
ui::WebDialogDelegate* delegate, ui::WebDialogDelegate* delegate,
std::unique_ptr<WebContentsHandler> handler, std::unique_ptr<WebContentsHandler> handler);
bool use_dialog_frame = false);
~WebDialogView() override; ~WebDialogView() override;
content::WebContents* web_contents(); content::WebContents* web_contents();
...@@ -206,9 +205,6 @@ class WEBVIEW_EXPORT WebDialogView : public ClientView, ...@@ -206,9 +205,6 @@ class WEBVIEW_EXPORT WebDialogView : public ClientView,
// Handler for unhandled key events from renderer. // Handler for unhandled key events from renderer.
UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_; UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
// Whether to use dialog frame view for non client frame view.
bool use_dialog_frame_ = false;
bool disable_url_load_for_test_ = false; bool disable_url_load_for_test_ = false;
DISALLOW_COPY_AND_ASSIGN(WebDialogView); DISALLOW_COPY_AND_ASSIGN(WebDialogView);
......
...@@ -85,4 +85,8 @@ bool WebDialogDelegate::CheckMediaAccessPermission( ...@@ -85,4 +85,8 @@ bool WebDialogDelegate::CheckMediaAccessPermission(
return false; return false;
} }
WebDialogDelegate::FrameKind WebDialogDelegate::GetWebDialogFrameKind() const {
return WebDialogDelegate::FrameKind::kNonClient;
}
} // namespace ui } // namespace ui
...@@ -36,6 +36,11 @@ class Accelerator; ...@@ -36,6 +36,11 @@ class Accelerator;
// Implement this class to receive notifications. // Implement this class to receive notifications.
class WEB_DIALOGS_EXPORT WebDialogDelegate { class WEB_DIALOGS_EXPORT WebDialogDelegate {
public: public:
enum class FrameKind {
kDialog, // Does not include a title bar or frame caption buttons.
kNonClient, // Includes a non client frame view with title & buttons.
};
// Returns the modal type for this dialog. Only called once, during // Returns the modal type for this dialog. Only called once, during
// WebDialogView creation. // WebDialogView creation.
virtual ModalType GetDialogModalType() const = 0; virtual ModalType GetDialogModalType() const = 0;
...@@ -184,6 +189,9 @@ class WEB_DIALOGS_EXPORT WebDialogDelegate { ...@@ -184,6 +189,9 @@ class WEB_DIALOGS_EXPORT WebDialogDelegate {
const GURL& security_origin, const GURL& security_origin,
blink::mojom::MediaStreamType type); blink::mojom::MediaStreamType type);
// Whether to use dialog frame view for non client frame view.
virtual FrameKind GetWebDialogFrameKind() const;
virtual ~WebDialogDelegate() = default; virtual ~WebDialogDelegate() = default;
private: private:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment