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.
7 #include "base/command_line.h"
8 #include "base/macros.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/app/chrome_command_ids.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
16 #include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h"
17 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
18 #include "chrome/browser/search_engines/template_url_service_factory.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
21 #include "chrome/common/render_messages.h"
22 #include "chrome/test/base/in_process_browser_test.h"
23 #include "chrome/test/base/ui_test_utils.h"
24 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
25 #include "components/search_engines/template_url_data.h"
26 #include "components/search_engines/template_url_service.h"
27 #include "content/public/browser/browser_message_filter.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/navigation_controller.h"
30 #include "content/public/browser/navigation_entry.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/render_process_host.h"
33 #include "content/public/browser/render_view_host.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/test/browser_test_utils.h"
36 #include "content/public/test/test_utils.h"
37 #include "third_party/WebKit/public/web/WebContextMenuData.h"
38 #include "third_party/WebKit/public/web/WebInputEvent.h"
40 using content::WebContents
;
44 class ContextMenuBrowserTest
: public InProcessBrowserTest
{
46 ContextMenuBrowserTest() {}
48 TestRenderViewContextMenu
* CreateContextMenu(
49 const GURL
& unfiltered_url
,
51 blink::WebContextMenuData::MediaType media_type
) {
52 content::ContextMenuParams params
;
53 params
.media_type
= media_type
;
54 params
.unfiltered_link_url
= unfiltered_url
;
55 params
.link_url
= url
;
57 WebContents
* web_contents
=
58 browser()->tab_strip_model()->GetActiveWebContents();
59 params
.page_url
= web_contents
->GetController().GetActiveEntry()->GetURL();
60 #if defined(OS_MACOSX)
61 params
.writing_direction_default
= 0;
62 params
.writing_direction_left_to_right
= 0;
63 params
.writing_direction_right_to_left
= 0;
65 TestRenderViewContextMenu
* menu
= new TestRenderViewContextMenu(
66 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
72 TestRenderViewContextMenu
* CreateContextMenuMediaTypeNone(
73 const GURL
& unfiltered_url
,
75 return CreateContextMenu(unfiltered_url
, url
,
76 blink::WebContextMenuData::MediaTypeNone
);
80 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
,
81 OpenEntryPresentForNormalURLs
) {
82 scoped_ptr
<TestRenderViewContextMenu
> menu(CreateContextMenuMediaTypeNone(
83 GURL("http://www.google.com/"), GURL("http://www.google.com/")));
85 ASSERT_TRUE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
));
86 ASSERT_TRUE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW
));
87 ASSERT_TRUE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION
));
90 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
,
91 OpenEntryAbsentForFilteredURLs
) {
92 scoped_ptr
<TestRenderViewContextMenu
> menu(
93 CreateContextMenuMediaTypeNone(GURL("chrome://history"), GURL()));
95 ASSERT_FALSE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
));
96 ASSERT_FALSE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW
));
97 ASSERT_TRUE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION
));
100 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
,
101 ContextMenuForCanvas
) {
102 content::ContextMenuParams params
;
103 params
.media_type
= blink::WebContextMenuData::MediaTypeCanvas
;
105 TestRenderViewContextMenu
menu(
106 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
110 ASSERT_TRUE(menu
.IsItemPresent(IDC_CONTENT_CONTEXT_SAVEIMAGEAS
));
111 ASSERT_TRUE(menu
.IsItemPresent(IDC_CONTENT_CONTEXT_COPYIMAGE
));
114 // Opens a link in a new tab via a "real" context menu.
115 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
, RealMenu
) {
116 ContextMenuNotificationObserver
menu_observer(
117 IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
);
118 ui_test_utils::WindowedTabAddedNotificationObserver
tab_observer(
119 content::NotificationService::AllSources());
121 // Go to a page with a link
122 ui_test_utils::NavigateToURL(
123 browser(), GURL("data:text/html,<a href='about:blank'>link</a>"));
125 // Open a context menu.
126 blink::WebMouseEvent mouse_event
;
127 mouse_event
.type
= blink::WebInputEvent::MouseDown
;
128 mouse_event
.button
= blink::WebMouseEvent::ButtonRight
;
131 content::WebContents
* tab
=
132 browser()->tab_strip_model()->GetActiveWebContents();
133 gfx::Rect offset
= tab
->GetContainerBounds();
134 mouse_event
.globalX
= 15 + offset
.x();
135 mouse_event
.globalY
= 15 + offset
.y();
136 mouse_event
.clickCount
= 1;
137 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
138 mouse_event
.type
= blink::WebInputEvent::MouseUp
;
139 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
141 // The menu_observer will select "Open in new tab", wait for the new tab to
144 tab
= tab_observer
.GetTab();
145 content::WaitForLoadStop(tab
);
147 // Verify that it's the correct tab.
148 EXPECT_EQ(GURL("about:blank"), tab
->GetURL());
151 // Verify that "Open Link in New Tab" doesn't send URL fragment as referrer.
152 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
, OpenInNewTabReferrer
) {
153 ui_test_utils::WindowedTabAddedNotificationObserver
tab_observer(
154 content::NotificationService::AllSources());
156 ASSERT_TRUE(test_server()->Start());
157 GURL
echoheader(test_server()->GetURL("echoheader?Referer"));
159 // Go to a |page| with a link to echoheader URL.
160 GURL
page("data:text/html,<a href='" + echoheader
.spec() + "'>link</a>");
161 ui_test_utils::NavigateToURL(browser(), page
);
163 // Set up referrer URL with fragment.
164 const GURL
kReferrerWithFragment("http://foo.com/test#fragment");
165 const std::string
kCorrectReferrer("http://foo.com/test");
167 // Set up menu with link URL.
168 content::ContextMenuParams context_menu_params
;
169 context_menu_params
.page_url
= kReferrerWithFragment
;
170 context_menu_params
.link_url
= echoheader
;
172 // Select "Open Link in New Tab" and wait for the new tab to be added.
173 TestRenderViewContextMenu
menu(
174 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
175 context_menu_params
);
177 menu
.ExecuteCommand(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
, 0);
180 content::WebContents
* tab
= tab_observer
.GetTab();
181 content::WaitForLoadStop(tab
);
183 // Verify that it's the correct tab.
184 ASSERT_EQ(echoheader
, tab
->GetURL());
185 // Verify that the text on the page matches |kCorrectReferrer|.
186 std::string actual_referrer
;
187 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
189 "window.domAutomationController.send(window.document.body.textContent);",
191 ASSERT_EQ(kCorrectReferrer
, actual_referrer
);
193 // Verify that the referrer on the page matches |kCorrectReferrer|.
194 std::string page_referrer
;
195 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
197 "window.domAutomationController.send(window.document.referrer);",
199 ASSERT_EQ(kCorrectReferrer
, page_referrer
);
202 // Verify that "Open Link in Incognito Window " doesn't send referrer URL.
203 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
, OpenIncognitoNoneReferrer
) {
204 ui_test_utils::WindowedTabAddedNotificationObserver
tab_observer(
205 content::NotificationService::AllSources());
207 ASSERT_TRUE(test_server()->Start());
208 GURL
echoheader(test_server()->GetURL("echoheader?Referer"));
210 // Go to a |page| with a link to echoheader URL.
211 GURL
page("data:text/html,<a href='" + echoheader
.spec() + "'>link</a>");
212 ui_test_utils::NavigateToURL(browser(), page
);
214 // Set up referrer URL with fragment.
215 const GURL
kReferrerWithFragment("http://foo.com/test#fragment");
216 const std::string
kNoneReferrer("None");
217 const std::string
kEmptyReferrer("");
219 // Set up menu with link URL.
220 content::ContextMenuParams context_menu_params
;
221 context_menu_params
.page_url
= kReferrerWithFragment
;
222 context_menu_params
.link_url
= echoheader
;
224 // Select "Open Link in Incognito Window" and wait for window to be added.
225 TestRenderViewContextMenu
menu(
226 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
227 context_menu_params
);
229 menu
.ExecuteCommand(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
, 0);
232 content::WebContents
* tab
= tab_observer
.GetTab();
233 content::WaitForLoadStop(tab
);
235 // Verify that it's the correct tab.
236 ASSERT_EQ(echoheader
, tab
->GetURL());
237 // Verify that the text on the page matches |kNoneReferrer|.
238 std::string actual_referrer
;
239 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
241 "window.domAutomationController.send(window.document.body.textContent);",
243 ASSERT_EQ(kNoneReferrer
, actual_referrer
);
245 // Verify that the referrer on the page matches |kEmptyReferrer|.
246 std::string page_referrer
;
247 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
249 "window.domAutomationController.send(window.document.referrer);",
251 ASSERT_EQ(kEmptyReferrer
, page_referrer
);
254 // Check filename on clicking "Save Link As" via a "real" context menu.
255 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
, SuggestedFileName
) {
256 // Register observer.
257 SaveLinkAsContextMenuObserver
menu_observer(
258 content::NotificationService::AllSources());
260 // Go to a page with a link having download attribute.
261 const std::string
kSuggestedFilename("test_filename.png");
262 ui_test_utils::NavigateToURL(
264 GURL("data:text/html,<a href='about:blank' download='" +
265 kSuggestedFilename
+ "'>link</a>"));
267 // Open a context menu.
268 blink::WebMouseEvent mouse_event
;
269 mouse_event
.type
= blink::WebInputEvent::MouseDown
;
270 mouse_event
.button
= blink::WebMouseEvent::ButtonRight
;
273 content::WebContents
* tab
=
274 browser()->tab_strip_model()->GetActiveWebContents();
275 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
276 mouse_event
.type
= blink::WebInputEvent::MouseUp
;
277 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
279 // Wait for context menu to be visible.
280 menu_observer
.WaitForMenu();
283 base::string16 suggested_filename
= menu_observer
.GetSuggestedFilename();
284 ASSERT_EQ(kSuggestedFilename
, base::UTF16ToUTF8(suggested_filename
).c_str());
287 // Ensure that View Page Info won't crash if there is no visible entry.
288 // See http://crbug.com/370863.
289 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
, ViewPageInfoWithNoEntry
) {
290 // Create a new tab with no committed entry.
291 ui_test_utils::WindowedTabAddedNotificationObserver
tab_observer(
292 content::NotificationService::AllSources());
293 ASSERT_TRUE(content::ExecuteScript(
294 browser()->tab_strip_model()->GetActiveWebContents(), "window.open();"));
296 content::WebContents
* tab
= tab_observer
.GetTab();
297 EXPECT_FALSE(tab
->GetController().GetLastCommittedEntry());
298 EXPECT_FALSE(tab
->GetController().GetVisibleEntry());
300 // Create a context menu.
301 content::ContextMenuParams context_menu_params
;
302 TestRenderViewContextMenu
menu(tab
->GetMainFrame(), context_menu_params
);
305 // The item shouldn't be enabled in the menu.
306 EXPECT_FALSE(menu
.IsCommandIdEnabled(IDC_CONTENT_CONTEXT_VIEWPAGEINFO
));
308 // Ensure that viewing page info doesn't crash even if you can get to it.
309 menu
.ExecuteCommand(IDC_CONTENT_CONTEXT_VIEWPAGEINFO
, 0);
312 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
, DataSaverOpenOrigImageInNewTab
) {
313 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
314 command_line
->AppendSwitch(
315 data_reduction_proxy::switches::kEnableDataReductionProxy
);
317 scoped_ptr
<TestRenderViewContextMenu
> menu(
318 CreateContextMenu(GURL(), GURL("http://url.com/image.png"),
319 blink::WebContextMenuData::MediaTypeImage
));
321 ASSERT_FALSE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
));
322 ASSERT_TRUE(menu
->IsItemPresent(
323 IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB
));
326 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
,
327 DataSaverHttpsOpenImageInNewTab
) {
328 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
329 command_line
->AppendSwitch(
330 data_reduction_proxy::switches::kEnableDataReductionProxy
);
332 scoped_ptr
<TestRenderViewContextMenu
> menu(
333 CreateContextMenu(GURL(), GURL("https://url.com/image.png"),
334 blink::WebContextMenuData::MediaTypeImage
));
337 menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB
));
338 ASSERT_TRUE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
));
341 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
, OpenImageInNewTab
) {
342 scoped_ptr
<TestRenderViewContextMenu
> menu(
343 CreateContextMenu(GURL(), GURL("http://url.com/image.png"),
344 blink::WebContextMenuData::MediaTypeImage
));
346 menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB
));
347 ASSERT_TRUE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
));
350 class ThumbnailResponseWatcher
: public content::NotificationObserver
{
358 class MessageFilter
: public content::BrowserMessageFilter
{
360 explicit MessageFilter(ThumbnailResponseWatcher
* owner
)
361 : content::BrowserMessageFilter(ChromeMsgStart
), owner_(owner
) {}
363 bool OnMessageReceived(const IPC::Message
& message
) override
{
364 if (message
.type() ==
365 ChromeViewHostMsg_RequestThumbnailForContextNode_ACK::ID
) {
366 content::BrowserThread::PostTask(
367 content::BrowserThread::UI
, FROM_HERE
,
368 base::Bind(&MessageFilter::OnRequestThumbnailForContextNodeACK
,
374 void OnRequestThumbnailForContextNodeACK() {
376 owner_
->OnRequestThumbnailForContextNodeACK();
379 void Disown() { owner_
= nullptr; }
382 ~MessageFilter() override
{}
384 ThumbnailResponseWatcher
* owner_
;
386 DISALLOW_COPY_AND_ASSIGN(MessageFilter
);
389 explicit ThumbnailResponseWatcher(
390 content::RenderProcessHost
* render_process_host
)
391 : message_loop_runner_(new content::MessageLoopRunner
),
392 filter_(new MessageFilter(this)),
393 quit_reason_(STILL_RUNNING
) {
394 notification_registrar_
.Add(
395 this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
396 content::Source
<content::RenderProcessHost
>(render_process_host
));
397 notification_registrar_
.Add(
398 this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
,
399 content::Source
<content::RenderProcessHost
>(render_process_host
));
400 render_process_host
->AddFilter(filter_
.get());
403 ~ThumbnailResponseWatcher() override
{ filter_
->Disown(); }
405 QuitReason
Wait() WARN_UNUSED_RESULT
{
406 message_loop_runner_
->Run();
407 DCHECK_NE(STILL_RUNNING
, quit_reason_
);
411 void Observe(int type
,
412 const content::NotificationSource
& source
,
413 const content::NotificationDetails
& details
) override
{
414 DCHECK(type
== content::NOTIFICATION_RENDERER_PROCESS_CLOSED
||
415 type
== content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
);
416 quit_reason_
= RENDER_PROCESS_GONE
;
417 message_loop_runner_
->Quit();
420 void OnRequestThumbnailForContextNodeACK() {
421 quit_reason_
= THUMBNAIL_RECEIVED
;
422 message_loop_runner_
->Quit();
426 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
427 scoped_refptr
<MessageFilter
> filter_
;
428 content::NotificationRegistrar notification_registrar_
;
429 QuitReason quit_reason_
;
431 DISALLOW_COPY_AND_ASSIGN(ThumbnailResponseWatcher
);
434 // Maintains image search test state. In particular, note that |menu_observer_|
435 // must live until the right-click completes asynchronously.
436 class SearchByImageBrowserTest
: public InProcessBrowserTest
{
438 void SetupAndLoadImagePage(const std::string
& image_path
) {
439 // The test server must start first, so that we know the port that the test
441 ASSERT_TRUE(test_server()->Start());
442 SetupImageSearchEngine();
444 // Go to a page with an image in it. The test server doesn't serve the image
445 // with the right MIME type, so use a data URL to make a page containing it.
446 GURL
image_url(test_server()->GetURL(image_path
));
447 GURL
page("data:text/html,<img src='" + image_url
.spec() + "'>");
448 ui_test_utils::NavigateToURL(browser(), page
);
451 void AttemptImageSearch() {
452 // Right-click where the image should be.
453 // |menu_observer_| will cause the search-by-image menu item to be clicked.
454 menu_observer_
.reset(new ContextMenuNotificationObserver(
455 IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
));
456 content::WebContents
* tab
=
457 browser()->tab_strip_model()->GetActiveWebContents();
458 content::SimulateMouseClickAt(tab
, 0, blink::WebMouseEvent::ButtonRight
,
462 GURL
GetImageSearchURL() {
463 static const char kImageSearchURL
[] = "imagesearch";
464 return test_server()->GetURL(kImageSearchURL
);
468 void SetupImageSearchEngine() {
469 static const char kShortName
[] = "test";
470 static const char kSearchURL
[] = "search?q={searchTerms}";
471 static const char kImageSearchPostParams
[] =
472 "thumb={google:imageThumbnail}";
474 TemplateURLService
* model
=
475 TemplateURLServiceFactory::GetForProfile(browser()->profile());
477 ui_test_utils::WaitForTemplateURLServiceToLoad(model
);
478 ASSERT_TRUE(model
->loaded());
480 TemplateURLData data
;
481 data
.short_name
= base::ASCIIToUTF16(kShortName
);
482 data
.SetKeyword(data
.short_name
);
483 data
.SetURL(test_server()->GetURL(kSearchURL
).spec());
484 data
.image_url
= GetImageSearchURL().spec();
485 data
.image_url_post_params
= kImageSearchPostParams
;
487 // The model takes ownership of |template_url|.
488 TemplateURL
* template_url
= new TemplateURL(data
);
489 ASSERT_TRUE(model
->Add(template_url
));
490 model
->SetUserSelectedDefaultSearchProvider(template_url
);
493 void TearDownInProcessBrowserTestFixture() override
{
494 menu_observer_
.reset();
497 scoped_ptr
<ContextMenuNotificationObserver
> menu_observer_
;
500 IN_PROC_BROWSER_TEST_F(SearchByImageBrowserTest
, ImageSearchWithValidImage
) {
501 static const char kValidImage
[] = "files/image_search/valid.png";
502 SetupAndLoadImagePage(kValidImage
);
504 ui_test_utils::WindowedTabAddedNotificationObserver
tab_observer(
505 content::NotificationService::AllSources());
506 AttemptImageSearch();
508 // The browser should open a new tab for an image search.
510 content::WebContents
* new_tab
= tab_observer
.GetTab();
511 content::WaitForLoadStop(new_tab
);
512 EXPECT_EQ(GetImageSearchURL(), new_tab
->GetURL());
515 IN_PROC_BROWSER_TEST_F(SearchByImageBrowserTest
, ImageSearchWithCorruptImage
) {
516 static const char kCorruptImage
[] = "files/image_search/corrupt.png";
517 SetupAndLoadImagePage(kCorruptImage
);
519 content::WebContents
* tab
=
520 browser()->tab_strip_model()->GetActiveWebContents();
521 ThumbnailResponseWatcher
watcher(tab
->GetRenderProcessHost());
522 AttemptImageSearch();
524 // The browser should receive a response from the renderer, because the
525 // renderer should not crash.
526 EXPECT_EQ(ThumbnailResponseWatcher::THUMBNAIL_RECEIVED
, watcher
.Wait());