summaryrefslogtreecommitdiffstats
path: root/chrome_frame
diff options
context:
space:
mode:
authorerikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-14 20:56:01 +0000
committererikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-14 20:56:01 +0000
commit2bf5f415ac56938fa2b99417b21ce0c75476d3f4 (patch)
tree2f32792d3fe9ea6f4a5b003c073e68e8d8ab2c2a /chrome_frame
parent61e512816c5510a234f0e5dd21d7556f207cc4cd (diff)
downloadchromium_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.gyp15
-rw-r--r--chrome_frame/ready_mode/internal/installation_state.h31
-rw-r--r--chrome_frame/ready_mode/internal/ready_mode_state.h24
-rw-r--r--chrome_frame/ready_mode/internal/ready_prompt_content.cc102
-rw-r--r--chrome_frame/ready_mode/internal/ready_prompt_content.h37
-rw-r--r--chrome_frame/ready_mode/internal/ready_prompt_window.cc74
-rw-r--r--chrome_frame/ready_mode/internal/ready_prompt_window.h87
-rw-r--r--chrome_frame/ready_mode/internal/registry_ready_mode_state.cc143
-rw-r--r--chrome_frame/ready_mode/internal/registry_ready_mode_state.h73
-rw-r--r--chrome_frame/ready_mode/ready_mode_manager.cc172
-rw-r--r--chrome_frame/ready_mode/ready_mode_manager.h16
-rw-r--r--chrome_frame/test/ready_mode_unittest.cc473
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());
+}