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.
5 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
7 #include "base/prefs/pref_registry_simple.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/run_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "chrome/app/chrome_command_ids.h"
13 #include "chrome/browser/custom_handlers/protocol_handler_registry.h"
14 #include "chrome/browser/extensions/menu_manager.h"
15 #include "chrome/browser/extensions/menu_manager_factory.h"
16 #include "chrome/browser/extensions/test_extension_environment.h"
17 #include "chrome/browser/extensions/test_extension_prefs.h"
18 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
19 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h"
20 #include "chrome/browser/prefs/incognito_mode_prefs.h"
21 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
24 #include "chrome/test/base/testing_profile.h"
25 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
26 #include "components/data_reduction_proxy/core/browser/data_store.h"
27 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
28 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
29 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
30 #include "content/public/browser/web_contents.h"
31 #include "content/public/test/web_contents_tester.h"
33 #include "extensions/browser/extension_prefs.h"
34 #include "extensions/common/url_pattern.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36 #include "third_party/WebKit/public/web/WebContextMenuData.h"
39 using extensions::Extension
;
40 using extensions::MenuItem
;
41 using extensions::MenuManager
;
42 using extensions::MenuManagerFactory
;
43 using extensions::URLPatternSet
;
47 // Generates a ContextMenuParams that matches the specified contexts.
48 static content::ContextMenuParams
CreateParams(int contexts
) {
49 content::ContextMenuParams rv
;
50 rv
.is_editable
= false;
51 rv
.media_type
= blink::WebContextMenuData::MediaTypeNone
;
52 rv
.page_url
= GURL("http://test.page/");
54 static const base::char16 selected_text
[] = { 's', 'e', 'l', 0 };
55 if (contexts
& MenuItem::SELECTION
)
56 rv
.selection_text
= selected_text
;
58 if (contexts
& MenuItem::LINK
)
59 rv
.link_url
= GURL("http://test.link/");
61 if (contexts
& MenuItem::EDITABLE
)
62 rv
.is_editable
= true;
64 if (contexts
& MenuItem::IMAGE
) {
65 rv
.src_url
= GURL("http://test.image/");
66 rv
.media_type
= blink::WebContextMenuData::MediaTypeImage
;
69 if (contexts
& MenuItem::VIDEO
) {
70 rv
.src_url
= GURL("http://test.video/");
71 rv
.media_type
= blink::WebContextMenuData::MediaTypeVideo
;
74 if (contexts
& MenuItem::AUDIO
) {
75 rv
.src_url
= GURL("http://test.audio/");
76 rv
.media_type
= blink::WebContextMenuData::MediaTypeAudio
;
79 if (contexts
& MenuItem::FRAME
)
80 rv
.frame_url
= GURL("http://test.frame/");
87 class RenderViewContextMenuTest
: public testing::Test
{
89 // Proxy defined here to minimize friend classes in RenderViewContextMenu
90 static bool ExtensionContextAndPatternMatch(
91 const content::ContextMenuParams
& params
,
92 MenuItem::ContextList contexts
,
93 const URLPatternSet
& patterns
) {
94 return RenderViewContextMenu::ExtensionContextAndPatternMatch(
95 params
, contexts
, patterns
);
98 // Returns a test item.
99 MenuItem
* CreateTestItem(const Extension
* extension
, int uid
) {
100 MenuItem::Type type
= MenuItem::NORMAL
;
101 MenuItem::ContextList
contexts(MenuItem::ALL
);
102 const MenuItem::ExtensionKey
key(extension
->id());
103 bool incognito
= false;
104 MenuItem::Id
id(incognito
, key
);
106 return new MenuItem(id
, "Added by an extension", false, true, type
,
110 // Returns a test context menu.
111 TestRenderViewContextMenu
* CreateContextMenu(
112 TestingProfile
* profile
,
113 content::WebContents
* web_contents
) {
114 content::ContextMenuParams params
= CreateParams(MenuItem::LINK
);
115 params
.unfiltered_link_url
= params
.link_url
;
116 TestRenderViewContextMenu
* menu
=
117 new TestRenderViewContextMenu(web_contents
->GetMainFrame(), params
);
118 // TestingProfile (returned by profile()) does not provide a protocol
120 scoped_ptr
<ProtocolHandlerRegistry
> registry_(
121 new ProtocolHandlerRegistry(profile
, NULL
));
122 menu
->protocol_handler_registry_
= registry_
.get();
128 // Generates a URLPatternSet with a single pattern
129 static URLPatternSet
CreatePatternSet(const std::string
& pattern
) {
130 URLPattern
target(URLPattern::SCHEME_HTTP
);
131 target
.Parse(pattern
);
134 rv
.AddPattern(target
);
139 TEST_F(RenderViewContextMenuTest
, TargetIgnoredForPage
) {
140 content::ContextMenuParams params
= CreateParams(0);
142 MenuItem::ContextList contexts
;
143 contexts
.Add(MenuItem::PAGE
);
145 URLPatternSet patterns
= CreatePatternSet("*://test.none/*");
147 EXPECT_TRUE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
150 TEST_F(RenderViewContextMenuTest
, TargetCheckedForLink
) {
151 content::ContextMenuParams params
= CreateParams(MenuItem::LINK
);
153 MenuItem::ContextList contexts
;
154 contexts
.Add(MenuItem::PAGE
);
155 contexts
.Add(MenuItem::LINK
);
157 URLPatternSet patterns
= CreatePatternSet("*://test.none/*");
159 EXPECT_FALSE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
162 TEST_F(RenderViewContextMenuTest
, TargetCheckedForImage
) {
163 content::ContextMenuParams params
= CreateParams(MenuItem::IMAGE
);
165 MenuItem::ContextList contexts
;
166 contexts
.Add(MenuItem::PAGE
);
167 contexts
.Add(MenuItem::IMAGE
);
169 URLPatternSet patterns
= CreatePatternSet("*://test.none/*");
171 EXPECT_FALSE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
174 TEST_F(RenderViewContextMenuTest
, TargetCheckedForVideo
) {
175 content::ContextMenuParams params
= CreateParams(MenuItem::VIDEO
);
177 MenuItem::ContextList contexts
;
178 contexts
.Add(MenuItem::PAGE
);
179 contexts
.Add(MenuItem::VIDEO
);
181 URLPatternSet patterns
= CreatePatternSet("*://test.none/*");
183 EXPECT_FALSE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
186 TEST_F(RenderViewContextMenuTest
, TargetCheckedForAudio
) {
187 content::ContextMenuParams params
= CreateParams(MenuItem::AUDIO
);
189 MenuItem::ContextList contexts
;
190 contexts
.Add(MenuItem::PAGE
);
191 contexts
.Add(MenuItem::AUDIO
);
193 URLPatternSet patterns
= CreatePatternSet("*://test.none/*");
195 EXPECT_FALSE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
198 TEST_F(RenderViewContextMenuTest
, MatchWhenLinkedImageMatchesTarget
) {
199 content::ContextMenuParams params
= CreateParams(MenuItem::IMAGE
|
202 MenuItem::ContextList contexts
;
203 contexts
.Add(MenuItem::LINK
);
204 contexts
.Add(MenuItem::IMAGE
);
206 URLPatternSet patterns
= CreatePatternSet("*://test.link/*");
208 EXPECT_TRUE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
211 TEST_F(RenderViewContextMenuTest
, MatchWhenLinkedImageMatchesSource
) {
212 content::ContextMenuParams params
= CreateParams(MenuItem::IMAGE
|
215 MenuItem::ContextList contexts
;
216 contexts
.Add(MenuItem::LINK
);
217 contexts
.Add(MenuItem::IMAGE
);
219 URLPatternSet patterns
= CreatePatternSet("*://test.image/*");
221 EXPECT_TRUE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
224 TEST_F(RenderViewContextMenuTest
, NoMatchWhenLinkedImageMatchesNeither
) {
225 content::ContextMenuParams params
= CreateParams(MenuItem::IMAGE
|
228 MenuItem::ContextList contexts
;
229 contexts
.Add(MenuItem::LINK
);
230 contexts
.Add(MenuItem::IMAGE
);
232 URLPatternSet patterns
= CreatePatternSet("*://test.none/*");
234 EXPECT_FALSE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
237 TEST_F(RenderViewContextMenuTest
, TargetIgnoredForFrame
) {
238 content::ContextMenuParams params
= CreateParams(MenuItem::FRAME
);
240 MenuItem::ContextList contexts
;
241 contexts
.Add(MenuItem::FRAME
);
243 URLPatternSet patterns
= CreatePatternSet("*://test.none/*");
245 EXPECT_TRUE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
248 TEST_F(RenderViewContextMenuTest
, TargetIgnoredForEditable
) {
249 content::ContextMenuParams params
= CreateParams(MenuItem::EDITABLE
);
251 MenuItem::ContextList contexts
;
252 contexts
.Add(MenuItem::EDITABLE
);
254 URLPatternSet patterns
= CreatePatternSet("*://test.none/*");
256 EXPECT_TRUE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
259 TEST_F(RenderViewContextMenuTest
, TargetIgnoredForSelection
) {
260 content::ContextMenuParams params
=
261 CreateParams(MenuItem::SELECTION
);
263 MenuItem::ContextList contexts
;
264 contexts
.Add(MenuItem::SELECTION
);
266 URLPatternSet patterns
= CreatePatternSet("*://test.none/*");
268 EXPECT_TRUE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
271 TEST_F(RenderViewContextMenuTest
, TargetIgnoredForSelectionOnLink
) {
272 content::ContextMenuParams params
= CreateParams(
273 MenuItem::SELECTION
| MenuItem::LINK
);
275 MenuItem::ContextList contexts
;
276 contexts
.Add(MenuItem::SELECTION
);
277 contexts
.Add(MenuItem::LINK
);
279 URLPatternSet patterns
= CreatePatternSet("*://test.none/*");
281 EXPECT_TRUE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
284 TEST_F(RenderViewContextMenuTest
, TargetIgnoredForSelectionOnImage
) {
285 content::ContextMenuParams params
= CreateParams(
286 MenuItem::SELECTION
| MenuItem::IMAGE
);
288 MenuItem::ContextList contexts
;
289 contexts
.Add(MenuItem::SELECTION
);
290 contexts
.Add(MenuItem::IMAGE
);
292 URLPatternSet patterns
= CreatePatternSet("*://test.none/*");
294 EXPECT_TRUE(ExtensionContextAndPatternMatch(params
, contexts
, patterns
));
297 TEST_F(RenderViewContextMenuTest
, ItemWithSameTitleFromTwoExtensions
) {
298 extensions::TestExtensionEnvironment env
;
300 MenuManager
* menu_manager
= // Owned by env.profile().
301 static_cast<MenuManager
*>(
302 (MenuManagerFactory::GetInstance()->SetTestingFactoryAndUse(
304 &MenuManagerFactory::BuildServiceInstanceForTesting
)));
306 const Extension
* extension1
=
307 env
.MakeExtension(base::DictionaryValue(),
308 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
309 const Extension
* extension2
=
310 env
.MakeExtension(base::DictionaryValue(),
311 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
313 // Create two items in two extensions with same title.
314 MenuItem
* item1
= CreateTestItem(extension1
, 1);
315 ASSERT_TRUE(menu_manager
->AddContextItem(extension1
, item1
));
316 MenuItem
* item2
= CreateTestItem(extension2
, 2);
317 ASSERT_TRUE(menu_manager
->AddContextItem(extension2
, item2
));
319 scoped_ptr
<content::WebContents
> web_contents
= env
.MakeTab();
320 scoped_ptr
<TestRenderViewContextMenu
> menu(
321 CreateContextMenu(env
.profile(), web_contents
.get()));
323 const ui::MenuModel
& model
= menu
->menu_model();
324 base::string16 expected_title
= base::ASCIIToUTF16("Added by an extension");
325 int num_items_found
= 0;
326 for (int i
= 0; i
< model
.GetItemCount(); ++i
) {
327 if (expected_title
== model
.GetLabelAt(i
))
331 // Expect both items to be found.
332 ASSERT_EQ(2, num_items_found
);
335 class RenderViewContextMenuPrefsTest
: public ChromeRenderViewHostTestHarness
{
337 void SetUp() override
{
338 ChromeRenderViewHostTestHarness::SetUp();
339 registry_
.reset(new ProtocolHandlerRegistry(profile(), NULL
));
342 void TearDown() override
{
344 ChromeRenderViewHostTestHarness::TearDown();
347 TestRenderViewContextMenu
* CreateContextMenu() {
348 content::ContextMenuParams params
= CreateParams(MenuItem::LINK
);
349 params
.unfiltered_link_url
= params
.link_url
;
350 content::WebContents
* wc
= web_contents();
351 TestRenderViewContextMenu
* menu
= new TestRenderViewContextMenu(
352 wc
->GetMainFrame(), params
);
353 // TestingProfile (returned by profile()) does not provide a protocol
355 menu
->protocol_handler_registry_
= registry_
.get();
360 void AppendImageItems(TestRenderViewContextMenu
* menu
) {
361 menu
->AppendImageItems();
364 void SetupDataReductionProxy(bool enable_data_reduction_proxy
) {
366 data_reduction_proxy::DataReductionProxyTestContext::Builder()
368 data_reduction_proxy::DataReductionProxyParams::kAllowed
|
369 data_reduction_proxy::DataReductionProxyParams::
371 data_reduction_proxy::DataReductionProxyParams::kPromoAllowed
)
373 .SkipSettingsInitialization()
376 DataReductionProxyChromeSettings
* settings
=
377 DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
380 // TODO(bengr): Remove prefs::kProxy registration after M46. See
381 // http://crbug.com/445599.
382 PrefRegistrySimple
* registry
=
383 drp_test_context_
->pref_service()->registry();
384 registry
->RegisterDictionaryPref(prefs::kProxy
);
386 drp_test_context_
->pref_service()->SetBoolean(
387 data_reduction_proxy::prefs::kDataReductionProxyEnabled
,
388 enable_data_reduction_proxy
);
389 drp_test_context_
->InitSettings();
391 settings
->InitDataReductionProxySettings(
392 drp_test_context_
->io_data(), drp_test_context_
->pref_service(),
393 drp_test_context_
->request_context_getter(),
394 make_scoped_ptr(new data_reduction_proxy::DataStore()),
395 base::ThreadTaskRunnerHandle::Get(),
396 base::ThreadTaskRunnerHandle::Get());
399 // Force destruction of |DataReductionProxySettings| so that objects on DB
400 // task runner can be destroyed before test threads are destroyed. This method
401 // must be called by tests that call |SetupDataReductionProxy|. We cannot
402 // destroy |drp_test_context_| until browser context keyed services are
403 // destroyed since |DataReductionProxyChromeSettings| holds a pointer to the
404 // |PrefService|, which is owned by |drp_test_context_|.
405 void DestroyDataReductionProxySettings() {
406 drp_test_context_
->DestroySettings();
410 scoped_ptr
<data_reduction_proxy::DataReductionProxyTestContext
>
414 scoped_ptr
<ProtocolHandlerRegistry
> registry_
;
417 // Verifies when Incognito Mode is not available (disabled by policy),
418 // Open Link in Incognito Window link in the context menu is disabled.
419 TEST_F(RenderViewContextMenuPrefsTest
,
420 DisableOpenInIncognitoWindowWhenIncognitoIsDisabled
) {
421 scoped_ptr
<TestRenderViewContextMenu
> menu(CreateContextMenu());
423 // Initially the Incognito mode is be enabled. So is the Open Link in
424 // Incognito Window link.
425 ASSERT_TRUE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
));
427 menu
->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
));
429 // Disable Incognito mode.
430 IncognitoModePrefs::SetAvailability(profile()->GetPrefs(),
431 IncognitoModePrefs::DISABLED
);
432 menu
.reset(CreateContextMenu());
433 ASSERT_TRUE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
));
435 menu
->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
));
438 // Make sure the checking custom command id that is not enabled will not
439 // cause DCHECK failure.
440 TEST_F(RenderViewContextMenuPrefsTest
,
441 IsCustomCommandIdEnabled
) {
442 scoped_ptr
<TestRenderViewContextMenu
> menu(CreateContextMenu());
444 EXPECT_FALSE(menu
->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_CUSTOM_FIRST
));
447 // Verify that request headers specify that data reduction proxy should return
448 // the original non compressed resource when "Save Image As..." is used with
449 // Data Saver enabled.
450 TEST_F(RenderViewContextMenuPrefsTest
, DataSaverEnabledSaveImageAs
) {
451 SetupDataReductionProxy(true);
453 content::ContextMenuParams params
= CreateParams(MenuItem::IMAGE
);
454 params
.unfiltered_link_url
= params
.link_url
;
455 content::WebContents
* wc
= web_contents();
456 scoped_ptr
<TestRenderViewContextMenu
> menu(
457 new TestRenderViewContextMenu(wc
->GetMainFrame(), params
));
459 menu
->ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEIMAGEAS
, 0);
461 const std::string
& headers
=
462 content::WebContentsTester::For(web_contents())->GetSaveFrameHeaders();
463 EXPECT_TRUE(headers
.find("Chrome-Proxy: pass-through") != std::string::npos
);
464 EXPECT_TRUE(headers
.find("Cache-Control: no-cache") != std::string::npos
);
466 DestroyDataReductionProxySettings();
469 // Verify that request headers do not specify pass through when "Save Image
470 // As..." is used with Data Saver disabled.
471 TEST_F(RenderViewContextMenuPrefsTest
, DataSaverDisabledSaveImageAs
) {
472 SetupDataReductionProxy(false);
474 content::ContextMenuParams params
= CreateParams(MenuItem::IMAGE
);
475 params
.unfiltered_link_url
= params
.link_url
;
476 content::WebContents
* wc
= web_contents();
477 scoped_ptr
<TestRenderViewContextMenu
> menu(
478 new TestRenderViewContextMenu(wc
->GetMainFrame(), params
));
480 menu
->ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEIMAGEAS
, 0);
482 const std::string
& headers
=
483 content::WebContentsTester::For(web_contents())->GetSaveFrameHeaders();
484 EXPECT_TRUE(headers
.find("Chrome-Proxy: pass-through") == std::string::npos
);
485 EXPECT_TRUE(headers
.find("Cache-Control: no-cache") == std::string::npos
);
487 DestroyDataReductionProxySettings();
490 // Verify that the Chrome-Proxy Lo-Fi directive causes the context menu to
491 // display the "Load Image" menu item.
492 TEST_F(RenderViewContextMenuPrefsTest
, DataSaverLoadImage
) {
493 SetupDataReductionProxy(true);
494 content::ContextMenuParams params
= CreateParams(MenuItem::IMAGE
);
495 params
.properties
[data_reduction_proxy::chrome_proxy_header()] =
496 data_reduction_proxy::chrome_proxy_lo_fi_directive();
497 params
.unfiltered_link_url
= params
.link_url
;
498 content::WebContents
* wc
= web_contents();
499 scoped_ptr
<TestRenderViewContextMenu
> menu(
500 new TestRenderViewContextMenu(wc
->GetMainFrame(), params
));
501 AppendImageItems(menu
.get());
503 ASSERT_TRUE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE
));
505 DestroyDataReductionProxySettings();