OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <stddef.h> | 5 #include <stddef.h> |
6 #include <utility> | 6 #include <utility> |
7 | 7 |
8 #include "base/base64.h" | 8 #include "base/base64.h" |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/json/json_reader.h" | 10 #include "base/json/json_reader.h" |
11 #include "base/json/json_writer.h" | 11 #include "base/json/json_writer.h" |
| 12 #include "base/memory/ptr_util.h" |
12 #include "base/run_loop.h" | 13 #include "base/run_loop.h" |
13 #include "base/values.h" | 14 #include "base/values.h" |
14 #include "build/build_config.h" | 15 #include "build/build_config.h" |
15 #include "content/public/browser/devtools_agent_host.h" | 16 #include "content/public/browser/devtools_agent_host.h" |
16 #include "content/public/browser/javascript_dialog_manager.h" | 17 #include "content/public/browser/javascript_dialog_manager.h" |
| 18 #include "content/public/browser/navigation_handle.h" |
| 19 #include "content/public/browser/render_frame_host.h" |
17 #include "content/public/browser/render_view_host.h" | 20 #include "content/public/browser/render_view_host.h" |
18 #include "content/public/browser/web_contents.h" | 21 #include "content/public/browser/web_contents.h" |
19 #include "content/public/common/url_constants.h" | 22 #include "content/public/common/url_constants.h" |
20 #include "content/public/test/browser_test_utils.h" | 23 #include "content/public/test/browser_test_utils.h" |
21 #include "content/public/test/content_browser_test.h" | 24 #include "content/public/test/content_browser_test.h" |
22 #include "content/public/test/content_browser_test_utils.h" | 25 #include "content/public/test/content_browser_test_utils.h" |
23 #include "content/public/test/test_navigation_observer.h" | 26 #include "content/public/test/test_navigation_observer.h" |
24 #include "content/shell/browser/shell.h" | 27 #include "content/shell/browser/shell.h" |
25 #include "net/dns/mock_host_resolver.h" | 28 #include "net/dns/mock_host_resolver.h" |
26 #include "net/test/embedded_test_server/embedded_test_server.h" | 29 #include "net/test/embedded_test_server/embedded_test_server.h" |
| 30 #include "testing/gmock/include/gmock/gmock.h" |
27 #include "third_party/skia/include/core/SkBitmap.h" | 31 #include "third_party/skia/include/core/SkBitmap.h" |
28 #include "ui/compositor/compositor_switches.h" | 32 #include "ui/compositor/compositor_switches.h" |
29 #include "ui/gfx/codec/png_codec.h" | 33 #include "ui/gfx/codec/png_codec.h" |
30 | 34 |
| 35 using testing::ElementsAre; |
| 36 |
31 namespace content { | 37 namespace content { |
32 | 38 |
33 namespace { | 39 namespace { |
34 | 40 |
35 const char kIdParam[] = "id"; | 41 const char kIdParam[] = "id"; |
36 const char kMethodParam[] = "method"; | 42 const char kMethodParam[] = "method"; |
37 const char kParamsParam[] = "params"; | 43 const char kParamsParam[] = "params"; |
38 | 44 |
39 class TestJavaScriptDialogManager : public JavaScriptDialogManager, | 45 class TestJavaScriptDialogManager : public JavaScriptDialogManager, |
40 public WebContentsDelegate { | 46 public WebContentsDelegate { |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
168 agent_host_->DetachClient(this); | 174 agent_host_->DetachClient(this); |
169 agent_host_ = nullptr; | 175 agent_host_ = nullptr; |
170 } | 176 } |
171 } | 177 } |
172 | 178 |
173 void WaitForNotification(const std::string& notification) { | 179 void WaitForNotification(const std::string& notification) { |
174 waiting_for_notification_ = notification; | 180 waiting_for_notification_ = notification; |
175 RunMessageLoop(); | 181 RunMessageLoop(); |
176 } | 182 } |
177 | 183 |
| 184 struct ExpectedNavigation { |
| 185 std::string url; |
| 186 bool is_in_main_frame; |
| 187 bool is_redirect; |
| 188 std::string navigation_response; |
| 189 }; |
| 190 |
| 191 std::string RemovePort(const GURL& url) { |
| 192 GURL::Replacements remove_port; |
| 193 remove_port.ClearPort(); |
| 194 return url.ReplaceComponents(remove_port).spec(); |
| 195 } |
| 196 |
| 197 // Waits for the expected navigations to occur in any order. If an expected |
| 198 // navigation occurs, Page.processNavigation is called with the specified |
| 199 // navigation_response to either allow it to proceed or to cancel it. |
| 200 void ProcessNavigationsAnyOrder( |
| 201 std::vector<ExpectedNavigation> expected_navigations) { |
| 202 while (!expected_navigations.empty()) { |
| 203 WaitForNotification("Page.navigationRequested"); |
| 204 ASSERT_TRUE(requested_notification_params_.get()); |
| 205 |
| 206 std::string url; |
| 207 ASSERT_TRUE(requested_notification_params_->GetString("url", &url)); |
| 208 |
| 209 // The url will typically have a random port which we want to remove. |
| 210 url = RemovePort(GURL(url)); |
| 211 |
| 212 int navigation_id; |
| 213 ASSERT_TRUE(requested_notification_params_->GetInteger("navigationId", |
| 214 &navigation_id)); |
| 215 bool is_in_main_frame; |
| 216 ASSERT_TRUE(requested_notification_params_->GetBoolean( |
| 217 "isInMainFrame", &is_in_main_frame)); |
| 218 bool is_redirect; |
| 219 ASSERT_TRUE(requested_notification_params_->GetBoolean("isRedirect", |
| 220 &is_redirect)); |
| 221 |
| 222 bool navigation_was_expected; |
| 223 for (auto it = expected_navigations.begin(); |
| 224 it != expected_navigations.end(); it++) { |
| 225 if (url != it->url || is_in_main_frame != it->is_in_main_frame || |
| 226 is_redirect != it->is_redirect) { |
| 227 continue; |
| 228 } |
| 229 |
| 230 std::unique_ptr<base::DictionaryValue> params( |
| 231 new base::DictionaryValue()); |
| 232 params->SetString("response", it->navigation_response); |
| 233 params->SetInteger("navigationId", navigation_id); |
| 234 SendCommand("Page.processNavigation", std::move(params), false); |
| 235 |
| 236 navigation_was_expected = true; |
| 237 expected_navigations.erase(it); |
| 238 break; |
| 239 } |
| 240 EXPECT_TRUE(navigation_was_expected) |
| 241 << "url = " << url << "is_in_main_frame = " << is_in_main_frame |
| 242 << "is_redirect = " << is_redirect; |
| 243 } |
| 244 } |
| 245 |
| 246 std::vector<std::string> GetAllFrameUrls() { |
| 247 std::vector<std::string> urls; |
| 248 for (RenderFrameHost* render_frame_host : |
| 249 shell()->web_contents()->GetAllFrames()) { |
| 250 urls.push_back(RemovePort(render_frame_host->GetLastCommittedURL())); |
| 251 } |
| 252 return urls; |
| 253 } |
| 254 |
178 std::unique_ptr<base::DictionaryValue> result_; | 255 std::unique_ptr<base::DictionaryValue> result_; |
179 scoped_refptr<DevToolsAgentHost> agent_host_; | 256 scoped_refptr<DevToolsAgentHost> agent_host_; |
180 int last_sent_id_; | 257 int last_sent_id_; |
181 std::vector<int> result_ids_; | 258 std::vector<int> result_ids_; |
182 std::vector<std::string> notifications_; | 259 std::vector<std::string> notifications_; |
| 260 std::unique_ptr<base::DictionaryValue> requested_notification_params_; |
183 | 261 |
184 private: | 262 private: |
185 void DispatchProtocolMessage(DevToolsAgentHost* agent_host, | 263 void DispatchProtocolMessage(DevToolsAgentHost* agent_host, |
186 const std::string& message) override { | 264 const std::string& message) override { |
187 std::unique_ptr<base::DictionaryValue> root( | 265 std::unique_ptr<base::DictionaryValue> root( |
188 static_cast<base::DictionaryValue*>( | 266 static_cast<base::DictionaryValue*>( |
189 base::JSONReader::Read(message).release())); | 267 base::JSONReader::Read(message).release())); |
190 int id; | 268 int id; |
191 if (root->GetInteger("id", &id)) { | 269 if (root->GetInteger("id", &id)) { |
192 result_ids_.push_back(id); | 270 result_ids_.push_back(id); |
193 base::DictionaryValue* result; | 271 base::DictionaryValue* result; |
194 ASSERT_TRUE(root->GetDictionary("result", &result)); | 272 ASSERT_TRUE(root->GetDictionary("result", &result)); |
195 result_.reset(result->DeepCopy()); | 273 result_.reset(result->DeepCopy()); |
196 in_dispatch_ = false; | 274 in_dispatch_ = false; |
197 if (id && id == waiting_for_command_result_id_) { | 275 if (id && id == waiting_for_command_result_id_) { |
198 waiting_for_command_result_id_ = 0; | 276 waiting_for_command_result_id_ = 0; |
199 base::MessageLoop::current()->QuitNow(); | 277 base::MessageLoop::current()->QuitNow(); |
200 } | 278 } |
201 } else { | 279 } else { |
202 std::string notification; | 280 std::string notification; |
203 EXPECT_TRUE(root->GetString("method", ¬ification)); | 281 EXPECT_TRUE(root->GetString("method", ¬ification)); |
204 notifications_.push_back(notification); | 282 notifications_.push_back(notification); |
205 if (waiting_for_notification_ == notification) { | 283 if (waiting_for_notification_ == notification) { |
| 284 base::DictionaryValue* params; |
| 285 if (root->GetDictionary("params", ¶ms)) { |
| 286 requested_notification_params_ = params->CreateDeepCopy(); |
| 287 } else { |
| 288 requested_notification_params_.reset(); |
| 289 } |
206 waiting_for_notification_ = std::string(); | 290 waiting_for_notification_ = std::string(); |
207 base::MessageLoop::current()->QuitNow(); | 291 base::MessageLoop::current()->QuitNow(); |
208 } | 292 } |
209 } | 293 } |
210 } | 294 } |
211 | 295 |
212 void AgentHostClosed(DevToolsAgentHost* agent_host, bool replaced) override { | 296 void AgentHostClosed(DevToolsAgentHost* agent_host, bool replaced) override { |
213 EXPECT_TRUE(false); | 297 EXPECT_TRUE(false); |
214 } | 298 } |
215 | 299 |
(...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
590 IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, BrowserCreateTarget) { | 674 IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, BrowserCreateTarget) { |
591 NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); | 675 NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); |
592 Attach(); | 676 Attach(); |
593 EXPECT_EQ(1u, shell()->windows().size()); | 677 EXPECT_EQ(1u, shell()->windows().size()); |
594 std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue()); | 678 std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue()); |
595 params->SetString("url", "about:blank"); | 679 params->SetString("url", "about:blank"); |
596 SendCommand("Browser.createTarget", std::move(params), true); | 680 SendCommand("Browser.createTarget", std::move(params), true); |
597 EXPECT_EQ(2u, shell()->windows().size()); | 681 EXPECT_EQ(2u, shell()->windows().size()); |
598 } | 682 } |
599 | 683 |
| 684 namespace { |
| 685 class NavigationFinishedObserver : public content::WebContentsObserver { |
| 686 public: |
| 687 explicit NavigationFinishedObserver(WebContents* web_contents) |
| 688 : WebContentsObserver(web_contents), |
| 689 num_finished_(0), |
| 690 num_to_wait_for_(0) {} |
| 691 |
| 692 ~NavigationFinishedObserver() override {} |
| 693 |
| 694 void DidFinishNavigation( |
| 695 content::NavigationHandle* navigation_handle) override { |
| 696 if (navigation_handle->WasServerRedirect()) |
| 697 return; |
| 698 |
| 699 num_finished_++; |
| 700 if (num_finished_ >= num_to_wait_for_ && num_to_wait_for_ != 0) { |
| 701 base::MessageLoop::current()->QuitNow(); |
| 702 } |
| 703 } |
| 704 |
| 705 void WaitForNavigationsToFinish(int num_to_wait_for) { |
| 706 if (num_finished_ < num_to_wait_for) { |
| 707 num_to_wait_for_ = num_to_wait_for; |
| 708 RunMessageLoop(); |
| 709 } |
| 710 num_to_wait_for_ = 0; |
| 711 } |
| 712 |
| 713 private: |
| 714 int num_finished_; |
| 715 int num_to_wait_for_; |
| 716 }; |
| 717 } // namespace |
| 718 |
| 719 IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, ControlNavigationsMainFrame) { |
| 720 ASSERT_TRUE(embedded_test_server()->Start()); |
| 721 |
| 722 // Navigate to about:blank first so we can make sure there is a target page we |
| 723 // can attach to, and have Page.setControlNavigations complete before we start |
| 724 // the navigations we're interested in. |
| 725 NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); |
| 726 Attach(); |
| 727 |
| 728 std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue()); |
| 729 params->SetBoolean("enabled", true); |
| 730 SendCommand("Page.setControlNavigations", std::move(params), true); |
| 731 |
| 732 NavigationFinishedObserver navigation_finished_observer( |
| 733 shell()->web_contents()); |
| 734 |
| 735 GURL test_url = embedded_test_server()->GetURL( |
| 736 "/devtools/control_navigations/meta_tag.html"); |
| 737 shell()->LoadURL(test_url); |
| 738 |
| 739 std::vector<ExpectedNavigation> expected_navigations = { |
| 740 {"http://127.0.0.1/devtools/control_navigations/meta_tag.html", |
| 741 true /* expected_is_in_main_frame */, false /* expected_is_redirect */, |
| 742 "Proceed"}, |
| 743 {"http://127.0.0.1/devtools/navigation.html", |
| 744 true /* expected_is_in_main_frame */, false /* expected_is_redirect */, |
| 745 "Cancel"}}; |
| 746 |
| 747 ProcessNavigationsAnyOrder(std::move(expected_navigations)); |
| 748 |
| 749 // Wait for the initial navigation and the cancelled meta refresh navigation |
| 750 // to finish. |
| 751 navigation_finished_observer.WaitForNavigationsToFinish(2); |
| 752 |
| 753 // Check main frame has the expected url. |
| 754 EXPECT_EQ( |
| 755 "http://127.0.0.1/devtools/control_navigations/meta_tag.html", |
| 756 RemovePort( |
| 757 shell()->web_contents()->GetMainFrame()->GetLastCommittedURL())); |
| 758 } |
| 759 |
| 760 class IsolatedDevToolsProtocolTest : public DevToolsProtocolTest { |
| 761 public: |
| 762 ~IsolatedDevToolsProtocolTest() override {} |
| 763 |
| 764 void SetUpCommandLine(base::CommandLine* command_line) override { |
| 765 IsolateAllSitesForTesting(command_line); |
| 766 } |
| 767 }; |
| 768 |
| 769 IN_PROC_BROWSER_TEST_F(IsolatedDevToolsProtocolTest, |
| 770 ControlNavigationsChildFrames) { |
| 771 host_resolver()->AddRule("*", "127.0.0.1"); |
| 772 ASSERT_TRUE(embedded_test_server()->Start()); |
| 773 content::SetupCrossSiteRedirector(embedded_test_server()); |
| 774 |
| 775 // Navigate to about:blank first so we can make sure there is a target page we |
| 776 // can attach to, and have Page.setControlNavigations complete before we start |
| 777 // the navigations we're interested in. |
| 778 NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); |
| 779 Attach(); |
| 780 |
| 781 std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue()); |
| 782 params->SetBoolean("enabled", true); |
| 783 SendCommand("Page.setControlNavigations", std::move(params), true); |
| 784 |
| 785 NavigationFinishedObserver navigation_finished_observer( |
| 786 shell()->web_contents()); |
| 787 |
| 788 GURL test_url = embedded_test_server()->GetURL( |
| 789 "/devtools/control_navigations/iframe_navigation.html"); |
| 790 shell()->LoadURL(test_url); |
| 791 |
| 792 // Allow main frame navigation, and all iframe navigations to http://a.com |
| 793 // Allow initial iframe navigation to http://b.com but dissallow it to |
| 794 // navigate to /devtools/navigation.html. |
| 795 std::vector<ExpectedNavigation> expected_navigations = { |
| 796 {"http://127.0.0.1/devtools/control_navigations/" |
| 797 "iframe_navigation.html", |
| 798 /* expected_is_in_main_frame */ true, |
| 799 /* expected_is_redirect */ false, "Proceed"}, |
| 800 {"http://127.0.0.1/cross-site/a.com/devtools/control_navigations/" |
| 801 "meta_tag.html", |
| 802 /* expected_is_in_main_frame */ false, |
| 803 /* expected_is_redirect */ false, "Proceed"}, |
| 804 {"http://127.0.0.1/cross-site/b.com/devtools/control_navigations/" |
| 805 "meta_tag.html", |
| 806 /* expected_is_in_main_frame */ false, |
| 807 /* expected_is_redirect */ false, "Proceed"}, |
| 808 {"http://a.com/devtools/control_navigations/meta_tag.html", |
| 809 /* expected_is_in_main_frame */ false, |
| 810 /* expected_is_redirect */ true, "Proceed"}, |
| 811 {"http://b.com/devtools/control_navigations/meta_tag.html", |
| 812 /* expected_is_in_main_frame */ false, |
| 813 /* expected_is_redirect */ true, "Proceed"}, |
| 814 {"http://a.com/devtools/navigation.html", |
| 815 /* expected_is_in_main_frame */ false, |
| 816 /* expected_is_redirect */ false, "Proceed"}, |
| 817 {"http://b.com/devtools/navigation.html", |
| 818 /* expected_is_in_main_frame */ false, |
| 819 /* expected_is_redirect */ false, "Cancel"}}; |
| 820 |
| 821 ProcessNavigationsAnyOrder(std::move(expected_navigations)); |
| 822 |
| 823 // Wait for each frame's navigation to finish, ignoring redirects. |
| 824 navigation_finished_observer.WaitForNavigationsToFinish(3); |
| 825 |
| 826 // Make sure each frame has the expected url. |
| 827 EXPECT_THAT( |
| 828 GetAllFrameUrls(), |
| 829 ElementsAre("http://127.0.0.1/devtools/control_navigations/" |
| 830 "iframe_navigation.html", |
| 831 "http://a.com/devtools/navigation.html", |
| 832 "http://b.com/devtools/control_navigations/meta_tag.html")); |
| 833 } |
| 834 |
600 } // namespace content | 835 } // namespace content |
OLD | NEW |