diff options
author | erikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-14 20:56:01 +0000 |
---|---|---|
committer | erikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-14 20:56:01 +0000 |
commit | 2bf5f415ac56938fa2b99417b21ce0c75476d3f4 (patch) | |
tree | 2f32792d3fe9ea6f4a5b003c073e68e8d8ab2c2a /chrome_frame | |
parent | 61e512816c5510a234f0e5dd21d7556f207cc4cd (diff) | |
download | chromium_src-2bf5f415ac56938fa2b99417b21ce0c75476d3f4.zip chromium_src-2bf5f415ac56938fa2b99417b21ce0c75476d3f4.tar.gz chromium_src-2bf5f415ac56938fa2b99417b21ce0c75476d3f4.tar.bz2 |
Implement a ReadyPromptContent that displays a prompt to accept, temporarily decline, or permanently decline ready mode. Implement a RegistryReadyModeState that manages the state (based on user's decision) in the registry. Interfaces to integrate those with the installer.
BUG=None
TEST=chrome_frame_unittests --gtest_filter='Ready*'
Review URL: http://codereview.chromium.org/5747002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@69175 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame')
-rw-r--r-- | chrome_frame/chrome_frame.gyp | 15 | ||||
-rw-r--r-- | chrome_frame/ready_mode/internal/installation_state.h | 31 | ||||
-rw-r--r-- | chrome_frame/ready_mode/internal/ready_mode_state.h | 24 | ||||
-rw-r--r-- | chrome_frame/ready_mode/internal/ready_prompt_content.cc | 102 | ||||
-rw-r--r-- | chrome_frame/ready_mode/internal/ready_prompt_content.h | 37 | ||||
-rw-r--r-- | chrome_frame/ready_mode/internal/ready_prompt_window.cc | 74 | ||||
-rw-r--r-- | chrome_frame/ready_mode/internal/ready_prompt_window.h | 87 | ||||
-rw-r--r-- | chrome_frame/ready_mode/internal/registry_ready_mode_state.cc | 143 | ||||
-rw-r--r-- | chrome_frame/ready_mode/internal/registry_ready_mode_state.h | 73 | ||||
-rw-r--r-- | chrome_frame/ready_mode/ready_mode_manager.cc | 172 | ||||
-rw-r--r-- | chrome_frame/ready_mode/ready_mode_manager.h | 16 | ||||
-rw-r--r-- | chrome_frame/test/ready_mode_unittest.cc | 473 |
12 files changed, 1245 insertions, 2 deletions
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp index 0803255..42054ff 100644 --- a/chrome_frame/chrome_frame.gyp +++ b/chrome_frame/chrome_frame.gyp @@ -168,12 +168,13 @@ 'test/chrome_frame_test_utils.h', 'test/chrome_frame_test_utils.cc', 'test/com_message_event_unittest.cc', + 'test/dll_redirector_test.cc', 'test/exception_barrier_unittest.cc', 'test/html_util_unittests.cc', 'test/http_negotiate_unittest.cc', 'test/infobar_unittests.cc', - 'test/dll_redirector_test.cc', 'test/policy_settings_unittest.cc', + 'test/ready_mode_unittest.cc', 'test/simulate_input.h', 'test/simulate_input.cc', 'test/urlmon_moniker_tests.h', @@ -845,6 +846,15 @@ 'policy_settings.h', 'protocol_sink_wrap.cc', 'protocol_sink_wrap.h', + 'ready_mode/internal/installation_state.h', + 'ready_mode/internal/ready_mode_state.h', + 'ready_mode/internal/ready_prompt_content.cc', + 'ready_mode/internal/ready_prompt_content.h', + 'ready_mode/internal/ready_prompt_window.cc', + 'ready_mode/internal/ready_prompt_window.h', + 'ready_mode/internal/registry_ready_mode_state.cc', + 'ready_mode/internal/registry_ready_mode_state.h', + 'ready_mode/ready_mode_manager.h', 'register_bho.rgs', 'stream_impl.cc', 'stream_impl.h', @@ -904,7 +914,8 @@ ], 'process_outputs_as_sources': 0, 'message': - 'Assembling <(RULE_INPUT_PATH) to <(INTERMEDIATE_DIR)\<(RULE_INPUT_ROOT).obj.', + 'Assembling <(RULE_INPUT_PATH) to ' \ + '<(INTERMEDIATE_DIR)\<(RULE_INPUT_ROOT).obj.', }, ], 'msvs_settings': { diff --git a/chrome_frame/ready_mode/internal/installation_state.h b/chrome_frame/ready_mode/internal/installation_state.h new file mode 100644 index 0000000..52bdfcb --- /dev/null +++ b/chrome_frame/ready_mode/internal/installation_state.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_FRAME_READY_MODE_INTERNAL_INSTALLATION_STATE_H_ +#define CHROME_FRAME_READY_MODE_INTERNAL_INSTALLATION_STATE_H_ +#pragma once + +// Provides an interface to query and manipulate the registration and +// installation state of the product. +class InstallationState { + public: + virtual ~InstallationState() {} + + // Queries the installation state of the product (whether the product appears + // in "Add/Remove Programs" or its equivalent). + virtual bool IsProductInstalled() = 0; + + // Queries the registration state of the product (whether the COM objects, + // BHO, etc. are registered). + virtual bool IsProductRegistered() = 0; + + // Installs the product. Returns true iff successful. + virtual bool InstallProduct() = 0; + + // Unregisters the product. Fails if the product is installed. Returns true + // iff successful. + virtual bool UnregisterProduct() = 0; +}; // class InstallationState + +#endif // CHROME_FRAME_READY_MODE_INTERNAL_INSTALLATION_STATE_H_ diff --git a/chrome_frame/ready_mode/internal/ready_mode_state.h b/chrome_frame/ready_mode/internal/ready_mode_state.h new file mode 100644 index 0000000..d219631 --- /dev/null +++ b/chrome_frame/ready_mode/internal/ready_mode_state.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_FRAME_READY_MODE_INTERNAL_READY_MODE_STATE_H_ +#define CHROME_FRAME_READY_MODE_INTERNAL_READY_MODE_STATE_H_ +#pragma once + +// Allows the UI element to signal the user's response to a Ready Mode prompt. +class ReadyModeState { + public: + virtual ~ReadyModeState() {} + + // Indicates that the user has temporarily declined the product. + virtual void TemporarilyDeclineChromeFrame() = 0; + + // Indicates that the user has permanently declined the product. + virtual void PermanentlyDeclineChromeFrame() = 0; + + // Indicates that the user has accepted the product. + virtual void AcceptChromeFrame() = 0; +}; // class ReadyModeState + +#endif // CHROME_FRAME_READY_MODE_INTERNAL_READY_MODE_STATE_H_ diff --git a/chrome_frame/ready_mode/internal/ready_prompt_content.cc b/chrome_frame/ready_mode/internal/ready_prompt_content.cc new file mode 100644 index 0000000..4764e63 --- /dev/null +++ b/chrome_frame/ready_mode/internal/ready_prompt_content.cc @@ -0,0 +1,102 @@ +// 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_frame/ready_mode/internal/ready_prompt_content.h" + +#include <atlbase.h> +#include <atlwin.h> + +#include "base/logging.h" +#include "chrome_frame/ready_mode/internal/ready_mode_state.h" +#include "chrome_frame/ready_mode/internal/ready_prompt_window.h" + +ReadyPromptContent::ReadyPromptContent(ReadyModeState* ready_mode_state) + : ready_mode_state_(ready_mode_state) { +} + +ReadyPromptContent::~ReadyPromptContent() { + if (window_ != NULL && window_->IsWindow()) { + // The window must discard its ContentFrame pointer at this time. + window_->DestroyWindow(); + window_.reset(); + } +} + +bool ReadyPromptContent::InstallInFrame(Frame* frame) { + DCHECK(window_ == NULL); + DCHECK(ready_mode_state_ != NULL); + + // The window owns itself upon call to Initialize. + ReadyPromptWindow* new_window_ = new ReadyPromptWindow(); + window_ = new_window_->Initialize(frame, ready_mode_state_.release()); + + return window_ != NULL; +} + +void ReadyPromptContent::SetDimensions(const RECT& dimensions) { + if (window_ != NULL && window_->IsWindow()) { + window_->SetWindowPos(HWND_TOP, &dimensions, + ::IsRectEmpty(&dimensions) ? SWP_HIDEWINDOW : + SWP_SHOWWINDOW); + } +} + +bool GetDialogTemplateDimensions(ReadyPromptWindow* window, RECT* dimensions) { + HRSRC resource = ::FindResource(_AtlBaseModule.GetResourceInstance(), + MAKEINTRESOURCE(ReadyPromptWindow::IDD), + RT_DIALOG); + + HGLOBAL handle = NULL; + DLGTEMPLATE* dlgtemplate = NULL; + _DialogSplitHelper::DLGTEMPLATEEX* dlgtemplateex = NULL; + + if (resource == NULL) { + DPLOG(ERROR) << "Failed to find resource for ReadyPromptWindow::IDD"; + return false; + } + + handle = ::LoadResource(_AtlBaseModule.GetResourceInstance(), resource); + + if (handle == NULL) { + DPLOG(ERROR) << "Failed to load resource for ReadyPromptWindow::IDD"; + return false; + } + + dlgtemplate = reinterpret_cast<DLGTEMPLATE*>(::LockResource(handle)); + if (dlgtemplate == NULL) { + DPLOG(ERROR) << "Failed to lock resource for ReadyPromptWindow::IDD"; + return false; + } + + if (!_DialogSplitHelper::IsDialogEx(dlgtemplate)) { + DLOG(ERROR) << "Resource ReadyPromptWindow::IDD is not a DLGTEMPLATEEX"; + return false; + } + + dlgtemplateex = + reinterpret_cast<_DialogSplitHelper::DLGTEMPLATEEX*>(dlgtemplate); + + RECT dlgdimensions = {0, 0, dlgtemplateex->cx, dlgtemplateex->cy}; + if (!window->MapDialogRect(&dlgdimensions)) { + DPLOG(ERROR) << "Failure in MapDialogRect"; + return false; + } + + *dimensions = dlgdimensions; + return true; +} + +size_t ReadyPromptContent::GetDesiredSize(size_t width, size_t height) { + DCHECK(window_ != NULL && window_->IsWindow()); + + if (window_ == NULL || !window_->IsWindow()) { + return 0; + } + RECT dialog_dimensions = {0, 0, 0, 0}; + + if (GetDialogTemplateDimensions(window_.get(), &dialog_dimensions)) + return width == 0 ? dialog_dimensions.right : dialog_dimensions.bottom; + else + return width == 0 ? 0 : 39; +} diff --git a/chrome_frame/ready_mode/internal/ready_prompt_content.h b/chrome_frame/ready_mode/internal/ready_prompt_content.h new file mode 100644 index 0000000..bb8692e --- /dev/null +++ b/chrome_frame/ready_mode/internal/ready_prompt_content.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_FRAME_READY_MODE_INTERNAL_READY_PROMPT_CONTENT_H_ +#define CHROME_FRAME_READY_MODE_INTERNAL_READY_PROMPT_CONTENT_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "base/weak_ptr.h" +#include "chrome_frame/infobars/infobar_content.h" + +class ReadyModeState; +class ReadyPromptWindow; + +// Encapsulates the Ready Mode prompt inviting users to permanently activate +// Chrome Frame, temporarily disable Ready Mode, or permanently disable Ready +// Mode. +class ReadyPromptContent : public InfobarContent { + public: + explicit ReadyPromptContent(ReadyModeState* ready_mode_state); + ~ReadyPromptContent(); + + // InfobarContent implementation + virtual bool InstallInFrame(Frame* frame); + virtual void SetDimensions(const RECT& dimensions); + virtual size_t GetDesiredSize(size_t width, size_t height); + + private: + base::WeakPtr<ReadyPromptWindow> window_; + scoped_ptr<ReadyModeState> ready_mode_state_; + + DISALLOW_COPY_AND_ASSIGN(ReadyPromptContent); +}; // class ReadyPromptContent + +#endif // CHROME_FRAME_READY_MODE_INTERNAL_READY_PROMPT_CONTENT_H_ diff --git a/chrome_frame/ready_mode/internal/ready_prompt_window.cc b/chrome_frame/ready_mode/internal/ready_prompt_window.cc new file mode 100644 index 0000000..b05fbce --- /dev/null +++ b/chrome_frame/ready_mode/internal/ready_prompt_window.cc @@ -0,0 +1,74 @@ +// 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_frame/ready_mode/internal/ready_prompt_window.h" + +#include "base/compiler_specific.h" +#include "chrome_frame/ready_mode/internal/ready_mode_state.h" + +ReadyPromptWindow::ReadyPromptWindow() + : frame_(NULL), + weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +base::WeakPtr<ReadyPromptWindow> ReadyPromptWindow::Initialize( + InfobarContent::Frame* frame, ReadyModeState* ready_mode_state) { + DCHECK(frame != NULL); + DCHECK(frame_ == NULL); + DCHECK(ready_mode_state != NULL); + DCHECK(ready_mode_state_ == NULL); + + frame_ = frame; + ready_mode_state_.reset(ready_mode_state); + + DCHECK(!IsWindow()); + + if (Create(frame->GetFrameWindow()) == NULL) { + DPLOG(ERROR) << "Failed to create HWND for ReadyPromptWindow."; + delete this; + return base::WeakPtr<ReadyPromptWindow>(); + } + + return weak_ptr_factory_.GetWeakPtr(); +} + +void ReadyPromptWindow::OnDestroy() { + frame_ = NULL; +} + +BOOL ReadyPromptWindow::OnInitDialog(CWindow wndFocus, LPARAM lInitParam) { + DlgResize_Init(false); // false => 'no gripper' + return TRUE; +} + +LRESULT ReadyPromptWindow::OnYes(WORD /*wNotifyCode*/, + WORD /*wID*/, + HWND /*hWndCtl*/, + BOOL& /*bHandled*/) { + frame_->CloseInfobar(); + ready_mode_state_->AcceptChromeFrame(); + return 0; +} + +LRESULT ReadyPromptWindow::OnRemindMeLater(WORD /*wNotifyCode*/, + WORD /*wID*/, + HWND /*hWndCtl*/, + BOOL& /*bHandled*/) { + frame_->CloseInfobar(); + ready_mode_state_->TemporarilyDeclineChromeFrame(); + return 0; +} + +LRESULT ReadyPromptWindow::OnNo(WORD /*wNotifyCode*/, + WORD /*wID*/, + HWND /*hWndCtl*/, + BOOL& /*bHandled*/) { + frame_->CloseInfobar(); + ready_mode_state_->PermanentlyDeclineChromeFrame(); + return 0; +} + +void ReadyPromptWindow::OnFinalMessage(HWND) { + delete this; +} diff --git a/chrome_frame/ready_mode/internal/ready_prompt_window.h b/chrome_frame/ready_mode/internal/ready_prompt_window.h new file mode 100644 index 0000000..2d931a3 --- /dev/null +++ b/chrome_frame/ready_mode/internal/ready_prompt_window.h @@ -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. + +#ifndef CHROME_FRAME_READY_MODE_INTERNAL_READY_PROMPT_WINDOW_H_ +#define CHROME_FRAME_READY_MODE_INTERNAL_READY_PROMPT_WINDOW_H_ +#pragma once + +#include <atlbase.h> +#include <atlapp.h> +#include <atlcrack.h> +#include <atlframe.h> +#include <atlwin.h> + +#include "base/weak_ptr.h" +#include "base/scoped_ptr.h" +#include "chrome_frame/infobars/infobar_content.h" +#include "chrome_frame/resource.h" +#include "grit/generated_resources.h" + +class ReadyModeState; + +// Implements a dialog with text and buttons inviting the user to permanently +// activate the product or temporarily/permanently disable Ready Mode. +class ReadyPromptWindow + : public CDialogImpl<ReadyPromptWindow, CWindow>, + public CDialogResize<ReadyPromptWindow> { + public: + enum { IDD = IDD_CHROME_FRAME_READY_PROMPT }; + + ReadyPromptWindow(); + ~ReadyPromptWindow() {} + + // Initializes the dialog for display in the provided frame. The + // ReadyModeState will be invoked to capture the user's response, if any. + // + // Upon success, takes ownership of itself (to be deleted upon WM_DESTROY) and + // returns a weak pointer to this dialog. Upon failure, returns a null weak + // pointer and deletes self. + // + // In either case, takes ownership of the ReadyModeState, but not the frame. + base::WeakPtr<ReadyPromptWindow> Initialize(InfobarContent::Frame* frame, + ReadyModeState* ready_mode_state); + + BEGIN_MSG_MAP(InfobarWindow) + MSG_WM_INITDIALOG(OnInitDialog) + MSG_WM_DESTROY(OnDestroy) + COMMAND_HANDLER(IDACTIVATE, BN_CLICKED, OnYes) + COMMAND_HANDLER(IDLATER, BN_CLICKED, OnRemindMeLater) + COMMAND_HANDLER(IDNEVER, BN_CLICKED, OnNo) + CHAIN_MSG_MAP(CDialogResize<ReadyPromptWindow>) + END_MSG_MAP() + + BEGIN_DLGRESIZE_MAP(InfobarWindow) + DLGRESIZE_CONTROL(IDACTIVATE, DLSZ_CENTER_Y | DLSZ_MOVE_X) + DLGRESIZE_CONTROL(IDLATER, DLSZ_CENTER_Y | DLSZ_MOVE_X) + DLGRESIZE_CONTROL(IDNEVER, DLSZ_CENTER_Y | DLSZ_MOVE_X) + DLGRESIZE_CONTROL(IDC_PROMPT_MESSAGE, DLSZ_SIZE_Y | DLSZ_SIZE_X) + END_DLGRESIZE_MAP() + + virtual void OnFinalMessage(HWND); + + private: + // Event handlers. + void OnDestroy(); + BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam); + LRESULT OnYes(WORD wNotifyCode, + WORD wID, + HWND hWndCtl, + BOOL& bHandled); + LRESULT OnRemindMeLater(WORD wNotifyCode, + WORD wID, + HWND hWndCtl, + BOOL& bHandled); + LRESULT OnNo(WORD wNotifyCode, + WORD wID, + HWND hWndCtl, + BOOL& bHandled); + + InfobarContent::Frame* frame_; // Not owned by this instance + scoped_ptr<ReadyModeState> ready_mode_state_; + + base::WeakPtrFactory<ReadyPromptWindow> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(ReadyPromptWindow); +}; // class ReadyPromptWindow + +#endif // CHROME_FRAME_READY_MODE_INTERNAL_READY_PROMPT_WINDOW_H_ diff --git a/chrome_frame/ready_mode/internal/registry_ready_mode_state.cc b/chrome_frame/ready_mode/internal/registry_ready_mode_state.cc new file mode 100644 index 0000000..8561732 --- /dev/null +++ b/chrome_frame/ready_mode/internal/registry_ready_mode_state.cc @@ -0,0 +1,143 @@ +// 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_frame/ready_mode/internal/registry_ready_mode_state.h" + +#include "base/time.h" +#include "base/task.h" +#include "base/win/registry.h" +#include "chrome_frame/ready_mode/internal/installation_state.h" +#include "chrome_frame/ready_mode/ready_mode_manager.h" + +namespace { + +const wchar_t kReadyModeStateValue[] = L"ReadyModeState"; + +}; // namespace + +RegistryReadyModeState::RegistryReadyModeState( + const std::wstring& key_name, base::TimeDelta temporary_decline_duration, + InstallationState* installation_state, Observer* observer) + : key_name_(key_name), + temporary_decline_duration_(temporary_decline_duration), + installation_state_(installation_state), + observer_(observer) { +} + +RegistryReadyModeState::~RegistryReadyModeState() { +} + +base::Time RegistryReadyModeState::GetNow() { + return base::Time::Now(); +} + +ReadyModeStatus RegistryReadyModeState::GetStatus() { + if (installation_state_->IsProductInstalled()) + return READY_MODE_ACCEPTED; + + if (!installation_state_->IsProductRegistered()) + return READY_MODE_PERMANENTLY_DECLINED; + + bool exists = false; + int64 value = 0; + + if (!GetValue(&value, &exists)) + return READY_MODE_TEMPORARILY_DECLINED; + + if (!exists) + return READY_MODE_ACTIVE; + + if (value == 0) + return READY_MODE_PERMANENTLY_DECLINED; + + base::Time when_declined(base::Time::FromInternalValue(value)); + base::Time now(GetNow()); + + // If the decline duration has passed, or is further in the future than + // the total timeout, consider it expired. + bool expired = (now - when_declined) > temporary_decline_duration_ || + (when_declined - now) > temporary_decline_duration_; + + // To avoid a race-condition in GetValue (between ValueExists and ReadValue) + // we never delete the temporary decline flag. + if (expired) + return READY_MODE_ACTIVE; + + return READY_MODE_TEMPORARILY_DECLINED; +} + +bool RegistryReadyModeState::GetValue(int64* value, bool* exists) { + *exists = false; + *value = 0; + + base::win::RegKey config_key; + if (!config_key.Open(HKEY_CURRENT_USER, key_name_.c_str(), KEY_QUERY_VALUE)) { + DLOG(ERROR) << "Failed to open registry key " << key_name_; + return false; + } + + if (!config_key.ValueExists(kReadyModeStateValue)) + return true; + + int64 temp; + DWORD value_size = sizeof(temp); + DWORD type = 0; + if (!config_key.ReadValue(kReadyModeStateValue, &temp, &value_size, &type)) { + DLOG(ERROR) << "Failed to open registry key " << key_name_; + return false; + } + + if (value_size != sizeof(temp) || type != REG_QWORD) { + DLOG(ERROR) << "Unexpected state found under registry key " << key_name_ + << " and value " << kReadyModeStateValue; + config_key.DeleteValue(kReadyModeStateValue); + return true; + } + + *value = temp; + *exists = true; + return true; +} + +bool RegistryReadyModeState::StoreValue(int64 value) { + base::win::RegKey config_key; + if (config_key.Open(HKEY_CURRENT_USER, key_name_.c_str(), KEY_SET_VALUE) && + config_key.WriteValue(kReadyModeStateValue, &value, sizeof(value), + REG_QWORD)) { + return true; + } + + DLOG(ERROR) << "Failed to open or write to registry key " << key_name_ + << " and value " << kReadyModeStateValue; + + return false; +} + +void RegistryReadyModeState::TemporarilyDeclineChromeFrame() { + int64 value = GetNow().ToInternalValue(); + + if (StoreValue(value)) + observer_->OnStateChange(); +} + +void RegistryReadyModeState::PermanentlyDeclineChromeFrame() { + bool success = false; + + // Either change, by itself, will deactivate Ready Mode, though we prefer to + // also unregister, in order to free up resources. + + if (StoreValue(0)) + success = true; + + if (installation_state_->UnregisterProduct()) + success = true; + + if (success) + observer_->OnStateChange(); +} + +void RegistryReadyModeState::AcceptChromeFrame() { + if (installation_state_->InstallProduct()) + observer_->OnStateChange(); +} diff --git a/chrome_frame/ready_mode/internal/registry_ready_mode_state.h b/chrome_frame/ready_mode/internal/registry_ready_mode_state.h new file mode 100644 index 0000000..54f44e3 --- /dev/null +++ b/chrome_frame/ready_mode/internal/registry_ready_mode_state.h @@ -0,0 +1,73 @@ +// 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_FRAME_READY_MODE_INTERNAL_REGISTRY_READY_MODE_STATE_H_ +#define CHROME_FRAME_READY_MODE_INTERNAL_REGISTRY_READY_MODE_STATE_H_ +#pragma once + +#include <string> + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "chrome_frame/ready_mode/internal/ready_mode_state.h" + +enum ReadyModeStatus; + +class InstallationState; +class Task; + +// Implements ReadyModeState, storing state in the Registry and delegating to an +// instance of InstallationState to interact with the installer. Notifies a +// single Observer when the state changes. +class RegistryReadyModeState : public ReadyModeState { + public: + // Receives notification when the Ready Mode state changes in response to a + // user interaction. Does not receive notification when a temporary decline of + // Ready Mode expires. + class Observer { + public: + virtual ~Observer() {} + // Indicates that a state change has occurred. + virtual void OnStateChange() = 0; + }; // class Observer + + // Construct an instance backed by the specified key + // (pre-existing under HKCU). The provided duration indicates how long, after + // a temporary decline, Ready Mode re-activates. + // + // Takes ownership of the Observer and InstallationState instances. + RegistryReadyModeState(const std::wstring& key_name, + base::TimeDelta temporary_decline_duration, + InstallationState* installation_state, + Observer* observer); + virtual ~RegistryReadyModeState(); + + // Returns the current Ready Mode status, as determined using our registry + // state and InstallationState instance. + ReadyModeStatus GetStatus(); + + // ReadyModeState implementation + virtual void TemporarilyDeclineChromeFrame(); + virtual void PermanentlyDeclineChromeFrame(); + virtual void AcceptChromeFrame(); + + protected: + // allow dependency replacement via derivation for tests + virtual base::Time GetNow(); + + private: + // Retrieves state from the registry. Returns true upon success. + bool GetValue(int64* value, bool* exists); + // Stores value in the registry. Returns true upon success. + bool StoreValue(int64 value); + + base::TimeDelta temporary_decline_duration_; + int temporary_decline_length_seconds_; + std::wstring key_name_; + scoped_ptr<InstallationState> installation_state_; + scoped_ptr<Observer> observer_; +}; // class RegistryReadyModeState + +#endif // CHROME_FRAME_READY_MODE_INTERNAL_REGISTRY_READY_MODE_STATE_H_ diff --git a/chrome_frame/ready_mode/ready_mode_manager.cc b/chrome_frame/ready_mode/ready_mode_manager.cc new file mode 100644 index 0000000..c0e03fc --- /dev/null +++ b/chrome_frame/ready_mode/ready_mode_manager.cc @@ -0,0 +1,172 @@ +// 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_frame/ready_mode/ready_mode_manager.h" + +#include <exdisp.h> +#include <atlbase.h> +#include <shlguid.h> + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/utf_string_conversions.h" +#include "base/win/scoped_comptr.h" +#include "net/base/registry_controlled_domain.h" +#include "chrome_frame/infobars/infobar_manager.h" +#include "chrome_frame/ready_mode/internal/ready_prompt_content.h" +#include "chrome_frame/ready_mode/internal/registry_ready_mode_state.h" +#include "chrome_frame/utils.h" + +namespace { + +const int kTemporaryDeclineDurationMinutes = 1; + +}; // namespace + +class ReadyModeManagerImpl : public ReadyModeManager { + public: + bool Initialize(IUnknown* site) { + DCHECK(!web_browser_); + DCHECK(site); + + if (web_browser_ != NULL) + return false; + + if (site != NULL) + web_browser_.QueryFrom(site); + + return web_browser_ != NULL; + } + + protected: + virtual void OnDeclineChromeFrame() { + VARIANT flags = { VT_I4 }; + V_I4(&flags) = navNoHistory; + web_browser_->Navigate(CComBSTR(ASCIIToWide(rendered_url_.spec()).c_str()), + &flags, NULL, NULL, NULL); + } + + virtual InfobarManager* GetInfobarManager() { + base::win::ScopedComPtr<IOleWindow> ole_window; + HWND web_browserhwnd = NULL; + + if (web_browser_ != NULL) + DoQueryService(SID_SShellBrowser, web_browser_, ole_window.Receive()); + + if (ole_window != NULL) + ole_window->GetWindow(&web_browserhwnd); + + if (web_browserhwnd != NULL) + return InfobarManager::Get(web_browserhwnd); + + return NULL; + } + + private: + base::win::ScopedComPtr<IWebBrowser2> web_browser_; +}; + +class DisableReadyModeObserver : public RegistryReadyModeState::Observer { + public: + DisableReadyModeObserver(IWebBrowser2* web_browser) + : web_browser_(web_browser) { + DCHECK(web_browser != NULL); + } + virtual void OnStateChange() { + VARIANT flags = { VT_I4 }; + V_I4(&flags) = navNoHistory; + web_browser_->Navigate(CComBSTR(ASCIIToWide(rendered_url_.spec()).c_str()), + &flags, NULL, NULL, NULL); + } + private: + base::win::ScopedComPtr<IWebBrowser2> web_browser_; +}; + +class ReadyModeUIImpl : public ReadyModeWebBrowserAdapter::ReadyModeUI { + public: + virtual void ShowPrompt() { + InfobarManager* infobar_manager = GetInfobarManager(); + if (infobar_manager) { + InstallationState* = new DummyInstallationState(); + + ReadyModeState* ready_mode_state = new RegistryReadyModeState( + kChromeFrameConfigKey, + base::TimeDelta::FromMinutes(kTemporaryDeclineDurationMinutes), + installation_state, + observer); + base::WeakPtr<ReadyModeManager> weak_ptr(weak_ptr_factory_.GetWeakPtr()); + infobar_manager->Show(new ReadyPromptContent(NULL /* TODO(erikwright)*/), TOP_INFOBAR); + } + } + virtual void Hide() { + } + + private: + virtual InfobarManager* GetInfobarManager() { + base::win::ScopedComPtr<IOleWindow> ole_window; + HWND web_browserhwnd = NULL; + + if (web_browser_ != NULL) + DoQueryService(SID_SShellBrowser, web_browser_, ole_window.Receive()); + + if (ole_window != NULL) + ole_window->GetWindow(&web_browserhwnd); + + if (web_browserhwnd != NULL) + return InfobarManager::Get(web_browserhwnd); + + return NULL; + } + + base::win::ScopedComPtr<IWebBrowser2> web_browser_; +}; + +void ReadyMode::Configure(ChromeFrameIntegration* integration, + IWebBrowser2* site) { + +} + +ReadyModeManager::ReadyModeManager() + : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +ReadyModeManager::~ReadyModeManager() { +} + +ReadyModeManager* ReadyModeManager::CreateReadyModeManager(IUnknown* site) { + scoped_ptr<ReadyModeManagerImpl> impl(new ReadyModeManagerImpl()); + + if (impl->Initialize(site)) + return impl.release(); + + return NULL; +} + +void ReadyModeManager::BeginNavigationTo(std::string http_method, std::wstring url) { + if (!net::RegistryControlledDomainService::SameDomainOrHost(GURL(url), + rendered_url_)) { + InfobarManager* infobar_manager = GetInfobarManager(); + if (infobar_manager) + infobar_manager->HideAll(); + + rendered_url_ = GURL(); + } +} + +void ReadyModeManager::RenderingInHost(std::string http_method, std::wstring url) { + InfobarManager* infobar_manager = GetInfobarManager(); + if (infobar_manager) + infobar_manager->HideAll(); + rendered_url_ = GURL(url); +} + +void ReadyModeManager::RenderingInChromeFrame(std::string http_method, std::wstring url) { + InfobarManager* infobar_manager = GetInfobarManager(); + if (infobar_manager) { + base::WeakPtr<ReadyModeManager> weak_ptr(weak_ptr_factory_.GetWeakPtr()); + infobar_manager->Show(new ReadyPromptContent(NULL /* TODO(erikwright)*/), TOP_INFOBAR); + } + rendered_url_ = GURL(url); +} diff --git a/chrome_frame/ready_mode/ready_mode_manager.h b/chrome_frame/ready_mode/ready_mode_manager.h new file mode 100644 index 0000000..f496a26 --- /dev/null +++ b/chrome_frame/ready_mode/ready_mode_manager.h @@ -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. + +#ifndef CHROME_FRAME_READY_MODE_READY_MODE_MANAGER_H_ +#define CHROME_FRAME_READY_MODE_READY_MODE_MANAGER_H_ +#pragma once + +enum ReadyModeStatus { + READY_MODE_PERMANENTLY_DECLINED, + READY_MODE_TEMPORARILY_DECLINED, + READY_MODE_ACTIVE, + READY_MODE_ACCEPTED +}; // enum ReadyModeStatus + +#endif // CHROME_FRAME_READY_MODE_READY_MODE_MANAGER_H_ diff --git a/chrome_frame/test/ready_mode_unittest.cc b/chrome_frame/test/ready_mode_unittest.cc new file mode 100644 index 0000000..8262cbc --- /dev/null +++ b/chrome_frame/test/ready_mode_unittest.cc @@ -0,0 +1,473 @@ +// 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 <atlbase.h> +#include <atlapp.h> +#include <atlmisc.h> +#include <atlwin.h> + +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/win/registry.h" +#include "chrome_frame/infobars/infobar_content.h" +#include "chrome_frame/ready_mode/internal/installation_state.h" +#include "chrome_frame/ready_mode/internal/ready_mode_state.h" +#include "chrome_frame/ready_mode/internal/ready_prompt_content.h" +#include "chrome_frame/ready_mode/internal/ready_prompt_window.h" +#include "chrome_frame/ready_mode/internal/registry_ready_mode_state.h" +#include "chrome_frame/ready_mode/ready_mode_manager.h" +#include "chrome_frame/simple_resource_loader.h" +#include "chrome_frame/test/chrome_frame_test_utils.h" + +namespace { + +class SetResourceInstance { + public: + SetResourceInstance() : res_dll_(NULL), old_res_dll_(NULL) { + SimpleResourceLoader* loader_instance = SimpleResourceLoader::GetInstance(); + EXPECT_TRUE(loader_instance != NULL); + if (loader_instance != NULL) { + res_dll_ = loader_instance->GetResourceModuleHandle(); + EXPECT_TRUE(res_dll_ != NULL); + if (res_dll_ != NULL) { + old_res_dll_ = ATL::_AtlBaseModule.SetResourceInstance(res_dll_); + } + } + } + + ~SetResourceInstance() { + if (old_res_dll_ != NULL) { + CHECK_EQ(res_dll_, ATL::_AtlBaseModule.SetResourceInstance(old_res_dll_)); + } + } + + private: + HMODULE res_dll_; + HMODULE old_res_dll_; +}; // class SetResourceInstance + +class SimpleWindow : public CWindowImpl<SimpleWindow, + CWindow, + CFrameWinTraits> { + public: + virtual ~SimpleWindow() { + if (IsWindow()) + DestroyWindow(); + } + + static BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM l_param) { + HWND* out = reinterpret_cast<HWND*>(l_param); + EXPECT_TRUE(out != NULL); + + if (out == NULL) + return FALSE; + + EXPECT_TRUE(*out == NULL || ::IsChild(*out, hwnd)); + + if (*out == NULL) + *out = hwnd; + + return TRUE; + } + + HWND GetZeroOrOneChildWindows() { + HWND child = NULL; + EnumChildWindows(m_hWnd, EnumChildProc, reinterpret_cast<LPARAM>(&child)); + return child; + } + + BEGIN_MSG_MAP(SimpleWindow) + END_MSG_MAP() +}; // class SimpleWindow + +class MockInfobarContentFrame : public InfobarContent::Frame { + public: + // InfobarContent::Frame implementation + MOCK_METHOD0(GetFrameWindow, HWND(void)); + MOCK_METHOD0(CloseInfobar, void(void)); +}; // class Frame + +class MockReadyModeState : public ReadyModeState { + public: + // ReadyModeState implementation + MOCK_METHOD0(TemporarilyDeclineChromeFrame, void(void)); + MOCK_METHOD0(PermanentlyDeclineChromeFrame, void(void)); + MOCK_METHOD0(AcceptChromeFrame, void(void)); +}; // class MockReadyModeState + +ACTION_P(ReturnPointee, pointer) { + return *pointer; +} + +ACTION_P2(SetPointeeTo, pointer, value) { + *pointer = value; +} + +class MockInstallationState : public InstallationState { + public: + // InstallationState implementation + MOCK_METHOD0(IsProductInstalled, bool(void)); + MOCK_METHOD0(IsProductRegistered, bool(void)); + MOCK_METHOD0(InstallProduct, bool(void)); + MOCK_METHOD0(UnregisterProduct, bool(void)); +}; // class MockInstallationState + +class MockRegistryReadyModeStateObserver + : public RegistryReadyModeState::Observer { + public: + // RegistryReadyModeState::Observer implementation + MOCK_METHOD0(OnStateChange, void(void)); +}; // class MockRegistryReadyModeStateObserver + +} // namespace + +class ReadyPromptTest : public testing::Test { + public: + ReadyPromptTest() : hwnd_(NULL) {}; + + void SetUp() { + hwnd_ = window_.Create(NULL); + EXPECT_TRUE(hwnd_ != NULL); + window_.ShowWindow(SW_SHOW); + EXPECT_TRUE(window_.IsWindowVisible()); + EXPECT_CALL(frame_, GetFrameWindow()).Times(testing::AnyNumber()) + .WillRepeatedly(testing::Return(hwnd_)); + } + + protected: + SimpleWindow window_; + HWND hwnd_; + MockInfobarContentFrame frame_; + SetResourceInstance set_resource_instance_; +}; // class ReadyPromptTest + +class ReadyPromptWindowTest : public ReadyPromptTest { + public: + void SetUp() { + ReadyPromptTest::SetUp(); + + // owned by ReadyPromptWindow + state_ = new MockReadyModeState(); + ready_prompt_window_ = (new ReadyPromptWindow())->Initialize(&frame_, + state_); + + ASSERT_TRUE(ready_prompt_window_ != NULL); + RECT position = {0, 0, 800, 39}; + ASSERT_TRUE(ready_prompt_window_->SetWindowPos(HWND_TOP, &position, + SWP_SHOWWINDOW)); + } + + protected: + MockReadyModeState* state_; + base::WeakPtr<ReadyPromptWindow> ready_prompt_window_; +}; // class ReadyPromptWindowTest + +class ReadyPromptWindowButtonTest : public ReadyPromptWindowTest { + public: + void TearDown() { + ASSERT_TRUE(ready_prompt_window_ != NULL); + ASSERT_TRUE(ready_prompt_window_->DestroyWindow()); + ASSERT_TRUE(ready_prompt_window_ == NULL); + ASSERT_FALSE(message_loop_.WasTimedOut()); + + ReadyPromptWindowTest::TearDown(); + } + + protected: + struct ClickOnCaptionData { + const wchar_t* target_caption; + bool found; + }; // struct ClickOnCaptionData + + static BOOL CALLBACK ClickOnCaptionProc(HWND hwnd, LPARAM l_param) { + wchar_t window_caption[256] = {0}; + size_t buffer_length = arraysize(window_caption); + + ClickOnCaptionData* data = reinterpret_cast<ClickOnCaptionData*>(l_param); + EXPECT_TRUE(data->target_caption != NULL); + + if (data->target_caption == NULL) + return FALSE; + + if (wcsnlen(data->target_caption, buffer_length + 1) == buffer_length + 1) + return FALSE; + + if (::GetWindowText(hwnd, window_caption, buffer_length) == + static_cast<int>(buffer_length)) { + return TRUE; + } + + if (wcscmp(data->target_caption, window_caption) == 0) { + EXPECT_FALSE(data->found); + + CRect client_rect; + EXPECT_TRUE(::GetClientRect(hwnd, client_rect)); + + CPoint center_point(client_rect.CenterPoint()); + LPARAM coordinates = (center_point.y << 16) | center_point.x; + + ::PostMessage(hwnd, WM_LBUTTONDOWN, 0, coordinates); + ::PostMessage(hwnd, WM_LBUTTONUP, 0, coordinates); + + data->found = true; + } + + return TRUE; + } + + bool ClickOnCaption(const std::wstring& caption) { + ClickOnCaptionData data = {caption.c_str(), false}; + + ::EnumChildWindows(hwnd_, ClickOnCaptionProc, + reinterpret_cast<LPARAM>(&data)); + return data.found; + } + + void RunUntilCloseInfobar() { + EXPECT_CALL(frame_, CloseInfobar()).WillOnce(QUIT_LOOP(message_loop_)); + ASSERT_NO_FATAL_FAILURE(message_loop_.RunFor(5)); // seconds + } + + chrome_frame_test::TimedMsgLoop message_loop_; +}; // class ReadyPromptWindowButtonTest + +TEST_F(ReadyPromptTest, ReadyPromptContentTest) { + // owned by ReadyPromptContent + MockReadyModeState* state = new MockReadyModeState(); + scoped_ptr<ReadyPromptContent> content_(new ReadyPromptContent(state)); + + content_->InstallInFrame(&frame_); + + // Ensure that, if a child is created, it is not visible yet. + HWND child_hwnd = window_.GetZeroOrOneChildWindows(); + if (child_hwnd != NULL) { + CWindow child(child_hwnd); + RECT child_dimensions; + EXPECT_TRUE(child.GetClientRect(&child_dimensions)); + EXPECT_FALSE(child.IsWindowVisible() && !::IsRectEmpty(&child_dimensions)); + } + + int desired_height = content_->GetDesiredSize(400, 0); + EXPECT_GT(desired_height, 0); + RECT dimensions = {10, 15, 410, 20}; + content_->SetDimensions(dimensions); + + child_hwnd = window_.GetZeroOrOneChildWindows(); + EXPECT_TRUE(child_hwnd != NULL); + + if (child_hwnd != NULL) { + CWindow child(child_hwnd); + EXPECT_TRUE(child.IsWindowVisible()); + RECT child_dimensions; + EXPECT_TRUE(child.GetWindowRect(&child_dimensions)); + EXPECT_TRUE(window_.ScreenToClient(&child_dimensions)); + EXPECT_TRUE(::EqualRect(&child_dimensions, &dimensions)); + } + + // Being visible doesn't change the desired height + EXPECT_EQ(desired_height, content_->GetDesiredSize(400, 0)); + + content_.reset(); + + EXPECT_TRUE(window_.GetZeroOrOneChildWindows() == NULL); +} + +TEST_F(ReadyPromptWindowTest, Destroy) { + // Should delete associated mocks, not invoke on ReadyModeState + ready_prompt_window_->DestroyWindow(); +} + +TEST_F(ReadyPromptWindowButtonTest, ClickYes) { + EXPECT_CALL(*state_, AcceptChromeFrame()); + ASSERT_TRUE(ClickOnCaption(L"&Yes")); + RunUntilCloseInfobar(); +} + +TEST_F(ReadyPromptWindowButtonTest, ClickRemindMeLater) { + EXPECT_CALL(*state_, TemporarilyDeclineChromeFrame()); + ASSERT_TRUE(ClickOnCaption(L"Remind me &Later")); + RunUntilCloseInfobar(); +} + +TEST_F(ReadyPromptWindowButtonTest, ClickNo) { + EXPECT_CALL(*state_, PermanentlyDeclineChromeFrame()); + ASSERT_TRUE(ClickOnCaption(L"&No")); + RunUntilCloseInfobar(); +} + +class ReadyModeRegistryTest : public testing::Test { + public: + class TimeControlledRegistryReadyModeState : public RegistryReadyModeState { + public: + TimeControlledRegistryReadyModeState( + const std::wstring& key_name, + base::TimeDelta temporary_decline_duration, + InstallationState* installation_state, + Observer* observer) + : RegistryReadyModeState(key_name, temporary_decline_duration, + installation_state, observer), + now_(base::Time::Now()) { + } + + base::Time now_; + + protected: + virtual base::Time GetNow() { + return now_; + } + }; // class TimeControlledRegistryReadyModeState + + ReadyModeRegistryTest() + : is_product_registered_(true), + is_product_installed_(false), + observer_(NULL), + installation_state_(NULL) { + } + + virtual void SetUp() { + base::win::RegKey key; + ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, kRootKey, KEY_ALL_ACCESS)); + observer_ = new MockRegistryReadyModeStateObserver(); + installation_state_ = new MockInstallationState(); + + EXPECT_CALL(*installation_state_, IsProductRegistered()) + .Times(testing::AnyNumber()) + .WillRepeatedly(ReturnPointee(&is_product_registered_)); + EXPECT_CALL(*installation_state_, IsProductInstalled()) + .Times(testing::AnyNumber()) + .WillRepeatedly(ReturnPointee(&is_product_installed_)); + + ready_mode_state_.reset(new TimeControlledRegistryReadyModeState( + kRootKey, + base::TimeDelta::FromSeconds(kTemporaryDeclineDurationInSeconds), + installation_state_, + observer_)); + } + + virtual void TearDown() { + base::win::RegKey key; + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS)); + EXPECT_TRUE(key.DeleteKey(kRootKey)); + } + + protected: + void AdjustClockBySeconds(int seconds) { + ready_mode_state_->now_ += base::TimeDelta::FromSeconds(seconds); + } + + void ExpectUnregisterProductAndReturn(bool success) { + EXPECT_CALL(*installation_state_, UnregisterProduct()) + .WillOnce(testing::DoAll( + SetPointeeTo(&is_product_registered_, !success), + testing::Return(success))); + } + + void ExpectInstallProductAndReturn(bool success) { + EXPECT_CALL(*installation_state_, InstallProduct()) + .WillOnce(testing::DoAll(SetPointeeTo(&is_product_installed_, success), + testing::Return(success))); + } + + bool is_product_registered_; + bool is_product_installed_; + MockInstallationState* installation_state_; + MockRegistryReadyModeStateObserver* observer_; + + scoped_ptr<TimeControlledRegistryReadyModeState> ready_mode_state_; + base::win::RegKey config_key; + static const wchar_t kRootKey[]; + static const int kTemporaryDeclineDurationInSeconds; +}; // class ReadyModeRegistryTest + +const int ReadyModeRegistryTest::kTemporaryDeclineDurationInSeconds = 2; +const wchar_t ReadyModeRegistryTest::kRootKey[] = L"chrome_frame_unittests"; + +TEST_F(ReadyModeRegistryTest, CallNothing) { + // expect it to delete the two mocks... Google Mock fails if they are leaked. +} + +TEST_F(ReadyModeRegistryTest, NotInstalledStatus) { + ASSERT_EQ(READY_MODE_ACTIVE, ready_mode_state_->GetStatus()); +} + +TEST_F(ReadyModeRegistryTest, NotRegisteredStatus) { + is_product_registered_ = false; + ASSERT_EQ(READY_MODE_PERMANENTLY_DECLINED, ready_mode_state_->GetStatus()); +} + +TEST_F(ReadyModeRegistryTest, InstalledStatus) { + is_product_installed_ = true; + ASSERT_EQ(READY_MODE_ACCEPTED, ready_mode_state_->GetStatus()); +} + +TEST_F(ReadyModeRegistryTest, TemporarilyDeclineChromeFrame) { + ASSERT_EQ(READY_MODE_ACTIVE, ready_mode_state_->GetStatus()); + + EXPECT_CALL(*observer_, OnStateChange()); + ready_mode_state_->TemporarilyDeclineChromeFrame(); + + ASSERT_EQ(READY_MODE_TEMPORARILY_DECLINED, ready_mode_state_->GetStatus()); + + AdjustClockBySeconds(kTemporaryDeclineDurationInSeconds + 1); + ASSERT_EQ(READY_MODE_ACTIVE, ready_mode_state_->GetStatus()); +} + +TEST_F(ReadyModeRegistryTest, TemporarilyDeclineChromeFrameSetClockBack) { + ASSERT_EQ(READY_MODE_ACTIVE, ready_mode_state_->GetStatus()); + + EXPECT_CALL(*observer_, OnStateChange()); + ready_mode_state_->TemporarilyDeclineChromeFrame(); + + ASSERT_EQ(READY_MODE_TEMPORARILY_DECLINED, ready_mode_state_->GetStatus()); + + AdjustClockBySeconds(kTemporaryDeclineDurationInSeconds + 1); + ASSERT_EQ(READY_MODE_ACTIVE, ready_mode_state_->GetStatus()); +} + +TEST_F(ReadyModeRegistryTest, PermanentlyDeclineChromeFrame) { + ASSERT_EQ(READY_MODE_ACTIVE, ready_mode_state_->GetStatus()); + + EXPECT_CALL(*observer_, OnStateChange()); + ExpectUnregisterProductAndReturn(true); + ready_mode_state_->PermanentlyDeclineChromeFrame(); + + ASSERT_EQ(READY_MODE_PERMANENTLY_DECLINED, ready_mode_state_->GetStatus()); +} + +TEST_F(ReadyModeRegistryTest, PermanentlyDeclineChromeFrameFailUnregister) { + ASSERT_EQ(READY_MODE_ACTIVE, ready_mode_state_->GetStatus()); + + EXPECT_CALL(*observer_, OnStateChange()); + ExpectUnregisterProductAndReturn(false); + ready_mode_state_->PermanentlyDeclineChromeFrame(); + + ASSERT_EQ(READY_MODE_PERMANENTLY_DECLINED, ready_mode_state_->GetStatus()); +} + +TEST_F(ReadyModeRegistryTest, AcceptChromeFrame) { + ASSERT_EQ(READY_MODE_ACTIVE, ready_mode_state_->GetStatus()); + + EXPECT_CALL(*observer_, OnStateChange()); + ExpectInstallProductAndReturn(true); + ready_mode_state_->AcceptChromeFrame(); + + ASSERT_EQ(READY_MODE_ACCEPTED, ready_mode_state_->GetStatus()); +} + +// TODO(erikwright): What do we actually want to happen if the install fails? +// Stay in Ready Mode? Attempt to unregister (deactivate ready mode)? +// +// Which component is responsible for messaging the user? The installer? The +// InstallationState implementation? The ReadyModeState implementation? +TEST_F(ReadyModeRegistryTest, AcceptChromeFrameInstallFails) { + ASSERT_EQ(READY_MODE_ACTIVE, ready_mode_state_->GetStatus()); + + ExpectInstallProductAndReturn(false); + ready_mode_state_->AcceptChromeFrame(); + + ASSERT_EQ(READY_MODE_ACTIVE, ready_mode_state_->GetStatus()); +} |