1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/views/extensions/extension_install_dialog_view.h" 6 7#include "base/strings/utf_string_conversions.h" 8#include "chrome/browser/extensions/extension_browsertest.h" 9#include "chrome/browser/extensions/extension_icon_manager.h" 10#include "chrome/browser/extensions/extension_install_prompt.h" 11#include "chrome/browser/extensions/extension_install_prompt_experiment.h" 12#include "chrome/browser/ui/browser.h" 13#include "chrome/browser/ui/tabs/tab_strip_model.h" 14#include "chrome/browser/ui/views/constrained_window_views.h" 15#include "chrome/browser/ui/webui/extensions/extension_settings_handler.h" 16#include "chrome/common/extensions/extension_test_util.h" 17#include "content/public/browser/browser_thread.h" 18#include "content/public/test/test_utils.h" 19#include "extensions/common/extension.h" 20#include "extensions/common/permissions/permissions_data.h" 21#include "extensions/common/test_util.h" 22#include "ui/views/controls/scroll_view.h" 23#include "ui/views/view.h" 24#include "ui/views/widget/widget.h" 25 26// A simple delegate implementation that counts the number of times 27// |InstallUIProceed| and |InstallUIAbort| are called. 28class MockExtensionInstallPromptDelegate 29 : public ExtensionInstallPrompt::Delegate { 30 public: 31 MockExtensionInstallPromptDelegate() 32 : proceed_count_(0), 33 abort_count_(0) {} 34 35 // ExtensionInstallPrompt::Delegate overrides. 36 virtual void InstallUIProceed() OVERRIDE; 37 virtual void InstallUIAbort(bool user_initiated) OVERRIDE; 38 39 int proceed_count() { return proceed_count_; } 40 int abort_count() { return abort_count_; } 41 42 protected: 43 int proceed_count_; 44 int abort_count_; 45}; 46 47void MockExtensionInstallPromptDelegate::InstallUIProceed() { 48 ++proceed_count_; 49} 50 51void MockExtensionInstallPromptDelegate::InstallUIAbort(bool user_initiated) { 52 ++abort_count_; 53} 54 55// This lets us construct the parent for the prompt we're constructing in our 56// tests. 57class MockExtensionInstallPrompt : public ExtensionInstallPrompt { 58 public: 59 explicit MockExtensionInstallPrompt(content::WebContents* web_contents) 60 : ExtensionInstallPrompt(web_contents), prompt_(NULL) {} 61 virtual ~MockExtensionInstallPrompt() {} 62 void set_prompt(ExtensionInstallPrompt::Prompt* prompt) { 63 prompt_ = prompt; 64 } 65 ExtensionInstallPrompt::Prompt* get_prompt() { 66 return prompt_; 67 } 68 69 private: 70 ExtensionInstallPrompt::Prompt* prompt_; 71}; 72 73class ExtensionInstallDialogViewTestBase : public ExtensionBrowserTest { 74 protected: 75 explicit ExtensionInstallDialogViewTestBase( 76 ExtensionInstallPrompt::PromptType prompt_type); 77 virtual ~ExtensionInstallDialogViewTestBase() {} 78 79 virtual void SetUpOnMainThread() OVERRIDE; 80 81 ExtensionInstallPrompt::Prompt* prompt() { return prompt_.get(); } 82 content::WebContents* web_contents() { return web_contents_; } 83 MockExtensionInstallPromptDelegate* delegate() { return &delegate_; } 84 85 void SetPromptPermissions(std::vector<base::string16> permissions); 86 void SetPromptDetails(std::vector<base::string16> details); 87 void SetPromptRetainedFiles(std::vector<base::FilePath> files); 88 89 private: 90 const extensions::Extension* extension_; 91 MockExtensionInstallPrompt* install_prompt_; 92 scoped_refptr<ExtensionInstallPrompt::Prompt> prompt_; 93 content::WebContents* web_contents_; 94 MockExtensionInstallPromptDelegate delegate_; 95 96 DISALLOW_COPY_AND_ASSIGN(ExtensionInstallDialogViewTestBase); 97}; 98 99ExtensionInstallDialogViewTestBase::ExtensionInstallDialogViewTestBase( 100 ExtensionInstallPrompt::PromptType prompt_type) 101 : extension_(NULL), 102 install_prompt_(NULL), 103 prompt_(new ExtensionInstallPrompt::Prompt(prompt_type)), 104 web_contents_(NULL) { 105} 106 107void ExtensionInstallDialogViewTestBase::SetUpOnMainThread() { 108 ExtensionBrowserTest::SetUpOnMainThread(); 109 110 extension_ = ExtensionBrowserTest::LoadExtension(test_data_dir_.AppendASCII( 111 "install_prompt/permissions_scrollbar_regression")); 112 113 web_contents_ = browser()->tab_strip_model()->GetWebContentsAt(0); 114 115 install_prompt_ = new MockExtensionInstallPrompt(web_contents_); 116 install_prompt_->set_prompt(prompt_.get()); 117 prompt_->set_experiment(ExtensionInstallPromptExperiment::ControlGroup()); 118 prompt_->set_extension(extension_); 119 120 scoped_ptr<ExtensionIconManager> icon_manager(new ExtensionIconManager()); 121 const SkBitmap icon_bitmap = icon_manager->GetIcon(extension_->id()); 122 gfx::Image icon = gfx::Image::CreateFrom1xBitmap(icon_bitmap); 123 prompt_->set_icon(icon); 124 125 this->SetPromptPermissions(std::vector<base::string16>()); 126 this->SetPromptDetails(std::vector<base::string16>()); 127 this->SetPromptRetainedFiles(std::vector<base::FilePath>()); 128} 129 130void ExtensionInstallDialogViewTestBase::SetPromptPermissions( 131 std::vector<base::string16> permissions) { 132 prompt_->SetPermissions(permissions, 133 ExtensionInstallPrompt::REGULAR_PERMISSIONS); 134} 135 136void ExtensionInstallDialogViewTestBase::SetPromptDetails( 137 std::vector<base::string16> details) { 138 prompt_->SetPermissionsDetails(details, 139 ExtensionInstallPrompt::REGULAR_PERMISSIONS); 140} 141 142void ExtensionInstallDialogViewTestBase::SetPromptRetainedFiles( 143 std::vector<base::FilePath> files) { 144 prompt_->set_retained_files(files); 145} 146 147class ScrollbarTest : public ExtensionInstallDialogViewTestBase { 148 protected: 149 ScrollbarTest(); 150 virtual ~ScrollbarTest() {} 151 152 bool IsScrollbarVisible(); 153 154 private: 155 DISALLOW_COPY_AND_ASSIGN(ScrollbarTest); 156}; 157 158ScrollbarTest::ScrollbarTest() 159 : ExtensionInstallDialogViewTestBase( 160 ExtensionInstallPrompt::PERMISSIONS_PROMPT) { 161} 162 163bool ScrollbarTest::IsScrollbarVisible() { 164 ExtensionInstallPrompt::ShowParams show_params(web_contents()); 165 ExtensionInstallDialogView* dialog = new ExtensionInstallDialogView( 166 show_params.navigator, delegate(), prompt()); 167 168 // Create the modal view around the install dialog view. 169 views::Widget* modal = 170 CreateBrowserModalDialogViews(dialog, show_params.parent_window); 171 modal->Show(); 172 content::RunAllBlockingPoolTasksUntilIdle(); 173 174 // Check if the vertical scrollbar is visible. 175 return dialog->scroll_view()->vertical_scroll_bar()->visible(); 176} 177 178// Tests that a scrollbar _is_ shown for an excessively long extension 179// install prompt. 180IN_PROC_BROWSER_TEST_F(ScrollbarTest, LongPromptScrollbar) { 181 base::string16 permission_string(base::ASCIIToUTF16("Test")); 182 std::vector<base::string16> permissions; 183 std::vector<base::string16> details; 184 for (int i = 0; i < 20; i++) { 185 permissions.push_back(permission_string); 186 details.push_back(base::string16()); 187 } 188 this->SetPromptPermissions(permissions); 189 this->SetPromptDetails(details); 190 ASSERT_TRUE(IsScrollbarVisible()) << "Scrollbar is not visible"; 191} 192 193// Tests that a scrollbar isn't shown for this regression case. 194// See crbug.com/385570 for details. 195IN_PROC_BROWSER_TEST_F(ScrollbarTest, ScrollbarRegression) { 196 base::string16 permission_string(base::ASCIIToUTF16( 197 "Read and modify your data on *.facebook.com")); 198 std::vector<base::string16> permissions; 199 permissions.push_back(permission_string); 200 this->SetPromptPermissions(permissions); 201 std::vector<base::string16> details; 202 details.push_back(base::string16()); 203 this->SetPromptDetails(details); 204 ASSERT_FALSE(IsScrollbarVisible()) << "Scrollbar is visible"; 205} 206 207class ExtensionInstallDialogViewTest 208 : public ExtensionInstallDialogViewTestBase { 209 protected: 210 ExtensionInstallDialogViewTest() 211 : ExtensionInstallDialogViewTestBase( 212 ExtensionInstallPrompt::INSTALL_PROMPT) {} 213 virtual ~ExtensionInstallDialogViewTest() {} 214 215 private: 216 DISALLOW_COPY_AND_ASSIGN(ExtensionInstallDialogViewTest); 217}; 218 219// Verifies that the delegate is notified when the user selects to accept or 220// cancel the install. 221IN_PROC_BROWSER_TEST_F(ExtensionInstallDialogViewTest, NotifyDelegate) { 222 { 223 // The user confirms the install. 224 MockExtensionInstallPromptDelegate delegate; 225 scoped_ptr<ExtensionInstallDialogView> dialog( 226 new ExtensionInstallDialogView(web_contents(), &delegate, prompt())); 227 views::DialogDelegateView* delegate_view = dialog.get(); 228 229 delegate_view->Accept(); 230 delegate_view->OnClosed(); 231 dialog.reset(); 232 233 EXPECT_EQ(0, delegate.abort_count()); 234 EXPECT_EQ(1, delegate.proceed_count()); 235 } 236 237 { 238 // The user cancels the install. 239 MockExtensionInstallPromptDelegate delegate; 240 scoped_ptr<ExtensionInstallDialogView> dialog( 241 new ExtensionInstallDialogView(web_contents(), &delegate, prompt())); 242 views::DialogDelegateView* delegate_view = dialog.get(); 243 244 delegate_view->Cancel(); 245 delegate_view->OnClosed(); 246 dialog.reset(); 247 248 EXPECT_EQ(1, delegate.abort_count()); 249 EXPECT_EQ(0, delegate.proceed_count()); 250 } 251 252 { 253 // Corner case: Dialog is closed without the user explicitly choosing to 254 // proceed or cancel. 255 MockExtensionInstallPromptDelegate delegate; 256 scoped_ptr<ExtensionInstallDialogView> dialog( 257 new ExtensionInstallDialogView(web_contents(), &delegate, prompt())); 258 dialog.reset(); 259 260 EXPECT_EQ(1, delegate.abort_count()); 261 EXPECT_EQ(0, delegate.proceed_count()); 262 } 263} 264