| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/public/test/browser_test_utils.h" |
| |
| #include <stddef.h> |
| |
| #include <set> |
| #include <tuple> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/command_line.h" |
| #include "base/guid.h" |
| #include "base/json/json_reader.h" |
| #include "base/macros.h" |
| #include "base/process/kill.h" |
| #include "base/run_loop.h" |
| #include "base/stl_util.h" |
| #include "base/strings/pattern.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/test/test_timeouts.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/values.h" |
| #include "build/build_config.h" |
| #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" |
| #include "components/viz/service/surfaces/surface.h" |
| #include "components/viz/service/surfaces/surface_manager.h" |
| #include "content/browser/accessibility/browser_accessibility.h" |
| #include "content/browser/accessibility/browser_accessibility_manager.h" |
| #include "content/browser/browser_plugin/browser_plugin_guest.h" |
| #include "content/browser/browser_plugin/browser_plugin_message_filter.h" |
| #include "content/browser/compositor/surface_utils.h" |
| #include "content/browser/frame_host/cross_process_frame_connector.h" |
| #include "content/browser/frame_host/frame_tree_node.h" |
| #include "content/browser/frame_host/interstitial_page_impl.h" |
| #include "content/browser/frame_host/render_frame_host_impl.h" |
| #include "content/browser/renderer_host/render_process_host_impl.h" |
| #include "content/browser/renderer_host/render_widget_host_impl.h" |
| #include "content/browser/renderer_host/render_widget_host_input_event_router.h" |
| #include "content/browser/renderer_host/render_widget_host_view_child_frame.h" |
| #include "content/browser/service_manager/service_manager_context.h" |
| #include "content/browser/web_contents/web_contents_impl.h" |
| #include "content/browser/web_contents/web_contents_view.h" |
| #include "content/common/browser_plugin/browser_plugin_messages.h" |
| #include "content/common/fileapi/file_system_messages.h" |
| #include "content/common/fileapi/webblob_messages.h" |
| #include "content/common/frame_messages.h" |
| #include "content/common/frame_visual_properties.h" |
| #include "content/common/input/synthetic_web_input_event_builders.h" |
| #include "content/common/input_messages.h" |
| #include "content/common/view_messages.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_plugin_guest_manager.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/child_process_termination_info.h" |
| #include "content/public/browser/histogram_fetcher.h" |
| #include "content/public/browser/interstitial_page.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/navigation_handle.h" |
| #include "content/public/browser/navigation_throttle.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/notification_types.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/common/service_names.mojom.h" |
| #include "content/public/common/use_zoom_for_dsf_policy.h" |
| #include "content/public/test/simple_url_loader_test_helper.h" |
| #include "content/public/test/test_fileapi_operation_waiter.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "content/public/test/test_utils.h" |
| #include "content/test/accessibility_browser_test_utils.h" |
| #include "content/test/did_commit_provisional_load_interceptor.h" |
| #include "ipc/ipc_security_test_util.h" |
| #include "net/base/filename_util.h" |
| #include "net/base/io_buffer.h" |
| #include "net/cookies/canonical_cookie.h" |
| #include "net/filter/gzip_header.h" |
| #include "net/filter/gzip_source_stream.h" |
| #include "net/filter/mock_source_stream.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "net/test/python_utils.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "services/network/public/cpp/features.h" |
| #include "services/network/public/cpp/simple_url_loader.h" |
| #include "services/network/public/mojom/cookie_manager.mojom.h" |
| #include "services/network/public/mojom/network_service.mojom.h" |
| #include "services/network/public/mojom/network_service_test.mojom.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "storage/browser/fileapi/file_system_context.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/clipboard/clipboard.h" |
| #include "ui/base/clipboard/scoped_clipboard_writer.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/base/test/test_clipboard.h" |
| #include "ui/compositor/test/draw_waiter_for_test.h" |
| #include "ui/events/base_event_utils.h" |
| #include "ui/events/gesture_detection/gesture_configuration.h" |
| #include "ui/events/keycodes/dom/dom_code.h" |
| #include "ui/events/keycodes/dom/keycode_converter.h" |
| #include "ui/latency/latency_info.h" |
| #include "ui/resources/grit/webui_resources.h" |
| |
| #if defined(USE_AURA) |
| #include "content/browser/renderer_host/overscroll_controller.h" |
| #include "content/browser/renderer_host/render_widget_host_view_aura.h" |
| #include "ui/aura/test/window_event_dispatcher_test_api.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_event_dispatcher.h" |
| #include "ui/aura/window_tree_host.h" |
| #include "ui/events/event.h" |
| #endif // USE_AURA |
| |
| namespace content { |
| namespace { |
| |
| class InterstitialObserver : public content::WebContentsObserver { |
| public: |
| InterstitialObserver(content::WebContents* web_contents, |
| base::OnceClosure attach_callback, |
| base::OnceClosure detach_callback) |
| : WebContentsObserver(web_contents), |
| attach_callback_(std::move(attach_callback)), |
| detach_callback_(std::move(detach_callback)) {} |
| ~InterstitialObserver() override {} |
| |
| // WebContentsObserver methods: |
| void DidAttachInterstitialPage() override { |
| std::move(attach_callback_).Run(); |
| } |
| void DidDetachInterstitialPage() override { |
| std::move(detach_callback_).Run(); |
| } |
| |
| private: |
| base::OnceClosure attach_callback_; |
| base::OnceClosure detach_callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InterstitialObserver); |
| }; |
| |
| // Specifying a prototype so that we can add the WARN_UNUSED_RESULT attribute. |
| bool ExecuteScriptHelper(RenderFrameHost* render_frame_host, |
| const std::string& original_script, |
| bool user_gesture, |
| std::unique_ptr<base::Value>* result) |
| WARN_UNUSED_RESULT; |
| |
| // Executes the passed |script| in the frame specified by |render_frame_host|. |
| // If |result| is not NULL, stores the value that the evaluation of the script |
| // in |result|. Returns true on success. |
| bool ExecuteScriptHelper(RenderFrameHost* render_frame_host, |
| const std::string& script, |
| bool user_gesture, |
| std::unique_ptr<base::Value>* result) { |
| // TODO(lukasza): Only get messages from the specific |render_frame_host|. |
| DOMMessageQueue dom_message_queue( |
| WebContents::FromRenderFrameHost(render_frame_host)); |
| if (user_gesture) { |
| render_frame_host->ExecuteJavaScriptWithUserGestureForTests( |
| base::UTF8ToUTF16(script)); |
| } else { |
| render_frame_host->ExecuteJavaScriptForTests(base::UTF8ToUTF16(script)); |
| } |
| std::string json; |
| if (!dom_message_queue.WaitForMessage(&json)) { |
| DLOG(ERROR) << "Cannot communicate with DOMMessageQueue."; |
| return false; |
| } |
| |
| // Nothing more to do for callers that ignore the returned JS value. |
| if (!result) |
| return true; |
| |
| base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS); |
| *result = reader.ReadToValue(json); |
| if (!*result) { |
| DLOG(ERROR) << reader.GetErrorMessage(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ExecuteScriptWithUserGestureControl(RenderFrameHost* frame, |
| const std::string& script, |
| bool user_gesture) { |
| // TODO(lukasza): ExecuteScript should just call |
| // ExecuteJavaScriptWithUserGestureForTests and avoid modifying the original |
| // script (and at that point we should merge it with and remove |
| // ExecuteScriptAsync). This is difficult to change, because many tests |
| // depend on the message loop pumping done by ExecuteScriptHelper below (this |
| // is fragile - these tests should wait on a more specific thing instead). |
| |
| std::string expected_response = "ExecuteScript-" + base::GenerateGUID(); |
| std::string new_script = base::StringPrintf( |
| R"( %s; // Original script. |
| window.domAutomationController.send('%s'); )", |
| script.c_str(), expected_response.c_str()); |
| |
| std::unique_ptr<base::Value> value; |
| if (!ExecuteScriptHelper(frame, new_script, user_gesture, &value) || |
| !value.get()) { |
| return false; |
| } |
| |
| DCHECK_EQ(base::Value::Type::STRING, value->type()); |
| std::string actual_response; |
| if (value->GetAsString(&actual_response)) |
| DCHECK_EQ(expected_response, actual_response); |
| |
| return true; |
| } |
| |
| // Specifying a prototype so that we can add the WARN_UNUSED_RESULT attribute. |
| bool ExecuteScriptInIsolatedWorldHelper(RenderFrameHost* render_frame_host, |
| const int world_id, |
| const std::string& original_script, |
| std::unique_ptr<base::Value>* result) |
| WARN_UNUSED_RESULT; |
| |
| bool ExecuteScriptInIsolatedWorldHelper(RenderFrameHost* render_frame_host, |
| const int world_id, |
| const std::string& script, |
| std::unique_ptr<base::Value>* result) { |
| // TODO(lukasza): Only get messages from the specific |render_frame_host|. |
| DOMMessageQueue dom_message_queue( |
| WebContents::FromRenderFrameHost(render_frame_host)); |
| render_frame_host->ExecuteJavaScriptInIsolatedWorld( |
| base::UTF8ToUTF16(script), |
| content::RenderFrameHost::JavaScriptResultCallback(), world_id); |
| std::string json; |
| if (!dom_message_queue.WaitForMessage(&json)) { |
| DLOG(ERROR) << "Cannot communicate with DOMMessageQueue."; |
| return false; |
| } |
| |
| // Nothing more to do for callers that ignore the returned JS value. |
| if (!result) |
| return true; |
| |
| base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS); |
| *result = reader.ReadToValue(json); |
| if (!*result) { |
| DLOG(ERROR) << reader.GetErrorMessage(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void BuildSimpleWebKeyEvent(blink::WebInputEvent::Type type, |
| ui::DomKey key, |
| ui::DomCode code, |
| ui::KeyboardCode key_code, |
| NativeWebKeyboardEvent* event) { |
| event->dom_key = key; |
| event->dom_code = static_cast<int>(code); |
| event->native_key_code = ui::KeycodeConverter::DomCodeToNativeKeycode(code); |
| event->windows_key_code = key_code; |
| event->is_system_key = false; |
| event->skip_in_browser = true; |
| |
| if (type == blink::WebInputEvent::kChar || |
| type == blink::WebInputEvent::kRawKeyDown) { |
| // |key| is the only parameter that contains information about the case of |
| // the character. Use it to be able to generate lower case input. |
| if (key.IsCharacter()) { |
| event->text[0] = key.ToCharacter(); |
| event->unmodified_text[0] = key.ToCharacter(); |
| } else { |
| event->text[0] = key_code; |
| event->unmodified_text[0] = key_code; |
| } |
| } |
| } |
| |
| void InjectRawKeyEvent(WebContents* web_contents, |
| blink::WebInputEvent::Type type, |
| ui::DomKey key, |
| ui::DomCode code, |
| ui::KeyboardCode key_code, |
| int modifiers) { |
| NativeWebKeyboardEvent event(type, modifiers, base::TimeTicks::Now()); |
| BuildSimpleWebKeyEvent(type, key, code, key_code, &event); |
| WebContentsImpl* web_contents_impl = |
| static_cast<WebContentsImpl*>(web_contents); |
| RenderWidgetHostImpl* main_frame_rwh = |
| web_contents_impl->GetMainFrame()->GetRenderWidgetHost(); |
| web_contents_impl->GetFocusedRenderWidgetHost(main_frame_rwh) |
| ->ForwardKeyboardEvent(event); |
| } |
| |
| int SimulateModifierKeysDown(WebContents* web_contents, |
| bool control, |
| bool shift, |
| bool alt, |
| bool command) { |
| int modifiers = 0; |
| |
| // The order of these key down events shouldn't matter for our simulation. |
| // For our simulation we can use either the left keys or the right keys. |
| if (control) { |
| modifiers |= blink::WebInputEvent::kControlKey; |
| InjectRawKeyEvent(web_contents, blink::WebInputEvent::kRawKeyDown, |
| ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT, |
| ui::VKEY_CONTROL, modifiers); |
| } |
| if (shift) { |
| modifiers |= blink::WebInputEvent::kShiftKey; |
| InjectRawKeyEvent(web_contents, blink::WebInputEvent::kRawKeyDown, |
| ui::DomKey::SHIFT, ui::DomCode::SHIFT_LEFT, |
| ui::VKEY_SHIFT, modifiers); |
| } |
| if (alt) { |
| modifiers |= blink::WebInputEvent::kAltKey; |
| InjectRawKeyEvent(web_contents, blink::WebInputEvent::kRawKeyDown, |
| ui::DomKey::ALT, ui::DomCode::ALT_LEFT, ui::VKEY_MENU, |
| modifiers); |
| } |
| if (command) { |
| modifiers |= blink::WebInputEvent::kMetaKey; |
| InjectRawKeyEvent(web_contents, blink::WebInputEvent::kRawKeyDown, |
| ui::DomKey::META, ui::DomCode::META_LEFT, |
| ui::VKEY_COMMAND, modifiers); |
| } |
| return modifiers; |
| } |
| |
| int SimulateModifierKeysUp(WebContents* web_contents, |
| bool control, |
| bool shift, |
| bool alt, |
| bool command, |
| int modifiers) { |
| // The order of these key releases shouldn't matter for our simulation. |
| if (control) { |
| modifiers &= ~blink::WebInputEvent::kControlKey; |
| InjectRawKeyEvent(web_contents, blink::WebInputEvent::kKeyUp, |
| ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT, |
| ui::VKEY_CONTROL, modifiers); |
| } |
| |
| if (shift) { |
| modifiers &= ~blink::WebInputEvent::kShiftKey; |
| InjectRawKeyEvent(web_contents, blink::WebInputEvent::kKeyUp, |
| ui::DomKey::SHIFT, ui::DomCode::SHIFT_LEFT, |
| ui::VKEY_SHIFT, modifiers); |
| } |
| |
| if (alt) { |
| modifiers &= ~blink::WebInputEvent::kAltKey; |
| InjectRawKeyEvent(web_contents, blink::WebInputEvent::kKeyUp, |
| ui::DomKey::ALT, ui::DomCode::ALT_LEFT, ui::VKEY_MENU, |
| modifiers); |
| } |
| |
| if (command) { |
| modifiers &= ~blink::WebInputEvent::kMetaKey; |
| InjectRawKeyEvent(web_contents, blink::WebInputEvent::kKeyUp, |
| ui::DomKey::META, ui::DomCode::META_LEFT, |
| ui::VKEY_COMMAND, modifiers); |
| } |
| return modifiers; |
| } |
| |
| void SimulateKeyEvent(WebContents* web_contents, |
| ui::DomKey key, |
| ui::DomCode code, |
| ui::KeyboardCode key_code, |
| bool send_char, |
| int modifiers) { |
| InjectRawKeyEvent(web_contents, blink::WebInputEvent::kRawKeyDown, key, code, |
| key_code, modifiers); |
| if (send_char) { |
| InjectRawKeyEvent(web_contents, blink::WebInputEvent::kChar, key, code, |
| key_code, modifiers); |
| } |
| InjectRawKeyEvent(web_contents, blink::WebInputEvent::kKeyUp, key, code, |
| key_code, modifiers); |
| } |
| |
| void SimulateKeyPressImpl(WebContents* web_contents, |
| ui::DomKey key, |
| ui::DomCode code, |
| ui::KeyboardCode key_code, |
| bool control, |
| bool shift, |
| bool alt, |
| bool command, |
| bool send_char) { |
| int modifiers = |
| SimulateModifierKeysDown(web_contents, control, shift, alt, command); |
| SimulateKeyEvent(web_contents, key, code, key_code, send_char, modifiers); |
| modifiers = SimulateModifierKeysUp(web_contents, control, shift, alt, command, |
| modifiers); |
| ASSERT_EQ(modifiers, 0); |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> |
| CrossSiteRedirectResponseHandler(const net::EmbeddedTestServer* test_server, |
| const net::test_server::HttpRequest& request) { |
| net::HttpStatusCode http_status_code; |
| |
| // Inspect the prefix and extract the remainder of the url into |params|. |
| size_t length_of_chosen_prefix; |
| std::string prefix_302("/cross-site/"); |
| std::string prefix_307("/cross-site-307/"); |
| if (base::StartsWith(request.relative_url, prefix_302, |
| base::CompareCase::SENSITIVE)) { |
| http_status_code = net::HTTP_MOVED_PERMANENTLY; |
| length_of_chosen_prefix = prefix_302.length(); |
| } else if (base::StartsWith(request.relative_url, prefix_307, |
| base::CompareCase::SENSITIVE)) { |
| http_status_code = net::HTTP_TEMPORARY_REDIRECT; |
| length_of_chosen_prefix = prefix_307.length(); |
| } else { |
| // Unrecognized prefix - let somebody else handle this request. |
| return std::unique_ptr<net::test_server::HttpResponse>(); |
| } |
| std::string params = request.relative_url.substr(length_of_chosen_prefix); |
| |
| // A hostname to redirect to must be included in the URL, therefore at least |
| // one '/' character is expected. |
| size_t slash = params.find('/'); |
| if (slash == std::string::npos) |
| return std::unique_ptr<net::test_server::HttpResponse>(); |
| |
| // Replace the host of the URL with the one passed in the URL. |
| GURL::Replacements replace_host; |
| replace_host.SetHostStr(base::StringPiece(params).substr(0, slash)); |
| GURL redirect_server = |
| test_server->base_url().ReplaceComponents(replace_host); |
| |
| // Append the real part of the path to the new URL. |
| std::string path = params.substr(slash + 1); |
| GURL redirect_target(redirect_server.Resolve(path)); |
| DCHECK(redirect_target.is_valid()); |
| |
| std::unique_ptr<net::test_server::BasicHttpResponse> http_response( |
| new net::test_server::BasicHttpResponse); |
| http_response->set_code(http_status_code); |
| http_response->AddCustomHeader("Location", redirect_target.spec()); |
| return std::move(http_response); |
| } |
| |
| // Helper class used by the TestNavigationManager to pause navigations. |
| // Note: the throttle should be added to the *end* of the list of throttles, |
| // so all NavigationThrottles that should be attached observe the |
| // WillStartRequest callback. RegisterThrottleForTesting has this behavior. |
| class TestNavigationManagerThrottle : public NavigationThrottle { |
| public: |
| TestNavigationManagerThrottle(NavigationHandle* handle, |
| base::Closure on_will_start_request_closure, |
| base::Closure on_will_process_response_closure) |
| : NavigationThrottle(handle), |
| on_will_start_request_closure_(on_will_start_request_closure), |
| on_will_process_response_closure_(on_will_process_response_closure) {} |
| ~TestNavigationManagerThrottle() override {} |
| |
| const char* GetNameForLogging() override { |
| return "TestNavigationManagerThrottle"; |
| } |
| |
| private: |
| // NavigationThrottle: |
| NavigationThrottle::ThrottleCheckResult WillStartRequest() override { |
| BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| on_will_start_request_closure_); |
| return NavigationThrottle::DEFER; |
| } |
| |
| NavigationThrottle::ThrottleCheckResult WillProcessResponse() override { |
| BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| on_will_process_response_closure_); |
| return NavigationThrottle::DEFER; |
| } |
| |
| base::Closure on_will_start_request_closure_; |
| base::Closure on_will_process_response_closure_; |
| }; |
| |
| bool HasGzipHeader(const base::RefCountedMemory& maybe_gzipped) { |
| net::GZipHeader header; |
| net::GZipHeader::Status header_status = net::GZipHeader::INCOMPLETE_HEADER; |
| const char* header_end = nullptr; |
| while (header_status == net::GZipHeader::INCOMPLETE_HEADER) { |
| header_status = header.ReadMore(maybe_gzipped.front_as<char>(), |
| maybe_gzipped.size(), |
| &header_end); |
| } |
| return header_status == net::GZipHeader::COMPLETE_HEADER; |
| } |
| |
| void AppendGzippedResource(const base::RefCountedMemory& encoded, |
| std::string* to_append) { |
| std::unique_ptr<net::MockSourceStream> source_stream( |
| new net::MockSourceStream()); |
| source_stream->AddReadResult(encoded.front_as<char>(), encoded.size(), |
| net::OK, net::MockSourceStream::SYNC); |
| // Add an EOF. |
| source_stream->AddReadResult(encoded.front_as<char>() + encoded.size(), 0, |
| net::OK, net::MockSourceStream::SYNC); |
| std::unique_ptr<net::GzipSourceStream> filter = net::GzipSourceStream::Create( |
| std::move(source_stream), net::SourceStream::TYPE_GZIP); |
| scoped_refptr<net::IOBufferWithSize> dest_buffer = |
| new net::IOBufferWithSize(4096); |
| net::CompletionCallback callback; |
| while (true) { |
| int rv = filter->Read(dest_buffer.get(), dest_buffer->size(), callback); |
| ASSERT_LE(0, rv); |
| if (rv <= 0) |
| break; |
| to_append->append(dest_buffer->data(), rv); |
| } |
| } |
| |
| // Queries for video input devices on the current system using the getSources |
| // API. |
| // |
| // This does not guarantee that a getUserMedia with video will succeed, as the |
| // camera could be busy for instance. |
| // |
| // Returns has-video-input-device to the test if there is a webcam available, |
| // no-video-input-devices otherwise. |
| const char kHasVideoInputDeviceOnSystem[] = R"( |
| (function() { |
| navigator.mediaDevices.enumerateDevices() |
| .then(function(devices) { |
| if (devices.some((device) => device.kind == 'videoinput')) { |
| window.domAutomationController.send('has-video-input-device'); |
| } else { |
| window.domAutomationController.send('no-video-input-devices'); |
| } |
| }); |
| })() |
| )"; |
| |
| const char kHasVideoInputDevice[] = "has-video-input-device"; |
| |
| // Interceptor that replaces params.url with |new_url| and params.origin with |
| // |new_origin| for any commits to |target_url|. |
| class CommitOriginInterceptor : public DidCommitProvisionalLoadInterceptor { |
| public: |
| CommitOriginInterceptor(WebContents* web_contents, |
| const GURL& target_url, |
| const GURL& new_url, |
| const url::Origin& new_origin) |
| : DidCommitProvisionalLoadInterceptor(web_contents), |
| target_url_(target_url), |
| new_url_(new_url), |
| new_origin_(new_origin) {} |
| ~CommitOriginInterceptor() override = default; |
| |
| // WebContentsObserver: |
| void WebContentsDestroyed() override { delete this; } |
| |
| protected: |
| bool WillDispatchDidCommitProvisionalLoad( |
| RenderFrameHost* render_frame_host, |
| ::FrameHostMsg_DidCommitProvisionalLoad_Params* params, |
| service_manager::mojom::InterfaceProviderRequest* |
| interface_provider_request) override { |
| if (params->url == target_url_) { |
| params->url = new_url_; |
| params->origin = new_origin_; |
| } |
| return true; |
| } |
| |
| private: |
| GURL target_url_; |
| GURL new_url_; |
| url::Origin new_origin_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CommitOriginInterceptor); |
| }; |
| |
| } // namespace |
| |
| bool NavigateIframeToURL(WebContents* web_contents, |
| std::string iframe_id, |
| const GURL& url) { |
| std::string script = base::StringPrintf( |
| "setTimeout(\"" |
| "var iframes = document.getElementById('%s');iframes.src='%s';" |
| "\",0)", |
| iframe_id.c_str(), url.spec().c_str()); |
| TestNavigationObserver load_observer(web_contents); |
| bool result = ExecuteScript(web_contents, script); |
| load_observer.Wait(); |
| return result; |
| } |
| |
| GURL GetFileUrlWithQuery(const base::FilePath& path, |
| const std::string& query_string) { |
| GURL url = net::FilePathToFileURL(path); |
| if (!query_string.empty()) { |
| GURL::Replacements replacements; |
| replacements.SetQueryStr(query_string); |
| return url.ReplaceComponents(replacements); |
| } |
| return url; |
| } |
| |
| void WaitForLoadStopWithoutSuccessCheck(WebContents* web_contents) { |
| // In many cases, the load may have finished before we get here. Only wait if |
| // the tab still has a pending navigation. |
| if (web_contents->IsLoading()) { |
| WindowedNotificationObserver load_stop_observer( |
| NOTIFICATION_LOAD_STOP, |
| Source<NavigationController>(&web_contents->GetController())); |
| load_stop_observer.Wait(); |
| } |
| } |
| |
| bool WaitForLoadStop(WebContents* web_contents) { |
| WebContentsDestroyedObserver observer(web_contents); |
| WaitForLoadStopWithoutSuccessCheck(web_contents); |
| if (observer.IsDestroyed()) { |
| LOG(ERROR) << "WebContents was destroyed during waiting for load stop."; |
| return false; |
| } |
| return IsLastCommittedEntryOfPageType(web_contents, PAGE_TYPE_NORMAL); |
| } |
| |
| void PrepContentsForBeforeUnloadTest(WebContents* web_contents) { |
| for (auto* frame : web_contents->GetAllFrames()) { |
| // JavaScript onbeforeunload dialogs are ignored unless the frame received a |
| // user gesture. Make sure the frames have user gestures. |
| frame->ExecuteJavaScriptWithUserGestureForTests(base::string16()); |
| |
| // Disable the hang monitor, otherwise there will be a race between the |
| // beforeunload dialog and the beforeunload hang timer. |
| frame->DisableBeforeUnloadHangMonitorForTesting(); |
| } |
| } |
| |
| bool IsLastCommittedEntryOfPageType(WebContents* web_contents, |
| content::PageType page_type) { |
| NavigationEntry* last_entry = |
| web_contents->GetController().GetLastCommittedEntry(); |
| if (!last_entry) |
| return false; |
| return last_entry->GetPageType() == page_type; |
| } |
| |
| void OverrideLastCommittedOrigin(RenderFrameHost* render_frame_host, |
| const url::Origin& origin) { |
| static_cast<RenderFrameHostImpl*>(render_frame_host) |
| ->SetLastCommittedOriginForTesting(origin); |
| } |
| |
| void CrashTab(WebContents* web_contents) { |
| RenderProcessHost* rph = web_contents->GetMainFrame()->GetProcess(); |
| RenderProcessHostWatcher watcher( |
| rph, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| rph->Shutdown(0); |
| watcher.Wait(); |
| } |
| |
| void PwnCommitIPC(WebContents* web_contents, |
| const GURL& target_url, |
| const GURL& new_url, |
| const url::Origin& new_origin) { |
| // This will be cleaned up when |web_contents| is destroyed. |
| new CommitOriginInterceptor(web_contents, target_url, new_url, new_origin); |
| } |
| |
| void SimulateUnresponsiveRenderer(WebContents* web_contents, |
| RenderWidgetHost* widget) { |
| static_cast<WebContentsImpl*>(web_contents) |
| ->RendererUnresponsive(RenderWidgetHostImpl::From(widget), |
| base::DoNothing::Repeatedly()); |
| } |
| |
| #if defined(USE_AURA) |
| bool IsResizeComplete(aura::test::WindowEventDispatcherTestApi* dispatcher_test, |
| RenderWidgetHostImpl* widget_host) { |
| dispatcher_test->WaitUntilPointerMovesDispatched(); |
| widget_host->SynchronizeVisualProperties(); |
| return !widget_host->visual_properties_ack_pending_for_testing(); |
| } |
| |
| void WaitForResizeComplete(WebContents* web_contents) { |
| aura::Window* content = web_contents->GetContentNativeView(); |
| if (!content) |
| return; |
| |
| aura::WindowTreeHost* window_host = content->GetHost(); |
| aura::WindowEventDispatcher* dispatcher = window_host->dispatcher(); |
| aura::test::WindowEventDispatcherTestApi dispatcher_test(dispatcher); |
| RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From( |
| web_contents->GetRenderViewHost()->GetWidget()); |
| if (!IsResizeComplete(&dispatcher_test, widget_host)) { |
| WindowedNotificationObserver resize_observer( |
| NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_VISUAL_PROPERTIES, |
| base::Bind(IsResizeComplete, &dispatcher_test, widget_host)); |
| resize_observer.Wait(); |
| } |
| } |
| #elif defined(OS_ANDROID) |
| bool IsResizeComplete(RenderWidgetHostImpl* widget_host) { |
| return !widget_host->visual_properties_ack_pending_for_testing(); |
| } |
| |
| void WaitForResizeComplete(WebContents* web_contents) { |
| RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From( |
| web_contents->GetRenderViewHost()->GetWidget()); |
| if (!IsResizeComplete(widget_host)) { |
| WindowedNotificationObserver resize_observer( |
| NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_VISUAL_PROPERTIES, |
| base::Bind(IsResizeComplete, widget_host)); |
| resize_observer.Wait(); |
| } |
| } |
| #endif |
| |
| void SimulateMouseClick(WebContents* web_contents, |
| int modifiers, |
| blink::WebMouseEvent::Button button) { |
| int x = web_contents->GetContainerBounds().width() / 2; |
| int y = web_contents->GetContainerBounds().height() / 2; |
| SimulateMouseClickAt(web_contents, modifiers, button, gfx::Point(x, y)); |
| } |
| |
| void SimulateMouseClickAt(WebContents* web_contents, |
| int modifiers, |
| blink::WebMouseEvent::Button button, |
| const gfx::Point& point) { |
| blink::WebMouseEvent mouse_event(blink::WebInputEvent::kMouseDown, modifiers, |
| ui::EventTimeForNow()); |
| mouse_event.button = button; |
| mouse_event.SetPositionInWidget(point.x(), point.y()); |
| // Mac needs positionInScreen for events to plugins. |
| gfx::Rect offset = web_contents->GetContainerBounds(); |
| mouse_event.SetPositionInScreen(point.x() + offset.x(), |
| point.y() + offset.y()); |
| mouse_event.click_count = 1; |
| web_contents->GetRenderViewHost()->GetWidget()->ForwardMouseEvent( |
| mouse_event); |
| mouse_event.SetType(blink::WebInputEvent::kMouseUp); |
| web_contents->GetRenderViewHost()->GetWidget()->ForwardMouseEvent( |
| mouse_event); |
| } |
| |
| void SimulateRoutedMouseClickAt(WebContents* web_contents, |
| int modifiers, |
| blink::WebMouseEvent::Button button, |
| const gfx::Point& point) { |
| content::WebContentsImpl* web_contents_impl = |
| static_cast<content::WebContentsImpl*>(web_contents); |
| content::RenderWidgetHostViewBase* rwhvb = |
| static_cast<content::RenderWidgetHostViewBase*>( |
| web_contents->GetRenderWidgetHostView()); |
| blink::WebMouseEvent mouse_event(blink::WebInputEvent::kMouseDown, modifiers, |
| ui::EventTimeForNow()); |
| mouse_event.button = button; |
| mouse_event.SetPositionInWidget(point.x(), point.y()); |
| // Mac needs positionInScreen for events to plugins. |
| gfx::Rect offset = web_contents->GetContainerBounds(); |
| mouse_event.SetPositionInScreen(point.x() + offset.x(), |
| point.y() + offset.y()); |
| mouse_event.click_count = 1; |
| web_contents_impl->GetInputEventRouter()->RouteMouseEvent(rwhvb, &mouse_event, |
| ui::LatencyInfo()); |
| mouse_event.SetType(blink::WebInputEvent::kMouseUp); |
| web_contents_impl->GetInputEventRouter()->RouteMouseEvent(rwhvb, &mouse_event, |
| ui::LatencyInfo()); |
| } |
| |
| void SimulateMouseEvent(WebContents* web_contents, |
| blink::WebInputEvent::Type type, |
| const gfx::Point& point) { |
| blink::WebMouseEvent mouse_event(type, blink::WebInputEvent::kNoModifiers, |
| ui::EventTimeForNow()); |
| mouse_event.SetPositionInWidget(point.x(), point.y()); |
| web_contents->GetRenderViewHost()->GetWidget()->ForwardMouseEvent( |
| mouse_event); |
| } |
| |
| void SimulateRoutedMouseEvent(WebContents* web_contents, |
| blink::WebInputEvent::Type type, |
| const gfx::Point& point) { |
| content::WebContentsImpl* web_contents_impl = |
| static_cast<content::WebContentsImpl*>(web_contents); |
| content::RenderWidgetHostViewBase* rwhvb = |
| static_cast<content::RenderWidgetHostViewBase*>( |
| web_contents->GetRenderWidgetHostView()); |
| blink::WebMouseEvent mouse_event(type, 0, ui::EventTimeForNow()); |
| mouse_event.SetPositionInWidget(point.x(), point.y()); |
| // Mac needs positionInScreen for events to plugins. |
| gfx::Rect offset = web_contents->GetContainerBounds(); |
| mouse_event.SetPositionInScreen(point.x() + offset.x(), |
| point.y() + offset.y()); |
| |
| web_contents_impl->GetInputEventRouter()->RouteMouseEvent(rwhvb, &mouse_event, |
| ui::LatencyInfo()); |
| } |
| |
| void SimulateMouseWheelEvent(WebContents* web_contents, |
| const gfx::Point& point, |
| const gfx::Vector2d& delta, |
| const blink::WebMouseWheelEvent::Phase phase) { |
| blink::WebMouseWheelEvent wheel_event(blink::WebInputEvent::kMouseWheel, |
| blink::WebInputEvent::kNoModifiers, |
| ui::EventTimeForNow()); |
| |
| wheel_event.SetPositionInWidget(point.x(), point.y()); |
| wheel_event.delta_x = delta.x(); |
| wheel_event.delta_y = delta.y(); |
| wheel_event.phase = phase; |
| RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From( |
| web_contents->GetRenderViewHost()->GetWidget()); |
| widget_host->ForwardWheelEvent(wheel_event); |
| } |
| |
| #if !defined(OS_MACOSX) |
| void SimulateMouseWheelCtrlZoomEvent(WebContents* web_contents, |
| const gfx::Point& point, |
| bool zoom_in, |
| blink::WebMouseWheelEvent::Phase phase) { |
| blink::WebMouseWheelEvent wheel_event(blink::WebInputEvent::kMouseWheel, |
| blink::WebInputEvent::kControlKey, |
| ui::EventTimeForNow()); |
| |
| wheel_event.SetPositionInWidget(point.x(), point.y()); |
| wheel_event.delta_y = |
| (zoom_in ? 1.0 : -1.0) * ui::MouseWheelEvent::kWheelDelta; |
| wheel_event.wheel_ticks_y = (zoom_in ? 1.0 : -1.0); |
| wheel_event.has_precise_scrolling_deltas = false; |
| wheel_event.phase = phase; |
| RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From( |
| web_contents->GetRenderViewHost()->GetWidget()); |
| widget_host->ForwardWheelEvent(wheel_event); |
| } |
| #endif // !defined(OS_MACOSX) |
| |
| void SimulateGesturePinchSequence(WebContents* web_contents, |
| const gfx::Point& point, |
| float scale, |
| blink::WebGestureDevice source_device) { |
| RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From( |
| web_contents->GetRenderViewHost()->GetWidget()); |
| |
| blink::WebGestureEvent pinch_begin(blink::WebInputEvent::kGesturePinchBegin, |
| blink::WebInputEvent::kNoModifiers, |
| ui::EventTimeForNow(), source_device); |
| pinch_begin.SetPositionInWidget(gfx::PointF(point)); |
| pinch_begin.SetPositionInScreen(gfx::PointF(point)); |
| pinch_begin.SetNeedsWheelEvent(source_device == |
| blink::kWebGestureDeviceTouchpad); |
| widget_host->ForwardGestureEvent(pinch_begin); |
| |
| blink::WebGestureEvent pinch_update(pinch_begin); |
| pinch_update.SetType(blink::WebInputEvent::kGesturePinchUpdate); |
| pinch_update.data.pinch_update.scale = scale; |
| pinch_update.SetNeedsWheelEvent(source_device == |
| blink::kWebGestureDeviceTouchpad); |
| widget_host->ForwardGestureEvent(pinch_update); |
| |
| blink::WebGestureEvent pinch_end(pinch_begin); |
| pinch_end.SetType(blink::WebInputEvent::kGesturePinchEnd); |
| pinch_end.SetNeedsWheelEvent(source_device == |
| blink::kWebGestureDeviceTouchpad); |
| widget_host->ForwardGestureEvent(pinch_end); |
| } |
| |
| void SimulateGestureScrollSequence(WebContents* web_contents, |
| const gfx::Point& point, |
| const gfx::Vector2dF& delta) { |
| RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From( |
| web_contents->GetRenderViewHost()->GetWidget()); |
| |
| blink::WebGestureEvent scroll_begin( |
| blink::WebGestureEvent::kGestureScrollBegin, |
| blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow(), |
| blink::kWebGestureDeviceTouchpad); |
| scroll_begin.SetPositionInWidget(gfx::PointF(point)); |
| scroll_begin.data.scroll_begin.delta_x_hint = delta.x(); |
| scroll_begin.data.scroll_begin.delta_y_hint = delta.y(); |
| widget_host->ForwardGestureEvent(scroll_begin); |
| |
| blink::WebGestureEvent scroll_update( |
| blink::WebGestureEvent::kGestureScrollUpdate, |
| blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow(), |
| blink::kWebGestureDeviceTouchpad); |
| scroll_update.SetPositionInWidget(gfx::PointF(point)); |
| scroll_update.data.scroll_update.delta_x = delta.x(); |
| scroll_update.data.scroll_update.delta_y = delta.y(); |
| scroll_update.data.scroll_update.velocity_x = 0; |
| scroll_update.data.scroll_update.velocity_y = 0; |
| widget_host->ForwardGestureEvent(scroll_update); |
| |
| blink::WebGestureEvent scroll_end(blink::WebGestureEvent::kGestureScrollEnd, |
| blink::WebInputEvent::kNoModifiers, |
| ui::EventTimeForNow(), |
| blink::kWebGestureDeviceTouchpad); |
| scroll_end.SetPositionInWidget(gfx::PointF(point)); |
| widget_host->ForwardGestureEvent(scroll_end); |
| } |
| |
| void SimulateGestureFlingSequence(WebContents* web_contents, |
| const gfx::Point& point, |
| const gfx::Vector2dF& velocity) { |
| RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From( |
| web_contents->GetRenderViewHost()->GetWidget()); |
| |
| blink::WebGestureEvent scroll_begin( |
| blink::WebGestureEvent::kGestureScrollBegin, |
| blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow(), |
| blink::kWebGestureDeviceTouchpad); |
| scroll_begin.SetPositionInWidget(gfx::PointF(point)); |
| widget_host->ForwardGestureEvent(scroll_begin); |
| |
| blink::WebGestureEvent scroll_end(blink::WebGestureEvent::kGestureScrollEnd, |
| blink::WebInputEvent::kNoModifiers, |
| ui::EventTimeForNow(), |
| blink::kWebGestureDeviceTouchpad); |
| scroll_end.SetPositionInWidget(gfx::PointF(point)); |
| widget_host->ForwardGestureEvent(scroll_end); |
| |
| blink::WebGestureEvent fling_start(blink::WebGestureEvent::kGestureFlingStart, |
| blink::WebInputEvent::kNoModifiers, |
| ui::EventTimeForNow(), |
| blink::kWebGestureDeviceTouchpad); |
| fling_start.SetPositionInWidget(gfx::PointF(point)); |
| fling_start.data.fling_start.target_viewport = false; |
| fling_start.data.fling_start.velocity_x = velocity.x(); |
| fling_start.data.fling_start.velocity_y = velocity.y(); |
| widget_host->ForwardGestureEvent(fling_start); |
| } |
| |
| void SimulateGestureEvent(WebContents* web_contents, |
| const blink::WebGestureEvent& gesture_event, |
| const ui::LatencyInfo& latency) { |
| RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>( |
| web_contents->GetRenderWidgetHostView()); |
| view->ProcessGestureEvent(gesture_event, latency); |
| } |
| |
| void SimulateTouchGestureAt(WebContents* web_contents, |
| const gfx::Point& point, |
| blink::WebInputEvent::Type type) { |
| blink::WebGestureEvent gesture(type, 0, ui::EventTimeForNow(), |
| blink::kWebGestureDeviceTouchscreen); |
| gesture.SetPositionInWidget(gfx::PointF(point)); |
| RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From( |
| web_contents->GetRenderViewHost()->GetWidget()); |
| widget_host->ForwardGestureEvent(gesture); |
| } |
| |
| void SimulateTapDownAt(WebContents* web_contents, const gfx::Point& point) { |
| SimulateTouchGestureAt(web_contents, point, |
| blink::WebGestureEvent::kGestureTapDown); |
| } |
| |
| void SimulateTapAt(WebContents* web_contents, const gfx::Point& point) { |
| SimulateTouchGestureAt(web_contents, point, |
| blink::WebGestureEvent::kGestureTap); |
| } |
| |
| void SimulateTapWithModifiersAt(WebContents* web_contents, |
| unsigned modifiers, |
| const gfx::Point& point) { |
| blink::WebGestureEvent tap(blink::WebGestureEvent::kGestureTap, modifiers, |
| ui::EventTimeForNow(), |
| blink::kWebGestureDeviceTouchpad); |
| tap.SetPositionInWidget(gfx::PointF(point)); |
| RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From( |
| web_contents->GetRenderViewHost()->GetWidget()); |
| widget_host->ForwardGestureEvent(tap); |
| } |
| |
| #if defined(USE_AURA) |
| void SimulateTouchPressAt(WebContents* web_contents, const gfx::Point& point) { |
| ui::TouchEvent touch( |
| ui::ET_TOUCH_PRESSED, point, base::TimeTicks(), |
| ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); |
| static_cast<RenderWidgetHostViewAura*>( |
| web_contents->GetRenderWidgetHostView()) |
| ->OnTouchEvent(&touch); |
| } |
| |
| void SimulateLongTapAt(WebContents* web_contents, const gfx::Point& point) { |
| RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>( |
| web_contents->GetRenderWidgetHostView()); |
| |
| ui::TouchEvent touch_start( |
| ui::ET_TOUCH_PRESSED, point, base::TimeTicks(), |
| ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); |
| rwhva->OnTouchEvent(&touch_start); |
| |
| ui::GestureEventDetails tap_down_details(ui::ET_GESTURE_TAP_DOWN); |
| tap_down_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHSCREEN); |
| ui::GestureEvent tap_down(point.x(), point.y(), 0, ui::EventTimeForNow(), |
| tap_down_details, touch_start.unique_event_id()); |
| rwhva->OnGestureEvent(&tap_down); |
| |
| ui::GestureEventDetails long_press_details(ui::ET_GESTURE_LONG_PRESS); |
| long_press_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHSCREEN); |
| ui::GestureEvent long_press(point.x(), point.y(), 0, ui::EventTimeForNow(), |
| long_press_details, |
| touch_start.unique_event_id()); |
| rwhva->OnGestureEvent(&long_press); |
| |
| ui::TouchEvent touch_end( |
| ui::ET_TOUCH_RELEASED, point, base::TimeTicks(), |
| ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); |
| rwhva->OnTouchEvent(&touch_end); |
| |
| ui::GestureEventDetails long_tap_details(ui::ET_GESTURE_LONG_TAP); |
| long_tap_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHSCREEN); |
| ui::GestureEvent long_tap(point.x(), point.y(), 0, ui::EventTimeForNow(), |
| long_tap_details, touch_end.unique_event_id()); |
| rwhva->OnGestureEvent(&long_tap); |
| } |
| #endif |
| |
| void SimulateKeyPress(WebContents* web_contents, |
| ui::DomKey key, |
| ui::DomCode code, |
| ui::KeyboardCode key_code, |
| bool control, |
| bool shift, |
| bool alt, |
| bool command) { |
| SimulateKeyPressImpl(web_contents, key, code, key_code, control, shift, alt, |
| command, /*send_char=*/true); |
| } |
| |
| void SimulateKeyPressWithoutChar(WebContents* web_contents, |
| ui::DomKey key, |
| ui::DomCode code, |
| ui::KeyboardCode key_code, |
| bool control, |
| bool shift, |
| bool alt, |
| bool command) { |
| SimulateKeyPressImpl(web_contents, key, code, key_code, control, shift, alt, |
| command, /*send_char=*/false); |
| } |
| |
| ScopedSimulateModifierKeyPress::ScopedSimulateModifierKeyPress( |
| WebContents* web_contents, |
| bool control, |
| bool shift, |
| bool alt, |
| bool command) |
| : web_contents_(web_contents), |
| modifiers_(0), |
| control_(control), |
| shift_(shift), |
| alt_(alt), |
| command_(command) { |
| modifiers_ = |
| SimulateModifierKeysDown(web_contents_, control_, shift_, alt_, command_); |
| } |
| |
| ScopedSimulateModifierKeyPress::~ScopedSimulateModifierKeyPress() { |
| modifiers_ = SimulateModifierKeysUp(web_contents_, control_, shift_, alt_, |
| command_, modifiers_); |
| DCHECK_EQ(0, modifiers_); |
| } |
| |
| void ScopedSimulateModifierKeyPress::MouseClickAt( |
| int additional_modifiers, |
| blink::WebMouseEvent::Button button, |
| const gfx::Point& point) { |
| SimulateMouseClickAt(web_contents_, modifiers_ | additional_modifiers, button, |
| point); |
| } |
| |
| void ScopedSimulateModifierKeyPress::KeyPress(ui::DomKey key, |
| ui::DomCode code, |
| ui::KeyboardCode key_code) { |
| SimulateKeyEvent(web_contents_, key, code, key_code, /*send_char=*/true, |
| modifiers_); |
| } |
| |
| void ScopedSimulateModifierKeyPress::KeyPressWithoutChar( |
| ui::DomKey key, |
| ui::DomCode code, |
| ui::KeyboardCode key_code) { |
| SimulateKeyEvent(web_contents_, key, code, key_code, /*send_char=*/false, |
| modifiers_); |
| } |
| |
| bool IsWebcamAvailableOnSystem(WebContents* web_contents) { |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| web_contents, kHasVideoInputDeviceOnSystem, &result)); |
| return result == kHasVideoInputDevice; |
| } |
| |
| RenderFrameHost* ConvertToRenderFrameHost(WebContents* web_contents) { |
| return web_contents->GetMainFrame(); |
| } |
| |
| RenderFrameHost* ConvertToRenderFrameHost(RenderFrameHost* render_frame_host) { |
| return render_frame_host; |
| } |
| |
| bool ExecuteScript(const ToRenderFrameHost& adapter, |
| const std::string& script) { |
| return ExecuteScriptWithUserGestureControl(adapter.render_frame_host(), |
| script, true); |
| } |
| |
| bool ExecuteScriptWithoutUserGesture(const ToRenderFrameHost& adapter, |
| const std::string& script) { |
| return ExecuteScriptWithUserGestureControl(adapter.render_frame_host(), |
| script, false); |
| } |
| |
| void ExecuteScriptAsync(const ToRenderFrameHost& adapter, |
| const std::string& script) { |
| adapter.render_frame_host()->ExecuteJavaScriptWithUserGestureForTests( |
| base::UTF8ToUTF16(script)); |
| } |
| |
| bool ExecuteScriptAndExtractDouble(const ToRenderFrameHost& adapter, |
| const std::string& script, double* result) { |
| DCHECK(result); |
| std::unique_ptr<base::Value> value; |
| if (!ExecuteScriptHelper(adapter.render_frame_host(), script, true, &value) || |
| !value.get()) { |
| return false; |
| } |
| |
| return value->GetAsDouble(result); |
| } |
| |
| bool ExecuteScriptAndExtractInt(const ToRenderFrameHost& adapter, |
| const std::string& script, int* result) { |
| DCHECK(result); |
| std::unique_ptr<base::Value> value; |
| if (!ExecuteScriptHelper(adapter.render_frame_host(), script, true, &value) || |
| !value.get()) { |
| return false; |
| } |
| |
| return value->GetAsInteger(result); |
| } |
| |
| bool ExecuteScriptAndExtractBool(const ToRenderFrameHost& adapter, |
| const std::string& script, bool* result) { |
| DCHECK(result); |
| std::unique_ptr<base::Value> value; |
| if (!ExecuteScriptHelper(adapter.render_frame_host(), script, true, &value) || |
| !value.get()) { |
| return false; |
| } |
| |
| return value->GetAsBoolean(result); |
| } |
| |
| bool ExecuteScriptInIsolatedWorldAndExtractBool( |
| const ToRenderFrameHost& adapter, |
| const int world_id, |
| const std::string& script, |
| bool* result) { |
| DCHECK(result); |
| std::unique_ptr<base::Value> value; |
| if (!ExecuteScriptInIsolatedWorldHelper(adapter.render_frame_host(), world_id, |
| script, &value) || |
| !value.get()) { |
| return false; |
| } |
| |
| return value->GetAsBoolean(result); |
| } |
| |
| bool ExecuteScriptAndExtractString(const ToRenderFrameHost& adapter, |
| const std::string& script, |
| std::string* result) { |
| DCHECK(result); |
| std::unique_ptr<base::Value> value; |
| if (!ExecuteScriptHelper(adapter.render_frame_host(), script, true, &value) || |
| !value.get()) { |
| return false; |
| } |
| |
| return value->GetAsString(result); |
| } |
| |
| bool ExecuteScriptWithoutUserGestureAndExtractDouble( |
| const ToRenderFrameHost& adapter, |
| const std::string& script, |
| double* result) { |
| DCHECK(result); |
| std::unique_ptr<base::Value> value; |
| return ExecuteScriptHelper(adapter.render_frame_host(), script, false, |
| &value) && |
| value && value->GetAsDouble(result); |
| } |
| |
| bool ExecuteScriptWithoutUserGestureAndExtractInt( |
| const ToRenderFrameHost& adapter, |
| const std::string& script, |
| int* result) { |
| DCHECK(result); |
| std::unique_ptr<base::Value> value; |
| return ExecuteScriptHelper(adapter.render_frame_host(), script, false, |
| &value) && |
| value && value->GetAsInteger(result); |
| } |
| |
| bool ExecuteScriptWithoutUserGestureAndExtractBool( |
| const ToRenderFrameHost& adapter, |
| const std::string& script, |
| bool* result) { |
| DCHECK(result); |
| std::unique_ptr<base::Value> value; |
| return ExecuteScriptHelper(adapter.render_frame_host(), script, false, |
| &value) && |
| value && value->GetAsBoolean(result); |
| } |
| |
| bool ExecuteScriptWithoutUserGestureAndExtractString( |
| const ToRenderFrameHost& adapter, |
| const std::string& script, |
| std::string* result) { |
| DCHECK(result); |
| std::unique_ptr<base::Value> value; |
| return ExecuteScriptHelper(adapter.render_frame_host(), script, false, |
| &value) && |
| value && value->GetAsString(result); |
| } |
| |
| namespace { |
| void AddToSetIfFrameMatchesPredicate( |
| std::set<RenderFrameHost*>* frame_set, |
| const base::Callback<bool(RenderFrameHost*)>& predicate, |
| RenderFrameHost* host) { |
| if (predicate.Run(host)) |
| frame_set->insert(host); |
| } |
| } |
| |
| RenderFrameHost* FrameMatchingPredicate( |
| WebContents* web_contents, |
| const base::Callback<bool(RenderFrameHost*)>& predicate) { |
| std::set<RenderFrameHost*> frame_set; |
| web_contents->ForEachFrame(base::BindRepeating( |
| &AddToSetIfFrameMatchesPredicate, &frame_set, predicate)); |
| EXPECT_EQ(1U, frame_set.size()); |
| return frame_set.size() == 1 ? *frame_set.begin() : nullptr; |
| } |
| |
| bool FrameMatchesName(const std::string& name, RenderFrameHost* frame) { |
| return frame->GetFrameName() == name; |
| } |
| |
| bool FrameIsChildOfMainFrame(RenderFrameHost* frame) { |
| return frame->GetParent() && !frame->GetParent()->GetParent(); |
| } |
| |
| bool FrameHasSourceUrl(const GURL& url, RenderFrameHost* frame) { |
| return frame->GetLastCommittedURL() == url; |
| } |
| |
| RenderFrameHost* ChildFrameAt(RenderFrameHost* frame, size_t index) { |
| RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(frame); |
| if (index >= rfh->frame_tree_node()->child_count()) |
| return nullptr; |
| return rfh->frame_tree_node()->child_at(index)->current_frame_host(); |
| } |
| |
| bool ExecuteWebUIResourceTest(WebContents* web_contents, |
| const std::vector<int>& js_resource_ids) { |
| // Inject WebUI test runner script first prior to other scripts required to |
| // run the test as scripts may depend on it being declared. |
| std::vector<int> ids; |
| ids.push_back(IDR_WEBUI_JS_WEBUI_RESOURCE_TEST); |
| ids.insert(ids.end(), js_resource_ids.begin(), js_resource_ids.end()); |
| |
| std::string script; |
| for (int id : ids) { |
| scoped_refptr<base::RefCountedMemory> bytes = |
| ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(id); |
| |
| if (HasGzipHeader(*bytes)) |
| AppendGzippedResource(*bytes, &script); |
| else |
| script.append(bytes->front_as<char>(), bytes->size()); |
| |
| script.append("\n"); |
| } |
| ExecuteScriptAsync(web_contents, script); |
| |
| DOMMessageQueue message_queue; |
| ExecuteScriptAsync(web_contents, "runTests()"); |
| |
| std::string message; |
| do { |
| if (!message_queue.WaitForMessage(&message)) |
| return false; |
| } while (message.compare("\"PENDING\"") == 0); |
| |
| return message.compare("\"SUCCESS\"") == 0; |
| } |
| |
| std::string GetCookies(BrowserContext* browser_context, const GURL& url) { |
| std::string cookies; |
| base::RunLoop run_loop; |
| network::mojom::CookieManagerPtr cookie_manager; |
| BrowserContext::GetDefaultStoragePartition(browser_context) |
| ->GetNetworkContext() |
| ->GetCookieManager(mojo::MakeRequest(&cookie_manager)); |
| cookie_manager->GetCookieList( |
| url, net::CookieOptions(), |
| base::BindOnce( |
| [](std::string* cookies_out, base::RunLoop* run_loop, |
| const std::vector<net::CanonicalCookie>& cookies) { |
| *cookies_out = net::CanonicalCookie::BuildCookieLine(cookies); |
| run_loop->Quit(); |
| }, |
| &cookies, &run_loop)); |
| run_loop.Run(); |
| return cookies; |
| } |
| |
| std::vector<net::CanonicalCookie> GetCanonicalCookies( |
| BrowserContext* browser_context, |
| const GURL& url) { |
| std::vector<net::CanonicalCookie> cookies; |
| base::RunLoop run_loop; |
| network::mojom::CookieManagerPtr cookie_manager; |
| BrowserContext::GetDefaultStoragePartition(browser_context) |
| ->GetNetworkContext() |
| ->GetCookieManager(mojo::MakeRequest(&cookie_manager)); |
| cookie_manager->GetCookieList( |
| url, net::CookieOptions(), |
| base::BindOnce( |
| [](base::RunLoop* run_loop, |
| std::vector<net::CanonicalCookie>* cookies_out, |
| const std::vector<net::CanonicalCookie>& cookies) { |
| *cookies_out = cookies; |
| run_loop->Quit(); |
| }, |
| &run_loop, &cookies)); |
| run_loop.Run(); |
| return cookies; |
| } |
| |
| bool SetCookie(BrowserContext* browser_context, |
| const GURL& url, |
| const std::string& value) { |
| bool result = false; |
| base::RunLoop run_loop; |
| network::mojom::CookieManagerPtr cookie_manager; |
| BrowserContext::GetDefaultStoragePartition(browser_context) |
| ->GetNetworkContext() |
| ->GetCookieManager(mojo::MakeRequest(&cookie_manager)); |
| std::unique_ptr<net::CanonicalCookie> cc(net::CanonicalCookie::Create( |
| url, value, base::Time::Now(), net::CookieOptions())); |
| DCHECK(cc.get()); |
| |
| cookie_manager->SetCanonicalCookie( |
| *cc.get(), true /* secure_source */, true /* modify_http_only */, |
| base::BindOnce( |
| [](bool* result, base::RunLoop* run_loop, bool success) { |
| *result = success; |
| run_loop->Quit(); |
| }, |
| &result, &run_loop)); |
| run_loop.Run(); |
| return result; |
| } |
| |
| void FetchHistogramsFromChildProcesses() { |
| base::RunLoop run_loop; |
| |
| FetchHistogramsAsynchronously( |
| base::ThreadTaskRunnerHandle::Get(), run_loop.QuitClosure(), |
| // If this call times out, it means that a child process is not |
| // responding, which is something we should not ignore. The timeout is |
| // set to be longer than the normal browser test timeout so that it will |
| // be prempted by the normal timeout. |
| TestTimeouts::action_max_timeout()); |
| run_loop.Run(); |
| } |
| |
| void SetupCrossSiteRedirector(net::EmbeddedTestServer* embedded_test_server) { |
| embedded_test_server->RegisterRequestHandler( |
| base::Bind(&CrossSiteRedirectResponseHandler, embedded_test_server)); |
| } |
| |
| void WaitForInterstitialAttach(content::WebContents* web_contents) { |
| if (web_contents->ShowingInterstitialPage()) |
| return; |
| base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); |
| InterstitialObserver observer(web_contents, run_loop.QuitClosure(), |
| base::OnceClosure()); |
| run_loop.Run(); |
| } |
| |
| void WaitForInterstitialDetach(content::WebContents* web_contents) { |
| RunTaskAndWaitForInterstitialDetach(web_contents, base::Closure()); |
| } |
| |
| void RunTaskAndWaitForInterstitialDetach(content::WebContents* web_contents, |
| const base::Closure& task) { |
| if (!web_contents || !web_contents->ShowingInterstitialPage()) |
| return; |
| base::RunLoop run_loop; |
| InterstitialObserver observer(web_contents, base::OnceClosure(), |
| run_loop.QuitClosure()); |
| if (!task.is_null()) |
| task.Run(); |
| // At this point, web_contents may have been deleted. |
| run_loop.Run(); |
| } |
| |
| bool WaitForRenderFrameReady(RenderFrameHost* rfh) { |
| if (!rfh) |
| return false; |
| std::string result; |
| EXPECT_TRUE( |
| content::ExecuteScriptAndExtractString( |
| rfh, |
| "(function() {" |
| " var done = false;" |
| " function checkState() {" |
| " if (!done && document.readyState == 'complete') {" |
| " done = true;" |
| " window.domAutomationController.send('pageLoadComplete');" |
| " }" |
| " }" |
| " checkState();" |
| " document.addEventListener('readystatechange', checkState);" |
| "})();", |
| &result)); |
| return result == "pageLoadComplete"; |
| } |
| |
| void EnableAccessibilityForWebContents(WebContents* web_contents) { |
| WebContentsImpl* web_contents_impl = |
| static_cast<WebContentsImpl*>(web_contents); |
| web_contents_impl->SetAccessibilityMode(ui::kAXModeComplete); |
| } |
| |
| void WaitForAccessibilityFocusChange() { |
| base::RunLoop run_loop; |
| BrowserAccessibilityManager::SetFocusChangeCallbackForTesting( |
| run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| ui::AXNodeData GetFocusedAccessibilityNodeInfo(WebContents* web_contents) { |
| WebContentsImpl* web_contents_impl = |
| static_cast<WebContentsImpl*>(web_contents); |
| BrowserAccessibilityManager* manager = |
| web_contents_impl->GetRootBrowserAccessibilityManager(); |
| if (!manager) |
| return ui::AXNodeData(); |
| BrowserAccessibility* focused_node = manager->GetFocus(); |
| return focused_node->GetData(); |
| } |
| |
| bool AccessibilityTreeContainsNodeWithName(BrowserAccessibility* node, |
| const std::string& name) { |
| if (node->GetStringAttribute(ax::mojom::StringAttribute::kName) == name) |
| return true; |
| for (unsigned i = 0; i < node->PlatformChildCount(); i++) { |
| if (AccessibilityTreeContainsNodeWithName(node->PlatformGetChild(i), name)) |
| return true; |
| } |
| return false; |
| } |
| |
| bool ListenToGuestWebContents( |
| AccessibilityNotificationWaiter* accessibility_waiter, |
| WebContents* web_contents) { |
| accessibility_waiter->ListenToAdditionalFrame( |
| static_cast<RenderFrameHostImpl*>(web_contents->GetMainFrame())); |
| return true; |
| } |
| |
| void WaitForAccessibilityTreeToContainNodeWithName(WebContents* web_contents, |
| const std::string& name) { |
| WebContentsImpl* web_contents_impl = static_cast<WebContentsImpl*>( |
| web_contents); |
| RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>( |
| web_contents_impl->GetMainFrame()); |
| BrowserAccessibilityManager* main_frame_manager = |
| main_frame->browser_accessibility_manager(); |
| FrameTree* frame_tree = web_contents_impl->GetFrameTree(); |
| while (!main_frame_manager || !AccessibilityTreeContainsNodeWithName( |
| main_frame_manager->GetRoot(), name)) { |
| AccessibilityNotificationWaiter accessibility_waiter( |
| main_frame, ax::mojom::Event::kNone); |
| for (FrameTreeNode* node : frame_tree->Nodes()) { |
| accessibility_waiter.ListenToAdditionalFrame( |
| node->current_frame_host()); |
| } |
| |
| content::BrowserPluginGuestManager* guest_manager = |
| web_contents_impl->GetBrowserContext()->GetGuestManager(); |
| if (guest_manager) { |
| guest_manager->ForEachGuest(web_contents_impl, |
| base::BindRepeating(&ListenToGuestWebContents, |
| &accessibility_waiter)); |
| } |
| |
| accessibility_waiter.WaitForNotification(); |
| main_frame_manager = main_frame->browser_accessibility_manager(); |
| } |
| } |
| |
| ui::AXTreeUpdate GetAccessibilityTreeSnapshot(WebContents* web_contents) { |
| WebContentsImpl* web_contents_impl = |
| static_cast<WebContentsImpl*>(web_contents); |
| BrowserAccessibilityManager* manager = |
| web_contents_impl->GetRootBrowserAccessibilityManager(); |
| if (!manager) |
| return ui::AXTreeUpdate(); |
| return manager->SnapshotAXTreeForTesting(); |
| } |
| |
| bool IsWebContentsBrowserPluginFocused(content::WebContents* web_contents) { |
| WebContentsImpl* web_contents_impl = |
| static_cast<WebContentsImpl*>(web_contents); |
| BrowserPluginGuest* browser_plugin_guest = |
| web_contents_impl->GetBrowserPluginGuest(); |
| return browser_plugin_guest ? browser_plugin_guest->focused() : false; |
| } |
| |
| RenderWidgetHost* GetMouseLockWidget(WebContents* web_contents) { |
| return static_cast<WebContentsImpl*>(web_contents)->GetMouseLockWidget(); |
| } |
| |
| RenderWidgetHost* GetKeyboardLockWidget(WebContents* web_contents) { |
| return static_cast<WebContentsImpl*>(web_contents)->GetKeyboardLockWidget(); |
| } |
| |
| bool RequestKeyboardLock(WebContents* web_contents, |
| base::Optional<base::flat_set<ui::DomCode>> codes) { |
| DCHECK(!codes.has_value() || !codes.value().empty()); |
| WebContentsImpl* web_contents_impl = |
| static_cast<WebContentsImpl*>(web_contents); |
| RenderWidgetHostImpl* render_widget_host_impl = |
| web_contents_impl->GetMainFrame()->GetRenderWidgetHost(); |
| return render_widget_host_impl->RequestKeyboardLock(std::move(codes)); |
| } |
| |
| void CancelKeyboardLock(WebContents* web_contents) { |
| WebContentsImpl* web_contents_impl = |
| static_cast<WebContentsImpl*>(web_contents); |
| RenderWidgetHostImpl* render_widget_host_impl = |
| web_contents_impl->GetMainFrame()->GetRenderWidgetHost(); |
| render_widget_host_impl->CancelKeyboardLock(); |
| } |
| |
| bool IsInnerInterstitialPageConnected(InterstitialPage* interstitial_page) { |
| InterstitialPageImpl* impl = |
| static_cast<InterstitialPageImpl*>(interstitial_page); |
| |
| RenderWidgetHostViewBase* rwhvb = |
| static_cast<RenderWidgetHostViewBase*>(impl->GetView()); |
| EXPECT_TRUE(rwhvb->IsRenderWidgetHostViewChildFrame()); |
| RenderWidgetHostViewChildFrame* rwhvcf = |
| static_cast<RenderWidgetHostViewChildFrame*>(rwhvb); |
| |
| CrossProcessFrameConnector* frame_connector = |
| static_cast<CrossProcessFrameConnector*>( |
| rwhvcf->FrameConnectorForTesting()); |
| |
| WebContentsImpl* inner_web_contents = |
| static_cast<WebContentsImpl*>(impl->GetWebContents()); |
| FrameTreeNode* outer_node = FrameTreeNode::GloballyFindByID( |
| inner_web_contents->GetOuterDelegateFrameTreeNodeId()); |
| |
| return outer_node->current_frame_host()->GetView() == |
| frame_connector->GetParentRenderWidgetHostView(); |
| } |
| |
| std::vector<RenderWidgetHostView*> GetInputEventRouterRenderWidgetHostViews( |
| WebContents* web_contents) { |
| return static_cast<WebContentsImpl*>(web_contents) |
| ->GetInputEventRouter() |
| ->GetRenderWidgetHostViewsForTests(); |
| } |
| |
| RenderWidgetHost* GetFocusedRenderWidgetHost(WebContents* web_contents) { |
| WebContentsImpl* web_contents_impl = |
| static_cast<WebContentsImpl*>(web_contents); |
| return web_contents_impl->GetFocusedRenderWidgetHost( |
| web_contents_impl->GetMainFrame()->GetRenderWidgetHost()); |
| } |
| |
| WebContents* GetFocusedWebContents(WebContents* web_contents) { |
| WebContentsImpl* web_contents_impl = |
| static_cast<WebContentsImpl*>(web_contents); |
| return web_contents_impl->GetFocusedWebContents(); |
| } |
| |
| void RouteMouseEvent(WebContents* web_contents, blink::WebMouseEvent* event) { |
| WebContentsImpl* web_contents_impl = |
| static_cast<WebContentsImpl*>(web_contents); |
| web_contents_impl->GetInputEventRouter()->RouteMouseEvent( |
| static_cast<RenderWidgetHostViewBase*>( |
| web_contents_impl->GetMainFrame()->GetView()), |
| event, ui::LatencyInfo()); |
| } |
| |
| #if defined(USE_AURA) |
| void SendRoutedTouchTapSequence(content::WebContents* web_contents, |
| gfx::Point point) { |
| RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>( |
| web_contents->GetRenderWidgetHostView()); |
| ui::TouchEvent touch_start( |
| ui::ET_TOUCH_PRESSED, point, base::TimeTicks::Now(), |
| ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); |
| rwhva->OnTouchEvent(&touch_start); |
| ui::TouchEvent touch_end( |
| ui::ET_TOUCH_RELEASED, point, base::TimeTicks::Now(), |
| ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); |
| rwhva->OnTouchEvent(&touch_end); |
| } |
| |
| void SendRoutedGestureTapSequence(content::WebContents* web_contents, |
| gfx::Point point) { |
| RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>( |
| web_contents->GetRenderWidgetHostView()); |
| ui::GestureEventDetails gesture_tap_down_details(ui::ET_GESTURE_TAP_DOWN); |
| gesture_tap_down_details.set_is_source_touch_event_set_non_blocking(true); |
| gesture_tap_down_details.set_device_type( |
| ui::GestureDeviceType::DEVICE_TOUCHSCREEN); |
| ui::GestureEvent gesture_tap_down(point.x(), point.y(), 0, |
| base::TimeTicks::Now(), |
| gesture_tap_down_details); |
| rwhva->OnGestureEvent(&gesture_tap_down); |
| ui::GestureEventDetails gesture_tap_details(ui::ET_GESTURE_TAP); |
| gesture_tap_details.set_is_source_touch_event_set_non_blocking(true); |
| gesture_tap_details.set_device_type( |
| ui::GestureDeviceType::DEVICE_TOUCHSCREEN); |
| gesture_tap_details.set_tap_count(1); |
| ui::GestureEvent gesture_tap(point.x(), point.y(), 0, base::TimeTicks::Now(), |
| gesture_tap_details); |
| rwhva->OnGestureEvent(&gesture_tap); |
| } |
| |
| #endif |
| |
| namespace { |
| |
| RenderFrameMetadataProvider* RenderFrameMetadataProviderFromFrameTreeNode( |
| FrameTreeNode* node) { |
| DCHECK(node); |
| DCHECK(node->current_frame_host()); |
| DCHECK(node->current_frame_host()->GetRenderWidgetHost()); |
| return node->current_frame_host() |
| ->GetRenderWidgetHost() |
| ->render_frame_metadata_provider(); |
| } |
| |
| RenderFrameMetadataProvider* RenderFrameMetadataProviderFromWebContents( |
| WebContents* web_contents) { |
| DCHECK(web_contents); |
| DCHECK(web_contents->GetRenderViewHost()); |
| DCHECK( |
| RenderWidgetHostImpl::From(web_contents->GetRenderViewHost()->GetWidget()) |
| ->render_frame_metadata_provider()); |
| return RenderWidgetHostImpl::From( |
| web_contents->GetRenderViewHost()->GetWidget()) |
| ->render_frame_metadata_provider(); |
| } |
| |
| } // namespace |
| |
| TitleWatcher::TitleWatcher(WebContents* web_contents, |
| const base::string16& expected_title) |
| : WebContentsObserver(web_contents) { |
| expected_titles_.push_back(expected_title); |
| } |
| |
| void TitleWatcher::AlsoWaitForTitle(const base::string16& expected_title) { |
| expected_titles_.push_back(expected_title); |
| } |
| |
| TitleWatcher::~TitleWatcher() { |
| } |
| |
| const base::string16& TitleWatcher::WaitAndGetTitle() { |
| TestTitle(); |
| run_loop_.Run(); |
| return observed_title_; |
| } |
| |
| void TitleWatcher::DidStopLoading() { |
| // When navigating through the history, the restored NavigationEntry's title |
| // will be used. If the entry ends up having the same title after we return |
| // to it, as will usually be the case, then WebContentsObserver::TitleSet |
| // will then be suppressed, since the NavigationEntry's title hasn't changed. |
| TestTitle(); |
| } |
| |
| void TitleWatcher::TitleWasSet(NavigationEntry* entry) { |
| TestTitle(); |
| } |
| |
| void TitleWatcher::TestTitle() { |
| const base::string16& current_title = web_contents()->GetTitle(); |
| if (base::ContainsValue(expected_titles_, current_title)) { |
| observed_title_ = current_title; |
| run_loop_.Quit(); |
| } |
| } |
| |
| RenderProcessHostWatcher::RenderProcessHostWatcher( |
| RenderProcessHost* render_process_host, |
| WatchType type) |
| : render_process_host_(render_process_host), |
| type_(type), |
| did_exit_normally_(true), |
| quit_closure_(run_loop_.QuitClosure()) { |
| render_process_host_->AddObserver(this); |
| } |
| |
| RenderProcessHostWatcher::RenderProcessHostWatcher(WebContents* web_contents, |
| WatchType type) |
| : RenderProcessHostWatcher(web_contents->GetMainFrame()->GetProcess(), |
| type) {} |
| |
| RenderProcessHostWatcher::~RenderProcessHostWatcher() { |
| if (render_process_host_) |
| render_process_host_->RemoveObserver(this); |
| } |
| |
| void RenderProcessHostWatcher::Wait() { |
| run_loop_.Run(); |
| } |
| |
| void RenderProcessHostWatcher::RenderProcessExited( |
| RenderProcessHost* host, |
| const ChildProcessTerminationInfo& info) { |
| did_exit_normally_ = |
| info.status == base::TERMINATION_STATUS_NORMAL_TERMINATION; |
| if (type_ == WATCH_FOR_PROCESS_EXIT) |
| std::move(quit_closure_).Run(); |
| } |
| |
| void RenderProcessHostWatcher::RenderProcessHostDestroyed( |
| RenderProcessHost* host) { |
| render_process_host_ = nullptr; |
| if (type_ == WATCH_FOR_HOST_DESTRUCTION) |
| std::move(quit_closure_).Run(); |
| } |
| |
| DOMMessageQueue::DOMMessageQueue() { |
| registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE, |
| NotificationService::AllSources()); |
| } |
| |
| DOMMessageQueue::DOMMessageQueue(WebContents* web_contents) |
| : WebContentsObserver(web_contents) { |
| registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE, |
| Source<WebContents>(web_contents)); |
| } |
| |
| DOMMessageQueue::~DOMMessageQueue() {} |
| |
| void DOMMessageQueue::Observe(int type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| Details<std::string> dom_op_result(details); |
| message_queue_.push(*dom_op_result.ptr()); |
| if (quit_closure_) |
| std::move(quit_closure_).Run(); |
| } |
| |
| void DOMMessageQueue::RenderProcessGone(base::TerminationStatus status) { |
| VLOG(0) << "DOMMessageQueue::RenderProcessGone " << status; |
| switch (status) { |
| case base::TERMINATION_STATUS_NORMAL_TERMINATION: |
| case base::TERMINATION_STATUS_STILL_RUNNING: |
| break; |
| default: |
| renderer_crashed_ = true; |
| if (quit_closure_) |
| std::move(quit_closure_).Run(); |
| break; |
| } |
| } |
| |
| void DOMMessageQueue::ClearQueue() { |
| message_queue_ = base::queue<std::string>(); |
| } |
| |
| bool DOMMessageQueue::WaitForMessage(std::string* message) { |
| DCHECK(message); |
| if (!renderer_crashed_ && message_queue_.empty()) { |
| // This will be quit when a new message comes in. |
| base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); |
| quit_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| return PopMessage(message); |
| } |
| |
| bool DOMMessageQueue::PopMessage(std::string* message) { |
| DCHECK(message); |
| if (renderer_crashed_ || message_queue_.empty()) |
| return false; |
| *message = message_queue_.front(); |
| message_queue_.pop(); |
| return true; |
| } |
| |
| class WebContentsAddedObserver::RenderViewCreatedObserver |
| : public WebContentsObserver { |
| public: |
| explicit RenderViewCreatedObserver(WebContents* web_contents) |
| : WebContentsObserver(web_contents), |
| render_view_created_called_(false), |
| main_frame_created_called_(false) {} |
| |
| // WebContentsObserver: |
| void RenderViewCreated(RenderViewHost* rvh) override { |
| render_view_created_called_ = true; |
| } |
| |
| void RenderFrameCreated(RenderFrameHost* rfh) override { |
| if (rfh == web_contents()->GetMainFrame()) |
| main_frame_created_called_ = true; |
| } |
| |
| bool render_view_created_called_; |
| bool main_frame_created_called_; |
| }; |
| |
| WebContentsAddedObserver::WebContentsAddedObserver() |
| : web_contents_created_callback_( |
| base::Bind(&WebContentsAddedObserver::WebContentsCreated, |
| base::Unretained(this))), |
| web_contents_(nullptr) { |
| WebContentsImpl::FriendWrapper::AddCreatedCallbackForTesting( |
| web_contents_created_callback_); |
| } |
| |
| WebContentsAddedObserver::~WebContentsAddedObserver() { |
| WebContentsImpl::FriendWrapper::RemoveCreatedCallbackForTesting( |
| web_contents_created_callback_); |
| } |
| |
| void WebContentsAddedObserver::WebContentsCreated(WebContents* web_contents) { |
| DCHECK(!web_contents_); |
| web_contents_ = web_contents; |
| child_observer_.reset(new RenderViewCreatedObserver(web_contents)); |
| |
| if (quit_closure_) |
| std::move(quit_closure_).Run(); |
| } |
| |
| WebContents* WebContentsAddedObserver::GetWebContents() { |
| if (web_contents_) |
| return web_contents_; |
| |
| base::RunLoop run_loop; |
| quit_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| return web_contents_; |
| } |
| |
| bool WebContentsAddedObserver::RenderViewCreatedCalled() { |
| if (child_observer_) { |
| return child_observer_->render_view_created_called_ && |
| child_observer_->main_frame_created_called_; |
| } |
| return false; |
| } |
| |
| WebContentsDestroyedObserver::WebContentsDestroyedObserver( |
| WebContents* web_contents) |
| : WebContentsObserver(web_contents) { |
| DCHECK(web_contents); |
| } |
| |
| WebContentsDestroyedObserver::~WebContentsDestroyedObserver() {} |
| |
| void WebContentsDestroyedObserver::WebContentsDestroyed() { |
| destroyed_ = true; |
| } |
| |
| bool RequestFrame(WebContents* web_contents) { |
| DCHECK(web_contents); |
| return RenderWidgetHostImpl::From( |
| web_contents->GetRenderViewHost()->GetWidget()) |
| ->RequestRepaintForTesting(); |
| } |
| |
| RenderFrameSubmissionObserver::RenderFrameSubmissionObserver( |
| RenderFrameMetadataProvider* render_frame_metadata_provider) |
| : render_frame_metadata_provider_(render_frame_metadata_provider) { |
| render_frame_metadata_provider_->AddObserver(this); |
| render_frame_metadata_provider_->ReportAllFrameSubmissionsForTesting(true); |
| } |
| |
| RenderFrameSubmissionObserver::RenderFrameSubmissionObserver( |
| FrameTreeNode* node) |
| : RenderFrameSubmissionObserver( |
| RenderFrameMetadataProviderFromFrameTreeNode(node)) {} |
| |
| RenderFrameSubmissionObserver::RenderFrameSubmissionObserver( |
| WebContents* web_contents) |
| : RenderFrameSubmissionObserver( |
| RenderFrameMetadataProviderFromWebContents(web_contents)) {} |
| |
| RenderFrameSubmissionObserver::~RenderFrameSubmissionObserver() { |
| render_frame_metadata_provider_->RemoveObserver(this); |
| render_frame_metadata_provider_->ReportAllFrameSubmissionsForTesting(false); |
| } |
| |
| void RenderFrameSubmissionObserver::WaitForAnyFrameSubmission() { |
| break_on_any_frame_ = true; |
| Wait(); |
| break_on_any_frame_ = false; |
| } |
| |
| void RenderFrameSubmissionObserver::WaitForMetadataChange() { |
| Wait(); |
| } |
| |
| void RenderFrameSubmissionObserver::WaitForScrollOffset( |
| const gfx::Vector2dF& expected_offset) { |
| while (render_frame_metadata_provider_->LastRenderFrameMetadata() |
| .root_scroll_offset != expected_offset) { |
| WaitForMetadataChange(); |
| } |
| } |
| |
| void RenderFrameSubmissionObserver::WaitForScrollOffsetAtTop( |
| bool expected_scroll_offset_at_top) { |
| while (render_frame_metadata_provider_->LastRenderFrameMetadata() |
| .is_scroll_offset_at_top != expected_scroll_offset_at_top) { |
| WaitForMetadataChange(); |
| } |
| } |
| |
| const cc::RenderFrameMetadata& |
| RenderFrameSubmissionObserver::LastRenderFrameMetadata() const { |
| return render_frame_metadata_provider_->LastRenderFrameMetadata(); |
| } |
| |
| void RenderFrameSubmissionObserver::Quit() { |
| if (quit_closure_) |
| std::move(quit_closure_).Run(); |
| } |
| |
| void RenderFrameSubmissionObserver::Wait() { |
| base::RunLoop run_loop; |
| quit_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| void RenderFrameSubmissionObserver:: |
| OnRenderFrameMetadataChangedBeforeActivation( |
| const cc::RenderFrameMetadata& metadata) {} |
| |
| void RenderFrameSubmissionObserver:: |
| OnRenderFrameMetadataChangedAfterActivation() { |
| Quit(); |
| } |
| |
| void RenderFrameSubmissionObserver::OnRenderFrameSubmission() { |
| render_frame_count_++; |
| if (break_on_any_frame_) |
| Quit(); |
| } |
| |
| void RenderFrameSubmissionObserver::OnLocalSurfaceIdChanged( |
| const cc::RenderFrameMetadata& metadata) {} |
| |
| MainThreadFrameObserver::MainThreadFrameObserver( |
| RenderWidgetHost* render_widget_host) |
| : render_widget_host_(render_widget_host), |
| routing_id_(render_widget_host_->GetProcess()->GetNextRoutingID()) { |
| // TODO(lfg): We should look into adding a way to observe RenderWidgetHost |
| // messages similarly to what WebContentsObserver can do with RFH and RVW. |
| render_widget_host_->GetProcess()->AddRoute(routing_id_, this); |
| } |
| |
| MainThreadFrameObserver::~MainThreadFrameObserver() { |
| render_widget_host_->GetProcess()->RemoveRoute(routing_id_); |
| } |
| |
| void MainThreadFrameObserver::Wait() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| render_widget_host_->Send(new ViewMsg_WaitForNextFrameForTests( |
| render_widget_host_->GetRoutingID(), routing_id_)); |
| base::RunLoop run_loop; |
| quit_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| void MainThreadFrameObserver::Quit() { |
| if (quit_closure_) |
| std::move(quit_closure_).Run(); |
| } |
| |
| bool MainThreadFrameObserver::OnMessageReceived(const IPC::Message& msg) { |
| if (msg.type() == ViewHostMsg_WaitForNextFrameForTests_ACK::ID && |
| msg.routing_id() == routing_id_) { |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| base::BindOnce(&MainThreadFrameObserver::Quit, base::Unretained(this))); |
| } |
| return true; |
| } |
| |
| InputMsgWatcher::InputMsgWatcher(RenderWidgetHost* render_widget_host, |
| blink::WebInputEvent::Type type) |
| : render_widget_host_(render_widget_host), |
| wait_for_type_(type), |
| ack_result_(INPUT_EVENT_ACK_STATE_UNKNOWN), |
| ack_source_(InputEventAckSource::UNKNOWN) { |
| render_widget_host->AddInputEventObserver(this); |
| } |
| |
| InputMsgWatcher::~InputMsgWatcher() { |
| render_widget_host_->RemoveInputEventObserver(this); |
| } |
| |
| void InputMsgWatcher::OnInputEventAck(InputEventAckSource ack_source, |
| InputEventAckState ack_state, |
| const blink::WebInputEvent& event) { |
| if (event.GetType() == wait_for_type_) { |
| ack_result_ = ack_state; |
| ack_source_ = ack_source; |
| if (quit_closure_) |
| std::move(quit_closure_).Run(); |
| } |
| } |
| |
| bool InputMsgWatcher::HasReceivedAck() const { |
| return ack_result_ != INPUT_EVENT_ACK_STATE_UNKNOWN; |
| } |
| |
| InputEventAckState InputMsgWatcher::WaitForAck() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| base::RunLoop run_loop; |
| quit_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| return ack_result_; |
| } |
| |
| InputEventAckState InputMsgWatcher::GetAckStateWaitIfNecessary() { |
| if (HasReceivedAck()) |
| return ack_result_; |
| return WaitForAck(); |
| } |
| |
| InputEventAckWaiter::InputEventAckWaiter(RenderWidgetHost* render_widget_host, |
| InputEventAckPredicate predicate) |
| : render_widget_host_(render_widget_host), |
| predicate_(predicate), |
| event_received_(false) { |
| render_widget_host_->AddInputEventObserver(this); |
| } |
| |
| namespace { |
| InputEventAckWaiter::InputEventAckPredicate EventAckHasType( |
| blink::WebInputEvent::Type type) { |
| return base::BindRepeating( |
| [](blink::WebInputEvent::Type expected_type, InputEventAckSource source, |
| InputEventAckState state, const blink::WebInputEvent& event) { |
| return event.GetType() == expected_type; |
| }, |
| type); |
| } |
| } // namespace |
| |
| InputEventAckWaiter::InputEventAckWaiter(RenderWidgetHost* render_widget_host, |
| blink::WebInputEvent::Type type) |
| : InputEventAckWaiter(render_widget_host, EventAckHasType(type)) {} |
| |
| InputEventAckWaiter::~InputEventAckWaiter() { |
| render_widget_host_->RemoveInputEventObserver(this); |
| } |
| |
| void InputEventAckWaiter::Wait() { |
| if (!event_received_) { |
| base::RunLoop run_loop; |
| quit_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| } |
| |
| void InputEventAckWaiter::Reset() { |
| event_received_ = false; |
| quit_closure_ = base::OnceClosure(); |
| } |
| |
| void InputEventAckWaiter::OnInputEventAck(InputEventAckSource source, |
| InputEventAckState state, |
| const blink::WebInputEvent& event) { |
| if (predicate_.Run(source, state, event)) { |
| event_received_ = true; |
| if (quit_closure_) |
| std::move(quit_closure_).Run(); |
| } |
| } |
| |
| // TODO(dcheng): Make the test clipboard on different threads share the |
| // same backing store. crbug.com/629765 |
| // TODO(slangley): crbug.com/775830 - Cleanup BrowserTestClipboardScope now that |
| // there is no need to thread hop for Windows. |
| BrowserTestClipboardScope::BrowserTestClipboardScope() { |
| ui::TestClipboard::CreateForCurrentThread(); |
| } |
| |
| BrowserTestClipboardScope::~BrowserTestClipboardScope() { |
| ui::Clipboard::DestroyClipboardForCurrentThread(); |
| } |
| |
| void BrowserTestClipboardScope::SetRtf(const std::string& rtf) { |
| ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_COPY_PASTE); |
| clipboard_writer.WriteRTF(rtf); |
| } |
| |
| void BrowserTestClipboardScope::SetText(const std::string& text) { |
| ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_COPY_PASTE); |
| clipboard_writer.WriteText(base::ASCIIToUTF16(text)); |
| } |
| |
| void BrowserTestClipboardScope::GetText(std::string* result) { |
| ui::Clipboard::GetForCurrentThread()->ReadAsciiText( |
| ui::CLIPBOARD_TYPE_COPY_PASTE, result); |
| } |
| |
| class FrameFocusedObserver::FrameTreeNodeObserverImpl |
| : public FrameTreeNode::Observer { |
| public: |
| explicit FrameTreeNodeObserverImpl(FrameTreeNode* owner) : owner_(owner) { |
| owner->AddObserver(this); |
| } |
| ~FrameTreeNodeObserverImpl() override { owner_->RemoveObserver(this); } |
| |
| void Run() { run_loop_.Run(); } |
| |
| void OnFrameTreeNodeFocused(FrameTreeNode* node) override { |
| if (node == owner_) |
| run_loop_.Quit(); |
| } |
| |
| private: |
| FrameTreeNode* owner_; |
| base::RunLoop run_loop_; |
| }; |
| |
| FrameFocusedObserver::FrameFocusedObserver(RenderFrameHost* owner_host) |
| : impl_(new FrameTreeNodeObserverImpl( |
| static_cast<RenderFrameHostImpl*>(owner_host)->frame_tree_node())) {} |
| |
| FrameFocusedObserver::~FrameFocusedObserver() {} |
| |
| void FrameFocusedObserver::Wait() { |
| impl_->Run(); |
| } |
| |
| class FrameDeletedObserver::FrameTreeNodeObserverImpl |
| : public FrameTreeNode::Observer { |
| public: |
| explicit FrameTreeNodeObserverImpl(FrameTreeNode* owner) : owner_(owner) { |
| owner->AddObserver(this); |
| } |
| ~FrameTreeNodeObserverImpl() override = default; |
| |
| void Run() { run_loop_.Run(); } |
| |
| private: |
| // FrameTreeNode::Observer |
| void OnFrameTreeNodeDestroyed(FrameTreeNode* node) override { |
| if (node == owner_) |
| run_loop_.Quit(); |
| } |
| |
| FrameTreeNode* owner_; |
| base::RunLoop run_loop_; |
| }; |
| |
| FrameDeletedObserver::FrameDeletedObserver(RenderFrameHost* owner_host) |
| : impl_(new FrameTreeNodeObserverImpl( |
| static_cast<RenderFrameHostImpl*>(owner_host)->frame_tree_node())) {} |
| |
| FrameDeletedObserver::~FrameDeletedObserver() = default; |
| |
| void FrameDeletedObserver::Wait() { |
| impl_->Run(); |
| } |
| |
| TestNavigationManager::TestNavigationManager(WebContents* web_contents, |
| const GURL& url) |
| : WebContentsObserver(web_contents), |
| url_(url), |
| handle_(nullptr), |
| navigation_paused_(false), |
| current_state_(NavigationState::INITIAL), |
| desired_state_(NavigationState::STARTED), |
| weak_factory_(this) {} |
| |
| TestNavigationManager::~TestNavigationManager() { |
| if (navigation_paused_) |
| handle_->CallResumeForTesting(); |
| } |
| |
| bool TestNavigationManager::WaitForRequestStart() { |
| // This is the default desired state. In PlzNavigate, a browser-initiated |
| // navigation can reach this state synchronously, so the TestNavigationManager |
| // is set to always pause navigations at WillStartRequest. This ensures the |
| // user can always call WaitForWillStartRequest. |
| DCHECK(desired_state_ == NavigationState::STARTED); |
| return WaitForDesiredState(); |
| } |
| |
| void TestNavigationManager::ResumeNavigation() { |
| DCHECK(current_state_ == NavigationState::STARTED || |
| current_state_ == NavigationState::RESPONSE); |
| DCHECK_EQ(current_state_, desired_state_); |
| DCHECK(navigation_paused_); |
| navigation_paused_ = false; |
| handle_->CallResumeForTesting(); |
| } |
| |
| NavigationHandle* TestNavigationManager::GetNavigationHandle() { |
| return handle_; |
| } |
| |
| bool TestNavigationManager::WaitForResponse() { |
| desired_state_ = NavigationState::RESPONSE; |
| return WaitForDesiredState(); |
| } |
| |
| void TestNavigationManager::WaitForNavigationFinished() { |
| desired_state_ = NavigationState::FINISHED; |
| WaitForDesiredState(); |
| } |
| |
| void TestNavigationManager::DidStartNavigation(NavigationHandle* handle) { |
| if (!ShouldMonitorNavigation(handle)) |
| return; |
| |
| handle_ = handle; |
| std::unique_ptr<NavigationThrottle> throttle( |
| new TestNavigationManagerThrottle( |
| handle_, base::Bind(&TestNavigationManager::OnWillStartRequest, |
| weak_factory_.GetWeakPtr()), |
| base::Bind(&TestNavigationManager::OnWillProcessResponse, |
| weak_factory_.GetWeakPtr()))); |
| handle_->RegisterThrottleForTesting(std::move(throttle)); |
| } |
| |
| void TestNavigationManager::DidFinishNavigation(NavigationHandle* handle) { |
| if (handle != handle_) |
| return; |
| was_successful_ = handle->HasCommitted() && !handle->IsErrorPage(); |
| current_state_ = NavigationState::FINISHED; |
| navigation_paused_ = false; |
| handle_ = nullptr; |
| OnNavigationStateChanged(); |
| } |
| |
| void TestNavigationManager::OnWillStartRequest() { |
| current_state_ = NavigationState::STARTED; |
| navigation_paused_ = true; |
| OnNavigationStateChanged(); |
| } |
| |
| void TestNavigationManager::OnWillProcessResponse() { |
| current_state_ = NavigationState::RESPONSE; |
| navigation_paused_ = true; |
| OnNavigationStateChanged(); |
| } |
| |
| // TODO(csharrison): Remove CallResumeForTesting method calls in favor of doing |
| // it through the throttle. |
| bool TestNavigationManager::WaitForDesiredState() { |
| // If the desired state has laready been reached, just return. |
| if (current_state_ == desired_state_) |
| return true; |
| |
| // Resume the navigation if it was paused. |
| if (navigation_paused_) |
| handle_->CallResumeForTesting(); |
| |
| // Wait for the desired state if needed. |
| if (current_state_ < desired_state_) { |
| DCHECK(!quit_closure_); |
| base::RunLoop run_loop; |
| quit_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| // Return false if the navigation did not reach the state specified by the |
| // user. |
| return current_state_ == desired_state_; |
| } |
| |
| void TestNavigationManager::OnNavigationStateChanged() { |
| // If the state the user was waiting for has been reached, exit the message |
| // loop. |
| if (current_state_ >= desired_state_) { |
| if (quit_closure_) |
| std::move(quit_closure_).Run(); |
| return; |
| } |
| |
| // Otherwise, the navigation should be resumed if it was previously paused. |
| if (navigation_paused_) |
| handle_->CallResumeForTesting(); |
| } |
| |
| bool TestNavigationManager::ShouldMonitorNavigation(NavigationHandle* handle) { |
| if (handle_ || handle->GetURL() != url_) |
| return false; |
| if (current_state_ != NavigationState::INITIAL) |
| return false; |
| return true; |
| } |
| |
| NavigationHandleCommitObserver::NavigationHandleCommitObserver( |
| content::WebContents* web_contents, |
| const GURL& url) |
| : content::WebContentsObserver(web_contents), |
| url_(url), |
| has_committed_(false), |
| was_same_document_(false), |
| was_renderer_initiated_(false) {} |
| |
| void NavigationHandleCommitObserver::DidFinishNavigation( |
| content::NavigationHandle* handle) { |
| if (handle->GetURL() != url_) |
| return; |
| has_committed_ = true; |
| was_same_document_ = handle->IsSameDocument(); |
| was_renderer_initiated_ = handle->IsRendererInitiated(); |
| } |
| |
| ConsoleObserverDelegate::ConsoleObserverDelegate(WebContents* web_contents, |
| const std::string& filter) |
| : web_contents_(web_contents), filter_(filter) {} |
| |
| ConsoleObserverDelegate::~ConsoleObserverDelegate() {} |
| |
| void ConsoleObserverDelegate::Wait() { |
| run_loop_.Run(); |
| } |
| |
| bool ConsoleObserverDelegate::DidAddMessageToConsole( |
| WebContents* source, |
| int32_t level, |
| const base::string16& message, |
| int32_t line_no, |
| const base::string16& source_id) { |
| DCHECK(source == web_contents_); |
| |
| std::string ascii_message = base::UTF16ToASCII(message); |
| if (base::MatchPattern(ascii_message, filter_)) { |
| message_ = ascii_message; |
| run_loop_.Quit(); |
| } |
| return false; |
| } |
| |
| // static |
| void PwnMessageHelper::RegisterBlobURL(RenderProcessHost* process, |
| GURL url, |
| std::string uuid) { |
| IPC::IpcSecurityTestUtil::PwnMessageReceived( |
| process->GetChannel(), BlobHostMsg_RegisterPublicURL(url, uuid)); |
| } |
| |
| // static |
| void PwnMessageHelper::FileSystemCreate(RenderProcessHost* process, |
| int request_id, |
| GURL path, |
| bool exclusive, |
| bool is_directory, |
| bool recursive) { |
| TestFileapiOperationWaiter waiter( |
| process->GetStoragePartition()->GetFileSystemContext()); |
| |
| IPC::IpcSecurityTestUtil::PwnMessageReceived( |
| process->GetChannel(), |
| FileSystemHostMsg_Create(request_id, path, exclusive, is_directory, |
| recursive)); |
| |
| // If this started an async operation, wait for it to complete. |
| if (waiter.did_start_update()) |
| waiter.WaitForEndUpdate(); |
| } |
| |
| // static |
| void PwnMessageHelper::FileSystemWrite(RenderProcessHost* process, |
| int request_id, |
| GURL file_path, |
| std::string blob_uuid, |
| int64_t position) { |
| TestFileapiOperationWaiter waiter( |
| process->GetStoragePartition()->GetFileSystemContext()); |
| |
| IPC::IpcSecurityTestUtil::PwnMessageReceived( |
| process->GetChannel(), |
| FileSystemHostMsg_Write(request_id, file_path, blob_uuid, position)); |
| |
| // If this started an async operation, wait for it to complete. |
| if (waiter.did_start_update()) |
| waiter.WaitForEndUpdate(); |
| } |
| |
| void PwnMessageHelper::LockMouse(RenderProcessHost* process, |
| int routing_id, |
| bool user_gesture, |
| bool privileged) { |
| IPC::IpcSecurityTestUtil::PwnMessageReceived( |
| process->GetChannel(), |
| ViewHostMsg_LockMouse(routing_id, user_gesture, privileged)); |
| } |
| |
| #if defined(USE_AURA) |
| namespace { |
| class MockOverscrollControllerImpl : public OverscrollController, |
| public MockOverscrollController { |
| public: |
| MockOverscrollControllerImpl() : content_scrolling_(false) {} |
| ~MockOverscrollControllerImpl() override {} |
| |
| // OverscrollController: |
| void ReceivedEventACK(const blink::WebInputEvent& event, |
| bool processed) override { |
| // Since we're only mocking this one method of OverscrollController and its |
| // other methods are non-virtual, we'll delegate to it so that it doesn't |
| // get into an inconsistent state. |
| OverscrollController::ReceivedEventACK(event, processed); |
| |
| if (event.GetType() == blink::WebInputEvent::kGestureScrollUpdate && |
| processed) { |
| content_scrolling_ = true; |
| if (quit_closure_) |
| std::move(quit_closure_).Run(); |
| } |
| } |
| |
| // MockOverscrollController: |
| void WaitForConsumedScroll() override { |
| if (!content_scrolling_) { |
| base::RunLoop run_loop; |
| quit_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| } |
| |
| private: |
| bool content_scrolling_; |
| base::OnceClosure quit_closure_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockOverscrollControllerImpl); |
| }; |
| } // namespace |
| |
| // static |
| MockOverscrollController* MockOverscrollController::Create( |
| RenderWidgetHostView* rwhv) { |
| std::unique_ptr<MockOverscrollControllerImpl> mock = |
| std::make_unique<MockOverscrollControllerImpl>(); |
| MockOverscrollController* raw_mock = mock.get(); |
| |
| RenderWidgetHostViewAura* rwhva = |
| static_cast<RenderWidgetHostViewAura*>(rwhv); |
| rwhva->SetOverscrollControllerForTesting(std::move(mock)); |
| |
| return raw_mock; |
| } |
| |
| #endif // defined(USE_AURA) |
| |
| ContextMenuFilter::ContextMenuFilter() |
| : content::BrowserMessageFilter(FrameMsgStart), |
| run_loop_(new base::RunLoop), |
| quit_closure_(run_loop_->QuitClosure()) {} |
| |
| bool ContextMenuFilter::OnMessageReceived(const IPC::Message& message) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| if (message.type() == FrameHostMsg_ContextMenu::ID) { |
| FrameHostMsg_ContextMenu::Param params; |
| FrameHostMsg_ContextMenu::Read(&message, ¶ms); |
| content::ContextMenuParams menu_params = std::get<0>(params); |
| content::BrowserThread::PostTask( |
| content::BrowserThread::UI, FROM_HERE, |
| base::BindOnce(&ContextMenuFilter::OnContextMenu, this, menu_params)); |
| } |
| return false; |
| } |
| |
| void ContextMenuFilter::Wait() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| run_loop_->Run(); |
| run_loop_ = nullptr; |
| } |
| |
| ContextMenuFilter::~ContextMenuFilter() {} |
| |
| void ContextMenuFilter::OnContextMenu( |
| const content::ContextMenuParams& params) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| last_params_ = params; |
| std::move(quit_closure_).Run(); |
| } |
| |
| WebContents* GetEmbedderForGuest(content::WebContents* guest) { |
| CHECK(guest); |
| return static_cast<content::WebContentsImpl*>(guest)->GetOuterWebContents(); |
| } |
| |
| bool IsNetworkServiceRunningInProcess() { |
| return base::FeatureList::IsEnabled(network::features::kNetworkService) && |
| (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kSingleProcess) || |
| base::FeatureList::IsEnabled(features::kNetworkServiceInProcess)); |
| } |
| |
| int LoadBasicRequest(network::mojom::NetworkContext* network_context, |
| const GURL& url, |
| int process_id, |
| int render_frame_id) { |
| network::mojom::URLLoaderFactoryPtr url_loader_factory; |
| network::mojom::URLLoaderFactoryParamsPtr url_loader_factory_params = |
| network::mojom::URLLoaderFactoryParams::New(); |
| url_loader_factory_params->process_id = process_id; |
| url_loader_factory_params->is_corb_enabled = false; |
| network_context->CreateURLLoaderFactory(MakeRequest(&url_loader_factory), |
| std::move(url_loader_factory_params)); |
| // |url_loader_factory| will receive error notification asynchronously if |
| // |network_context| has already encountered error. However it's still false |
| // at this point. |
| EXPECT_FALSE(url_loader_factory.encountered_error()); |
| |
| auto request = std::make_unique<network::ResourceRequest>(); |
| request->url = url; |
| request->render_frame_id = render_frame_id; |
| |
| content::SimpleURLLoaderTestHelper simple_loader_helper; |
| std::unique_ptr<network::SimpleURLLoader> simple_loader = |
| network::SimpleURLLoader::Create(std::move(request), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie( |
| url_loader_factory.get(), simple_loader_helper.GetCallback()); |
| simple_loader_helper.WaitForCallback(); |
| |
| return simple_loader->NetError(); |
| } |
| |
| void EnsureCookiesFlushed(BrowserContext* browser_context) { |
| BrowserContext::ForEachStoragePartition( |
| browser_context, base::BindRepeating([](StoragePartition* partition) { |
| base::RunLoop run_loop; |
| partition->GetCookieManagerForBrowserProcess()->FlushCookieStore( |
| run_loop.QuitClosure()); |
| run_loop.Run(); |
| })); |
| } |
| |
| bool HasValidProcessForProcessGroup(const std::string& process_group_name) { |
| return ServiceManagerContext::HasValidProcessForProcessGroup( |
| process_group_name); |
| } |
| |
| bool TestChildOrGuestAutoresize(bool is_guest, |
| RenderProcessHost* embedder_rph, |
| RenderWidgetHost* guest_rwh) { |
| RenderProcessHostImpl* embedder_rph_impl = |
| static_cast<RenderProcessHostImpl*>(embedder_rph); |
| RenderWidgetHostImpl* guest_rwh_impl = |
| static_cast<RenderWidgetHostImpl*>(guest_rwh); |
| |
| scoped_refptr<SynchronizeVisualPropertiesMessageFilter> filter( |
| new SynchronizeVisualPropertiesMessageFilter()); |
| |
| // Register the message filter for the guest or child. For guest, we must use |
| // a special hook, as there are already message filters installed which will |
| // supercede us. |
| if (is_guest) { |
| embedder_rph_impl->SetBrowserPluginMessageFilterSubFilterForTesting( |
| filter.get()); |
| } else { |
| embedder_rph_impl->AddFilter(filter.get()); |
| } |
| |
| viz::LocalSurfaceId current_id = |
| guest_rwh_impl->GetView()->GetLocalSurfaceId(); |
| // The guest may not yet be fully attached / initted. If not, |current_id| |
| // will be invalid, and we should wait for an ID before proceeding. |
| if (!current_id.is_valid()) |
| current_id = filter->WaitForSurfaceId(); |
| |
| // Enable auto-resize. |
| gfx::Size min_size(10, 10); |
| gfx::Size max_size(100, 100); |
| guest_rwh_impl->SetAutoResize(true, min_size, max_size); |
| guest_rwh_impl->GetView()->EnableAutoResize(min_size, max_size); |
| |
| // Enabling auto resize generates a surface ID, wait for it. |
| current_id = filter->WaitForSurfaceId(); |
| |
| // Fake an auto-resize update. |
| viz::LocalSurfaceId local_surface_id(current_id.parent_sequence_number(), |
| current_id.child_sequence_number() + 1, |
| current_id.embed_token()); |
| cc::RenderFrameMetadata metadata; |
| metadata.viewport_size_in_pixels = gfx::Size(75, 75); |
| metadata.local_surface_id = local_surface_id; |
| guest_rwh_impl->DidUpdateVisualProperties(metadata); |
| |
| // This won't generate a response, as we short-circuit auto-resizes, so cause |
| // an additional update by disabling auto-resize. |
| guest_rwh_impl->GetView()->DisableAutoResize(gfx::Size(75, 75)); |
| |
| // Get the first delivered surface id and ensure it has the surface id which |
| // we expect. |
| return filter->WaitForSurfaceId() == |
| viz::LocalSurfaceId(current_id.parent_sequence_number() + 1, |
| current_id.child_sequence_number() + 1, |
| current_id.embed_token()); |
| } |
| |
| const uint32_t |
| SynchronizeVisualPropertiesMessageFilter::kMessageClassesToFilter[2] = { |
| FrameMsgStart, BrowserPluginMsgStart}; |
| |
| SynchronizeVisualPropertiesMessageFilter:: |
| SynchronizeVisualPropertiesMessageFilter() |
| : content::BrowserMessageFilter(kMessageClassesToFilter, |
| arraysize(kMessageClassesToFilter)), |
| screen_space_rect_run_loop_(std::make_unique<base::RunLoop>()), |
| screen_space_rect_received_(false) {} |
| |
| void SynchronizeVisualPropertiesMessageFilter::WaitForRect() { |
| screen_space_rect_run_loop_->Run(); |
| } |
| |
| void SynchronizeVisualPropertiesMessageFilter::ResetRectRunLoop() { |
| last_rect_ = gfx::Rect(); |
| screen_space_rect_run_loop_.reset(new base::RunLoop); |
| screen_space_rect_received_ = false; |
| } |
| |
| viz::FrameSinkId SynchronizeVisualPropertiesMessageFilter::GetOrWaitForId() { |
| // No-op if already quit. |
| frame_sink_id_run_loop_.Run(); |
| return frame_sink_id_; |
| } |
| |
| viz::LocalSurfaceId |
| SynchronizeVisualPropertiesMessageFilter::WaitForSurfaceId() { |
| surface_id_run_loop_.reset(new base::RunLoop); |
| surface_id_run_loop_->Run(); |
| return last_surface_id_; |
| } |
| |
| SynchronizeVisualPropertiesMessageFilter:: |
| ~SynchronizeVisualPropertiesMessageFilter() {} |
| |
| void SynchronizeVisualPropertiesMessageFilter:: |
| OnSynchronizeFrameHostVisualProperties( |
| const viz::SurfaceId& surface_id, |
| const FrameVisualProperties& resize_params) { |
| OnSynchronizeVisualProperties(surface_id.local_surface_id(), |
| surface_id.frame_sink_id(), resize_params); |
| } |
| |
| void SynchronizeVisualPropertiesMessageFilter:: |
| OnSynchronizeBrowserPluginVisualProperties( |
| int browser_plugin_guest_instance_id, |
| viz::LocalSurfaceId surface_id, |
| FrameVisualProperties resize_params) { |
| OnSynchronizeVisualProperties(surface_id, viz::FrameSinkId(), resize_params); |
| } |
| |
| void SynchronizeVisualPropertiesMessageFilter::OnSynchronizeVisualProperties( |
| const viz::LocalSurfaceId& local_surface_id, |
| const viz::FrameSinkId& frame_sink_id, |
| const FrameVisualProperties& resize_params) { |
| gfx::Rect screen_space_rect_in_dip = resize_params.screen_space_rect; |
| if (IsUseZoomForDSFEnabled()) { |
| screen_space_rect_in_dip = |
| gfx::Rect(gfx::ScaleToFlooredPoint( |
| resize_params.screen_space_rect.origin(), |
| 1.f / resize_params.screen_info.device_scale_factor), |
| gfx::ScaleToCeiledSize( |
| resize_params.screen_space_rect.size(), |
| 1.f / resize_params.screen_info.device_scale_factor)); |
| } |
| // Track each rect updates. |
| content::BrowserThread::PostTask( |
| content::BrowserThread::UI, FROM_HERE, |
| base::BindOnce( |
| &SynchronizeVisualPropertiesMessageFilter::OnUpdatedFrameRectOnUI, |
| this, screen_space_rect_in_dip)); |
| |
| // Track each surface id update. |
| content::BrowserThread::PostTask( |
| content::BrowserThread::UI, FROM_HERE, |
| base::BindOnce( |
| &SynchronizeVisualPropertiesMessageFilter::OnUpdatedSurfaceIdOnUI, |
| this, local_surface_id)); |
| |
| // Record the received value. We cannot check the current state of the child |
| // frame, as it can only be processed on the UI thread, and we cannot block |
| // here. |
| frame_sink_id_ = frame_sink_id; |
| |
| // There can be several updates before a valid viz::FrameSinkId is ready. Do |
| // not quit |run_loop_| until after we receive a valid one. |
| if (!frame_sink_id_.is_valid()) |
| return; |
| |
| // We can't nest on the IO thread. So tests will wait on the UI thread, so |
| // post there to exit the nesting. |
| content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI) |
| ->PostTask(FROM_HERE, |
| base::BindOnce(&SynchronizeVisualPropertiesMessageFilter:: |
| OnUpdatedFrameSinkIdOnUI, |
| this)); |
| } |
| |
| void SynchronizeVisualPropertiesMessageFilter::OnUpdatedFrameRectOnUI( |
| const gfx::Rect& rect) { |
| last_rect_ = rect; |
| if (!screen_space_rect_received_) { |
| screen_space_rect_received_ = true; |
| // Tests looking at the rect currently expect all received input to finish |
| // processing before the test continutes. |
| screen_space_rect_run_loop_->QuitWhenIdle(); |
| } |
| } |
| |
| void SynchronizeVisualPropertiesMessageFilter::OnUpdatedFrameSinkIdOnUI() { |
| frame_sink_id_run_loop_.Quit(); |
| } |
| |
| void SynchronizeVisualPropertiesMessageFilter::OnUpdatedSurfaceIdOnUI( |
| viz::LocalSurfaceId surface_id) { |
| last_surface_id_ = surface_id; |
| if (surface_id_run_loop_) { |
| surface_id_run_loop_->QuitWhenIdle(); |
| } |
| } |
| |
| bool SynchronizeVisualPropertiesMessageFilter::OnMessageReceived( |
| const IPC::Message& message) { |
| IPC_BEGIN_MESSAGE_MAP(SynchronizeVisualPropertiesMessageFilter, message) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_SynchronizeVisualProperties, |
| OnSynchronizeFrameHostVisualProperties) |
| IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SynchronizeVisualProperties, |
| OnSynchronizeBrowserPluginVisualProperties) |
| IPC_END_MESSAGE_MAP() |
| |
| // We do not consume the message, so that we can verify the effects of it |
| // being processed. |
| return false; |
| } |
| |
| } // namespace content |