1// Copyright (c) 2012 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 "base/path_service.h"
6#include "base/strings/utf_string_conversions.h"
7#include "content/browser/child_process_security_policy_impl.h"
8#include "content/browser/frame_host/render_frame_host_impl.h"
9#include "content/browser/renderer_host/render_message_filter.h"
10#include "content/browser/renderer_host/render_view_host_delegate_view.h"
11#include "content/browser/renderer_host/render_widget_helper.h"
12#include "content/common/input_messages.h"
13#include "content/common/view_messages.h"
14#include "content/public/browser/browser_context.h"
15#include "content/public/browser/navigation_entry.h"
16#include "content/public/common/bindings_policy.h"
17#include "content/public/common/drop_data.h"
18#include "content/public/common/url_constants.h"
19#include "content/public/test/mock_render_process_host.h"
20#include "content/test/test_content_browser_client.h"
21#include "content/test/test_render_view_host.h"
22#include "content/test/test_web_contents.h"
23#include "net/base/filename_util.h"
24#include "third_party/WebKit/public/web/WebDragOperation.h"
25#include "ui/base/page_transition_types.h"
26
27namespace content {
28
29class RenderViewHostTestBrowserClient : public TestContentBrowserClient {
30 public:
31  RenderViewHostTestBrowserClient() {}
32  virtual ~RenderViewHostTestBrowserClient() {}
33
34  virtual bool IsHandledURL(const GURL& url) OVERRIDE {
35    return url.scheme() == url::kFileScheme;
36  }
37
38 private:
39  DISALLOW_COPY_AND_ASSIGN(RenderViewHostTestBrowserClient);
40};
41
42class RenderViewHostTest : public RenderViewHostImplTestHarness {
43 public:
44  RenderViewHostTest() : old_browser_client_(NULL) {}
45  virtual ~RenderViewHostTest() {}
46
47  virtual void SetUp() OVERRIDE {
48    RenderViewHostImplTestHarness::SetUp();
49    old_browser_client_ = SetBrowserClientForTesting(&test_browser_client_);
50  }
51
52  virtual void TearDown() OVERRIDE {
53    SetBrowserClientForTesting(old_browser_client_);
54    RenderViewHostImplTestHarness::TearDown();
55  }
56
57 private:
58  RenderViewHostTestBrowserClient test_browser_client_;
59  ContentBrowserClient* old_browser_client_;
60
61  DISALLOW_COPY_AND_ASSIGN(RenderViewHostTest);
62};
63
64// All about URLs reported by the renderer should get rewritten to about:blank.
65// See RenderViewHost::OnNavigate for a discussion.
66TEST_F(RenderViewHostTest, FilterAbout) {
67  test_rvh()->SendNavigate(1, GURL("about:cache"));
68  ASSERT_TRUE(controller().GetVisibleEntry());
69  EXPECT_EQ(GURL(url::kAboutBlankURL),
70            controller().GetVisibleEntry()->GetURL());
71}
72
73// Create a full screen popup RenderWidgetHost and View.
74TEST_F(RenderViewHostTest, CreateFullscreenWidget) {
75  int routing_id = process()->GetNextRoutingID();
76  test_rvh()->CreateNewFullscreenWidget(routing_id);
77}
78
79// Makes sure that the RenderViewHost is not waiting for an unload ack when
80// reloading a page. If this is not the case, when reloading, the contents may
81// get closed out even though the user pressed the reload button.
82TEST_F(RenderViewHostTest, ResetUnloadOnReload) {
83  const GURL url1("http://foo1");
84  const GURL url2("http://foo2");
85
86  // This test is for a subtle timing bug. Here's the sequence that triggered
87  // the bug:
88  // . go to a page.
89  // . go to a new page, preferably one that takes a while to resolve, such
90  //   as one on a site that doesn't exist.
91  //   . After this step IsWaitingForUnloadACK returns true on the first RVH.
92  // . click stop before the page has been commited.
93  // . click reload.
94  //   . IsWaitingForUnloadACK still returns true, and if the hang monitor fires
95  //     the contents gets closed.
96
97  NavigateAndCommit(url1);
98  controller().LoadURL(
99      url2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
100  // Simulate the ClosePage call which is normally sent by the net::URLRequest.
101  rvh()->ClosePage();
102  // Needed so that navigations are not suspended on the RVH.
103  test_rvh()->SendBeforeUnloadACK(true);
104  contents()->Stop();
105  controller().Reload(false);
106  EXPECT_FALSE(test_rvh()->IsWaitingForUnloadACK());
107}
108
109// Ensure we do not grant bindings to a process shared with unprivileged views.
110TEST_F(RenderViewHostTest, DontGrantBindingsToSharedProcess) {
111  // Create another view in the same process.
112  scoped_ptr<TestWebContents> new_web_contents(
113      TestWebContents::Create(browser_context(), rvh()->GetSiteInstance()));
114
115  rvh()->AllowBindings(BINDINGS_POLICY_WEB_UI);
116  EXPECT_FALSE(rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
117}
118
119class MockDraggingRenderViewHostDelegateView
120    : public RenderViewHostDelegateView {
121 public:
122  virtual ~MockDraggingRenderViewHostDelegateView() {}
123  virtual void StartDragging(const DropData& drop_data,
124                             blink::WebDragOperationsMask allowed_ops,
125                             const gfx::ImageSkia& image,
126                             const gfx::Vector2d& image_offset,
127                             const DragEventSourceInfo& event_info) OVERRIDE {
128    drag_url_ = drop_data.url;
129    html_base_url_ = drop_data.html_base_url;
130  }
131  virtual void UpdateDragCursor(blink::WebDragOperation operation) OVERRIDE {}
132  virtual void GotFocus() OVERRIDE {}
133  virtual void TakeFocus(bool reverse) OVERRIDE {}
134  virtual void UpdatePreferredSize(const gfx::Size& pref_size) {}
135
136  GURL drag_url() {
137    return drag_url_;
138  }
139
140  GURL html_base_url() {
141    return html_base_url_;
142  }
143
144 private:
145  GURL drag_url_;
146  GURL html_base_url_;
147};
148
149TEST_F(RenderViewHostTest, StartDragging) {
150  TestWebContents* web_contents = contents();
151  MockDraggingRenderViewHostDelegateView delegate_view;
152  web_contents->set_delegate_view(&delegate_view);
153
154  DropData drop_data;
155  GURL file_url = GURL("file:///home/user/secrets.txt");
156  drop_data.url = file_url;
157  drop_data.html_base_url = file_url;
158  test_rvh()->TestOnStartDragging(drop_data);
159  EXPECT_EQ(GURL(url::kAboutBlankURL), delegate_view.drag_url());
160  EXPECT_EQ(GURL(url::kAboutBlankURL), delegate_view.html_base_url());
161
162  GURL http_url = GURL("http://www.domain.com/index.html");
163  drop_data.url = http_url;
164  drop_data.html_base_url = http_url;
165  test_rvh()->TestOnStartDragging(drop_data);
166  EXPECT_EQ(http_url, delegate_view.drag_url());
167  EXPECT_EQ(http_url, delegate_view.html_base_url());
168
169  GURL https_url = GURL("https://www.domain.com/index.html");
170  drop_data.url = https_url;
171  drop_data.html_base_url = https_url;
172  test_rvh()->TestOnStartDragging(drop_data);
173  EXPECT_EQ(https_url, delegate_view.drag_url());
174  EXPECT_EQ(https_url, delegate_view.html_base_url());
175
176  GURL javascript_url = GURL("javascript:alert('I am a bookmarklet')");
177  drop_data.url = javascript_url;
178  drop_data.html_base_url = http_url;
179  test_rvh()->TestOnStartDragging(drop_data);
180  EXPECT_EQ(javascript_url, delegate_view.drag_url());
181  EXPECT_EQ(http_url, delegate_view.html_base_url());
182}
183
184TEST_F(RenderViewHostTest, DragEnteredFileURLsStillBlocked) {
185  DropData dropped_data;
186  gfx::Point client_point;
187  gfx::Point screen_point;
188  // We use "//foo/bar" path (rather than "/foo/bar") since dragged paths are
189  // expected to be absolute on any platforms.
190  base::FilePath highlighted_file_path(FILE_PATH_LITERAL("//tmp/foo.html"));
191  base::FilePath dragged_file_path(FILE_PATH_LITERAL("//tmp/image.jpg"));
192  base::FilePath sensitive_file_path(FILE_PATH_LITERAL("//etc/passwd"));
193  GURL highlighted_file_url = net::FilePathToFileURL(highlighted_file_path);
194  GURL dragged_file_url = net::FilePathToFileURL(dragged_file_path);
195  GURL sensitive_file_url = net::FilePathToFileURL(sensitive_file_path);
196  dropped_data.url = highlighted_file_url;
197  dropped_data.filenames.push_back(
198      ui::FileInfo(dragged_file_path, base::FilePath()));
199
200  rvh()->DragTargetDragEnter(dropped_data, client_point, screen_point,
201                              blink::WebDragOperationNone, 0);
202
203  int id = process()->GetID();
204  ChildProcessSecurityPolicyImpl* policy =
205      ChildProcessSecurityPolicyImpl::GetInstance();
206
207  EXPECT_FALSE(policy->CanRequestURL(id, highlighted_file_url));
208  EXPECT_FALSE(policy->CanReadFile(id, highlighted_file_path));
209  EXPECT_TRUE(policy->CanRequestURL(id, dragged_file_url));
210  EXPECT_TRUE(policy->CanReadFile(id, dragged_file_path));
211  EXPECT_FALSE(policy->CanRequestURL(id, sensitive_file_url));
212  EXPECT_FALSE(policy->CanReadFile(id, sensitive_file_path));
213}
214
215TEST_F(RenderViewHostTest, MessageWithBadHistoryItemFiles) {
216  base::FilePath file_path;
217  EXPECT_TRUE(PathService::Get(base::DIR_TEMP, &file_path));
218  file_path = file_path.AppendASCII("foo");
219  EXPECT_EQ(0, process()->bad_msg_count());
220  test_rvh()->TestOnUpdateStateWithFile(-1, file_path);
221  EXPECT_EQ(1, process()->bad_msg_count());
222
223  ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
224      process()->GetID(), file_path);
225  test_rvh()->TestOnUpdateStateWithFile(-1, file_path);
226  EXPECT_EQ(1, process()->bad_msg_count());
227}
228
229TEST_F(RenderViewHostTest, NavigationWithBadHistoryItemFiles) {
230  GURL url("http://www.google.com");
231  base::FilePath file_path;
232  EXPECT_TRUE(PathService::Get(base::DIR_TEMP, &file_path));
233  file_path = file_path.AppendASCII("bar");
234  EXPECT_EQ(0, process()->bad_msg_count());
235  test_rvh()->SendNavigateWithFile(1, url, file_path);
236  EXPECT_EQ(1, process()->bad_msg_count());
237
238  ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
239      process()->GetID(), file_path);
240  test_rvh()->SendNavigateWithFile(process()->GetID(), url, file_path);
241  EXPECT_EQ(1, process()->bad_msg_count());
242}
243
244TEST_F(RenderViewHostTest, RoutingIdSane) {
245  RenderFrameHostImpl* root_rfh =
246      contents()->GetFrameTree()->root()->current_frame_host();
247  EXPECT_EQ(test_rvh()->GetProcess(), root_rfh->GetProcess());
248  EXPECT_NE(test_rvh()->GetRoutingID(), root_rfh->routing_id());
249}
250
251class TestSaveImageFromDataURL : public RenderMessageFilter {
252 public:
253  TestSaveImageFromDataURL(
254      BrowserContext* context)
255      : RenderMessageFilter(
256            0,
257            nullptr,
258            context,
259            context->GetRequestContext(),
260            nullptr,
261            nullptr,
262            nullptr,
263            nullptr) {
264    Reset();
265  }
266
267  void Reset() {
268    url_string_ = std::string();
269    is_downloaded_ = false;
270  }
271
272  std::string& UrlString() const {
273    return url_string_;
274  }
275
276  bool IsDownloaded() const {
277    return is_downloaded_;
278  }
279
280  void Test(const std::string& url) {
281    OnMessageReceived(ViewHostMsg_SaveImageFromDataURL(0, url));
282  }
283
284 protected:
285  virtual ~TestSaveImageFromDataURL() { }
286  virtual void DownloadUrl(int render_view_id,
287                           const GURL& url,
288                           const Referrer& referrer,
289                           const base::string16& suggested_name,
290                           const bool use_prompt) const OVERRIDE {
291    url_string_ = url.spec();
292    is_downloaded_ = true;
293  }
294
295 private:
296  mutable std::string url_string_;
297  mutable bool is_downloaded_;
298};
299
300TEST_F(RenderViewHostTest, SaveImageFromDataURL) {
301  scoped_refptr<TestSaveImageFromDataURL> tester(
302      new TestSaveImageFromDataURL(browser_context()));
303
304  tester->Reset();
305  tester->Test("http://non-data-url.com");
306  EXPECT_EQ(tester->UrlString(), "");
307  EXPECT_FALSE(tester->IsDownloaded());
308
309  const std::string data_url = "data:image/gif;base64,"
310      "R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=";
311
312  tester->Reset();
313  tester->Test(data_url);
314  EXPECT_EQ(tester->UrlString(), data_url);
315  EXPECT_TRUE(tester->IsDownloaded());
316}
317
318}  // namespace content
319