diff options
author | erg <erg@chromium.org> | 2015-10-05 21:30:38 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-10-06 04:33:07 +0000 |
commit | fe308b3359314164d5a74633dfbcf52c6367630b (patch) | |
tree | d16c9bcbf9b7fd7887a22a8e09f4f70dad042359 /components/web_view | |
parent | 6d5f60a81ff97645015695c4289523d0007defa4 (diff) | |
download | chromium_src-fe308b3359314164d5a74633dfbcf52c6367630b.zip chromium_src-fe308b3359314164d5a74633dfbcf52c6367630b.tar.gz chromium_src-fe308b3359314164d5a74633dfbcf52c6367630b.tar.bz2 |
mandoline: Add find in page.
This adds Ctrl+F as a keyboard shortcut to replace the current toolbar
with a find in page bar, where the user can enter some text.
BUG=496978
Review URL: https://codereview.chromium.org/1371773003
Cr-Commit-Position: refs/heads/master@{#352535}
Diffstat (limited to 'components/web_view')
-rw-r--r-- | components/web_view/BUILD.gn | 2 | ||||
-rw-r--r-- | components/web_view/find_controller.cc | 155 | ||||
-rw-r--r-- | components/web_view/find_controller.h | 78 | ||||
-rw-r--r-- | components/web_view/find_controller_delegate.h | 29 | ||||
-rw-r--r-- | components/web_view/frame.cc | 33 | ||||
-rw-r--r-- | components/web_view/frame.h | 15 | ||||
-rw-r--r-- | components/web_view/frame_apptest.cc | 8 | ||||
-rw-r--r-- | components/web_view/frame_tree_delegate.cc | 8 | ||||
-rw-r--r-- | components/web_view/frame_tree_delegate.h | 9 | ||||
-rw-r--r-- | components/web_view/public/interfaces/frame.mojom | 39 | ||||
-rw-r--r-- | components/web_view/public/interfaces/web_view.mojom | 11 | ||||
-rw-r--r-- | components/web_view/test_runner/test_runner_application_delegate.h | 5 | ||||
-rw-r--r-- | components/web_view/web_view_apptest.cc | 65 | ||||
-rw-r--r-- | components/web_view/web_view_impl.cc | 56 | ||||
-rw-r--r-- | components/web_view/web_view_impl.h | 22 |
15 files changed, 526 insertions, 9 deletions
diff --git a/components/web_view/BUILD.gn b/components/web_view/BUILD.gn index 5b634e2..f86231c 100644 --- a/components/web_view/BUILD.gn +++ b/components/web_view/BUILD.gn @@ -15,6 +15,8 @@ source_set("lib") { sources = [ "client_initiated_frame_connection.cc", "client_initiated_frame_connection.h", + "find_controller.cc", + "find_controller.h", "frame.cc", "frame.h", "frame_connection.cc", diff --git a/components/web_view/find_controller.cc b/components/web_view/find_controller.cc new file mode 100644 index 0000000..d129d6f --- /dev/null +++ b/components/web_view/find_controller.cc @@ -0,0 +1,155 @@ +// Copyright 2015 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 "components/web_view/find_controller.h" + +#include "base/bind.h" +#include "components/web_view/find_controller_delegate.h" +#include "components/web_view/frame.h" + +namespace web_view { + +FindController::FindController(FindControllerDelegate* delegate) + : delegate_(delegate), current_find_request_(-1), weak_ptr_factory_(this) {} + +FindController::~FindController() {} + +void FindController::Find(int32_t request_id, const mojo::String& search_text) { + // TODO(erg): While this deals with multiple frames, it does not deal with + // going forward or backwards. To do that, we'll have to port all frame + // traversal and focusing concepts from blink::WebFrame to mojo::Frame. + + // TODO(erg): This isn't great and causes flashes on character + // entry. However, it's needed for now because the internals of TextFinder + // still track the entire state of the blink frame tree, and if there are any + // frames that have marked text, doing a find clears the results of all + // frames _except_ for the first frame that it finds a result on. + StopFinding(); + + // TODO(erg): This cheap method does not traverse in the order that blink + // does. + pending_find_frames_ = delegate_->GetAllFrames(); + + current_find_request_ = request_id; + returned_find_data_.clear(); + + // Prime the continue loop. + OnContinueFinding(request_id, search_text, false); +} + +void FindController::StopFinding() { + // Don't report any callbacks that we get after this. + current_find_request_ = -1; + + for (Frame* f : delegate_->GetAllFrames()) + f->StopFinding(true); +} + +void FindController::OnFindInFrameCountUpdated(int32_t request_id, + Frame* frame, + int32_t count, + bool final_update) { + if (request_id != current_find_request_) + return; + + auto it = returned_find_data_.find(frame); + if (it == returned_find_data_.end()) { + NOTREACHED(); + return; + } + + it->second.count = count; + it->second.final_update = final_update; + + int merged_count = 0; + bool merged_final_update = true; + for (auto const& data : returned_find_data_) { + merged_count += data.second.count; + merged_final_update = merged_final_update && data.second.final_update; + } + + // We can now take the individual FindInFrame messages and construct a + // FindInPage message. + delegate_->GetWebViewClient()->FindInPageMatchCountUpdated( + request_id, merged_count, merged_final_update); +} + +void FindController::OnFindInPageSelectionUpdated( + int32_t request_id, + Frame* frame, + int32_t active_match_ordinal) { + if (request_id != current_find_request_) + return; + + // TODO(erg): This is the one that's really hard. To give an accurate count + // here, we need to have all the results for frames that are before the Frame + // that contains the selected match so we can add their sums together. + // + // Thankfully, we don't have to worry about this now. Since there aren't + // back/forward controls yet, active_match_ordinal will always be 1. + delegate_->GetWebViewClient()->FindInPageSelectionUpdated( + request_id, active_match_ordinal); +} + +void FindController::DidDestroyFrame(Frame* frame) { + auto it = + find(pending_find_frames_.begin(), pending_find_frames_.end(), frame); + if (it != pending_find_frames_.end()) + pending_find_frames_.erase(it); +} + +void FindController::OnContinueFinding(int32_t request_id, + const mojo::String& search_text, + bool found) { + if (!found && !pending_find_frames_.empty()) { + // No match found, search on the next frame. + Frame* next_frame = pending_find_frames_.front(); + pending_find_frames_.pop_front(); + next_frame->Find( + request_id, search_text, + base::Bind(&FindController::OnContinueFinding, + weak_ptr_factory_.GetWeakPtr(), request_id, search_text)); + + // TODO(erg): This doesn't deal with wrapping around the document at the + // end when there are multiple frames. + return; + } + + pending_find_frames_.clear(); + + // We either found a match or we got the final rejection. Either way, we + // alert our caller. + + // If nothing is found, set result to "0 of 0", otherwise, set it to + // "-1 of 1" to indicate that we found at least one item, but we don't know + // yet what is active. + int ordinal = found ? -1 : 0; // -1 here means, we might know more later. + int match_count = found ? 1 : 0; // 1 here means possibly more coming. + + // If we find no matches then this will be our last status update. + // Otherwise the scoping effort will send more results. + bool final_status_update = !found; + + // Send priming messages. + delegate_->GetWebViewClient()->FindInPageSelectionUpdated(request_id, + ordinal); + delegate_->GetWebViewClient()->FindInPageMatchCountUpdated( + request_id, match_count, final_status_update); + + // TODO(erg): This doesn't iterate in the same order as the current code + // because we don't have the correct iteration primitives. + std::deque<Frame*> frames = delegate_->GetAllFrames(); + for (Frame* f : frames) { + f->StopHighlightingFindResults(); + + if (found) { + MatchData& match_data = returned_find_data_[f]; + match_data.count = 0; + match_data.final_update = false; + f->HighlightFindResults(request_id, search_text, true); + } + } +} + +} // namespace web_view diff --git a/components/web_view/find_controller.h b/components/web_view/find_controller.h new file mode 100644 index 0000000..186d393 --- /dev/null +++ b/components/web_view/find_controller.h @@ -0,0 +1,78 @@ +// Copyright 2015 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. + +#ifndef COMPONENTS_WEB_VIEW_FIND_CONTROLLER_H_ +#define COMPONENTS_WEB_VIEW_FIND_CONTROLLER_H_ + +#include <deque> + +#include "base/basictypes.h" +#include "base/memory/weak_ptr.h" +#include "components/web_view/public/interfaces/web_view.mojom.h" + +namespace web_view { + +class FindControllerDelegate; +class Frame; + +// Contains all the find code used by WebViewImpl. +class FindController { + public: + FindController(FindControllerDelegate* delegate); + ~FindController(); + + // Starts a find session looking for |search_text|. This method will first + // scan through frames one by one to look for the first instance of + // |search_text|, and return the data through + // OnFindInPageSelectionUpdated(). If found, it will highlight all instances + // of the text and report the final total count through + // FindInPageMatchCountUpdated(). + void Find(int32_t request_id, const mojo::String& search_text); + + // Unhighlights all find instances on the page. + void StopFinding(); + + void OnFindInFrameCountUpdated(int32_t request_id, + Frame* frame, + int32_t count, + bool final_update); + void OnFindInPageSelectionUpdated(int32_t request_id, + Frame* frame, + int32_t active_match_ordinal); + + void DidDestroyFrame(Frame* frame); + + private: + struct MatchData { + int count; + bool final_update; + }; + + // Callback method invoked by Find(). + void OnContinueFinding(int32_t request_id, + const mojo::String& search_text, + bool found); + + // Our owner. + FindControllerDelegate* delegate_; + + // A list of Frames which we have not sent a Find() command to. Initialized + // in Find(), and read from OnContinueFinding(). + std::deque<Frame*> pending_find_frames_; + + // Current find session number. We keep track of this to prevent recording + // stale callbacks. + int current_find_request_; + + // The current callback data from various frames. + std::map<Frame*, MatchData> returned_find_data_; + + base::WeakPtrFactory<FindController> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(FindController); +}; + +} // namespace web_view + +#endif // COMPONENTS_WEB_VIEW_FIND_CONTROLLER_H_ diff --git a/components/web_view/find_controller_delegate.h b/components/web_view/find_controller_delegate.h new file mode 100644 index 0000000..56f1d16 --- /dev/null +++ b/components/web_view/find_controller_delegate.h @@ -0,0 +1,29 @@ +// Copyright 2015 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. + +#ifndef COMPONENTS_WEB_VIEW_FIND_CONTROLLER_DELEGATE_H_ +#define COMPONENTS_WEB_VIEW_FIND_CONTROLLER_DELEGATE_H_ + +#include <deque> + +namespace mojom { +class WebViewClient; +} + +namespace web_view { + +class Frame; + +class FindControllerDelegate { + public: + ~FindControllerDelegate() {} + + virtual std::deque<Frame*> GetAllFrames() = 0; + + virtual mojom::WebViewClient* GetWebViewClient() = 0; +}; + +} // namespace web_view + +#endif // COMPONENTS_WEB_VIEW_FIND_CONTROLLER_DELEGATE_H_ diff --git a/components/web_view/frame.cc b/components/web_view/frame.cc index e5f82f5..93232af 100644 --- a/components/web_view/frame.cc +++ b/components/web_view/frame.cc @@ -161,6 +161,26 @@ double Frame::GatherProgress(int* frame_count) const { return progress_; } +void Frame::Find(int32 request_id, + const mojo::String& search_text, + const FindCallback& callback) { + frame_client_->Find(request_id, search_text, callback); +} + +void Frame::StopFinding(bool clear_selection) { + frame_client_->StopFinding(clear_selection); +} + +void Frame::HighlightFindResults(int32_t request_id, + const mojo::String& search_text, + bool reset) { + frame_client_->HighlightFindResults(request_id, search_text, reset); +} + +void Frame::StopHighlightingFindResults() { + frame_client_->StopHighlightingFindResults(); +} + void Frame::InitClient(ClientType client_type, scoped_ptr<FrameUserDataAndBinding> data_and_binding, mojo::ViewTreeClientPtr view_tree_client, @@ -543,4 +563,17 @@ void Frame::DispatchLoadEventToParent() { } } +void Frame::OnFindInFrameCountUpdated(int32_t request_id, + int32_t count, + bool final_update) { + tree_->delegate_->OnFindInFrameCountUpdated(request_id, this, count, + final_update); +} + +void Frame::OnFindInPageSelectionUpdated(int32_t request_id, + int32_t active_match_ordinal) { + tree_->delegate_->OnFindInPageSelectionUpdated(request_id, this, + active_match_ordinal); +} + } // namespace web_view diff --git a/components/web_view/frame.h b/components/web_view/frame.h index 380bc8e..f0756df0 100644 --- a/components/web_view/frame.h +++ b/components/web_view/frame.h @@ -54,6 +54,7 @@ enum class ViewOwnership { class Frame : public mus::ViewObserver, public mojom::Frame { public: using ClientPropertyMap = std::map<std::string, std::vector<uint8_t>>; + using FindCallback = mojo::Callback<void(bool)>; Frame(FrameTree* tree, mus::View* view, @@ -112,6 +113,15 @@ class Frame : public mus::ViewObserver, public mojom::Frame { // children, as well as the number of Frames accumulated. double GatherProgress(int* frame_count) const; + void Find(int32_t request_id, + const mojo::String& search_text, + const FindCallback& callback); + void StopFinding(bool clear_selection); + void HighlightFindResults(int32_t request_id, + const mojo::String& search_text, + bool reset); + void StopHighlightingFindResults(); + private: friend class FrameTest; friend class FrameTree; @@ -224,6 +234,11 @@ class Frame : public mus::ViewObserver, public mojom::Frame { mojo::URLRequestPtr request) override; void DidNavigateLocally(const mojo::String& url) override; void DispatchLoadEventToParent() override; + void OnFindInFrameCountUpdated(int32_t request_id, + int32_t count, + bool final_update) override; + void OnFindInPageSelectionUpdated(int32_t request_id, + int32_t active_match_ordinal) override; FrameTree* const tree_; // WARNING: this may be null. See class description for details. diff --git a/components/web_view/frame_apptest.cc b/components/web_view/frame_apptest.cc index fb6171d..40c31bf 100644 --- a/components/web_view/frame_apptest.cc +++ b/components/web_view/frame_apptest.cc @@ -166,6 +166,14 @@ class TestFrameClient : public mojom::FrameClient { if (!on_dispatch_load_event_callback_.is_null()) on_dispatch_load_event_callback_.Run(); } + void Find(int32_t request_id, + const mojo::String& search_text, + const FindCallback& callback) override {} + void StopFinding(bool clear_selection) override {} + void HighlightFindResults(int32_t request_id, + const mojo::String& search_test, + bool reset) override {} + void StopHighlightingFindResults() override {} private: struct LoadingStateChangedNotification { diff --git a/components/web_view/frame_tree_delegate.cc b/components/web_view/frame_tree_delegate.cc index ab7e8d3..c04ec70 100644 --- a/components/web_view/frame_tree_delegate.cc +++ b/components/web_view/frame_tree_delegate.cc @@ -9,5 +9,13 @@ namespace web_view { void FrameTreeDelegate::DidCreateFrame(Frame* frame) {} void FrameTreeDelegate::DidDestroyFrame(Frame* frame) {} void FrameTreeDelegate::OnViewEmbeddedInFrameDisconnected(Frame* frame) {} +void FrameTreeDelegate::OnFindInFrameCountUpdated(int32_t request_id, + Frame* frame, + int32_t count, + bool final_update) {} +void FrameTreeDelegate::OnFindInPageSelectionUpdated( + int32_t request_id, + Frame* frame, + int32_t active_match_ordinal) {} } // namespace web_view diff --git a/components/web_view/frame_tree_delegate.h b/components/web_view/frame_tree_delegate.h index d14fe72..b1eab678 100644 --- a/components/web_view/frame_tree_delegate.h +++ b/components/web_view/frame_tree_delegate.h @@ -84,6 +84,15 @@ class FrameTreeDelegate { // action. virtual void OnViewEmbeddedInFrameDisconnected(Frame* frame); + // Reports the current find state back to our owner. + virtual void OnFindInFrameCountUpdated(int32_t request_id, + Frame* frame, + int32_t count, + bool final_update); + virtual void OnFindInPageSelectionUpdated(int32_t request_id, + Frame* frame, + int32_t active_match_ordinal); + protected: virtual ~FrameTreeDelegate() {} }; diff --git a/components/web_view/public/interfaces/frame.mojom b/components/web_view/public/interfaces/frame.mojom index d1a8f80..ffe1f8f 100644 --- a/components/web_view/public/interfaces/frame.mojom +++ b/components/web_view/public/interfaces/frame.mojom @@ -110,6 +110,14 @@ interface Frame { // Dispatches a load event to the parent of the frame. DispatchLoadEventToParent(); + + // Reports the number of matches for a given find. This is an asynchronous + // notification and can fire multiple times per HighlightFindResults() call. + OnFindInFrameCountUpdated(int32 request_id, int32 count, + bool final_update); + + // Reports which match is currently highlighted. + OnFindInPageSelectionUpdated(int32 request_id, int32 active_match_ordinal); }; enum ViewConnectType { @@ -166,4 +174,35 @@ interface FrameClient { // Called to dispatch a load event of |frame_id| in its parent. This is only // called on the FrameClient rendering the parent of |frame_id|. OnDispatchFrameLoadEvent(uint32 frame_id); + + // TODO(erg): Several of these take a WebFindOptions struct; we probably need + // to build a Frame version of that struct. + + // Searches for a given string. If a match is found, it will be + // selected. Find() will only return true if it found a match, and will return + // the result in the future through OnFindInPageSelectionUpdated(). That + // callback will return |request_id|, and the listener should verify the + // |request_id| on callback to guard against race conditions. + // + // |request_id| should be a monotonically increasing number which should only + // be reused between Find() and the HighlightFindResults() calls that are + // searching for the same string. |search_text| may be empty. + Find(int32 request_id, string search_text) => (bool found); + + // Stop finding the single find result on the page. If |clear_selection| is + // set, it will also clear the selected find text. + StopFinding(bool clear_selection); + + // Match every instance of a string in a document asynchronously, highlighting + // them and putting a tick mark in the scroll bar. This differs from Find() as + // Find() is about finding the one selected instance of the text. + // HighlightFindResults() is about highlighting all the instances of the text. + // + // HighlightFindResults() will asynchronously call + // OnFindInFrameCountUpdated() multiple times to report its progress. + HighlightFindResults(int32 request_id, string search_test, bool reset); + + // Removes the tick marks and highlighting done by HighlightFindResults() in + // this frame. + StopHighlightingFindResults(); }; diff --git a/components/web_view/public/interfaces/web_view.mojom b/components/web_view/public/interfaces/web_view.mojom index 1385b8d..788b554 100644 --- a/components/web_view/public/interfaces/web_view.mojom +++ b/components/web_view/public/interfaces/web_view.mojom @@ -27,6 +27,13 @@ interface WebViewClient { // TODO(beng): also forward text direction. TitleChanged(string? title); + + // Reports the number of matches for a given Find() call. + FindInPageMatchCountUpdated(int32 request_id, int32 count, bool final_update); + + // Reports which find match is selected. (If there are five highlighted + // matches on a page, and the 2nd is selected, |active_match_ordinal| is 2.) + FindInPageSelectionUpdated(int32 request_id, int32 active_match_ordinal); }; interface WebView { @@ -36,6 +43,10 @@ interface WebView { // Provide a ViewTreeClient for this specific WebView. GetViewTreeClient(mojo.ViewTreeClient& view_tree_client); + // Finds a string in page. + Find(int32 request_id, string search_text); + StopFinding(); + // Moves forward and backward. GoBack(); GoForward(); diff --git a/components/web_view/test_runner/test_runner_application_delegate.h b/components/web_view/test_runner/test_runner_application_delegate.h index 48327df..03d5b5e 100644 --- a/components/web_view/test_runner/test_runner_application_delegate.h +++ b/components/web_view/test_runner/test_runner_application_delegate.h @@ -55,6 +55,11 @@ class TestRunnerApplicationDelegate void BackForwardChanged(mojom::ButtonState back_button, mojom::ButtonState forward_button) override; void TitleChanged(const mojo::String& title) override; + void FindInPageMatchCountUpdated(int32_t request_id, + int32_t count, + bool final_update) override {} + void FindInPageSelectionUpdated(int32_t request_id, + int32_t active_match_ordinal) override {} // LayoutTestRunner: void TestFinished() override; diff --git a/components/web_view/web_view_apptest.cc b/components/web_view/web_view_apptest.cc index 130ef0a..1aa84aa 100644 --- a/components/web_view/web_view_apptest.cc +++ b/components/web_view/web_view_apptest.cc @@ -26,6 +26,8 @@ const char kTestTwoFile[] = "test_two.html"; const char kTestTwoTitle[] = "Test Title Two"; const char kTestThreeFile[] = "test_three.html"; const char kTestThreeTitle[] = "Test Title Three"; +const char kTheWordGreenFiveTimes[] = "the_word_green_five_times.html"; +const char kTwoIframesWithGreen[] = "two_iframes_with_green.html"; GURL GetTestFileURL(const std::string& file) { base::FilePath data_file; @@ -41,7 +43,11 @@ GURL GetTestFileURL(const std::string& file) { class WebViewTest : public mus::ViewManagerTestBase, public mojom::WebViewClient { public: - WebViewTest() : web_view_(this) {} + WebViewTest() + : web_view_(this), + quit_condition_(NO_QUIT), + active_find_match_(0), + find_count_(0) {} ~WebViewTest() override {} mojom::WebView* web_view() { return web_view_.web_view(); } @@ -55,7 +61,17 @@ class WebViewTest : public mus::ViewManagerTestBase, return last_forward_button_state_; } - void StartNestedRunLoopUntilLoadingDone() { + int32_t active_find_match() const { return active_find_match_; } + int32_t find_count() const { return find_count_; } + + enum NestedLoopQuitCondition { + NO_QUIT, + LOADING_DONE, + FINAL_FIND_UPATE, + }; + + void StartNestedRunLoopUntil(NestedLoopQuitCondition quit_condition) { + quit_condition_ = quit_condition; run_loop_.reset(new base::RunLoop); run_loop_->Run(); } @@ -64,12 +80,13 @@ class WebViewTest : public mus::ViewManagerTestBase, mojo::URLRequestPtr request(mojo::URLRequest::New()); request->url = GetTestFileURL(file).spec(); web_view()->LoadRequest(request.Pass()); - StartNestedRunLoopUntilLoadingDone(); + StartNestedRunLoopUntil(LOADING_DONE); } private: void QuitNestedRunLoop() { if (run_loop_) { + quit_condition_ = NO_QUIT; run_loop_->Quit(); } } @@ -83,6 +100,7 @@ class WebViewTest : public mus::ViewManagerTestBase, // Overridden from ViewTreeDelegate: void OnEmbed(mus::View* root) override { content_ = root->connection()->CreateView(); + content_->SetBounds(root->bounds()); root->AddChild(content_); content_->SetVisible(true); @@ -102,7 +120,7 @@ class WebViewTest : public mus::ViewManagerTestBase, navigation_url_ = url.get(); } void LoadingStateChanged(bool is_loading, double progress) override { - if (is_loading == false) + if (is_loading == false && quit_condition_ == LOADING_DONE) QuitNestedRunLoop(); } void BackForwardChanged(mojom::ButtonState back_button, @@ -113,6 +131,17 @@ class WebViewTest : public mus::ViewManagerTestBase, void TitleChanged(const mojo::String& title) override { last_title_ = title.get(); } + void FindInPageMatchCountUpdated(int32_t request_id, + int32_t count, + bool final_update) override { + find_count_ = count; + if (final_update && quit_condition_ == FINAL_FIND_UPATE) + QuitNestedRunLoop(); + } + void FindInPageSelectionUpdated(int32_t request_id, + int32_t active_match_ordinal) override { + active_find_match_ = active_match_ordinal; + } mojo::ApplicationImpl* app_; @@ -127,6 +156,11 @@ class WebViewTest : public mus::ViewManagerTestBase, mojom::ButtonState last_back_button_state_; mojom::ButtonState last_forward_button_state_; + NestedLoopQuitCondition quit_condition_; + + int32_t active_find_match_; + int32_t find_count_; + DISALLOW_COPY_AND_ASSIGN(WebViewTest); }; @@ -157,7 +191,7 @@ TEST_F(WebViewTest, CanGoBackAndForward) { last_forward_button_state()); web_view()->GoBack(); - StartNestedRunLoopUntilLoadingDone(); + StartNestedRunLoopUntil(LOADING_DONE); EXPECT_EQ(GetTestFileURL(kTestOneFile).spec(), navigation_url()); EXPECT_EQ(kTestOneTitle, last_title()); @@ -167,7 +201,7 @@ TEST_F(WebViewTest, CanGoBackAndForward) { last_forward_button_state()); web_view()->GoForward(); - StartNestedRunLoopUntilLoadingDone(); + StartNestedRunLoopUntil(LOADING_DONE); EXPECT_EQ(GetTestFileURL(kTestTwoFile).spec(), navigation_url()); EXPECT_EQ(kTestTwoTitle, last_title()); EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_ENABLED, last_back_button_state()); @@ -182,7 +216,7 @@ TEST_F(WebViewTest, NavigationClearsForward) { ASSERT_NO_FATAL_FAILURE(NavigateTo(kTestTwoFile)); web_view()->GoBack(); - StartNestedRunLoopUntilLoadingDone(); + StartNestedRunLoopUntil(LOADING_DONE); EXPECT_EQ(GetTestFileURL(kTestOneFile).spec(), navigation_url()); EXPECT_EQ(kTestOneTitle, last_title()); @@ -201,4 +235,21 @@ TEST_F(WebViewTest, NavigationClearsForward) { last_forward_button_state()); } +TEST_F(WebViewTest, Find) { + ASSERT_NO_FATAL_FAILURE(NavigateTo(kTheWordGreenFiveTimes)); + + web_view()->Find(1, "Green"); + StartNestedRunLoopUntil(FINAL_FIND_UPATE); + EXPECT_EQ(1, active_find_match()); + EXPECT_EQ(5, find_count()); +} + +TEST_F(WebViewTest, FindAcrossIframes) { + ASSERT_NO_FATAL_FAILURE(NavigateTo(kTwoIframesWithGreen)); + + web_view()->Find(1, "Green"); + StartNestedRunLoopUntil(FINAL_FIND_UPATE); + EXPECT_EQ(13, find_count()); +} + } // namespace web_view diff --git a/components/web_view/web_view_impl.cc b/components/web_view/web_view_impl.cc index 8992496..545fbe6 100644 --- a/components/web_view/web_view_impl.cc +++ b/components/web_view/web_view_impl.cc @@ -4,6 +4,9 @@ #include "components/web_view/web_view_impl.h" +#include <queue> + +#include "base/bind.h" #include "base/command_line.h" #include "components/devtools_service/public/cpp/switches.h" #include "components/mus/public/cpp/scoped_view_ptr.h" @@ -44,7 +47,8 @@ WebViewImpl::WebViewImpl(mojo::ApplicationImpl* app, binding_(this, request.Pass()), root_(nullptr), content_(nullptr), - navigation_controller_(this) { + navigation_controller_(this), + find_controller_(this) { if (EnableRemoteDebugging()) devtools_agent_.reset(new FrameDevToolsAgent(app_, this)); OnDidNavigate(); @@ -110,6 +114,14 @@ void WebViewImpl::GetViewTreeClient( mus::ViewTreeConnection::Create(this, view_tree_client.Pass()); } +void WebViewImpl::Find(int32_t request_id, const mojo::String& search_text) { + find_controller_.Find(request_id, search_text); +} + +void WebViewImpl::StopFinding() { + find_controller_.StopFinding(); +} + void WebViewImpl::GoBack() { if (!navigation_controller_.CanGoBack()) return; @@ -203,6 +215,25 @@ void WebViewImpl::DidCommitProvisionalLoad(Frame* frame) { navigation_controller_.FrameDidCommitProvisionalLoad(frame); } +void WebViewImpl::DidDestroyFrame(Frame* frame) { + find_controller_.DidDestroyFrame(frame); +} + +void WebViewImpl::OnFindInFrameCountUpdated(int32_t request_id, + Frame* frame, + int32_t count, + bool final_update) { + find_controller_.OnFindInFrameCountUpdated(request_id, frame, count, + final_update); +} + +void WebViewImpl::OnFindInPageSelectionUpdated(int32_t request_id, + Frame* frame, + int32_t active_match_ordinal) { + find_controller_.OnFindInPageSelectionUpdated(request_id, frame, + active_match_ordinal); +} + //////////////////////////////////////////////////////////////////////////////// // WebViewImpl, FrameDevToolsAgentDelegate implementation: @@ -229,4 +260,27 @@ void WebViewImpl::OnDidNavigate() { : ButtonState::BUTTON_STATE_DISABLED); } +//////////////////////////////////////////////////////////////////////////////// +// WebViewImpl, FindControllerDelegate implementation: + +std::deque<Frame*> WebViewImpl::GetAllFrames() { + std::deque<Frame*> all_frames; + std::queue<Frame*> frames_to_search; + frames_to_search.push(frame_tree_->root()); + while (!frames_to_search.empty()) { + // TODO(erg): This is not in depth first order. I'm not actually sure how + // blink does traversal though. + Frame* current = frames_to_search.front(); + frames_to_search.pop(); + for (Frame* child : current->children()) + frames_to_search.push(child); + all_frames.push_back(current); + } + return all_frames; +} + +mojom::WebViewClient* WebViewImpl::GetWebViewClient() { + return client_.get(); +} + } // namespace web_view diff --git a/components/web_view/web_view_impl.h b/components/web_view/web_view_impl.h index c7a49d4a..449fdd8 100644 --- a/components/web_view/web_view_impl.h +++ b/components/web_view/web_view_impl.h @@ -5,12 +5,15 @@ #ifndef COMPONENTS_WEB_VIEW_WEB_VIEW_IMPL_H_ #define COMPONENTS_WEB_VIEW_WEB_VIEW_IMPL_H_ +#include <deque> #include <string> #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "components/mus/public/cpp/view_observer.h" #include "components/mus/public/cpp/view_tree_delegate.h" +#include "components/web_view/find_controller.h" +#include "components/web_view/find_controller_delegate.h" #include "components/web_view/frame_devtools_agent_delegate.h" #include "components/web_view/frame_tree_delegate.h" #include "components/web_view/navigation_controller.h" @@ -39,7 +42,8 @@ class WebViewImpl : public mojom::WebView, public mus::ViewObserver, public FrameTreeDelegate, public FrameDevToolsAgentDelegate, - public NavigationControllerDelegate { + public NavigationControllerDelegate, + public FindControllerDelegate { public: WebViewImpl(mojo::ApplicationImpl* app, mojom::WebViewClientPtr client, @@ -57,6 +61,8 @@ class WebViewImpl : public mojom::WebView, void GetViewTreeClient( mojo::InterfaceRequest<mojo::ViewTreeClient> view_tree_client) override; + void Find(int32_t request_id, const mojo::String& search_text) override; + void StopFinding() override; void GoBack() override; void GoForward() override; @@ -84,6 +90,14 @@ class WebViewImpl : public mojom::WebView, const CanNavigateFrameCallback& callback) override; void DidStartNavigation(Frame* frame) override; void DidCommitProvisionalLoad(Frame* frame) override; + void DidDestroyFrame(Frame* frame) override; + void OnFindInFrameCountUpdated(int32_t request_id, + Frame* frame, + int32_t count, + bool final_update) override; + void OnFindInPageSelectionUpdated(int32_t request_id, + Frame* frame, + int32_t active_match_ordinal) override; // Overridden from FrameDevToolsAgent::Delegate: void HandlePageNavigateRequest(const GURL& url) override; @@ -92,6 +106,10 @@ class WebViewImpl : public mojom::WebView, void OnNavigate(mojo::URLRequestPtr request) override; void OnDidNavigate() override; + // Overridden from FindControllerDelegate: + std::deque<Frame*> GetAllFrames() override; + mojom::WebViewClient* GetWebViewClient() override; + mojo::ApplicationImpl* app_; mojom::WebViewClientPtr client_; mojo::StrongBinding<WebView> binding_; @@ -108,6 +126,8 @@ class WebViewImpl : public mojom::WebView, NavigationController navigation_controller_; + FindController find_controller_; + DISALLOW_COPY_AND_ASSIGN(WebViewImpl); }; |