diff --git a/DEPS b/DEPS index 09ff504..fb0219b 100644 --- a/DEPS +++ b/DEPS
@@ -40,11 +40,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '3f142b6a785ab7af64383fccf98ae2528cdd71e3', + 'skia_revision': '8dadd9e8936a1c8f44fccdfde7f883dfe2d89c96', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'f6aefd1d65e1ce2bef3c0d4928c56cc738051e0f', + 'v8_revision': '2d82e0a3a5d19dfea81cd74771dc031c47e6b15e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -64,7 +64,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '21ae2b7297e005576afeb9f0230d1f69b3abc857', + 'pdfium_revision': '09065aeee59e51e88de054c8dab3b0e538c0893f', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other.
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc index ebd7380..18b255a 100644 --- a/base/trace_event/memory_dump_manager.cc +++ b/base/trace_event/memory_dump_manager.cc
@@ -53,19 +53,14 @@ // TODO(ssid): Fix all the dump providers to unregister if needed and clear the // blacklist, crbug.com/643438. const char* const kStrictThreadCheckBlacklist[] = { - "android::ResourceManagerImpl", "AndroidGraphics", "BrowserGpuMemoryBufferManager", "ClientDiscardableSharedMemoryManager", "ContextProviderCommandBuffer", - "DOMStorage", "DiscardableSharedMemoryManager", "FontCaches", "GpuMemoryBufferVideoFramePool", "IndexedDBBackingStore", - "LeveldbValueStore", - "MemoryCache", - "Skia", "Sql", "ThreadLocalEventBuffer", "TraceLog", @@ -79,10 +74,8 @@ "gpu::BufferManager", "gpu::MappedMemoryManager", "gpu::RenderbufferManager", - "gpu::TextureManager", "gpu::TransferBufferManager", "sql::Connection", - "SyncDirectory", "BlacklistTestDumpProvider" // for testing };
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn index 80427f26..09cb18f 100644 --- a/build/config/BUILD.gn +++ b/build/config/BUILD.gn
@@ -221,6 +221,12 @@ defines += [ "DYNAMIC_ANNOTATIONS_ENABLED=0" ] } } + + if (is_ios) { + # Disable NSAssert and GTMDevAssert (from Google Toolbox for Mac). This + # follows XCode's default behavior for Release builds. + defines += [ "NS_BLOCK_ASSERTIONS=1" ] + } } # Default libraries ------------------------------------------------------------
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java index 59542057..29a4532 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
@@ -298,9 +298,6 @@ contentView.addOnLayoutChangeListener(mFullscreenOnLayoutChangeListener); contentView.setSystemUiVisibility(systemUiVisibility); - // Request a layout so the updated system visibility takes affect. - contentView.getRootView().requestLayout(); - mContentViewCoreInFullscreen = contentViewCore; mTabInFullscreen = tab; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java index 7339ca29..b53c8c4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -80,8 +80,7 @@ } @Override - // TODO(estevenson): Replace with Build.VERSION_CODES.N when available. - @TargetApi(24) + @TargetApi(Build.VERSION_CODES.N) protected void attachBaseContext(Context newBase) { super.attachBaseContext(newBase);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java index b683aa3..c6cc0ea 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -63,6 +63,7 @@ import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrlServiceObserver; import org.chromium.chrome.browser.snackbar.Snackbar; import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController; +import org.chromium.chrome.browser.suggestions.SuggestionsMetricsReporter; import org.chromium.chrome.browser.sync.SyncSessionsMetrics; import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.Tab; @@ -327,56 +328,17 @@ return matchByHost ? UrlUtilities.sameHost(url1, url2) : url1.equals(url2); } - @Override - public void trackSnippetsPageImpression(int[] categories, int[] suggestionsPerCategory) { - mSnippetsBridge.onPageShown(categories, suggestionsPerCategory); + public SuggestionsMetricsReporter getSuggestionsMetricsReporter() { + return mSnippetsBridge; + } + + public void trackSnippetOpened(int windowOpenDisposition, SnippetArticle article) { + mSnippetsBridge.onSuggestionOpened(article, windowOpenDisposition); } @Override - public void trackSnippetImpression(SnippetArticle article) { - mSnippetsBridge.onSuggestionShown(article); - } - - @Override - public void trackSnippetMenuOpened(SnippetArticle article) { - mSnippetsBridge.onSuggestionMenuOpened(article); - } - - @Override - public void trackSnippetCategoryActionImpression(int category, int position) { - mSnippetsBridge.onMoreButtonShown(category, position); - } - - @Override - public void trackSnippetCategoryActionClick(int category, int position) { - mSnippetsBridge.onMoreButtonClicked(category, position); - switch (category) { - case KnownCategories.BOOKMARKS: - NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_BOOKMARKS_MANAGER); - break; - // MORE button in both categories leads to the recent tabs manager - case KnownCategories.FOREIGN_TABS: - case KnownCategories.RECENT_TABS: - NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_RECENT_TABS_MANAGER); - break; - case KnownCategories.DOWNLOADS: - NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_DOWNLOADS_MANAGER); - break; - default: - // No action associated - break; - } - } - - public void trackSnippetOpened( - int windowOpenDisposition, SnippetArticle article, int categoryIndex) { - mSnippetsBridge.onSuggestionOpened(article, categoryIndex, windowOpenDisposition); - } - - @Override - public void openSnippet( - int windowOpenDisposition, SnippetArticle article, int categoryIndex) { - trackSnippetOpened(windowOpenDisposition, article, categoryIndex); + public void openSnippet(int windowOpenDisposition, SnippetArticle article) { + trackSnippetOpened(windowOpenDisposition, article); NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_SNIPPET); if (article.mIsAssetDownload) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java index 1915b3c..141dbabca 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -59,6 +59,7 @@ import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; import org.chromium.chrome.browser.profiles.MostVisitedSites.MostVisitedURLsObserver; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.suggestions.SuggestionsMetricsReporter; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.util.MathUtils; import org.chromium.chrome.browser.util.ViewUtils; @@ -164,45 +165,11 @@ void navigateToDownloadManager(); /** - * Tracks per-page-load metrics for content suggestions. - * @param categories The categories of content suggestions. - * @param suggestionsPerCategory The number of content suggestions in each category. - */ - void trackSnippetsPageImpression(int[] categories, int[] suggestionsPerCategory); - - /** - * Tracks impression metrics for a content suggestion. - * @param article The content suggestion that was shown to the user. - */ - void trackSnippetImpression(SnippetArticle article); - - /** - * Tracks impression metrics for the long-press menu for a content suggestion. - * @param article The content suggestion for which the long-press menu was opened. - */ - void trackSnippetMenuOpened(SnippetArticle article); - - /** - * Tracks impression metrics for a category's action button ("More"). - * @param category The category for which the action button was shown. - * @param position The position of the action button within the category. - */ - void trackSnippetCategoryActionImpression(int category, int position); - - /** - * Tracks click metrics for a category's action button ("More"). - * @param category The category for which the action button was clicked. - * @param position The position of the action button within the category. - */ - void trackSnippetCategoryActionClick(int category, int position); - - /** * Opens a content suggestion and records related metrics. * @param windowOpenDisposition How to open (current tab, new tab, new window etc). * @param article The content suggestion to open. - * @param categoryIndex The index of the category |article| belongs to. */ - void openSnippet(int windowOpenDisposition, SnippetArticle article, int categoryIndex); + void openSnippet(int windowOpenDisposition, SnippetArticle article); /** * Animates the search box up into the omnibox and bring up the keyboard. @@ -301,6 +268,13 @@ */ @Nullable ContextMenuManager getContextMenuManager(); + + /** + * @return The suggestion metrics reporter. Will be {@code null} if the + * {@link NewTabPageView} is not done initialising. + */ + @Nullable + SuggestionsMetricsReporter getSuggestionsMetricsReporter(); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java index a1a4e11..34e6edc1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java
@@ -14,7 +14,9 @@ import org.chromium.chrome.browser.ntp.ContextMenuManager.Delegate; import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager; import org.chromium.chrome.browser.ntp.UiConfig; +import org.chromium.chrome.browser.ntp.snippets.CategoryInt; import org.chromium.chrome.browser.ntp.snippets.SnippetsConfig; +import org.chromium.chrome.browser.suggestions.SuggestionsRanker; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -24,7 +26,7 @@ * Note: Use {@link #refreshVisibility()} to update the visibility of the button instead of calling * {@link #setVisible(boolean)} directly. */ -class ActionItem extends OptionalLeaf { +public class ActionItem extends OptionalLeaf { @IntDef({ACTION_NONE, ACTION_VIEW_ALL, ACTION_FETCH_MORE, ACTION_RELOAD}) @Retention(RetentionPolicy.SOURCE) public @interface Action {} @@ -35,14 +37,17 @@ private final SuggestionsCategoryInfo mCategoryInfo; private final SuggestionsSection mParentSection; + private final SuggestionsRanker mSuggestionsRanker; @Action private int mCurrentAction = ACTION_NONE; private boolean mImpressionTracked; + private int mPerSectionRank = -1; - public ActionItem(SuggestionsSection section) { + public ActionItem(SuggestionsSection section, SuggestionsRanker ranker) { mCategoryInfo = section.getCategoryInfo(); mParentSection = section; + mSuggestionsRanker = ranker; } /** Call this instead of {@link #setVisible(boolean)} to update the visibility. */ @@ -59,17 +64,26 @@ @Override protected void onBindViewHolder(NewTabPageViewHolder holder) { assert holder instanceof ViewHolder; + mSuggestionsRanker.rankActionItem(this, mParentSection); ((ViewHolder) holder).onBindViewHolder(this); } - private int getPosition() { - // TODO(dgn): looks dodgy. Confirm that's what we want. - return mParentSection.getSuggestionsCount(); + @CategoryInt + public int getCategory() { + return mCategoryInfo.getCategory(); + } + + public void setPerSectionRank(int perSectionRank) { + mPerSectionRank = perSectionRank; + } + + public int getPerSectionRank() { + return mPerSectionRank; } @VisibleForTesting void performAction(NewTabPageManager manager) { - manager.trackSnippetCategoryActionClick(mCategoryInfo.getCategory(), getPosition()); + manager.getSuggestionsMetricsReporter().onMoreButtonClicked(this); switch (mCurrentAction) { case ACTION_VIEW_ALL: @@ -97,6 +111,7 @@ return ACTION_NONE; } + /** ViewHolder associated to {@link ItemViewType#ACTION}. */ public static class ViewHolder extends CardViewHolder implements ContextMenuManager.Delegate { private ActionItem mActionListItem; @@ -117,9 +132,7 @@ public void onImpression() { if (mActionListItem != null && !mActionListItem.mImpressionTracked) { mActionListItem.mImpressionTracked = true; - manager.trackSnippetCategoryActionImpression( - mActionListItem.mCategoryInfo.getCategory(), - mActionListItem.getPosition()); + manager.getSuggestionsMetricsReporter().onMoreButtonShown(mActionListItem); } } });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java index f75a9f3f8..b7edb85 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
@@ -15,6 +15,7 @@ import org.chromium.chrome.browser.ntp.snippets.SnippetsConfig; import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; +import org.chromium.chrome.browser.suggestions.SuggestionsRanker; import java.util.LinkedHashMap; import java.util.List; @@ -32,10 +33,13 @@ private final Map<Integer, SuggestionsSection> mSections = new LinkedHashMap<>(); private final NewTabPageManager mNewTabPageManager; private final OfflinePageBridge mOfflinePageBridge; + private final SuggestionsRanker mSuggestionsRanker; public SectionList(NewTabPageManager newTabPageManager, OfflinePageBridge offlinePageBridge) { + mSuggestionsRanker = new SuggestionsRanker(); mNewTabPageManager = newTabPageManager; mNewTabPageManager.getSuggestionsSource().setObserver(this); + mNewTabPageManager.getSuggestionsMetricsReporter().setRanker(mSuggestionsRanker); mOfflinePageBridge = offlinePageBridge; resetSections(/* alwaysAllowEmptySections = */ false); } @@ -61,12 +65,11 @@ suggestionsPerCategory[categoryIndex] = resetSection(category, categoryStatus, alwaysAllowEmptySections); - SuggestionsSection section = mSections.get(category); - if (section != null) section.setCategoryIndex(categoryIndex); ++categoryIndex; } - mNewTabPageManager.trackSnippetsPageImpression(categories, suggestionsPerCategory); + mNewTabPageManager.getSuggestionsMetricsReporter().onPageShown( + categories, suggestionsPerCategory); } /** @@ -95,8 +98,10 @@ // Create the section if needed. if (section == null) { - section = new SuggestionsSection(this, mNewTabPageManager, mOfflinePageBridge, info); + section = new SuggestionsSection( + this, mNewTabPageManager, mSuggestionsRanker, mOfflinePageBridge, info); mSections.put(category, section); + mSuggestionsRanker.registerCategory(category); addChild(section); } @@ -186,17 +191,6 @@ */ private void setSuggestions(@CategoryInt int category, List<SnippetArticle> suggestions, @CategoryStatusEnum int status, boolean replaceExisting) { - // Count the number of suggestions before this category. - int globalPositionOffset = 0; - for (Map.Entry<Integer, SuggestionsSection> entry : mSections.entrySet()) { - if (entry.getKey() == category) break; - globalPositionOffset += entry.getValue().getSuggestionsCount(); - } - // Assign global indices to the new suggestions. - for (SnippetArticle suggestion : suggestions) { - suggestion.mGlobalPosition = globalPositionOffset + suggestion.mPosition; - } - mSections.get(category).setSuggestions(suggestions, status, replaceExisting); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java index acb529e..2d7b839 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -19,6 +19,7 @@ import org.chromium.chrome.browser.offlinepages.ClientId; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; import org.chromium.chrome.browser.offlinepages.OfflinePageItem; +import org.chromium.chrome.browser.suggestions.SuggestionsRanker; import java.util.ArrayList; import java.util.Iterator; @@ -34,6 +35,7 @@ private final Delegate mDelegate; private final SuggestionsCategoryInfo mCategoryInfo; private final OfflinePageBridge mOfflinePageBridge; + private final SuggestionsRanker mSuggestionsRanker; // Children private final SectionHeader mHeader; @@ -60,15 +62,17 @@ } public SuggestionsSection(Delegate delegate, NewTabPageManager manager, - OfflinePageBridge offlinePageBridge, SuggestionsCategoryInfo info) { + SuggestionsRanker ranker, OfflinePageBridge offlinePageBridge, + SuggestionsCategoryInfo info) { mDelegate = delegate; mCategoryInfo = info; mOfflinePageBridge = offlinePageBridge; + mSuggestionsRanker = ranker; mHeader = new SectionHeader(info.getTitle()); mSuggestionsList = new SuggestionsList(manager, info); mStatus = StatusItem.createNoSuggestionsItem(info); - mMoreButton = new ActionItem(this); + mMoreButton = new ActionItem(this, ranker); mProgressIndicator = new ProgressItem(); addChildren(mHeader, mSuggestionsList, mStatus, mMoreButton, mProgressIndicator); @@ -80,7 +84,6 @@ private final List<SnippetArticle> mSuggestions = new ArrayList<>(); private final NewTabPageManager mNewTabPageManager; private final SuggestionsCategoryInfo mCategoryInfo; - private int mCategoryIndex; public SuggestionsList(NewTabPageManager newTabPageManager, SuggestionsCategoryInfo categoryInfo) { @@ -88,14 +91,6 @@ mCategoryInfo = categoryInfo; } - public void setCategoryIndex(int categoryIndex) { - mCategoryIndex = categoryIndex; - } - - public int getCategoryIndex() { - return mCategoryIndex; - } - @Override public int getItemCount() { return mSuggestions.size(); @@ -114,8 +109,7 @@ checkIndex(position); assert holder instanceof SnippetArticleViewHolder; ((SnippetArticleViewHolder) holder) - .onBindViewHolder( - getSuggestionAt(position), mCategoryInfo, payloads, mCategoryIndex); + .onBindViewHolder(getSuggestionAt(position), mCategoryInfo, payloads); } @Override @@ -359,6 +353,7 @@ mSuggestionsList.addAll(suggestions); for (SnippetArticle article : suggestions) { + mSuggestionsRanker.rankSuggestion(article); if (!article.requiresExactOfflinePage()) { updateSnippetOfflineAvailability(article); } @@ -436,14 +431,6 @@ return mCategoryInfo; } - public int getCategoryIndex() { - return mSuggestionsList.getCategoryIndex(); - } - - public void setCategoryIndex(int categoryIndex) { - mSuggestionsList.setCategoryIndex(categoryIndex); - } - public String getHeaderText() { return mHeader.getHeaderText(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java index bc37e383a..95f20e50 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java
@@ -36,11 +36,11 @@ /** The score expressing relative quality of the article for the user. */ public final float mScore; - /** The position of this article within its section. */ - public final int mPosition; + /** The rank of this article within its section. */ + private int mPerSectionRank = -1; - /** The position of this article in the complete list. Populated by NewTabPageAdapter. */ - public int mGlobalPosition = -1; + /** The global rank of this article in the complete list. */ + private int mGlobalRank = -1; /** Bitmap of the thumbnail, fetched lazily, when the RecyclerView wants to show the snippet. */ private Bitmap mThumbnailBitmap; @@ -67,7 +67,7 @@ * Creates a SnippetArticleListItem object that will hold the data. */ public SnippetArticle(int category, String idWithinCategory, String title, String publisher, - String previewText, String url, long timestamp, float score, int position) { + String previewText, String url, long timestamp, float score) { mCategory = category; mIdWithinCategory = idWithinCategory; mTitle = title; @@ -76,7 +76,6 @@ mUrl = url; mPublishTimestampMilliseconds = timestamp; mScore = score; - mPosition = position; } @Override @@ -208,4 +207,17 @@ // For debugging purposes. Displays the first 42 characters of the title. return String.format("{%s, %1.42s}", getClass().getSimpleName(), mTitle); } + + public void setRank(int perSectionRank, int globalRank) { + mPerSectionRank = perSectionRank; + mGlobalRank = globalRank; + } + + public int getGlobalRank() { + return mGlobalRank; + } + + public int getPerSectionRank() { + return mPerSectionRank; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java index 7bb3616..92cdf2b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
@@ -70,7 +70,6 @@ private FetchImageCallback mImageCallback; private SnippetArticle mArticle; private SuggestionsCategoryInfo mCategoryInfo; - private int mCategoryIndex; private int mPublisherFaviconSizePx; private final boolean mUseFaviconService; @@ -111,19 +110,19 @@ @Override public void onImpression() { if (mArticle != null && mArticle.trackImpression()) { - mNewTabPageManager.trackSnippetImpression(mArticle); + mNewTabPageManager.getSuggestionsMetricsReporter().onSuggestionShown(mArticle); mRecyclerView.onSnippetImpression(); } } @Override public void onCardTapped() { - mNewTabPageManager.openSnippet(WindowOpenDisposition.CURRENT_TAB, mArticle, mCategoryIndex); + mNewTabPageManager.openSnippet(WindowOpenDisposition.CURRENT_TAB, mArticle); } @Override public void openItem(int windowDisposition) { - mNewTabPageManager.openSnippet(windowDisposition, mArticle, mCategoryIndex); + mNewTabPageManager.openSnippet(windowDisposition, mArticle); } @Override @@ -152,7 +151,7 @@ @Override public void onContextMenuCreated() { - mNewTabPageManager.trackSnippetMenuOpened(mArticle); + mNewTabPageManager.getSuggestionsMetricsReporter().onSuggestionMenuOpened(mArticle); } @Override @@ -224,8 +223,8 @@ BidiFormatter.getInstance().unicodeWrap(article.mPublisher), relativeTimeSpan); } - public void onBindViewHolder(SnippetArticle article, SuggestionsCategoryInfo categoryInfo, - List<Object> payloads, int categoryIndex) { + public void onBindViewHolder( + SnippetArticle article, SuggestionsCategoryInfo categoryInfo, List<Object> payloads) { if (!payloads.isEmpty() && article.equals(mArticle)) { performPartialBind(payloads); return; @@ -235,7 +234,6 @@ mArticle = article; mCategoryInfo = categoryInfo; - mCategoryIndex = categoryIndex; updateLayout(); mHeadlineTextView.setText(mArticle.mTitle);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java index a9928ce1..87e5e00 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
@@ -8,9 +8,13 @@ import org.chromium.base.Callback; import org.chromium.base.annotations.CalledByNative; +import org.chromium.chrome.browser.ntp.NewTabPageUma; +import org.chromium.chrome.browser.ntp.cards.ActionItem; import org.chromium.chrome.browser.ntp.cards.SuggestionsCategoryInfo; import org.chromium.chrome.browser.ntp.snippets.CategoryStatus.CategoryStatusEnum; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.suggestions.SuggestionsMetricsReporter; +import org.chromium.chrome.browser.suggestions.SuggestionsRanker; import java.util.ArrayList; import java.util.List; @@ -18,11 +22,12 @@ /** * Provides access to the snippets to display on the NTP using the C++ ContentSuggestionsService. */ -public class SnippetsBridge implements SuggestionsSource { +public class SnippetsBridge implements SuggestionsSource, SuggestionsMetricsReporter { private static final String TAG = "SnippetsBridge"; private long mNativeSnippetsBridge; private SuggestionsSource.Observer mObserver; + private SuggestionsRanker mSuggestionsRanker; public static boolean isCategoryStatusAvailable(@CategoryStatusEnum int status) { // Note: This code is duplicated in content_suggestions_category_status.cc. @@ -119,8 +124,8 @@ @Override public void dismissSuggestion(SnippetArticle suggestion) { assert mNativeSnippetsBridge != 0; - nativeDismissSuggestion(mNativeSnippetsBridge, suggestion.mUrl, suggestion.mGlobalPosition, - suggestion.mCategory, suggestion.mPosition, suggestion.mIdWithinCategory); + nativeDismissSuggestion(mNativeSnippetsBridge, suggestion.mUrl, suggestion.getGlobalRank(), + suggestion.mCategory, suggestion.getPerSectionRank(), suggestion.mIdWithinCategory); } @Override @@ -135,41 +140,66 @@ nativeRestoreDismissedCategories(mNativeSnippetsBridge); } + @Override public void onPageShown(int[] categories, int[] suggestionsPerCategory) { assert mNativeSnippetsBridge != 0; nativeOnPageShown(mNativeSnippetsBridge, categories, suggestionsPerCategory); } + @Override public void onSuggestionShown(SnippetArticle suggestion) { assert mNativeSnippetsBridge != 0; - nativeOnSuggestionShown(mNativeSnippetsBridge, suggestion.mGlobalPosition, - suggestion.mCategory, suggestion.mPosition, + nativeOnSuggestionShown(mNativeSnippetsBridge, suggestion.getGlobalRank(), + suggestion.mCategory, suggestion.getPerSectionRank(), suggestion.mPublishTimestampMilliseconds, suggestion.mScore); } - public void onSuggestionOpened( - SnippetArticle suggestion, int categoryIndex, int windowOpenDisposition) { + @Override + public void onSuggestionOpened(SnippetArticle suggestion, int windowOpenDisposition) { assert mNativeSnippetsBridge != 0; - nativeOnSuggestionOpened(mNativeSnippetsBridge, suggestion.mGlobalPosition, - suggestion.mCategory, categoryIndex, suggestion.mPosition, + int categoryIndex = mSuggestionsRanker.getCategoryRank(suggestion.mCategory); + nativeOnSuggestionOpened(mNativeSnippetsBridge, suggestion.getGlobalRank(), + suggestion.mCategory, categoryIndex, suggestion.getPerSectionRank(), suggestion.mPublishTimestampMilliseconds, suggestion.mScore, windowOpenDisposition); } + @Override public void onSuggestionMenuOpened(SnippetArticle suggestion) { assert mNativeSnippetsBridge != 0; - nativeOnSuggestionMenuOpened(mNativeSnippetsBridge, suggestion.mGlobalPosition, - suggestion.mCategory, suggestion.mPosition, + nativeOnSuggestionMenuOpened(mNativeSnippetsBridge, suggestion.getGlobalRank(), + suggestion.mCategory, suggestion.getPerSectionRank(), suggestion.mPublishTimestampMilliseconds, suggestion.mScore); } - public void onMoreButtonShown(int category, int position) { + @Override + public void onMoreButtonShown(ActionItem actionItem) { assert mNativeSnippetsBridge != 0; - nativeOnMoreButtonShown(mNativeSnippetsBridge, category, position); + nativeOnMoreButtonShown( + mNativeSnippetsBridge, actionItem.getCategory(), actionItem.getPerSectionRank()); } - public void onMoreButtonClicked(int category, int position) { + @Override + public void onMoreButtonClicked(ActionItem actionItem) { assert mNativeSnippetsBridge != 0; - nativeOnMoreButtonClicked(mNativeSnippetsBridge, category, position); + @CategoryInt + int category = actionItem.getCategory(); + nativeOnMoreButtonClicked(mNativeSnippetsBridge, category, actionItem.getPerSectionRank()); + switch (category) { + case KnownCategories.BOOKMARKS: + NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_BOOKMARKS_MANAGER); + break; + // MORE button in both categories leads to the recent tabs manager + case KnownCategories.FOREIGN_TABS: + case KnownCategories.RECENT_TABS: + NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_RECENT_TABS_MANAGER); + break; + case KnownCategories.DOWNLOADS: + NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_DOWNLOADS_MANAGER); + break; + default: + // No action associated + break; + } } /** @@ -194,21 +224,19 @@ nativeOnSuggestionTargetVisited(category, visitTimeMs); } - /** - * Sets the recipient for the fetched snippets. - * - * An observer needs to be set before the native code attempts to transmit snippets them to - * java. Upon registration, the observer will be notified of already fetched snippets. - * - * @param observer object to notify when snippets are received. - */ @Override - public void setObserver(SuggestionsSource.Observer observer) { + public void setObserver(Observer observer) { assert observer != null; mObserver = observer; } @Override + public void setRanker(SuggestionsRanker suggestionsRanker) { + assert suggestionsRanker != null; + mSuggestionsRanker = suggestionsRanker; + } + + @Override public void fetchSuggestions(@CategoryInt int category, String[] displayedSuggestionIds) { nativeFetch(mNativeSnippetsBridge, category, displayedSuggestionIds); } @@ -234,7 +262,7 @@ long timestamp, float score) { int position = suggestions.size(); suggestions.add(new SnippetArticle( - category, id, title, publisher, previewText, url, timestamp, score, position)); + category, id, title, publisher, previewText, url, timestamp, score)); return suggestions.get(position); } @@ -306,18 +334,18 @@ private native void nativeFetch( long nativeNTPSnippetsBridge, int category, String[] knownSuggestions); private native void nativeDismissSuggestion(long nativeNTPSnippetsBridge, String url, - int globalPosition, int category, int categoryPosition, String idWithinCategory); + int globalPosition, int category, int positionInCategory, String idWithinCategory); private native void nativeDismissCategory(long nativeNTPSnippetsBridge, int category); private native void nativeRestoreDismissedCategories(long nativeNTPSnippetsBridge); private native void nativeOnPageShown( long nativeNTPSnippetsBridge, int[] categories, int[] suggestionsPerCategory); private native void nativeOnSuggestionShown(long nativeNTPSnippetsBridge, int globalPosition, - int category, int categoryPosition, long publishTimestampMs, float score); + int category, int positionInCategory, long publishTimestampMs, float score); private native void nativeOnSuggestionOpened(long nativeNTPSnippetsBridge, int globalPosition, - int category, int categoryIndex, int categoryPosition, long publishTimestampMs, + int category, int categoryIndex, int positionInCategory, long publishTimestampMs, float score, int windowOpenDisposition); private native void nativeOnSuggestionMenuOpened(long nativeNTPSnippetsBridge, - int globalPosition, int category, int categoryPosition, long publishTimestampMs, + int globalPosition, int category, int positionInCategory, long publishTimestampMs, float score); private native void nativeOnMoreButtonShown( long nativeNTPSnippetsBridge, int category, int position);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ContentSuggestionsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ContentSuggestionsActivity.java index 8ca0ed4..0d66ebc7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ContentSuggestionsActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ContentSuggestionsActivity.java
@@ -94,23 +94,7 @@ public void navigateToDownloadManager() {} @Override - public void trackSnippetsPageImpression(int[] categories, int[] suggestionsPerCategory) {} - - @Override - public void trackSnippetImpression(SnippetArticle article) {} - - @Override - public void trackSnippetMenuOpened(SnippetArticle article) {} - - @Override - public void trackSnippetCategoryActionImpression(int category, int position) {} - - @Override - public void trackSnippetCategoryActionClick(int category, int position) {} - - @Override - public void openSnippet( - int windowOpenDisposition, SnippetArticle article, int categoryIndex) {} + public void openSnippet(int windowOpenDisposition, SnippetArticle article) {} @Override public void focusSearchBox(boolean beginVoiceSearch, String pastedText) {} @@ -163,6 +147,11 @@ public ContextMenuManager getContextMenuManager() { return mContextMenuManager; } + + @Override + public SuggestionsMetricsReporter getSuggestionsMetricsReporter() { + return mSnippetsBridge; + } } @Override @@ -198,6 +187,10 @@ for (DestructionObserver observer : mDestructionObservers) { observer.onDestroy(); } + + mSnippetsBridge.destroy(); + mSnippetsBridge = null; + super.onDestroy(); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetricsReporter.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetricsReporter.java new file mode 100644 index 0000000..b2aa548b --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetricsReporter.java
@@ -0,0 +1,56 @@ +// Copyright 2017 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. + +package org.chromium.chrome.browser.suggestions; + +import org.chromium.chrome.browser.ntp.cards.ActionItem; +import org.chromium.chrome.browser.ntp.snippets.CategoryInt; +import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; + +/** + * Exposes UMA related methods. + */ +public interface SuggestionsMetricsReporter { + /** + * Tracks per-page-load metrics for content suggestions. + * @param categories The categories of content suggestions. + * @param suggestionsPerCategory The number of content suggestions in each category. + */ + void onPageShown(int[] categories, int[] suggestionsPerCategory); + + /** + * Tracks impression metrics for a content suggestion. + * @param suggestion The content suggestion that was shown to the user. + */ + void onSuggestionShown(SnippetArticle suggestion); + + /** + * Tracks interaction metrics for a content suggestion. + * @param suggestion The content suggestion that the user opened. + * @param windowOpenDisposition How the suggestion was opened (current tab, new tab, + * new window etc). + */ + void onSuggestionOpened(SnippetArticle suggestion, int windowOpenDisposition); + + /** + * Tracks impression metrics for the long-press menu for a content suggestion. + * @param suggestion The content suggestion for which the long-press menu was opened. + */ + void onSuggestionMenuOpened(SnippetArticle suggestion); + + /** + * Tracks impression metrics for a category's action button ("More"). + * @param category The action button that was shown. + */ + void onMoreButtonShown(@CategoryInt ActionItem category); + + /** + * Tracks click metrics for a category's action button ("More"). + * @param category The action button that was clicked. + */ + void onMoreButtonClicked(@CategoryInt ActionItem category); + + /** Sets the ranker to use to compute some of the reported metrics. */ + void setRanker(SuggestionsRanker suggestionsRanker); +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRanker.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRanker.java new file mode 100644 index 0000000..49b546e --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRanker.java
@@ -0,0 +1,61 @@ +// Copyright 2017 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. + +package org.chromium.chrome.browser.suggestions; + +import org.chromium.chrome.browser.ntp.cards.ActionItem; +import org.chromium.chrome.browser.ntp.cards.SuggestionsSection; +import org.chromium.chrome.browser.ntp.snippets.CategoryInt; +import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Attributes ranks to suggestions and related elements. + * + * Ranks here are 0-based scores attributed based on the position or loading order of the + * elements. + */ +public class SuggestionsRanker { + private final Map<Integer, Integer> mSuggestionsAddedPerSection = new LinkedHashMap<>(); + private int mTotalAddedSuggestions; + + /** + * Attributes a per section rank to the provided action item. + * @see ActionItem#getPerSectionRank() + */ + public void rankActionItem(ActionItem actionItem, SuggestionsSection section) { + if (actionItem.getPerSectionRank() != -1) return; // Item was already ranked. + actionItem.setPerSectionRank(section.getSuggestionsCount()); + } + + /** + * Attributes global and per section rank to the provided suggestion. + * @see SnippetArticle#getPerSectionRank() + * @see SnippetArticle#getGlobalRank() + */ + public void rankSuggestion(SnippetArticle suggestion) { + int globalRank = mTotalAddedSuggestions++; + int perSectionRank = mSuggestionsAddedPerSection.get(suggestion.mCategory); + mSuggestionsAddedPerSection.put(suggestion.mCategory, perSectionRank + 1); + + suggestion.setRank(perSectionRank, globalRank); + } + + public void registerCategory(@CategoryInt int category) { + // Check we are not simply resetting an already registered category. + if (mSuggestionsAddedPerSection.containsKey(category)) return; + mSuggestionsAddedPerSection.put(category, 0); + } + + public int getCategoryRank(@CategoryInt int category) { + int rank = 0; + for (Integer key : mSuggestionsAddedPerSection.keySet()) { + if (key == category) return rank; + ++rank; + } + return -1; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java index 149a3e2c..a32ff99e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java
@@ -20,6 +20,8 @@ import org.chromium.chrome.browser.ChromeApplication; import org.chromium.chrome.browser.ShortcutHelper; import org.chromium.chrome.browser.banners.InstallerDelegate; +import org.chromium.chrome.browser.externalauth.ExternalAuthUtils; +import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler; import org.chromium.chrome.browser.util.IntentUtils; import java.io.File; @@ -62,8 +64,11 @@ } @CalledByNative - private boolean hasGooglePlayWebApkInstallDelegate() { - return mGooglePlayWebApkInstallDelegate != null; + private boolean canUseGooglePlayInstallService() { + return mGooglePlayWebApkInstallDelegate != null + && ExternalAuthUtils.getInstance().canUseGooglePlayServices( + ContextUtils.getApplicationContext(), + new UserRecoverableErrorHandler.Silent()); } @CalledByNative
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index 4ea2d7e..f0f2631 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -918,6 +918,8 @@ "java/src/org/chromium/chrome/browser/snackbar/undo/UndoBarController.java", "java/src/org/chromium/chrome/browser/ssl/SecurityStateModel.java", "java/src/org/chromium/chrome/browser/suggestions/ContentSuggestionsActivity.java", + "java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetricsReporter.java", + "java/src/org/chromium/chrome/browser/suggestions/SuggestionsRanker.java", "java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java", "java/src/org/chromium/chrome/browser/sync/GmsCoreSyncListener.java", "java/src/org/chromium/chrome/browser/sync/GoogleServiceAuthError.java", @@ -1521,6 +1523,7 @@ "junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java", "junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java", "junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java", + "junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java", "junit/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerTest.java", "junit/src/org/chromium/chrome/browser/offlinepages/BackgroundOfflinerTaskTest.java", "junit/src/org/chromium/chrome/browser/offlinepages/ClientIdTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java index e4c6804..5cd10e94 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
@@ -301,7 +301,7 @@ for (int i = 0; i < suggestionsCount; i++) { String url = mTestServer.getURL(TEST_PAGE) + "#" + i; suggestions.add(new SnippetArticle(KnownCategories.ARTICLES, "id" + i, "title" + i, - "publisher" + i, "previewText" + i, url, 1466614774 + i, 10f, 0)); + "publisher" + i, "previewText" + i, url, 1466614774 + i, 10f)); } return suggestions; }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java index ab722ec..091e335 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
@@ -26,12 +26,15 @@ import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver; import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager; import org.chromium.chrome.browser.ntp.UiConfig; +import org.chromium.chrome.browser.ntp.cards.ActionItem; import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter; import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView; import org.chromium.chrome.browser.ntp.cards.SuggestionsCategoryInfo; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; import org.chromium.chrome.browser.profiles.MostVisitedSites.MostVisitedURLsObserver; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.suggestions.SuggestionsMetricsReporter; +import org.chromium.chrome.browser.suggestions.SuggestionsRanker; import org.chromium.chrome.test.ChromeActivityTestCaseBase; import org.chromium.chrome.test.util.RenderUtils.ViewRenderer; @@ -128,51 +131,30 @@ int fullCategory = 0; @CategoryInt int minimalCategory = 1; - SnippetArticle shortSnippet = new SnippetArticle( - fullCategory, - "id1", - "Snippet", - "Publisher", - "Preview Text", - "www.google.com", - 1466614774, // Timestamp - 10f, // Score - 0); // Position + SnippetArticle shortSnippet = new SnippetArticle(fullCategory, "id1", "Snippet", + "Publisher", "Preview Text", "www.google.com", + 1466614774, // Timestamp + 10f); // Score shortSnippet.setThumbnailBitmap(BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.signin_promo_illustration)); - SnippetArticle longSnippet = new SnippetArticle( - fullCategory, - "id2", + SnippetArticle longSnippet = new SnippetArticle(fullCategory, "id2", new String(new char[20]).replace("\0", "Snippet "), new String(new char[20]).replace("\0", "Publisher "), - new String(new char[80]).replace("\0", "Preview Text "), - "www.google.com", - 1466614074, // Timestamp - 20f, // Score - 1); // Position + new String(new char[80]).replace("\0", "Preview Text "), "www.google.com", + 1466614074, // Timestamp + 20f); // Score - SnippetArticle minimalSnippet = new SnippetArticle( - minimalCategory, - "id3", - new String(new char[20]).replace("\0", "Bookmark "), - "Publisher", - "This should not be displayed", - "www.google.com", - 1466614774, // Timestamp - 10f, // Score - 0); // Position + SnippetArticle minimalSnippet = new SnippetArticle(minimalCategory, "id3", + new String(new char[20]).replace("\0", "Bookmark "), "Publisher", + "This should not be displayed", "www.google.com", + 1466614774, // Timestamp + 10f); // Score - SnippetArticle minimalSnippet2 = new SnippetArticle( - minimalCategory, - "id4", - "Bookmark", - "Publisher", - "This should not be displayed", - "www.google.com", - 1466614774, // Timestamp - 10f, // Score - 0); // Position + SnippetArticle minimalSnippet2 = new SnippetArticle(minimalCategory, "id4", "Bookmark", + "Publisher", "This should not be displayed", "www.google.com", + 1466614774, // Timestamp + 10f); // Score mSnippetsSource.setInfoForCategory( fullCategory, new SuggestionsCategoryInfo(fullCategory, "Section Title", @@ -212,6 +194,8 @@ // TODO(dgn): provide a RecyclerView if we need to test the context menu. private ContextMenuManager mContextMenuManager = new ContextMenuManager(getActivity(), this, null); + private SuggestionsMetricsReporter mSuggestionsMetricsReporter = + new DummySuggestionsMetricsReporter(); @Override public void getLocalFaviconImageForURL( @@ -265,23 +249,7 @@ } @Override - public void trackSnippetsPageImpression(int[] categories, int[] suggestionsPerCategory) {} - - @Override - public void trackSnippetImpression(SnippetArticle article) {} - - @Override - public void trackSnippetMenuOpened(SnippetArticle article) {} - - @Override - public void trackSnippetCategoryActionImpression(int category, int position) {} - - @Override - public void trackSnippetCategoryActionClick(int category, int position) {} - - @Override - public void openSnippet( - int windowOpenDisposition, SnippetArticle article, int categoryIndex) { + public void openSnippet(int windowOpenDisposition, SnippetArticle article) { throw new UnsupportedOperationException(); } @@ -363,5 +331,32 @@ public ContextMenuManager getContextMenuManager() { return mContextMenuManager; } + + public SuggestionsMetricsReporter getSuggestionsMetricsReporter() { + return mSuggestionsMetricsReporter; + } + } + + private static class DummySuggestionsMetricsReporter implements SuggestionsMetricsReporter { + @Override + public void onPageShown(int[] categories, int[] suggestionsPerCategory) {} + + @Override + public void onSuggestionShown(SnippetArticle suggestion) {} + + @Override + public void onSuggestionOpened(SnippetArticle suggestion, int windowOpenDisposition) {} + + @Override + public void onSuggestionMenuOpened(SnippetArticle suggestion) {} + + @Override + public void onMoreButtonShown(@CategoryInt ActionItem category) {} + + @Override + public void onMoreButtonClicked(@CategoryInt ActionItem category) {} + + @Override + public void setRanker(SuggestionsRanker suggestionsRanker) {} } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsTestUtils.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsTestUtils.java index 0979f149..df22287 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsTestUtils.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsTestUtils.java
@@ -19,27 +19,54 @@ public final class ContentSuggestionsTestUtils { private ContentSuggestionsTestUtils() {} - public static List<SnippetArticle> createDummySuggestions(int count) { + public static List<SnippetArticle> createDummySuggestions( + int count, @CategoryInt int category) { List<SnippetArticle> suggestions = new ArrayList<>(); for (int index = 0; index < count; index++) { - suggestions.add(new SnippetArticle(KnownCategories.BOOKMARKS, - "https://site.com/url" + index, "title" + index, "pub" + index, "txt" + index, - "https://site.com/url" + index, 0, 0, 0)); + suggestions.add( + new SnippetArticle(category, "https://site.com/url" + index, "title" + index, + "pub" + index, "txt" + index, "https://site.com/url" + index, 0, 0)); } return suggestions; } - /** Registers a category that has a reload action and is shown if empty. */ - public static void registerCategory(FakeSuggestionsSource suggestionsSource, + /** + * @deprecated The hardcoded category is a common source of bugs. Prefer + * {@link #createDummySuggestions(int, int)} + */ + @Deprecated + public static List<SnippetArticle> createDummySuggestions(int count) { + return createDummySuggestions(count, KnownCategories.BOOKMARKS); + } + + /** + * Registers a category according to the provided category info. + * @return the suggestions added to the newly registered category. + */ + public static List<SnippetArticle> registerCategory(FakeSuggestionsSource suggestionsSource, @CategoryInt int category, int suggestionCount) { + // Important: showIfEmpty flag to true. + SuggestionsCategoryInfo categoryInfo = + new CategoryInfoBuilder(category).withReloadAction().showIfEmpty().build(); + return registerCategory(suggestionsSource, categoryInfo, suggestionCount); + } + + /** + * Registers a category that has a reload action and is shown if empty. + * @return the suggestions added to the newly registered category. + */ + public static List<SnippetArticle> registerCategory(FakeSuggestionsSource suggestionsSource, + SuggestionsCategoryInfo categoryInfo, int suggestionCount) { // FakeSuggestionSource does not provide suggestions if the category's status is not // AVAILABLE. - suggestionsSource.setStatusForCategory(category, CategoryStatus.AVAILABLE); - // Important: showIfEmpty flag to true. - suggestionsSource.setInfoForCategory(category, - new CategoryInfoBuilder(category).withReloadAction().showIfEmpty().build()); - suggestionsSource.setSuggestionsForCategory( - category, createDummySuggestions(suggestionCount)); + suggestionsSource.setStatusForCategory( + categoryInfo.getCategory(), CategoryStatus.AVAILABLE); + suggestionsSource.setInfoForCategory(categoryInfo.getCategory(), categoryInfo); + + List<SnippetArticle> suggestions = + createDummySuggestions(suggestionCount, categoryInfo.getCategory()); + suggestionsSource.setSuggestionsForCategory(categoryInfo.getCategory(), suggestions); + return suggestions; } public static String viewTypeToString(@ItemViewType int viewType) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java index 2c50fc0..6b3fa15 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -67,6 +67,7 @@ import org.chromium.chrome.browser.signin.SigninManager; import org.chromium.chrome.browser.signin.SigninManager.SignInAllowedObserver; import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver; +import org.chromium.chrome.browser.suggestions.SuggestionsMetricsReporter; import org.chromium.testing.local.LocalRobolectricTestRunner; import java.util.ArrayList; @@ -210,6 +211,8 @@ new CategoryInfoBuilder(category).showIfEmpty().build()); when(mNewTabPageManager.getSuggestionsSource()).thenReturn(mSource); + when(mNewTabPageManager.getSuggestionsMetricsReporter()) + .thenReturn(mock(SuggestionsMetricsReporter.class)); when(mNewTabPageManager.isCurrentPage()).thenReturn(true); reloadNtp(); @@ -232,7 +235,8 @@ assertItemsFor(sectionWithStatusCard().withProgress()); final int numSuggestions = 3; - List<SnippetArticle> suggestions = createDummySuggestions(numSuggestions); + List<SnippetArticle> suggestions = + createDummySuggestions(numSuggestions, KnownCategories.ARTICLES); mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, suggestions); @@ -245,16 +249,18 @@ @Test @Feature({"Ntp"}) public void testSuggestionLoadingInitiallyEmpty() { + final int category = KnownCategories.ARTICLES; + // If we don't get anything, we should be in the same situation as the initial one. - mSource.setSuggestionsForCategory( - KnownCategories.ARTICLES, new ArrayList<SnippetArticle>()); + mSource.setSuggestionsForCategory(category, new ArrayList<SnippetArticle>()); assertItemsFor(sectionWithStatusCard().withProgress()); // We should load new suggestions when we get notified about them. final int numSuggestions = 5; - List<SnippetArticle> suggestions = createDummySuggestions(numSuggestions); - mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); - mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, suggestions); + + List<SnippetArticle> suggestions = createDummySuggestions(numSuggestions, category); + mSource.setStatusForCategory(category, CategoryStatus.AVAILABLE); + mSource.setSuggestionsForCategory(category, suggestions); assertItemsFor(section(numSuggestions)); } @@ -265,7 +271,7 @@ @Test @Feature({"Ntp"}) public void testSuggestionClearing() { - List<SnippetArticle> suggestions = createDummySuggestions(4); + List<SnippetArticle> suggestions = createDummySuggestions(4, KnownCategories.ARTICLES); mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, suggestions); assertItemsFor(section(4)); @@ -281,7 +287,7 @@ assertItemsFor(sectionWithStatusCard()); // The adapter should now be waiting for new suggestions. - suggestions = createDummySuggestions(6); + suggestions = createDummySuggestions(6, KnownCategories.ARTICLES); mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, suggestions); assertItemsFor(section(6)); @@ -293,7 +299,7 @@ @Test @Feature({"Ntp"}) public void testSuggestionLoadingBlock() { - List<SnippetArticle> suggestions = createDummySuggestions(3); + List<SnippetArticle> suggestions = createDummySuggestions(3, KnownCategories.ARTICLES); // By default, status is INITIALIZING, so we can load suggestions. mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); @@ -301,8 +307,8 @@ assertItemsFor(section(3)); // Add another snippet. - suggestions.add(new SnippetArticle(0, "https://site.com/url1", "title1", "pub1", "txt1", - "https://site.com/url1", 0, 0, 0)); + suggestions.add(new SnippetArticle(KnownCategories.ARTICLES, "https://site.com/url1", + "title1", "pub1", "txt1", "https://site.com/url1", 0, 0)); // When snippets are disabled, we should not be able to load them. mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.SIGNED_OUT); @@ -350,7 +356,7 @@ @Test @Feature({"Ntp"}) public void testSectionClearingWhenUnavailable() { - List<SnippetArticle> snippets = createDummySuggestions(5); + List<SnippetArticle> snippets = createDummySuggestions(5, KnownCategories.ARTICLES); mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, snippets); assertItemsFor(section(5)); @@ -382,7 +388,7 @@ @Test @Feature({"Ntp"}) public void testUIUntouchedWhenNotProvided() { - List<SnippetArticle> snippets = createDummySuggestions(4); + List<SnippetArticle> snippets = createDummySuggestions(4, KnownCategories.ARTICLES); mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, snippets); assertItemsFor(section(4)); @@ -402,11 +408,11 @@ @Test @Feature({"Ntp"}) public void testUIUpdatesOnNewSuggestionsWhenOtherSectionSeen() { - List<SnippetArticle> snippets = createDummySuggestions(4); + List<SnippetArticle> snippets = createDummySuggestions(4, KnownCategories.ARTICLES); mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, snippets); - List<SnippetArticle> bookmarks = createDummySuggestions(2); + List<SnippetArticle> bookmarks = createDummySuggestions(2, KnownCategories.BOOKMARKS); mSource.setStatusForCategory(KnownCategories.BOOKMARKS, CategoryStatus.AVAILABLE); mSource.setInfoForCategory(KnownCategories.BOOKMARKS, new CategoryInfoBuilder(KnownCategories.BOOKMARKS).showIfEmpty().build()); @@ -419,7 +425,7 @@ .getSectionForTesting(KnownCategories.BOOKMARKS) .childSeen(2); - List<SnippetArticle> newSnippets = createDummySuggestions(3); + List<SnippetArticle> newSnippets = createDummySuggestions(3, KnownCategories.ARTICLES); mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, newSnippets); assertItemsFor(section(3), section(2)); @@ -434,12 +440,11 @@ @Test @Feature({"Ntp"}) public void testUIUpdatesOnNewSuggestionsWhenFirstOfOtherSectionIsSeen() { - List<SnippetArticle> snippets = createDummySuggestions(4); - SnippetArticle earlier = snippets.get(0); + List<SnippetArticle> snippets = createDummySuggestions(4, KnownCategories.ARTICLES); mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, snippets); - List<SnippetArticle> bookmarks = createDummySuggestions(1); + List<SnippetArticle> bookmarks = createDummySuggestions(1, KnownCategories.BOOKMARKS); mSource.setStatusForCategory(KnownCategories.BOOKMARKS, CategoryStatus.AVAILABLE); mSource.setInfoForCategory(KnownCategories.BOOKMARKS, new CategoryInfoBuilder(KnownCategories.BOOKMARKS).showIfEmpty().build()); @@ -452,7 +457,7 @@ .getSectionForTesting(KnownCategories.BOOKMARKS) .childSeen(1); - List<SnippetArticle> newSnippets = createDummySuggestions(3); + List<SnippetArticle> newSnippets = createDummySuggestions(3, KnownCategories.ARTICLES); SnippetArticle newer = newSnippets.get(0); mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, newSnippets); @@ -481,7 +486,8 @@ assertItemsFor(sectionWithStatusCard().withProgress()); // 1.2 - With suggestions - List<SnippetArticle> articles = Collections.unmodifiableList(createDummySuggestions(3)); + List<SnippetArticle> articles = + Collections.unmodifiableList(createDummySuggestions(3, category)); suggestionsSource.setStatusForCategory(category, CategoryStatus.AVAILABLE); suggestionsSource.setSuggestionsForCategory(category, articles); assertItemsFor(section(3)); @@ -536,7 +542,8 @@ assertItemsFor(sectionWithStatusCard().withActionButton().withProgress()); // 1.2 - With suggestions. - List<SnippetArticle> articles = Collections.unmodifiableList(createDummySuggestions(3)); + List<SnippetArticle> articles = + Collections.unmodifiableList(createDummySuggestions(3, category)); suggestionsSource.setStatusForCategory(category, CategoryStatus.AVAILABLE); suggestionsSource.setSuggestionsForCategory(category, articles); assertItemsFor(section(3).withActionButton()); @@ -581,7 +588,7 @@ @Test @Feature({"Ntp"}) public void testSuggestionInvalidated() { - List<SnippetArticle> articles = createDummySuggestions(3); + List<SnippetArticle> articles = createDummySuggestions(3, KnownCategories.ARTICLES); mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, articles); assertItemsFor(section(3)); @@ -598,13 +605,13 @@ @Test @Feature({"Ntp"}) public void testDynamicCategories() { - List<SnippetArticle> articles = createDummySuggestions(3); + List<SnippetArticle> articles = createDummySuggestions(3, KnownCategories.ARTICLES); mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, articles); assertItemsFor(section(3)); int dynamicCategory1 = 1010; - List<SnippetArticle> dynamics1 = createDummySuggestions(5); + List<SnippetArticle> dynamics1 = createDummySuggestions(5, KnownCategories.ARTICLES); mSource.setInfoForCategory(dynamicCategory1, new CategoryInfoBuilder(dynamicCategory1) .withViewAllAction() .build()); @@ -615,7 +622,7 @@ assertItemsFor(section(3), section(5).withActionButton()); int dynamicCategory2 = 1011; - List<SnippetArticle> dynamics2 = createDummySuggestions(11); + List<SnippetArticle> dynamics2 = createDummySuggestions(11, KnownCategories.ARTICLES); mSource.setInfoForCategory(dynamicCategory2, new CategoryInfoBuilder(dynamicCategory1).build()); mSource.setStatusForCategory(dynamicCategory2, CategoryStatus.AVAILABLE); @@ -752,8 +759,8 @@ final int newSuggestionCount = 7; reset(dataObserver); - suggestionsSource.setSuggestionsForCategory( - KnownCategories.ARTICLES, createDummySuggestions(newSuggestionCount)); + suggestionsSource.setSuggestionsForCategory(KnownCategories.ARTICLES, + createDummySuggestions(newSuggestionCount, KnownCategories.ARTICLES)); verify(dataObserver).onItemRangeInserted(2, newSuggestionCount); verify(dataObserver).onItemRangeChanged(5 + newSuggestionCount, 1, null); // Spacer refresh verify(dataObserver, times(2)).onItemRangeRemoved(2 + newSuggestionCount, 1); @@ -772,7 +779,7 @@ verifyNoMoreInteractions(dataObserver); reset(dataObserver); suggestionsSource.setSuggestionsForCategory( - KnownCategories.ARTICLES, createDummySuggestions(0)); + KnownCategories.ARTICLES, createDummySuggestions(0, KnownCategories.ARTICLES)); mAdapter.getSectionListForTesting().onCategoryStatusChanged( KnownCategories.ARTICLES, CategoryStatus.SIGNED_OUT); verify(dataObserver).onItemRangeRemoved(2, newSuggestionCount); @@ -946,7 +953,8 @@ // Prepare some suggestions. They should not load because the category is dismissed on // the current NTP. mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE); - mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, createDummySuggestions(1)); + mSource.setSuggestionsForCategory( + KnownCategories.ARTICLES, createDummySuggestions(1, KnownCategories.ARTICLES)); mSource.setInfoForCategory(KnownCategories.ARTICLES, new CategoryInfoBuilder(KnownCategories.ARTICLES).build()); assertEquals(4, mAdapter.getItemCount()); // TODO(dgn): rewrite with section descriptors.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java new file mode 100644 index 0000000..452238f --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java
@@ -0,0 +1,190 @@ +// Copyright 2017 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. + +package org.chromium.chrome.browser.ntp.cards; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsTestUtils.createDummySuggestions; +import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsTestUtils.registerCategory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import org.chromium.base.Callback; +import org.chromium.base.test.util.Feature; +import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager; +import org.chromium.chrome.browser.ntp.cards.ContentSuggestionsTestUtils.CategoryInfoBuilder; +import org.chromium.chrome.browser.ntp.snippets.CategoryInt; +import org.chromium.chrome.browser.ntp.snippets.FakeSuggestionsSource; +import org.chromium.chrome.browser.ntp.snippets.KnownCategories; +import org.chromium.chrome.browser.ntp.snippets.SectionHeaderViewHolder; +import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; +import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder; +import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; +import org.chromium.chrome.browser.suggestions.SuggestionsMetricsReporter; +import org.chromium.testing.local.LocalRobolectricTestRunner; + +import java.util.Collections; +import java.util.List; + +/** + * Unit tests for {@link SuggestionsSection}. + */ +@RunWith(LocalRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class SectionListTest { + @Mock + private NewTabPageManager mNtpManager; + @Mock + private OfflinePageBridge mOfflinePageBridge; + @Mock + private SuggestionsMetricsReporter mMetricsReporter; + private FakeSuggestionsSource mSuggestionSource; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mSuggestionSource = new FakeSuggestionsSource(); + + when(mNtpManager.getSuggestionsSource()).thenReturn(mSuggestionSource); + when(mNtpManager.getSuggestionsMetricsReporter()).thenReturn(mMetricsReporter); + } + + @Test + @Feature({"Ntp"}) + public void testGetSuggestionRank() { + // Setup the section list the following way: + // + // Item type | local rank | global rank + // -----------+------------+------------- + // HEADER | | + // ARTICLE | 0 | 0 + // ARTICLE | 1 | 1 + // ARTICLE | 2 | 2 + // HEADER | | + // STATUS | | + // ACTION | 0 | + // BOOKMARK | 0 | 3 + // BOOKMARK | 1 | 4 + // BOOKMARK | 2 | 5 + // BOOKMARK | 3 | 6 + List<SnippetArticle> articles = + registerCategory(mSuggestionSource, KnownCategories.ARTICLES, 3); + registerCategory(mSuggestionSource, KnownCategories.DOWNLOADS, 0); + List<SnippetArticle> bookmarks = + registerCategory(mSuggestionSource, KnownCategories.BOOKMARKS, 4); + + SectionList sectionList = new SectionList(mNtpManager, mOfflinePageBridge); + + assertThat(articles.get(0).getGlobalRank(), equalTo(0)); + assertThat(articles.get(0).getPerSectionRank(), equalTo(0)); + assertThat(articles.get(2).getGlobalRank(), equalTo(2)); + assertThat(articles.get(2).getPerSectionRank(), equalTo(2)); + assertThat(bookmarks.get(1).getGlobalRank(), equalTo(4)); + assertThat(bookmarks.get(1).getPerSectionRank(), equalTo(1)); + + // Test ranks after changes: remove then add some items. + @SuppressWarnings("unchecked") + Callback<String> cb = mock(Callback.class); + sectionList.dismissItem(2, cb); + + // Using sublists to skip the first items and avoid using existing ones + List<SnippetArticle> newArticles = + createDummySuggestions(5, KnownCategories.ARTICLES).subList(3, 5); + List<SnippetArticle> newBookmarks = + createDummySuggestions(6, KnownCategories.BOOKMARKS).subList(4, 6); + + sectionList.onMoreSuggestions(KnownCategories.ARTICLES, newArticles); + sectionList.onMoreSuggestions(KnownCategories.BOOKMARKS, newBookmarks); + + // After the changes we should have: + // Item type | local rank | global rank + // -----------+------------+------------- + // HEADER | | + // ARTICLE | 0 | 0 + // ARTICLE | 1 | 1 + // | - | - (deleted) + // ARTICLE | 3 | 7 (new) + // ARTICLE | 4 | 8 (new) + // HEADER | | + // STATUS | | + // ACTION | 0 | + // BOOKMARK | 0 | 3 + // BOOKMARK | 1 | 4 + // BOOKMARK | 2 | 5 + // BOOKMARK | 3 | 6 + // BOOKMARK | 4 | 9 (new) + // BOOKMARK | 5 | 10 (new) + + // The old ranks should not change. + assertThat(articles.get(0).getGlobalRank(), equalTo(0)); + assertThat(articles.get(0).getPerSectionRank(), equalTo(0)); + assertThat(articles.get(2).getGlobalRank(), equalTo(2)); + assertThat(articles.get(2).getPerSectionRank(), equalTo(2)); + assertThat(bookmarks.get(1).getGlobalRank(), equalTo(4)); + assertThat(bookmarks.get(1).getPerSectionRank(), equalTo(1)); + + // New ranks take into account the previously existing items. + assertThat(newArticles.get(1).getGlobalRank(), equalTo(8)); + assertThat(newArticles.get(1).getPerSectionRank(), equalTo(4)); + assertThat(newBookmarks.get(1).getGlobalRank(), equalTo(10)); + assertThat(newBookmarks.get(1).getPerSectionRank(), equalTo(5)); + } + + @Test + @Feature({"Ntp"}) + public void testGetActionItemRank() { + registerCategory(mSuggestionSource, KnownCategories.ARTICLES, 0); + registerCategory(mSuggestionSource, + new CategoryInfoBuilder(KnownCategories.DOWNLOADS).withViewAllAction().build(), 3); + + SectionList sectionList = new SectionList(mNtpManager, mOfflinePageBridge); + bindViewHolders(sectionList); + + assertThat(sectionList.getSectionForTesting(KnownCategories.ARTICLES) + .getActionItem() + .getPerSectionRank(), + equalTo(0)); + assertThat(sectionList.getSectionForTesting(KnownCategories.DOWNLOADS) + .getActionItem() + .getPerSectionRank(), + equalTo(3)); + } + + private static void bindViewHolders(InnerNode node) { + bindViewHolders(node, 0, node.getItemCount()); + } + + private static void bindViewHolders(InnerNode node, int startIndex, int endIndex) { + for (int i = startIndex; i < endIndex; ++i) { + node.onBindViewHolder( + makeViewHolder(node.getItemViewType(i)), i, Collections.emptyList()); + } + } + + private static NewTabPageViewHolder makeViewHolder(@CategoryInt int viewType) { + switch (viewType) { + case ItemViewType.SNIPPET: + return mock(SnippetArticleViewHolder.class); + case ItemViewType.HEADER: + return mock(SectionHeaderViewHolder.class); + case ItemViewType.STATUS: + return mock(StatusCardViewHolder.class); + case ItemViewType.ACTION: + return mock(ActionItem.ViewHolder.class); + case ItemViewType.PROGRESS: + return mock(ProgressViewHolder.class); + default: + return mock(NewTabPageViewHolder.class); + } + } +}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java index 1336e85..5d965e1 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
@@ -39,6 +39,8 @@ import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource; import org.chromium.chrome.browser.offlinepages.OfflinePageItem; +import org.chromium.chrome.browser.suggestions.SuggestionsMetricsReporter; +import org.chromium.chrome.browser.suggestions.SuggestionsRanker; import org.chromium.testing.local.LocalRobolectricTestRunner; import java.util.Arrays; @@ -462,7 +464,8 @@ } private SuggestionsSection createSection(SuggestionsCategoryInfo info) { - SuggestionsSection section = new SuggestionsSection(mDelegate, mManager, mBridge, info); + SuggestionsSection section = new SuggestionsSection( + mDelegate, mManager, mock(SuggestionsRanker.class), mBridge, info); section.setParent(mParent); return section; } @@ -475,6 +478,8 @@ SuggestionsSource suggestionsSource = mock(SuggestionsSource.class); NewTabPageManager manager = mock(NewTabPageManager.class); when(manager.getSuggestionsSource()).thenReturn(suggestionsSource); + when(manager.getSuggestionsMetricsReporter()) + .thenReturn(mock(SuggestionsMetricsReporter.class)); if (action != ActionItem.ACTION_NONE) { section.getActionItem().performAction(manager);
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 8f4d0b4..dc4b5f27 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -15604,6 +15604,16 @@ <message name="IDS_NATIVE_ANDROID_HISTORY_MANAGER_DESCRIPTION" desc="Description of the flag that enables the native Android history UI." translateable="false"> Show the native Android UI for browsing history. </message> + + <!-- Play Services LSD permission prompt chrome://flags strings --> + <if expr="is_android"> + <message name="IDS_FLAGS_LSD_PERMISSION_PROMPT_NAME" desc="Name for the flag to enable LSD permission prompts on Android" translateable="false"> + Location Settings Dialog Permission Prompt + </message> + <message name="IDS_FLAGS_LSD_PERMISSION_PROMPT_DESCRIPTION" desc="Description for the flag to enable LSD permission prompts on Android." translateable="false"> + Whether to use the Google Play Services Location Settings Dialog permission dialog. + </message> + </if> </if> </messages> </release>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 6df2def..deff54ca 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2185,7 +2185,13 @@ #endif // OS_ANDROID {"enable-faster-location-reload", IDS_FLAGS_FASTER_LOCATION_RELOAD_NAME, IDS_FLAGS_FASTER_LOCATION_RELOAD_DESCRIPTION, kOsAll, - FEATURE_VALUE_TYPE(features::kFasterLocationReload)} + FEATURE_VALUE_TYPE(features::kFasterLocationReload)}, +#if defined(OS_ANDROID) + {"lsd-permission-prompt", IDS_FLAGS_LSD_PERMISSION_PROMPT_NAME, + IDS_FLAGS_LSD_PERMISSION_PROMPT_DESCRIPTION, kOsAndroid, + FEATURE_VALUE_TYPE(features::kLsdPermissionPrompt)}, +#endif + // NOTE: Adding new command-line switches requires adding corresponding // entries to enum "LoginCustomFlags" in histograms.xml. See note in
diff --git a/chrome/browser/android/ntp/ntp_snippets_bridge.cc b/chrome/browser/android/ntp/ntp_snippets_bridge.cc index d920bfd..3bc97fa3 100644 --- a/chrome/browser/android/ntp/ntp_snippets_bridge.cc +++ b/chrome/browser/android/ntp/ntp_snippets_bridge.cc
@@ -262,7 +262,7 @@ const JavaParamRef<jstring>& jurl, jint global_position, jint j_category_id, - jint category_position, + jint position_in_category, const JavaParamRef<jstring>& id_within_category) { Category category = Category::FromIDValue(j_category_id); @@ -272,14 +272,14 @@ history_service_->QueryURL( GURL(ConvertJavaStringToUTF8(env, jurl)), /*want_visits=*/false, base::Bind( - [](int global_position, Category category, int category_position, + [](int global_position, Category category, int position_in_category, bool success, const history::URLRow& row, const history::VisitVector& visit_vector) { bool visited = success && row.visit_count() != 0; ntp_snippets::metrics::OnSuggestionDismissed( - global_position, category, category_position, visited); + global_position, category, position_in_category, visited); }, - global_position, category, category_position), + global_position, category, position_in_category), &tracker_); } @@ -325,7 +325,7 @@ const JavaParamRef<jobject>& obj, jint global_position, jint j_category_id, - jint category_position, + jint position_in_category, jlong publish_timestamp_ms, jfloat score) { PrefService* pref_service = ProfileManager::GetLastUsedProfile()->GetPrefs(); @@ -334,8 +334,8 @@ ntp_snippets::prefs::kLastSuccessfulBackgroundFetchTime)); ntp_snippets::metrics::OnSuggestionShown( - global_position, Category::FromIDValue(j_category_id), category_position, - base::Time::FromJavaTime(publish_timestamp_ms), + global_position, Category::FromIDValue(j_category_id), + position_in_category, base::Time::FromJavaTime(publish_timestamp_ms), last_background_fetch_time, score); if (global_position == 0) { content_suggestions_service_->user_classifier()->OnEvent( @@ -348,19 +348,19 @@ jint global_position, jint j_category_id, jint category_index, - jint category_position, + jint position_in_category, jlong publish_timestamp_ms, jfloat score, int windowOpenDisposition) { + const Category category = Category::FromIDValue(j_category_id); ntp_snippets::metrics::OnSuggestionOpened( - global_position, Category::FromIDValue(j_category_id), category_position, + global_position, category, category_index, position_in_category, base::Time::FromJavaTime(publish_timestamp_ms), score, static_cast<WindowOpenDisposition>(windowOpenDisposition)); // TODO(vitaliii): Add ContentSuggestionsService::OnSuggestionOpened and // notify the ranker and the classifier there instead. Do not expose both of // them at all. See crbug.com/674080. - content_suggestions_service_->category_ranker()->OnSuggestionOpened( - Category::FromIDValue(j_category_id)); + content_suggestions_service_->category_ranker()->OnSuggestionOpened(category); content_suggestions_service_->user_classifier()->OnEvent( ntp_snippets::UserClassifier::Metric::SUGGESTIONS_USED); } @@ -369,12 +369,13 @@ const JavaParamRef<jobject>& obj, jint global_position, jint j_category_id, - jint category_position, + jint position_in_category, jlong publish_timestamp_ms, jfloat score) { ntp_snippets::metrics::OnSuggestionMenuOpened( - global_position, Category::FromIDValue(j_category_id), category_position, - base::Time::FromJavaTime(publish_timestamp_ms), score); + global_position, Category::FromIDValue(j_category_id), + position_in_category, base::Time::FromJavaTime(publish_timestamp_ms), + score); } void NTPSnippetsBridge::OnMoreButtonShown(JNIEnv* env,
diff --git a/chrome/browser/android/ntp/ntp_snippets_bridge.h b/chrome/browser/android/ntp/ntp_snippets_bridge.h index 2e3f051..f7af988 100644 --- a/chrome/browser/android/ntp/ntp_snippets_bridge.h +++ b/chrome/browser/android/ntp/ntp_snippets_bridge.h
@@ -76,7 +76,7 @@ const base::android::JavaParamRef<jstring>& jurl, jint global_position, jint j_category_id, - jint category_position, + jint position_in_category, const base::android::JavaParamRef<jstring>& id_within_category); void DismissCategory(JNIEnv* env, @@ -97,7 +97,7 @@ const base::android::JavaParamRef<jobject>& obj, jint global_position, jint j_category_id, - jint category_position, + jint position_in_category, jlong publish_timestamp_ms, jfloat score); @@ -106,7 +106,7 @@ jint global_position, jint j_category_id, jint category_index, - jint category_position, + jint position_in_category, jlong publish_timestamp_ms, jfloat score, int windowOpenDisposition); @@ -115,7 +115,7 @@ const base::android::JavaParamRef<jobject>& obj, jint global_position, jint j_category_id, - jint category_position, + jint position_in_category, jlong publish_timestamp_ms, jfloat score);
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc index 1384c59..cf8c3e5 100644 --- a/chrome/browser/android/webapk/webapk_installer.cc +++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -310,9 +310,9 @@ env, java_ref_, java_file_path); } -bool WebApkInstaller::HasGooglePlayWebApkInstallDelegate() { +bool WebApkInstaller::CanUseGooglePlayInstallService() { JNIEnv* env = base::android::AttachCurrentThread(); - return Java_WebApkInstaller_hasGooglePlayWebApkInstallDelegate( + return Java_WebApkInstaller_canUseGooglePlayInstallService( env, java_ref_); } @@ -423,7 +423,7 @@ return; } - if (HasGooglePlayWebApkInstallDelegate()) { + if (CanUseGooglePlayInstallService()) { int version = 1; base::StringToInt(response->version(), &version); if (!InstallOrUpdateWebApkFromGooglePlay(
diff --git a/chrome/browser/android/webapk/webapk_installer.h b/chrome/browser/android/webapk/webapk_installer.h index a987e04..94b807c 100644 --- a/chrome/browser/android/webapk/webapk_installer.h +++ b/chrome/browser/android/webapk/webapk_installer.h
@@ -124,10 +124,11 @@ JNIEnv* env, const base::android::ScopedJavaLocalRef<jstring>& java_file_path); - // Returns whether the Google Play install delegate is available. + // Returns whether Google Play Services can be used and the install delegate + // is available. // Note: it is possible that this delegate is null even when installing // WebAPKs using Google Play is enabled. - virtual bool HasGooglePlayWebApkInstallDelegate(); + virtual bool CanUseGooglePlayInstallService(); // Called when the package name of the WebAPK is available and the install // or update request is handled by Google Play.
diff --git a/chrome/browser/android/webapk/webapk_installer_unittest.cc b/chrome/browser/android/webapk/webapk_installer_unittest.cc index 84265e7..1fddb62 100644 --- a/chrome/browser/android/webapk/webapk_installer_unittest.cc +++ b/chrome/browser/android/webapk/webapk_installer_unittest.cc
@@ -54,17 +54,17 @@ // WebApkInstaller subclass where // WebApkInstaller::StartInstallingDownloadedWebApk() and // WebApkInstaller::StartUpdateUsingDownloadedWebApk() and -// WebApkInstaller::HasGooglePlayWebApkInstallDelegate() and +// WebApkInstaller::CanUseGooglePlayInstallService() and // WebApkInstaller::InstallOrUpdateWebApkFromGooglePlay() are stubbed out. class TestWebApkInstaller : public WebApkInstaller { public: TestWebApkInstaller(content::BrowserContext* browser_context, const ShortcutInfo& shortcut_info, const SkBitmap& shortcut_icon, - bool has_google_play_webapk_install_delegate) + bool can_use_google_play_install_service) : WebApkInstaller(browser_context, shortcut_info, shortcut_icon), - has_google_play_webapk_install_delegate_( - has_google_play_webapk_install_delegate) {} + can_use_google_play_install_service_( + can_use_google_play_install_service) {} bool StartInstallingDownloadedWebApk( JNIEnv* env, @@ -80,8 +80,8 @@ return true; } - bool HasGooglePlayWebApkInstallDelegate() override { - return has_google_play_webapk_install_delegate_; + bool CanUseGooglePlayInstallService() override { + return can_use_google_play_install_service_; } bool InstallOrUpdateWebApkFromGooglePlay(const std::string& package_name, @@ -98,8 +98,9 @@ } private: - // Whether the Google Play install delegate is available. - bool has_google_play_webapk_install_delegate_; + // Whether the Google Play Services can be used and the install delegate is + // available. + bool can_use_google_play_install_service_; DISALLOW_COPY_AND_ASSIGN(TestWebApkInstaller); }; @@ -111,12 +112,13 @@ const GURL& best_icon_url) : browser_context_(browser_context), best_icon_url_(best_icon_url), - has_google_play_webapk_install_delegate_(false) {} + can_use_google_play_install_service_(false) {} ~WebApkInstallerRunner() {} - void SetHasGooglePlayWebApkInstallDelegate(bool has_delegate) { - has_google_play_webapk_install_delegate_ = has_delegate; + void SetCanUseGooglePlayInstallService( + bool can_use_google_play_install_service) { + can_use_google_play_install_service_ = can_use_google_play_install_service; } void RunInstallWebApk() { @@ -151,7 +153,7 @@ // WebApkInstaller owns itself. WebApkInstaller* installer = new TestWebApkInstaller(browser_context_, info, SkBitmap(), - has_google_play_webapk_install_delegate_); + can_use_google_play_install_service_); installer->SetTimeoutMs(100); return installer; } @@ -181,8 +183,9 @@ // Whether the installation process succeeded. bool success_; - // Whether the Google Play install delegate is available. - bool has_google_play_webapk_install_delegate_; + // Whether Google Play Service can be used and the install delegate is + // available. + bool can_use_google_play_install_service_; DISALLOW_COPY_AND_ASSIGN(WebApkInstallerRunner); }; @@ -427,7 +430,7 @@ // Test installation succeeds using Google Play. TEST_F(WebApkInstallerTest, InstallFromGooglePlaySuccess) { std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner(); - runner->SetHasGooglePlayWebApkInstallDelegate(true); + runner->SetCanUseGooglePlayInstallService(true); runner->RunInstallWebApk(); EXPECT_TRUE(runner->success()); }
diff --git a/chrome/browser/chrome_content_utility_manifest_overlay.json b/chrome/browser/chrome_content_utility_manifest_overlay.json index 8dd89f3..eb6541048 100644 --- a/chrome/browser/chrome_content_utility_manifest_overlay.json +++ b/chrome/browser/chrome_content_utility_manifest_overlay.json
@@ -8,6 +8,7 @@ "chrome::mojom::ResourceUsageReporter", "chrome::mojom::ShellHandler", "extensions::mojom::MediaParser", + "extensions::mojom::WiFiCredentialsGetter", "net::interfaces::ProxyResolverFactory", "safe_json::mojom::SafeJsonParser" ]
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc b/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc new file mode 100644 index 0000000..5e1e31f --- /dev/null +++ b/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc
@@ -0,0 +1,79 @@ +// Copyright 2017 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 "base/bind.h" +#include "base/callback.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "chrome/browser/extensions/api/networking_private/networking_private_credentials_getter.h" +#include "chrome/common/extensions/wifi_credentials_getter.mojom.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "content/public/browser/browser_thread.h" + +#if !defined(OS_WIN) +#error This test is OS_WIN only. +#endif + +class NetworkingPrivateCredentialsGetterTest : public InProcessBrowserTest { + public: + NetworkingPrivateCredentialsGetterTest() = default; + + void RunTest(bool use_test_network) { + base::RunLoop run_loop; + quit_closure_ = run_loop.QuitClosure(); + + if (use_test_network) + network_ = extensions::mojom::WiFiCredentialsGetter::kWiFiTestNetwork; + + done_called_ = false; + content::BrowserThread::PostBlockingPoolTask( + FROM_HERE, + base::Bind(&NetworkingPrivateCredentialsGetterTest::GetCredentials, + base::Unretained(this))); + run_loop.Run(); + + EXPECT_TRUE(done_called_); + } + + private: + void GetCredentials() { + std::unique_ptr<extensions::NetworkingPrivateCredentialsGetter> getter( + extensions::NetworkingPrivateCredentialsGetter::Create()); + getter->Start( + network_, "public_key", + base::Bind(&NetworkingPrivateCredentialsGetterTest::CredentialsDone, + base::Unretained(this))); + } + + void CredentialsDone(const std::string& key_data, const std::string& error) { + done_called_ = true; + + if (!network_.empty()) { + EXPECT_EQ(network_, key_data); + EXPECT_EQ("", error); + } else { + EXPECT_EQ("", key_data); + EXPECT_FALSE(error.empty()); + } + + content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, + quit_closure_); + } + + base::Closure quit_closure_; + std::string network_; + bool done_called_; + + DISALLOW_COPY_AND_ASSIGN(NetworkingPrivateCredentialsGetterTest); +}; + +IN_PROC_BROWSER_TEST_F(NetworkingPrivateCredentialsGetterTest, + GetCredentialsSuccess) { + RunTest(true); +} + +IN_PROC_BROWSER_TEST_F(NetworkingPrivateCredentialsGetterTest, + GetCredentialsFailure) { + RunTest(false); +}
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_win.cc b/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_win.cc index b5a047d7..824a4a2 100644 --- a/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_win.cc +++ b/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_win.cc
@@ -10,113 +10,123 @@ #include "base/bind.h" #include "base/macros.h" #include "base/strings/string_piece.h" -#include "base/strings/stringprintf.h" -#include "base/threading/sequenced_worker_pool.h" -#include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/extensions/api/networking_private/networking_private_crypto.h" -#include "chrome/common/extensions/chrome_utility_extensions_messages.h" +#include "chrome/common/extensions/wifi_credentials_getter.mojom.h" #include "chrome/grit/generated_resources.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/utility_process_host.h" -#include "content/public/browser/utility_process_host_client.h" +#include "content/public/browser/utility_process_mojo_client.h" #include "ui/base/l10n/l10n_util.h" -using content::BrowserThread; -using content::UtilityProcessHost; -using content::UtilityProcessHostClient; -using extensions::NetworkingPrivateCredentialsGetter; - namespace { -class CredentialsGetterHostClient : public UtilityProcessHostClient { +using extensions::NetworkingPrivateCredentialsGetter; + +class CredentialsGetterHostClient { public: - explicit CredentialsGetterHostClient(const std::string& public_key); + static CredentialsGetterHostClient* Create(const std::string& public_key) { + return new CredentialsGetterHostClient(public_key); + } - // UtilityProcessHostClient - bool OnMessageReceived(const IPC::Message& message) override; - void OnProcessCrashed(int exit_code) override; - void OnProcessLaunchFailed(int error_code) override; - - // IPC message handlers. - void OnGotCredentials(const std::string& key_data, bool success); - - // Starts the utility process that gets wifi passphrase from system. - void StartProcessOnIOThread( + void GetWiFiCredentialsOnIOThread( const std::string& network_guid, const NetworkingPrivateCredentialsGetter::CredentialsCallback& callback); private: - ~CredentialsGetterHostClient() override; + explicit CredentialsGetterHostClient(const std::string& public_key) + : public_key_(public_key.begin(), public_key.end()) {} - // Public key used to encrypt results + ~CredentialsGetterHostClient() = default; + + // Credentials result handler. + void GetWiFiCredentialsDone(bool success, const std::string& key_data); + + // Report the result to |callback_|. + void ReportResult(bool success, const std::string& key_data); + + // Public key used to encrypt the result. std::vector<uint8_t> public_key_; - // Callback for reporting the result. + // Callback for reporting the encrypted result. NetworkingPrivateCredentialsGetter::CredentialsCallback callback_; + // Utility process used to get the credentials. + std::unique_ptr<content::UtilityProcessMojoClient< + extensions::mojom::WiFiCredentialsGetter>> + utility_process_mojo_client_; + + // WiFi network to get the credentials from. + std::string wifi_network_; + DISALLOW_COPY_AND_ASSIGN(CredentialsGetterHostClient); }; -CredentialsGetterHostClient::CredentialsGetterHostClient( - const std::string& public_key) - : public_key_(public_key.begin(), public_key.end()) { -} - -bool CredentialsGetterHostClient::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(CredentialsGetterHostClient, message) - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GotWiFiCredentials, OnGotCredentials) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void CredentialsGetterHostClient::OnProcessCrashed(int exit_code) { - callback_.Run( - "", base::StringPrintf("Process Crashed with code %08x.", exit_code)); -} - -void CredentialsGetterHostClient::OnProcessLaunchFailed(int error_code) { - callback_.Run("", base::StringPrintf("Process Launch Failed with code %08x.", - error_code)); -} - -void CredentialsGetterHostClient::OnGotCredentials(const std::string& key_data, - bool success) { - if (success) { - std::vector<uint8_t> ciphertext; - if (!networking_private_crypto::EncryptByteString( - public_key_, key_data, &ciphertext)) { - callback_.Run("", "Encrypt Credentials Failed"); - return; - } - - std::string base64_encoded_key_data; - base::Base64Encode( - base::StringPiece(reinterpret_cast<const char*>(ciphertext.data()), - ciphertext.size()), - &base64_encoded_key_data); - callback_.Run(base64_encoded_key_data, ""); - } else { - callback_.Run("", "Get Credentials Failed"); - } -} - -void CredentialsGetterHostClient::StartProcessOnIOThread( +void CredentialsGetterHostClient::GetWiFiCredentialsOnIOThread( const std::string& network_guid, const NetworkingPrivateCredentialsGetter::CredentialsCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + DCHECK(!utility_process_mojo_client_); + DCHECK(!callback.is_null()); + + wifi_network_ = network_guid; callback_ = callback; - UtilityProcessHost* host = - UtilityProcessHost::Create(this, base::ThreadTaskRunnerHandle::Get()); - host->SetName(l10n_util::GetStringUTF16( - IDS_UTILITY_PROCESS_WIFI_CREDENTIALS_GETTER_NAME)); - host->ElevatePrivileges(); - host->Send(new ChromeUtilityHostMsg_GetWiFiCredentials(network_guid)); + + const base::string16 utility_process_name = l10n_util::GetStringUTF16( + IDS_UTILITY_PROCESS_WIFI_CREDENTIALS_GETTER_NAME); + + utility_process_mojo_client_.reset( + new content::UtilityProcessMojoClient< + extensions::mojom::WiFiCredentialsGetter>(utility_process_name)); + utility_process_mojo_client_->set_error_callback( + base::Bind(&CredentialsGetterHostClient::GetWiFiCredentialsDone, + base::Unretained(this), false, std::string())); + + utility_process_mojo_client_->set_run_elevated(); + + utility_process_mojo_client_->Start(); // Start the utility process. + + utility_process_mojo_client_->service()->GetWiFiCredentials( + wifi_network_, + base::Bind(&CredentialsGetterHostClient::GetWiFiCredentialsDone, + base::Unretained(this))); } -CredentialsGetterHostClient::~CredentialsGetterHostClient() { +void CredentialsGetterHostClient::GetWiFiCredentialsDone( + bool success, + const std::string& key_data) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + utility_process_mojo_client_.reset(); // Terminate the utility process. + ReportResult(success, key_data); + delete this; +} + +void CredentialsGetterHostClient::ReportResult(bool success, + const std::string& key_data) { + if (!success) { + callback_.Run(std::string(), "Get Credentials Failed"); + return; + } + + if (wifi_network_ == + extensions::mojom::WiFiCredentialsGetter::kWiFiTestNetwork) { + DCHECK_EQ(wifi_network_, key_data); + callback_.Run(key_data, std::string()); + return; + } + + std::vector<uint8_t> ciphertext; + if (!networking_private_crypto::EncryptByteString(public_key_, key_data, + &ciphertext)) { + callback_.Run(std::string(), "Encrypt Credentials Failed"); + return; + } + + std::string base64_encoded_key_data; + base::Base64Encode( + base::StringPiece(reinterpret_cast<const char*>(ciphertext.data()), + ciphertext.size()), + &base64_encoded_key_data); + callback_.Run(base64_encoded_key_data, std::string()); } } // namespace @@ -126,37 +136,35 @@ class NetworkingPrivateCredentialsGetterWin : public NetworkingPrivateCredentialsGetter { public: - NetworkingPrivateCredentialsGetterWin(); + NetworkingPrivateCredentialsGetterWin() = default; void Start(const std::string& network_guid, const std::string& public_key, - const CredentialsCallback& callback) override; + const CredentialsCallback& callback) override { + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind( + &NetworkingPrivateCredentialsGetterWin::GetCredentialsOnIOThread, + network_guid, public_key, callback)); + } private: - ~NetworkingPrivateCredentialsGetterWin() override; + ~NetworkingPrivateCredentialsGetterWin() override = default; + + static void GetCredentialsOnIOThread(const std::string& network_guid, + const std::string& public_key, + const CredentialsCallback& callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + // CredentialsGetterHostClient is self deleting. + CredentialsGetterHostClient* client = + CredentialsGetterHostClient::Create(public_key); + client->GetWiFiCredentialsOnIOThread(network_guid, callback); + } DISALLOW_COPY_AND_ASSIGN(NetworkingPrivateCredentialsGetterWin); }; -NetworkingPrivateCredentialsGetterWin::NetworkingPrivateCredentialsGetterWin() { -} - -void NetworkingPrivateCredentialsGetterWin::Start( - const std::string& network_guid, - const std::string& public_key, - const CredentialsCallback& callback) { - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&CredentialsGetterHostClient::StartProcessOnIOThread, - new CredentialsGetterHostClient(public_key), - network_guid, - callback)); -} - -NetworkingPrivateCredentialsGetterWin:: - ~NetworkingPrivateCredentialsGetterWin() {} - NetworkingPrivateCredentialsGetter* NetworkingPrivateCredentialsGetter::Create() { return new NetworkingPrivateCredentialsGetterWin();
diff --git a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc index 00dd9e6d..5c94f10 100644 --- a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc +++ b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
@@ -34,9 +34,9 @@ #include "components/ntp_snippets/content_suggestions_service.h" #include "components/ntp_snippets/features.h" #include "components/ntp_snippets/ntp_snippets_constants.h" -#include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" #include "components/ntp_snippets/remote/persistent_scheduler.h" #include "components/ntp_snippets/remote/remote_suggestions_database.h" +#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h" #include "components/ntp_snippets/remote/remote_suggestions_provider_impl.h" #include "components/ntp_snippets/remote/remote_suggestions_status_service.h" #include "components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h" @@ -84,9 +84,9 @@ using ntp_snippets::CategoryRanker; using ntp_snippets::ContentSuggestionsService; using ntp_snippets::ForeignSessionsSuggestionsProvider; -using ntp_snippets::NTPSnippetsFetcher; using ntp_snippets::PersistentScheduler; using ntp_snippets::RemoteSuggestionsDatabase; +using ntp_snippets::RemoteSuggestionsFetcher; using ntp_snippets::RemoteSuggestionsProviderImpl; using ntp_snippets::RemoteSuggestionsStatusService; using ntp_snippets::SchedulingRemoteSuggestionsProvider; @@ -183,7 +183,7 @@ auto provider = base::MakeUnique<RemoteSuggestionsProviderImpl>( service, pref_service, g_browser_process->GetApplicationLocale(), service->category_ranker(), - base::MakeUnique<NTPSnippetsFetcher>( + base::MakeUnique<RemoteSuggestionsFetcher>( signin_manager, token_service, request_context, pref_service, language_model, base::Bind(&safe_json::SafeJsonParser::Parse), is_stable_channel ? google_apis::GetAPIKey()
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html index f9fe8ce7..c9a7031 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_page.html +++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -67,18 +67,17 @@ label="$i18n{linkDoctorPref}"> </settings-toggle-button> </div> - <div class="settings-box"> + <div class="settings-box" hidden="[[!pageVisibility.searchPrediction]]"> <settings-toggle-button class="start" pref="{{prefs.search.suggest_enabled}}" - label="$i18n{searchSuggestPref}" - hidden="[[!pageVisibility.searchPrediction]]"> + label="$i18n{searchSuggestPref}"> </settings-toggle-button> </div> - <div class="settings-box"> + <div class="settings-box" + hidden="[[!pageVisibility.networkPrediction]]"> <settings-toggle-button class="start" pref="{{prefs.net.network_prediction_options}}" - label="$i18n{networkPredictionEnabled}" - hidden="[[!pageVisibility.networkPrediction]]"> + label="$i18n{networkPredictionEnabled}"> </settings-toggle-button> </div> <div class="settings-box">
diff --git a/chrome/browser/ui/webui/OWNERS b/chrome/browser/ui/webui/OWNERS index 855764c9..3be2defe 100644 --- a/chrome/browser/ui/webui/OWNERS +++ b/chrome/browser/ui/webui/OWNERS
@@ -26,5 +26,7 @@ per-file signin_internals_ui*=achuith@chromium.org +per-file snippets_internals*=file://components/ntp_snippets/OWNERS + # Maintaining ownership from this file's original Chrome OS location. per-file device_log_ui*=stevenjb@chromium.org
diff --git a/chrome/browser/ui/webui/snippets_internals_message_handler.cc b/chrome/browser/ui/webui/snippets_internals_message_handler.cc index d2bca2c..4207a04 100644 --- a/chrome/browser/ui/webui/snippets_internals_message_handler.cc +++ b/chrome/browser/ui/webui/snippets_internals_message_handler.cc
@@ -29,7 +29,7 @@ #include "components/ntp_snippets/category_info.h" #include "components/ntp_snippets/features.h" #include "components/ntp_snippets/pref_names.h" -#include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" +#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h" #include "components/ntp_snippets/remote/remote_suggestions_provider.h" #include "components/ntp_snippets/switches.h" #include "components/offline_pages/core/offline_page_feature.h" @@ -306,9 +306,8 @@ SendLastRemoteSuggestionsBackgroundFetchTime(); if (remote_suggestions_provider_) { - const ntp_snippets::NTPSnippetsFetcher* fetcher = - remote_suggestions_provider_ - ->snippets_fetcher_for_testing_and_debugging(); + const ntp_snippets::RemoteSuggestionsFetcher* fetcher = + remote_suggestions_provider_->suggestions_fetcher_for_debugging(); // TODO(fhorschig): Read this string from variations directly. SendString("switch-personalized", fetcher->PersonalizationModeString()); @@ -386,8 +385,7 @@ if (remote_suggestions_provider_) { const std::string& status = - remote_suggestions_provider_ - ->snippets_fetcher_for_testing_and_debugging() + remote_suggestions_provider_->suggestions_fetcher_for_debugging() ->last_status(); if (!status.empty()) SendString("remote-status", "Finished: " + status);
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 6df346e7..0c2394d 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -107,6 +107,11 @@ "LinuxObsoleteSystemIsEndOfTheLine", base::FEATURE_DISABLED_BY_DEFAULT}; #endif +// Enables or disables the Location Settings Dialog (LSD). The LSD is an Android +// system-level geolocation permission prompt. +const base::Feature kLsdPermissionPrompt{"LsdPermissionPrompt", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Enables or disables the Material Design version of chrome://bookmarks. const base::Feature kMaterialDesignBookmarks{"MaterialDesignBookmarks", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 51d6c0b7..298f6b0 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -68,6 +68,8 @@ extern const base::Feature kLinuxObsoleteSystemIsEndOfTheLine; #endif +extern const base::Feature kLsdPermissionPrompt; + extern const base::Feature kMaterialDesignBookmarks; #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/common/extensions/BUILD.gn b/chrome/common/extensions/BUILD.gn index 763ceb7..2be3aaa 100644 --- a/chrome/common/extensions/BUILD.gn +++ b/chrome/common/extensions/BUILD.gn
@@ -28,6 +28,10 @@ "media_parser.mojom", ] + if (is_win) { + sources += [ "wifi_credentials_getter.mojom" ] + } + public_deps = [ "//mojo/common:common_custom_types", ]
diff --git a/chrome/common/extensions/chrome_utility_extensions_messages.h b/chrome/common/extensions/chrome_utility_extensions_messages.h index 51ceb4bd..2a9f3ab 100644 --- a/chrome/common/extensions/chrome_utility_extensions_messages.h +++ b/chrome/common/extensions/chrome_utility_extensions_messages.h
@@ -168,16 +168,3 @@ // Periodic status update about the progress of an operation. IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_ImageWriter_Progress, int64_t /* number of bytes processed */) - -#if defined(OS_WIN) -// Get plain-text WiFi credentials from the system (requires UAC privilege -// elevation). -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_GetWiFiCredentials, - std::string /* ssid */) - -// Reply after getting WiFi credentials from the system. |success| is false if -// error occurred. -IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_GotWiFiCredentials, - std::string /* key_data */, - bool /* success */) -#endif // defined(OS_WIN)
diff --git a/chrome/common/extensions/wifi_credentials_getter.mojom b/chrome/common/extensions/wifi_credentials_getter.mojom new file mode 100644 index 0000000..e5328b6 --- /dev/null +++ b/chrome/common/extensions/wifi_credentials_getter.mojom
@@ -0,0 +1,17 @@ +// Copyright 2017 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. + +// WiFi credentials interface provided by the utility process and exposed +// by mojo policy control to the chrome browser process on OS_WIN. + +module extensions.mojom; + +interface WiFiCredentialsGetter { + const string kWiFiTestNetwork = "chrome://test-get-wifi-credentials"; + + // OS_WIN: Get plain-text WiFi credentials from the system (requires UAC + // privilege elevation). Returns |success| false if an error occurred or + // |success| true and the result in |key_data|. + GetWiFiCredentials(string ssid) => (bool success, string key_data); +};
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 092dacbce..60093e5 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2527,6 +2527,9 @@ ] } } + if (is_win) { + sources += [ "../browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc" ] + } if (is_mac || is_win) { sources += [ "../browser/extensions/api/networking_private/networking_private_apitest.cc",
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc index 436ee9e..1788cba 100644 --- a/chrome/utility/chrome_content_utility_client.cc +++ b/chrome/utility/chrome_content_utility_client.cc
@@ -200,8 +200,8 @@ extensions::ExtensionsHandler::ExposeInterfacesToBrowser( registry, utility_client, running_elevated); #endif - // If our process runs with elevated privileges, only add elevated - // Mojo services to the interface registry. + // If our process runs with elevated privileges, only add elevated Mojo + // services to the interface registry. if (running_elevated) return;
diff --git a/chrome/utility/chrome_content_utility_ipc_whitelist.cc b/chrome/utility/chrome_content_utility_ipc_whitelist.cc index 8afb0f6..c9fc8c6 100644 --- a/chrome/utility/chrome_content_utility_ipc_whitelist.cc +++ b/chrome/utility/chrome_content_utility_ipc_whitelist.cc
@@ -14,9 +14,6 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) const uint32_t kMessageWhitelist[] = { -#if defined(OS_WIN) - ChromeUtilityHostMsg_GetWiFiCredentials::ID, -#endif // defined(OS_WIN) ChromeUtilityMsg_ImageWriter_Cancel::ID, ChromeUtilityMsg_ImageWriter_Write::ID, ChromeUtilityMsg_ImageWriter_Verify::ID};
diff --git a/chrome/utility/extensions/extensions_handler.cc b/chrome/utility/extensions/extensions_handler.cc index 9992d42..753279f 100644 --- a/chrome/utility/extensions/extensions_handler.cc +++ b/chrome/utility/extensions/extensions_handler.cc
@@ -32,6 +32,7 @@ #endif #if defined(OS_WIN) +#include "chrome/common/extensions/wifi_credentials_getter.mojom.h" #include "chrome/utility/media_galleries/itunes_pref_parser_win.h" #include "components/wifi/wifi_service.h" #endif // defined(OS_WIN) @@ -95,6 +96,46 @@ DISALLOW_COPY_AND_ASSIGN(MediaParserImpl); }; +#if defined(OS_WIN) +class WiFiCredentialsGetterImpl + : public extensions::mojom::WiFiCredentialsGetter { + public: + WiFiCredentialsGetterImpl() = default; + ~WiFiCredentialsGetterImpl() override = default; + + static void Create(extensions::mojom::WiFiCredentialsGetterRequest request) { + mojo::MakeStrongBinding(base::MakeUnique<WiFiCredentialsGetterImpl>(), + std::move(request)); + } + + private: + // extensions::mojom::WiFiCredentialsGetter: + void GetWiFiCredentials(const std::string& ssid, + const GetWiFiCredentialsCallback& callback) override { + if (ssid == kWiFiTestNetwork) { + callback.Run(true, ssid); // test-mode: return the ssid in key_data. + return; + } + + std::unique_ptr<wifi::WiFiService> wifi_service( + wifi::WiFiService::Create()); + wifi_service->Initialize(nullptr); + + std::string key_data; + std::string error; + wifi_service->GetKeyFromSystem(ssid, &key_data, &error); + + const bool success = error.empty(); + if (!success) + key_data.clear(); + + callback.Run(success, key_data); + } + + DISALLOW_COPY_AND_ASSIGN(WiFiCredentialsGetterImpl); +}; +#endif // defined(OS_WIN) + } // namespace namespace extensions { @@ -117,10 +158,14 @@ service_manager::InterfaceRegistry* registry, ChromeContentUtilityClient* utility_client, bool running_elevated) { - // If our process runs with elevated privileges, only add elevated - // Mojo services to the interface registry. - if (running_elevated) + // If our process runs with elevated privileges, only add elevated Mojo + // services to the interface registry. + if (running_elevated) { +#if defined(OS_WIN) + registry->AddInterface(base::Bind(&WiFiCredentialsGetterImpl::Create)); +#endif return; + } registry->AddInterface(base::Bind(&MediaParserImpl::Create, utility_client)); } @@ -144,11 +189,6 @@ OnIndexPicasaAlbumsContents) #endif // defined(OS_WIN) || defined(OS_MACOSX) -#if defined(OS_WIN) - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetWiFiCredentials, - OnGetWiFiCredentials) -#endif // defined(OS_WIN) - IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -227,17 +267,4 @@ } #endif // defined(OS_WIN) || defined(OS_MACOSX) -#if defined(OS_WIN) -void ExtensionsHandler::OnGetWiFiCredentials(const std::string& network_guid) { - std::unique_ptr<wifi::WiFiService> wifi_service(wifi::WiFiService::Create()); - wifi_service->Initialize(NULL); - - std::string key_data; - std::string error; - wifi_service->GetKeyFromSystem(network_guid, &key_data, &error); - - Send(new ChromeUtilityHostMsg_GotWiFiCredentials(key_data, error.empty())); -} -#endif // defined(OS_WIN) - } // namespace extensions
diff --git a/chrome/utility/extensions/extensions_handler.h b/chrome/utility/extensions/extensions_handler.h index 9b9efa1..4c023464 100644 --- a/chrome/utility/extensions/extensions_handler.h +++ b/chrome/utility/extensions/extensions_handler.h
@@ -67,10 +67,6 @@ const std::vector<picasa::FolderINIContents>& folders_inis); #endif // defined(OS_WIN) || defined(OS_MACOSX) -#if defined(OS_WIN) - void OnGetWiFiCredentials(const std::string& network_guid); -#endif // defined(OS_WIN) - UtilityHandler utility_handler_; DISALLOW_COPY_AND_ASSIGN(ExtensionsHandler);
diff --git a/components/cronet/ios/cronet_environment.mm b/components/cronet/ios/cronet_environment.mm index 03f36dd..eb413786 100644 --- a/components/cronet/ios/cronet_environment.mm +++ b/components/cronet/ios/cronet_environment.mm
@@ -22,6 +22,8 @@ #include "base/path_service.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/waitable_event.h" +#include "base/sys_info.h" +#include "base/task_scheduler/task_scheduler.h" #include "base/threading/worker_pool.h" #include "components/cronet/histogram_manager.h" #include "components/cronet/ios/version.h" @@ -117,6 +119,9 @@ if (!g_at_exit_) g_at_exit_ = new base::AtExitManager; + base::TaskScheduler::CreateAndSetSimpleTaskScheduler( + base::SysInfo::NumberOfProcessors()); + url::Initialize(); base::CommandLine::Init(0, nullptr); @@ -246,6 +251,13 @@ CronetEnvironment::~CronetEnvironment() { // net::HTTPProtocolHandlerDelegate::SetInstance(nullptr); + + // TODO(lilyhoughton) right now this is relying on there being + // only one CronetEnvironment (per process). if (when?) that + // changes, so will this have to. + base::TaskScheduler* ts = base::TaskScheduler::GetInstance(); + if (ts) + ts->Shutdown(); } void CronetEnvironment::InitializeOnNetworkThread() {
diff --git a/components/ntp_snippets/BUILD.gn b/components/ntp_snippets/BUILD.gn index 652704b..c29c9133 100644 --- a/components/ntp_snippets/BUILD.gn +++ b/components/ntp_snippets/BUILD.gn
@@ -49,8 +49,6 @@ "pref_util.h", "remote/ntp_snippet.cc", "remote/ntp_snippet.h", - "remote/ntp_snippets_fetcher.cc", - "remote/ntp_snippets_fetcher.h", "remote/ntp_snippets_json_request.cc", "remote/ntp_snippets_json_request.h", "remote/ntp_snippets_request_params.cc", @@ -58,6 +56,8 @@ "remote/persistent_scheduler.h", "remote/remote_suggestions_database.cc", "remote/remote_suggestions_database.h", + "remote/remote_suggestions_fetcher.cc", + "remote/remote_suggestions_fetcher.h", "remote/remote_suggestions_provider.cc", "remote/remote_suggestions_provider.h", "remote/remote_suggestions_provider_impl.cc", @@ -138,9 +138,9 @@ "offline_pages/recent_tab_suggestions_provider_unittest.cc", "physical_web_pages/physical_web_page_suggestions_provider_unittest.cc", "remote/ntp_snippet_unittest.cc", - "remote/ntp_snippets_fetcher_unittest.cc", "remote/ntp_snippets_json_request_unittest.cc", "remote/remote_suggestions_database_unittest.cc", + "remote/remote_suggestions_fetcher_unittest.cc", "remote/remote_suggestions_provider_impl_unittest.cc", "remote/remote_suggestions_status_service_unittest.cc", "remote/request_throttler_unittest.cc",
diff --git a/components/ntp_snippets/content_suggestions_metrics.cc b/components/ntp_snippets/content_suggestions_metrics.cc index 493f8c0..b6c26ed2 100644 --- a/components/ntp_snippets/content_suggestions_metrics.cc +++ b/components/ntp_snippets/content_suggestions_metrics.cc
@@ -22,6 +22,7 @@ const int kMaxSuggestionsPerCategory = 10; const int kMaxSuggestionsTotal = 50; +const int kMaxCategories = 10; const char kHistogramCountOnNtpOpened[] = "NewTabPage.ContentSuggestions.CountOnNtpOpened"; @@ -31,6 +32,8 @@ "NewTabPage.ContentSuggestions.ShownScoreNormalized"; const char kHistogramOpened[] = "NewTabPage.ContentSuggestions.Opened"; const char kHistogramOpenedAge[] = "NewTabPage.ContentSuggestions.OpenedAge"; +const char kHistogramOpenedCategoryIndex[] = + "NewTabPage.ContentSuggestions.OpenedCategoryIndex"; const char kHistogramOpenedScore[] = "NewTabPage.ContentSuggestions.OpenedScoreNormalized"; const char kHistogramOpenDisposition[] = @@ -216,13 +219,13 @@ void OnSuggestionShown(int global_position, Category category, - int category_position, + int position_in_category, base::Time publish_date, base::Time last_background_fetch_time, float score) { UMA_HISTOGRAM_EXACT_LINEAR(kHistogramShown, global_position, kMaxSuggestionsTotal); - LogCategoryHistogramPosition(kHistogramShown, category, category_position, + LogCategoryHistogramPosition(kHistogramShown, category, position_in_category, kMaxSuggestionsPerCategory); base::TimeDelta age = base::Time::Now() - publish_date; @@ -235,7 +238,7 @@ // When the first of the articles suggestions is shown, then we count this as // a single usage of content suggestions. if (category.IsKnownCategory(KnownCategories::ARTICLES) && - category_position == 0) { + position_in_category == 0) { RecordContentSuggestionsUsage(); // Records the time since the last background fetch of the remote content @@ -250,13 +253,19 @@ void OnSuggestionOpened(int global_position, Category category, - int category_position, + int category_index, + int position_in_category, base::Time publish_date, float score, WindowOpenDisposition disposition) { + UMA_HISTOGRAM_EXACT_LINEAR(kHistogramOpenedCategoryIndex, category_index, + kMaxCategories); + LogCategoryHistogramPosition(kHistogramOpenedCategoryIndex, category, + category_index, kMaxCategories); + UMA_HISTOGRAM_EXACT_LINEAR(kHistogramOpened, global_position, kMaxSuggestionsTotal); - LogCategoryHistogramPosition(kHistogramOpened, category, category_position, + LogCategoryHistogramPosition(kHistogramOpened, category, position_in_category, kMaxSuggestionsPerCategory); base::TimeDelta age = base::Time::Now() - publish_date; @@ -281,13 +290,14 @@ void OnSuggestionMenuOpened(int global_position, Category category, - int category_position, + int position_in_category, base::Time publish_date, float score) { UMA_HISTOGRAM_EXACT_LINEAR(kHistogramMenuOpened, global_position, kMaxSuggestionsTotal); LogCategoryHistogramPosition(kHistogramMenuOpened, category, - category_position, kMaxSuggestionsPerCategory); + position_in_category, + kMaxSuggestionsPerCategory); base::TimeDelta age = base::Time::Now() - publish_date; LogCategoryHistogramAge(kHistogramMenuOpenedAge, category, age); @@ -297,18 +307,20 @@ void OnSuggestionDismissed(int global_position, Category category, - int category_position, + int position_in_category, bool visited) { if (visited) { UMA_HISTOGRAM_EXACT_LINEAR(kHistogramDismissedVisited, global_position, kMaxSuggestionsTotal); LogCategoryHistogramPosition(kHistogramDismissedVisited, category, - category_position, kMaxSuggestionsPerCategory); + position_in_category, + kMaxSuggestionsPerCategory); } else { UMA_HISTOGRAM_EXACT_LINEAR(kHistogramDismissedUnvisited, global_position, kMaxSuggestionsTotal); LogCategoryHistogramPosition(kHistogramDismissedUnvisited, category, - category_position, kMaxSuggestionsPerCategory); + position_in_category, + kMaxSuggestionsPerCategory); } }
diff --git a/components/ntp_snippets/content_suggestions_metrics.h b/components/ntp_snippets/content_suggestions_metrics.h index d03fb42..12e2b46 100644 --- a/components/ntp_snippets/content_suggestions_metrics.h +++ b/components/ntp_snippets/content_suggestions_metrics.h
@@ -21,27 +21,30 @@ // Should only be called once per NTP for each suggestion. void OnSuggestionShown(int global_position, Category category, - int category_position, + int position_in_category, base::Time publish_date, base::Time last_background_fetch_time, float score); +// TODO(crbug.com/682160): Take struct, so that one could not mix up the +// order of arguments. void OnSuggestionOpened(int global_position, Category category, - int category_position, + int category_index, + int position_in_category, base::Time publish_date, float score, WindowOpenDisposition disposition); void OnSuggestionMenuOpened(int global_position, Category category, - int category_position, + int position_in_category, base::Time publish_date, float score); void OnSuggestionDismissed(int global_position, Category category, - int category_position, + int position_in_category, bool visited); void OnSuggestionTargetVisited(Category category, base::TimeDelta visit_time);
diff --git a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc index 147cfdb..e7d1ebf 100644 --- a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc +++ b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc
@@ -37,6 +37,14 @@ const size_t kMaxSuggestionsCount = 10; +std::string GetGroupId(const DictionaryValue& page_dictionary) { + std::string group_id; + if (!page_dictionary.GetString(physical_web::kGroupIdKey, &group_id)) { + LOG(DFATAL) << physical_web::kGroupIdKey << " field is missing."; + } + return group_id; +} + std::string GetPageId(const DictionaryValue& page_dictionary) { std::string raw_resolved_url; if (!page_dictionary.GetString(physical_web::kResolvedUrlKey, @@ -46,6 +54,62 @@ return raw_resolved_url; } +bool CompareByDistance(const DictionaryValue* left, + const DictionaryValue* right) { + double left_distance, right_distance; + bool success = + left->GetDouble(physical_web::kDistanceEstimateKey, &left_distance); + success = + right->GetDouble(physical_web::kDistanceEstimateKey, &right_distance) && + success; + if (!success) { + LOG(DFATAL) << "Distance field is missing."; + } + + // When there is no estimate, the value is <= 0, so we implicitly treat it as + // infinity. + bool is_left_estimated = left_distance > 0; + bool is_right_estimated = right_distance > 0; + + if (is_left_estimated != is_right_estimated) + return is_left_estimated; + return left_distance < right_distance; +} + +void FilterOutByGroupId( + std::vector<const DictionaryValue*>* page_dictionaries) { + // |std::unique| only removes duplicates that immediately follow each other. + // Thus, first, we have to sort by group_id and distance and only then remove + // duplicates. + std::sort(page_dictionaries->begin(), page_dictionaries->end(), + [](const DictionaryValue* left, const DictionaryValue* right) { + std::string left_group_id = GetGroupId(*left); + std::string right_group_id = GetGroupId(*right); + + if (left_group_id != right_group_id) { + return left_group_id < right_group_id; + } + + // We want closest pages first, so in case of same group_id we + // sort by distance. + return CompareByDistance(left, right); + }); + + // Each empty group_id must be treated as unique, so we do not apply + // std::unique to them at all. + auto nonempty_group_id_begin = std::find_if( + page_dictionaries->begin(), page_dictionaries->end(), + [](const DictionaryValue* page) { return !GetGroupId(*page).empty(); }); + + auto new_end = std::unique( + nonempty_group_id_begin, page_dictionaries->end(), + [](const DictionaryValue* left, const DictionaryValue* right) { + return GetGroupId(*left) == GetGroupId(*right); + }); + + page_dictionaries->erase(new_end, page_dictionaries->end()); +} + } // namespace PhysicalWebPageSuggestionsProvider::PhysicalWebPageSuggestionsProvider( @@ -222,19 +286,10 @@ StoreDismissedIDsToPrefs(new_dismissed_ids); } + FilterOutByGroupId(&page_dictionaries); + std::sort(page_dictionaries.begin(), page_dictionaries.end(), - [](const DictionaryValue* left, const DictionaryValue* right) { - double left_distance, right_distance; - bool success = left->GetDouble(physical_web::kDistanceEstimateKey, - &left_distance); - success = right->GetDouble(physical_web::kDistanceEstimateKey, - &right_distance) && - success; - if (!success) { - LOG(DFATAL) << "Distance field is missing."; - } - return left_distance < right_distance; - }); + CompareByDistance); std::vector<ContentSuggestion> suggestions; for (const DictionaryValue* page_dictionary : page_dictionaries) {
diff --git a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc index 763abe0..633fd97 100644 --- a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc +++ b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc
@@ -179,6 +179,70 @@ } TEST_F(PhysicalWebPageSuggestionsProviderTest, + ShouldConsiderPagesWithoutDistanceEstimateFurthest) { + IgnoreOnCategoryStatusChangedToAvailable(); + IgnoreOnSuggestionInvalidated(); + // |CreateDummyPhysicalWebPages| builds pages with distances 1, 2 and 3 + // respectively. + std::unique_ptr<base::ListValue> pages = + CreateDummyPhysicalWebPages({3, 2, 1}); + DictionaryValue* second_page; + // Set the second page distance estimate to unknown. + ASSERT_TRUE(pages->GetDictionary(1, &second_page)); + second_page->SetDouble(physical_web::kDistanceEstimateKey, -1); + physical_web_data_source()->SetMetadata(std::move(pages)); + EXPECT_CALL( + *observer(), + OnNewSuggestions(_, provided_category(), + ElementsAre(HasUrl("https://resolved_url.com/3"), + HasUrl("https://resolved_url.com/1"), + HasUrl("https://resolved_url.com/2")))); + CreateProvider(); +} + +TEST_F(PhysicalWebPageSuggestionsProviderTest, + ShouldNotShowSuggestionsWithSameGroupId) { + IgnoreOnCategoryStatusChangedToAvailable(); + IgnoreOnSuggestionInvalidated(); + // |CreateDummyPhysicalWebPages| builds pages with distances 1, 2 + // respectively. + std::unique_ptr<base::ListValue> pages = CreateDummyPhysicalWebPages({2, 1}); + for (int i = 0; i < 2; ++i) { + DictionaryValue* page; + ASSERT_TRUE(pages->GetDictionary(i, &page)); + page->SetString(physical_web::kGroupIdKey, "some_group_id"); + } + + physical_web_data_source()->SetMetadata(std::move(pages)); + // The closest page should be reported. + EXPECT_CALL( + *observer(), + OnNewSuggestions(_, provided_category(), + ElementsAre(HasUrl("https://resolved_url.com/2")))); + CreateProvider(); +} + +TEST_F(PhysicalWebPageSuggestionsProviderTest, + ShouldShowSuggestionsWithEmptyGroupId) { + IgnoreOnCategoryStatusChangedToAvailable(); + IgnoreOnSuggestionInvalidated(); + std::unique_ptr<base::ListValue> pages = CreateDummyPhysicalWebPages({1, 2}); + for (int i = 0; i < 2; ++i) { + DictionaryValue* page; + pages->GetDictionary(i, &page); + page->SetString(physical_web::kGroupIdKey, ""); + } + + physical_web_data_source()->SetMetadata(std::move(pages)); + EXPECT_CALL(*observer(), + OnNewSuggestions( + _, provided_category(), + UnorderedElementsAre(HasUrl("https://resolved_url.com/1"), + HasUrl("https://resolved_url.com/2")))); + CreateProvider(); +} + +TEST_F(PhysicalWebPageSuggestionsProviderTest, FetchMoreShouldFilterAndReturnSortedSuggestions) { IgnoreOnCategoryStatusChangedToAvailable(); IgnoreOnSuggestionInvalidated();
diff --git a/components/ntp_snippets/pref_names.h b/components/ntp_snippets/pref_names.h index 60c05e8..7ff783d 100644 --- a/components/ntp_snippets/pref_names.h +++ b/components/ntp_snippets/pref_names.h
@@ -30,11 +30,12 @@ // there is no WiFi connectivity. extern const char kSnippetPersistentFetchingIntervalFallback[]; -// The pref name for today's count of NTPSnippetsFetcher requests, so far. +// The pref name for today's count of RemoteSuggestionsFetcher requests, so far. extern const char kSnippetFetcherRequestCount[]; -// The pref name for today's count of NTPSnippetsFetcher interactive requests. +// The pref name for today's count of RemoteSuggestionsFetcher interactive +// requests. extern const char kSnippetFetcherInteractiveRequestCount[]; -// The pref name for the current day for the counter of NTPSnippetsFetcher +// The pref name for the current day for the counter of RemoteSuggestionsFetcher // requests. extern const char kSnippetFetcherRequestsDay[];
diff --git a/components/ntp_snippets/remote/ntp_snippets_fetcher.cc b/components/ntp_snippets/remote/remote_suggestions_fetcher.cc similarity index 90% rename from components/ntp_snippets/remote/ntp_snippets_fetcher.cc rename to components/ntp_snippets/remote/remote_suggestions_fetcher.cc index 4969440..44392e7 100644 --- a/components/ntp_snippets/remote/ntp_snippets_fetcher.cc +++ b/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" +#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h" #include <cstdlib> #include <utility> @@ -181,30 +181,29 @@ // The response from the backend might include suggestions from multiple // categories. If only a single category was requested, this function filters // all other categories out. -void FilterCategories(NTPSnippetsFetcher::FetchedCategoriesVector* categories, - base::Optional<Category> exclusive_category) { +void FilterCategories( + RemoteSuggestionsFetcher::FetchedCategoriesVector* categories, + base::Optional<Category> exclusive_category) { if (!exclusive_category.has_value()) { return; } Category exclusive = exclusive_category.value(); auto category_it = std::find_if( categories->begin(), categories->end(), - [&exclusive](const NTPSnippetsFetcher::FetchedCategory& c) -> bool { + [&exclusive](const RemoteSuggestionsFetcher::FetchedCategory& c) -> bool { return c.category == exclusive; }); if (category_it == categories->end()) { categories->clear(); return; } - NTPSnippetsFetcher::FetchedCategory category = std::move(*category_it); + RemoteSuggestionsFetcher::FetchedCategory category = std::move(*category_it); categories->clear(); categories->push_back(std::move(category)); } } // namespace - - CategoryInfo BuildArticleCategoryInfo( const base::Optional<base::string16>& title) { return CategoryInfo( @@ -238,19 +237,20 @@ l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_EMPTY)); } -NTPSnippetsFetcher::FetchedCategory::FetchedCategory(Category c, - CategoryInfo&& info) +RemoteSuggestionsFetcher::FetchedCategory::FetchedCategory(Category c, + CategoryInfo&& info) : category(c), info(info) {} -NTPSnippetsFetcher::FetchedCategory::FetchedCategory(FetchedCategory&&) = +RemoteSuggestionsFetcher::FetchedCategory::FetchedCategory(FetchedCategory&&) = default; -NTPSnippetsFetcher::FetchedCategory::~FetchedCategory() = default; +RemoteSuggestionsFetcher::FetchedCategory::~FetchedCategory() = default; -NTPSnippetsFetcher::FetchedCategory& NTPSnippetsFetcher::FetchedCategory:: -operator=(FetchedCategory&&) = default; +RemoteSuggestionsFetcher::FetchedCategory& +RemoteSuggestionsFetcher::FetchedCategory::operator=(FetchedCategory&&) = + default; -NTPSnippetsFetcher::NTPSnippetsFetcher( +RemoteSuggestionsFetcher::RemoteSuggestionsFetcher( SigninManagerBase* signin_manager, OAuth2TokenService* token_service, scoped_refptr<URLRequestContextGetter> url_request_context_getter, @@ -300,14 +300,15 @@ } } -NTPSnippetsFetcher::~NTPSnippetsFetcher() { +RemoteSuggestionsFetcher::~RemoteSuggestionsFetcher() { if (waiting_for_refresh_token_) { token_service_->RemoveObserver(this); } } -void NTPSnippetsFetcher::FetchSnippets(const NTPSnippetsRequestParams& params, - SnippetsAvailableCallback callback) { +void RemoteSuggestionsFetcher::FetchSnippets( + const NTPSnippetsRequestParams& params, + SnippetsAvailableCallback callback) { if (!DemandQuotaForRequest(params.interactive_request)) { FetchFinished(OptionalFetchedCategories(), std::move(callback), params.interactive_request @@ -357,7 +358,7 @@ } } -void NTPSnippetsFetcher::FetchSnippetsNonAuthenticated( +void RemoteSuggestionsFetcher::FetchSnippetsNonAuthenticated( NTPSnippetsJsonRequest::Builder builder, SnippetsAvailableCallback callback) { // When not providing OAuth token, we need to pass the Google API key. @@ -367,7 +368,7 @@ StartRequest(std::move(builder), std::move(callback)); } -void NTPSnippetsFetcher::FetchSnippetsAuthenticated( +void RemoteSuggestionsFetcher::FetchSnippetsAuthenticated( NTPSnippetsJsonRequest::Builder builder, SnippetsAvailableCallback callback, const std::string& account_id, @@ -380,16 +381,17 @@ StartRequest(std::move(builder), std::move(callback)); } -void NTPSnippetsFetcher::StartRequest(NTPSnippetsJsonRequest::Builder builder, - SnippetsAvailableCallback callback) { +void RemoteSuggestionsFetcher::StartRequest( + NTPSnippetsJsonRequest::Builder builder, + SnippetsAvailableCallback callback) { std::unique_ptr<NTPSnippetsJsonRequest> request = builder.Build(); NTPSnippetsJsonRequest* raw_request = request.get(); - raw_request->Start(base::BindOnce(&NTPSnippetsFetcher::JsonRequestDone, + raw_request->Start(base::BindOnce(&RemoteSuggestionsFetcher::JsonRequestDone, base::Unretained(this), std::move(request), std::move(callback))); } -void NTPSnippetsFetcher::StartTokenRequest() { +void RemoteSuggestionsFetcher::StartTokenRequest() { OAuth2TokenService::ScopeSet scopes; scopes.insert(fetch_api_ == FetchAPI::CHROME_CONTENT_SUGGESTIONS_API ? kContentSuggestionsApiScope @@ -400,7 +402,7 @@ //////////////////////////////////////////////////////////////////////////////// // OAuth2TokenService::Consumer overrides -void NTPSnippetsFetcher::OnGetTokenSuccess( +void RemoteSuggestionsFetcher::OnGetTokenSuccess( const OAuth2TokenService::Request* request, const std::string& access_token, const base::Time& expiration_time) { @@ -420,7 +422,7 @@ } } -void NTPSnippetsFetcher::OnGetTokenFailure( +void RemoteSuggestionsFetcher::OnGetTokenFailure( const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) { oauth_request_.reset(); @@ -450,7 +452,7 @@ //////////////////////////////////////////////////////////////////////////////// // OAuth2TokenService::Observer overrides -void NTPSnippetsFetcher::OnRefreshTokenAvailable( +void RemoteSuggestionsFetcher::OnRefreshTokenAvailable( const std::string& account_id) { // Only react on tokens for the account the user has signed in with. if (account_id != signin_manager_->GetAuthenticatedAccountId()) { @@ -463,7 +465,7 @@ StartTokenRequest(); } -void NTPSnippetsFetcher::JsonRequestDone( +void RemoteSuggestionsFetcher::JsonRequestDone( std::unique_ptr<NTPSnippetsJsonRequest> request, SnippetsAvailableCallback callback, std::unique_ptr<base::Value> result, @@ -496,10 +498,11 @@ FetchResult::SUCCESS, std::string()); } -void NTPSnippetsFetcher::FetchFinished(OptionalFetchedCategories categories, - SnippetsAvailableCallback callback, - FetchResult fetch_result, - const std::string& error_details) { +void RemoteSuggestionsFetcher::FetchFinished( + OptionalFetchedCategories categories, + SnippetsAvailableCallback callback, + FetchResult fetch_result, + const std::string& error_details) { DCHECK(fetch_result == FetchResult::SUCCESS || !categories.has_value()); last_status_ = FetchResultToString(fetch_result) + error_details; @@ -514,8 +517,9 @@ std::move(categories)); } -bool NTPSnippetsFetcher::JsonToSnippets(const base::Value& parsed, - FetchedCategoriesVector* categories) { +bool RemoteSuggestionsFetcher::JsonToSnippets( + const base::Value& parsed, + FetchedCategoriesVector* categories) { const base::DictionaryValue* top_dict = nullptr; if (!parsed.GetAsDictionary(&top_dict)) { return false; @@ -587,7 +591,7 @@ return false; } -bool NTPSnippetsFetcher::DemandQuotaForRequest(bool interactive_request) { +bool RemoteSuggestionsFetcher::DemandQuotaForRequest(bool interactive_request) { switch (user_classifier_->GetUserClass()) { case UserClassifier::UserClass::RARE_NTP_USER: return request_throttler_rare_ntp_user_.DemandQuotaForRequest( @@ -603,12 +607,12 @@ return false; } -bool NTPSnippetsFetcher::NeedsAuthentication() const { +bool RemoteSuggestionsFetcher::NeedsAuthentication() const { return (personalization_ == Personalization::kPersonal || personalization_ == Personalization::kBoth); } -std::string NTPSnippetsFetcher::PersonalizationModeString() const { +std::string RemoteSuggestionsFetcher::PersonalizationModeString() const { switch (personalization_) { case Personalization::kPersonal: return "Only personalized";
diff --git a/components/ntp_snippets/remote/ntp_snippets_fetcher.h b/components/ntp_snippets/remote/remote_suggestions_fetcher.h similarity index 90% rename from components/ntp_snippets/remote/ntp_snippets_fetcher.h rename to components/ntp_snippets/remote/remote_suggestions_fetcher.h index 19dce66..44fcd6e 100644 --- a/components/ntp_snippets/remote/ntp_snippets_fetcher.h +++ b/components/ntp_snippets/remote/remote_suggestions_fetcher.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_FETCHER_H_ -#define COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_FETCHER_H_ +#ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_FETCHER_H_ +#define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_FETCHER_H_ #include <memory> #include <queue> @@ -37,7 +37,7 @@ class UserClassifier; // TODO(tschumann): BuildArticleCategoryInfo() and BuildRemoteCategoryInfo() -// don't really belong into this library. However, as the snippets fetcher is +// don't really belong into this library. However, as the fetcher is // providing this data for server-defined remote sections it's a good starting // point. Candiates to add to such a library would be persisting categories // (have all category managment in one place) or turning parsed JSON into @@ -52,11 +52,12 @@ CategoryInfo BuildRemoteCategoryInfo(const base::string16& title, bool allow_fetching_more_results); -// Fetches snippet data for the NTP from the server. +// Fetches suggestion data for the NTP from the server. // TODO(fhorschig): Untangle cyclic dependencies by introducing a -// NTPSnippetsFetcherInterface. (Would be good for mock implementations, too) -class NTPSnippetsFetcher : public OAuth2TokenService::Consumer, - public OAuth2TokenService::Observer { +// RemoteSuggestionsFetcherInterface. (Would be good for mock implementations, +// too!) +class RemoteSuggestionsFetcher : public OAuth2TokenService::Consumer, + public OAuth2TokenService::Observer { public: struct FetchedCategory { Category category; @@ -78,7 +79,7 @@ base::OnceCallback<void(Status status, OptionalFetchedCategories fetched_categories)>; - NTPSnippetsFetcher( + RemoteSuggestionsFetcher( SigninManagerBase* signin_manager, OAuth2TokenService* token_service, scoped_refptr<net::URLRequestContextGetter> url_request_context_getter, @@ -87,7 +88,7 @@ const ParseJSONCallback& parse_json_callback, const std::string& api_key, const UserClassifier* user_classifier); - ~NTPSnippetsFetcher() override; + ~RemoteSuggestionsFetcher() override; // Initiates a fetch from the server. When done (successfully or not), calls // the callback. @@ -103,9 +104,7 @@ const std::string& last_status() const { return last_status_; } // Returns the last JSON fetched from the server. - const std::string& last_json() const { - return last_fetch_json_; - } + const std::string& last_json() const { return last_fetch_json_; } // Returns the personalization setting of the fetcher as used in tests. // TODO(fhorschig): Reconsider these tests and remove this getter. @@ -171,7 +170,7 @@ const std::string& error_details); bool JsonToSnippets(const base::Value& parsed, - NTPSnippetsFetcher::FetchedCategoriesVector* categories); + FetchedCategoriesVector* categories); bool DemandQuotaForRequest(bool interactive_request); @@ -226,10 +225,11 @@ std::string last_status_; std::string last_fetch_json_; - base::WeakPtrFactory<NTPSnippetsFetcher> weak_ptr_factory_; + base::WeakPtrFactory<RemoteSuggestionsFetcher> weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(NTPSnippetsFetcher); + DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsFetcher); }; + } // namespace ntp_snippets -#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_FETCHER_H_ +#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_FETCHER_H_
diff --git a/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc similarity index 95% rename from components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc rename to components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc index c989ea81..f5f4aa6 100644 --- a/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc +++ b/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" +#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h" #include <deque> #include <map> @@ -83,13 +83,15 @@ } MATCHER(IsEmptyArticleList, "is an empty list of articles") { - NTPSnippetsFetcher::OptionalFetchedCategories& fetched_categories = *arg; + RemoteSuggestionsFetcher::OptionalFetchedCategories& fetched_categories = + *arg; return fetched_categories && fetched_categories->size() == 1 && fetched_categories->begin()->snippets.empty(); } MATCHER_P(IsSingleArticle, url, "is a list with the single article %(url)s") { - NTPSnippetsFetcher::OptionalFetchedCategories& fetched_categories = *arg; + RemoteSuggestionsFetcher::OptionalFetchedCategories& fetched_categories = + *arg; if (!fetched_categories) { *result_listener << "got empty categories."; return false; @@ -136,8 +138,8 @@ if (!arg->has_value() || arg->value().size() == 0) { *result_listener << "No category found."; } - return testing::ExplainMatchResult( - info_matcher, arg->value().front().info, result_listener); + return testing::ExplainMatchResult(info_matcher, arg->value().front().info, + result_listener); } class MockSnippetsAvailableCallback { @@ -145,14 +147,14 @@ // Workaround for gMock's lack of support for movable arguments. void WrappedRun( Status status, - NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { + RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories) { Run(status, &fetched_categories); } - MOCK_METHOD2( - Run, - void(Status status, - NTPSnippetsFetcher::OptionalFetchedCategories* fetched_categories)); + MOCK_METHOD2(Run, + void(Status status, + RemoteSuggestionsFetcher::OptionalFetchedCategories* + fetched_categories)); }; // TODO(fhorschig): Transfer this class' functionality to call delegates @@ -236,7 +238,9 @@ class FailingFakeURLFetcherFactory : public net::URLFetcherFactory { public: std::unique_ptr<net::URLFetcher> CreateURLFetcher( - int id, const GURL& url, net::URLFetcher::RequestType request_type, + int id, + const GURL& url, + net::URLFetcher::RequestType request_type, net::URLFetcherDelegate* d) override { return base::MakeUnique<net::FakeURLFetcher>( url, d, /*response_data=*/std::string(), net::HTTP_NOT_FOUND, @@ -267,9 +271,9 @@ } // namespace -class NTPSnippetsFetcherTestBase : public testing::Test { +class RemoteSuggestionsFetcherTestBase : public testing::Test { public: - explicit NTPSnippetsFetcherTestBase(const GURL& gurl) + explicit RemoteSuggestionsFetcherTestBase(const GURL& gurl) : default_variation_params_( {{"send_top_languages", "true"}, {"send_user_class", "true"}}), params_manager_(ntp_snippets::kStudyName, @@ -294,7 +298,7 @@ } void ResetSnippetsFetcher() { - snippets_fetcher_ = base::MakeUnique<NTPSnippetsFetcher>( + snippets_fetcher_ = base::MakeUnique<RemoteSuggestionsFetcher>( fake_signin_manager_.get(), fake_token_service_.get(), scoped_refptr<net::TestURLRequestContextGetter>( new net::TestURLRequestContextGetter(mock_task_runner_.get())), @@ -305,13 +309,13 @@ mock_task_runner_->GetMockTickClock()); } - NTPSnippetsFetcher::SnippetsAvailableCallback ToSnippetsAvailableCallback( - MockSnippetsAvailableCallback* callback) { + RemoteSuggestionsFetcher::SnippetsAvailableCallback + ToSnippetsAvailableCallback(MockSnippetsAvailableCallback* callback) { return base::BindOnce(&MockSnippetsAvailableCallback::WrappedRun, base::Unretained(callback)); } - NTPSnippetsFetcher& snippets_fetcher() { return *snippets_fetcher_; } + RemoteSuggestionsFetcher& snippets_fetcher() { return *snippets_fetcher_; } MockSnippetsAvailableCallback& mock_callback() { return mock_callback_; } void FastForwardUntilNoTasksRemain() { mock_task_runner_->FastForwardUntilNoTasksRemain(); @@ -369,20 +373,21 @@ std::unique_ptr<AccountTrackerService> account_tracker_; std::unique_ptr<SigninManagerBase> fake_signin_manager_; std::unique_ptr<OAuth2TokenService> fake_token_service_; - std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher_; + std::unique_ptr<RemoteSuggestionsFetcher> snippets_fetcher_; std::unique_ptr<TestingPrefServiceSimple> pref_service_; std::unique_ptr<UserClassifier> user_classifier_; MockSnippetsAvailableCallback mock_callback_; const GURL test_url_; base::HistogramTester histogram_tester_; - DISALLOW_COPY_AND_ASSIGN(NTPSnippetsFetcherTestBase); + DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsFetcherTestBase); }; -class ChromeReaderSnippetsFetcherTest : public NTPSnippetsFetcherTestBase { +class ChromeReaderSnippetsFetcherTest + : public RemoteSuggestionsFetcherTestBase { public: ChromeReaderSnippetsFetcherTest() - : NTPSnippetsFetcherTestBase(GURL(kTestChromeReaderUrl)) { + : RemoteSuggestionsFetcherTestBase(GURL(kTestChromeReaderUrl)) { default_variation_params_["content_suggestions_backend"] = kChromeReaderServer; SetVariationParam("content_suggestions_backend", kChromeReaderServer); @@ -391,10 +396,11 @@ }; class NTPSnippetsContentSuggestionsFetcherTest - : public NTPSnippetsFetcherTestBase { + : public RemoteSuggestionsFetcherTestBase { public: NTPSnippetsContentSuggestionsFetcherTest() - : NTPSnippetsFetcherTestBase(GURL(kTestChromeContentSuggestionsUrl)) {} + : RemoteSuggestionsFetcherTestBase( + GURL(kTestChromeContentSuggestionsUrl)) {} }; TEST_F(ChromeReaderSnippetsFetcherTest, ShouldNotFetchOnCreation) { @@ -534,7 +540,7 @@ "}]}"; SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS); - NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories; + RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories; EXPECT_CALL(mock_callback(), Run(IsSuccess(), _)) .WillOnce(MoveArgument1PointeeTo(&fetched_categories)); snippets_fetcher().FetchSnippets( @@ -596,7 +602,7 @@ "}]}"; SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS); - NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories; + RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories; EXPECT_CALL(mock_callback(), Run(IsSuccess(), _)) .WillOnce(MoveArgument1PointeeTo(&fetched_categories)); snippets_fetcher().FetchSnippets( @@ -660,7 +666,7 @@ "}]}"; SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS); - NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories; + RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories; EXPECT_CALL(mock_callback(), Run(IsSuccess(), _)) .WillOnce(MoveArgument1PointeeTo(&fetched_categories)); @@ -911,7 +917,8 @@ ::std::ostream& operator<<( ::std::ostream& os, - const NTPSnippetsFetcher::OptionalFetchedCategories& fetched_categories) { + const RemoteSuggestionsFetcher::OptionalFetchedCategories& + fetched_categories) { if (fetched_categories) { // Matchers above aren't any more precise than this, so this is sufficient // for test-failure diagnostics.
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider.h b/components/ntp_snippets/remote/remote_suggestions_provider.h index 8b818b5..8e07099 100644 --- a/components/ntp_snippets/remote/remote_suggestions_provider.h +++ b/components/ntp_snippets/remote/remote_suggestions_provider.h
@@ -13,7 +13,7 @@ namespace ntp_snippets { -class NTPSnippetsFetcher; +class RemoteSuggestionsFetcher; // Retrieves fresh content data (articles) from the server, stores them and // provides them as content suggestions. @@ -50,7 +50,7 @@ virtual void RefetchInTheBackground( std::unique_ptr<FetchStatusCallback> callback) = 0; - virtual const NTPSnippetsFetcher* snippets_fetcher_for_testing_and_debugging() + virtual const RemoteSuggestionsFetcher* suggestions_fetcher_for_debugging() const = 0; protected:
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc index 4521227ea..dcdbe065 100644 --- a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc +++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
@@ -265,7 +265,7 @@ PrefService* pref_service, const std::string& application_language_code, CategoryRanker* category_ranker, - std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, + std::unique_ptr<RemoteSuggestionsFetcher> suggestions_fetcher, std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher, std::unique_ptr<image_fetcher::ImageDecoder> image_decoder, std::unique_ptr<RemoteSuggestionsDatabase> database, @@ -277,7 +277,7 @@ Category::FromKnownCategory(KnownCategories::ARTICLES)), application_language_code_(application_language_code), category_ranker_(category_ranker), - snippets_fetcher_(std::move(snippets_fetcher)), + suggestions_fetcher_(std::move(suggestions_fetcher)), database_(std::move(database)), image_fetcher_(std::move(image_fetcher), std::move(image_decoder), @@ -350,10 +350,9 @@ FetchSnippets(/*interactive_request=*/false, std::move(callback)); } -const NTPSnippetsFetcher* -RemoteSuggestionsProviderImpl::snippets_fetcher_for_testing_and_debugging() - const { - return snippets_fetcher_.get(); +const RemoteSuggestionsFetcher* +RemoteSuggestionsProviderImpl::suggestions_fetcher_for_debugging() const { + return suggestions_fetcher_.get(); } void RemoteSuggestionsProviderImpl::FetchSnippets( @@ -370,7 +369,7 @@ NTPSnippetsRequestParams params = BuildFetchParams(); params.interactive_request = interactive_request; - snippets_fetcher_->FetchSnippets( + suggestions_fetcher_->FetchSnippets( params, base::BindOnce(&RemoteSuggestionsProviderImpl::OnFetchFinished, base::Unretained(this), std::move(callback), interactive_request)); @@ -392,7 +391,7 @@ params.interactive_request = true; params.exclusive_category = category; - snippets_fetcher_->FetchSnippets( + suggestions_fetcher_->FetchSnippets( params, base::BindOnce(&RemoteSuggestionsProviderImpl::OnFetchMoreFinished, base::Unretained(this), callback)); @@ -615,7 +614,7 @@ void RemoteSuggestionsProviderImpl::OnFetchMoreFinished( const FetchDoneCallback& fetching_callback, Status status, - NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { + RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories) { if (!fetched_categories) { DCHECK(!status.IsSuccess()); CallWithEmptyResults(fetching_callback, status); @@ -676,7 +675,7 @@ std::unique_ptr<FetchStatusCallback> callback, bool interactive_request, Status status, - NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { + RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories) { if (!ready()) { // TODO(tschumann): What happens if this was a user-triggered, interactive // request? Is the UI waiting indefinitely now? @@ -704,7 +703,7 @@ if (fetched_categories) { // TODO(treib): Reorder |category_contents_| to match the order we received // from the server. crbug.com/653816 - for (NTPSnippetsFetcher::FetchedCategory& fetched_category : + for (RemoteSuggestionsFetcher::FetchedCategory& fetched_category : *fetched_categories) { // TODO(tschumann): Remove this histogram once we only talk to the content // suggestions cloud backend.
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl.h b/components/ntp_snippets/remote/remote_suggestions_provider_impl.h index 9301dd8..e7d28fe 100644 --- a/components/ntp_snippets/remote/remote_suggestions_provider_impl.h +++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl.h
@@ -25,8 +25,8 @@ #include "components/ntp_snippets/content_suggestion.h" #include "components/ntp_snippets/content_suggestions_provider.h" #include "components/ntp_snippets/remote/ntp_snippet.h" -#include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" #include "components/ntp_snippets/remote/ntp_snippets_request_params.h" +#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h" #include "components/ntp_snippets/remote/remote_suggestions_provider.h" #include "components/ntp_snippets/remote/remote_suggestions_status_service.h" #include "components/ntp_snippets/remote/request_throttler.h" @@ -117,7 +117,7 @@ PrefService* pref_service, const std::string& application_language_code, CategoryRanker* category_ranker, - std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, + std::unique_ptr<RemoteSuggestionsFetcher> suggestions_fetcher, std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher, std::unique_ptr<image_fetcher::ImageDecoder> image_decoder, std::unique_ptr<RemoteSuggestionsDatabase> database, @@ -144,7 +144,7 @@ // TODO(fhorschig): Remove this getter when there is an interface for the // fetcher that allows better mocks. - const NTPSnippetsFetcher* snippets_fetcher_for_testing_and_debugging() + const RemoteSuggestionsFetcher* suggestions_fetcher_for_debugging() const override; // ContentSuggestionsProvider implementation. @@ -303,18 +303,18 @@ void OnDatabaseLoaded(NTPSnippet::PtrVector snippets); void OnDatabaseError(); - // Callback for fetch-more requests with the NTPSnippetsFetcher. + // Callback for fetch-more requests with the RemoteSuggestionsFetcher. void OnFetchMoreFinished( const FetchDoneCallback& fetching_callback, Status status, - NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories); + RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories); - // Callback for regular fetch requests with the NTPSnippetsFetcher. + // Callback for regular fetch requests with the RemoteSuggestionsFetcher. void OnFetchFinished( std::unique_ptr<FetchStatusCallback> callback, bool interactive_request, Status status, - NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories); + RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories); // Moves all snippets from |to_archive| into the archive of the |content|. // Clears |to_archive|. As the archive is a FIFO buffer of limited size, this @@ -415,10 +415,10 @@ // Ranker that orders the categories. Not owned. CategoryRanker* category_ranker_; - // The snippets fetcher. - std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher_; + // The suggestions fetcher. + std::unique_ptr<RemoteSuggestionsFetcher> suggestions_fetcher_; - // The database for persisting snippets. + // The database for persisting suggestions. std::unique_ptr<RemoteSuggestionsDatabase> database_; base::TimeTicks database_load_start_;
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc index af4ccc8..9dc9b61 100644 --- a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc +++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
@@ -35,9 +35,9 @@ #include "components/ntp_snippets/ntp_snippets_constants.h" #include "components/ntp_snippets/pref_names.h" #include "components/ntp_snippets/remote/ntp_snippet.h" -#include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" #include "components/ntp_snippets/remote/persistent_scheduler.h" #include "components/ntp_snippets/remote/remote_suggestions_database.h" +#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h" #include "components/ntp_snippets/remote/test_utils.h" #include "components/ntp_snippets/user_classifier.h" #include "components/prefs/testing_pref_service.h" @@ -374,6 +374,7 @@ test_url_(kTestContentSuggestionsServerWithAPIKey), category_ranker_(base::MakeUnique<ConstantCategoryRanker>()), user_classifier_(/*pref_service=*/nullptr), + suggestions_fetcher_(nullptr), image_fetcher_(nullptr), image_decoder_(nullptr), database_(nullptr) { @@ -408,11 +409,11 @@ new net::TestURLRequestContextGetter(task_runner.get()); utils_.ResetSigninManager(); - std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher = - base::MakeUnique<NTPSnippetsFetcher>( - utils_.fake_signin_manager(), fake_token_service_.get(), - std::move(request_context_getter), utils_.pref_service(), nullptr, - base::Bind(&ParseJson), kAPIKey, &user_classifier_); + auto suggestions_fetcher = base::MakeUnique<RemoteSuggestionsFetcher>( + utils_.fake_signin_manager(), fake_token_service_.get(), + std::move(request_context_getter), utils_.pref_service(), nullptr, + base::Bind(&ParseJson), kAPIKey, &user_classifier_); + suggestions_fetcher_ = suggestions_fetcher.get(); utils_.fake_signin_manager()->SignIn("foo@bar.com"); @@ -429,7 +430,7 @@ database_ = database.get(); return base::MakeUnique<RemoteSuggestionsProviderImpl>( observer_.get(), utils_.pref_service(), "fr", category_ranker_.get(), - std::move(snippets_fetcher), std::move(image_fetcher), + std::move(suggestions_fetcher), std::move(image_fetcher), std::move(image_decoder), std::move(database), base::MakeUnique<RemoteSuggestionsStatusService>( utils_.fake_signin_manager(), utils_.pref_service())); @@ -488,6 +489,9 @@ protected: const GURL& test_url() { return test_url_; } FakeContentSuggestionsProviderObserver& observer() { return *observer_; } + RemoteSuggestionsFetcher* suggestions_fetcher() { + return suggestions_fetcher_; + } // TODO(tschumann): Make this a strict-mock. We want to avoid unneccesary // network requests. NiceMock<MockImageFetcher>* image_fetcher() { return image_fetcher_; } @@ -538,6 +542,7 @@ std::unique_ptr<CategoryRanker> category_ranker_; UserClassifier user_classifier_; std::unique_ptr<FakeContentSuggestionsProviderObserver> observer_; + RemoteSuggestionsFetcher* suggestions_fetcher_; NiceMock<MockImageFetcher>* image_fetcher_; FakeImageDecoder* image_decoder_; @@ -1080,10 +1085,10 @@ IdEq("http://id-10"))); } -// TODO(tschumann): We don't have test making sure the NTPSnippetsFetcher +// TODO(tschumann): We don't have test making sure the RemoteSuggestionsFetcher // actually gets the proper parameters. Add tests with an injected -// NTPSnippetsFetcher to verify the parameters, including proper handling of -// dismissed and known_ids. +// RemoteSuggestionsFetcher to verify the parameters, including proper handling +// of dismissed and known_ids. namespace { @@ -1115,9 +1120,8 @@ "invalid json string}]}", /*known_ids=*/std::set<std::string>(), base::Bind(&SuggestionsLoaded, &loaded)); - EXPECT_THAT( - service->snippets_fetcher_for_testing_and_debugging()->last_status(), - StartsWith("Received invalid JSON")); + EXPECT_THAT(suggestions_fetcher()->last_status(), + StartsWith("Received invalid JSON")); } TEST_F(RemoteSuggestionsProviderImplTest, @@ -1130,9 +1134,8 @@ GetTestJson({GetIncompleteSnippet()}), /*known_ids=*/std::set<std::string>(), base::Bind(&SuggestionsLoaded, &loaded)); - EXPECT_THAT( - service->snippets_fetcher_for_testing_and_debugging()->last_status(), - StartsWith("Invalid / empty list")); + EXPECT_THAT(suggestions_fetcher()->last_status(), + StartsWith("Invalid / empty list")); } TEST_F(RemoteSuggestionsProviderImplTest, @@ -1164,9 +1167,8 @@ auto service = MakeSnippetsService(); LoadFromJSONString(service.get(), GetTestJson({GetInvalidSnippet()})); - EXPECT_THAT( - service->snippets_fetcher_for_testing_and_debugging()->last_status(), - StartsWith("Received invalid JSON")); + EXPECT_THAT(suggestions_fetcher()->last_status(), + StartsWith("Received invalid JSON")); EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty()); } @@ -1175,14 +1177,11 @@ LoadFromJSONString(service.get(), GetTestJson({GetSnippet()})); ASSERT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1)); - ASSERT_EQ( - "OK", - service->snippets_fetcher_for_testing_and_debugging()->last_status()); + ASSERT_EQ("OK", suggestions_fetcher()->last_status()); LoadFromJSONString(service.get(), GetTestJson({GetInvalidSnippet()})); - EXPECT_THAT( - service->snippets_fetcher_for_testing_and_debugging()->last_status(), - StartsWith("Received invalid JSON")); + EXPECT_THAT(suggestions_fetcher()->last_status(), + StartsWith("Received invalid JSON")); // This should not have changed the existing snippets. EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1)); } @@ -1191,9 +1190,7 @@ auto service = MakeSnippetsService(); LoadFromJSONString(service.get(), GetTestJson({GetIncompleteSnippet()})); - EXPECT_EQ( - "Invalid / empty list.", - service->snippets_fetcher_for_testing_and_debugging()->last_status()); + EXPECT_EQ("Invalid / empty list.", suggestions_fetcher()->last_status()); EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty()); } @@ -1205,9 +1202,7 @@ ASSERT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1)); LoadFromJSONString(service.get(), GetTestJson({GetIncompleteSnippet()})); - EXPECT_EQ( - "Invalid / empty list.", - service->snippets_fetcher_for_testing_and_debugging()->last_status()); + EXPECT_EQ("Invalid / empty list.", suggestions_fetcher()->last_status()); // This should not have changed the existing snippets. EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1)); }
diff --git a/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc index a746e7b0..0b79eac 100644 --- a/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc +++ b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc
@@ -249,9 +249,9 @@ std::move(wrapper_callback))); } -const NTPSnippetsFetcher* SchedulingRemoteSuggestionsProvider:: - snippets_fetcher_for_testing_and_debugging() const { - return provider_->snippets_fetcher_for_testing_and_debugging(); +const RemoteSuggestionsFetcher* +SchedulingRemoteSuggestionsProvider::suggestions_fetcher_for_debugging() const { + return provider_->suggestions_fetcher_for_debugging(); } CategoryStatus SchedulingRemoteSuggestionsProvider::GetCategoryStatus(
diff --git a/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h index 053c9c5..7a42b47 100644 --- a/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h +++ b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h
@@ -79,7 +79,7 @@ std::unique_ptr<ProviderStatusCallback> callback) override; void RefetchInTheBackground( std::unique_ptr<FetchStatusCallback> callback) override; - const NTPSnippetsFetcher* snippets_fetcher_for_testing_and_debugging() + const RemoteSuggestionsFetcher* suggestions_fetcher_for_debugging() const override; // ContentSuggestionsProvider implementation.
diff --git a/components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc index a65c61ef..950923f 100644 --- a/components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc +++ b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc
@@ -51,7 +51,7 @@ namespace ntp_snippets { -class NTPSnippetsFetcher; +class RemoteSuggestionsFetcher; namespace { @@ -91,8 +91,8 @@ MOCK_METHOD1(RefetchInTheBackground, void(RemoteSuggestionsProvider::FetchStatusCallback)); - MOCK_CONST_METHOD0(snippets_fetcher_for_testing_and_debugging, - const NTPSnippetsFetcher*()); + MOCK_CONST_METHOD0(suggestions_fetcher_for_debugging, + const RemoteSuggestionsFetcher*()); MOCK_METHOD1(GetCategoryStatus, CategoryStatus(Category)); MOCK_METHOD1(GetCategoryInfo, CategoryInfo(Category));
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc index 574e49a..a2bbaf12 100644 --- a/components/password_manager/core/browser/password_manager.cc +++ b/components/password_manager/core/browser/password_manager.cc
@@ -382,6 +382,10 @@ all_visible_forms_.clear(); } +bool PasswordManager::IsPasswordFieldDetectedOnPage() { + return !pending_login_managers_.empty(); +} + void PasswordManager::RecordFailure(ProvisionalSaveFailure failure, const GURL& form_origin, BrowserSavePasswordProgressLogger* logger) {
diff --git a/components/password_manager/core/browser/password_manager.h b/components/password_manager/core/browser/password_manager.h index ccc1d89..6cb79e1 100644 --- a/components/password_manager/core/browser/password_manager.h +++ b/components/password_manager/core/browser/password_manager.h
@@ -172,6 +172,9 @@ // visible forms. void DropFormManagers(); + // Returns true if password element is detected on the current page. + bool IsPasswordFieldDetectedOnPage(); + PasswordManagerClient* client() { return client_; } #if defined(UNIT_TEST)
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.cc b/components/password_manager/core/browser/password_manager_metrics_util.cc index 8bc566bc..dc57abb 100644 --- a/components/password_manager/core/browser/password_manager_metrics_util.cc +++ b/components/password_manager/core/browser/password_manager_metrics_util.cc
@@ -131,13 +131,18 @@ void LogPasswordReuse(int password_length, int saved_passwords, - int number_matches) { + int number_matches, + bool password_field_detected) { UMA_HISTOGRAM_COUNTS_100("PasswordManager.PasswordReuse.PasswordLength", password_length); UMA_HISTOGRAM_COUNTS_1000("PasswordManager.PasswordReuse.TotalPasswords", saved_passwords); UMA_HISTOGRAM_COUNTS_1000("PasswordManager.PasswordReuse.NumberOfMatches", number_matches); + UMA_HISTOGRAM_ENUMERATION( + "PasswordManager.PasswordReuse.PasswordFieldDetected", + password_field_detected ? HAS_PASSWORD_FIELD : NO_PASSWORD_FIELD, + PASSWORD_REUSE_PASSWORD_FIELD_DETECTED_COUNT); } } // namespace metrics_util
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.h b/components/password_manager/core/browser/password_manager_metrics_util.h index c2377f1..fbc97bc 100644 --- a/components/password_manager/core/browser/password_manager_metrics_util.h +++ b/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -172,6 +172,12 @@ CREDENTIAL_MANAGER_GET_UNMEDIATED }; +enum PasswordReusePasswordFieldDetected { + NO_PASSWORD_FIELD, + HAS_PASSWORD_FIELD, + PASSWORD_REUSE_PASSWORD_FIELD_DETECTED_COUNT +}; + // A version of the UMA_HISTOGRAM_BOOLEAN macro that allows the |name| // to vary over the program's runtime. void LogUMAHistogramBoolean(const std::string& name, bool sample); @@ -230,7 +236,8 @@ // Log the password reuse. void LogPasswordReuse(int password_length, int saved_passwords, - int number_matches); + int number_matches, + bool password_field_detected); } // namespace metrics_util
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc index 5ce7d40..da9feef 100644 --- a/components/password_manager/core/browser/password_manager_unittest.cc +++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -391,7 +391,9 @@ observed.push_back(form); EXPECT_CALL(*store_, GetLogins(_, _)) .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms())); + EXPECT_FALSE(manager()->IsPasswordFieldDetectedOnPage()); manager()->OnPasswordFormsParsed(&driver_, observed); + EXPECT_TRUE(manager()->IsPasswordFieldDetectedOnPage()); manager()->OnPasswordFormsRendered(&driver_, observed, true); EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage()) @@ -407,6 +409,7 @@ observed.clear(); manager()->OnPasswordFormsParsed(&driver_, observed); manager()->OnPasswordFormsRendered(&driver_, observed, true); + EXPECT_FALSE(manager()->IsPasswordFieldDetectedOnPage()); // Simulate saving the form, as if the info bar was accepted. EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
diff --git a/components/password_manager/core/browser/password_reuse_detection_manager.cc b/components/password_manager/core/browser/password_reuse_detection_manager.cc index a4bab65..24a7c4f 100644 --- a/components/password_manager/core/browser/password_reuse_detection_manager.cc +++ b/components/password_manager/core/browser/password_reuse_detection_manager.cc
@@ -5,6 +5,7 @@ #include "components/password_manager/core/browser/password_reuse_detection_manager.h" #include "components/password_manager/core/browser/browser_save_password_progress_logger.h" +#include "components/password_manager/core/browser/password_manager.h" #include "components/password_manager/core/browser/password_manager_client.h" #include "components/password_manager/core/browser/password_manager_metrics_util.h" #include "components/password_manager/core/browser/password_manager_util.h" @@ -55,8 +56,10 @@ logger->LogString(BrowserSavePasswordProgressLogger::STRING_REUSE_FOUND, saved_domain); } - metrics_util::LogPasswordReuse(password.size(), saved_passwords, - number_matches); + + metrics_util::LogPasswordReuse( + password.size(), saved_passwords, number_matches, + client_->GetPasswordManager()->IsPasswordFieldDetectedOnPage()); } } // namespace password_manager
diff --git a/components/physical_web/data_source/fake_physical_web_data_source.cc b/components/physical_web/data_source/fake_physical_web_data_source.cc index d5ebbb11..70fea37 100644 --- a/components/physical_web/data_source/fake_physical_web_data_source.cc +++ b/components/physical_web/data_source/fake_physical_web_data_source.cc
@@ -17,6 +17,7 @@ std::unique_ptr<DictionaryValue> CreatePhysicalWebPage( const std::string& resolved_url, double distance_estimate, + const std::string& group_id, int scan_timestamp, const std::string& title, const std::string& description, @@ -24,6 +25,7 @@ auto page = base::MakeUnique<DictionaryValue>(); page->SetString(kScannedUrlKey, scanned_url); page->SetDouble(kDistanceEstimateKey, distance_estimate); + page->SetString(kGroupIdKey, group_id); // TODO(crbug.com/667722): Remove this integer workaround once timestamp is // fixed. page->SetInteger(kScanTimestampKey, scan_timestamp); @@ -38,8 +40,8 @@ int timestamp) { const std::string id_string = base::IntToString(id); return CreatePhysicalWebPage("https://resolved_url.com/" + id_string, - distance, timestamp, "title " + id_string, - "description " + id_string, + distance, /*group_id=*/std::string(), timestamp, + "title " + id_string, "description " + id_string, "https://scanned_url.com/" + id_string); }
diff --git a/components/physical_web/data_source/fake_physical_web_data_source.h b/components/physical_web/data_source/fake_physical_web_data_source.h index 6ccd1de6..c10543e3 100644 --- a/components/physical_web/data_source/fake_physical_web_data_source.h +++ b/components/physical_web/data_source/fake_physical_web_data_source.h
@@ -23,6 +23,7 @@ std::unique_ptr<base::DictionaryValue> CreatePhysicalWebPage( const std::string& resolved_url, double distance_estimate, + const std::string& group_id, int scan_timestamp, const std::string& title, const std::string& description,
diff --git a/content/browser/utility_process_mojo_client_browsertest.cc b/content/browser/utility_process_mojo_client_browsertest.cc index e3f031a6..6ac13b5 100644 --- a/content/browser/utility_process_mojo_client_browsertest.cc +++ b/content/browser/utility_process_mojo_client_browsertest.cc
@@ -20,7 +20,7 @@ // Test fixture used to make different Mojo calls to the utility process. class UtilityProcessMojoClientBrowserTest : public ContentBrowserTest { public: - void StartMojoService(bool disable_sandbox) { + void StartMojoService(bool disable_sandbox, bool run_elevated = false) { mojo_client_.reset(new UtilityProcessMojoClient<mojom::TestService>( base::ASCIIToUTF16("TestMojoProcess"))); @@ -31,6 +31,14 @@ // This test case needs to have the sandbox disabled. if (disable_sandbox) mojo_client_->set_disable_sandbox(); +#if defined(OS_WIN) + // This test case needs utility process privilege elevation. + if (run_elevated) { + CHECK(disable_sandbox); + mojo_client_->set_run_elevated(); + } +#endif // defined(OS_WIN) + mojo_client_->Start(); } @@ -136,6 +144,28 @@ EXPECT_TRUE(sandbox_succeeded_); EXPECT_FALSE(error_happened_); } -#endif +#endif // !defined(OS_ANDROID) + +#if defined(OS_WIN) +// Call a function that succeeds with process elevation. Elevation is only +// available on WIN, and is used to permit UAC prompts for operations that +// need user approval before proceeding. +IN_PROC_BROWSER_TEST_F(UtilityProcessMojoClientBrowserTest, ElevatedSuccess) { + base::RunLoop run_loop; + done_closure_ = run_loop.QuitClosure(); + + bool elevated_utility_process = true; + StartMojoService(true, elevated_utility_process); + + mojo_client_->service()->CreateFolder( + base::Bind(&UtilityProcessMojoClientBrowserTest::OnCreateFolderFinished, + base::Unretained(this))); + + run_loop.Run(); + EXPECT_TRUE(response_received_); + EXPECT_TRUE(sandbox_succeeded_); + EXPECT_FALSE(error_happened_); +} +#endif // defined(OS_WIN) } // namespace content
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index eca23b3f5..1fcfb01 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -44,6 +44,7 @@ WebRuntimeFeatures::enableAutoplayMutedVideos(true); // Android does not yet support SystemMonitor. WebRuntimeFeatures::enableOnDeviceChange(false); + WebRuntimeFeatures::enableMediaSession(true); #else // defined(OS_ANDROID) WebRuntimeFeatures::enableNavigatorContentUtils(true); if (base::FeatureList::IsEnabled(
diff --git a/content/public/browser/utility_process_mojo_client.h b/content/public/browser/utility_process_mojo_client.h index 29e76fa..cfcb93b 100644 --- a/content/public/browser/utility_process_mojo_client.h +++ b/content/public/browser/utility_process_mojo_client.h
@@ -48,6 +48,14 @@ helper_->set_disable_sandbox(); } +#if defined(OS_WIN) + // Allow the utility process to run with elevated privileges. + void set_run_elevated() { + DCHECK(!start_called_); + helper_->set_run_elevated(); + } +#endif // defined(OS_WIN) + // Starts the utility process and connect to the remote Mojo service. void Start() { DCHECK(thread_checker_.CalledOnValidThread()); @@ -96,15 +104,30 @@ void set_disable_sandbox() { disable_sandbox_ = true; } +#if defined(OS_WIN) + void set_run_elevated() { + disable_sandbox_ = true; + run_elevated_ = true; + } +#endif // defined(OS_WIN) + private: // Starts the utility process and connects to the remote Mojo service. void StartOnIOThread(const std::string& mojo_interface_name, mojo::ScopedMessagePipeHandle interface_pipe) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + utility_host_ = UtilityProcessHost::Create(nullptr, nullptr)->AsWeakPtr(); utility_host_->SetName(process_name_); + if (disable_sandbox_) utility_host_->DisableSandbox(); +#if defined(OS_WIN) + if (run_elevated_) { + DCHECK(disable_sandbox_); + utility_host_->ElevatePrivileges(); + } +#endif // defined(OS_WIN) utility_host_->Start(); @@ -115,6 +138,9 @@ // Properties of the utility process. base::string16 process_name_; bool disable_sandbox_ = false; +#if defined(OS_WIN) + bool run_elevated_ = false; +#endif // defined(OS_WIN) // Must only be accessed on the IO thread. base::WeakPtr<UtilityProcessHost> utility_host_;
diff --git a/content/public/renderer/render_thread.h b/content/public/renderer/render_thread.h index 86e7e9f..3e1759c 100644 --- a/content/public/renderer/render_thread.h +++ b/content/public/renderer/render_thread.h
@@ -106,6 +106,11 @@ // Retrieve the process ID of the browser process. virtual int32_t GetClientId() = 0; + + // Handles for posting tasks to appropriate renderer scheduler task queues. + virtual scoped_refptr<base::SingleThreadTaskRunner> GetTimerTaskRunner() = 0; + virtual scoped_refptr<base::SingleThreadTaskRunner> + GetLoadingTaskRunner() = 0; }; } // namespace content
diff --git a/content/public/test/mock_render_thread.cc b/content/public/test/mock_render_thread.cc index 4c9afbb..2ab1d2a 100644 --- a/content/public/test/mock_render_thread.cc +++ b/content/public/test/mock_render_thread.cc
@@ -249,6 +249,16 @@ return 1; } +scoped_refptr<base::SingleThreadTaskRunner> +MockRenderThread::GetTimerTaskRunner() { + return base::ThreadTaskRunnerHandle::Get(); +} + +scoped_refptr<base::SingleThreadTaskRunner> +MockRenderThread::GetLoadingTaskRunner() { + return base::ThreadTaskRunnerHandle::Get(); +} + #if defined(OS_WIN) void MockRenderThread::PreCacheFont(const LOGFONT& log_font) { }
diff --git a/content/public/test/mock_render_thread.h b/content/public/test/mock_render_thread.h index 63f492c24..67e625d 100644 --- a/content/public/test/mock_render_thread.h +++ b/content/public/test/mock_render_thread.h
@@ -81,6 +81,8 @@ bool ResolveProxy(const GURL& url, std::string* proxy_list) override; base::WaitableEvent* GetShutdownEvent() override; int32_t GetClientId() override; + scoped_refptr<base::SingleThreadTaskRunner> GetTimerTaskRunner() override; + scoped_refptr<base::SingleThreadTaskRunner> GetLoadingTaskRunner() override; #if defined(OS_WIN) void PreCacheFont(const LOGFONT& log_font) override; void ReleaseCachedFonts() override;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index a041df66..9966cc5 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc
@@ -1470,6 +1470,16 @@ return client_id_; } +scoped_refptr<base::SingleThreadTaskRunner> +RenderThreadImpl::GetTimerTaskRunner() { + return renderer_scheduler_->TimerTaskRunner(); +} + +scoped_refptr<base::SingleThreadTaskRunner> +RenderThreadImpl::GetLoadingTaskRunner() { + return renderer_scheduler_->LoadingTaskRunner(); +} + void RenderThreadImpl::OnAssociatedInterfaceRequest( const std::string& name, mojo::ScopedInterfaceEndpointHandle handle) {
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h index 2f00381..9c0cfe3 100644 --- a/content/renderer/render_thread_impl.h +++ b/content/renderer/render_thread_impl.h
@@ -209,6 +209,8 @@ bool ResolveProxy(const GURL& url, std::string* proxy_list) override; base::WaitableEvent* GetShutdownEvent() override; int32_t GetClientId() override; + scoped_refptr<base::SingleThreadTaskRunner> GetTimerTaskRunner() override; + scoped_refptr<base::SingleThreadTaskRunner> GetLoadingTaskRunner() override; // IPC::Listener implementation via ChildThreadImpl: void OnAssociatedInterfaceRequest(
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm index 21abdeef5..291afaf 100644 --- a/ios/chrome/app/main_controller.mm +++ b/ios/chrome/app/main_controller.mm
@@ -727,6 +727,8 @@ if (switchFromIncognito) startInIncognito = NO; + [MDCTypography setFontLoader:[MDFRobotoFontLoader sharedInstance]]; + [self createInitialUI:(startInIncognito ? ApplicationMode::INCOGNITO : ApplicationMode::NORMAL)]; @@ -738,8 +740,6 @@ [_browserViewWrangler updateDeviceSharingManager]; - [MDCTypography setFontLoader:[MDFRobotoFontLoader sharedInstance]]; - [self openTabFromLaunchOptions:_launchOptions startupInformation:self appState:self.appState];
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc index 338e71d..fe6d78da 100644 --- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc +++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
@@ -25,9 +25,9 @@ #include "components/ntp_snippets/content_suggestions_service.h" #include "components/ntp_snippets/features.h" #include "components/ntp_snippets/ntp_snippets_constants.h" -#include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" #include "components/ntp_snippets/remote/persistent_scheduler.h" #include "components/ntp_snippets/remote/remote_suggestions_database.h" +#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h" #include "components/ntp_snippets/remote/remote_suggestions_provider_impl.h" #include "components/ntp_snippets/remote/remote_suggestions_status_service.h" #include "components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h" @@ -52,9 +52,9 @@ using ios::BookmarkModelFactory; using ntp_snippets::BookmarkSuggestionsProvider; using ntp_snippets::ContentSuggestionsService; -using ntp_snippets::NTPSnippetsFetcher; using ntp_snippets::PersistentScheduler; using ntp_snippets::RemoteSuggestionsDatabase; +using ntp_snippets::RemoteSuggestionsFetcher; using ntp_snippets::RemoteSuggestionsProviderImpl; using ntp_snippets::RemoteSuggestionsStatusService; using ntp_snippets::SchedulingRemoteSuggestionsProvider; @@ -163,7 +163,7 @@ auto provider = base::MakeUnique<RemoteSuggestionsProviderImpl>( service.get(), prefs, GetApplicationContext()->GetApplicationLocale(), service->category_ranker(), - base::MakeUnique<NTPSnippetsFetcher>( + base::MakeUnique<RemoteSuggestionsFetcher>( signin_manager, token_service, request_context, prefs, nullptr, base::Bind(&ParseJson), GetChannel() == version_info::Channel::STABLE
diff --git a/ios/chrome/browser/payments/payment_request_coordinator.mm b/ios/chrome/browser/payments/payment_request_coordinator.mm index 5f03a15..11e16da 100644 --- a/ios/chrome/browser/payments/payment_request_coordinator.mm +++ b/ios/chrome/browser/payments/payment_request_coordinator.mm
@@ -165,33 +165,35 @@ if (!_selectedPaymentMethod->billing_address_id().empty()) { autofill::AutofillProfile* address = _personalDataManager->GetProfileByGUID( _selectedPaymentMethod->billing_address_id()); - paymentResponse.details.billing_address.country = - address->GetRawInfo(autofill::ADDRESS_HOME_COUNTRY); - paymentResponse.details.billing_address.address_line.push_back( - address->GetRawInfo(autofill::ADDRESS_HOME_LINE1)); - paymentResponse.details.billing_address.address_line.push_back( - address->GetRawInfo(autofill::ADDRESS_HOME_LINE2)); - paymentResponse.details.billing_address.address_line.push_back( - address->GetRawInfo(autofill::ADDRESS_HOME_LINE3)); - paymentResponse.details.billing_address.region = - address->GetRawInfo(autofill::ADDRESS_HOME_STATE); - paymentResponse.details.billing_address.city = - address->GetRawInfo(autofill::ADDRESS_HOME_CITY); - paymentResponse.details.billing_address.dependent_locality = - address->GetRawInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY); - paymentResponse.details.billing_address.postal_code = - address->GetRawInfo(autofill::ADDRESS_HOME_ZIP); - paymentResponse.details.billing_address.sorting_code = - address->GetRawInfo(autofill::ADDRESS_HOME_SORTING_CODE); - paymentResponse.details.billing_address.language_code = - base::UTF8ToUTF16(address->language_code()); - paymentResponse.details.billing_address.organization = - address->GetRawInfo(autofill::COMPANY_NAME); - paymentResponse.details.billing_address.recipient = - address->GetInfo(autofill::AutofillType(autofill::NAME_FULL), - GetApplicationContext()->GetApplicationLocale()); - paymentResponse.details.billing_address.phone = - address->GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER); + if (address) { + paymentResponse.details.billing_address.country = + address->GetRawInfo(autofill::ADDRESS_HOME_COUNTRY); + paymentResponse.details.billing_address.address_line.push_back( + address->GetRawInfo(autofill::ADDRESS_HOME_LINE1)); + paymentResponse.details.billing_address.address_line.push_back( + address->GetRawInfo(autofill::ADDRESS_HOME_LINE2)); + paymentResponse.details.billing_address.address_line.push_back( + address->GetRawInfo(autofill::ADDRESS_HOME_LINE3)); + paymentResponse.details.billing_address.region = + address->GetRawInfo(autofill::ADDRESS_HOME_STATE); + paymentResponse.details.billing_address.city = + address->GetRawInfo(autofill::ADDRESS_HOME_CITY); + paymentResponse.details.billing_address.dependent_locality = + address->GetRawInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY); + paymentResponse.details.billing_address.postal_code = + address->GetRawInfo(autofill::ADDRESS_HOME_ZIP); + paymentResponse.details.billing_address.sorting_code = + address->GetRawInfo(autofill::ADDRESS_HOME_SORTING_CODE); + paymentResponse.details.billing_address.language_code = + base::UTF8ToUTF16(address->language_code()); + paymentResponse.details.billing_address.organization = + address->GetRawInfo(autofill::COMPANY_NAME); + paymentResponse.details.billing_address.recipient = + address->GetInfo(autofill::AutofillType(autofill::NAME_FULL), + GetApplicationContext()->GetApplicationLocale()); + paymentResponse.details.billing_address.phone = + address->GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER); + } } [_delegate paymentRequestCoordinatorDidConfirm:paymentResponse];
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn index b5b979e..d5f8a7d 100644 --- a/ios/chrome/browser/ui/ntp/BUILD.gn +++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -69,6 +69,9 @@ "resources/ntp_mv_welcome_favicon@2x.png", "resources/ntp_opentabs@2x.png", "resources/ntp_opentabs@3x.png", + "resources/ntp_opentabs_clock.png", + "resources/ntp_opentabs_clock@2x.png", + "resources/ntp_opentabs_clock@3x.png", "resources/ntp_opentabs_header@2x~ipad.png", "resources/ntp_opentabs_header~ipad.png", "resources/ntp_opentabs_laptop.png",
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/views/show_full_history_view.mm b/ios/chrome/browser/ui/ntp/recent_tabs/views/show_full_history_view.mm index 74fb81b..c0c25734 100644 --- a/ios/chrome/browser/ui/ntp/recent_tabs/views/show_full_history_view.mm +++ b/ios/chrome/browser/ui/ntp/recent_tabs/views/show_full_history_view.mm
@@ -29,7 +29,7 @@ if (self) { UIImageView* icon = [[UIImageView alloc] initWithImage:nil]; [icon setTranslatesAutoresizingMaskIntoConstraints:NO]; - [icon setImage:[UIImage imageNamed:@"ntp_opentabs"]]; + [icon setImage:[UIImage imageNamed:@"ntp_opentabs_clock"]]; UILabel* label = [[UILabel alloc] initWithFrame:CGRectZero]; [label setTranslatesAutoresizingMaskIntoConstraints:NO];
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_opentabs_clock.png b/ios/chrome/browser/ui/ntp/resources/ntp_opentabs_clock.png new file mode 100644 index 0000000..fa96864 --- /dev/null +++ b/ios/chrome/browser/ui/ntp/resources/ntp_opentabs_clock.png Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_opentabs_clock@2x.png b/ios/chrome/browser/ui/ntp/resources/ntp_opentabs_clock@2x.png new file mode 100644 index 0000000..534a04f --- /dev/null +++ b/ios/chrome/browser/ui/ntp/resources/ntp_opentabs_clock@2x.png Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_opentabs_clock@3x.png b/ios/chrome/browser/ui/ntp/resources/ntp_opentabs_clock@3x.png new file mode 100644 index 0000000..a10996bc --- /dev/null +++ b/ios/chrome/browser/ui/ntp/resources/ntp_opentabs_clock@3x.png Binary files differ
diff --git a/ios/net/crn_http_protocol_handler.mm b/ios/net/crn_http_protocol_handler.mm index c067305..4da8359 100644 --- a/ios/net/crn_http_protocol_handler.mm +++ b/ios/net/crn_http_protocol_handler.mm
@@ -341,11 +341,8 @@ DVLOG(2) << "Redirect, to client:"; LogNSURLRequest(request_); #endif // !defined(NDEBUG) - if (tracker_) { + if (tracker_) tracker_->StopRedirectedRequest(request); - // Add clients from tracker that depend on redirect data. - PushClients(tracker_->ClientsHandlingRedirect(*request, new_url, response)); - } [top_level_client_ wasRedirectedToRequest:request_ nativeRequest:request @@ -501,10 +498,6 @@ long long expectedContentLength = [response expectedContentLength]; if (expectedContentLength > 0) tracker_->CaptureExpectedLength(net_request_, expectedContentLength); - - // Add clients from tracker. - PushClients( - tracker_->ClientsHandlingRequestAndResponse(*net_request_, response)); } // Don't call any function on the response from now on, as the client may be @@ -660,11 +653,6 @@ return; } - if (tracker_) { - // Set up any clients that can operate regardless of the request - PushClients(tracker_->ClientsHandlingAnyRequest()); - } - // Now that all of the network clients are set up, if there was an error with // the URL, it can be raised and all of the clients will have a chance to // handle it. @@ -696,10 +684,6 @@ CopyHttpHeaders(request_, net_request_); - // Add network clients. - if (tracker_) - PushClients(tracker_->ClientsHandlingRequest(*net_request_)); - [top_level_client_ didCreateNativeRequest:net_request_]; SetLoadFlags();
diff --git a/ios/net/request_tracker.h b/ios/net/request_tracker.h index 117b412..098dbc92 100644 --- a/ios/net/request_tracker.h +++ b/ios/net/request_tracker.h
@@ -9,7 +9,6 @@ #include <stdint.h> #include "base/callback_forward.h" -#include "base/mac/scoped_nsobject.h" #include "base/memory/weak_ptr.h" #include "base/threading/thread_checker.h" @@ -19,13 +18,9 @@ class URLRequestContext; } -@class CRNForwardingNetworkClientFactory; -class GURL; - namespace net { -// RequestTracker can be used to observe the network requests and customize the -// behavior of the network stack with CRNForwardingNetworkClients. +// RequestTracker can be used to observe the network requests. // Each network request can be associated with a RequestTracker through the // GetRequestTracker(). // RequestTracker requires a RequestTrackerFactory. @@ -74,32 +69,9 @@ // This function has to be called before using the tracker. virtual void Init(); - // Add a factory that may create network clients for requests going through - // this tracker. - void AddNetworkClientFactory(CRNForwardingNetworkClientFactory* factory); - // Gets the request context associated with the tracker. virtual URLRequestContext* GetRequestContext() = 0; - // Network client generation methods. All of these four ClientsHandling... - // methods return an array of CRNForwardingNetworkClient instances, according - // to the CRNForwardingNetworkClientFactories added to the tracker. The array - // may be empty. The caller is responsible for taking ownership of the clients - // in the array. - - // Returns clients that can handle any request. - NSArray* ClientsHandlingAnyRequest(); - // Returns clients that can handle |request|. - NSArray* ClientsHandlingRequest(const URLRequest& request); - // Returns clients that can handle |request| with |response|. - NSArray* ClientsHandlingRequestAndResponse(const URLRequest& request, - NSURLResponse* response); - // Returns clients that can handle a redirect of |request| to |new_url| based - // on |redirect_response|. - NSArray* ClientsHandlingRedirect(const URLRequest& request, - const GURL& new_url, - NSURLResponse* redirect_response); - // Informs the tracker that a request has started. virtual void StartRequest(URLRequest* request) = 0; @@ -146,10 +118,6 @@ void InvalidateWeakPtrs(); private: - // Array of client factories that may be added by CRNHTTPProtocolHandler. The - // array lives on the IO thread. - base::scoped_nsobject<NSMutableArray> client_factories_; - bool initialized_; CacheMode cache_mode_; base::ThreadChecker thread_checker_;
diff --git a/ios/net/request_tracker.mm b/ios/net/request_tracker.mm index 85dce7f..a28c739 100644 --- a/ios/net/request_tracker.mm +++ b/ios/net/request_tracker.mm
@@ -5,8 +5,6 @@ #include "ios/net/request_tracker.h" #include "base/logging.h" -#import "ios/net/clients/crn_forwarding_network_client.h" -#import "ios/net/clients/crn_forwarding_network_client_factory.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -41,26 +39,8 @@ return g_request_tracker_factory->GetRequestTracker(request, tracker); } -void RequestTracker::AddNetworkClientFactory( - CRNForwardingNetworkClientFactory* factory) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK([[factory clientClass] - isSubclassOfClass:[CRNForwardingNetworkClient class]]); - // Sanity check: We don't already have a factory of the type being added. - DCHECK([client_factories_ indexOfObjectPassingTest:^BOOL(id obj, - NSUInteger idx, - BOOL* stop) { - return [obj clientClass] == [factory clientClass]; - }] == NSNotFound); - [client_factories_ addObject:factory]; - if ([factory requiresOrdering]) { - [client_factories_ sortUsingSelector:@selector(orderRelativeTo:)]; - } -} - RequestTracker::RequestTracker() - : client_factories_([[NSMutableArray alloc] init]), - initialized_(false), + : initialized_(false), cache_mode_(CACHE_NORMAL), weak_ptr_factory_(this) { // RequestTracker can be created from the main thread and used from another @@ -85,64 +65,6 @@ initialized_ = true; } -NSArray* RequestTracker::ClientsHandlingAnyRequest() { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(initialized_); - NSMutableArray* applicable_clients = [NSMutableArray array]; - for (CRNForwardingNetworkClientFactory* factory in client_factories_.get()) { - CRNForwardingNetworkClient* client = [factory clientHandlingAnyRequest]; - if (client) - [applicable_clients addObject:client]; - } - return applicable_clients; -} - -NSArray* RequestTracker::ClientsHandlingRequest(const URLRequest& request) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(initialized_); - NSMutableArray* applicable_clients = [NSMutableArray array]; - for (CRNForwardingNetworkClientFactory* factory in client_factories_.get()) { - CRNForwardingNetworkClient* client = - [factory clientHandlingRequest:request]; - if (client) - [applicable_clients addObject:client]; - } - return applicable_clients; -} - -NSArray* RequestTracker::ClientsHandlingRequestAndResponse( - const URLRequest& request, - NSURLResponse* response) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(initialized_); - NSMutableArray* applicable_clients = [NSMutableArray array]; - for (CRNForwardingNetworkClientFactory* factory in client_factories_.get()) { - CRNForwardingNetworkClient* client = - [factory clientHandlingResponse:response request:request]; - if (client) - [applicable_clients addObject:client]; - } - return applicable_clients; -} - -NSArray* RequestTracker::ClientsHandlingRedirect( - const URLRequest& request, - const GURL& new_url, - NSURLResponse* redirect_response) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(initialized_); - NSMutableArray* applicable_clients = [NSMutableArray array]; - for (CRNForwardingNetworkClientFactory* factory in client_factories_.get()) { - CRNForwardingNetworkClient* client = - [factory clientHandlingRedirect:request - url:new_url - response:redirect_response]; - if (client) - [applicable_clients addObject:client]; - } - return applicable_clients; -} - RequestTracker::CacheMode RequestTracker::GetCacheMode() const { DCHECK(thread_checker_.CalledOnValidThread()); return cache_mode_;
diff --git a/pdf/pdf.cc b/pdf/pdf.cc index e6cf5480..917ae472 100644 --- a/pdf/pdf.cc +++ b/pdf/pdf.cc
@@ -123,6 +123,10 @@ void SetPDFUseGDIPrinting(bool enable) { PDFEngineExports::Get()->SetPDFUseGDIPrinting(enable); } + +void SetPDFPostscriptPrintingLevel(int postscript_level) { + PDFEngineExports::Get()->SetPDFPostscriptPrintingLevel(postscript_level); +} #endif // defined(OS_WIN) bool GetPDFDocInfo(const void* pdf_buffer,
diff --git a/pdf/pdf.h b/pdf/pdf.h index c785b551..d18489d 100644 --- a/pdf/pdf.h +++ b/pdf/pdf.h
@@ -82,6 +82,8 @@ PDFEnsureTypefaceCharactersAccessible func); void SetPDFUseGDIPrinting(bool enable); + +void SetPDFPostscriptPrintingLevel(int postscript_level); #endif // defined(OS_WIN) // |page_count| and |max_page_width| are optional and can be NULL.
diff --git a/pdf/pdf_engine.h b/pdf/pdf_engine.h index 54e9f2f..2688f25 100644 --- a/pdf/pdf_engine.h +++ b/pdf/pdf_engine.h
@@ -341,6 +341,8 @@ PDFEnsureTypefaceCharactersAccessible func) = 0; virtual void SetPDFUseGDIPrinting(bool enable) = 0; + + virtual void SetPDFPostscriptPrintingLevel(int postscript_level) = 0; #endif // defined(OS_WIN) // See the definition of RenderPDFPageToBitmap in pdf.cc for details.
diff --git a/pdf/pdfium/fuzzers/BUILD.gn b/pdf/pdfium/fuzzers/BUILD.gn index 1300910..4636a39b 100644 --- a/pdf/pdfium/fuzzers/BUILD.gn +++ b/pdf/pdfium/fuzzers/BUILD.gn
@@ -35,6 +35,13 @@ ] } +fuzzer_test("pdf_codec_a85_fuzzer") { + sources = [] + deps = [ + "//third_party/pdfium/testing/libfuzzer:pdf_codec_a85_fuzzer", + ] +} + fuzzer_test("pdf_codec_fax_fuzzer") { sources = [] deps = [ @@ -42,6 +49,13 @@ ] } +fuzzer_test("pdf_codec_rle_fuzzer") { + sources = [] + deps = [ + "//third_party/pdfium/testing/libfuzzer:pdf_codec_rle_fuzzer", + ] +} + fuzzer_test("pdf_codec_icc_fuzzer") { sources = [] deps = [
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc index 111ea87..3e8f5b49 100644 --- a/pdf/pdfium/pdfium_engine.cc +++ b/pdf/pdfium/pdfium_engine.cc
@@ -4103,6 +4103,12 @@ void PDFiumEngineExports::SetPDFUseGDIPrinting(bool enable) { FPDF_SetPrintTextWithGDI(enable); } + +void PDFiumEngineExports::SetPDFPostscriptPrintingLevel( + int postscript_level) { + FPDF_SetPrintPostscriptLevel(postscript_level); +} + #endif // defined(OS_WIN) bool PDFiumEngineExports::RenderPDFPageToBitmap(
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h index e6ff4fcb2..3a525dc 100644 --- a/pdf/pdfium/pdfium_engine.h +++ b/pdf/pdfium/pdfium_engine.h
@@ -775,6 +775,8 @@ PDFEnsureTypefaceCharactersAccessible func) override; void SetPDFUseGDIPrinting(bool enable) override; + + void SetPDFPostscriptPrintingLevel(int postscript_level) override; #endif // defined(OS_WIN) bool RenderPDFPageToBitmap(const void* pdf_buffer, int pdf_buffer_size,
diff --git a/skia/ext/raster_handle_allocator_win.cc b/skia/ext/raster_handle_allocator_win.cc index e78d622e..f5c4bdd 100644 --- a/skia/ext/raster_handle_allocator_win.cc +++ b/skia/ext/raster_handle_allocator_win.cc
@@ -118,7 +118,7 @@ bool is_opaque, HANDLE shared_section, OnFailureType failure_type) { - SkAlphaType alpha = is_opaque ? kPremul_SkAlphaType : kOpaque_SkAlphaType; + SkAlphaType alpha = is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType; SkImageInfo info = SkImageInfo::MakeN32(width, height, alpha); size_t row_bytes = PlatformCanvasStrideForWidth(width);
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index bdec830..132628a 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2312,4 +2312,5 @@ # Sheriff failures Jan 17 2017 crbug.com/682105 shapedetection/detection-HTMLImageElement.html [ Pass Failure ] +crbug.com/682105 shapedetection/detection-HTMLVideoElement.html [ Pass Failure ] crbug.com/682105 fast/shapedetection/shapedetection-security-test.html [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/fast/css3-text/css3-text-decoration/text-decoration-skip-expected.png b/third_party/WebKit/LayoutTests/fast/css3-text/css3-text-decoration/text-decoration-skip-expected.png index 3a33b31..c563597 100644 --- a/third_party/WebKit/LayoutTests/fast/css3-text/css3-text-decoration/text-decoration-skip-expected.png +++ b/third_party/WebKit/LayoutTests/fast/css3-text/css3-text-decoration/text-decoration-skip-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/dom/geometry-interface-dom-quad.html b/third_party/WebKit/LayoutTests/fast/dom/geometry-interface-dom-quad.html index 2ffb2b7..071ba7f 100644 --- a/third_party/WebKit/LayoutTests/fast/dom/geometry-interface-dom-quad.html +++ b/third_party/WebKit/LayoutTests/fast/dom/geometry-interface-dom-quad.html
@@ -15,4 +15,16 @@ assert_dom_point_equals(quad.p4, p4); }, "DOMQuad() constructor"); +test(() => { + var p1 = new DOMPoint(0, 1, 3, 4); + var p2 = new DOMPoint(5, 6, 7, 8); + var p3 = new DOMPoint(9, 10, 11, 12); + var p4 = new DOMPoint(13, 14, 15, 16); + var quad = new DOMQuad(p1, p2, p3, p4); + assert_dom_point_equals(quad.toJSON().p1, p1, "p1"); + assert_dom_point_equals(quad.toJSON().p2, p2, "p2"); + assert_dom_point_equals(quad.toJSON().p3, p3, "p3"); + assert_dom_point_equals(quad.toJSON().p4, p4, "p4"); +}, "DOMQuad() toJSON()"); + </script>
diff --git a/third_party/WebKit/LayoutTests/media/deprecated-css-selectors-expected.txt b/third_party/WebKit/LayoutTests/media/deprecated-css-selectors-expected.txt new file mode 100644 index 0000000..6f44507 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/deprecated-css-selectors-expected.txt
@@ -0,0 +1,11 @@ +CONSOLE WARNING: -internal-media-controls-cast-button selector is deprecated and will be removed in M59, around June 2017. See https://www.chromestatus.com/features/5734009183141888 for more details. +CONSOLE WARNING: -internal-media-controls-overlay-cast-button selector is deprecated and will be removed in M59, around June 2017. See https://www.chromestatus.com/features/5714245488476160 for more details. +CONSOLE WARNING: -internal-media-controls-text-track-list* selectors is deprecated and will be removed in M59, around June 2017. See https://www.chromestatus.com/features/5661431349379072 for more details. +CONSOLE WARNING: -internal-media-controls-text-track-list* selectors is deprecated and will be removed in M59, around June 2017. See https://www.chromestatus.com/features/5661431349379072 for more details. +CONSOLE WARNING: -internal-media-controls-text-track-list* selectors is deprecated and will be removed in M59, around June 2017. See https://www.chromestatus.com/features/5661431349379072 for more details. +CONSOLE WARNING: -internal-media-controls-text-track-list* selectors is deprecated and will be removed in M59, around June 2017. See https://www.chromestatus.com/features/5661431349379072 for more details. +CONSOLE WARNING: -internal-media-controls-text-track-list* selectors is deprecated and will be removed in M59, around June 2017. See https://www.chromestatus.com/features/5661431349379072 for more details. +This is a testharness.js-based test. +PASS Test that -internal-media-controls-* deprecated selectors throw a warning +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/media/deprecated-css-selectors.html b/third_party/WebKit/LayoutTests/media/deprecated-css-selectors.html new file mode 100644 index 0000000..2624a2b --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/deprecated-css-selectors.html
@@ -0,0 +1,21 @@ +<!DOCTYPE html> +<title>Test that -internal-media-controls-* deprecated selectors throw a warning</title> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<style> +video::-internal-media-controls-cast-button, +video::-internal-media-controls-overlay-cast-button, +video::-internal-media-controls-text-track-list, +video::-internal-media-controls-text-track-list-item, +video::-internal-media-controls-text-track-list-item-input, +video::-internal-media-controls-text-track-list-kind-captions, +video::-internal-media-controls-text-track-list-kind-subtitles { + background-color: blue; +} +</style> +<video controls></video> +<script> +test(() => { + // Doing nothing, we just want to check the warnings. +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt index eae9fa9..50dda35d 100644 --- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -1148,6 +1148,7 @@ getter p3 getter p4 method constructor + method toJSON interface DOMRect : DOMRectReadOnly attribute @@toStringTag getter height
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp index c6fb0b8b..0e8ed5a 100644 --- a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
@@ -394,6 +394,9 @@ // NOTE: Some threads (namely utility threads) don't have a scheduler. WebScheduler* scheduler = Platform::current()->currentThread()->scheduler(); + // When timer task runner is used for PerIsolateData, GC tasks are getting + // throttled and memory usage goes up. For now we're using loading task queue + // to prevent this. // TODO(altimin): Consider switching to timerTaskRunner here. v8::Isolate* isolate = V8PerIsolateData::initialize( scheduler ? scheduler->loadingTaskRunner()
diff --git a/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp index 37b6f8f..221797d 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp
@@ -7,6 +7,7 @@ #include "core/css/CSSSelectorList.h" #include "core/css/StyleSheetContents.h" #include "core/css/parser/CSSParserContext.h" +#include "core/frame/Deprecation.h" #include "core/frame/UseCounter.h" #include "platform/RuntimeEnabledFeatures.h" #include "wtf/PtrUtil.h" @@ -14,84 +15,7 @@ namespace blink { -static void recordSelectorStats(const CSSParserContext* context, - const CSSSelectorList& selectorList) { - if (!context->isUseCounterRecordingEnabled()) - return; - - for (const CSSSelector* selector = selectorList.first(); selector; - selector = CSSSelectorList::next(*selector)) { - for (const CSSSelector* current = selector; current; - current = current->tagHistory()) { - UseCounter::Feature feature = UseCounter::NumberOfFeatures; - switch (current->getPseudoType()) { - case CSSSelector::PseudoAny: - feature = UseCounter::CSSSelectorPseudoAny; - break; - case CSSSelector::PseudoUnresolved: - feature = UseCounter::CSSSelectorPseudoUnresolved; - break; - case CSSSelector::PseudoDefined: - feature = UseCounter::CSSSelectorPseudoDefined; - break; - case CSSSelector::PseudoSlotted: - feature = UseCounter::CSSSelectorPseudoSlotted; - break; - case CSSSelector::PseudoContent: - feature = UseCounter::CSSSelectorPseudoContent; - break; - case CSSSelector::PseudoHost: - feature = UseCounter::CSSSelectorPseudoHost; - break; - case CSSSelector::PseudoHostContext: - feature = UseCounter::CSSSelectorPseudoHostContext; - break; - case CSSSelector::PseudoFullScreenAncestor: - feature = UseCounter::CSSSelectorPseudoFullScreenAncestor; - break; - case CSSSelector::PseudoFullScreen: - feature = UseCounter::CSSSelectorPseudoFullScreen; - break; - case CSSSelector::PseudoListBox: - if (context->mode() != UASheetMode) - feature = UseCounter::CSSSelectorInternalPseudoListBox; - break; - case CSSSelector::PseudoWebKitCustomElement: - if (context->mode() != UASheetMode) { - if (current->value() == "-internal-media-controls-cast-button") - feature = UseCounter::CSSSelectorInternalMediaControlsCastButton; - else if (current->value() == - "-internal-media-controls-overlay-cast-button") - feature = - UseCounter::CSSSelectorInternalMediaControlsOverlayCastButton; - } - break; - case CSSSelector::PseudoSpatialNavigationFocus: - if (context->mode() != UASheetMode) - feature = - UseCounter::CSSSelectorInternalPseudoSpatialNavigationFocus; - break; - case CSSSelector::PseudoReadOnly: - if (context->mode() != UASheetMode) - feature = UseCounter::CSSSelectorPseudoReadOnly; - break; - case CSSSelector::PseudoReadWrite: - if (context->mode() != UASheetMode) - feature = UseCounter::CSSSelectorPseudoReadWrite; - break; - default: - break; - } - if (feature != UseCounter::NumberOfFeatures) - context->useCounter()->count(feature); - if (current->relation() == CSSSelector::IndirectAdjacent) - context->useCounter()->count(UseCounter::CSSSelectorIndirectAdjacent); - if (current->selectorList()) - recordSelectorStats(context, *current->selectorList()); - } - } -} - +// static CSSSelectorList CSSSelectorParser::parseSelector( CSSParserTokenRange range, const CSSParserContext* context, @@ -102,7 +26,7 @@ if (!range.atEnd()) return CSSSelectorList(); - recordSelectorStats(context, result); + parser.recordUsageAndDeprecations(result); return result; } @@ -880,4 +804,113 @@ return secondCompound; } +void CSSSelectorParser::recordUsageAndDeprecations( + const CSSSelectorList& selectorList) { + if (!m_context->useCounter()) + return; + + for (const CSSSelector* selector = selectorList.first(); selector; + selector = CSSSelectorList::next(*selector)) { + for (const CSSSelector* current = selector; current; + current = current->tagHistory()) { + UseCounter::Feature feature = UseCounter::NumberOfFeatures; + switch (current->getPseudoType()) { + case CSSSelector::PseudoAny: + feature = UseCounter::CSSSelectorPseudoAny; + break; + case CSSSelector::PseudoUnresolved: + feature = UseCounter::CSSSelectorPseudoUnresolved; + break; + case CSSSelector::PseudoDefined: + feature = UseCounter::CSSSelectorPseudoDefined; + break; + case CSSSelector::PseudoSlotted: + feature = UseCounter::CSSSelectorPseudoSlotted; + break; + case CSSSelector::PseudoContent: + feature = UseCounter::CSSSelectorPseudoContent; + break; + case CSSSelector::PseudoHost: + feature = UseCounter::CSSSelectorPseudoHost; + break; + case CSSSelector::PseudoHostContext: + feature = UseCounter::CSSSelectorPseudoHostContext; + break; + case CSSSelector::PseudoFullScreenAncestor: + feature = UseCounter::CSSSelectorPseudoFullScreenAncestor; + break; + case CSSSelector::PseudoFullScreen: + feature = UseCounter::CSSSelectorPseudoFullScreen; + break; + case CSSSelector::PseudoListBox: + if (m_context->mode() != UASheetMode) + feature = UseCounter::CSSSelectorInternalPseudoListBox; + break; + case CSSSelector::PseudoWebKitCustomElement: + if (m_context->mode() != UASheetMode) { + if (current->value() == "-internal-media-controls-cast-button") { + feature = UseCounter::CSSSelectorInternalMediaControlsCastButton; + } else if (current->value() == + "-internal-media-controls-overlay-cast-button") { + feature = + UseCounter::CSSSelectorInternalMediaControlsOverlayCastButton; + } else if (current->value() == + "-internal-media-controls-text-track-list") { + feature = + UseCounter::CSSSelectorInternalMediaControlsTextTrackList; + } else if (current->value() == + "-internal-media-controls-text-track-list-item") { + feature = + UseCounter::CSSSelectorInternalMediaControlsTextTrackListItem; + } else if (current->value() == + "-internal-media-controls-text-track-list-item-input") { + feature = UseCounter:: + CSSSelectorInternalMediaControlsTextTrackListItemInput; + } else if (current->value() == + "-internal-media-controls-text-track-list-kind-" + "captions") { + feature = UseCounter:: + CSSSelectorInternalMediaControlsTextTrackListKindCaptions; + } else if (current->value() == + "-internal-media-controls-text-track-list-kind-" + "subtitles") { + feature = UseCounter:: + CSSSelectorInternalMediaControlsTextTrackListKindSubtitles; + } + } + break; + case CSSSelector::PseudoSpatialNavigationFocus: + if (m_context->mode() != UASheetMode) { + feature = + UseCounter::CSSSelectorInternalPseudoSpatialNavigationFocus; + } + break; + case CSSSelector::PseudoReadOnly: + if (m_context->mode() != UASheetMode) + feature = UseCounter::CSSSelectorPseudoReadOnly; + break; + case CSSSelector::PseudoReadWrite: + if (m_context->mode() != UASheetMode) + feature = UseCounter::CSSSelectorPseudoReadWrite; + break; + default: + break; + } + if (feature != UseCounter::NumberOfFeatures) { + if (!Deprecation::deprecationMessage(feature).isEmpty() && + m_styleSheet->anyOwnerDocument()) { + Deprecation::countDeprecation(*m_styleSheet->anyOwnerDocument(), + feature); + } else { + m_context->useCounter()->count(feature); + } + } + if (current->relation() == CSSSelector::IndirectAdjacent) + m_context->useCounter()->count(UseCounter::CSSSelectorIndirectAdjacent); + if (current->selectorList()) + recordUsageAndDeprecations(*current->selectorList()); + } + } +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.h b/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.h index 216e262..fa3180c 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.h +++ b/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.h
@@ -69,6 +69,7 @@ static std::unique_ptr<CSSParserSelector> splitCompoundAtImplicitShadowCrossingCombinator( std::unique_ptr<CSSParserSelector> compoundSelector); + void recordUsageAndDeprecations(const CSSSelectorList&); Member<const CSSParserContext> m_context; Member<StyleSheetContents> m_styleSheet; // FIXME: Should be const
diff --git a/third_party/WebKit/Source/core/dom/DOMQuad.cpp b/third_party/WebKit/Source/core/dom/DOMQuad.cpp index 21952c0..e93bce22 100644 --- a/third_party/WebKit/Source/core/dom/DOMQuad.cpp +++ b/third_party/WebKit/Source/core/dom/DOMQuad.cpp
@@ -4,6 +4,7 @@ #include "core/dom/DOMQuad.h" +#include "bindings/core/v8/V8ObjectBuilder.h" #include "core/dom/DOMPoint.h" namespace blink { @@ -24,4 +25,13 @@ m_p3(DOMPoint::fromPoint(p3)), m_p4(DOMPoint::fromPoint(p4)) {} +ScriptValue DOMQuad::toJSONForBinding(ScriptState* scriptState) const { + V8ObjectBuilder result(scriptState); + result.add("p1", p1()); + result.add("p2", p2()); + result.add("p3", p3()); + result.add("p4", p4()); + return result.scriptValue(); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/DOMQuad.h b/third_party/WebKit/Source/core/dom/DOMQuad.h index 0b5aac6..d13c4c09 100644 --- a/third_party/WebKit/Source/core/dom/DOMQuad.h +++ b/third_party/WebKit/Source/core/dom/DOMQuad.h
@@ -5,6 +5,7 @@ #ifndef DOMQuad_h #define DOMQuad_h +#include "bindings/core/v8/ScriptValue.h" #include "bindings/core/v8/ScriptWrappable.h" #include "core/CoreExport.h" @@ -28,6 +29,8 @@ DOMPoint* p3() const { return m_p3; } DOMPoint* p4() const { return m_p4; } + ScriptValue toJSONForBinding(ScriptState*) const; + DEFINE_INLINE_TRACE() { visitor->trace(m_p1); visitor->trace(m_p2);
diff --git a/third_party/WebKit/Source/core/dom/DOMQuad.idl b/third_party/WebKit/Source/core/dom/DOMQuad.idl index 9063cb1..061e9da9 100644 --- a/third_party/WebKit/Source/core/dom/DOMQuad.idl +++ b/third_party/WebKit/Source/core/dom/DOMQuad.idl
@@ -20,5 +20,5 @@ [SameObject] readonly attribute DOMPoint p4; // TODO(hs1217.lee): [NewObject] DOMRect getBounds(); - // TODO(hs1217.lee): serializer = { attribute }; + serializer = { attribute }; };
diff --git a/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp b/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp index 7ab3191..52676347 100644 --- a/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp +++ b/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp
@@ -11,16 +11,6 @@ namespace blink { -namespace { - -bool isInRemoteFrame(const Document& document) { - DCHECK(document.frame()); - Frame* mainFrame = document.frame()->tree().top(); - return !mainFrame || mainFrame->isRemoteFrame(); -} - -} // anonymous namespace - ElementVisibilityObserver::ElementVisibilityObserver( Element* element, std::unique_ptr<VisibilityCallback> callback) @@ -29,35 +19,25 @@ ElementVisibilityObserver::~ElementVisibilityObserver() = default; void ElementVisibilityObserver::start() { + DCHECK(!m_intersectionObserver); + ExecutionContext* context = m_element->getExecutionContext(); DCHECK(context->isDocument()); Document& document = toDocument(*context); - // TODO(zqzhang): IntersectionObserver does not work for RemoteFrame. - // Remove this early return when it's fixed. See https://crbug.com/615156 - if (isInRemoteFrame(document)) { - m_element.release(); - return; - } - - DCHECK(!m_intersectionObserver); m_intersectionObserver = IntersectionObserver::create( Vector<Length>(), Vector<float>({std::numeric_limits<float>::min()}), &document, WTF::bind(&ElementVisibilityObserver::onVisibilityChanged, wrapWeakPersistent(this))); DCHECK(m_intersectionObserver); + m_intersectionObserver->setInitialState( IntersectionObserver::InitialState::kAuto); m_intersectionObserver->observe(m_element.release()); } void ElementVisibilityObserver::stop() { - // TODO(zqzhang): IntersectionObserver does not work for RemoteFrame, - // so |m_intersectionObserver| may be null at this point after start(). - // Replace this early return with DCHECK when this has been fixed. See - // https://crbug.com/615156 - if (!m_intersectionObserver) - return; + DCHECK(m_intersectionObserver); m_intersectionObserver->disconnect(); m_intersectionObserver = nullptr;
diff --git a/third_party/WebKit/Source/core/dom/ElementVisibilityObserverTest.cpp b/third_party/WebKit/Source/core/dom/ElementVisibilityObserverTest.cpp index 2645259..02f03b0 100644 --- a/third_party/WebKit/Source/core/dom/ElementVisibilityObserverTest.cpp +++ b/third_party/WebKit/Source/core/dom/ElementVisibilityObserverTest.cpp
@@ -6,8 +6,10 @@ #include "core/dom/DOMImplementation.h" #include "core/dom/Document.h" +#include "core/frame/RemoteFrame.h" #include "core/html/HTMLDivElement.h" #include "core/html/HTMLDocument.h" +#include "core/loader/EmptyClients.h" #include "core/testing/DummyPageHolder.h" #include "testing/gtest/include/gtest/gtest.h" @@ -15,14 +17,47 @@ namespace { +// Stub implementation of FrameLoaderClient for the purpose of testing. It will +// alow callers to set the parent/top frames by calling |setParent|. It is used +// in ElementVisibilityObserverTest in order to mock a RemoteFrame parent of a +// LocalFrame. +class StubFrameLoaderClient final : public EmptyFrameLoaderClient { + public: + Frame* parent() const override { return m_parent; } + Frame* top() const override { return m_parent; } + + void setParent(Frame* frame) { m_parent = frame; } + + DEFINE_INLINE_VIRTUAL_TRACE() { + visitor->trace(m_parent); + EmptyFrameLoaderClient::trace(visitor); + } + + private: + WeakMember<Frame> m_parent = nullptr; +}; + class ElementVisibilityObserverTest : public ::testing::Test { protected: - void SetUp() override { m_dummyPageHolder = DummyPageHolder::create(); } + void SetUp() override { + m_frameLoaderClient = new StubFrameLoaderClient(); + m_dummyPageHolder = DummyPageHolder::create( + IntSize(), nullptr, m_frameLoaderClient, nullptr, nullptr); + } + + void TearDown() override { + m_dummyPageHolder->frame().detach(FrameDetachType::Remove); + } Document& document() { return m_dummyPageHolder->document(); } + FrameHost& frameHost() { return m_dummyPageHolder->page().frameHost(); } + StubFrameLoaderClient* frameLoaderClient() const { + return m_frameLoaderClient; + } private: std::unique_ptr<DummyPageHolder> m_dummyPageHolder; + Persistent<StubFrameLoaderClient> m_frameLoaderClient; }; TEST_F(ElementVisibilityObserverTest, ObserveElementWithoutDocumentFrame) { @@ -35,6 +70,20 @@ // It should not crash. } +TEST_F(ElementVisibilityObserverTest, ObserveElementInRemoteFrame) { + Persistent<RemoteFrame> remoteFrame = + RemoteFrame::create(new EmptyRemoteFrameClient(), &frameHost(), nullptr); + frameLoaderClient()->setParent(remoteFrame); + + Persistent<HTMLElement> element = HTMLDivElement::create(document()); + ElementVisibilityObserver* observer = + new ElementVisibilityObserver(element, WTF::bind([](bool) {})); + observer->start(); + observer->deliverObservationsForTesting(); + observer->stop(); + // It should not crash. +} + } // anonymous namespace } // blink namespace
diff --git a/third_party/WebKit/Source/core/frame/Deprecation.cpp b/third_party/WebKit/Source/core/frame/Deprecation.cpp index f826cc0..f0bbf76 100644 --- a/third_party/WebKit/Source/core/frame/Deprecation.cpp +++ b/third_party/WebKit/Source/core/frame/Deprecation.cpp
@@ -394,6 +394,24 @@ "redirected response. This will result in an error in %s.", milestoneString(M59)); + case UseCounter::CSSSelectorInternalMediaControlsCastButton: + return willBeRemoved("-internal-media-controls-cast-button selector", M59, + "5734009183141888"); + + case UseCounter::CSSSelectorInternalMediaControlsOverlayCastButton: + return willBeRemoved( + "-internal-media-controls-overlay-cast-button selector", M59, + "5714245488476160"); + + case UseCounter::CSSSelectorInternalMediaControlsTextTrackList: + case UseCounter::CSSSelectorInternalMediaControlsTextTrackListItem: + case UseCounter::CSSSelectorInternalMediaControlsTextTrackListItemInput: + case UseCounter::CSSSelectorInternalMediaControlsTextTrackListKindCaptions: + case UseCounter::CSSSelectorInternalMediaControlsTextTrackListKindSubtitles: + return willBeRemoved( + "-internal-media-controls-text-track-list* selectors", M59, + "5661431349379072"); + // Features that aren't deprecated don't have a deprecation message. default: return String();
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h index 2fa053d0..66c1d0b4 100644 --- a/third_party/WebKit/Source/core/frame/UseCounter.h +++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1439,6 +1439,11 @@ DocumentCompleteURLHTTPContainingLessThan = 1769, DocumentCompleteURLHTTPContainingNewlineAndLessThan = 1770, DocumentCompleteURLNonHTTPContainingNewlineOrLessThan = 1771, + CSSSelectorInternalMediaControlsTextTrackList = 1772, + CSSSelectorInternalMediaControlsTextTrackListItem = 1773, + CSSSelectorInternalMediaControlsTextTrackListItemInput = 1774, + CSSSelectorInternalMediaControlsTextTrackListKindCaptions = 1775, + CSSSelectorInternalMediaControlsTextTrackListKindSubtitles = 1776, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/loader/EmptyClients.cpp b/third_party/WebKit/Source/core/loader/EmptyClients.cpp index c9d0e5e..b3bd635 100644 --- a/third_party/WebKit/Source/core/loader/EmptyClients.cpp +++ b/third_party/WebKit/Source/core/loader/EmptyClients.cpp
@@ -196,4 +196,6 @@ return nullptr; } +EmptyRemoteFrameClient::EmptyRemoteFrameClient() = default; + } // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/EmptyClients.h b/third_party/WebKit/Source/core/loader/EmptyClients.h index 416e2c1..0c02290 100644 --- a/third_party/WebKit/Source/core/loader/EmptyClients.h +++ b/third_party/WebKit/Source/core/loader/EmptyClients.h
@@ -30,6 +30,7 @@ #define EmptyClients_h #include "core/CoreExport.h" +#include "core/frame/RemoteFrameClient.h" #include "core/loader/FrameLoaderClient.h" #include "core/page/ChromeClient.h" #include "core/page/ContextMenuClient.h" @@ -423,6 +424,42 @@ void clearContextMenu() override {} }; +class CORE_EXPORT EmptyRemoteFrameClient + : NON_EXPORTED_BASE(public RemoteFrameClient) { + WTF_MAKE_NONCOPYABLE(EmptyRemoteFrameClient); + + public: + EmptyRemoteFrameClient(); + + // RemoteFrameClient implementation. + void navigate(const ResourceRequest&, + bool shouldReplaceCurrentEntry) override {} + void reload(FrameLoadType, ClientRedirectPolicy) override {} + unsigned backForwardLength() override { return 0; } + void forwardPostMessage(MessageEvent*, + PassRefPtr<SecurityOrigin> target, + LocalFrame* sourceFrame) const override {} + void forwardInputEvent(Event*) override {} + void frameRectsChanged(const IntRect& frameRect) override {} + void updateRemoteViewportIntersection( + const IntRect& viewportIntersection) override {} + void advanceFocus(WebFocusType, LocalFrame* source) override {} + void visibilityChanged(bool visible) override {} + void setHasReceivedUserGesture() override {} + + // FrameClient implementation. + bool inShadowTree() const override { return false; } + void willBeDetached() override {} + void detached(FrameDetachType) override {} + Frame* opener() const override { return nullptr; } + void setOpener(Frame*) override {} + Frame* parent() const override { return nullptr; } + Frame* top() const override { return nullptr; } + Frame* nextSibling() const override { return nullptr; } + Frame* firstChild() const override { return nullptr; } + void frameFocused() const override {} +}; + CORE_EXPORT void fillWithEmptyClients(Page::PageClients&); } // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp index c963f48..eb5a60a 100644 --- a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp +++ b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
@@ -1134,13 +1134,8 @@ context, FloatPoint(localOrigin) + FloatPoint(0, lineThroughOffset), width.toFloat(), decoration, textDecorationThickness, doubleOffset, 0, antialiasDecoration); - if (skipIntercepts) { - textPainter.clipDecorationsStripe( - -baseline + decorationPainter.decorationBounds().y() - - FloatPoint(localOrigin).y(), - decorationPainter.decorationBounds().height(), - textDecorationThickness); - } + // No skip: ink for line-through, + // compare https://github.com/w3c/csswg-drafts/issues/711 decorationPainter.paint(); } }
diff --git a/third_party/WebKit/Source/platform/heap/WrapperVisitor.h b/third_party/WebKit/Source/platform/heap/WrapperVisitor.h index 13de57b..3e0c49b 100644 --- a/third_party/WebKit/Source/platform/heap/WrapperVisitor.h +++ b/third_party/WebKit/Source/platform/heap/WrapperVisitor.h
@@ -91,7 +91,7 @@ public: template <typename T> - static NEVER_INLINE void missedWriteBarrier() { + static NOINLINE void missedWriteBarrier() { NOTREACHED(); } @@ -167,7 +167,7 @@ } template <typename T> - void markAndPushToMarkingDeque(const T* traceable) const { + ALWAYS_INLINE void markAndPushToMarkingDeque(const T* traceable) const { if (pushToMarkingDeque(TraceTrait<T>::traceMarkedWrapper, TraceTrait<T>::heapObjectHeader, WrapperVisitor::missedWriteBarrier<T>, traceable)) {
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 744dac8..26a7b49 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -39181,6 +39181,18 @@ </summary> </histogram> +<histogram name="NewTabPage.ContentSuggestions.OpenedCategoryIndex" + units="index"> + <owner>vitaliii@chromium.org</owner> + <summary> + Android: The index of a category on the NTP, whose suggestion card is + clicked through to the host website of the content. This tracked index can + be different from the position observed by the user, e.g. for the user a + category may be at the top of the NTP, but with index 1, because there is an + empty category above (with index 0). + </summary> +</histogram> + <histogram name="NewTabPage.ContentSuggestions.OpenedScore" units="score"> <obsolete> Replaced by NewTabPage.ContentSuggestions.OpenedScoreNormalized. @@ -44714,6 +44726,20 @@ </summary> </histogram> +<histogram name="PasswordManager.PasswordReuse.PasswordFieldDetected" + enum="PasswordReusePasswordFieldDetected"> + <owner>dvadym@chromium.org</owner> + <owner>nparker@chromium.org</owner> + <summary> + A password reuse is an event when the user typed a string that is equal to a + saved password on another domain, and this saved password is called reused + password. + + This metric reports whether a password field had been detected on a page + when a password reuse happened. + </summary> +</histogram> + <histogram name="PasswordManager.PasswordReuse.PasswordLength" units="characters"> <owner>dvadym@chromium.org</owner> @@ -89320,6 +89346,14 @@ label="DocumentCompleteURLHTTPContainingNewlineAndLessThan"/> <int value="1771" label="DocumentCompleteURLNonHTTPContainingNewlineOrLessThan"/> + <int value="1772" label="CSSSelectorInternalMediaControlsTextTrackList"/> + <int value="1773" label="CSSSelectorInternalMediaControlsTextTrackListItem"/> + <int value="1774" + label="CSSSelectorInternalMediaControlsTextTrackListItemInput"/> + <int value="1775" + label="CSSSelectorInternalMediaControlsTextTrackListKindCaptions"/> + <int value="1776" + label="CSSSelectorInternalMediaControlsTextTrackListKindSubtitles"/> </enum> <enum name="FetchRequestMode" type="int"> @@ -94896,6 +94930,7 @@ <int value="-1203742042" label="enable-gesture-selection"/> <int value="-1201183153" label="enable-centered-app-list"/> <int value="-1184904651" label="enable-npapi"/> + <int value="-1184480269" label="LsdPermissionPrompt:enabled"/> <int value="-1177802205" label="enable-hosted-app-quit-notification"/> <int value="-1176748003" label="FramebustingNeedsSameOriginOrUserGesture:disabled"/> @@ -95605,6 +95640,7 @@ <int value="2037756154" label="enable-impl-side-painting"/> <int value="2059322877" label="new-avatar-menu"/> <int value="2063091429" label="OfflinePagesSharing:enabled"/> + <int value="2067634730" label="LsdPermissionPrompt:disabled"/> <int value="2071340353" label="progress-bar-completion"/> <int value="2071461362" label="disable-credit-card-scan"/> <int value="2076903744" label="progress-bar-animation"/> @@ -100115,6 +100151,11 @@ <int value="11" label="Clicked 'Google Smart Lock'"/> </enum> +<enum name="PasswordReusePasswordFieldDetected" type="int"> + <int value="0" label="No password field"/> + <int value="1" label="Has password field"/> +</enum> + <enum name="PasswordSubmissionEvent" type="int"> <int value="0" label="Password submission succeeded"/> <int value="1" label="Password submission failed"/> @@ -110569,6 +110610,7 @@ <affected-histogram name="NewTabPage.ContentSuggestions.OpenDisposition"/> <affected-histogram name="NewTabPage.ContentSuggestions.Opened"/> <affected-histogram name="NewTabPage.ContentSuggestions.OpenedAge"/> + <affected-histogram name="NewTabPage.ContentSuggestions.OpenedCategoryIndex"/> <affected-histogram name="NewTabPage.ContentSuggestions.OpenedScore"/> <affected-histogram name="NewTabPage.ContentSuggestions.OpenedScoreNormalized"/>
diff --git a/tools/perf/benchmarks/benchmark_smoke_unittest.py b/tools/perf/benchmarks/benchmark_smoke_unittest.py index f236444..8808a991c 100644 --- a/tools/perf/benchmarks/benchmark_smoke_unittest.py +++ b/tools/perf/benchmarks/benchmark_smoke_unittest.py
@@ -34,7 +34,13 @@ from benchmarks import v8_browsing -def SmokeTestGenerator(benchmark): +def SmokeTestGenerator(benchmark, num_pages=1): + """Generates a benchmark that includes first N pages from pageset. + + Args: + benchmark: benchmark object to make smoke test. + num_pages: use the first N pages to run smoke test. + """ # NOTE TO SHERIFFS: DO NOT DISABLE THIS TEST. # # This smoke test dynamically tests all benchmarks. So disabling it for one @@ -53,9 +59,10 @@ def CreateStorySet(self, options): # pylint: disable=super-on-old-class story_set = super(SinglePageBenchmark, self).CreateStorySet(options) + # Only smoke test the first story since smoke testing everything takes # too long. - for s in story_set.stories[1:]: + for s in story_set.stories[num_pages:]: story_set.RemoveStory(s) return story_set @@ -135,7 +142,11 @@ class BenchmarkSmokeTest(unittest.TestCase): pass - method = SmokeTestGenerator(benchmark) + # tab_switching needs more than one page to test correctly. + if 'tab_switching' in benchmark.Name(): + method = SmokeTestGenerator(benchmark, num_pages=2) + else: + method = SmokeTestGenerator(benchmark) # Make sure any decorators are propagated from the original declaration. # (access to protected members) pylint: disable=protected-access
diff --git a/tools/perf/measurements/tab_switching.py b/tools/perf/measurements/tab_switching.py index 7f71359a..dd64dfa 100644 --- a/tools/perf/measurements/tab_switching.py +++ b/tools/perf/measurements/tab_switching.py
@@ -10,6 +10,7 @@ Power usage is also measured. """ +import json import time from telemetry.core import util @@ -64,13 +65,17 @@ def ValidateAndMeasurePage(self, page, tab, results): """On the last tab, cycle through each tab that was opened and then record a single histogram for the tab switching metric.""" - if len(tab.browser.tabs) != len(page.story_set.stories): + browser = tab.browser + if len(browser.tabs) != len(page.story_set.stories): return + if browser.tabs < 2: + raise Exception('Should have at least two tabs for tab switching') + # Measure power usage of tabs after quiescence. util.WaitFor(tab.HasReachedQuiescence, 60) - if tab.browser.platform.CanMonitorPower(): + if browser.platform.CanMonitorPower(): self._power_metric.Start(page, tab) time.sleep(TabSwitching.SAMPLE_TIME) self._power_metric.Stop(page, tab) @@ -83,27 +88,32 @@ histogram_type, histogram_name, tab) prev_histogram = first_histogram - for t in tab.browser.tabs: - t.Activate() - + for tab_to_switch in browser.tabs: + tab_to_switch.Activate() def _IsDone(): + # pylint: disable=W0640 cur_histogram = histogram_util.GetHistogram( - histogram_type, histogram_name, tab) + histogram_type, histogram_name, tab_to_switch) diff_histogram = histogram_util.SubtractHistogram( cur_histogram, prev_histogram) - return diff_histogram + # TODO(deanliao): Add SubtractHistogramRawValue to process histogram + # object instead of JSON string. + diff_histogram_count = json.loads(diff_histogram).get('count', 0) + return diff_histogram_count > 0 util.WaitFor(_IsDone, 30) + + # We need to get histogram again instead of getting cur_histogram as + # variables modified inside inner function cannot be retrieved. However, + # inner function can see external scope's variables. prev_histogram = histogram_util.GetHistogram( - histogram_type, histogram_name, tab) + histogram_type, histogram_name, tab_to_switch) - last_histogram = histogram_util.GetHistogram( - histogram_type, histogram_name, tab) - diff_histogram = histogram_util.SubtractHistogram(last_histogram, - first_histogram) - + last_histogram = prev_histogram + total_diff_histogram = histogram_util.SubtractHistogram(last_histogram, + first_histogram) results.AddSummaryValue( histogram.HistogramValue(None, display_name, 'ms', - raw_value_json=diff_histogram, + raw_value_json=total_diff_histogram, important=False)) keychain_metric.KeychainMetric().AddResults(tab, results)
diff --git a/tools/perf/measurements/tab_switching_unittest.py b/tools/perf/measurements/tab_switching_unittest.py new file mode 100644 index 0000000..79efbb1 --- /dev/null +++ b/tools/perf/measurements/tab_switching_unittest.py
@@ -0,0 +1,97 @@ +# 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. + +import contextlib +from telemetry.internal.results import page_test_results +from telemetry.testing import page_test_test_case + +from measurements import tab_switching + +import mock + + +class BrowserForTest(object): + def __init__(self): + self.tabs = [] + self.platform = mock.MagicMock() + self.platform.CanMonitorPower = mock.Mock(return_value=False) + + def AddTab(self, tab): + tab.browser = self + self.tabs.append(tab) + + +class StorySetForTest(object): + def __init__(self): + self.stories = [] + + def AddStory(self, story): + story.story_set = self + self.stories.append(story) + + +class TabSwitchingUnittest(page_test_test_case.PageTestTestCase): + @staticmethod + def MakeStoryForTest(): + story = mock.MagicMock() + story.story_set = None + return story + + @staticmethod + def MakeTabForTest(): + tab = mock.MagicMock() + tab.browser = None + tab.HasReachedQuiescence = mock.Mock(return_value=True) + return tab + + def testIsDone(self): + """Tests ValidateAndMeasurePage, specifically _IsDone check.""" + measure = tab_switching.TabSwitching() + + # For sanity check: #tabs == #stories + story_set = StorySetForTest() + story_set.AddStory(self.MakeStoryForTest()) + story_set.AddStory(self.MakeStoryForTest()) + + # Set up a browser with two tabs open + browser = BrowserForTest() + tab_0 = self.MakeTabForTest() + browser.AddTab(tab_0) + tab_1 = self.MakeTabForTest() + browser.AddTab(tab_1) + + # Mock histogram result to test _IsDone really works. + expected_histogram = [ + # To get first_histogram for last tab (tab_1). + '{"count": 0, "buckets": []}', + # First _IsDone check for tab_0. Retry. + '{"count": 0, "buckets": []}', + # Second _IsDone check for tab_0. Retry. + '{"count": 0, "buckets": []}', + # Third _IsDone check for tab_0. Pass. + '{"count": 1, "buckets": [{"low": 1, "high": 2, "count": 1}]}', + # To get prev_histogram. End of tab_0 loop. + '{"count": 1, "buckets": [{"low": 1, "high": 2, "count": 1}]}', + # First _IsDone check for tab_1. Retry. + '{"count": 1, "buckets": [{"low": 1, "high": 2, "count": 1}]}', + # Second _IsDone check for tab_1. Pass. + '{"count": 2, "buckets": [{"low": 1, "high": 2, "count": 1},' + '{"low": 2, "high": 3, "count": 1}]}', + # To get prev_histogram. End of tab_1 loop. + '{"count": 2, "buckets": [{"low": 1, "high": 2, "count": 1},' + '{"low": 2, "high": 3, "count": 1}]}', + ] + mock_get_histogram = mock.MagicMock(side_effect=expected_histogram) + + with contextlib.nested( + mock.patch('telemetry.value.histogram_util.GetHistogram', + mock_get_histogram), + mock.patch('metrics.keychain_metric.KeychainMetric')): + measure.ValidateAndMeasurePage(story_set.stories[0], browser.tabs[-1], + page_test_results.PageTestResults()) + self.assertEqual(len(expected_histogram), + len(mock_get_histogram.mock_calls)) + expected_calls = [mock.call(mock.ANY, mock.ANY, t) for t in + [tab_1] + [tab_0] * 4 + [tab_1] * 3] + self.assertEqual(expected_calls, mock_get_histogram.mock_calls)