Commit 62233ab4 authored by Xiaocheng Hu's avatar Xiaocheng Hu Committed by Commit Bot

Fix memory management of spellcheck callbacks on Mac

Mojo doesn't allow an unrun callback to be removed before the
message pipe is closed. However, Mac spellchecker manages
RequestTextCheckCallbacks as (indirectly) owned by a completion
barrier in SpellingRequest, which lives independently with the
message pipe. Hence, in some shutdown orders, the callback can
be cleared while the message pipe is still open.

This patch fixes the issue by making SpellCheckHost own
all pending SpellingRequests, and SpellingRequest own
RequestTextCheckCallback.

Note: this patch serves as a quick fix to the regressed test
cases. A follow-up patch will clean up the code.

Bug: 814845
Change-Id: I22a08cdce2744b50fe23c0ff48c10d5207fa5f8b
Reviewed-on: https://chromium-review.googlesource.com/946848
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#541624}
parent 2a8c55cf
...@@ -16,11 +16,15 @@ ...@@ -16,11 +16,15 @@
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/bindings/strong_binding.h"
#if !defined(OS_MACOSX)
// Mac needs different constructor and destructor for Mac-only members.
SpellCheckHostChromeImpl::SpellCheckHostChromeImpl( SpellCheckHostChromeImpl::SpellCheckHostChromeImpl(
const service_manager::Identity& renderer_identity) const service_manager::Identity& renderer_identity)
: renderer_identity_(renderer_identity), weak_factory_(this) {} : renderer_identity_(renderer_identity), weak_factory_(this) {}
SpellCheckHostChromeImpl::~SpellCheckHostChromeImpl() = default; SpellCheckHostChromeImpl::~SpellCheckHostChromeImpl() = default;
#endif
// static // static
void SpellCheckHostChromeImpl::Create( void SpellCheckHostChromeImpl::Create(
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_CHROME_IMPL_H_ #ifndef CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_CHROME_IMPL_H_
#define CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_CHROME_IMPL_H_ #define CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_CHROME_IMPL_H_
#include "base/containers/unique_ptr_adapters.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "components/spellcheck/browser/spell_check_host_impl.h" #include "components/spellcheck/browser/spell_check_host_impl.h"
#include "components/spellcheck/browser/spelling_service_client.h" #include "components/spellcheck/browser/spelling_service_client.h"
...@@ -12,6 +13,7 @@ ...@@ -12,6 +13,7 @@
class SpellcheckCustomDictionary; class SpellcheckCustomDictionary;
class SpellcheckService; class SpellcheckService;
class SpellingRequest;
struct SpellCheckResult; struct SpellCheckResult;
...@@ -65,6 +67,9 @@ class SpellCheckHostChromeImpl : public SpellCheckHostImpl { ...@@ -65,6 +67,9 @@ class SpellCheckHostChromeImpl : public SpellCheckHostImpl {
int route_id, int route_id,
RequestTextCheckCallback callback) override; RequestTextCheckCallback callback) override;
// Clears a finished request from |requests_|. Exposed to SpellingRequest.
void OnRequestFinished(SpellingRequest* request);
// Exposed to tests only. // Exposed to tests only.
static void CombineResultsForTesting( static void CombineResultsForTesting(
std::vector<SpellCheckResult>* remote_results, std::vector<SpellCheckResult>* remote_results,
...@@ -73,6 +78,10 @@ class SpellCheckHostChromeImpl : public SpellCheckHostImpl { ...@@ -73,6 +78,10 @@ class SpellCheckHostChromeImpl : public SpellCheckHostImpl {
int ToDocumentTag(int route_id); int ToDocumentTag(int route_id);
void RetireDocumentTag(int route_id); void RetireDocumentTag(int route_id);
std::map<int, int> tag_map_; std::map<int, int> tag_map_;
// All pending requests.
std::set<std::unique_ptr<SpellingRequest>, base::UniquePtrComparator>
requests_;
#endif // defined(OS_MACOSX) #endif // defined(OS_MACOSX)
// Returns the SpellcheckService of our |render_process_id_|. The return // Returns the SpellcheckService of our |render_process_id_|. The return
......
...@@ -52,16 +52,19 @@ void CombineResults(std::vector<SpellCheckResult>* remote_results, ...@@ -52,16 +52,19 @@ void CombineResults(std::vector<SpellCheckResult>* remote_results,
} // namespace } // namespace
// SpellingRequest is owned by SpellCheckHostChromeImpl.
class SpellingRequest { class SpellingRequest {
public: public:
using RequestTextCheckCallback = using RequestTextCheckCallback =
spellcheck::mojom::SpellCheckHost::RequestTextCheckCallback; spellcheck::mojom::SpellCheckHost::RequestTextCheckCallback;
using DestructionCallback = base::OnceCallback<void(SpellingRequest*)>;
SpellingRequest(SpellingServiceClient* client, SpellingRequest(SpellingServiceClient* client,
const base::string16& text, const base::string16& text,
const service_manager::Identity& renderer_identity, const service_manager::Identity& renderer_identity,
int document_tag, int document_tag,
RequestTextCheckCallback callback); RequestTextCheckCallback callback,
DestructionCallback destruction_callback);
private: private:
// Request server-side checking for |text_|. // Request server-side checking for |text_|.
...@@ -73,14 +76,19 @@ class SpellingRequest { ...@@ -73,14 +76,19 @@ class SpellingRequest {
// Check if all pending requests are done, send reply to render process if so. // Check if all pending requests are done, send reply to render process if so.
void OnCheckCompleted(); void OnCheckCompleted();
// Called when server-side checking is complete. // Called when server-side checking is complete. Must be called on UI thread.
void OnRemoteCheckCompleted(bool success, void OnRemoteCheckCompleted(bool success,
const base::string16& text, const base::string16& text,
const std::vector<SpellCheckResult>& results); const std::vector<SpellCheckResult>& results);
// Called when local checking is complete. // Called when local checking is complete. Must be called on UI thread.
void OnLocalCheckCompleted(const std::vector<SpellCheckResult>& results); void OnLocalCheckCompleted(const std::vector<SpellCheckResult>& results);
// Forwards the results back to UI thread when local checking completes.
static void OnLocalCheckCompletedOnAnyThread(
base::WeakPtr<SpellingRequest> request,
const std::vector<SpellCheckResult>& results);
std::vector<SpellCheckResult> local_results_; std::vector<SpellCheckResult> local_results_;
std::vector<SpellCheckResult> remote_results_; std::vector<SpellCheckResult> remote_results_;
...@@ -92,6 +100,10 @@ class SpellingRequest { ...@@ -92,6 +100,10 @@ class SpellingRequest {
const service_manager::Identity renderer_identity_; const service_manager::Identity renderer_identity_;
int document_tag_; int document_tag_;
RequestTextCheckCallback callback_; RequestTextCheckCallback callback_;
DestructionCallback destruction_callback_;
base::WeakPtrFactory<SpellingRequest> weak_factory_;
}; };
SpellingRequest::SpellingRequest( SpellingRequest::SpellingRequest(
...@@ -99,19 +111,21 @@ SpellingRequest::SpellingRequest( ...@@ -99,19 +111,21 @@ SpellingRequest::SpellingRequest(
const base::string16& text, const base::string16& text,
const service_manager::Identity& renderer_identity, const service_manager::Identity& renderer_identity,
int document_tag, int document_tag,
RequestTextCheckCallback callback) RequestTextCheckCallback callback,
DestructionCallback destruction_callback)
: remote_success_(false), : remote_success_(false),
text_(text), text_(text),
renderer_identity_(renderer_identity), renderer_identity_(renderer_identity),
document_tag_(document_tag), document_tag_(document_tag),
callback_(std::move(callback)) { callback_(std::move(callback)),
destruction_callback_(std::move(destruction_callback)),
weak_factory_(this) {
DCHECK(!text_.empty()); DCHECK(!text_.empty());
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Send the remote query out. The barrier owns |this|, ensuring it is deleted completion_barrier_ =
// after completion. BarrierClosure(2, base::BindOnce(&SpellingRequest::OnCheckCompleted,
completion_barrier_ = BarrierClosure( weak_factory_.GetWeakPtr()));
2, base::BindOnce(&SpellingRequest::OnCheckCompleted, base::Owned(this)));
RequestRemoteCheck(client); RequestRemoteCheck(client);
RequestLocalCheck(); RequestLocalCheck();
} }
...@@ -123,21 +137,24 @@ void SpellingRequest::RequestRemoteCheck(SpellingServiceClient* client) { ...@@ -123,21 +137,24 @@ void SpellingRequest::RequestRemoteCheck(SpellingServiceClient* client) {
if (host) if (host)
context = host->GetBrowserContext(); context = host->GetBrowserContext();
// |this| may be gone at callback invocation if the owner has been removed.
client->RequestTextCheck( client->RequestTextCheck(
context, SpellingServiceClient::SPELLCHECK, text_, context, SpellingServiceClient::SPELLCHECK, text_,
base::BindOnce(&SpellingRequest::OnRemoteCheckCompleted, base::BindOnce(&SpellingRequest::OnRemoteCheckCompleted,
base::Unretained(this))); weak_factory_.GetWeakPtr()));
} }
void SpellingRequest::RequestLocalCheck() { void SpellingRequest::RequestLocalCheck() {
// |this| may be gone at callback invocation if the owner has been removed.
spellcheck_platform::RequestTextCheck( spellcheck_platform::RequestTextCheck(
document_tag_, text_, document_tag_, text_,
base::BindOnce(&SpellingRequest::OnLocalCheckCompleted, base::BindOnce(&SpellingRequest::OnLocalCheckCompletedOnAnyThread,
base::Unretained(this))); weak_factory_.GetWeakPtr()));
} }
void SpellingRequest::OnCheckCompleted() { void SpellingRequest::OnCheckCompleted() {
// Final completion can happen on any thread - don't DCHECK thread. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
std::vector<SpellCheckResult>* check_results = &local_results_; std::vector<SpellCheckResult>* check_results = &local_results_;
if (remote_success_) { if (remote_success_) {
std::sort(remote_results_.begin(), remote_results_.end(), CompareLocation); std::sort(remote_results_.begin(), remote_results_.end(), CompareLocation);
...@@ -146,13 +163,11 @@ void SpellingRequest::OnCheckCompleted() { ...@@ -146,13 +163,11 @@ void SpellingRequest::OnCheckCompleted() {
check_results = &remote_results_; check_results = &remote_results_;
} }
// |callback_| must be run on UI thread. std::move(callback_).Run(*check_results);
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE, std::move(destruction_callback_).Run(this);
base::BindOnce(std::move(callback_), std::move(*check_results)));
// Object is self-managed - at this point, its life span is over. // |destruction_callback_| removes |this|. No more operations allowed.
// No need to delete, since the OnCheckCompleted callback owns |this|.
} }
void SpellingRequest::OnRemoteCheckCompleted( void SpellingRequest::OnRemoteCheckCompleted(
...@@ -165,13 +180,30 @@ void SpellingRequest::OnRemoteCheckCompleted( ...@@ -165,13 +180,30 @@ void SpellingRequest::OnRemoteCheckCompleted(
completion_barrier_.Run(); completion_barrier_.Run();
} }
void SpellingRequest::OnLocalCheckCompleted( // static
void SpellingRequest::OnLocalCheckCompletedOnAnyThread(
base::WeakPtr<SpellingRequest> request,
const std::vector<SpellCheckResult>& results) { const std::vector<SpellCheckResult>& results) {
// Local checking can happen on any thread - don't DCHECK thread. // Local checking can happen on any thread - don't DCHECK thread.
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::BindOnce(&SpellingRequest::OnLocalCheckCompleted, request,
results));
}
void SpellingRequest::OnLocalCheckCompleted(
const std::vector<SpellCheckResult>& results) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
local_results_ = results; local_results_ = results;
completion_barrier_.Run(); completion_barrier_.Run();
} }
SpellCheckHostChromeImpl::SpellCheckHostChromeImpl(
const service_manager::Identity& renderer_identity)
: renderer_identity_(renderer_identity), weak_factory_(this) {}
SpellCheckHostChromeImpl::~SpellCheckHostChromeImpl() = default;
// static // static
void SpellCheckHostChromeImpl::CombineResultsForTesting( void SpellCheckHostChromeImpl::CombineResultsForTesting(
std::vector<SpellCheckResult>* remote_results, std::vector<SpellCheckResult>* remote_results,
...@@ -208,9 +240,18 @@ void SpellCheckHostChromeImpl::RequestTextCheck( ...@@ -208,9 +240,18 @@ void SpellCheckHostChromeImpl::RequestTextCheck(
// happen on UI thread. // happen on UI thread.
GetSpellcheckService(); GetSpellcheckService();
// SpellingRequest self-destructs. // |SpellingRequest| self-destructs on completion.
new SpellingRequest(&client_, text, renderer_identity_, // OK to store unretained |this| in a |SpellingRequest| owned by |this|.
ToDocumentTag(route_id), std::move(callback)); requests_.insert(std::make_unique<SpellingRequest>(
&client_, text, renderer_identity_, ToDocumentTag(route_id),
std::move(callback),
base::BindOnce(&SpellCheckHostChromeImpl::OnRequestFinished,
base::Unretained(this))));
}
void SpellCheckHostChromeImpl::OnRequestFinished(SpellingRequest* request) {
auto iterator = requests_.find(request);
requests_.erase(iterator);
} }
int SpellCheckHostChromeImpl::ToDocumentTag(int route_id) { int SpellCheckHostChromeImpl::ToDocumentTag(int route_id) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment