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 ContextMenuWaiter
menu_observer(content::NotificationService::AllSources());
259 // Go to a page with a link having download attribute.
260 const std::string
kSuggestedFilename("test_filename.png");
261 ui_test_utils::NavigateToURL(
263 GURL("data:text/html,<a href='about:blank' download='" +
264 kSuggestedFilename
+ "'>link</a>"));
266 // Open a context menu.
267 blink::WebMouseEvent mouse_event
;
268 mouse_event
.type
= blink::WebInputEvent::MouseDown
;
269 mouse_event
.button
= blink::WebMouseEvent::ButtonRight
;
272 content::WebContents
* tab
=
273 browser()->tab_strip_model()->GetActiveWebContents();
274 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
275 mouse_event
.type
= blink::WebInputEvent::MouseUp
;
276 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
278 // Wait for context menu to be visible.
279 menu_observer
.WaitForMenuOpenAndClose();
282 base::string16 suggested_filename
= menu_observer
.params().suggested_filename
;
283 ASSERT_EQ(kSuggestedFilename
, base::UTF16ToUTF8(suggested_filename
).c_str());
286 // Ensure that View Page Info won't crash if there is no visible entry.
287 // See http://crbug.com/370863.
288 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
, ViewPageInfoWithNoEntry
) {
289 // Create a new tab with no committed entry.
290 ui_test_utils::WindowedTabAddedNotificationObserver
tab_observer(
291 content::NotificationService::AllSources());
292 ASSERT_TRUE(content::ExecuteScript(
293 browser()->tab_strip_model()->GetActiveWebContents(), "window.open();"));
295 content::WebContents
* tab
= tab_observer
.GetTab();
296 EXPECT_FALSE(tab
->GetController().GetLastCommittedEntry());
297 EXPECT_FALSE(tab
->GetController().GetVisibleEntry());
299 // Create a context menu.
300 content::ContextMenuParams context_menu_params
;
301 TestRenderViewContextMenu
menu(tab
->GetMainFrame(), context_menu_params
);
304 // The item shouldn't be enabled in the menu.
305 EXPECT_FALSE(menu
.IsCommandIdEnabled(IDC_CONTENT_CONTEXT_VIEWPAGEINFO
));
307 // Ensure that viewing page info doesn't crash even if you can get to it.
308 menu
.ExecuteCommand(IDC_CONTENT_CONTEXT_VIEWPAGEINFO
, 0);
311 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
, DataSaverOpenOrigImageInNewTab
) {
312 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
313 command_line
->AppendSwitch(
314 data_reduction_proxy::switches::kEnableDataReductionProxy
);
316 scoped_ptr
<TestRenderViewContextMenu
> menu(
317 CreateContextMenu(GURL(), GURL("http://url.com/image.png"),
318 blink::WebContextMenuData::MediaTypeImage
));
320 ASSERT_FALSE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
));
321 ASSERT_TRUE(menu
->IsItemPresent(
322 IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB
));
325 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
,
326 DataSaverHttpsOpenImageInNewTab
) {
327 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
328 command_line
->AppendSwitch(
329 data_reduction_proxy::switches::kEnableDataReductionProxy
);
331 scoped_ptr
<TestRenderViewContextMenu
> menu(
332 CreateContextMenu(GURL(), GURL("https://url.com/image.png"),
333 blink::WebContextMenuData::MediaTypeImage
));
336 menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB
));
337 ASSERT_TRUE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
));
340 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest
, OpenImageInNewTab
) {
341 scoped_ptr
<TestRenderViewContextMenu
> menu(
342 CreateContextMenu(GURL(), GURL("http://url.com/image.png"),
343 blink::WebContextMenuData::MediaTypeImage
));
345 menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB
));
346 ASSERT_TRUE(menu
->IsItemPresent(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
));
349 class ThumbnailResponseWatcher
: public content::NotificationObserver
{
357 class MessageFilter
: public content::BrowserMessageFilter
{
359 explicit MessageFilter(ThumbnailResponseWatcher
* owner
)
360 : content::BrowserMessageFilter(ChromeMsgStart
), owner_(owner
) {}
362 bool OnMessageReceived(const IPC::Message
& message
) override
{
363 if (message
.type() ==
364 ChromeViewHostMsg_RequestThumbnailForContextNode_ACK::ID
) {
365 content::BrowserThread::PostTask(
366 content::BrowserThread::UI
, FROM_HERE
,
367 base::Bind(&MessageFilter::OnRequestThumbnailForContextNodeACK
,
373 void OnRequestThumbnailForContextNodeACK() {
375 owner_
->OnRequestThumbnailForContextNodeACK();
378 void Disown() { owner_
= nullptr; }
381 ~MessageFilter() override
{}
383 ThumbnailResponseWatcher
* owner_
;
385 DISALLOW_COPY_AND_ASSIGN(MessageFilter
);
388 explicit ThumbnailResponseWatcher(
389 content::RenderProcessHost
* render_process_host
)
390 : message_loop_runner_(new content::MessageLoopRunner
),
391 filter_(new MessageFilter(this)),
392 quit_reason_(STILL_RUNNING
) {
393 notification_registrar_
.Add(
394 this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
395 content::Source
<content::RenderProcessHost
>(render_process_host
));
396 notification_registrar_
.Add(
397 this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
,
398 content::Source
<content::RenderProcessHost
>(render_process_host
));
399 render_process_host
->AddFilter(filter_
.get());
402 ~ThumbnailResponseWatcher() override
{ filter_
->Disown(); }
404 QuitReason
Wait() WARN_UNUSED_RESULT
{
405 message_loop_runner_
->Run();
406 DCHECK_NE(STILL_RUNNING
, quit_reason_
);
410 void Observe(int type
,
411 const content::NotificationSource
& source
,
412 const content::NotificationDetails
& details
) override
{
413 DCHECK(type
== content::NOTIFICATION_RENDERER_PROCESS_CLOSED
||
414 type
== content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
);
415 quit_reason_
= RENDER_PROCESS_GONE
;
416 message_loop_runner_
->Quit();
419 void OnRequestThumbnailForContextNodeACK() {
420 quit_reason_
= THUMBNAIL_RECEIVED
;
421 message_loop_runner_
->Quit();
425 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
426 scoped_refptr
<MessageFilter
> filter_
;
427 content::NotificationRegistrar notification_registrar_
;
428 QuitReason quit_reason_
;
430 DISALLOW_COPY_AND_ASSIGN(ThumbnailResponseWatcher
);
433 // Maintains image search test state. In particular, note that |menu_observer_|
434 // must live until the right-click completes asynchronously.
435 class SearchByImageBrowserTest
: public InProcessBrowserTest
{
437 void SetupAndLoadImagePage(const std::string
& image_path
) {
438 // The test server must start first, so that we know the port that the test
440 ASSERT_TRUE(test_server()->Start());
441 SetupImageSearchEngine();
443 // Go to a page with an image in it. The test server doesn't serve the image
444 // with the right MIME type, so use a data URL to make a page containing it.
445 GURL
image_url(test_server()->GetURL(image_path
));
446 GURL
page("data:text/html,<img src='" + image_url
.spec() + "'>");
447 ui_test_utils::NavigateToURL(browser(), page
);
450 void AttemptImageSearch() {
451 // Right-click where the image should be.
452 // |menu_observer_| will cause the search-by-image menu item to be clicked.
453 menu_observer_
.reset(new ContextMenuNotificationObserver(
454 IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
));
455 content::WebContents
* tab
=
456 browser()->tab_strip_model()->GetActiveWebContents();
457 content::SimulateMouseClickAt(tab
, 0, blink::WebMouseEvent::ButtonRight
,
461 GURL
GetImageSearchURL() {
462 static const char kImageSearchURL
[] = "imagesearch";
463 return test_server()->GetURL(kImageSearchURL
);
467 void SetupImageSearchEngine() {
468 static const char kShortName
[] = "test";
469 static const char kSearchURL
[] = "search?q={searchTerms}";
470 static const char kImageSearchPostParams
[] =
471 "thumb={google:imageThumbnail}";
473 TemplateURLService
* model
=
474 TemplateURLServiceFactory::GetForProfile(browser()->profile());
476 ui_test_utils::WaitForTemplateURLServiceToLoad(model
);
477 ASSERT_TRUE(model
->loaded());
479 TemplateURLData data
;
480 data
.short_name
= base::ASCIIToUTF16(kShortName
);
481 data
.SetKeyword(data
.short_name
);
482 data
.SetURL(test_server()->GetURL(kSearchURL
).spec());
483 data
.image_url
= GetImageSearchURL().spec();
484 data
.image_url_post_params
= kImageSearchPostParams
;
486 // The model takes ownership of |template_url|.
487 TemplateURL
* template_url
= new TemplateURL(data
);
488 ASSERT_TRUE(model
->Add(template_url
));
489 model
->SetUserSelectedDefaultSearchProvider(template_url
);
492 void TearDownInProcessBrowserTestFixture() override
{
493 menu_observer_
.reset();
496 scoped_ptr
<ContextMenuNotificationObserver
> menu_observer_
;
499 IN_PROC_BROWSER_TEST_F(SearchByImageBrowserTest
, ImageSearchWithValidImage
) {
500 static const char kValidImage
[] = "files/image_search/valid.png";
501 SetupAndLoadImagePage(kValidImage
);
503 ui_test_utils::WindowedTabAddedNotificationObserver
tab_observer(
504 content::NotificationService::AllSources());
505 AttemptImageSearch();
507 // The browser should open a new tab for an image search.
509 content::WebContents
* new_tab
= tab_observer
.GetTab();
510 content::WaitForLoadStop(new_tab
);
511 EXPECT_EQ(GetImageSearchURL(), new_tab
->GetURL());
514 IN_PROC_BROWSER_TEST_F(SearchByImageBrowserTest
, ImageSearchWithCorruptImage
) {
515 static const char kCorruptImage
[] = "files/image_search/corrupt.png";
516 SetupAndLoadImagePage(kCorruptImage
);
518 content::WebContents
* tab
=
519 browser()->tab_strip_model()->GetActiveWebContents();
520 ThumbnailResponseWatcher
watcher(tab
->GetRenderProcessHost());
521 AttemptImageSearch();
523 // The browser should receive a response from the renderer, because the
524 // renderer should not crash.
525 EXPECT_EQ(ThumbnailResponseWatcher::THUMBNAIL_RECEIVED
, watcher
.Wait());