Commit 9b89d615 authored by Dan Beam's avatar Dan Beam Committed by Commit Bot

NTP Realbox: fix some renderer<->browser communication issues

* DCHECK() about not calling mojo callbacks before they're destroyed

This happens when the user types more quickly than we can respond

* Holding backspace accidentally shows matches for last deleted char

This happens because when input is emptied, the UI immediately hides
the matches UI, but an asynchronous result from the autocomplete
"backend" returns right after, which renders a new result.

R=mahmadi@chromium.org

Bug: 1001761, 1001762
Change-Id: Ic18dd8c63e79d4fe2d1563134920725dc19daf97
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1790434Reviewed-by: default avatarTommy Li <tommycli@chromium.org>
Reviewed-by: default avatarNasko Oskov <nasko@chromium.org>
Reviewed-by: default avatarMoe Ahmadi <mahmadi@chromium.org>
Commit-Queue: Dan Beam <dbeam@chromium.org>
Auto-Submit: Dan Beam <dbeam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#695816}
parent af1dd5c0
......@@ -411,7 +411,19 @@ let ACMatchClassification;
*/
let AutocompleteMatch;
/** @type {function(!Array<!AutocompleteMatch>):void} */
/** @enum {number} */
let AutocompleteResultStatus;
/**
* @typedef {{
* input: string,
* matches: !Array<!AutocompleteMatch>,
* status: !AutocompleteResultStatus
* }}
*/
let AutocompleteResult;
/** @type {function(!AutocompleteResult):void} */
window.chrome.embeddedSearch.searchBox.onqueryautocompletedone;
/**************************** Translated Strings *****************************/
......
......@@ -41,6 +41,12 @@ const ACMatchClassificationStyle = {
DIM: 1 << 2,
};
/** @enum {number} */
const AutocompleteResultStatus = {
SUCCESS: 0,
SKIPPED: 1,
};
/** @typedef {{inline: string, text: string}} */
let RealboxOutput;
......@@ -1025,11 +1031,16 @@ function onMostVisitedChange() {
reloadTiles();
}
/** @param {!Array<!AutocompleteMatch>} matches */
function onQueryAutocompleteDone(matches) {
/** @param {!AutocompleteResult} result */
function onQueryAutocompleteDone(result) {
if (result.status === AutocompleteResultStatus.SKIPPED ||
result.input !== lastOutput.text) {
return; // Stale or skipped result; ignore.
}
const realboxMatchesEl = document.createElement('div');
for (const [i, match] of matches.entries()) {
for (const [i, match] of result.matches.entries()) {
const matchEl = document.createElement('a');
matchEl.href = match.destinationUrl;
......@@ -1068,10 +1079,11 @@ function onQueryAutocompleteDone(matches) {
realboxMatchesEl.append(matchEl);
}
// TODO(crbug.com/996516): this should probably not select the first match by
// default in the case of zero-suggest. One easy (but possibly imperfect) way
// would be to check if ($(IDS.REALBOX).value) {...}.
const hasMatches = result.matches.length > 0;
if (hasMatches) {
realboxMatchesEl.firstElementChild.classList.add(CLASSES.SELECTED);
}
$(IDS.REALBOX_MATCHES).remove();
realboxMatchesEl.id = IDS.REALBOX_MATCHES;
......@@ -1079,7 +1091,6 @@ function onQueryAutocompleteDone(matches) {
const realboxWrapper = $(IDS.REALBOX_INPUT_WRAPPER);
realboxWrapper.appendChild(realboxMatchesEl);
const hasMatches = matches.length > 0;
realboxWrapper.classList.toggle(CLASSES.SHOW_MATCHES, hasMatches);
if (hasMatches) {
......@@ -1088,14 +1099,14 @@ function onQueryAutocompleteDone(matches) {
// If the user is deleting content, don't quickly re-suggest the same
// output.
if (!isDeleting) {
const first = matches[0];
const first = result.matches[0];
if (first.allowedToBeDefaultMatch && first.inlineAutocompletion) {
updateRealboxOutput({inline: first.inlineAutocompletion});
}
}
}
autocompleteMatches = matches;
autocompleteMatches = result.matches;
}
/** @param {!Event} e */
......
......@@ -441,10 +441,12 @@ void SearchIPCRouter::ConfirmThemeChanges() {
}
void SearchIPCRouter::QueryAutocomplete(
const std::string& input,
const base::string16& input,
chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback) {
if (!policy_->ShouldProcessQueryAutocomplete(is_active_tab_)) {
std::move(callback).Run(std::vector<chrome::mojom::AutocompleteMatchPtr>());
std::move(callback).Run(chrome::mojom::AutocompleteResult::New(
input, std::vector<chrome::mojom::AutocompleteMatchPtr>(),
chrome::mojom::AutocompleteResultStatus::SKIPPED));
return;
}
......
......@@ -157,7 +157,7 @@ class SearchIPCRouter : public content::WebContentsObserver,
virtual void OnConfirmThemeChanges() = 0;
virtual void QueryAutocomplete(
const std::string& input,
const base::string16& input,
chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback) = 0;
virtual void StopAutocomplete(bool clear_result) = 0;
......@@ -307,7 +307,7 @@ class SearchIPCRouter : public content::WebContentsObserver,
void RevertThemeChanges() override;
void ConfirmThemeChanges() override;
void QueryAutocomplete(
const std::string& input,
const base::string16& input,
chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback)
override;
void StopAutocomplete(bool clear_result) override;
......
......@@ -105,7 +105,7 @@ class MockSearchIPCRouterDelegate : public SearchIPCRouter::Delegate {
MOCK_METHOD0(OnConfirmThemeChanges, void());
MOCK_METHOD2(
QueryAutocomplete,
void(const std::string& input,
void(const base::string16& input,
chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback));
MOCK_METHOD1(StopAutocomplete, void(bool clear_result));
};
......@@ -1070,3 +1070,17 @@ TEST_F(SearchIPCRouterTest, IgnoreOptOutOfSearchSuggestions) {
GetSearchIPCRouter().OptOutOfSearchSuggestions();
}
TEST_F(SearchIPCRouterTest, IgnoreQueryAutocomplete) {
NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
SetupMockDelegateAndPolicy();
MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
EXPECT_CALL(*mock_delegate(), QueryAutocomplete(_, _)).Times(0);
EXPECT_CALL(*policy, ShouldProcessQueryAutocomplete(_))
.Times(1)
.WillOnce(Return(false));
GetSearchIPCRouter().QueryAutocomplete(
base::string16(),
base::Bind([](chrome::mojom::AutocompleteResultPtr result) {}));
}
......@@ -125,6 +125,13 @@ SearchTabHelper::SearchTabHelper(content::WebContents* web_contents)
SearchTabHelper::~SearchTabHelper() {
if (instant_service_)
instant_service_->RemoveObserver(this);
if (query_autocomplete_callback_) {
std::move(query_autocomplete_callback_)
.Run(chrome::mojom::AutocompleteResult::New(
autocomplete_controller_->input().text(),
std::vector<chrome::mojom::AutocompleteMatchPtr>(),
chrome::mojom::AutocompleteResultStatus::SKIPPED));
}
}
void SearchTabHelper::OmniboxInputStateChanged() {
......@@ -423,11 +430,14 @@ void SearchTabHelper::FileSelectionCanceled(void* params) {
}
void SearchTabHelper::OnResultChanged(bool default_result_changed) {
if (!autocomplete_controller_ || !autocomplete_controller_->done() ||
!query_autocomplete_callback_) {
if (!autocomplete_controller_) {
NOTREACHED();
return;
}
if (!autocomplete_controller_->done() || !query_autocomplete_callback_)
return;
std::vector<chrome::mojom::AutocompleteMatchPtr> matches;
for (const AutocompleteMatch& match : autocomplete_controller_->result()) {
chrome::mojom::AutocompleteMatchPtr mojom_match =
......@@ -455,7 +465,11 @@ void SearchTabHelper::OnResultChanged(bool default_result_changed) {
mojom_match->type = AutocompleteMatchType::ToString(match.type);
matches.push_back(std::move(mojom_match));
}
std::move(query_autocomplete_callback_).Run(std::move(matches));
std::move(query_autocomplete_callback_)
.Run(chrome::mojom::AutocompleteResult::New(
autocomplete_controller_->input().text(), std::move(matches),
chrome::mojom::AutocompleteResultStatus::SUCCESS));
}
void SearchTabHelper::OnSelectLocalBackgroundImage() {
......@@ -540,10 +554,12 @@ void SearchTabHelper::OnConfirmThemeChanges() {
}
void SearchTabHelper::QueryAutocomplete(
const std::string& input,
const base::string16& input,
chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback) {
if (!search::DefaultSearchProviderIsGoogle(profile())) {
std::move(callback).Run(std::vector<chrome::mojom::AutocompleteMatchPtr>());
std::move(callback).Run(chrome::mojom::AutocompleteResult::New(
input, std::vector<chrome::mojom::AutocompleteMatchPtr>(),
chrome::mojom::AutocompleteResultStatus::SKIPPED));
return;
}
......@@ -559,10 +575,17 @@ void SearchTabHelper::QueryAutocomplete(
providers);
}
if (query_autocomplete_callback_) {
std::move(query_autocomplete_callback_)
.Run(chrome::mojom::AutocompleteResult::New(
input, std::vector<chrome::mojom::AutocompleteMatchPtr>(),
chrome::mojom::AutocompleteResultStatus::SKIPPED));
autocomplete_controller_->Stop(/*clear_results=*/false);
}
query_autocomplete_callback_ = std::move(callback);
AutocompleteInput autocomplete_input(
base::UTF8ToUTF16(input), metrics::OmniboxEventProto::NTP_REALBOX,
input, metrics::OmniboxEventProto::NTP_REALBOX,
ChromeAutocompleteSchemeClassifier(profile()));
autocomplete_input.set_from_omnibox_focus(input.empty());
autocomplete_controller_->Start(autocomplete_input);
......
......@@ -138,7 +138,7 @@ class SearchTabHelper : public content::WebContentsObserver,
void OnRevertThemeChanges() override;
void OnConfirmThemeChanges() override;
void QueryAutocomplete(
const std::string& input,
const base::string16& input,
chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback)
override;
void StopAutocomplete(bool clear_result) override;
......
......@@ -52,6 +52,17 @@ struct AutocompleteMatch {
bool swap_contents_and_description;
};
enum AutocompleteResultStatus {
SUCCESS = 0,
SKIPPED = 1,
};
struct AutocompleteResult {
mojo_base.mojom.String16 input;
array<AutocompleteMatch> matches;
AutocompleteResultStatus status;
};
// Browser interface to support embedded search. Render frames connect to this
// interface to query browser data, such as the most visited pages.
// See http://dev.chromium.org/embeddedsearch
......@@ -164,7 +175,8 @@ interface EmbeddedSearch {
ConfirmThemeChanges();
// Gets autocomplete matches from the browser.
QueryAutocomplete(string input) => (array<AutocompleteMatch> matches);
QueryAutocomplete(mojo_base.mojom.String16 input) =>
(AutocompleteResult result);
// Cancels the current autocomplete query. Clears the result set if
// |clear_result| is true.
......
......@@ -432,7 +432,7 @@ void SearchBox::ConfirmThemeChanges() {
embedded_search_service_->ConfirmThemeChanges();
}
void SearchBox::QueryAutocomplete(const std::string& input) {
void SearchBox::QueryAutocomplete(const base::string16& input) {
embedded_search_service_->QueryAutocomplete(
input, base::BindOnce(&SearchBox::QueryAutocompleteResult,
weak_ptr_factory_.GetWeakPtr()));
......@@ -443,10 +443,10 @@ void SearchBox::StopAutocomplete(bool clear_result) {
}
void SearchBox::QueryAutocompleteResult(
std::vector<chrome::mojom::AutocompleteMatchPtr> results) {
chrome::mojom::AutocompleteResultPtr result) {
if (can_run_js_in_renderframe_) {
SearchBoxExtension::DispatchQueryAutocompleteResult(
render_frame()->GetWebFrame(), results);
render_frame()->GetWebFrame(), std::move(result));
}
}
......
......@@ -191,7 +191,7 @@ class SearchBox : public content::RenderFrameObserver,
// Queries the autocomplete backend for realbox results for |input| as a
// search term. Handled by |QueryAutocompleteResult|.
void QueryAutocomplete(const std::string& input);
void QueryAutocomplete(const base::string16& input);
// Cancels the current autocomplete query. Clears the result set if
// |clear_result| is true.
......@@ -225,8 +225,7 @@ class SearchBox : public content::RenderFrameObserver,
GURL GetURLForMostVisitedItem(InstantRestrictedID item_id) const;
// Asynchronous callback for autocomplete query results. Sends to renderer.
void QueryAutocompleteResult(
std::vector<chrome::mojom::AutocompleteMatchPtr> matches);
void QueryAutocompleteResult(chrome::mojom::AutocompleteResultPtr result);
// The connection to the EmbeddedSearch service in the browser process.
chrome::mojom::EmbeddedSearchAssociatedPtr embedded_search_service_;
......
......@@ -545,7 +545,7 @@ class SearchBoxBindings : public gin::Wrappable<SearchBoxBindings> {
// Handlers for JS functions.
static void Paste(const std::string& text);
static void QueryAutocomplete(const std::string& input);
static void QueryAutocomplete(const base::string16& input);
static void StopAutocomplete(bool clear_result);
static void StartCapturingKeyStrokes();
static void StopCapturingKeyStrokes();
......@@ -604,7 +604,7 @@ void SearchBoxBindings::Paste(const std::string& text) {
}
// static
void SearchBoxBindings::QueryAutocomplete(const std::string& input) {
void SearchBoxBindings::QueryAutocomplete(const base::string16& input) {
SearchBox* search_box = GetSearchBoxForCurrentContext();
if (!search_box)
return;
......@@ -1345,9 +1345,13 @@ void SearchBoxExtension::DispatchDeleteCustomLinkResult(
void SearchBoxExtension::DispatchQueryAutocompleteResult(
blink::WebLocalFrame* frame,
const std::vector<chrome::mojom::AutocompleteMatchPtr>& matches) {
chrome::mojom::AutocompleteResultPtr result) {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetStringKey("input", result->input);
dict.SetDoubleKey("status", static_cast<double>(result->status));
base::Value list(base::Value::Type::LIST);
for (const chrome::mojom::AutocompleteMatchPtr& match : matches) {
for (const chrome::mojom::AutocompleteMatchPtr& match : result->matches) {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetBoolKey("allowedToBeDefaultMatch",
match->allowed_to_be_default_match);
......@@ -1378,8 +1382,10 @@ void SearchBoxExtension::DispatchQueryAutocompleteResult(
dict.SetStringKey("type", match->type);
list.Append(std::move(dict));
}
dict.SetKey("matches", std::move(list));
std::string json;
base::JSONWriter::Write(list, &json);
base::JSONWriter::Write(dict, &json);
Dispatch(frame, blink::WebString::FromUTF8(base::StringPrintf(
kDispatchQueryAutocompleteResult, json.c_str())));
}
......
......@@ -39,7 +39,7 @@ class SearchBoxExtension {
bool success);
static void DispatchQueryAutocompleteResult(
blink::WebLocalFrame* frame,
const std::vector<chrome::mojom::AutocompleteMatchPtr>& matches);
chrome::mojom::AutocompleteResultPtr result);
static void DispatchInputCancel(blink::WebLocalFrame* frame);
static void DispatchInputStart(blink::WebLocalFrame* frame);
static void DispatchKeyCaptureChange(blink::WebLocalFrame* frame);
......
......@@ -22,6 +22,12 @@ test.realbox.CLASSES = {
SHOW_MATCHES: 'show-matches',
};
/** @enum {number} */
test.realbox.AutocompleteResultStatus = {
SUCCESS: 0,
SKIPPED: 1,
};
/**
* @param {string} name
* @param {!ClipboardEvent}
......@@ -138,7 +144,8 @@ test.realbox.testReplyWithMatches = function() {
assertEquals('hello world', test.realbox.queries[0]);
const matches = [test.realbox.getSearchMatch(), test.realbox.getUrlMatch()];
chrome.embeddedSearch.searchBox.onqueryautocompletedone(matches);
chrome.embeddedSearch.searchBox.onqueryautocompletedone(
{input: test.realbox.realboxEl.value, matches});
assertTrue(test.realbox.wrapperEl.classList.contains(
test.realbox.CLASSES.SHOW_MATCHES));
......@@ -161,7 +168,10 @@ test.realbox.testReplyWithInlineAutocompletion = function() {
contents: 'hello ',
inlineAutocompletion: 'world',
});
chrome.embeddedSearch.searchBox.onqueryautocompletedone([match]);
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [match],
});
assertTrue(test.realbox.wrapperEl.classList.contains(
test.realbox.CLASSES.SHOW_MATCHES));
......@@ -188,7 +198,10 @@ test.realbox.testDeleteWithInlineAutocompletion = function() {
contents: 'supercal',
inlineAutocompletion: 'ifragilisticexpialidocious',
});
chrome.embeddedSearch.searchBox.onqueryautocompletedone([match]);
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [match],
});
const realboxValue = test.realbox.realboxEl.value;
assertEquals('supercalifragilisticexpialidocious', realboxValue);
......@@ -198,7 +211,10 @@ test.realbox.testDeleteWithInlineAutocompletion = function() {
match.contents = 'superca';
match.inlineAutocompletion = 'lifragilisticexpialidocious';
chrome.embeddedSearch.searchBox.onqueryautocompletedone([match]);
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [match],
});
// Ensure that removing characters from input doesn't just inline autocomplete
// to the same contents on backspace/cut/etc.
......@@ -209,7 +225,10 @@ test.realbox.testDeleteWithInlineAutocompletion = function() {
match.contents = 'super';
match.inlineAutocompletion = 'califragilisticexpialidocious';
chrome.embeddedSearch.searchBox.onqueryautocompletedone([match]);
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [match],
});
assertEquals('super', test.realbox.realboxEl.value);
};
......@@ -217,11 +236,15 @@ test.realbox.testTypeInlineAutocompletion = function() {
test.realbox.realboxEl.value = 'what are the';
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
chrome.embeddedSearch.searchBox.onqueryautocompletedone(
[test.realbox.getSearchMatch({
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [
test.realbox.getSearchMatch({
contents: 'what are the',
inlineAutocompletion: 'se strawberries',
})]);
}),
],
});
assertEquals('what are these strawberries', test.realbox.realboxEl.value);
assertEquals('what are the'.length, test.realbox.realboxEl.selectionStart);
......@@ -241,19 +264,22 @@ test.realbox.testTypeInlineAutocompletion = function() {
}
});
// test.realbox.realboxEl.value = 'what are the';
// Pretend the user typed the next character of the inline autocompletion.
const keyEvent =
new KeyboardEvent('keydown', {bubbles: true, cancelable: true, key: 's'});
test.realbox.realboxEl.dispatchEvent(keyEvent);
assertTrue(keyEvent.defaultPrevented);
chrome.embeddedSearch.searchBox.onqueryautocompletedone(
[test.realbox.getSearchMatch({
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [
test.realbox.getSearchMatch({
contents: 'what are thes',
inlineAutocompletion: 'e strawberries',
})]);
}),
],
});
assertEquals('what are these strawberries', test.realbox.realboxEl.value);
assertEquals('what are thes'.length, test.realbox.realboxEl.selectionStart);
......@@ -267,16 +293,20 @@ test.realbox.testResultsPreserveCursorPosition = function() {
test.realbox.realboxEl.value = 'z';
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
chrome.embeddedSearch.searchBox.onqueryautocompletedone(
[test.realbox.getSearchMatch({contents: 'z'})]);
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [test.realbox.getSearchMatch({contents: 'z'})],
});
test.realbox.realboxEl.value = 'az';
test.realbox.realboxEl.selectionStart = 1;
test.realbox.realboxEl.selectionEnd = 1;
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
chrome.embeddedSearch.searchBox.onqueryautocompletedone(
[test.realbox.getSearchMatch({contents: 'az'})]);
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [test.realbox.getSearchMatch({contents: 'az'})],
});
assertEquals(1, test.realbox.realboxEl.selectionStart);
assertEquals(1, test.realbox.realboxEl.selectionEnd);
......@@ -292,8 +322,10 @@ test.realbox.testCopySearchResultFails = function() {
test.realbox.realboxEl.value = 'skittles!';
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
chrome.embeddedSearch.searchBox.onqueryautocompletedone(
[test.realbox.getSearchMatch({contents: 'skittles!'})]);
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [test.realbox.getSearchMatch({contents: 'skittles!'})],
});
test.realbox.realboxEl.setSelectionRange(0, 'skittles!'.length);
......@@ -306,12 +338,14 @@ test.realbox.testCopyUrlSucceeds = function() {
test.realbox.realboxEl.value = 'go';
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
chrome.embeddedSearch.searchBox.onqueryautocompletedone(
[test.realbox.getUrlMatch({
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [test.realbox.getUrlMatch({
contents: 'go',
inlineAutocompletion: 'ogle.com',
destinationUrl: 'https://www.google.com/',
})]);
})]
});
assertEquals('google.com', test.realbox.realboxEl.value);
......@@ -323,3 +357,61 @@ test.realbox.testCopyUrlSucceeds = function() {
assertEquals(
'https://www.google.com/', copyEvent.clipboardData.getData('text/plain'));
};
test.realbox.testStaleAutocompleteResult = function() {
assertEquals('', test.realbox.realboxEl.value);
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value + 'a', // Simulate stale response.
matches: [test.realbox.getSearchMatch(), test.realbox.getUrlMatch()],
});
};
test.realbox.testAutocompleteResultStatus = function() {
test.realbox.realboxEl.value = 'g';
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
test.realbox.realboxEl.value += 'o';
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [],
status: test.realbox.AutocompleteResultStatus.SKIPPED,
});
assertEquals(0, $(test.realbox.IDS.REALBOX_MATCHES).children.length);
let RESULTS = [test.realbox.getSearchMatch(), test.realbox.getUrlMatch()];
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: RESULTS,
status: test.realbox.AutocompleteResultStatus.SUCCESS,
});
const matchesEl = $(test.realbox.IDS.REALBOX_MATCHES);
assertEquals(RESULTS.length, matchesEl.children.length);
test.realbox.realboxEl.value += 'o';
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
test.realbox.realboxEl.value += 'g';
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: [],
status: test.realbox.AutocompleteResultStatus.SKIPPED,
});
// Checks to see that the matches UI wasn't re-rendered.
assertTrue(matchesEl === $(test.realbox.IDS.REALBOX_MATCHES));
chrome.embeddedSearch.searchBox.onqueryautocompletedone({
input: test.realbox.realboxEl.value,
matches: RESULTS,
status: test.realbox.AutocompleteResultStatus.SUCCESS,
});
const newMatchesEl = $(test.realbox.IDS.REALBOX_MATCHES);
assertEquals(RESULTS.length, newMatchesEl.children.length);
assertFalse(matchesEl === newMatchesEl);
};
......@@ -136,6 +136,7 @@ class AutocompleteController : public AutocompleteProviderListener,
KeywordProvider* keyword_provider() const { return keyword_provider_; }
SearchProvider* search_provider() const { return search_provider_; }
const AutocompleteInput& input() const { return input_; }
const AutocompleteResult& result() const { return result_; }
bool done() const { return done_; }
const Providers& providers() const { return providers_; }
......
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