Commit f62d1dab authored by manuk's avatar manuk Committed by Commit Bot

[chrome:omnibox] Add answer entities and favicons.

Added favicons to the 'URL' column. Favicons are displayed to the left of the
url, similar to how they're displayed in the omnibox.
Added answer entities to the 'Contents & Description & Answer' column. Answer
images are displayed to the left of the contents and description, similar to how
they're displayed in the omniobx. For answers which have text associated with
them (e.g. 'weathe<b>r</b> / 76°F Thu / answer type weather'), it will be
displayed in orange after the contents and description.

Bug: 891303
Change-Id: I46d50fcce39f93b57a70aae5d2f439333d203dbf
Reviewed-on: https://chromium-review.googlesource.com/c/1376661Reviewed-by: default avatarNicolas Ouellet-Payeur <nicolaso@chromium.org>
Reviewed-by: default avatarMartin Barbella <mbarbella@chromium.org>
Reviewed-by: default avatarTommy Li <tommycli@chromium.org>
Commit-Queue: manuk hovanesian <manukh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#619427}
parent 346bf49f
......@@ -320,8 +320,9 @@ void ChromeAutocompleteProviderClient::PrefetchImage(const GURL& url) {
DCHECK(image_service);
// TODO(jdonnelly, rhalavati): Create a helper function with Callback to
// create annotation and pass it to image_service, merging this annotation and
// chrome/browser/ui/omnibox/chrome_omnibox_client.cc
// create annotation and pass it to image_service, merging the annotations
// in omnibox_page_handler.cc, chrome_omnibox_client.cc,
// and chrome_autocomplete_provider_client.cc.
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("omnibox_prefetch_image", R"(
semantics {
......
......@@ -154,6 +154,10 @@ tbody:not(:first-of-type) td.cell-provider-and-type .pair-item:first-child {
font-size: 0;
}
.pair-container {
align-items: center;
}
.cell-contents-and-description .pair-container,
.cell-fill-and-inline .pair-container {
display: flex;
......@@ -168,12 +172,20 @@ tbody:not(:first-of-type) td.cell-provider-and-type .pair-item:first-child {
margin-right: 15px;
}
.cell-contents-and-description .pair-item:first-child {
.cell-contents-and-description img.pair-item {
border-radius: 3px;
height: 28px;
}
.cell-contents-and-description .pair-item.contents {
color: blue;
}
.cell-contents-and-description .pair-item.answer {
color: orange;
}
.cell-fill-and-inline .pair-container {
align-items: center;
margin-right: -1px;
}
......@@ -181,7 +193,7 @@ tbody:not(:first-of-type) td.cell-provider-and-type .pair-item:first-child {
margin-right: 1px;
}
.cell-fill-and-inline .pair-item:nth-child(2):not(:empty) {
.cell-fill-and-inline .pair-item.inline:not(:empty) {
border: 1px solid;
font-weight: bold;
}
......@@ -190,6 +202,11 @@ tbody:not(:first-of-type) td.cell-provider-and-type .pair-item:first-child {
color: red;
}
.cell-destination-url img {
margin-right: 5px;
vertical-align: middle;
}
.accesskey {
text-decoration: underline;
text-transform: capitalize;
......@@ -207,6 +224,11 @@ tbody:not(:first-of-type) td.cell-provider-and-type .pair-item:first-child {
border: 1px solid;
}
img:not([src]),
.pair-item:empty:not(img) {
display: none;
}
omnibox-input {
--action-color: rgb(66, 133, 244);
--hover-color: #f2f2f2;
......
......@@ -19,7 +19,8 @@
(function () {
class BrowserProxy {
constructor() {
/** @param {!omnibox_output.OmniboxOutput} omniboxOutput */
constructor(omniboxOutput) {
/** @private {!mojom.OmniboxPageCallbackRouter} */
this.callbackRouter_ = new mojom.OmniboxPageCallbackRouter;
......@@ -28,7 +29,9 @@
// match. Response refers to the data returned from the C++
// AutocompleteController.
this.callbackRouter_.handleNewAutocompleteResult.addListener(
result => omniboxOutput.addAutocompleteResponse(result));
omniboxOutput.addAutocompleteResponse.bind(omniboxOutput));
this.callbackRouter_.handleAnswerImageData.addListener(
omniboxOutput.updateAnswerImage.bind(omniboxOutput));
/** @private {!mojom.OmniboxPageHandlerProxy} */
this.handler_ = mojom.OmniboxPageHandler.getProxy();
......@@ -43,7 +46,7 @@
}
/** @type {!BrowserProxy} */
const browserProxy = new BrowserProxy();
let browserProxy;
/** @type {!OmniboxInput} */
let omniboxInput;
/** @type {!omnibox_output.OmniboxOutput} */
......@@ -53,6 +56,7 @@
omniboxInput = /** @type {!OmniboxInput} */ ($('omnibox-input'));
omniboxOutput =
/** @type {!omnibox_output.OmniboxOutput} */ ($('omnibox-output'));
browserProxy = new BrowserProxy(omniboxOutput);
omniboxInput.addEventListener('query-inputs-changed', event => {
omniboxOutput.clearAutocompleteResponses();
......
......@@ -64,6 +64,14 @@ cr.define('omnibox_output', function() {
this.updateFilterHighlights_();
}
/**
* @param {string} url
* @param {string} data
*/
updateAnswerImage(url, data) {
this.matches.forEach(match => match.updateAnswerImage(url, data));
}
/**
* Show or hide various output elements depending on display inputs.
* 1) Show non-last result groups only if showIncompleteResults is true.
......@@ -102,7 +110,7 @@ cr.define('omnibox_output', function() {
get visibleTableText() {
return this.resultsGroups_
.flatMap(resultsGroup => resultsGroup.visibleText)
.reduce((prev, cur) => `${prev}${cur}\n`, '');
.join('\n');
}
}
......@@ -326,8 +334,10 @@ cr.define('omnibox_output', function() {
/** @param {!mojom.AutocompleteMatch} match */
set match(match) {
/** @type {!Object} */
/** @type {!Object<string, !OutputProperty>} */
this.properties = {};
/** @type {!OutputProperty} */
this.properties.contentsAndDescription;
/** @type {?string} */
this.providerName = match.providerName || null;
......@@ -367,6 +377,15 @@ cr.define('omnibox_output', function() {
this.appendChild(this.additionalProperties);
}
/**
* @param {string} url
* @param {string} data
*/
updateAnswerImage(url, data) {
if (this.properties.contentsAndDescription.value === url)
this.properties.contentsAndDescription.setAnswerImageData(data);
}
/** @param {boolean} showDetails */
updateVisibility(showDetails) {
// Show certain columns only if they showDetails is true.
......@@ -398,7 +417,9 @@ cr.define('omnibox_output', function() {
* needs to be displayed for this match.
*/
get hasAdditionalProperties() {
return Object.keys(this.additionalProperties.value).length > 0;
return Object
.keys(/** @type {!Object} */ (this.additionalProperties.value))
.length > 0;
}
/** @private @return {!Array<!OutputProperty>} */
......@@ -446,7 +467,7 @@ cr.define('omnibox_output', function() {
class OutputProperty extends HTMLTableCellElement {
/**
* @param {Column} column
* @param {!Array<!Object>} values
* @param {!Array<*>} values
* @return {!OutputProperty}
*/
static create(column, values) {
......@@ -457,16 +478,11 @@ cr.define('omnibox_output', function() {
return outputProperty;
}
/** @return {!Object} */
get value() {
return this.value_;
}
/** @param {!Array<!Object>} values */
/** @param {!Array<*>} values */
set values(values) {
/** @private {!Object} */
this.value_ = values[0];
/** @private {!Array<!Object>} */
/** @type {*} */
this.value = values[0];
/** @private {!Array<*>} */
this.values_ = values;
/** @override */
this.render_();
......@@ -477,7 +493,7 @@ cr.define('omnibox_output', function() {
/** @return {string} */
get text() {
return this.value_ + '';
return this.value + '';
}
}
......@@ -538,6 +554,54 @@ cr.define('omnibox_output', function() {
}
}
class OutputAnswerProperty extends OutputProperty {
constructor() {
super();
/** @private {!Element} */
this.container_ = document.createElement('div');
this.container_.classList.add('pair-container');
this.appendChild(this.container_);
/** @type {!Element} */
this.image_ = document.createElement('img');
this.image_.classList.add('pair-item', 'image');
this.container_.appendChild(this.image_);
/** @type {!Element} */
this.contents_ = document.createElement('div');
this.contents_.classList.add('pair-item', 'contents');
this.container_.appendChild(this.contents_);
/** @type {!Element} */
this.description_ = document.createElement('div');
this.description_.classList.add('pair-item', 'description');
this.container_.appendChild(this.description_);
/** @type {!Element} */
this.answer_ = document.createElement('div');
this.answer_.classList.add('pair-item', 'answer');
this.container_.appendChild(this.answer_);
}
/** @param {string} imageData */
setAnswerImageData(imageData) {
this.image_.src = imageData;
}
/** @private @override */
render_() {
this.contents_.textContent = this.values_[1];
this.description_.textContent = this.values_[2];
this.answer_.textContent = this.values_[3];
}
/** @override @return {string} */
get text() {
return this.values_.join('.');
}
}
class OutputBooleanProperty extends OutputProperty {
constructor() {
super();
......@@ -554,7 +618,7 @@ cr.define('omnibox_output', function() {
}
get text() {
return (this.value_ ? 'is: ' : 'not: ') + this.name;
return (this.value ? 'is: ' : 'not: ') + this.name;
}
}
......@@ -579,7 +643,7 @@ cr.define('omnibox_output', function() {
/** @override @return {string} */
get text() {
return JSON.stringify(this.value_, null, 2);
return JSON.stringify(this.value, null, 2);
}
/**
......@@ -627,23 +691,37 @@ cr.define('omnibox_output', function() {
/** @override @return {string} */
get text() {
return this.value_.reduce(
return this.value.reduce(
(prev, {key, value}) => `${prev}${key}: ${value}\n`, '');
}
}
class OutputLinkProperty extends OutputProperty {
class OutputUrlProperty extends OutputProperty {
constructor() {
super();
/** @private {!Element} */
this.container_ = document.createElement('div');
this.container_.classList.add('pair-container');
this.appendChild(this.container_);
/** @private {!Element} */
this.icon_ = document.createElement('img');
this.container_.appendChild(this.icon_);
/** @private {!Element} */
this.link_ = document.createElement('a');
this.appendChild(this.link_);
this.container_.appendChild(this.link_);
}
/** @private @override */
render_() {
this.link_.textContent = this.value_;
this.link_.href = this.value_;
if (this.values_[1])
this.icon_.removeAttribute('src');
else
this.icon_.src = `chrome://favicon/${this.value}`;
this.link_.textContent = this.value;
this.link_.href = this.value;
}
}
......@@ -657,7 +735,7 @@ cr.define('omnibox_output', function() {
/** @private @override */
render_() {
this.div_.textContent = this.value_;
this.div_.textContent = this.value;
}
}
......@@ -786,10 +864,11 @@ cr.define('omnibox_output', function() {
'The result score. Higher is more relevant.', ['relevance'],
OutputTextProperty),
new Column(
['Contents', 'Description'], '', 'contentsAndDescription', true,
['Contents', 'Description', 'Answer'], '', 'contentsAndDescription',
true,
'The text that is presented identifying the result. / The page title ' +
'of the result.',
['contents', 'description'], OutputPairProperty),
['image', 'contents', 'description', 'answer'], OutputAnswerProperty),
new Column(
['D'], '', 'allowedToBeDefaultMatch', true,
'Can Be Default\nA green checkmark indicates that the result can be ' +
......@@ -808,7 +887,7 @@ cr.define('omnibox_output', function() {
['hasTabMatch'], OutputBooleanProperty),
new Column(
['URL'], '', 'destinationUrl', true, 'The URL for the result.',
['destinationUrl'], OutputLinkProperty),
['destinationUrl', 'isSearchType'], OutputUrlProperty),
new Column(
['Fill', 'Inline'], '', 'fillAndInline', false,
'The text shown in the omnibox when the result is selected. / The ' +
......@@ -875,6 +954,8 @@ cr.define('omnibox_output', function() {
customElements.define(
'output-overlapping-pair-property', OutputOverlappingPairProperty,
{extends: 'td'});
customElements.define(
'output-answer-property', OutputAnswerProperty, {extends: 'td'});
customElements.define(
'output-boolean-property', OutputBooleanProperty, {extends: 'td'});
customElements.define(
......@@ -883,7 +964,7 @@ cr.define('omnibox_output', function() {
'output-key-value-tuple-property', OutputKeyValueTuplesProperty,
{extends: 'td'});
customElements.define(
'output-link-property', OutputLinkProperty, {extends: 'td'});
'output-url-property', OutputUrlProperty, {extends: 'td'});
customElements.define(
'output-text-property', OutputTextProperty, {extends: 'td'});
......
......@@ -334,9 +334,9 @@ void ChromeOmniboxClient::OnResultChanged(
continue;
}
// TODO(jdonnelly, rhalavati): Create a helper function with Callback to
// create annotation and pass it to image_service, merging this annotation
// and the one in
// chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
// create annotation and pass it to image_service, merging the annotations
// in omnibox_page_handler.cc, chrome_omnibox_client.cc,
// and chrome_autocomplete_provider_client.cc.
constexpr net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("omnibox_result_change", R"(
semantics {
......
......@@ -20,11 +20,14 @@ struct AutocompleteMatch {
string fill_into_edit;
string inline_autocompletion;
string destination_url;
string image;
string contents;
string description;
string answer;
string transition;
bool allowed_to_be_default_match;
string type;
bool is_search_type;
bool has_tab_match;
string? associated_keyword;
string keyword;
......@@ -66,4 +69,5 @@ interface OmniboxPageHandler {
interface OmniboxPage {
HandleNewAutocompleteResult(OmniboxResult result);
HandleAnswerImageData(string image_url, string image_data);
};
......@@ -10,6 +10,7 @@
#include <utility>
#include "base/auto_reset.h"
#include "base/base64.h"
#include "base/bind.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
......@@ -17,6 +18,8 @@
#include "base/values.h"
#include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
#include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service.h"
#include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service_factory.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile.h"
......@@ -32,8 +35,84 @@
#include "components/search_engines/template_url.h"
#include "content/public/browser/web_ui.h"
#include "third_party/metrics_proto/omnibox_event.pb.h"
#include "ui/gfx/image/image.h"
using bookmarks::BookmarkModel;
using OmniboxPageImageCallback =
base::RepeatingCallback<void(const SkBitmap& bitmap)>;
namespace {
using ImageReciever = std::function<void(std::string)>;
class OmniboxPageImageObserver : public BitmapFetcherService::Observer {
public:
explicit OmniboxPageImageObserver(ImageReciever image_reciever)
: image_reciever_(image_reciever) {}
void OnImageChanged(BitmapFetcherService::RequestId request_id,
const SkBitmap& bitmap) override {
auto data = gfx::Image::CreateFrom1xBitmap(bitmap).As1xPNGBytes();
std::string base_64;
base::Base64Encode(base::StringPiece(data->front_as<char>(), data->size()),
&base_64);
const char kDataUrlPrefix[] = "data:image/png;base64,";
std::string data_url = GURL(kDataUrlPrefix + base_64).spec();
image_reciever_(data_url);
}
private:
const ImageReciever image_reciever_;
DISALLOW_COPY_AND_ASSIGN(OmniboxPageImageObserver);
};
std::string SuggestionAnswerTypeToString(int answer_type) {
switch (answer_type) {
case SuggestionAnswer::ANSWER_TYPE_INVALID:
return "invalid";
case SuggestionAnswer::ANSWER_TYPE_DICTIONARY:
return "dictionary";
case SuggestionAnswer::ANSWER_TYPE_FINANCE:
return "finance";
case SuggestionAnswer::ANSWER_TYPE_KNOWLEDGE_GRAPH:
return "knowledge graph";
case SuggestionAnswer::ANSWER_TYPE_LOCAL:
return "local";
case SuggestionAnswer::ANSWER_TYPE_SPORTS:
return "sports";
case SuggestionAnswer::ANSWER_TYPE_SUNRISE:
return "sunrise";
case SuggestionAnswer::ANSWER_TYPE_TRANSLATION:
return "translation";
case SuggestionAnswer::ANSWER_TYPE_WEATHER:
return "weather";
case SuggestionAnswer::ANSWER_TYPE_WHEN_IS:
return "when is";
case SuggestionAnswer::ANSWER_TYPE_CURRENCY:
return "currency";
case SuggestionAnswer::ANSWER_TYPE_LOCAL_TIME:
return "local time";
case SuggestionAnswer::ANSWER_TYPE_PLAY_INSTALL:
return "play install";
default:
return std::to_string(answer_type);
}
}
std::string SuggestionAnswerImageLineToString(
const SuggestionAnswer::ImageLine& image_line) {
std::string string;
for (auto text_field : image_line.text_fields())
string += base::UTF16ToUTF8(text_field.text());
if (image_line.additional_text())
string += " " + base::UTF16ToUTF8(image_line.additional_text()->text());
if (image_line.status_text())
string += " " + base::UTF16ToUTF8(image_line.status_text()->text());
return string;
}
} // namespace
namespace mojo {
......@@ -69,6 +148,7 @@ struct TypeConverter<mojom::AutocompleteMatchPtr, AutocompleteMatch> {
result->inline_autocompletion =
base::UTF16ToUTF8(input.inline_autocompletion);
result->destination_url = input.destination_url.spec();
result->image = input.ImageUrl().spec().c_str();
result->contents = base::UTF16ToUTF8(input.contents);
// At this time, we're not bothering to send along the long vector that
// represent contents classification. i.e., for each character, what
......@@ -77,10 +157,18 @@ struct TypeConverter<mojom::AutocompleteMatchPtr, AutocompleteMatch> {
// At this time, we're not bothering to send along the long vector that
// represents description classification. i.e., for each character, what
// type of text it is.
if (input.answer) {
result->answer =
SuggestionAnswerImageLineToString(input.answer->first_line()) +
" / " +
SuggestionAnswerImageLineToString(input.answer->second_line()) +
" / " + SuggestionAnswerTypeToString(input.answer->type());
}
result->transition =
ui::PageTransitionGetCoreTransitionString(input.transition);
result->allowed_to_be_default_match = input.allowed_to_be_default_match;
result->type = AutocompleteMatchType::ToString(input.type);
result->is_search_type = AutocompleteMatch::IsSearchType(input.type);
result->has_tab_match = input.has_tab_match;
if (input.associated_keyword.get() != NULL) {
result->associated_keyword =
......@@ -166,7 +254,72 @@ void OmniboxPageHandler::OnResultChanged(bool default_match_changed) {
}
}
// Obtain a vector of all image urls required.
std::vector<std::string> image_urls;
for (size_t i = 0; i < result->combined_results.size(); ++i)
image_urls.push_back(result->combined_results[i]->image);
for (size_t i = 0; i < result->results_by_provider.size(); ++i) {
const mojom::AutocompleteResultsForProvider& result_by_provider =
*result->results_by_provider[i];
for (size_t j = 0; j < result_by_provider.results.size(); ++j)
image_urls.push_back(result_by_provider.results[j]->image);
}
page_->HandleNewAutocompleteResult(std::move(result));
// Fill in image data
BitmapFetcherService* image_service =
BitmapFetcherServiceFactory::GetForBrowserContext(profile_);
for (std::string image_url : image_urls) {
const ImageReciever handleAnswerImageData = [this,
image_url](std::string data) {
page_->HandleAnswerImageData(image_url, data);
};
if (!image_url.empty()) {
// TODO(jdonnelly, rhalavati, manukh): Create a helper function with
// Callback to create annotation and pass it to image_service, merging
// the annotations in omnibox_page_handler.cc, chrome_omnibox_client.cc,
// and chrome_autocomplete_provider_client.cc.
auto traffic_annotation = net::DefineNetworkTrafficAnnotation(
"omnibox_debug_results_change", R"(
semantics {
sender: "Omnibox"
description:
"Chromium provides answers in the suggestion list for "
"certain queries that user types in the omnibox. This request "
"retrieves a small image (for example, an icon illustrating "
"the current weather conditions) when this can add information "
"to an answer."
trigger:
"Change of results for the query typed by the user in the "
"omnibox."
data:
"The only data sent is the path to an image. No user data is "
"included, although some might be inferrable (e.g. whether the "
"weather is sunny or rainy in the user's current location) "
"from the name of the image in the path."
destination: WEBSITE
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting:
"You can enable or disable this feature via 'Use a prediction "
"service to help complete searches and URLs typed in the "
"address bar.' in Chromium's settings under Advanced. The "
"feature is enabled by default."
chrome_policy {
SearchSuggestEnabled {
policy_options {mode: MANDATORY}
SearchSuggestEnabled: false
}
}
})");
image_service->RequestImage(
GURL(image_url), new OmniboxPageImageObserver(handleAnswerImageData),
traffic_annotation);
}
}
}
bool OmniboxPageHandler::LookupIsTypedHost(const base::string16& host,
......
......@@ -292,7 +292,7 @@ bool SuggestionAnswer::Equals(const SuggestionAnswer& answer) const {
second_line_.Equals(answer.second_line_);
}
void SuggestionAnswer::AddImageURLsTo(std::vector<GURL>* urls) const {
void SuggestionAnswer::AddImageURLsTo(URLs* urls) const {
// Note: first_line_.image_url() is not used in practice (so it's ignored).
if (image_url_.is_valid())
urls->push_back(image_url_);
......
......@@ -164,6 +164,7 @@ Refer to README.md for content description and update process.
<item id="oauth2_mint_token_flow" hash_code="1112842" type="1" second_id="29188932" content_hash_code="91581432" os_list="linux,windows" semantics_fields="1,2,3,4,5" policy_fields="3,4" file_path="google_apis/gaia/oauth2_mint_token_flow.cc"/>
<item id="ocsp_start_url_request" hash_code="60921996" type="0" content_hash_code="24127780" os_list="linux" file_path="net/cert_net/nss_ocsp.cc"/>
<item id="offline_prefetch" hash_code="19185953" type="0" content_hash_code="112039446" os_list="linux,windows" file_path="components/offline_pages/core/prefetch/prefetch_request_fetcher.cc"/>
<item id="omnibox_debug_results_change" hash_code="71252052" type="0" content_hash_code="88668874" os_list="linux,windows" file_path="chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc"/>
<item id="omnibox_documentsuggest" hash_code="6055066" type="0" content_hash_code="126973249" os_list="linux,windows" file_path="components/omnibox/browser/document_suggestions_service.cc"/>
<item id="omnibox_navigation_observer" hash_code="61684939" type="0" content_hash_code="70941231" os_list="linux,windows" file_path="chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer.cc"/>
<item id="omnibox_prefetch_image" hash_code="109200878" type="0" content_hash_code="107906693" os_list="linux,windows" file_path="chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc"/>
......@@ -207,8 +208,8 @@ Refer to README.md for content description and update process.
<item id="proxy_config_system" hash_code="11258689" type="0" content_hash_code="77057929" os_list="linux,windows" file_path="net/proxy_resolution/proxy_resolution_service.cc"/>
<item id="proxy_script_fetcher" hash_code="37531401" type="0" deprecated="2018-03-16" content_hash_code="31866133" file_path=""/>
<item id="puch_client_channel" hash_code="34459548" type="0" content_hash_code="92475475" os_list="linux,windows" file_path="components/invalidation/impl/push_client_channel.cc"/>
<item id="quic_chromium_incoming_session" hash_code="87635401" type="0" content_hash_code="78573093" os_list="linux,windows" file_path="net/quic/quic_chromium_client_session.cc"/>
<item id="quic_chromium_incoming_pending_session" hash_code="120830730" type="0" content_hash_code="52904665" os_list="linux,windows" file_path="net/quic/quic_chromium_client_session.cc"/>
<item id="quic_chromium_incoming_session" hash_code="87635401" type="0" content_hash_code="78573093" os_list="linux,windows" file_path="net/quic/quic_chromium_client_session.cc"/>
<item id="quic_chromium_packet_writer" hash_code="20153177" type="0" content_hash_code="29657765" os_list="linux,windows" file_path="net/quic/quic_chromium_packet_writer.cc"/>
<item id="ranker_url_fetcher" hash_code="95682324" type="0" content_hash_code="45958626" os_list="linux,windows" file_path="components/assist_ranker/ranker_url_fetcher.cc"/>
<item id="rappor_report" hash_code="44606780" type="0" content_hash_code="111287826" os_list="linux,windows" file_path="components/rappor/log_uploader.cc"/>
......
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