diff --git a/DEPS b/DEPS index d9100da..24171ad6 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': 'c6912f712f3423b2a09a6e96c999b65a1fd86062', + 'skia_revision': '1bfece8556cc03b6aa904b2445d2332136bce037', # 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': 'dccdc02878d7136d2d4b6ca80bd025294cf1c7f8', + 'v8_revision': '8aacdbbccf43caf1ed51109609797545d6fad4d3', # 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. @@ -52,7 +52,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': 'cae72d6ab32cca4c9ce4af6a10bc6f4b89138ed4', + 'angle_revision': 'b7d5e303339bb90447e12f05ee50e6269f97493e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling build tools # 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': '5f34d479d06ebab9079c2d0704dee872cc45dd86', + 'pdfium_revision': '5171a27eaa7489939310bd2864864867cc78ce21', # 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. @@ -96,7 +96,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': 'c294424b587eb0a331556f215cacb5b22e066438', + 'catapult_revision': '817b8684be607f1ac5aad1e6ef5713188a8cfb2c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other.
diff --git a/ash/devtools/ash_devtools_dom_agent.cc b/ash/devtools/ash_devtools_dom_agent.cc index ce290b5..72fc8e3 100644 --- a/ash/devtools/ash_devtools_dom_agent.cc +++ b/ash/devtools/ash_devtools_dom_agent.cc
@@ -359,8 +359,7 @@ constexpr int kBorderThickness = 1; views::View* root_view = widget_for_highlighting_->GetRootView(); root_view->SetBorder(views::CreateSolidBorder(kBorderThickness, border)); - root_view->set_background( - views::Background::CreateSolidBackground(background)); + root_view->SetBackground(views::CreateSolidBackground(background)); display::Display display = display::Screen::GetScreen()->GetDisplayNearestWindow( window_and_bounds.first);
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc index 7107e989..59b5d15 100644 --- a/ash/login/ui/lock_contents_view.cc +++ b/ash/login/ui/lock_contents_view.cc
@@ -21,7 +21,7 @@ views::Label* label = new views::Label(); label->SetBorder(views::CreateEmptyBorder(2, 3, 4, 5)); - label->set_background(views::Background::CreateThemedSolidBackground( + label->SetBackground(views::CreateThemedSolidBackground( label, ui::NativeTheme::kColorId_BubbleBackground)); label->SetText(base::ASCIIToUTF16("User")); AddChildView(label);
diff --git a/ash/shelf/overflow_bubble_view.cc b/ash/shelf/overflow_bubble_view.cc index de12a49..dcada6d 100644 --- a/ash/shelf/overflow_bubble_view.cc +++ b/ash/shelf/overflow_bubble_view.cc
@@ -54,7 +54,7 @@ SetAnchorView(anchor); set_arrow(views::BubbleBorder::NONE); - set_background(nullptr); + SetBackground(nullptr); if (shelf_->IsHorizontalAlignment()) set_margins(gfx::Insets(0, kEndPadding)); else
diff --git a/ash/system/audio/volume_view.cc b/ash/system/audio/volume_view.cc index ea4a5d4..9d42635 100644 --- a/ash/system/audio/volume_view.cc +++ b/ash/system/audio/volume_view.cc
@@ -133,7 +133,7 @@ l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_VOLUME)); tri_view_->AddView(TriView::Container::CENTER, slider_); - set_background(views::Background::CreateThemedSolidBackground( + SetBackground(views::CreateThemedSolidBackground( this, ui::NativeTheme::kColorId_BubbleBackground)); Update();
diff --git a/ash/system/display_scale/scale_view.cc b/ash/system/display_scale/scale_view.cc index 8a80128..0543bb7 100644 --- a/ash/system/display_scale/scale_view.cc +++ b/ash/system/display_scale/scale_view.cc
@@ -50,7 +50,7 @@ l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SCALE_SLIDER)); tri_view_->AddView(TriView::Container::CENTER, slider_); - set_background(views::Background::CreateThemedSolidBackground( + SetBackground(views::CreateThemedSolidBackground( this, ui::NativeTheme::kColorId_BubbleBackground)); if (!is_default_view_) {
diff --git a/ash/system/network/network_list.cc b/ash/system/network/network_list.cc index 21ee550..3f4cff25 100644 --- a/ash/system/network/network_list.cc +++ b/ash/system/network/network_list.cc
@@ -739,23 +739,21 @@ // Set up layout and apply sticky row property. TriView* connection_warning = TrayPopupUtils::CreateDefaultRowView(); TrayPopupUtils::ConfigureAsStickyHeader(connection_warning); - connection_warning->set_background( - views::Background::CreateSolidBackground(kHeaderBackgroundColor)); + connection_warning->SetBackground( + views::CreateSolidBackground(kHeaderBackgroundColor)); // Set 'info' icon on left side. views::ImageView* image_view = TrayPopupUtils::CreateMainImageView(); image_view->SetImage( gfx::CreateVectorIcon(kSystemMenuInfoIcon, kMenuIconColor)); - image_view->set_background( - views::Background::CreateSolidBackground(SK_ColorTRANSPARENT)); + image_view->SetBackground(views::CreateSolidBackground(SK_ColorTRANSPARENT)); connection_warning->AddView(TriView::Container::START, image_view); // Set message label in middle of row. views::Label* label = TrayPopupUtils::CreateDefaultLabel(); label->SetText( l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_MONITORED_WARNING)); - label->set_background( - views::Background::CreateSolidBackground(SK_ColorTRANSPARENT)); + label->SetBackground(views::CreateSolidBackground(SK_ColorTRANSPARENT)); TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL); style.SetupLabel(label); connection_warning->AddView(TriView::Container::CENTER, label);
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc index d360779..ec269d2 100644 --- a/ash/system/tray/tray_background_view.cc +++ b/ash/system/tray/tray_background_view.cc
@@ -149,7 +149,8 @@ SetLayoutManager(new views::FillLayout); - tray_container_->set_background(background_); + tray_container_->SetBackground( + std::unique_ptr<views::Background>(background_)); AddChildView(tray_container_); tray_event_filter_.reset(new TrayEventFilter);
diff --git a/ash/system/tray/tray_details_view.cc b/ash/system/tray/tray_details_view.cc index 4aaf772..ea8d244f 100644 --- a/ash/system/tray/tray_details_view.cc +++ b/ash/system/tray/tray_details_view.cc
@@ -279,7 +279,7 @@ tri_view_(nullptr), back_button_(nullptr) { SetLayoutManager(box_layout_); - set_background(views::Background::CreateThemedSolidBackground( + SetBackground(views::CreateThemedSolidBackground( this, ui::NativeTheme::kColorId_BubbleBackground)); } @@ -337,7 +337,7 @@ // Make the |scroller_| have a layer to clip |scroll_content_|'s children. // TODO(varkha): Make the sticky rows work with EnableViewPortLayer(). scroller_->SetPaintToLayer(); - scroller_->set_background(views::Background::CreateThemedSolidBackground( + scroller_->SetBackground(views::CreateThemedSolidBackground( scroller_, ui::NativeTheme::kColorId_BubbleBackground)); scroller_->layer()->SetMasksToBounds(true);
diff --git a/ash/system/tray/tray_popup_header_button.cc b/ash/system/tray/tray_popup_header_button.cc index b6e37e5..0a69db8b 100644 --- a/ash/system/tray/tray_popup_header_button.cc +++ b/ash/system/tray/tray_popup_header_button.cc
@@ -62,10 +62,9 @@ void TrayPopupHeaderButton::StateChanged(ButtonState old_state) { if (state() == STATE_HOVERED || state() == STATE_PRESSED) { - set_background(views::Background::CreateSolidBackground( - kTrayPopupHoverBackgroundColor)); + SetBackground(views::CreateSolidBackground(kTrayPopupHoverBackgroundColor)); } else { - set_background(nullptr); + SetBackground(nullptr); } SchedulePaint(); }
diff --git a/ash/system/tray/tray_popup_utils.cc b/ash/system/tray/tray_popup_utils.cc index 820647d..0fdbd9c 100644 --- a/ash/system/tray/tray_popup_utils.cc +++ b/ash/system/tray/tray_popup_utils.cc
@@ -210,7 +210,7 @@ // Frequently the label will paint to a layer that's non-opaque, so subpixel // rendering won't work unless we explicitly set a background. See // crbug.com/686363 - label->set_background(views::Background::CreateThemedSolidBackground( + label->SetBackground(views::CreateThemedSolidBackground( label, ui::NativeTheme::kColorId_BubbleBackground)); return label; } @@ -269,7 +269,7 @@ void TrayPopupUtils::ConfigureAsStickyHeader(views::View* view) { view->set_id(VIEW_ID_STICKY_HEADER); - view->set_background(views::Background::CreateThemedSolidBackground( + view->SetBackground(views::CreateThemedSolidBackground( view, ui::NativeTheme::kColorId_BubbleBackground)); view->SetBorder( views::CreateEmptyBorder(gfx::Insets(kMenuSeparatorVerticalPadding, 0)));
diff --git a/ash/system/user/user_card_view.cc b/ash/system/user/user_card_view.cc index 0cd6512..aa69592 100644 --- a/ash/system/user/user_card_view.cc +++ b/ash/system/user/user_card_view.cc
@@ -290,7 +290,7 @@ layout->set_cross_axis_alignment( views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); - set_background(views::Background::CreateThemedSolidBackground( + SetBackground(views::CreateThemedSolidBackground( this, ui::NativeTheme::kColorId_BubbleBackground)); Shell::Get()->media_controller()->AddObserver(this);
diff --git a/ash/system/user/user_view.cc b/ash/system/user/user_view.cc index c7294b8..9b79b8ca 100644 --- a/ash/system/user/user_view.cc +++ b/ash/system/user/user_view.cc
@@ -80,7 +80,7 @@ kTrayPopupLabelHorizontalPadding + icon_padding); layout->set_minimum_cross_axis_size(kTrayPopupItemMinHeight); view->SetLayoutManager(layout); - view->set_background(views::Background::CreateThemedSolidBackground( + view->SetBackground(views::CreateThemedSolidBackground( view, ui::NativeTheme::kColorId_BubbleBackground)); int message_id = 0;
diff --git a/ash/test/child_modal_window.cc b/ash/test/child_modal_window.cc index 297ad9ab..426d8710 100644 --- a/ash/test/child_modal_window.cc +++ b/ash/test/child_modal_window.cc
@@ -114,8 +114,8 @@ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.context = context; widget_->Init(params); - widget_->GetRootView()->set_background( - views::Background::CreateSolidBackground(kModalParentColor)); + widget_->GetRootView()->SetBackground( + views::CreateSolidBackground(kModalParentColor)); widget_->GetNativeView()->SetName("ModalParent"); AddChildView(button_); AddChildView(textfield_);
diff --git a/ash/wm/overview/window_grid.cc b/ash/wm/overview/window_grid.cc index e1448593..3b27667 100644 --- a/ash/wm/overview/window_grid.cc +++ b/ash/wm/overview/window_grid.cc
@@ -242,7 +242,7 @@ } else { views::View* content_view = new RoundedRectView(border_radius, SK_ColorTRANSPARENT); - content_view->set_background(new BackgroundWith1PxBorder( + content_view->SetBackground(base::MakeUnique<BackgroundWith1PxBorder>( background_color, border_color, border_thickness, border_radius)); widget->SetContentsView(content_view); }
diff --git a/ash/wm/panels/panel_layout_manager.cc b/ash/wm/panels/panel_layout_manager.cc index b57ab56..d19b82b8 100644 --- a/ash/wm/panels/panel_layout_manager.cc +++ b/ash/wm/panels/panel_layout_manager.cc
@@ -229,7 +229,8 @@ DCHECK_EQ(widget_window->GetRootWindow(), parent->GetRootWindow()); views::View* content_view = new views::View; background_ = new CalloutWidgetBackground; - content_view->set_background(background_); + content_view->SetBackground( + std::unique_ptr<views::Background>(background_)); SetContentsView(content_view); widget_window->layer()->SetOpacity(0); }
diff --git a/ash/wm/window_cycle_list.cc b/ash/wm/window_cycle_list.cc index a2c7c38..5047f2c 100644 --- a/ash/wm/window_cycle_list.cc +++ b/ash/wm/window_cycle_list.cc
@@ -89,9 +89,8 @@ AddChildView(window_title_); // Preview padding is black at 50% opacity. - preview_background_->set_background( - views::Background::CreateSolidBackground( - SkColorSetA(SK_ColorBLACK, 0xFF / 2))); + preview_background_->SetBackground( + views::CreateSolidBackground(SkColorSetA(SK_ColorBLACK, 0xFF / 2))); AddChildView(preview_background_); AddChildView(mirror_view_); @@ -247,7 +246,7 @@ // The background needs to be painted to fill the layer, not the View, // because the layer animates bounds changes but the View's bounds change // immediately. - highlight_view_->set_background(new LayerFillBackgroundPainter( + highlight_view_->SetBackground(base::MakeUnique<LayerFillBackgroundPainter>( views::Painter::CreateRoundRectWith1PxBorderPainter( SkColorSetA(SK_ColorWHITE, 0x4D), SkColorSetA(SK_ColorWHITE, 0x33), kBackgroundCornerRadius)));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java index 2e371ea7..39f330c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.contextualsearch; +import android.annotation.SuppressLint; import android.text.TextUtils; import org.chromium.base.annotations.CalledByNative; @@ -17,7 +18,10 @@ * or changed. */ public abstract class ContextualSearchContext { - static final int INVALID_SELECTION_OFFSET = -1; + static final int INVALID_OFFSET = -1; + + // Non-visible word-break marker. + private static final int SOFT_HYPHEN_CHAR = '\u00AD'; // Pointer to the native instance of this class. private long mNativePointer; @@ -30,8 +34,11 @@ private String mSurroundingText; // The start and end offsets of the selection within the text content. - private int mSelectionStartOffset = INVALID_SELECTION_OFFSET; - private int mSelectionEndOffset = INVALID_SELECTION_OFFSET; + private int mSelectionStartOffset = INVALID_OFFSET; + private int mSelectionEndOffset = INVALID_OFFSET; + + // The offset of an initial Tap gesture within the text content. + private int mTapOffset = INVALID_OFFSET; // The initial word selected by a Tap, or null. private String mInitialSelectedWord; @@ -39,6 +46,13 @@ // The original encoding of the base page. private String mEncoding; + // The tapped word, as analyzed internally before selection takes place, or {@code null} if no + // analysis has been done yet. + private String mTappedWord; + + // The offset of the tap within the tapped word or {@code INVALID_OFFSET} if not yet analyzed. + private int mTappedWordOffset = INVALID_OFFSET; + /** * Constructs a context that tracks the selection and some amount of page content. */ @@ -85,6 +99,9 @@ mSurroundingText = surroundingText; mSelectionStartOffset = startOffset; mSelectionEndOffset = endOffset; + if (startOffset == endOffset && !hasAnalyzedTap()) { + analyzeTap(startOffset); + } // Notify of an initial selection if it's not empty. if (endOffset > startOffset) onSelectionChanged(); } @@ -99,7 +116,7 @@ /** * @return The offset into the surrounding text of the start of the selection, or - * {@link #INVALID_SELECTION_OFFSET} if not yet established. + * {@link #INVALID_OFFSET} if not yet established. */ int getSelectionStartOffset() { return mSelectionStartOffset; @@ -107,7 +124,7 @@ /** * @return The offset into the surrounding text of the end of the selection, or - * {@link #INVALID_SELECTION_OFFSET} if not yet established. + * {@link #INVALID_OFFSET} if not yet established. */ int getSelectionEndOffset() { return mSelectionEndOffset; @@ -143,9 +160,7 @@ * @return Whether this context can Resolve the Search Term. */ boolean canResolve() { - return mHasSetResolveProperties && mSelectionStartOffset != INVALID_SELECTION_OFFSET - && mSelectionEndOffset != INVALID_SELECTION_OFFSET - && mSelectionEndOffset > mSelectionStartOffset; + return mHasSetResolveProperties && hasValidSelection(); } /** @@ -180,7 +195,138 @@ */ abstract void onSelectionChanged(); - // TODO(donnd): Add a test for this class! + // ============================================================================================ + // Content Analysis. + // ============================================================================================ + + /** + * @return Whether this context has valid Surrounding text and initial Tap offset. + */ + private boolean hasValidTappedText() { + return !TextUtils.isEmpty(mSurroundingText) && mTapOffset >= 0 + && mTapOffset <= mSurroundingText.length(); + } + + /** + * @return Whether this context has a valid selection. + */ + private boolean hasValidSelection() { + if (!hasValidTappedText()) return false; + + return mSelectionStartOffset != INVALID_OFFSET && mSelectionEndOffset != INVALID_OFFSET + && mSelectionStartOffset < mSelectionEndOffset + && mSelectionEndOffset < mSurroundingText.length(); + } + + /** + * @return Whether a Tap gesture has occurred and been analyzed. + */ + private boolean hasAnalyzedTap() { + return mTapOffset >= 0; + } + + /** + * @return The tapped word, or {@code null} if the tapped word cannot be identified by the + * current limited parsing capability. + * @see #analyzeTap + */ + String getTappedWord() { + return mTappedWord; + } + + /** + * @return The offset of the tap within the tapped word, or {@code -1} if the tapped word cannot + * be identified by the current parsing capability. + * @see #analyzeTap + */ + int getTappedWordOffset() { + return mTappedWordOffset; + } + + /** + * Finds the tapped word by expanding from the initial Tap offset looking for word-breaks. + * This mimics the Blink word-segmentation invoked by SelectWordAroundCaret and similar + * selection logic, but is only appropriate for limited use. Does not work on ideographic + * languages and possibly many other cases. Should only be used only for ML signal evaluation. + * @param tapOffset The offset of the Tap within the surrounding text. + */ + private void analyzeTap(int tapOffset) { + mTapOffset = tapOffset; + mTappedWord = null; + mTappedWordOffset = INVALID_OFFSET; + + assert hasValidTappedText(); + + int wordStartOffset = findWordStartOffset(mSurroundingText, mTapOffset); + int wordEndOffset = findWordEndOffset(mSurroundingText, mTapOffset); + if (wordStartOffset == INVALID_OFFSET || wordEndOffset == INVALID_OFFSET) return; + + mTappedWord = mSurroundingText.substring(wordStartOffset, wordEndOffset); + mTappedWordOffset = mTapOffset - wordStartOffset; + } + + /** + * Finds the offset of the start of the word that includes the given initial offset. + * The character at the initial offset is not examined, but the one before it is, and scanning + * continues on to earlier characters until a non-word character is found. The offset just + * before the non-word character is returned. If the initial offset is a space immediately + * following a word then the start offset of that word is returned. + * @param text The text to scan. + * @param initial The initial offset to scan before. + * @return The start of the word that contains the given initial offset, within {@code text}. + */ + private int findWordStartOffset(String text, int initial) { + // Scan before, aborting if we hit any ideographic letter. + for (int offset = initial - 1; offset >= 0; offset--) { + if (isIdeographicAtIndex(text, offset)) return INVALID_OFFSET; + + if (isWordBreakAtIndex(text, offset)) { + // The start of the word is after this word break. + return offset + 1; + } + } + return INVALID_OFFSET; + } + + /** + * Finds the offset of the end of the word that includes the given initial offset. + * NOTE: this is the index of the character just past the last character of the word, + * so a 3 character word "who" has start index 0 and end index 3. + * The character at the initial offset is examined and each one after that too until a non-word + * character is encountered, and that offset will be returned. + * @param text The text to scan. + * @param initial The initial offset to scan from. + * @return The end of the word that contains the given initial offset, within {@code text}. + */ + private int findWordEndOffset(String text, int initial) { + // Scan after, aborting if we hit any CJKN letter. + for (int offset = initial; offset < text.length(); offset++) { + if (isIdeographicAtIndex(text, offset)) return INVALID_OFFSET; + + if (isWordBreakAtIndex(text, offset)) { + // The end of the word is the offset of this word break. + return offset; + } + } + return INVALID_OFFSET; + } + + /** + * @return Whether the character at the given index in the text is "Ideographic" (as in CJKV + * languages), which means there may not be reliable word breaks. + */ + @SuppressLint("NewApi") + private boolean isIdeographicAtIndex(String text, int index) { + return Character.isIdeographic(text.charAt(index)); + } + + /** + * @return Whether the character at the given index is a word-break. + */ + private boolean isWordBreakAtIndex(String text, int index) { + return !Character.isLetterOrDigit(text.charAt(index)) + && text.codePointAt(index) != SOFT_HYPHEN_CHAR; + } // ============================================================================================ // Native callback support.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java index 8f2fd64..768d0c6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
@@ -48,6 +48,10 @@ private static final String SCREEN_TOP_SUPPRESSION_DPS = "screen_top_suppression_dps"; private static final String ENABLE_BAR_OVERLAP_COLLECTION = "enable_bar_overlap_collection"; private static final String BAR_OVERLAP_SUPPRESSION_ENABLED = "enable_bar_overlap_suppression"; + private static final String WORD_EDGE_SUPPRESSION_ENABLED = "enable_word_edge_suppression"; + private static final String SHORT_WORD_SUPPRESSION_ENABLED = "enable_short_word_suppression"; + private static final String NOT_LONG_WORD_SUPPRESSION_ENABLED = + "enable_not_long_word_suppression"; private static final String MINIMUM_SELECTION_LENGTH = "minimum_selection_length"; @@ -67,6 +71,7 @@ "disable_page_content_notification"; // Cached values to avoid repeated and redundant JNI operations. + // TODO(donnd): consider creating a single Map to cache these static values. private static Boolean sEnabled; private static Boolean sDisableSearchTermResolution; private static Boolean sIsMandatoryPromoEnabled; @@ -78,6 +83,9 @@ private static Integer sScreenTopSuppressionDps; private static Boolean sIsBarOverlapCollectionEnabled; private static Boolean sIsBarOverlapSuppressionEnabled; + private static Boolean sIsWordEdgeSuppressionEnabled; + private static Boolean sIsShortWordSuppressionEnabled; + private static Boolean sIsNotLongWordSuppressionEnabled; private static Integer sMinimumSelectionLength; private static Boolean sIsOnlineDetectionDisabled; private static Boolean sIsAmpAsSeparateTabDisabled; @@ -248,6 +256,36 @@ } /** + * @return Whether triggering is suppressed by a tap that's near the edge of a word. + */ + static boolean isWordEdgeSuppressionEnabled() { + if (sIsWordEdgeSuppressionEnabled == null) { + sIsWordEdgeSuppressionEnabled = getBooleanParam(WORD_EDGE_SUPPRESSION_ENABLED); + } + return sIsWordEdgeSuppressionEnabled.booleanValue(); + } + + /** + * @return Whether triggering is suppressed by a tap that's in a short word. + */ + static boolean isShortWordSuppressionEnabled() { + if (sIsShortWordSuppressionEnabled == null) { + sIsShortWordSuppressionEnabled = getBooleanParam(SHORT_WORD_SUPPRESSION_ENABLED); + } + return sIsShortWordSuppressionEnabled.booleanValue(); + } + + /** + * @return Whether triggering is suppressed by a tap that's not in a long word. + */ + static boolean isNotLongWordSuppressionEnabled() { + if (sIsNotLongWordSuppressionEnabled == null) { + sIsNotLongWordSuppressionEnabled = getBooleanParam(NOT_LONG_WORD_SUPPRESSION_ENABLED); + } + return sIsNotLongWordSuppressionEnabled.booleanValue(); + } + + /** * @return The minimum valid selection length. */ static int getMinimumSelectionLength() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java index 3f43032a..7075f12 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -1475,13 +1475,14 @@ @Override public void decideSuppression() { mInternalStateController.notifyStartingWorkOn(InternalState.DECIDING_SUPPRESSION); + // Ranker will handle the suppression, but our legacy implementation uses // TapSuppressionHeuristics (run from the ContextualSearchSelectionConroller). // Usage includes tap-far-from-previous suppression. mTapSuppressionRankerLogger.setupLoggingForPage(getBasePageUrl()); // TODO(donnd): Move handleShouldSuppressTap out of the Selection Controller. - mSelectionController.handleShouldSuppressTap(mTapSuppressionRankerLogger); + mSelectionController.handleShouldSuppressTap(mContext, mTapSuppressionRankerLogger); } /** Starts showing the Tap UI by selecting a word around the current caret. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java index 9f296ce..edae544 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
@@ -205,7 +205,7 @@ if (baseContentView != null) { baseContentView.clearSelection(); } - resetAllStates(); + resetSelectionStates(); } /** @@ -351,10 +351,12 @@ * or #handleNonSuppressedTap() after a possible delay. * This should be called when the context is fully built (by gathering surrounding text * if needed, etc) but before showing any UX. + * @param contextualSearchContext The {@link ContextualSearchContext} for the Tap gesture. * @param rankerLogger The {@link ContextualSearchRankerLogger} currently being used to measure * or suppress the UI by Ranker. */ - void handleShouldSuppressTap(ContextualSearchRankerLogger rankerLogger) { + void handleShouldSuppressTap(ContextualSearchContext contextualSearchContext, + ContextualSearchRankerLogger rankerLogger) { int x = (int) mX; int y = (int) mY; @@ -362,9 +364,8 @@ ChromePreferenceManager prefs = ChromePreferenceManager.getInstance(); int adjustedTapsSinceOpen = prefs.getContextualSearchTapCount() - prefs.getContextualSearchTapQuickAnswerCount(); - TapSuppressionHeuristics tapHeuristics = - new TapSuppressionHeuristics(this, mLastTapState, x, y, adjustedTapsSinceOpen); - + TapSuppressionHeuristics tapHeuristics = new TapSuppressionHeuristics( + this, mLastTapState, x, y, adjustedTapsSinceOpen, contextualSearchContext); // TODO(donnd): Move to be called when the panel closes to work with states that change. tapHeuristics.logConditionState();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java index b1acdc6..589c9767 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java
@@ -803,6 +803,47 @@ } /** + * Log whether results were seen due to a Tap on a short word. + * @param wasSearchContentViewSeen If the panel was opened. + * @param isTapOnShortWord Whether this tap was on a "short" word. + */ + public static void logTapShortWordSeen( + boolean wasSearchContentViewSeen, boolean isTapOnShortWord) { + if (!isTapOnShortWord) return; + + // We just record CTR of short words. + RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchTapShortWordSeen", + wasSearchContentViewSeen ? RESULTS_SEEN : RESULTS_NOT_SEEN, RESULTS_SEEN_BOUNDARY); + } + + /** + * Log whether results were seen due to a Tap on a long word. + * @param wasSearchContentViewSeen If the panel was opened. + * @param isTapOnLongWord Whether this tap was on a long word. + */ + public static void logTapLongWordSeen( + boolean wasSearchContentViewSeen, boolean isTapOnLongWord) { + if (!isTapOnLongWord) return; + + RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchTapLongWordSeen", + wasSearchContentViewSeen ? RESULTS_SEEN : RESULTS_NOT_SEEN, RESULTS_SEEN_BOUNDARY); + } + + /** + * Log whether results were seen due to a Tap that was on the middle of a word. + * @param wasSearchContentViewSeen If the panel was opened. + * @param isTapOnWordMiddle Whether this tap was on the middle of a word. + */ + public static void logTapOnWordMiddleSeen( + boolean wasSearchContentViewSeen, boolean isTapOnWordMiddle) { + if (!isTapOnWordMiddle) return; + + // We just record CTR of words tapped in the "middle". + RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchTapOnWordMiddleSeen", + wasSearchContentViewSeen ? RESULTS_SEEN : RESULTS_NOT_SEEN, RESULTS_SEEN_BOUNDARY); + } + + /** * Logs whether results were seen and whether any tap suppression heuristics were satisfied. * @param wasSearchContentViewSeen If the panel was opened. * @param wasAnySuppressionHeuristicSatisfied Whether any of the implemented suppression
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppressionHeuristics.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppressionHeuristics.java index fb27502..c12da127 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppressionHeuristics.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppressionHeuristics.java
@@ -19,22 +19,19 @@ * @param tapsSinceOpen the number of Tap gestures since the last open of the panel. */ TapSuppressionHeuristics(ContextualSearchSelectionController selectionController, - ContextualSearchTapState previousTapState, int x, int y, int tapsSinceOpen) { + ContextualSearchTapState previousTapState, int x, int y, int tapsSinceOpen, + ContextualSearchContext contextualSearchContext) { super(); mCtrSuppression = new CtrSuppression(); mHeuristics.add(mCtrSuppression); - RecentScrollTapSuppression scrollTapExperiment = - new RecentScrollTapSuppression(selectionController); - mHeuristics.add(scrollTapExperiment); + mHeuristics.add(new RecentScrollTapSuppression(selectionController)); TapFarFromPreviousSuppression farFromPreviousHeuristic = new TapFarFromPreviousSuppression(selectionController, previousTapState, x, y); mHeuristics.add(farFromPreviousHeuristic); - NearTopTapSuppression tapNearTopSuppression = - new NearTopTapSuppression(selectionController, y); - mHeuristics.add(tapNearTopSuppression); - BarOverlapTapSuppression barOverlapTapSuppression = - new BarOverlapTapSuppression(selectionController, y); - mHeuristics.add(barOverlapTapSuppression); + mHeuristics.add(new TapWordLengthSuppression(contextualSearchContext)); + mHeuristics.add(new TapWordEdgeSuppression(contextualSearchContext)); + mHeuristics.add(new NearTopTapSuppression(selectionController, y)); + mHeuristics.add(new BarOverlapTapSuppression(selectionController, y)); // General Tap Suppression and Tap Twice. TapSuppression tapSuppression = new TapSuppression(selectionController, previousTapState, x, y, tapsSinceOpen);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapWordEdgeSuppression.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapWordEdgeSuppression.java new file mode 100644 index 0000000..0e25b12e --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapWordEdgeSuppression.java
@@ -0,0 +1,75 @@ +// 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.contextualsearch; + +import android.text.TextUtils; + +/** + * Implements the policy that a Tap relatively far away from the middle of a word should be + * ignored. When a Tap is close to the middle of the word tapped it's treated normally. + */ +class TapWordEdgeSuppression extends ContextualSearchHeuristic { + private static final int INVALID_OFFSET = ContextualSearchContext.INVALID_OFFSET; + private static final int MIN_WORD_LENGTH = 4; + private static final double MIN_WORD_START_RATIO = 0.25; + private static final double MIN_WORD_END_RATIO = 0.25; + + private final boolean mIsSuppressionEnabled; + private final boolean mIsConditionSatisfied; + + /** + * Constructs a heuristic to determine if the current Tap is close to the edge of the word. + * @param contextualSearchContext The current {@link ContextualSearchContext} so we can figure + * out what part of the word has been tapped. + */ + TapWordEdgeSuppression(ContextualSearchContext contextualSearchContext) { + mIsSuppressionEnabled = ContextualSearchFieldTrial.isWordEdgeSuppressionEnabled(); + mIsConditionSatisfied = isTapNearWordEdge(contextualSearchContext); + } + + @Override + protected boolean isConditionSatisfiedAndEnabled() { + return mIsSuppressionEnabled && mIsConditionSatisfied; + } + + @Override + protected void logResultsSeen(boolean wasSearchContentViewSeen, boolean wasActivatedByTap) { + if (wasActivatedByTap) { + ContextualSearchUma.logTapOnWordMiddleSeen( + wasSearchContentViewSeen, !mIsConditionSatisfied); + } + } + + @Override + protected boolean shouldAggregateLogForTapSuppression() { + return true; + } + + @Override + protected boolean isConditionSatisfiedForAggregateLogging() { + return mIsConditionSatisfied; + } + + /** + * Whether the tap is near the word edge and not a second-tap (tap following a suppressed tap). + * @param contextualSearchContext The {@link ContextualSearchContext}, used to determine how + * close the tap offset is to the word edges. + * @return Whether the tap is near an edge and not a second tap. + */ + private boolean isTapNearWordEdge(ContextualSearchContext contextualSearchContext) { + // If setup failed, don't suppress. + String tappedWord = contextualSearchContext.getTappedWord(); + int tapOffset = contextualSearchContext.getTappedWordOffset(); + if (TextUtils.isEmpty(tappedWord) || tapOffset == INVALID_OFFSET) return false; + + // If the word is long enough, suppress if the tap was near one end or the other. + boolean isInStartEdge = (double) tapOffset / tappedWord.length() < MIN_WORD_START_RATIO; + boolean isInEndEdge = (double) (tappedWord.length() - tapOffset) / tappedWord.length() + < MIN_WORD_END_RATIO; + if (tappedWord.length() >= MIN_WORD_LENGTH && (isInStartEdge || isInEndEdge)) return true; + + return false; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapWordLengthSuppression.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapWordLengthSuppression.java new file mode 100644 index 0000000..11a039d --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapWordLengthSuppression.java
@@ -0,0 +1,75 @@ +// 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.contextualsearch; + +import android.text.TextUtils; + +/** + * Implements signals for Taps on short and long words. + * This signal could be used for suppression when the word is short, so we aggregate-log too. + * We log CTR to UMA for Taps on both short and long words. + */ +class TapWordLengthSuppression extends ContextualSearchHeuristic { + private static final int MAXIMUM_SHORT_WORD_LENGTH = 3; + private static final int MAXIMUM_NOT_LONG_WORD_LENGTH = 9; + + private final boolean mIsShortWordSuppressionEnabled; + private final boolean mIsNotLongWordSuppressionEnabled; + private final boolean mIsShortWordConditionSatisfied; + private final boolean mIsNotLongWordConditionSatisfied; + + /** + * Constructs a heuristic to categorize the Tap based on word length of the tapped word. + * @param contextualSearchContext The current {@link ContextualSearchContext} so we can inspect + * the word tapped. + */ + TapWordLengthSuppression(ContextualSearchContext contextualSearchContext) { + mIsShortWordSuppressionEnabled = ContextualSearchFieldTrial.isShortWordSuppressionEnabled(); + mIsNotLongWordSuppressionEnabled = + ContextualSearchFieldTrial.isNotLongWordSuppressionEnabled(); + mIsShortWordConditionSatisfied = + !isTapOnWordLongerThan(MAXIMUM_SHORT_WORD_LENGTH, contextualSearchContext); + mIsNotLongWordConditionSatisfied = + !isTapOnWordLongerThan(MAXIMUM_NOT_LONG_WORD_LENGTH, contextualSearchContext); + } + + @Override + protected boolean isConditionSatisfiedAndEnabled() { + return mIsShortWordSuppressionEnabled && mIsShortWordConditionSatisfied + || mIsNotLongWordSuppressionEnabled && mIsNotLongWordConditionSatisfied; + } + + @Override + protected void logResultsSeen(boolean wasSearchContentViewSeen, boolean wasActivatedByTap) { + if (wasActivatedByTap) { + ContextualSearchUma.logTapShortWordSeen( + wasSearchContentViewSeen, mIsShortWordConditionSatisfied); + // Log CTR of long words, since not-long word CTR is probably not useful. + ContextualSearchUma.logTapLongWordSeen( + wasSearchContentViewSeen, !mIsNotLongWordConditionSatisfied); + } + } + + @Override + protected boolean shouldAggregateLogForTapSuppression() { + return true; + } + + @Override + protected boolean isConditionSatisfiedForAggregateLogging() { + // Short-word suppression is a good candidate for aggregate logging of overall suppression. + return mIsShortWordConditionSatisfied; + } + + /** + * @return Whether the tap is on a word longer than the given word length maximum. + */ + private boolean isTapOnWordLongerThan( + int maxWordLength, ContextualSearchContext contextualSearchContext) { + // If setup failed, don't suppress. + String tappedWord = contextualSearchContext.getTappedWord(); + return !TextUtils.isEmpty(tappedWord) && tappedWord.length() > maxWordLength; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java index 40e1851..ce22e1e9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -73,6 +73,7 @@ import org.chromium.chrome.browser.webapps.ChromeWebApkHost; import org.chromium.chrome.browser.webapps.WebApkVersionManager; import org.chromium.chrome.browser.webapps.WebappRegistry; +import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory; import org.chromium.components.minidump_uploader.CrashFileManager; import org.chromium.components.signin.AccountManagerHelper; import org.chromium.content.browser.ChildProcessLauncher; @@ -400,6 +401,13 @@ } } }); + + deferredStartupHandler.addDeferredTask(new Runnable() { + @Override + public void run() { + BackgroundTaskSchedulerFactory.getScheduler().checkForOSUpgrade(application); + } + }); } private void initChannelsAsync() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java index 34120dc..a03d62d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java
@@ -5,7 +5,6 @@ package org.chromium.chrome.browser.locale; import android.app.Activity; -import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.Nullable; @@ -22,7 +21,6 @@ import org.chromium.chrome.browser.search_engines.TemplateUrlService; import org.chromium.chrome.browser.widget.PromoDialog; import org.chromium.chrome.browser.widget.RadioButtonLayout; -import org.chromium.ui.base.WindowAndroid; /** A dialog that forces the user to choose a default search engine. */ public class DefaultSearchEnginePromoDialog extends PromoDialog { @@ -46,11 +44,11 @@ * Construct and show the dialog. Will be asynchronous if the TemplateUrlService has not yet * been loaded. * - * @param context Context to build the dialog with. + * @param activity Activity to build the dialog with. * @param dialogType Type of dialog to show. * @param onDismissed Notified about whether the user chose an engine when it got dismissed. */ - public static void show(final Context context, @SearchEnginePromoType final int dialogType, + public static void show(final Activity activity, @SearchEnginePromoType final int dialogType, @Nullable final Callback<Boolean> onDismissed) { assert LibraryLoader.isInitialized(); @@ -61,21 +59,20 @@ public void onTemplateUrlServiceLoaded() { instance.unregisterLoadListener(this); - Activity activity = WindowAndroid.activityFromContext(context); if (ApplicationStatus.getStateForActivity(activity) == ActivityState.DESTROYED) { if (onDismissed != null) onDismissed.onResult(false); return; } - new DefaultSearchEnginePromoDialog(context, dialogType, onDismissed).show(); + new DefaultSearchEnginePromoDialog(activity, dialogType, onDismissed).show(); } }); if (!instance.isLoaded()) instance.load(); } private DefaultSearchEnginePromoDialog( - Context context, int dialogType, @Nullable Callback<Boolean> onDismissed) { - super(context); + Activity activity, int dialogType, @Nullable Callback<Boolean> onDismissed) { + super(activity); mDialogType = dialogType; mOnDismissed = onDismissed; setOnDismissListener(this); @@ -129,8 +126,8 @@ @Override public void onDismiss(DialogInterface dialog) { if (mHelper.getCurrentlySelectedKeyword() == null) { - // This shouldn't happen, but in case it does, finish the Activity so that the user has - // to respond to the dialog next time. + // If no selection, finish the Activity so that the user has to respond to the dialog + // next time. if (getOwnerActivity() != null) getOwnerActivity().finish(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java index ef9bf57..19491e4f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.locale; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -208,22 +209,22 @@ * Shows a promotion dialog about search engines depending on Locale and other conditions. * See {@link LocaleManager#getSearchEnginePromoShowType()} for possible types and logic. * - * @param context Context showing the dialog. + * @param activity Activity showing the dialog. * @param onDismissed Notified when the dialog is dismissed and whether the user acted on it. * @return Whether such dialog is needed. */ public boolean showSearchEnginePromoIfNeeded( - Context context, @Nullable Callback<Boolean> onDismissed) { + Activity activity, @Nullable Callback<Boolean> onDismissed) { int shouldShow = getSearchEnginePromoShowType(); switch (shouldShow) { case SEARCH_ENGINE_PROMO_DONT_SHOW: return false; case SEARCH_ENGINE_PROMO_SHOW_SOGOU: - new SogouPromoDialog(context, this, onDismissed).show(); + new SogouPromoDialog(activity, this, onDismissed).show(); return true; case SEARCH_ENGINE_PROMO_SHOW_EXISTING: case SEARCH_ENGINE_PROMO_SHOW_NEW: - DefaultSearchEnginePromoDialog.show(context, shouldShow, onDismissed); + DefaultSearchEnginePromoDialog.show(activity, shouldShow, onDismissed); return true; default: assert false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java index 3b9244e..144a7ba0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
@@ -4,7 +4,7 @@ package org.chromium.chrome.browser.locale; -import android.content.Context; +import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; @@ -68,9 +68,9 @@ /** * Creates an instance of the dialog. */ - public SogouPromoDialog( - Context context, LocaleManager localeManager, @Nullable Callback<Boolean> onDismissed) { - super(context); + public SogouPromoDialog(Activity activity, LocaleManager localeManager, + @Nullable Callback<Boolean> onDismissed) { + super(activity); mLocaleManager = localeManager; setOnDismissListener(this); setCanceledOnTouchOutside(false);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java index 818e58e2..34ed542 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java
@@ -5,7 +5,6 @@ package org.chromium.chrome.browser.preferences.datareduction; import android.app.Activity; -import android.content.Context; import android.content.DialogInterface; import android.view.View; @@ -42,10 +41,10 @@ /** * DataReductionPromoScreen constructor. * - * @param context An Android context. + * @param activity An Android activity to display the dialog. */ - public DataReductionPromoScreen(Context context) { - super(context); + public DataReductionPromoScreen(Activity activity) { + super(activity); mState = DataReductionProxyUma.ACTION_DISMISSED; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java index 3d3f272..52d0523 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
@@ -936,7 +936,8 @@ // mListeningForWebVrActivate for them. if (mVrSupportLevel != VR_DAYDREAM) return; mListeningForWebVrActivate = listening; - if (listening && !mPaused) { + if (mPaused) return; + if (listening) { registerDaydreamIntent(mVrDaydreamApi, mActivity); if (mAutopresentWebVr) { // Dispatch vrdisplayactivate so that the WebVr page can call requestPresent @@ -946,7 +947,7 @@ // UI which is suboptimal. nativeDisplayActivate(mNativeVrShellDelegate); } - } else { + } else if (!canEnterVr(mActivity.getActivityTab())) { unregisterDaydreamIntent(mVrDaydreamApi); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/PromoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/PromoDialog.java index cd3b51b..30626367 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/PromoDialog.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/PromoDialog.java
@@ -4,8 +4,7 @@ package org.chromium.chrome.browser.widget; -import android.app.Dialog; -import android.content.Context; +import android.app.Activity; import android.content.DialogInterface; import android.os.Bundle; import android.view.LayoutInflater; @@ -19,8 +18,8 @@ /** * Generic builder for promo dialogs. */ -public abstract class PromoDialog - extends Dialog implements View.OnClickListener, DialogInterface.OnDismissListener { +public abstract class PromoDialog extends AlwaysDismissedDialog + implements View.OnClickListener, DialogInterface.OnDismissListener { /** Parameters that can be used to create a new PromoDialog. */ public static class DialogParams { /** @@ -56,13 +55,13 @@ private final FrameLayout mScrimView; private final PromoDialogLayout mDialogLayout; - protected PromoDialog(Context context) { - super(context, R.style.PromoDialog); + protected PromoDialog(Activity activity) { + super(activity, R.style.PromoDialog); - mScrimView = new FrameLayout(context); + mScrimView = new FrameLayout(activity); mScrimView.setBackgroundColor(ApiCompatibilityUtils.getColor( - context.getResources(), R.color.modal_dialog_scrim_color)); - LayoutInflater.from(context).inflate(R.layout.promo_dialog_layout, mScrimView, true); + activity.getResources(), R.color.modal_dialog_scrim_color)); + LayoutInflater.from(activity).inflate(R.layout.promo_dialog_layout, mScrimView, true); mDialogLayout = (PromoDialogLayout) mScrimView.findViewById(R.id.promo_dialog_layout); mDialogLayout.initialize(getDialogParams());
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index b64a883c..4d9e84e99 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -262,6 +262,8 @@ "java/src/org/chromium/chrome/browser/contextualsearch/TapFarFromPreviousSuppression.java", "java/src/org/chromium/chrome/browser/contextualsearch/TapSuppression.java", "java/src/org/chromium/chrome/browser/contextualsearch/TapSuppressionHeuristics.java", + "java/src/org/chromium/chrome/browser/contextualsearch/TapWordEdgeSuppression.java", + "java/src/org/chromium/chrome/browser/contextualsearch/TapWordLengthSuppression.java", "java/src/org/chromium/chrome/browser/cookies/CanonicalCookie.java", "java/src/org/chromium/chrome/browser/cookies/CookiesFetcher.java", "java/src/org/chromium/chrome/browser/crash/ChromeMinidumpUploadJobService.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java index 1fde63c6..d436ba18 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java
@@ -23,6 +23,7 @@ import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.DisableIf; +import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.MinAndroidSdkLevel; import org.chromium.base.test.util.Restriction; import org.chromium.chrome.R; @@ -161,7 +162,8 @@ * Tests that non-focused tabs cannot get pose information. */ @Test - @SmallTest + // @SmallTest + @DisabledTest(message = "Flaky. http://crbug.com/726986") public void testPoseDataUnfocusedTab() throws InterruptedException { mVrTestRule.loadUrlAndAwaitInitialization( VrTestRule.getHtmlTestFile("test_pose_data_unfocused_tab"), PAGE_LOAD_TIMEOUT_S);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/PromoDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/PromoDialogTest.java index c9287f7..4871303 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/PromoDialogTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/PromoDialogTest.java
@@ -4,7 +4,7 @@ package org.chromium.chrome.browser.widget; -import android.content.Context; +import android.app.Activity; import android.content.DialogInterface; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; @@ -13,27 +13,39 @@ import android.view.ViewGroup; import android.widget.LinearLayout; -import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.ThreadUtils; -import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.widget.PromoDialog.DialogParams; -import org.chromium.chrome.test.util.ApplicationTestUtils; +import org.chromium.chrome.test.ChromeActivityTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; import java.util.concurrent.Callable; /** * Tests for the PromoDialog and PromoDialogLayout. */ -@RunWith(BaseJUnit4ClassRunner.class) +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ + ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, + ChromeActivityTestRule.DISABLE_NETWORK_PREDICTION_FLAG, +}) public class PromoDialogTest { + // TODO(tedchoc): Find a way to introduce a lightweight activity that doesn't spin up the world. + // crbug.com/728297 + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + /** * Creates a PromoDialog. Doesn't call {@link PromoDialog#show} because there is no Window to * attach them to, but it does create them and inflate the layouts. @@ -44,16 +56,15 @@ public final PromoDialog dialog; public final PromoDialogLayout dialogLayout; - private final Context mContext; private final DialogParams mDialogParams; - PromoDialogWrapper(final DialogParams dialogParams) throws Exception { - mContext = InstrumentationRegistry.getTargetContext(); + PromoDialogWrapper(final Activity activity, final DialogParams dialogParams) + throws Exception { mDialogParams = dialogParams; dialog = ThreadUtils.runOnUiThreadBlocking(new Callable<PromoDialog>() { @Override public PromoDialog call() throws Exception { - PromoDialog dialog = new PromoDialog(mContext) { + PromoDialog dialog = new PromoDialog(activity) { @Override public DialogParams getDialogParams() { return mDialogParams; @@ -105,13 +116,8 @@ } @Before - public void setUp() { - ApplicationTestUtils.setUp(InstrumentationRegistry.getTargetContext(), true); - } - - @After - public void tearDown() throws Exception { - ApplicationTestUtils.tearDown(InstrumentationRegistry.getTargetContext()); + public void setUp() throws Exception { + mActivityTestRule.startMainActivityOnBlankPage(); } @Test @@ -136,7 +142,8 @@ /** Confirm that PromoDialogs are constructed with all the elements expected. */ private void checkDialogControlVisibility(final DialogParams dialogParams) throws Exception { - PromoDialogWrapper wrapper = new PromoDialogWrapper(dialogParams); + PromoDialogWrapper wrapper = + new PromoDialogWrapper(mActivityTestRule.getActivity(), dialogParams); PromoDialogLayout promoDialogLayout = wrapper.dialogLayout; View illustration = promoDialogLayout.findViewById(R.id.illustration); @@ -175,7 +182,8 @@ dialogParams.secondaryButtonStringResource = R.string.cancel; dialogParams.footerStringResource = R.string.learn_more; - PromoDialogWrapper wrapper = new PromoDialogWrapper(dialogParams); + PromoDialogWrapper wrapper = + new PromoDialogWrapper(mActivityTestRule.getActivity(), dialogParams); final PromoDialogLayout promoDialogLayout = wrapper.dialogLayout; LinearLayout flippableLayout = (LinearLayout) promoDialogLayout.findViewById(R.id.full_promo_content); @@ -211,7 +219,8 @@ dialogParams.primaryButtonStringResource = R.string.ok; dialogParams.secondaryButtonStringResource = R.string.cancel; - PromoDialogWrapper wrapper = new PromoDialogWrapper(dialogParams); + PromoDialogWrapper wrapper = + new PromoDialogWrapper(mActivityTestRule.getActivity(), dialogParams); final PromoDialogLayout promoDialogLayout = wrapper.dialogLayout; // Nothing should have been clicked yet. @@ -248,7 +257,8 @@ dialogParams.headerStringResource = R.string.data_reduction_promo_title; dialogParams.primaryButtonStringResource = R.string.data_reduction_enable_button; - PromoDialogWrapper wrapper = new PromoDialogWrapper(dialogParams); + PromoDialogWrapper wrapper = + new PromoDialogWrapper(mActivityTestRule.getActivity(), dialogParams); PromoDialogLayout promoDialogLayout = wrapper.dialogLayout; ViewGroup scrollableLayout = (ViewGroup) promoDialogLayout.findViewById(R.id.scrollable_promo_content); @@ -267,7 +277,8 @@ dialogParams.headerStringResource = R.string.search_with_sogou; dialogParams.primaryButtonStringResource = R.string.ok; - PromoDialogWrapper wrapper = new PromoDialogWrapper(dialogParams); + PromoDialogWrapper wrapper = + new PromoDialogWrapper(mActivityTestRule.getActivity(), dialogParams); PromoDialogLayout promoDialogLayout = wrapper.dialogLayout; ViewGroup scrollableLayout = (ViewGroup) promoDialogLayout.findViewById(R.id.scrollable_promo_content); @@ -286,7 +297,8 @@ dialogParams.headerStringResource = R.string.search_with_sogou; dialogParams.primaryButtonStringResource = R.string.ok; - PromoDialogWrapper wrapper = new PromoDialogWrapper(dialogParams); + PromoDialogWrapper wrapper = + new PromoDialogWrapper(mActivityTestRule.getActivity(), dialogParams); PromoDialogLayout promoDialogLayout = wrapper.dialogLayout; // Add a dummy control view to ensure the scrolling container has some content.
diff --git a/chrome/app/bookmarks_strings.grdp b/chrome/app/bookmarks_strings.grdp index d6d070a..22d0be7 100644 --- a/chrome/app/bookmarks_strings.grdp +++ b/chrome/app/bookmarks_strings.grdp
@@ -383,8 +383,8 @@ <message name="IDS_MD_BOOKMARK_MANAGER_CLEAR_SEARCH" desc="Title of the button in the bookmark manager that stops a search."> Clear search </message> - <message name="IDS_MD_BOOKMARK_MANAGER_EMPTY_LIST" desc="Text on the bookmarks list that indicates that a folder has no bookmarks."> - This folder is empty + <message name="IDS_MD_BOOKMARK_MANAGER_EMPTY_LIST" desc="The message shown when the user has no bookmarks added. 'Star' refers to the icon in the omnibox for adding to bookmarks."> + To bookmark pages, click the star in the address bar </message> <message name="IDS_MD_BOOKMARK_MANAGER_FOLDER_RENAME_TITLE" desc="Title of the bookmark editor window when editing folders."> Rename Folder
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd index 1965435..b304bf8 100644 --- a/chrome/app/chromium_strings.grd +++ b/chrome/app/chromium_strings.grd
@@ -1183,9 +1183,11 @@ </message> <!-- Plugin Placeholders --> - <message name="IDS_PLUGIN_RESTART_REQUIRED" desc="The placeholder text for a plugin that can't be loaded until the browser is restarted."> - Restart Chromium to enable <ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph> - </message> + <if expr="is_linux"> + <message name="IDS_PLUGIN_RESTART_REQUIRED" desc="The placeholder text for a plugin that can't be loaded until the browser is restarted."> + Restart Chromium to enable <ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph> + </message> + </if> <!-- Desktop to iOS promotion --> <message name="IDS_PASSWORD_MANAGER_DESKTOP_TO_IOS_PROMO_TEXT" desc="Text for Chrome iOS Promotion appearing in the password bubble after a password is saved.">
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index e13f52b..32ceee2a 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -1545,7 +1545,7 @@ No search results found </message> <message name="IDS_MD_DOWNLOAD_NO_DOWNLOADS" desc="A message shown when the user has no downloads to show on chrome://downloads."> - Nothing to see here... + Files you download appear here </message> <message name="IDS_DOWNLOAD_LINK_RESUME" desc="In the download view, 'Resume' link text">
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd index abb9a307..ed18a2a 100644 --- a/chrome/app/google_chrome_strings.grd +++ b/chrome/app/google_chrome_strings.grd
@@ -1193,9 +1193,11 @@ </message> <!-- Plugin Placeholders --> - <message name="IDS_PLUGIN_RESTART_REQUIRED" desc="The placeholder text for a plugin that can't be loaded until the browser is restarted."> - Restart Chrome to enable <ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph> - </message> + <if expr="is_linux"> + <message name="IDS_PLUGIN_RESTART_REQUIRED" desc="The placeholder text for a plugin that can't be loaded until the browser is restarted."> + Restart Chrome to enable <ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph> + </message> + </if> <!-- Desktop to iOS promotion --> <message name="IDS_PASSWORD_MANAGER_DESKTOP_TO_IOS_PROMO_TEXT" desc="Text for Chrome iOS Promotion appearing in the password bubble after a password is saved.">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 26f3eb9..e185ebc1 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -108,6 +108,7 @@ #if defined(OS_ANDROID) #include "chrome/browser/android/chrome_feature_list.h" #include "components/feature_engagement_tracker/public/feature_constants.h" +#include "components/feature_engagement_tracker/public/feature_list.h" #else // OS_ANDROID #include "ui/message_center/message_center_switches.h" #endif // OS_ANDROID @@ -1781,9 +1782,12 @@ MULTI_VALUE_TYPE(kChromeHomeSwipeLogicChoices)}, #endif // OS_ANDROID #if defined(OS_ANDROID) - {"enable-iph-demo-mode", flag_descriptions::kEnableIphDemoModeName, - flag_descriptions::kEnableIphDemoModeDescription, kOsAndroid, - FEATURE_VALUE_TYPE(feature_engagement_tracker::kIPHDemoMode)}, + {"iph-demo-mode-choice", flag_descriptions::kIphDemoModeChoiceName, + flag_descriptions::kIphDemoModeChoiceDescription, kOsAndroid, + FEATURE_WITH_PARAMS_VALUE_TYPE( + feature_engagement_tracker::kIPHDemoMode, + feature_engagement_tracker::kIPHDemoModeChoiceVariations, + feature_engagement_tracker::kIPHDemoMode.name)}, #endif // OS_ANDROID {"num-raster-threads", flag_descriptions::kNumRasterThreadsName, flag_descriptions::kNumRasterThreadsDescription, kOsAll,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 12d496f..7e31d29 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -93,8 +93,10 @@ #include "chrome/browser/translate/chrome_translate_client.h" #include "chrome/browser/ui/blocked_content/blocked_window_params.h" #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h" +#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator_params.h" +#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/chrome_select_file_policy.h" #include "chrome/browser/ui/sync/sync_promo_ui.h" #include "chrome/browser/ui/tab_contents/chrome_web_contents_view_delegate.h" @@ -542,20 +544,30 @@ // mash::mojom::Launchable: void Launch(uint32_t what, mash::mojom::LaunchMode how) override { - if (how != mash::mojom::LaunchMode::MAKE_NEW) { - LOG(ERROR) << "Unable to handle Launch request with how = " << how; - return; - } + bool is_incognito; switch (what) { case mash::mojom::kWindow: - CreateNewWindowImpl(false /* is_incognito */); + is_incognito = false; break; case mash::mojom::kIncognitoWindow: - CreateNewWindowImpl(true /* is_incognito */); + is_incognito = true; break; default: NOTREACHED(); } + + bool reuse = how != mash::mojom::LaunchMode::MAKE_NEW; + if (reuse) { + Profile* profile = ProfileManager::GetActiveUserProfile(); + Browser* browser = chrome::FindTabbedBrowser( + is_incognito ? profile->GetOffTheRecordProfile() : profile, false); + if (browser) { + browser->window()->Show(); + return; + } + } + + CreateNewWindowImpl(is_incognito); } void Create(const service_manager::BindSourceInfo& source_info,
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc index 32ff477..b653898 100644 --- a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc +++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
@@ -554,8 +554,8 @@ touch_point_view_->SetPaintToLayer(); touch_point_view_->layer()->SetFillsBoundsOpaquely(false); touch_point_view_->layer()->GetAnimator()->AddObserver(this); - touch_point_view_->set_background( - views::Background::CreateSolidBackground(SK_ColorTRANSPARENT)); + touch_point_view_->SetBackground( + views::CreateSolidBackground(SK_ColorTRANSPARENT)); touch_point_view_->AddChildView(throbber_circle_); touch_point_view_->AddChildView(tap_label_); @@ -612,8 +612,8 @@ completion_message_view_->SetPaintToLayer(); completion_message_view_->layer()->SetFillsBoundsOpaquely(false); completion_message_view_->layer()->GetAnimator()->AddObserver(this); - completion_message_view_->set_background( - views::Background::CreateSolidBackground(SK_ColorTRANSPARENT)); + completion_message_view_->SetBackground( + views::CreateSolidBackground(SK_ColorTRANSPARENT)); AddChildView(completion_message_view_); }
diff --git a/chrome/browser/chromeos/logging.cc b/chrome/browser/chromeos/logging.cc index 41a1302..5cf1d0de 100644 --- a/chrome/browser/chromeos/logging.cc +++ b/chrome/browser/chromeos/logging.cc
@@ -9,6 +9,7 @@ #include "base/files/file_path.h" #include "base/logging.h" #include "base/task_scheduler/post_task.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/logging_chrome.h" #include "content/public/browser/browser_thread.h" @@ -51,8 +52,8 @@ return; } - DCHECK(!chrome_logging_redirected_) - << "Attempted to redirect logging when it was already initialized."; + if (command_line.HasSwitch(switches::kDisableLoggingRedirect)) + return; // Redirect logs to the session log directory, if set. Otherwise // defaults to the profile dir.
diff --git a/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc b/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc index f395a02e3..850938e 100644 --- a/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc +++ b/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc
@@ -58,7 +58,7 @@ class ToolbarRowView : public views::View { public: ToolbarRowView() { - set_background(views::Background::CreateSolidBackground(kDialogColor)); + SetBackground(views::CreateSolidBackground(kDialogColor)); } ~ToolbarRowView() override {} @@ -168,7 +168,7 @@ toolbar_model_.reset( new ToolbarModelImpl(this, content::kMaxURLDisplayChars)); - set_background(views::Background::CreateSolidBackground(kDialogColor)); + SetBackground(views::CreateSolidBackground(kDialogColor)); // Back/Forward buttons. back_ = new views::ImageButton(this);
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index ee3cb30..81d4b8e3 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -1991,10 +1991,10 @@ #if defined(OS_ANDROID) -const char kEnableIphDemoModeName[] = "In-Product Help Demo Mode"; +const char kIphDemoModeChoiceName[] = "In-Product Help Demo Mode"; -const char kEnableIphDemoModeDescription[] = - "Enables In-Product Help demo mode on Android."; +const char kIphDemoModeChoiceDescription[] = + "Selects the In-Product Help demo mode."; #endif // defined(OS_ANDROID)
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 1c2adae..a6f9845c 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -908,9 +908,6 @@ extern const char kEnableDataReductionProxySiteBreakdownName[]; extern const char kEnableDataReductionProxySiteBreakdownDescription[]; -extern const char kEnableIphDemoModeName[]; -extern const char kEnableIphDemoModeDescription[]; - extern const char kEnableOmniboxClipboardProviderName[]; extern const char kEnableOmniboxClipboardProviderDescription[]; @@ -988,6 +985,9 @@ extern const char kHerbPrototypeChoicesDescription[]; extern const char kHerbPrototypeFlavorElderberry[]; +extern const char kIphDemoModeChoiceName[]; +extern const char kIphDemoModeChoiceDescription[]; + extern const char kLsdPermissionPromptName[]; extern const char kLsdPermissionPromptDescription[];
diff --git a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc index b7a7ddf9..99e8752 100644 --- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc +++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
@@ -283,7 +283,7 @@ #endif } -#if defined(USE_X11) && !defined(OS_CHROMEOS) +#if defined(OS_LINUX) && defined(USE_X11) && !defined(OS_CHROMEOS) UMALinuxWindowManager GetLinuxWindowManager() { switch (ui::GuessWindowManager()) { case ui::WM_OTHER:
diff --git a/chrome/browser/plugins/plugin_info_message_filter.cc b/chrome/browser/plugins/plugin_info_message_filter.cc index 8c31c98..917e3db 100644 --- a/chrome/browser/plugins/plugin_info_message_filter.cc +++ b/chrome/browser/plugins/plugin_info_message_filter.cc
@@ -11,6 +11,7 @@ #include <utility> #include "base/bind.h" +#include "base/memory/ptr_util.h" #include "base/memory/singleton.h" #include "base/metrics/histogram_macros.h" #include "base/strings/utf_string_conversions.h" @@ -497,10 +498,10 @@ output->status = ChromeViewHostMsg_GetPluginInfo_Status::kRestartRequired; } -#endif // defined(OS_LINUX) - plugin_metadata.reset(new PluginMetadata( +#endif + plugin_metadata = base::MakeUnique<PluginMetadata>( cus_plugin_info->id, cus_plugin_info->name, false, GURL(), GURL(), - base::ASCIIToUTF16(cus_plugin_info->id), std::string())); + base::ASCIIToUTF16(cus_plugin_info->id), std::string()); } GetPluginInfoReply(params, std::move(output), std::move(plugin_metadata), reply_msg);
diff --git a/chrome/browser/prefs/active_profile_pref_service.cc b/chrome/browser/prefs/active_profile_pref_service.cc index 35293d8..5bbb03a 100644 --- a/chrome/browser/prefs/active_profile_pref_service.cc +++ b/chrome/browser/prefs/active_profile_pref_service.cc
@@ -29,6 +29,17 @@ std::move(callback)); } +void ActiveProfilePrefService::ConnectToUserPrefStore( + const std::vector<std::string>& prefs_to_observe, + ConnectToUserPrefStoreCallback callback) { + auto* connector = content::BrowserContext::GetConnectorFor( + ProfileManager::GetActiveUserProfile()->GetOriginalProfile()); + connector->BindInterface(prefs::mojom::kServiceName, &connector_ptr_); + connector_ptr_.set_connection_error_handler(base::Bind( + &ActiveProfilePrefService::OnConnectError, base::Unretained(this))); + connector_ptr_->ConnectToUserPrefStore(prefs_to_observe, std::move(callback)); +} + void ActiveProfilePrefService::Create( const service_manager::BindSourceInfo& source_info, prefs::mojom::PrefStoreConnectorRequest request) {
diff --git a/chrome/browser/prefs/active_profile_pref_service.h b/chrome/browser/prefs/active_profile_pref_service.h index c2e2c29..ca0674d 100644 --- a/chrome/browser/prefs/active_profile_pref_service.h +++ b/chrome/browser/prefs/active_profile_pref_service.h
@@ -32,6 +32,8 @@ prefs::mojom::PrefRegistryPtr pref_registry, const std::vector<PrefValueStore::PrefStoreType>& already_connected_types, ConnectCallback callback) override; + void ConnectToUserPrefStore(const std::vector<std::string>& prefs_to_observe, + ConnectToUserPrefStoreCallback callback) override; // service_manager::Service: void OnStart() override;
diff --git a/chrome/browser/prefs/profile_pref_store_manager.h b/chrome/browser/prefs/profile_pref_store_manager.h index 95757a67..ee38a40 100644 --- a/chrome/browser/prefs/profile_pref_store_manager.h +++ b/chrome/browser/prefs/profile_pref_store_manager.h
@@ -14,7 +14,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "services/preferences/public/interfaces/preferences_configuration.mojom.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" #include "services/preferences/public/interfaces/tracked_preference_validation_delegate.mojom.h" class PersistentPrefStore;
diff --git a/chrome/browser/resources/chromeos/login/md_login_shared.js b/chrome/browser/resources/chromeos/login/md_login_shared.js index 41afbaa..195102d 100644 --- a/chrome/browser/resources/chromeos/login/md_login_shared.js +++ b/chrome/browser/resources/chromeos/login/md_login_shared.js
@@ -425,6 +425,12 @@ Oobe.setVirtualKeyboardShown = function(shown) { Oobe.getInstance().virtualKeyboardShown = shown; $('pod-row').setFocusedPodPinVisibility(!shown); + // The dark overlay should always have the same size with the larger one + // of the outer-container and the scroll-container. + if (shown) + $('login-shield').style.minHeight = $('outer-container').style.minHeight; + else + $('login-shield').style.minHeight = 'unset'; }; /**
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js index 116b1c64..19499638 100644 --- a/chrome/browser/resources/print_preview/data/destination_store.js +++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -1078,7 +1078,8 @@ startLoadLocalDestinations: function() { if (!this.hasLoadedAllLocalDestinations_) { this.hasLoadedAllLocalDestinations_ = true; - this.nativeLayer_.startGetLocalDestinations(); + this.nativeLayer_.getPrinters().then( + this.onLocalDestinationsSet_.bind(this)); this.isLocalDestinationSearchInProgress_ = true; cr.dispatchSimpleEvent( this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED); @@ -1362,10 +1363,6 @@ var nativeLayerEventTarget = this.nativeLayer_.getEventTarget(); this.tracker_.add( nativeLayerEventTarget, - print_preview.NativeLayer.EventType.LOCAL_DESTINATIONS_SET, - this.onLocalDestinationsSet_.bind(this)); - this.tracker_.add( - nativeLayerEventTarget, print_preview.NativeLayer.EventType.CAPABILITIES_SET, this.onLocalDestinationCapabilitiesSet_.bind(this)); this.tracker_.add( @@ -1437,11 +1434,12 @@ /** * Called when the local destinations have been got from the native layer. - * @param {Event} event Contains the local destinations. + * @param {!Array<!print_preview.LocalDestinationInfo>} destinationInfos A + * list of the local destinations retrieved. * @private */ - onLocalDestinationsSet_: function(event) { - var localDestinations = event.destinationInfos.map(function(destInfo) { + onLocalDestinationsSet_: function(destinationInfos) { + var localDestinations = destinationInfos.map(function(destInfo) { return print_preview.LocalDestinationParser.parse(destInfo); }); this.insertDestinations_(localDestinations);
diff --git a/chrome/browser/resources/print_preview/data/local_parsers.js b/chrome/browser/resources/print_preview/data/local_parsers.js index 4155f5a..97cceab 100644 --- a/chrome/browser/resources/print_preview/data/local_parsers.js +++ b/chrome/browser/resources/print_preview/data/local_parsers.js
@@ -10,8 +10,8 @@ /** * Parses a local print destination. - * @param {!Object} destinationInfo Information describing a local print - * destination. + * @param {!print_preview.LocalDestinationInfo} destinationInfo Information + * describing a local print destination. * @return {!print_preview.Destination} Parsed local print destination. */ LocalDestinationParser.parse = function(destinationInfo) {
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js index d3a12f02..4ad76dea 100644 --- a/chrome/browser/resources/print_preview/native_layer.js +++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -17,6 +17,17 @@ /** * @typedef {{ + * deviceName: string, + * printerName: string, + * printerDescription: (string | undefined), + * cupsEnterprisePrinter: (boolean | undefined), + * printerOptions: (Object | undefined), + * }} + */ +print_preview.LocalDestinationInfo; + +/** + * @typedef {{ * printerId: string, * success: boolean, * capabilities: Object, @@ -34,7 +45,6 @@ function NativeLayer() { // Bind global handlers global.setUseCloudPrint = this.onSetUseCloudPrint_.bind(this); - global.setPrinters = this.onSetPrinters_.bind(this); global.updateWithPrinterCapabilities = this.onUpdateWithPrinterCapabilities_.bind(this); global.failedToGetPrinterCapabilities = @@ -220,9 +230,10 @@ /** * Requests the system's local print destinations. A LOCAL_DESTINATIONS_SET * event will be dispatched in response. + * @return {!Promise<!Array<print_preview.LocalDestinationInfo>>} */ - startGetLocalDestinations: function() { - chrome.send('getPrinters'); + getPrinters: function() { + return cr.sendWithPromise('getPrinters'); }, /** @@ -550,19 +561,6 @@ }, /** - * Updates the print preview with local printers. - * Called from PrintPreviewHandler::SetupPrinterList(). - * @param {Array} printers Array of printer info objects. - * @private - */ - onSetPrinters_: function(printers) { - var localDestsSetEvent = new Event( - NativeLayer.EventType.LOCAL_DESTINATIONS_SET); - localDestsSetEvent.destinationInfos = printers; - this.eventTarget_.dispatchEvent(localDestsSetEvent); - }, - - /** * Called when native layer gets settings information for a requested local * destination. * @param {Object} settingsInfo printer setting information.
diff --git a/chrome/browser/resources/settings/about_page/about_page.html b/chrome/browser/resources/settings/about_page/about_page.html index f4da9748..05b00cf 100644 --- a/chrome/browser/resources/settings/about_page/about_page.html +++ b/chrome/browser/resources/settings/about_page/about_page.html
@@ -36,7 +36,7 @@ } .copyable { - -webkit-user-select: text; + user-select: text; } .info-section { @@ -107,7 +107,8 @@ src="[[getIconSrc_(obsoleteSystemInfo_, currentUpdateStatusEvent_)]]"> </iron-icon> <div class="start padded"> - <div id="updateStatusMessage" hidden="[[!showUpdateStatus_]]" + <div id="updateStatusMessage" class="copyable" + hidden="[[!showUpdateStatus_]]" <if expr="not chromeos"> inner-h-t-m-l="[[getUpdateStatusMessage_( currentUpdateStatusEvent_)]]">
diff --git a/chrome/browser/resources/settings/site_settings/site_details.html b/chrome/browser/resources/settings/site_settings/site_details.html index 3e902fe..7cc87ab5 100644 --- a/chrome/browser/resources/settings/site_settings/site_details.html +++ b/chrome/browser/resources/settings/site_settings/site_details.html
@@ -5,6 +5,7 @@ <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> +<link rel="import" href="../icons.html"> <link rel="import" href="../route.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="constants.html"> @@ -90,6 +91,10 @@ icon="cr:extension" id="plugins" label="$i18n{siteSettingsFlash}" site="[[site]]"> </site-details-permission> + <site-details-permission category="{{ContentSettingsTypes.IMAGES}}" + icon="settings:photo" id="images" label="$i18n{siteSettingsImages}" + site="[[site]]"> + </site-details-permission> <site-details-permission category="{{ContentSettingsTypes.POPUPS}}" icon="cr:open-in-new" id="popups" label="$i18n{siteSettingsPopups}" site="[[site]]">
diff --git a/chrome/browser/ui/cocoa/hover_close_button.mm b/chrome/browser/ui/cocoa/hover_close_button.mm index f025a58..973ef4f 100644 --- a/chrome/browser/ui/cocoa/hover_close_button.mm +++ b/chrome/browser/ui/cocoa/hover_close_button.mm
@@ -59,16 +59,18 @@ gTooltip = [l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_CLOSE_TAB) copy]; } -- (id)initWithFrame:(NSRect)frameRect { - if ((self = [super initWithFrame:frameRect])) { - [self commonInit]; - } - return self; -} +- (void)commonInit { + [super commonInit]; -- (void)awakeFromNib { - [super awakeFromNib]; - [self commonInit]; + [self setAccessibilityTitle:nil]; + + // Add a tooltip. Using 'owner:self' means that + // -view:stringForToolTip:point:userData: will be called to provide the + // tooltip contents immediately before showing it. + [self addToolTipRect:[self bounds] owner:self userData:NULL]; + + previousState_ = kHoverStateNone; + iconColor_ = kDefaultIconColor; } - (void)removeFromSuperview { @@ -198,18 +200,6 @@ } } -- (void)commonInit { - [self setAccessibilityTitle:nil]; - - // Add a tooltip. Using 'owner:self' means that - // -view:stringForToolTip:point:userData: will be called to provide the - // tooltip contents immediately before showing it. - [self addToolTipRect:[self bounds] owner:self userData:NULL]; - - previousState_ = kHoverStateNone; - iconColor_ = kDefaultIconColor; -} - // Called each time a tooltip is about to be shown. - (NSString*)view:(NSView*)view stringForToolTip:(NSToolTipTag)tag
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_container.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_container.cc index aa6938e..52216a8 100644 --- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_container.cc +++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_container.cc
@@ -134,7 +134,7 @@ AppListDialogContainer(views::View* dialog_body, const base::Closure& close_callback) : BaseDialogContainer(dialog_body, close_callback) { - set_background(new AppListOverlayBackground()); + SetBackground(base::MakeUnique<AppListOverlayBackground>()); close_button_ = views::BubbleFrameView::CreateCloseButton(this); AddChildView(close_button_); }
diff --git a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc index 1758bc0..ede410f 100644 --- a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc +++ b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc
@@ -228,8 +228,7 @@ storage_row_->SetLayoutManager(storage_row_layout); storage_row_->SetBorder( views::CreateSolidSidedBorder(1, 0, 0, 0, kSubtleBorderColor)); - storage_row_->set_background( - views::Background::CreateSolidBackground(kLightShadingColor)); + storage_row_->SetBackground(views::CreateSolidBackground(kLightShadingColor)); storage_checkbox_ = new views::Checkbox(l10n_util::GetStringUTF16( IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_CHECKBOX)); @@ -276,8 +275,7 @@ void CardUnmaskPromptViews::OnNativeThemeChanged(const ui::NativeTheme* theme) { SkColor bg_color = theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground); - progress_overlay_->set_background( - views::Background::CreateSolidBackground(bg_color)); + progress_overlay_->SetBackground(views::CreateSolidBackground(bg_color)); progress_label_->SetBackgroundColor(bg_color); progress_label_->SetEnabledColor(theme->GetSystemColor( ui::NativeTheme::kColorId_ThrobberSpinningColor)); @@ -393,8 +391,8 @@ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); permanent_error_label_->SetFontList( rb.GetFontList(ui::ResourceBundle::BoldFont)); - permanent_error_label_->set_background( - views::Background::CreateSolidBackground(kWarningColor)); + permanent_error_label_->SetBackground( + views::CreateSolidBackground(kWarningColor)); permanent_error_label_->SetBorder( views::CreateEmptyBorder(12, kEdgePadding, 12, kEdgePadding)); permanent_error_label_->SetEnabledColor(SK_ColorWHITE);
diff --git a/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc b/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc index 6970f7d..c226346 100644 --- a/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc +++ b/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc
@@ -143,9 +143,8 @@ link_style.disable_line_wrapping = false; help_label_->AddStyleRange(controller_->HelpTextLinkRange(), link_style); - help_label_->set_background( - views::Background::CreateSolidBackground( - kExplanatoryTextBackgroundColor)); + help_label_->SetBackground( + views::CreateSolidBackground(kExplanatoryTextBackgroundColor)); help_label_->SetBorder(views::CreateEmptyBorder( PasswordGenerationPopupController::kHelpVerticalPadding - kHelpVerticalOffset, @@ -155,7 +154,7 @@ PasswordGenerationPopupController::kHorizontalPadding)); AddChildView(help_label_); - set_background(views::Background::CreateThemedSolidBackground( + SetBackground(views::CreateThemedSolidBackground( this, ui::NativeTheme::kColorId_ResultsTableNormalBackground)); } @@ -211,7 +210,7 @@ if (controller_->password_selected()) NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true); - password_view_->set_background(views::Background::CreateThemedSolidBackground( + password_view_->SetBackground(views::CreateThemedSolidBackground( password_view_, controller_->password_selected() ? ui::NativeTheme::kColorId_ResultsTableHoveredBackground
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views.cc b/chrome/browser/ui/views/autofill/save_card_bubble_views.cc index 68bd769..56566fe2 100644 --- a/chrome/browser/ui/views/autofill/save_card_bubble_views.cc +++ b/chrome/browser/ui/views/autofill/save_card_bubble_views.cc
@@ -234,9 +234,8 @@ std::unique_ptr<views::View> SaveCardBubbleViews::CreateRequestCvcView() { auto request_cvc_view = base::MakeUnique<views::View>(); - request_cvc_view->set_background( - views::Background::CreateThemedSolidBackground( - request_cvc_view.get(), ui::NativeTheme::kColorId_BubbleBackground)); + request_cvc_view->SetBackground(views::CreateThemedSolidBackground( + request_cvc_view.get(), ui::NativeTheme::kColorId_BubbleBackground)); views::BoxLayout* layout = new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, ChromeLayoutProvider::Get()->GetDistanceMetric( @@ -280,7 +279,7 @@ void SaveCardBubbleViews::Init() { SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); view_stack_ = new ViewStack(); - view_stack_->set_background(views::Background::CreateThemedSolidBackground( + view_stack_->SetBackground(views::CreateThemedSolidBackground( view_stack_, ui::NativeTheme::kColorId_BubbleBackground)); view_stack_->Push(CreateMainContentView(), /*animate=*/false); AddChildView(view_stack_);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc index b2cd4c1..7ab33a64 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
@@ -303,8 +303,7 @@ bb_view_->set_owned_by_client(); // Real bookmark bars get a BookmarkBarViewBackground. Set an opaque // background here just to avoid triggering subpixel rendering issues. - bb_view_->set_background( - views::Background::CreateSolidBackground(SK_ColorWHITE)); + bb_view_->SetBackground(views::CreateSolidBackground(SK_ColorWHITE)); bb_view_->SetPageNavigator(&navigator_); AddTestData(CreateBigMenu());
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc index d38df91..875173c 100644 --- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc +++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
@@ -117,8 +117,7 @@ kGenericScreenStyle.item_size.height(), kGenericScreenStyle.item_size.height() * 2); screen_scroll_view->set_hide_horizontal_scrollbar(true); - screen_scroll_view->set_background( - views::Background::CreateSolidBackground(bg_color)); + screen_scroll_view->SetBackground(views::CreateSolidBackground(bg_color)); pane_->AddTab(screen_title_text, screen_scroll_view); pane_->set_listener(this); @@ -148,8 +147,7 @@ window_scroll_view->ClipHeightTo(kWindowStyle.item_size.height(), kWindowStyle.item_size.height() * 2); window_scroll_view->set_hide_horizontal_scrollbar(true); - window_scroll_view->set_background( - views::Background::CreateSolidBackground(bg_color)); + window_scroll_view->SetBackground(views::CreateSolidBackground(bg_color)); pane_->AddTab(window_title_text, window_scroll_view); pane_->set_listener(this); @@ -179,8 +177,7 @@ tab_scroll_view->ClipHeightTo(kTabStyle.item_size.height(), kTabStyle.item_size.height() * 10); tab_scroll_view->set_hide_horizontal_scrollbar(true); - tab_scroll_view->set_background( - views::Background::CreateSolidBackground(bg_color)); + tab_scroll_view->SetBackground(views::CreateSolidBackground(bg_color)); pane_->AddTab(tab_title_text, tab_scroll_view); pane_->set_listener(this);
diff --git a/chrome/browser/ui/views/download/download_shelf_view.cc b/chrome/browser/ui/views/download/download_shelf_view.cc index 541bbdb..12eff65 100644 --- a/chrome/browser/ui/views/download/download_shelf_view.cc +++ b/chrome/browser/ui/views/download/download_shelf_view.cc
@@ -329,7 +329,7 @@ if (show_all_view_) ConfigureButtonForTheme(show_all_view_); - set_background(views::Background::CreateSolidBackground( + SetBackground(views::CreateSolidBackground( GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR))); views::SetImageFromVectorIcon(
diff --git a/chrome/browser/ui/views/extensions/extension_dialog.cc b/chrome/browser/ui/views/extensions/extension_dialog.cc index 6620241..67287457 100644 --- a/chrome/browser/ui/views/extensions/extension_dialog.cc +++ b/chrome/browser/ui/views/extensions/extension_dialog.cc
@@ -89,8 +89,7 @@ // Show a white background while the extension loads. This is prettier than // flashing a black unfilled window frame. - view->set_background( - views::Background::CreateSolidBackground(0xFF, 0xFF, 0xFF)); + view->SetBackground(views::CreateSolidBackground(SK_ColorWHITE)); view->SetVisible(true); // Ensure the DOM JavaScript can respond immediately to keyboard shortcuts. @@ -209,7 +208,7 @@ case extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_FIRST_LOAD: // Avoid potential overdraw by removing the temporary background after // the extension finishes loading. - GetExtensionView(host_.get())->set_background(NULL); + GetExtensionView(host_.get())->SetBackground(nullptr); // The render view is created during the LoadURL(), so we should // set the focus to the view if nobody else takes the focus. if (content::Details<extensions::ExtensionHost>(host()) == details)
diff --git a/chrome/browser/ui/views/find_bar_view.cc b/chrome/browser/ui/views/find_bar_view.cc index 57550b7b..c0a768c 100644 --- a/chrome/browser/ui/views/find_bar_view.cc +++ b/chrome/browser/ui/views/find_bar_view.cc
@@ -405,7 +405,7 @@ auto border = base::MakeUnique<views::BubbleBorder>( views::BubbleBorder::NONE, views::BubbleBorder::SMALL_SHADOW, bg_color); - set_background(new views::BubbleBackground(border.get())); + SetBackground(base::MakeUnique<views::BubbleBackground>(border.get())); SetBorder(std::move(border)); match_count_text_->SetBackgroundColor(bg_color);
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index de4fdc63..e530c8d 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -2100,7 +2100,7 @@ devtools_web_view_->SetVisible(false); contents_container_ = new views::View(); - contents_container_->set_background(views::Background::CreateSolidBackground( + contents_container_->SetBackground(views::CreateSolidBackground( GetThemeProvider()->GetColor(ThemeProperties::COLOR_CONTROL_BACKGROUND))); contents_container_->AddChildView(devtools_web_view_); contents_container_->AddChildView(contents_web_view_); @@ -2208,8 +2208,9 @@ if (!bookmark_bar_view_.get()) { bookmark_bar_view_.reset(new BookmarkBarView(browser_.get(), this)); bookmark_bar_view_->set_owned_by_client(); - bookmark_bar_view_->set_background( - new BookmarkBarViewBackground(this, bookmark_bar_view_.get())); + bookmark_bar_view_->SetBackground( + base::MakeUnique<BookmarkBarViewBackground>(this, + bookmark_bar_view_.get())); bookmark_bar_view_->SetBookmarkBarState( browser_->bookmark_bar_state(), BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
diff --git a/chrome/browser/ui/views/frame/contents_web_view.cc b/chrome/browser/ui/views/frame/contents_web_view.cc index 57f2004..994241a 100644 --- a/chrome/browser/ui/views/frame/contents_web_view.cc +++ b/chrome/browser/ui/views/frame/contents_web_view.cc
@@ -61,12 +61,11 @@ // Make sure the background is opaque. const SkColor ntp_background = color_utils::GetResultingPaintColor( theme->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND), SK_ColorWHITE); - set_background(views::Background::CreateSolidBackground( + SetBackground(views::CreateSolidBackground(SkColorSetARGB( + SkColorGetA(ntp_background), SkColorGetR(ntp_background) * kBackgroundBrightness / 0xFF, SkColorGetG(ntp_background) * kBackgroundBrightness / 0xFF, - SkColorGetB(ntp_background) * kBackgroundBrightness / 0xFF, - SkColorGetA(ntp_background))); - + SkColorGetB(ntp_background) * kBackgroundBrightness / 0xFF))); if (web_contents()) { content::RenderWidgetHostView* rwhv =
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc index 09b9db8..66a01df7 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -469,7 +469,7 @@ button->SetImage(views::CustomButton::STATE_PRESSED, tp->GetImageSkiaNamed(pushed_image_id)); if (browser_view()->IsBrowserTypeNormal()) { - button->SetBackground( + button->SetBackgroundImage( tp->GetColor(ThemeProperties::COLOR_BUTTON_BACKGROUND), tp->GetImageSkiaNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND), tp->GetImageSkiaNamed(mask_image_id));
diff --git a/chrome/browser/ui/views/infobars/infobar_view.cc b/chrome/browser/ui/views/infobars/infobar_view.cc index 2327752..ad0e3e6 100644 --- a/chrome/browser/ui/views/infobars/infobar_view.cc +++ b/chrome/browser/ui/views/infobars/infobar_view.cc
@@ -64,8 +64,8 @@ icon_(nullptr), close_button_(nullptr) { set_owned_by_client(); // InfoBar deletes itself at the appropriate time. - set_background( - new InfoBarBackground(infobars::InfoBar::delegate()->GetInfoBarType())); + SetBackground(base::MakeUnique<InfoBarBackground>( + infobars::InfoBar::delegate()->GetInfoBarType())); SetEventTargeter(base::MakeUnique<views::ViewTargeter>(this)); AddChildView(child_container_); @@ -75,8 +75,8 @@ child_container_->SetPaintToLayer(); child_container_->layer()->SetMasksToBounds(true); - child_container_->set_background(views::Background::CreateSolidBackground( - infobars::InfoBar::GetBackgroundColor( + child_container_->SetBackground( + views::CreateSolidBackground(infobars::InfoBar::GetBackgroundColor( infobars::InfoBar::delegate()->GetInfoBarType()))); }
diff --git a/chrome/browser/ui/views/location_bar/keyword_hint_view.cc b/chrome/browser/ui/views/location_bar/keyword_hint_view.cc index 8d41a703..4e4dcc61 100644 --- a/chrome/browser/ui/views/location_bar/keyword_hint_view.cc +++ b/chrome/browser/ui/views/location_bar/keyword_hint_view.cc
@@ -66,8 +66,8 @@ chip_container_->SetBorder(views::CreateEmptyBorder( gfx::Insets(LocationBarView::kBubbleVerticalPadding, 0))); - chip_container_->set_background( - new BackgroundWith1PxBorder(tab_bg_color, tab_border_color)); + chip_container_->SetBackground(base::MakeUnique<BackgroundWith1PxBorder>( + tab_bg_color, tab_border_color)); chip_container_->AddChildView(chip_label_); chip_container_->SetLayoutManager(new views::FillLayout()); AddChildView(chip_container_);
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc index 066eb09c..3603e71 100644 --- a/chrome/browser/ui/views/location_bar/location_bar_view.cc +++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -195,8 +195,8 @@ new views::Label(base::string16(), {font_list}); ime_inline_autocomplete_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); ime_inline_autocomplete_view_->SetAutoColorReadabilityEnabled(false); - ime_inline_autocomplete_view_->set_background( - views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor( + ime_inline_autocomplete_view_->SetBackground( + views::CreateSolidBackground(GetNativeTheme()->GetSystemColor( ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused))); ime_inline_autocomplete_view_->SetEnabledColor( GetNativeTheme()->GetSystemColor( @@ -587,13 +587,12 @@ void LocationBarView::OnNativeThemeChanged(const ui::NativeTheme* theme) { RefreshLocationIcon(); if (is_popup_mode_) { - set_background( - views::Background::CreateSolidBackground(GetColor(BACKGROUND))); + SetBackground(views::CreateSolidBackground(GetColor(BACKGROUND))); } else { // This border color will be blended on top of the toolbar (which may use an // image in the case of themes). - set_background( - new BackgroundWith1PxBorder(GetColor(BACKGROUND), GetBorderColor())); + SetBackground(base::MakeUnique<BackgroundWith1PxBorder>( + GetColor(BACKGROUND), GetBorderColor())); } SchedulePaint(); }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc index ce33862..5c4cdc2 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -272,10 +272,11 @@ void OmniboxResultView::Invalidate() { const ResultViewState state = GetState(); if (state == NORMAL) { - set_background(nullptr); + SetBackground(nullptr); } else { const SkColor bg_color = GetColor(state, BACKGROUND); - set_background(new BackgroundWith1PxBorder(bg_color, bg_color)); + SetBackground( + base::MakeUnique<BackgroundWith1PxBorder>(bg_color, bg_color)); } // While the text in the RenderTexts may not have changed, the styling
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc index 14ff3d01..ca2e17c 100644 --- a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc +++ b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
@@ -342,8 +342,7 @@ throbber_overlay_.SetVisible(false); // The throbber overlay has to have a solid white background to hide whatever // would be under it. - throbber_overlay_.set_background( - views::Background::CreateSolidBackground(SK_ColorWHITE)); + throbber_overlay_.SetBackground(views::CreateSolidBackground(SK_ColorWHITE)); std::unique_ptr<views::GridLayout> layout = base::MakeUnique<views::GridLayout>(&throbber_overlay_);
diff --git a/chrome/browser/ui/views/payments/payment_request_row_view.cc b/chrome/browser/ui/views/payments/payment_request_row_view.cc index bc138a1..fbe5a09 100644 --- a/chrome/browser/ui/views/payments/payment_request_row_view.cc +++ b/chrome/browser/ui/views/payments/payment_request_row_view.cc
@@ -26,7 +26,7 @@ void PaymentRequestRowView::SetActiveBackground() { ui::NativeTheme* theme = GetWidget()->GetNativeTheme(); - set_background(views::Background::CreateSolidBackground(theme->GetSystemColor( + SetBackground(views::CreateSolidBackground(theme->GetSystemColor( ui::NativeTheme::kColorId_ResultsTableHoveredBackground))); } @@ -46,7 +46,7 @@ SetActiveBackground(); HideBottomSeparator(); } else { - set_background(nullptr); + SetBackground(nullptr); ShowBottomSeparator(); } }
diff --git a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc index d8d31ba2..452bd47 100644 --- a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc +++ b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
@@ -196,7 +196,7 @@ if (GetSheetId(&sheet_id)) view->set_id(static_cast<int>(sheet_id)); - view->set_background(views::Background::CreateSolidBackground(SK_ColorWHITE)); + view->SetBackground(views::CreateSolidBackground(SK_ColorWHITE)); // Paint the sheets to layers, otherwise the MD buttons (which do paint to a // layer) won't do proper clipping. @@ -233,8 +233,7 @@ content_view_ = new views::View; content_view_->SetPaintToLayer(); content_view_->layer()->SetFillsBoundsOpaquely(true); - content_view_->set_background( - views::Background::CreateSolidBackground(SK_ColorWHITE)); + content_view_->SetBackground(views::CreateSolidBackground(SK_ColorWHITE)); pane_layout->AddView(content_view_); pane_->SizeToPreferredSize();
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc index e7535c42..ba4f2141 100644 --- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc +++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -246,11 +246,10 @@ void UpdateColors() { bool is_selected = HasFocus(); - set_background( + SetBackground( is_selected - ? views::Background::CreateSolidBackground( - GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor)) + ? views::CreateSolidBackground(GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor)) : nullptr); SkColor text_color = GetNativeTheme()->GetSystemColor( @@ -630,9 +629,8 @@ void ProfileChooserView::OnNativeThemeChanged( const ui::NativeTheme* native_theme) { views::BubbleDialogDelegateView::OnNativeThemeChanged(native_theme); - set_background(views::Background::CreateSolidBackground( - GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_DialogBackground))); + SetBackground(views::CreateSolidBackground(GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_DialogBackground))); if (auth_error_email_button_) { auth_error_email_button_->SetTextColor( views::LabelButton::STATE_NORMAL, @@ -1381,7 +1379,7 @@ const AvatarMenu::Item& avatar_item) { DCHECK(avatar_item.signed_in); views::View* view = new views::View(); - view->set_background(views::Background::CreateSolidBackground( + view->SetBackground(views::CreateSolidBackground( profiles::kAvatarBubbleAccountsBackgroundColor)); views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth);
diff --git a/chrome/browser/ui/views/sad_tab_view.cc b/chrome/browser/ui/views/sad_tab_view.cc index 710467f..544917a4 100644 --- a/chrome/browser/ui/views/sad_tab_view.cc +++ b/chrome/browser/ui/views/sad_tab_view.cc
@@ -51,7 +51,7 @@ SadTabView::SadTabView(content::WebContents* web_contents, chrome::SadTabKind kind) : SadTab(web_contents, kind) { - set_background(views::Background::CreateThemedSolidBackground( + SetBackground(views::CreateThemedSolidBackground( this, ui::NativeTheme::kColorId_DialogBackground)); views::GridLayout* layout = new views::GridLayout(this);
diff --git a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc index 6bbc7bb..802385a4 100644 --- a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc +++ b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
@@ -192,8 +192,8 @@ widget->Init(params); widget->SetAlwaysOnTop(true); - set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> - GetSystemColor(ui::NativeTheme::kColorId_DialogBackground))); + SetBackground(views::CreateSolidBackground(GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_DialogBackground))); display::Screen* screen = display::Screen::GetScreen(); // TODO(sergeyu): Move the notification to the display being captured when
diff --git a/chrome/browser/ui/views/subtle_notification_view.cc b/chrome/browser/ui/views/subtle_notification_view.cc index c590ddf6..44bc472 100644 --- a/chrome/browser/ui/views/subtle_notification_view.cc +++ b/chrome/browser/ui/views/subtle_notification_view.cc
@@ -144,7 +144,7 @@ std::unique_ptr<views::BubbleBorder> bubble_border(new views::BubbleBorder( views::BubbleBorder::NONE, views::BubbleBorder::NO_ASSETS, kBackgroundColor)); - set_background(new views::BubbleBackground(bubble_border.get())); + SetBackground(base::MakeUnique<views::BubbleBackground>(bubble_border.get())); SetBorder(std::move(bubble_border)); instruction_view_ =
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc index a4d5ad2..cab51a4 100644 --- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc +++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
@@ -178,8 +178,8 @@ 1, 0, 1, 0, ui::GetSigninConfirmationPromptBarColor( GetNativeTheme(), ui::kSigninConfirmationPromptBarBorderAlpha))); - prompt_bar->set_background(views::Background::CreateSolidBackground( - kPromptBarBackgroundColor)); + prompt_bar->SetBackground( + views::CreateSolidBackground(kPromptBarBackgroundColor)); // Create the explanation label. std::vector<size_t> offsets;
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc index 79da62d..386bf97 100644 --- a/chrome/browser/ui/views/toolbar/app_menu.cc +++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -243,7 +243,7 @@ class InMenuButton : public LabelButton { public: InMenuButton(views::ButtonListener* listener, const base::string16& text) - : LabelButton(listener, text), in_menu_background_(NULL) {} + : LabelButton(listener, text) {} ~InMenuButton() override {} void Init(InMenuButtonBackground::ButtonType type) { @@ -252,8 +252,7 @@ SetFocusBehavior(FocusBehavior::ALWAYS); SetHorizontalAlignment(gfx::ALIGN_CENTER); - in_menu_background_ = new InMenuButtonBackground(type); - set_background(in_menu_background_); + SetBackground(base::MakeUnique<InMenuButtonBackground>(type)); SetBorder( views::CreateEmptyBorder(0, kHorizontalPadding, 0, kHorizontalPadding)); label()->SetFontList(MenuConfig::instance().font_list); @@ -287,8 +286,6 @@ } private: - InMenuButtonBackground* in_menu_background_; - DISALLOW_COPY_AND_ASSIGN(InMenuButton); }; @@ -491,9 +488,8 @@ zoom_label_->SetAutoColorReadabilityEnabled(false); zoom_label_->SetHorizontalAlignment(gfx::ALIGN_RIGHT); - InMenuButtonBackground* center_bg = - new InMenuButtonBackground(InMenuButtonBackground::NO_BORDER); - zoom_label_->set_background(center_bg); + zoom_label_->SetBackground(base::MakeUnique<InMenuButtonBackground>( + InMenuButtonBackground::NO_BORDER)); AddChildView(zoom_label_); zoom_label_max_width_valid_ = false; @@ -517,8 +513,8 @@ fullscreen_button_->set_tag(fullscreen_index); fullscreen_button_->SetImageAlignment( ImageButton::ALIGN_CENTER, ImageButton::ALIGN_MIDDLE); - fullscreen_button_->set_background( - new InMenuButtonBackground(InMenuButtonBackground::LEADING_BORDER)); + fullscreen_button_->SetBackground(base::MakeUnique<InMenuButtonBackground>( + InMenuButtonBackground::LEADING_BORDER)); fullscreen_button_->SetAccessibleName(GetAccessibleNameForAppMenuItem( menu_model, fullscreen_index, IDS_ACCNAME_FULLSCREEN)); AddChildView(fullscreen_button_); @@ -1105,7 +1101,7 @@ new ExtensionToolbarMenuView(browser_, this, item)); for (int i = 0; i < extension_toolbar->contents()->child_count(); ++i) { View* action_view = extension_toolbar->contents()->child_at(i); - action_view->set_background(new InMenuButtonBackground( + action_view->SetBackground(base::MakeUnique<InMenuButtonBackground>( InMenuButtonBackground::ROUNDED_BUTTON)); } extension_toolbar_ = extension_toolbar.get();
diff --git a/chrome/browser/ui/views/try_chrome_dialog_view.cc b/chrome/browser/ui/views/try_chrome_dialog_view.cc index 7a361551..ae5022f9 100644 --- a/chrome/browser/ui/views/try_chrome_dialog_view.cc +++ b/chrome/browser/ui/views/try_chrome_dialog_view.cc
@@ -99,8 +99,8 @@ views::View* root_view = popup_->GetRootView(); // The window color is a tiny bit off-white. - root_view->set_background( - views::Background::CreateSolidBackground(0xfc, 0xfc, 0xfc)); + root_view->SetBackground( + views::CreateSolidBackground(SkColorSetRGB(0xfc, 0xfc, 0xfc))); views::GridLayout* layout = views::GridLayout::CreatePanel(root_view); views::ColumnSet* columns;
diff --git a/chrome/browser/ui/webui/help/help_utils_chromeos.cc b/chrome/browser/ui/webui/help/help_utils_chromeos.cc index 1420ac5e..ea4884e 100644 --- a/chrome/browser/ui/webui/help/help_utils_chromeos.cc +++ b/chrome/browser/ui/webui/help/help_utils_chromeos.cc
@@ -13,6 +13,7 @@ #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/grit/generated_resources.h" #include "chromeos/chromeos_switches.h" +#include "chromeos/network/network_state.h" #include "chromeos/network/network_type_pattern.h" #include "chromeos/settings/cros_settings_names.h" #include "third_party/cros_system_api/dbus/service_constants.h" @@ -51,7 +52,9 @@ return false; } -base::string16 GetConnectionTypeAsUTF16(const std::string& type) { +base::string16 GetConnectionTypeAsUTF16(const chromeos::NetworkState* network) { + const std::string type = + network->IsUsingMobileData() ? shill::kTypeCellular : network->type(); if (chromeos::NetworkTypePattern::Ethernet().MatchesType(type)) return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_ETHERNET); if (type == shill::kTypeWifi)
diff --git a/chrome/browser/ui/webui/help/help_utils_chromeos.h b/chrome/browser/ui/webui/help/help_utils_chromeos.h index 1afb8c5..ec388ef 100644 --- a/chrome/browser/ui/webui/help/help_utils_chromeos.h +++ b/chrome/browser/ui/webui/help/help_utils_chromeos.h
@@ -5,10 +5,12 @@ #ifndef CHROME_BROWSER_UI_WEBUI_HELP_HELP_UTILS_CHROMEOS_H_ #define CHROME_BROWSER_UI_WEBUI_HELP_HELP_UTILS_CHROMEOS_H_ -#include <string> - #include "base/strings/string16.h" +namespace chromeos { +class NetworkState; +} + namespace help_utils_chromeos { // Returns true if updates over cellular networks are allowed. If |interactive| @@ -19,7 +21,7 @@ bool IsUpdateOverCellularAllowed(bool interactive); // Returns localized name for the connection |type|. -base::string16 GetConnectionTypeAsUTF16(const std::string& type); +base::string16 GetConnectionTypeAsUTF16(const chromeos::NetworkState* network); } // namespace help_utils_chromeos
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos.cc b/chrome/browser/ui/webui/help/version_updater_chromeos.cc index e5885ce..f5a75e9a 100644 --- a/chrome/browser/ui/webui/help/version_updater_chromeos.cc +++ b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
@@ -55,7 +55,8 @@ if (network->type() == shill::kTypeBluetooth) return NETWORK_STATUS_DISALLOWED; - if (network->type() == shill::kTypeCellular && + // Treats tethered networks as cellular networks. + if (network->IsUsingMobileData() && !help_utils_chromeos::IsUpdateOverCellularAllowed(interactive)) { return NETWORK_STATUS_DISALLOWED; } @@ -99,10 +100,9 @@ l10n_util::GetStringUTF16(IDS_UPGRADE_OFFLINE)); return false; } else if (status == NETWORK_STATUS_DISALLOWED) { - base::string16 message = - l10n_util::GetStringFUTF16( - IDS_UPGRADE_DISALLOWED, - help_utils_chromeos::GetConnectionTypeAsUTF16(network->type())); + base::string16 message = l10n_util::GetStringFUTF16( + IDS_UPGRADE_DISALLOWED, + help_utils_chromeos::GetConnectionTypeAsUTF16(network)); callback.Run(VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED, 0, std::string(), 0, message); return false;
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc index fba7d030c..710dcd91 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc +++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -661,10 +661,17 @@ return printer_backend_proxy_.get(); } -void PrintPreviewHandler::HandleGetPrinters(const base::ListValue* /*args*/) { +void PrintPreviewHandler::HandleGetPrinters(const base::ListValue* args) { VLOG(1) << "Enumerate printers start"; - printer_backend_proxy()->EnumeratePrinters(base::Bind( - &PrintPreviewHandler::SetupPrinterList, weak_factory_.GetWeakPtr())); + std::string callback_id; + CHECK(args->GetString(0, &callback_id)); + CHECK(!callback_id.empty()); + + AllowJavascript(); + + printer_backend_proxy()->EnumeratePrinters( + base::Bind(&PrintPreviewHandler::SetupPrinterList, + weak_factory_.GetWeakPtr(), callback_id)); } void PrintPreviewHandler::HandleGetPrivetPrinters(const base::ListValue* args) { @@ -1325,6 +1332,7 @@ } void PrintPreviewHandler::SetupPrinterList( + const std::string& callback_id, const printing::PrinterList& printer_list) { base::ListValue printers; PrintersToValues(printer_list, &printers); @@ -1337,7 +1345,7 @@ has_logged_printers_count_ = true; } - web_ui()->CallJavascriptFunctionUnsafe("setPrinters", printers); + ResolveJavascriptCallback(base::Value(callback_id), printers); } void PrintPreviewHandler::SendCloudPrintEnabled() {
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h index 2166080..8cee113 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h +++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -242,7 +242,8 @@ std::unique_ptr<base::DictionaryValue> settings_info); // Send the list of printers to the Web UI. - void SetupPrinterList(const printing::PrinterList& printer_list); + void SetupPrinterList(const std::string& callback_id, + const printing::PrinterList& printer_list); // Send whether cloud print integration should be enabled. void SendCloudPrintEnabled();
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc index bad234e..05db7d11 100644 --- a/chrome/browser/ui/webui/settings/about_handler.cc +++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -103,12 +103,12 @@ const chromeos::NetworkState* network = chromeos::NetworkHandler::Get() ->network_state_handler() ->DefaultNetwork(); - const bool cellular = network && network->IsConnectedState() && - network->type() == shill::kTypeCellular; + const bool mobile_data = + network && network->IsConnectedState() && network->IsUsingMobileData(); if (help_utils_chromeos::IsUpdateOverCellularAllowed( true /* interactive */)) { - return cellular + return mobile_data ? l10n_util::GetStringUTF16( IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED_NOT_AUTOMATIC) : l10n_util::GetStringUTF16(
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 5367d52e..45b08d8 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc
@@ -940,6 +940,9 @@ // Custom crosh command. const char kCroshCommand[] = "crosh-command"; +// Disables logging redirect for testing. +const char kDisableLoggingRedirect[] = "disable-logging-redirect"; + // Disables apps on the login screen. By default, they are allowed and can be // installed through policy. const char kDisableLoginScreenApps[] = "disable-login-screen-apps";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index d983d8c8..03043e8 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h
@@ -273,6 +273,7 @@ #if defined(OS_CHROMEOS) extern const char kCroshCommand[]; +extern const char kDisableLoggingRedirect[]; extern const char kDisableLoginScreenApps[]; extern const char kDisableNativeCups[]; #endif // defined(OS_CHROMEOS)
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index 8ce6a83..12d7c45 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h
@@ -55,7 +55,9 @@ kOutdatedBlocked, kOutdatedDisallowed, kPlayImportantContent, +#if defined(OS_LINUX) kRestartRequired, +#endif kUnauthorized, };
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index 95f3aa6..55acb6a 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -956,6 +956,7 @@ identifier)); break; } +#if defined(OS_LINUX) case ChromeViewHostMsg_GetPluginInfo_Status::kRestartRequired: { placeholder = create_blocked_plugin( IDR_BLOCKED_PLUGIN_HTML, @@ -963,6 +964,7 @@ group_name)); break; } +#endif } } placeholder->SetStatus(status);
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.js b/chrome/test/data/webui/print_preview/native_layer_stub.js index d16b11b..92f9f29 100644 --- a/chrome/test/data/webui/print_preview/native_layer_stub.js +++ b/chrome/test/data/webui/print_preview/native_layer_stub.js
@@ -10,7 +10,7 @@ */ function NativeLayerStub() { settings.TestBrowserProxy.call(this, [ - 'getInitialSettings', 'setupPrinter' ]); + 'getInitialSettings', 'getPrinters', 'setupPrinter' ]); /** * @private {!cr.EventTarget} The event target used for dispatching and @@ -34,6 +34,13 @@ this.initialSettings_ = null; /** + * + * @private {!Array<!print_preview.LocalDestinationInfo>} Local destination + * list to be used for the response to |getPrinters|. + */ + this.localDestinationInfos_ = []; + + /** * @private {!print_preview.PrinterSetupResponse} The response to be sent * on a |setupPrinter| call. */ @@ -67,6 +74,12 @@ }, /** @override */ + getPrinters: function() { + this.methodCalled('getPrinters'); + return Promise.resolve(this.localDestinationInfos_); + }, + + /** @override */ setupPrinter: function(printerId) { this.methodCalled('setupPrinter', printerId); return this.shouldRejectPrinterSetup_ ? @@ -132,6 +145,14 @@ }, /** + * @param {!Array<!print_preview.LocalDestinationInfo>} localDestinations + * The local destinations to return as a response to |getPrinters|. + */ + setLocalDestinations: function(localDestinations) { + this.localDestinationInfos_ = localDestinations; + }, + + /** * @param {boolean} reject Whether printSetup requests should be rejected. * @param {!print_preview.PrinterSetupResponse} The response to send when * |setupPrinter| is called.
diff --git a/chrome/test/data/webui/print_preview/print_preview_tests.js b/chrome/test/data/webui/print_preview/print_preview_tests.js index c7277f5..e70b7c3 100644 --- a/chrome/test/data/webui/print_preview/print_preview_tests.js +++ b/chrome/test/data/webui/print_preview/print_preview_tests.js
@@ -32,8 +32,7 @@ /** * Initialize print preview with the initial settings currently stored in - * |initialSettings|. Creates |printPreview| if it does not - * already exist. + * |initialSettings|. */ function setInitialSettings() { nativeLayer.setInitialSettings(initialSettings); @@ -41,14 +40,29 @@ } /** - * Dispatch the LOCAL_DESTINATIONS_SET event. This call is NOT async and will - * happen in the same thread. + * Start loading the local destinations using the destination infos currently + * stored in |localDestinationInfos|. */ function setLocalDestinations() { - var localDestsSetEvent = - new Event(print_preview.NativeLayer.EventType.LOCAL_DESTINATIONS_SET); - localDestsSetEvent.destinationInfos = localDestinationInfos; - nativeLayer.getEventTarget().dispatchEvent(localDestsSetEvent); + nativeLayer.setLocalDestinations(localDestinationInfos); + printPreview.destinationStore_.startLoadLocalDestinations(); + } + + /** + * Initializes print preview with the initial settings currently stored in + * |initialSettings|, waits for the getInitialSettings promise to resolve, + * and loads local destinations using destination infos currently stored in + * |localDestinationInfos|. + * @return {!Promise<!Array<!print_preview.LocalDestinationInfo>>} a + * promise that will resolve when getPrinters has been resolved by + * the native layer stub. + */ + function setupSettingsAndDestinations() { + setInitialSettings(); + return nativeLayer.whenCalled('getInitialSettings').then(function() { + setLocalDestinations(); + return nativeLayer.whenCalled('getPrinters'); + }); } /** @@ -196,16 +210,25 @@ /** * Repeated setup steps for the advanced settings tests. - * Sets initial settings, and verifies advanced options section is visible - * after expanding more settings. + * Sets capabilities, and verifies advanced options section is visible + * after expanding more settings. Then opens the advanced settings overlay + * and verifies it is displayed. */ - function setupAdvancedSettingsTest(device) { - setLocalDestinations(); + function startAdvancedSettingsTest(device) { setCapabilities(device); expandMoreSettings(); // Check that the advanced options settings section is visible. checkSectionVisible($('advanced-options-settings'), true); + + // Open the advanced settings overlay. + openAdvancedSettings(); + + // Check advanced settings overlay is visible by checking that the close + // button is displayed. + var advancedSettingsCloseButton = $('advanced-settings'). + querySelector('.close-button'); + checkElementDisplayed(advancedSettingsCloseButton, true); } /** @return {boolean} */ @@ -262,92 +285,84 @@ // Test some basic assumptions about the print preview WebUI. test('PrinterList', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then( - function() { - setLocalDestinations(); - var recentList = - $('destination-search').querySelector('.recent-list ul'); - var localList = - $('destination-search').querySelector('.local-list ul'); - assertNotEquals(null, recentList); - assertEquals(1, recentList.childNodes.length); - assertEquals('FooName', - recentList.childNodes.item(0).querySelector( - '.destination-list-item-name').textContent); - assertNotEquals(null, localList); - assertEquals(3, localList.childNodes.length); - assertEquals( - 'Save as PDF', - localList.childNodes.item(PDF_INDEX). - querySelector('.destination-list-item-name').textContent); - assertEquals( - 'FooName', - localList.childNodes.item(FOO_INDEX). - querySelector('.destination-list-item-name').textContent); - assertEquals( - 'BarName', - localList.childNodes.item(BAR_INDEX). - querySelector('.destination-list-item-name').textContent); - }); + return setupSettingsAndDestinations().then(function() { + var recentList = + $('destination-search').querySelector('.recent-list ul'); + var localList = + $('destination-search').querySelector('.local-list ul'); + assertNotEquals(null, recentList); + assertEquals(1, recentList.childNodes.length); + assertEquals('FooName', + recentList.childNodes.item(0).querySelector( + '.destination-list-item-name').textContent); + assertNotEquals(null, localList); + assertEquals(3, localList.childNodes.length); + assertEquals( + 'Save as PDF', + localList.childNodes.item(PDF_INDEX). + querySelector('.destination-list-item-name').textContent); + assertEquals( + 'FooName', + localList.childNodes.item(FOO_INDEX). + querySelector('.destination-list-item-name').textContent); + assertEquals( + 'BarName', + localList.childNodes.item(BAR_INDEX). + querySelector('.destination-list-item-name').textContent); + }); }); // Test that the printer list is structured correctly after calling // addCloudPrinters with an empty list. test('PrinterListCloudEmpty', function() { - setInitialSettings(); + return setupSettingsAndDestinations().then(function() { + var cloudPrintEnableEvent = new Event( + print_preview.NativeLayer.EventType.CLOUD_PRINT_ENABLE); + cloudPrintEnableEvent.baseCloudPrintUrl = 'cloudprint url'; + nativeLayer.getEventTarget().dispatchEvent( + cloudPrintEnableEvent); - return nativeLayer.whenCalled('getInitialSettings').then( - function() { - setLocalDestinations(); + var searchDoneEvent = + new Event(cloudprint.CloudPrintInterfaceEventType.SEARCH_DONE); + searchDoneEvent.printers = []; + searchDoneEvent.isRecent = true; + searchDoneEvent.email = 'foo@chromium.org'; + printPreview.cloudPrintInterface_.dispatchEvent(searchDoneEvent); - var cloudPrintEnableEvent = new Event( - print_preview.NativeLayer.EventType.CLOUD_PRINT_ENABLE); - cloudPrintEnableEvent.baseCloudPrintUrl = 'cloudprint url'; - nativeLayer.getEventTarget().dispatchEvent( - cloudPrintEnableEvent); + var recentList = + $('destination-search').querySelector('.recent-list ul'); + var localList = + $('destination-search').querySelector('.local-list ul'); + var cloudList = + $('destination-search').querySelector('.cloud-list ul'); - var searchDoneEvent = - new Event(cloudprint.CloudPrintInterfaceEventType.SEARCH_DONE); - searchDoneEvent.printers = []; - searchDoneEvent.isRecent = true; - searchDoneEvent.email = 'foo@chromium.org'; - printPreview.cloudPrintInterface_.dispatchEvent(searchDoneEvent); + assertNotEquals(null, recentList); + assertEquals(1, recentList.childNodes.length); + assertEquals('FooName', + recentList.childNodes.item(0). + querySelector('.destination-list-item-name'). + textContent); - var recentList = - $('destination-search').querySelector('.recent-list ul'); - var localList = - $('destination-search').querySelector('.local-list ul'); - var cloudList = - $('destination-search').querySelector('.cloud-list ul'); + assertNotEquals(null, localList); + assertEquals(3, localList.childNodes.length); + assertEquals('Save as PDF', + localList.childNodes.item(PDF_INDEX). + querySelector('.destination-list-item-name'). + textContent); + assertEquals('FooName', + localList.childNodes. + item(FOO_INDEX). + querySelector('.destination-list-item-name'). + textContent); + assertEquals('BarName', + localList.childNodes. + item(BAR_INDEX). + querySelector('.destination-list-item-name'). + textContent); - assertNotEquals(null, recentList); - assertEquals(1, recentList.childNodes.length); - assertEquals('FooName', - recentList.childNodes.item(0). - querySelector('.destination-list-item-name'). - textContent); - - assertNotEquals(null, localList); - assertEquals(3, localList.childNodes.length); - assertEquals('Save as PDF', - localList.childNodes.item(PDF_INDEX). - querySelector('.destination-list-item-name'). - textContent); - assertEquals('FooName', - localList.childNodes. - item(FOO_INDEX). - querySelector('.destination-list-item-name'). - textContent); - assertEquals('BarName', - localList.childNodes. - item(BAR_INDEX). - querySelector('.destination-list-item-name'). - textContent); - - assertNotEquals(null, cloudList); - assertEquals(0, cloudList.childNodes.length); - }); + assertNotEquals(null, cloudList); + assertEquals(0, cloudList.childNodes.length); + }); }); // Test restore settings with one destination. @@ -446,14 +461,16 @@ // It also makes sure these rules do override system default destination. initialSettings.serializedDefaultDestinationSelectionRulesStr_ = JSON.stringify({namePattern: '.*Bar.*'}); + // Set this early as the app state selection string will trigger a load + // of local destinations on initialization. + nativeLayer.setLocalDestinations(localDestinationInfos); setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then( - function() { - setLocalDestinations(); - assertEquals( - 'BarDevice', - printPreview.destinationStore_.selectedDestination.id); - }); + return nativeLayer.whenCalled('getInitialSettings').then(function() { + return nativeLayer.whenCalled('getPrinters').then(function() { + assertEquals('BarDevice', + printPreview.destinationStore_.selectedDestination.id); + }); + }); }); test('SystemDialogLinkIsHiddenInAppKioskMode', function() { @@ -475,23 +492,20 @@ checkSectionVisible($('color-settings'), false); checkSectionVisible($('copies-settings'), false); - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then( - function() { - setLocalDestinations(); - var device = getCddTemplate('FooDevice'); - device.capabilities.printer.color = { - option: [{is_default: true, type: 'STANDARD_COLOR'}] - }; - delete device.capabilities.printer.copies; - setCapabilities(device); + return setupSettingsAndDestinations().then(function() { + var device = getCddTemplate('FooDevice'); + device.capabilities.printer.color = { + option: [{is_default: true, type: 'STANDARD_COLOR'}] + }; + delete device.capabilities.printer.copies; + setCapabilities(device); - checkSectionVisible($('layout-settings'), true); - checkSectionVisible($('color-settings'), false); - checkSectionVisible($('copies-settings'), false); + checkSectionVisible($('layout-settings'), true); + checkSectionVisible($('color-settings'), false); + checkSectionVisible($('copies-settings'), false); - return whenAnimationDone('other-options-collapsible'); - }); + return whenAnimationDone('other-options-collapsible'); + }); }); // When the source is 'PDF' and 'Save as PDF' option is selected, we hide @@ -533,10 +547,9 @@ } }; setCapabilities(device); - var otherOptions = $('other-options-settings'); - // If rasterization is an option, other options should be visible. If - // not, there should be no available other options. + // If rasterization is an option, other options should be visible. + // If not, there should be no available other options. checkSectionVisible(otherOptions, isPrintAsImageEnabled()); if (isPrintAsImageEnabled()) { checkElementDisplayed( @@ -552,9 +565,7 @@ // When the source is 'HTML', we always hide the fit to page option and show // media size option. test('SourceIsHTMLCapabilities', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); + return setupSettingsAndDestinations().then(function() { setCapabilities(getCddTemplate('FooDevice')); var otherOptions = $('other-options-settings'); @@ -565,8 +576,8 @@ var mediaSize = $('media-size-settings'); var scalingSettings = $('scaling-settings'); - // Check that options are collapsed (section is visible, because duplex - // is available). + // Check that options are collapsed (section is visible, because + // duplex is available). checkSectionVisible(otherOptions, true); checkElementDisplayed(fitToPage, false); if (isPrintAsImageEnabled()) @@ -590,89 +601,81 @@ // we show/hide the fit to page option and hide media size selection. test('SourceIsPDFCapabilities', function() { initialSettings.isDocumentModifiable_ = false; - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then( - function() { - setLocalDestinations(); - setCapabilities(getCddTemplate('FooDevice')); + return setupSettingsAndDestinations().then(function() { + setCapabilities(getCddTemplate('FooDevice')); - var otherOptions = $('other-options-settings'); - var scalingSettings = $('scaling-settings'); - var fitToPageContainer = - otherOptions.querySelector('#fit-to-page-container'); - var rasterizeContainer; - if (isPrintAsImageEnabled()) { - rasterizeContainer = - otherOptions.querySelector('#rasterize-container'); - } + var otherOptions = $('other-options-settings'); + var scalingSettings = $('scaling-settings'); + var fitToPageContainer = + otherOptions.querySelector('#fit-to-page-container'); + var rasterizeContainer; + if (isPrintAsImageEnabled()) { + rasterizeContainer = + otherOptions.querySelector('#rasterize-container'); + } - checkSectionVisible(otherOptions, true); - checkElementDisplayed(fitToPageContainer, true); - if (isPrintAsImageEnabled()) - checkElementDisplayed(rasterizeContainer, false); - expectTrue( - fitToPageContainer.querySelector('.checkbox').checked); - expandMoreSettings(); - if (isPrintAsImageEnabled()) { - checkElementDisplayed(rasterizeContainer, true); - expectFalse( - rasterizeContainer.querySelector('.checkbox').checked); - } - checkSectionVisible($('media-size-settings'), true); - checkSectionVisible(scalingSettings, true); + checkSectionVisible(otherOptions, true); + checkElementDisplayed(fitToPageContainer, true); + if (isPrintAsImageEnabled()) + checkElementDisplayed(rasterizeContainer, false); + expectTrue( + fitToPageContainer.querySelector('.checkbox').checked); + expandMoreSettings(); + if (isPrintAsImageEnabled()) { + checkElementDisplayed(rasterizeContainer, true); + expectFalse( + rasterizeContainer.querySelector('.checkbox').checked); + } + checkSectionVisible($('media-size-settings'), true); + checkSectionVisible(scalingSettings, true); - return whenAnimationDone('other-options-collapsible'); - }); + return whenAnimationDone('other-options-collapsible'); + }); }); // When the source is 'PDF', depending on the selected destination printer, // we show/hide the fit to page option and hide media size selection. test('ScalingUnchecksFitToPage', function() { initialSettings.isDocumentModifiable_ = false; - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then( - function() { - setLocalDestinations(); - setCapabilities(getCddTemplate('FooDevice')); + return setupSettingsAndDestinations().then(function() { + setCapabilities(getCddTemplate('FooDevice')); - var otherOptions = $('other-options-settings'); - var scalingSettings = $('scaling-settings'); + var otherOptions = $('other-options-settings'); + var scalingSettings = $('scaling-settings'); - checkSectionVisible(otherOptions, true); - var fitToPageContainer = - otherOptions.querySelector('#fit-to-page-container'); - checkElementDisplayed(fitToPageContainer, true); - expectTrue( - fitToPageContainer.querySelector('.checkbox').checked); - expandMoreSettings(); - checkSectionVisible($('media-size-settings'), true); - checkSectionVisible(scalingSettings, true); + checkSectionVisible(otherOptions, true); + var fitToPageContainer = + otherOptions.querySelector('#fit-to-page-container'); + checkElementDisplayed(fitToPageContainer, true); + expectTrue( + fitToPageContainer.querySelector('.checkbox').checked); + expandMoreSettings(); + checkSectionVisible($('media-size-settings'), true); + checkSectionVisible(scalingSettings, true); - // Change scaling input - var scalingInput = scalingSettings.querySelector('.user-value'); - expectEquals('100', scalingInput.value); - scalingInput.stepUp(5); - expectEquals('105', scalingInput.value); + // Change scaling input + var scalingInput = scalingSettings.querySelector('.user-value'); + expectEquals('100', scalingInput.value); + scalingInput.stepUp(5); + expectEquals('105', scalingInput.value); - // Trigger the event - var enterEvent = document.createEvent('Event'); - enterEvent.initEvent('keydown'); - enterEvent.keyCode = 'Enter'; - scalingInput.dispatchEvent(enterEvent); - expectFalse( - fitToPageContainer.querySelector('.checkbox').checked); + // Trigger the event + var enterEvent = document.createEvent('Event'); + enterEvent.initEvent('keydown'); + enterEvent.keyCode = 'Enter'; + scalingInput.dispatchEvent(enterEvent); + expectFalse( + fitToPageContainer.querySelector('.checkbox').checked); - return whenAnimationDone('other-options-collapsible'); - }); + return whenAnimationDone('other-options-collapsible'); + }); }); // When the number of copies print preset is set for source 'PDF', we update // the copies value if capability is supported by printer. test('CheckNumCopiesPrintPreset', function() { initialSettings.isDocumentModifiable_ = false; - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); + return setupSettingsAndDestinations().then(function() { setCapabilities(getCddTemplate('FooDevice')); // Indicate that the number of copies print preset is set for source @@ -690,7 +693,8 @@ checkSectionVisible($('copies-settings'), true); expectEquals( printPresetOptions.copies, - parseInt($('copies-settings').querySelector('.user-value').value)); + parseInt($('copies-settings'). + querySelector('.user-value').value)); return whenAnimationDone('other-options-collapsible'); }); @@ -700,95 +704,83 @@ // duplex setting if capability is supported by printer. test('CheckDuplexPrintPreset', function() { initialSettings.isDocumentModifiable_ = false; - setInitialSettings(); + return setupSettingsAndDestinations().then(function() { + setCapabilities(getCddTemplate('FooDevice')); - return nativeLayer.whenCalled('getInitialSettings').then( - function() { - setLocalDestinations(); - setCapabilities(getCddTemplate('FooDevice')); + // Indicate that the duplex print preset is set to 'long edge' for + // source PDF. + var printPresetOptions = { + duplex: 1 + }; + var printPresetOptionsEvent = new Event( + print_preview.NativeLayer.EventType.PRINT_PRESET_OPTIONS); + printPresetOptionsEvent.optionsFromDocument = printPresetOptions; + nativeLayer.getEventTarget(). + dispatchEvent(printPresetOptionsEvent); - // Indicate that the duplex print preset is set to 'long edge' for - // source PDF. - var printPresetOptions = { - duplex: 1 - }; - var printPresetOptionsEvent = new Event( - print_preview.NativeLayer.EventType.PRINT_PRESET_OPTIONS); - printPresetOptionsEvent.optionsFromDocument = printPresetOptions; - nativeLayer.getEventTarget(). - dispatchEvent(printPresetOptionsEvent); + var otherOptions = $('other-options-settings'); + checkSectionVisible(otherOptions, true); + var duplexContainer = + otherOptions.querySelector('#duplex-container'); + checkElementDisplayed(duplexContainer, true); + expectTrue(duplexContainer.querySelector('.checkbox').checked); - var otherOptions = $('other-options-settings'); - checkSectionVisible(otherOptions, true); - var duplexContainer = - otherOptions.querySelector('#duplex-container'); - checkElementDisplayed(duplexContainer, true); - expectTrue(duplexContainer.querySelector('.checkbox').checked); - - return whenAnimationDone('other-options-collapsible'); - }); + return whenAnimationDone('other-options-collapsible'); + }); }); // Make sure that custom margins controls are properly set up. test('CustomMarginsControlsCheck', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then( - function() { - setLocalDestinations(); - setCapabilities(getCddTemplate('FooDevice')); + return setupSettingsAndDestinations().then(function() { + setCapabilities(getCddTemplate('FooDevice')); - printPreview.printTicketStore_.marginsType.updateValue( - print_preview.ticket_items.MarginsTypeValue.CUSTOM); + printPreview.printTicketStore_.marginsType.updateValue( + print_preview.ticket_items.MarginsTypeValue.CUSTOM); - ['left', 'top', 'right', 'bottom'].forEach(function(margin) { - var control = - $('preview-area').querySelector('.margin-control-' + margin); - assertNotEquals(null, control); - var input = control.querySelector('.margin-control-textbox'); - assertTrue(input.hasAttribute('aria-label')); - assertNotEquals('undefined', input.getAttribute('aria-label')); - }); - return whenAnimationDone('more-settings'); - }); + ['left', 'top', 'right', 'bottom'].forEach(function(margin) { + var control = + $('preview-area').querySelector('.margin-control-' + margin); + assertNotEquals(null, control); + var input = control.querySelector('.margin-control-textbox'); + assertTrue(input.hasAttribute('aria-label')); + assertNotEquals('undefined', input.getAttribute('aria-label')); + }); + return whenAnimationDone('more-settings'); + }); }); // Page layout has zero margins. Hide header and footer option. test('PageLayoutHasNoMarginsHideHeaderFooter', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then( - function() { - setLocalDestinations(); - setCapabilities(getCddTemplate('FooDevice')); + return setupSettingsAndDestinations().then(function() { + setCapabilities(getCddTemplate('FooDevice')); - var otherOptions = $('other-options-settings'); - var headerFooter = - otherOptions.querySelector('#header-footer-container'); + var otherOptions = $('other-options-settings'); + var headerFooter = + otherOptions.querySelector('#header-footer-container'); - // Check that options are collapsed (section is visible, because - // duplex is available). - checkSectionVisible(otherOptions, true); - checkElementDisplayed(headerFooter, false); + // Check that options are collapsed (section is visible, because + // duplex is available). + checkSectionVisible(otherOptions, true); + checkElementDisplayed(headerFooter, false); - expandMoreSettings(); + expandMoreSettings(); - checkElementDisplayed(headerFooter, true); + checkElementDisplayed(headerFooter, true); - printPreview.printTicketStore_.marginsType.updateValue( - print_preview.ticket_items.MarginsTypeValue.CUSTOM); - printPreview.printTicketStore_.customMargins.updateValue( - new print_preview.Margins(0, 0, 0, 0)); + printPreview.printTicketStore_.marginsType.updateValue( + print_preview.ticket_items.MarginsTypeValue.CUSTOM); + printPreview.printTicketStore_.customMargins.updateValue( + new print_preview.Margins(0, 0, 0, 0)); - checkElementDisplayed(headerFooter, false); + checkElementDisplayed(headerFooter, false); - return whenAnimationDone('more-settings'); - }); + return whenAnimationDone('more-settings'); + }); }); // Page layout has half-inch margins. Show header and footer option. test('PageLayoutHasMarginsShowHeaderFooter', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); + return setupSettingsAndDestinations().then(function() { setCapabilities(getCddTemplate('FooDevice')); var otherOptions = $('other-options-settings'); @@ -815,21 +807,18 @@ }); }); - // Page layout has zero top and bottom margins. Hide header and footer // option. test('ZeroTopAndBottomMarginsHideHeaderFooter', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); + return setupSettingsAndDestinations().then(function() { setCapabilities(getCddTemplate('FooDevice')); var otherOptions = $('other-options-settings'); var headerFooter = otherOptions.querySelector('#header-footer-container'); - // Check that options are collapsed (section is visible, because duplex - // is available). + // Check that options are collapsed (section is visible, because + // duplex is available). checkSectionVisible(otherOptions, true); checkElementDisplayed(headerFooter, false); @@ -851,17 +840,15 @@ // Page layout has zero top and half-inch bottom margin. Show header and // footer option. test('ZeroTopAndNonZeroBottomMarginShowHeaderFooter', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); + return setupSettingsAndDestinations().then(function() { setCapabilities(getCddTemplate('FooDevice')); var otherOptions = $('other-options-settings'); var headerFooter = otherOptions.querySelector('#header-footer-container'); - // Check that options are collapsed (section is visible, because duplex - // is available). + // Check that options are collapsed (section is visible, because + // duplex is available). checkSectionVisible(otherOptions, true); checkElementDisplayed(headerFooter, false); @@ -882,9 +869,7 @@ // Check header footer availability with small (label) page size. test('SmallPaperSizeHeaderFooter', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); + return setupSettingsAndDestinations().then(function() { var device = getCddTemplate('FooDevice'); device.capabilities.printer.media_size = { 'option': [ @@ -900,8 +885,8 @@ var headerFooter = otherOptions.querySelector('#header-footer-container'); - // Check that options are collapsed (section is visible, because duplex - // is available). + // Check that options are collapsed (section is visible, because + // duplex is available). checkSectionVisible(otherOptions, true); checkElementDisplayed(headerFooter, false); @@ -926,10 +911,7 @@ // Test that the color settings, one option, standard monochrome. test('ColorSettingsMonochrome', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); - + return setupSettingsAndDestinations().then(function() { // Only one option, standard monochrome. var device = getCddTemplate('FooDevice'); device.capabilities.printer.color = { @@ -947,10 +929,7 @@ // Test that the color settings, one option, custom monochrome. test('ColorSettingsCustomMonochrome', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); - + return setupSettingsAndDestinations().then(function() { // Only one option, standard monochrome. var device = getCddTemplate('FooDevice'); device.capabilities.printer.color = { @@ -969,10 +948,7 @@ // Test that the color settings, one option, standard color. test('ColorSettingsColor', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); - + return setupSettingsAndDestinations().then(function() { var device = getCddTemplate('FooDevice'); device.capabilities.printer.color = { 'option': [ @@ -989,10 +965,7 @@ // Test that the color settings, one option, custom color. test('ColorSettingsCustomColor', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); - + return setupSettingsAndDestinations().then(function() { var device = getCddTemplate('FooDevice'); device.capabilities.printer.color = { 'option': [ @@ -1010,10 +983,7 @@ // Test that the color settings, two options, both standard, defaults to // color. test('ColorSettingsBothStandardDefaultColor', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); - + return setupSettingsAndDestinations().then(function() { var device = getCddTemplate('FooDevice'); device.capabilities.printer.color = { 'option': [ @@ -1026,7 +996,8 @@ checkSectionVisible($('color-settings'), true); expectEquals( 'color', - $('color-settings').querySelector('.color-settings-select').value); + $('color-settings').querySelector( + '.color-settings-select').value); return whenAnimationDone('more-settings'); }); @@ -1035,10 +1006,7 @@ // Test that the color settings, two options, both standard, defaults to // monochrome. test('ColorSettingsBothStandardDefaultMonochrome', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); - + return setupSettingsAndDestinations().then(function() { var device = getCddTemplate('FooDevice'); device.capabilities.printer.color = { 'option': [ @@ -1051,7 +1019,8 @@ checkSectionVisible($('color-settings'), true); expectEquals( 'bw', - $('color-settings').querySelector('.color-settings-select').value); + $('color-settings').querySelector( + '.color-settings-select').value); return whenAnimationDone('more-settings'); }); @@ -1060,10 +1029,7 @@ // Test that the color settings, two options, both custom, defaults to // color. test('ColorSettingsBothCustomDefaultColor', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); - + return setupSettingsAndDestinations().then(function() { var device = getCddTemplate('FooDevice'); device.capabilities.printer.color = { 'option': [ @@ -1086,9 +1052,7 @@ // Test to verify that duplex settings are set according to the printer // capabilities. test('DuplexSettingsTrue', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); + return setupSettingsAndDestinations().then(function() { setCapabilities(getCddTemplate('FooDevice')); var otherOptions = $('other-options-settings'); @@ -1104,9 +1068,7 @@ // Test to verify that duplex settings are set according to the printer // capabilities. test('DuplexSettingsFalse', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); + return setupSettingsAndDestinations().then(function() { var device = getCddTemplate('FooDevice'); delete device.capabilities.printer.duplex; setCapabilities(device); @@ -1127,9 +1089,7 @@ // Test that changing the selected printer updates the preview. test('PrinterChangeUpdatesPreview', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); + return setupSettingsAndDestinations().then(function() { setCapabilities(getCddTemplate('FooDevice')); var previewGenerator = mock(print_preview.PreviewGenerator); @@ -1188,10 +1148,7 @@ // Test custom localized paper names. test('CustomPaperNames', function() { - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); - + return setupSettingsAndDestinations().then(function() { var customLocalizedMediaName = 'Vendor defined localized media name'; var customMediaName = 'Vendor defined media name'; @@ -1240,20 +1197,10 @@ // search box). test('AdvancedSettings1Option', function() { var device = getCddTemplateWithAdvancedSettings('FooDevice'); - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setupAdvancedSettingsTest(device); - - // Open the advanced settings overlay. - openAdvancedSettings(); - - // Check that advanced settings close button is now visible, - // but not the search box (only 1 capability). - var advancedSettingsCloseButton = $('advanced-settings'). - querySelector('.close-button'); - checkElementDisplayed(advancedSettingsCloseButton, true); + return setupSettingsAndDestinations().then(function() { + startAdvancedSettingsTest(device); checkElementDisplayed($('advanced-settings'). - querySelector('.search-box-area'), false); + querySelector('.search-box-area'), false); return whenAnimationDone('more-settings'); }); @@ -1277,20 +1224,11 @@ ] } }); - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setupAdvancedSettingsTest(device); + return setupSettingsAndDestinations().then(function() { + startAdvancedSettingsTest(device); - // Open the advanced settings overlay. - openAdvancedSettings(); - - // Check advanced settings is visible and that the search box now - // appears. - var advancedSettingsCloseButton = $('advanced-settings'). - querySelector('.close-button'); - checkElementDisplayed(advancedSettingsCloseButton, true); checkElementDisplayed($('advanced-settings'). - querySelector('.search-box-area'), true); + querySelector('.search-box-area'), true); return whenAnimationDone('more-settings'); }); @@ -1336,10 +1274,7 @@ // an error and that the preview dialog can be recovered by selecting a // new destination. test('InvalidSettingsError', function() { - // Setup - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); + return setupSettingsAndDestinations().then(function() { setCapabilities(getCddTemplate('FooDevice')); // Manually enable the print header. This is needed since there is no @@ -1347,7 +1282,8 @@ printPreview.printHeader_.isEnabled = true; // There will be an error message in the preview area since the plugin - // is not running. However, it should not be the invalid settings error. + // is not running. However, it should not be the invalid settings + // error. var previewAreaEl = $('preview-area'); var customMessageEl = previewAreaEl. @@ -1355,7 +1291,8 @@ expectFalse(customMessageEl.hidden); var expectedMessageStart = 'The selected printer is not available or ' + 'not installed correctly.' - expectFalse(customMessageEl.textContent.includes(expectedMessageStart)); + expectFalse(customMessageEl.textContent.includes( + expectedMessageStart)); // Verify that the print button is enabled. var printHeader = $('print-header'); @@ -1371,7 +1308,8 @@ // Should be in an error state, print button disabled, invalid custom // error message shown. expectFalse(customMessageEl.hidden); - expectTrue(customMessageEl.textContent.includes(expectedMessageStart)); + expectTrue(customMessageEl.textContent.includes( + expectedMessageStart)); expectTrue(printButton.disabled); // Select a new destination @@ -1383,8 +1321,8 @@ printPreview.destinationStore_.selectDestination(barDestination); - // Dispatch events indicating capabilities were fetched and new preview - // has loaded. + // Dispatch events indicating capabilities were fetched and new + // preview has loaded. setCapabilities(getCddTemplate('BarDevice')); var previewDoneEvent = new Event( print_preview.PreviewArea.EventType.PREVIEW_GENERATION_DONE); @@ -1407,10 +1345,7 @@ new print_preview.PreviewGenerator(printPreview.destinationStore_, printPreview.printTicketStore_, nativeLayer, printPreview.documentInfo_); - - setInitialSettings(); - return nativeLayer.whenCalled('getInitialSettings').then(function() { - setLocalDestinations(); + return setupSettingsAndDestinations().then(function() { setCapabilities(getCddTemplate('FooDevice')); // The first request should generate draft because there was no
diff --git a/chrome/test/data/webui/settings/site_details_tests.js b/chrome/test/data/webui/settings/site_details_tests.js index db9e3de..732be78 100644 --- a/chrome/test/data/webui/settings/site_details_tests.js +++ b/chrome/test/data/webui/settings/site_details_tests.js
@@ -55,6 +55,14 @@ source: 'preference', }, ], + images: [ + { + embeddingOrigin: 'https://foo-allow.com:443', + origin: 'https://foo-allow.com:443', + setting: 'allow', + source: 'preference', + }, + ], javascript: [ { embeddingOrigin: 'https://foo-allow.com:443',
diff --git a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js index ba07822..97633e75b 100644 --- a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js +++ b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
@@ -15,6 +15,7 @@ * midiDevices: !Array<!RawSiteException>}, * notifications: !Array<!RawSiteException>}, * plugins: !Array<!RawSiteException>}, + * images: !Array<!RawSiteException>}, * popups: !Array<!RawSiteException>}, * unsandboxed_plugins: !Array<!RawSiteException>}, * }} @@ -45,6 +46,7 @@ midiDevices: '', notifications: '', plugins: '', + images: '', popups: '', subresource_filter: '', unsandboxed_plugins: '', @@ -60,6 +62,7 @@ midiDevices: [], notifications: [], plugins: [], + images: [], popups: [], subresource_filter: [], unsandboxed_plugins: [],
diff --git a/chrome/utility/importer/firefox_importer_unittest_utils_mac.cc b/chrome/utility/importer/firefox_importer_unittest_utils_mac.cc index ac22dfe..53f3a77 100644 --- a/chrome/utility/importer/firefox_importer_unittest_utils_mac.cc +++ b/chrome/utility/importer/firefox_importer_unittest_utils_mac.cc
@@ -24,7 +24,6 @@ #include "content/public/common/content_descriptors.h" #include "content/public/common/mojo_channel_switches.h" #include "ipc/ipc_channel.h" -#include "ipc/ipc_descriptors.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_message.h" #include "mojo/edk/embedder/embedder.h"
diff --git a/chromeos/components/tether/active_host_unittest.cc b/chromeos/components/tether/active_host_unittest.cc index 09344d4..12e05c1 100644 --- a/chromeos/components/tether/active_host_unittest.cc +++ b/chromeos/components/tether/active_host_unittest.cc
@@ -37,13 +37,6 @@ devices_equal = !other.remote_device; } - LOG(ERROR) << (active_host_status == other.active_host_status); - LOG(ERROR) << devices_equal; - LOG(ERROR) << (other.tether_network_guid); - LOG(ERROR) << (tether_network_guid); - LOG(ERROR) << (other.wifi_network_guid); - LOG(ERROR) << (wifi_network_guid); - return active_host_status == other.active_host_status && devices_equal && tether_network_guid == other.tether_network_guid && wifi_network_guid == other.wifi_network_guid;
diff --git a/chromeos/components/tether/initializer.cc b/chromeos/components/tether/initializer.cc index d88ae944..94ebddc 100644 --- a/chromeos/components/tether/initializer.cc +++ b/chromeos/components/tether/initializer.cc
@@ -193,11 +193,24 @@ network_state_handler_); device_id_tether_network_guid_map_ = base::MakeUnique<DeviceIdTetherNetworkGuidMap>(); + host_scan_cache_ = base::MakeUnique<HostScanCache>( + network_state_handler_, active_host_.get(), + tether_host_response_recorder_.get(), + device_id_tether_network_guid_map_.get()); + clock_ = base::MakeUnique<base::DefaultClock>(); + host_scanner_ = base::MakeUnique<HostScanner>( + tether_host_fetcher_.get(), ble_connection_manager_.get(), + host_scan_device_prioritizer_.get(), tether_host_response_recorder_.get(), + notification_presenter_.get(), device_id_tether_network_guid_map_.get(), + host_scan_cache_.get(), clock_.get()); + host_scan_scheduler_ = base::MakeUnique<HostScanScheduler>( + network_state_handler_, host_scanner_.get()); tether_connector_ = base::MakeUnique<TetherConnector>( network_state_handler_, wifi_hotspot_connector_.get(), active_host_.get(), tether_host_fetcher_.get(), ble_connection_manager_.get(), tether_host_response_recorder_.get(), - device_id_tether_network_guid_map_.get()); + device_id_tether_network_guid_map_.get(), host_scan_cache_.get(), + notification_presenter_.get()); network_configuration_remover_ = base::MakeUnique<NetworkConfigurationRemover>( network_state_handler_, managed_network_configuration_handler_); @@ -214,18 +227,6 @@ base::MakeUnique<NetworkConnectionHandlerTetherDelegate>( network_connection_handler_, tether_connector_.get(), tether_disconnector_.get()); - host_scan_cache_ = base::MakeUnique<HostScanCache>( - network_state_handler_, active_host_.get(), - tether_host_response_recorder_.get(), - device_id_tether_network_guid_map_.get()); - clock_ = base::MakeUnique<base::DefaultClock>(); - host_scanner_ = base::MakeUnique<HostScanner>( - tether_host_fetcher_.get(), ble_connection_manager_.get(), - host_scan_device_prioritizer_.get(), tether_host_response_recorder_.get(), - notification_presenter_.get(), device_id_tether_network_guid_map_.get(), - host_scan_cache_.get(), clock_.get()); - host_scan_scheduler_ = base::MakeUnique<HostScanScheduler>( - network_state_handler_, host_scanner_.get()); // Because Initializer is created on each user log in, it's appropriate to // call this method now.
diff --git a/chromeos/components/tether/initializer.h b/chromeos/components/tether/initializer.h index ef17a1a..b6f0b58 100644 --- a/chromeos/components/tether/initializer.h +++ b/chromeos/components/tether/initializer.h
@@ -124,6 +124,10 @@ active_host_network_state_updater_; std::unique_ptr<DeviceIdTetherNetworkGuidMap> device_id_tether_network_guid_map_; + std::unique_ptr<HostScanCache> host_scan_cache_; + std::unique_ptr<base::DefaultClock> clock_; + std::unique_ptr<HostScanner> host_scanner_; + std::unique_ptr<HostScanScheduler> host_scan_scheduler_; std::unique_ptr<TetherConnector> tether_connector_; std::unique_ptr<TetherDisconnector> tether_disconnector_; std::unique_ptr<NetworkConfigurationRemover> network_configuration_remover_; @@ -131,10 +135,6 @@ network_connection_handler_tether_delegate_; std::unique_ptr<TetherNetworkDisconnectionHandler> tether_network_disconnection_handler_; - std::unique_ptr<HostScanCache> host_scan_cache_; - std::unique_ptr<base::DefaultClock> clock_; - std::unique_ptr<HostScanner> host_scanner_; - std::unique_ptr<HostScanScheduler> host_scan_scheduler_; base::WeakPtrFactory<Initializer> weak_ptr_factory_;
diff --git a/chromeos/components/tether/network_connection_handler_tether_delegate_unittest.cc b/chromeos/components/tether/network_connection_handler_tether_delegate_unittest.cc index 60160ca..aac1503 100644 --- a/chromeos/components/tether/network_connection_handler_tether_delegate_unittest.cc +++ b/chromeos/components/tether/network_connection_handler_tether_delegate_unittest.cc
@@ -73,7 +73,9 @@ nullptr /* tether_host_fetcher */, nullptr /* connection_manager */, nullptr /* tether_host_response_recorder */, - nullptr /* device_id_tether_network_guid_map */) {} + nullptr /* device_id_tether_network_guid_map */, + nullptr /* host_scan_cache */, + nullptr /* notification_presenter */) {} ~MockTetherConnector() override {} MOCK_METHOD3( @@ -149,4 +151,4 @@ } // namespace tether -} // namespace cryptauth +} // namespace chromeos
diff --git a/chromeos/components/tether/tether_connector.cc b/chromeos/components/tether/tether_connector.cc index 02cd147..7eb23ad 100644 --- a/chromeos/components/tether/tether_connector.cc +++ b/chromeos/components/tether/tether_connector.cc
@@ -7,9 +7,12 @@ #include "base/bind.h" #include "chromeos/components/tether/active_host.h" #include "chromeos/components/tether/device_id_tether_network_guid_map.h" +#include "chromeos/components/tether/host_scan_cache.h" +#include "chromeos/components/tether/notification_presenter.h" #include "chromeos/components/tether/tether_host_fetcher.h" #include "chromeos/components/tether/wifi_hotspot_connector.h" #include "chromeos/network/network_handler.h" +#include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" #include "components/proximity_auth/logging/logging.h" @@ -24,7 +27,9 @@ TetherHostFetcher* tether_host_fetcher, BleConnectionManager* connection_manager, TetherHostResponseRecorder* tether_host_response_recorder, - DeviceIdTetherNetworkGuidMap* device_id_tether_network_guid_map) + DeviceIdTetherNetworkGuidMap* device_id_tether_network_guid_map, + HostScanCache* host_scan_cache, + NotificationPresenter* notification_presenter) : network_state_handler_(network_state_handler), wifi_hotspot_connector_(wifi_hotspot_connector), active_host_(active_host), @@ -32,6 +37,8 @@ connection_manager_(connection_manager), tether_host_response_recorder_(tether_host_response_recorder), device_id_tether_network_guid_map_(device_id_tether_network_guid_map), + host_scan_cache_(host_scan_cache), + notification_presenter_(notification_presenter), weak_ptr_factory_(this) {} TetherConnector::~TetherConnector() { @@ -71,6 +78,13 @@ device_id_pending_connection_)); } + if (host_scan_cache_->DoesHostRequireSetup(tether_network_guid)) { + const std::string& device_name = + network_state_handler_->GetNetworkStateFromGuid(tether_network_guid) + ->name(); + notification_presenter_->NotifySetupRequired(device_name); + } + device_id_pending_connection_ = device_id; success_callback_ = success_callback; error_callback_ = error_callback; @@ -185,6 +199,8 @@ DCHECK(device_id == tether_host_to_connect->GetDeviceId()); + // TODO (hansberry): Indicate to ConnectTetheringOperation if first-time setup + // is required, so that it can adjust its timeout duration. connect_tethering_operation_ = ConnectTetheringOperation::Factory::NewInstance( *tether_host_to_connect, connection_manager_, @@ -197,6 +213,8 @@ DCHECK(!device_id_pending_connection_.empty()); DCHECK(!error_callback_.is_null()); + notification_presenter_->RemoveSetupRequiredNotification(); + // Save a copy of the callback before resetting it below. network_handler::StringResultCallback error_callback = error_callback_; @@ -215,6 +233,8 @@ DCHECK(device_id_pending_connection_ == device_id); DCHECK(!success_callback_.is_null()); + notification_presenter_->RemoveSetupRequiredNotification(); + // Save a copy of the callback before resetting it below. base::Closure success_callback = success_callback_;
diff --git a/chromeos/components/tether/tether_connector.h b/chromeos/components/tether/tether_connector.h index 9f7f5919..5498f893 100644 --- a/chromeos/components/tether/tether_connector.h +++ b/chromeos/components/tether/tether_connector.h
@@ -19,6 +19,8 @@ class ActiveHost; class BleConnectionManager; class DeviceIdTetherNetworkGuidMap; +class HostScanCache; +class NotificationPresenter; class TetherHostFetcher; class TetherHostResponseRecorder; class WifiHotspotConnector; @@ -37,7 +39,9 @@ TetherHostFetcher* tether_host_fetcher, BleConnectionManager* connection_manager, TetherHostResponseRecorder* tether_host_response_recorder, - DeviceIdTetherNetworkGuidMap* device_id_tether_network_guid_map); + DeviceIdTetherNetworkGuidMap* device_id_tether_network_guid_map, + HostScanCache* host_scan_cache, + NotificationPresenter* notification_presenter); virtual ~TetherConnector(); virtual void ConnectToNetwork( @@ -78,6 +82,8 @@ BleConnectionManager* connection_manager_; TetherHostResponseRecorder* tether_host_response_recorder_; DeviceIdTetherNetworkGuidMap* device_id_tether_network_guid_map_; + HostScanCache* host_scan_cache_; + NotificationPresenter* notification_presenter_; std::string device_id_pending_connection_; base::Closure success_callback_;
diff --git a/chromeos/components/tether/tether_connector_unittest.cc b/chromeos/components/tether/tether_connector_unittest.cc index df7e664..4910a09 100644 --- a/chromeos/components/tether/tether_connector_unittest.cc +++ b/chromeos/components/tether/tether_connector_unittest.cc
@@ -10,6 +10,8 @@ #include "chromeos/components/tether/device_id_tether_network_guid_map.h" #include "chromeos/components/tether/fake_active_host.h" #include "chromeos/components/tether/fake_ble_connection_manager.h" +#include "chromeos/components/tether/fake_host_scan_cache.h" +#include "chromeos/components/tether/fake_notification_presenter.h" #include "chromeos/components/tether/fake_tether_host_fetcher.h" #include "chromeos/components/tether/fake_wifi_hotspot_connector.h" #include "chromeos/components/tether/mock_tether_host_response_recorder.h" @@ -131,6 +133,9 @@ base::MakeUnique<MockTetherHostResponseRecorder>(); device_id_tether_network_guid_map_ = base::MakeUnique<DeviceIdTetherNetworkGuidMap>(); + fake_host_scan_cache_ = base::MakeUnique<FakeHostScanCache>(); + fake_notification_presenter_ = + base::MakeUnique<FakeNotificationPresenter>(); result_.clear(); @@ -139,7 +144,8 @@ fake_active_host_.get(), fake_tether_host_fetcher_.get(), fake_ble_connection_manager_.get(), mock_tether_host_response_recorder_.get(), - device_id_tether_network_guid_map_.get())); + device_id_tether_network_guid_map_.get(), fake_host_scan_cache_.get(), + fake_notification_presenter_.get())); SetUpTetherNetworks(); } @@ -164,16 +170,31 @@ // Add a tether network corresponding to both of the test devices. These // networks are expected to be added already before // TetherConnector::ConnectToNetwork is called. + AddTetherNetwork(GetTetherNetworkGuid(test_devices_[0].GetDeviceId()), + "TetherNetworkName1", "TetherNetworkCarrier1", + 85 /* battery_percentage */, 75 /* signal_strength */, + true /* has_connected_to_host */, + false /* setup_required */); + AddTetherNetwork(GetTetherNetworkGuid(test_devices_[1].GetDeviceId()), + "TetherNetworkName2", "TetherNetworkCarrier2", + 90 /* battery_percentage */, 50 /* signal_strength */, + true /* has_connected_to_host */, + true /* setup_required */); + } + + virtual void AddTetherNetwork(const std::string& tether_network_guid, + const std::string& device_name, + const std::string& carrier, + int battery_percentage, + int signal_strength, + bool has_connected_to_host, + bool setup_required) { network_state_handler()->AddTetherNetworkState( - GetTetherNetworkGuid(test_devices_[0].GetDeviceId()), - "TetherNetworkName1", "TetherNetworkCarrier1", - 85 /* battery_percentage */, 75 /* signal_strength */, - true /* has_connected_to_host */); - network_state_handler()->AddTetherNetworkState( - GetTetherNetworkGuid(test_devices_[1].GetDeviceId()), - "TetherNetworkName2", "TetherNetworkCarrier2", - 90 /* battery_percentage */, 50 /* signal_strength */, - true /* has_connected_to_host */); + tether_network_guid, device_name, carrier, battery_percentage, + signal_strength, has_connected_to_host); + fake_host_scan_cache_->SetHostScanResult(tether_network_guid, device_name, + carrier, battery_percentage, + signal_strength, setup_required); } void SuccessfullyJoinWifiNetwork() { @@ -213,6 +234,8 @@ // TODO(hansberry): Use a fake for this when a real mapping scheme is created. std::unique_ptr<DeviceIdTetherNetworkGuidMap> device_id_tether_network_guid_map_; + std::unique_ptr<FakeHostScanCache> fake_host_scan_cache_; + std::unique_ptr<FakeNotificationPresenter> fake_notification_presenter_; std::string result_; @@ -292,6 +315,30 @@ EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed, GetResultAndReset()); } +TEST_F(TetherConnectorTest, TestConnectTetheringOperationFails_SetupRequired) { + EXPECT_FALSE( + fake_notification_presenter_->is_setup_required_notification_shown()); + + CallConnect(GetTetherNetworkGuid(test_devices_[1].GetDeviceId())); + + EXPECT_TRUE( + fake_notification_presenter_->is_setup_required_notification_shown()); + + fake_tether_host_fetcher_->InvokePendingCallbacks(); + + EXPECT_TRUE( + fake_notification_presenter_->is_setup_required_notification_shown()); + + fake_operation_factory_->created_operations()[0]->SendFailedResponse( + ConnectTetheringResponse_ResponseCode:: + ConnectTetheringResponse_ResponseCode_UNKNOWN_ERROR); + + EXPECT_FALSE( + fake_notification_presenter_->is_setup_required_notification_shown()); + + EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed, GetResultAndReset()); +} + TEST_F(TetherConnectorTest, TestConnectingToWifiFails) { CallConnect(GetTetherNetworkGuid(test_devices_[0].GetDeviceId())); EXPECT_EQ(ActiveHost::ActiveHostStatus::CONNECTING, @@ -363,6 +410,8 @@ EXPECT_EQ(GetTetherNetworkGuid(test_devices_[0].GetDeviceId()), fake_active_host_->GetTetherNetworkGuid()); EXPECT_TRUE(fake_active_host_->GetWifiNetworkGuid().empty()); + EXPECT_FALSE( + fake_notification_presenter_->is_setup_required_notification_shown()); fake_tether_host_fetcher_->InvokePendingCallbacks(); @@ -394,6 +443,34 @@ EXPECT_EQ(kSuccessResult, GetResultAndReset()); } +TEST_F(TetherConnectorTest, TestSuccessfulConnection_SetupRequired) { + EXPECT_FALSE( + fake_notification_presenter_->is_setup_required_notification_shown()); + + CallConnect(GetTetherNetworkGuid(test_devices_[1].GetDeviceId())); + + EXPECT_TRUE( + fake_notification_presenter_->is_setup_required_notification_shown()); + + fake_tether_host_fetcher_->InvokePendingCallbacks(); + + EXPECT_TRUE( + fake_notification_presenter_->is_setup_required_notification_shown()); + + fake_operation_factory_->created_operations()[0]->SendSuccessfulResponse( + kSsid, kPassword); + + EXPECT_TRUE( + fake_notification_presenter_->is_setup_required_notification_shown()); + + SuccessfullyJoinWifiNetwork(); + + EXPECT_FALSE( + fake_notification_presenter_->is_setup_required_notification_shown()); + + EXPECT_EQ(kSuccessResult, GetResultAndReset()); +} + TEST_F(TetherConnectorTest, TestNewConnectionAttemptDuringFetch_DifferentDevice) { CallConnect(GetTetherNetworkGuid(test_devices_[0].GetDeviceId()));
diff --git a/chromeos/components/tether/tether_disconnector_unittest.cc b/chromeos/components/tether/tether_disconnector_unittest.cc index bb14ccc..b421426 100644 --- a/chromeos/components/tether/tether_disconnector_unittest.cc +++ b/chromeos/components/tether/tether_disconnector_unittest.cc
@@ -48,7 +48,7 @@ class TestNetworkConnectionHandler : public NetworkConnectionHandler { public: - TestNetworkConnectionHandler(base::Closure disconnect_callback) + explicit TestNetworkConnectionHandler(base::Closure disconnect_callback) : disconnect_callback_(disconnect_callback) {} ~TestNetworkConnectionHandler() override {} @@ -105,7 +105,9 @@ nullptr /* tether_host_fetcher */, nullptr /* connection_manager */, nullptr /* tether_host_response_recorder */, - nullptr /* device_id_tether_network_guid_map */), + nullptr /* device_id_tether_network_guid_map */, + nullptr /* host_scan_cache */, + nullptr /* notification_presenter */), should_cancel_successfully_(true) {} ~TestTetherConnector() override {}
diff --git a/chromeos/network/network_state.cc b/chromeos/network/network_state.cc index dff74c4..8db65350 100644 --- a/chromeos/network/network_state.cc +++ b/chromeos/network/network_state.cc
@@ -201,6 +201,8 @@ vpn_provider_type_ = vpn_provider_type; return true; + } else if (key == shill::kTetheringProperty) { + return GetStringValue(key, value, &tethering_state_); } return false; } @@ -364,6 +366,11 @@ connection_state_ = connection_state; } +bool NetworkState::IsUsingMobileData() const { + return type() == shill::kTypeCellular || type() == chromeos::kTypeTether || + tethering_state() == shill::kTetheringConfirmedState; +} + bool NetworkState::IsDynamicWep() const { return security_class_ == shill::kSecurityWep && eap_key_mgmt_ == shill::kKeyManagementIEEE8021X;
diff --git a/chromeos/network/network_state.h b/chromeos/network/network_state.h index d43c562..18dba041 100644 --- a/chromeos/network/network_state.h +++ b/chromeos/network/network_state.h
@@ -100,6 +100,7 @@ const std::string& roaming() const { return roaming_; } const std::string& payment_url() const { return payment_url_; } bool cellular_out_of_credits() const { return cellular_out_of_credits_; } + const std::string& tethering_state() const { return tethering_state_; } // VPN property accessors const std::string& vpn_provider_type() const { return vpn_provider_type_; } @@ -123,6 +124,9 @@ const std::string& tether_guid() const { return tether_guid_; } void set_tether_guid(const std::string& guid) { tether_guid_ = guid; } + // Returns true if current connection is using mobile data. + bool IsUsingMobileData() const; + // Returns true if the network securty is WEP_8021x (Dynamic WEP) bool IsDynamicWep() const; @@ -226,6 +230,7 @@ std::string roaming_; std::string payment_url_; bool cellular_out_of_credits_ = false; + std::string tethering_state_; // VPN properties, used to construct the display name and to show the correct // configuration dialog. @@ -235,6 +240,7 @@ // Tether properties. std::string carrier_; int battery_percentage_; + // Whether the current device has already connected to the tether host device // providing the hotspot corresponding to this NetworkState. // Note: this means that the current device has already connected to the
diff --git a/components/background_task_scheduler/BUILD.gn b/components/background_task_scheduler/BUILD.gn index e23f4837..f77a15c 100644 --- a/components/background_task_scheduler/BUILD.gn +++ b/components/background_task_scheduler/BUILD.gn
@@ -31,6 +31,7 @@ "android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerGcmNetworkManager.java", "android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerJobService.java", "android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerPrefs.java", + "android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java", "android/java/src/org/chromium/components/background_task_scheduler/BundleToPersistableBundleConverter.java", "android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java", "android/java/src/org/chromium/components/background_task_scheduler/TaskInfo.java", @@ -68,6 +69,7 @@ "android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskGcmTaskServiceTest.java", "android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerPrefsTest.java", "android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerTest.java", + "android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java", "android/junit/src/org/chromium/components/background_task_scheduler/ShadowGcmNetworkManager.java", "android/junit/src/org/chromium/components/background_task_scheduler/TestBackgroundTask.java", ]
diff --git a/components/background_task_scheduler/OWNERS b/components/background_task_scheduler/OWNERS index 4d5ae7f..5c8a6c0 100644 --- a/components/background_task_scheduler/OWNERS +++ b/components/background_task_scheduler/OWNERS
@@ -1,4 +1,5 @@ awdf@chromium.org dtrainor@chromium.org +fgorski@chromium.org nyquist@chromium.org
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskGcmTaskService.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskGcmTaskService.java index 4a9521a..b109980 100644 --- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskGcmTaskService.java +++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskGcmTaskService.java
@@ -98,6 +98,7 @@ ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { + BackgroundTaskSchedulerUma.getInstance().reportTaskStarted(taskParams.getTaskId()); taskNeedsBackgroundProcessing.set( backgroundTask.onStartTask(ContextUtils.getApplicationContext(), taskParams, new TaskFinishedCallbackGcmTaskService(waiter))); @@ -115,6 +116,7 @@ ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { + BackgroundTaskSchedulerUma.getInstance().reportTaskStopped(taskParams.getTaskId()); taskNeedsRescheduling.set(backgroundTask.onStopTask( ContextUtils.getApplicationContext(), taskParams)); }
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskJobService.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskJobService.java index b53b599e..a3e6ce49 100644 --- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskJobService.java +++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskJobService.java
@@ -76,6 +76,8 @@ TaskParameters taskParams = BackgroundTaskSchedulerJobService.getTaskParametersFromJobParameters(params); + + BackgroundTaskSchedulerUma.getInstance().reportTaskStarted(taskParams.getTaskId()); boolean taskNeedsBackgroundProcessing = backgroundTask.onStartTask(getApplicationContext(), taskParams, new TaskFinishedCallbackJobService(this, backgroundTask, params)); @@ -96,6 +98,7 @@ TaskParameters taskParams = BackgroundTaskSchedulerJobService.getTaskParametersFromJobParameters(params); + BackgroundTaskSchedulerUma.getInstance().reportTaskStopped(taskParams.getTaskId()); boolean taskNeedsReschedule = backgroundTask.onStopTask(getApplicationContext(), taskParams); mCurrentTasks.remove(params.getJobId());
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskScheduler.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskScheduler.java index e690273..bf85375 100644 --- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskScheduler.java +++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskScheduler.java
@@ -77,6 +77,7 @@ public boolean schedule(Context context, TaskInfo taskInfo) { ThreadUtils.assertOnUiThread(); boolean success = mSchedulerDelegate.schedule(context, taskInfo); + BackgroundTaskSchedulerUma.getInstance().reportTaskScheduled(taskInfo.getTaskId(), success); if (success) { BackgroundTaskSchedulerPrefs.addScheduledTask(taskInfo); } @@ -91,6 +92,7 @@ */ public void cancel(Context context, int taskId) { ThreadUtils.assertOnUiThread(); + BackgroundTaskSchedulerUma.getInstance().reportTaskCanceled(taskId); BackgroundTaskSchedulerPrefs.removeScheduledTask(taskId); mSchedulerDelegate.cancel(context, taskId); } @@ -105,14 +107,19 @@ public void checkForOSUpgrade(Context context) { int oldSdkInt = BackgroundTaskSchedulerPrefs.getLastSdkVersion(); int newSdkInt = Build.VERSION.SDK_INT; - // No OS upgrade detected. - if (oldSdkInt == newSdkInt) return; - // Save the current SDK version to preferences. - BackgroundTaskSchedulerPrefs.setLastSdkVersion(newSdkInt); + if (oldSdkInt != newSdkInt) { + // Save the current SDK version to preferences. + BackgroundTaskSchedulerPrefs.setLastSdkVersion(newSdkInt); + } - // Check for OS upgrades forcing delegate change or "just in case" rescheduling. - if (!osUpgradeChangesDelegateType(oldSdkInt, newSdkInt)) return; + // No OS upgrade detected or OS upgrade does not change delegate. + if (oldSdkInt == newSdkInt || !osUpgradeChangesDelegateType(oldSdkInt, newSdkInt)) { + BackgroundTaskSchedulerUma.getInstance().flushStats(); + return; + } + + BackgroundTaskSchedulerUma.getInstance().removeCachedStats(); // Explicitly create and invoke old delegate type to cancel all scheduled tasks. // All preference entries are kept until reschedule call, which removes then then.
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java new file mode 100644 index 0000000..77252d7c --- /dev/null +++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java
@@ -0,0 +1,248 @@ +// 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.components.background_task_scheduler; + +import android.content.SharedPreferences; + +import org.chromium.base.ContextUtils; +import org.chromium.base.ThreadUtils; +import org.chromium.base.VisibleForTesting; +import org.chromium.base.library_loader.LibraryLoader; +import org.chromium.base.metrics.RecordHistogram; + +import java.util.HashSet; +import java.util.Set; + +class BackgroundTaskSchedulerUma { + // BackgroundTaskId defined in tools/metrics/histograms/enums.xml + static final int BACKGROUND_TASK_TEST = 0; + static final int BACKGROUND_TASK_OMAHA = 1; + static final int BACKGROUND_TASK_GCM = 2; + static final int BACKGROUND_TASK_NOTIFICATIONS = 3; + static final int BACKGROUND_TASK_WEBVIEW_MINIDUMP = 4; + static final int BACKGROUND_TASK_CHROME_MINIDUMP = 5; + static final int BACKGROUND_TASK_OFFLINE_PAGES = 6; + static final int BACKGROUND_TASK_OFFLINE_PREFETCH = 7; + // Keep this one at the end and increment appropriately when adding new tasks. + static final int BACKGROUND_TASK_COUNT = 8; + + static final String KEY_CACHED_UMA = "bts_cached_uma"; + + private static BackgroundTaskSchedulerUma sInstance; + + private static class CachedUmaEntry { + private static final String SEPARATOR = ":"; + private String mEvent; + private int mValue; + private int mCount; + + /** + * Parses a cached UMA entry from a string. + * + * @param entry A serialized entry from preferences store. + * @return A parsed CachedUmaEntry object, or <c>null</c> if parsing failed. + */ + public static CachedUmaEntry parseEntry(String entry) { + if (entry == null) return null; + + String[] entryParts = entry.split(SEPARATOR); + if (entryParts.length != 3 || entryParts[0].isEmpty() || entryParts[1].isEmpty() + || entryParts[2].isEmpty()) { + return null; + } + int value = -1; + int count = -1; + try { + value = Integer.parseInt(entryParts[1]); + count = Integer.parseInt(entryParts[2]); + } catch (NumberFormatException e) { + return null; + } + return new CachedUmaEntry(entryParts[0], value, count); + } + + /** Returns a string for partial matching of the prefs entry. */ + public static String getStringForPartialMatching(String event, int value) { + return event + SEPARATOR + value + SEPARATOR; + } + + public CachedUmaEntry(String event, int value, int count) { + mEvent = event; + mValue = value; + mCount = count; + } + + /** Converts cached UMA entry to a string in format: EVENT:VALUE:COUNT. */ + public String toString() { + return mEvent + SEPARATOR + mValue + SEPARATOR + mCount; + } + + /** Gets the name of the event (UMA). */ + public String getEvent() { + return mEvent; + } + + /** Gets the value of the event (concrete value of the enum). */ + public int getValue() { + return mValue; + } + + /** Gets the count of events that happened. */ + public int getCount() { + return mCount; + } + + /** Increments the count of the event. */ + public void increment() { + mCount++; + } + } + + public static BackgroundTaskSchedulerUma getInstance() { + if (sInstance == null) { + sInstance = new BackgroundTaskSchedulerUma(); + } + return sInstance; + } + + @VisibleForTesting + public static void setInstanceForTesting(BackgroundTaskSchedulerUma instance) { + sInstance = instance; + } + + /** Reports metrics for task scheduling and whether it was successful. */ + public void reportTaskScheduled(int taskId, boolean success) { + if (success) { + cacheEvent("Android.BackgroundTaskScheduler.TaskScheduled.Success", + toUmaEnumValueFromTaskId(taskId)); + } else { + cacheEvent("Android.BackgroundTaskScheduler.TaskScheduled.Failure", + toUmaEnumValueFromTaskId(taskId)); + } + } + + /** Reports metrics for task canceling. */ + public void reportTaskCanceled(int taskId) { + cacheEvent( + "Android.BackgroundTaskScheduler.TaskCanceled", toUmaEnumValueFromTaskId(taskId)); + } + + /** Reports metrics for starting a task. */ + public void reportTaskStarted(int taskId) { + cacheEvent("Android.BackgroundTaskScheduler.TaskStarted", toUmaEnumValueFromTaskId(taskId)); + } + + /** Reports metrics for stopping a task. */ + public void reportTaskStopped(int taskId) { + cacheEvent("Android.BackgroundTaskScheduler.TaskStopped", toUmaEnumValueFromTaskId(taskId)); + } + + /** Method that actually invokes histogram recording. Extracted for testing. */ + @VisibleForTesting + void recordEnumeratedHistogram(String histogram, int value, int maxCount) { + RecordHistogram.recordEnumeratedHistogram(histogram, value, maxCount); + } + + /** Records histograms for cached stats. Should only be called when native is initialized. */ + public void flushStats() { + assertNativeIsLoaded(); + ThreadUtils.assertOnUiThread(); + + Set<String> cachedUmaStrings = getCachedUmaEntries(ContextUtils.getAppSharedPreferences()); + + for (String cachedUmaString : cachedUmaStrings) { + CachedUmaEntry entry = CachedUmaEntry.parseEntry(cachedUmaString); + if (entry == null) continue; + for (int i = 0; i < entry.getCount(); i++) { + recordEnumeratedHistogram( + entry.getEvent(), entry.getValue(), BACKGROUND_TASK_COUNT); + } + } + + // Once all metrics are reported, we can simply remove the shared preference key. + removeCachedStats(); + } + + /** Removes all of the cached stats without reporting. */ + public void removeCachedStats() { + ThreadUtils.assertOnUiThread(); + ContextUtils.getAppSharedPreferences().edit().remove(KEY_CACHED_UMA).apply(); + } + + /** Caches the event to be reported through UMA in shared preferences. */ + @VisibleForTesting + void cacheEvent(String event, int value) { + SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); + Set<String> cachedUmaStrings = getCachedUmaEntries(prefs); + String partialMatch = CachedUmaEntry.getStringForPartialMatching(event, value); + + String existingEntry = null; + for (String cachedUmaString : cachedUmaStrings) { + if (cachedUmaString.startsWith(partialMatch)) { + existingEntry = cachedUmaString; + break; + } + } + + Set<String> setToWriteBack = new HashSet<>(cachedUmaStrings); + CachedUmaEntry entry = null; + if (existingEntry != null) { + entry = CachedUmaEntry.parseEntry(existingEntry); + if (entry == null) { + entry = new CachedUmaEntry(event, value, 1); + } + setToWriteBack.remove(existingEntry); + entry.increment(); + } else { + entry = new CachedUmaEntry(event, value, 1); + } + + setToWriteBack.add(entry.toString()); + updateCachedUma(prefs, setToWriteBack); + } + + @VisibleForTesting + static int toUmaEnumValueFromTaskId(int taskId) { + switch (taskId) { + case TaskIds.TEST: + return BACKGROUND_TASK_TEST; + case TaskIds.OMAHA_JOB_ID: + return BACKGROUND_TASK_OMAHA; + case TaskIds.GCM_BACKGROUND_TASK_JOB_ID: + return BACKGROUND_TASK_GCM; + case TaskIds.NOTIFICATION_SERVICE_JOB_ID: + return BACKGROUND_TASK_NOTIFICATIONS; + case TaskIds.WEBVIEW_MINIDUMP_UPLOADING_JOB_ID: + return BACKGROUND_TASK_WEBVIEW_MINIDUMP; + case TaskIds.CHROME_MINIDUMP_UPLOADING_JOB_ID: + return BACKGROUND_TASK_CHROME_MINIDUMP; + case TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID: + return BACKGROUND_TASK_OFFLINE_PAGES; + case TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID: + return BACKGROUND_TASK_OFFLINE_PREFETCH; + default: + assert false; + } + // Returning a value that is not expected to ever be reported. + return BACKGROUND_TASK_TEST; + } + + @VisibleForTesting + static Set<String> getCachedUmaEntries(SharedPreferences prefs) { + return prefs.getStringSet(KEY_CACHED_UMA, new HashSet<String>(1)); + } + + @VisibleForTesting + static void updateCachedUma(SharedPreferences prefs, Set<String> cachedUma) { + ThreadUtils.assertOnUiThread(); + SharedPreferences.Editor editor = prefs.edit(); + editor.putStringSet(KEY_CACHED_UMA, cachedUma); + editor.apply(); + } + + void assertNativeIsLoaded() { + assert LibraryLoader.isInitialized(); + } +}
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java index d5b5b78..e4a39529 100644 --- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java +++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java
@@ -9,6 +9,8 @@ * that there is no overlap of task IDs between different users of the BackgroundTaskScheduler. */ public final class TaskIds { + // When adding your job id to the list below, remember to make a corresponding update to the + // BackgroundTaskSchedulerUma#toUmaEnumValueFromTaskId(int) method. public static final int TEST = 0x00008378; public static final int OMAHA_JOB_ID = 0x00011684;
diff --git a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskGcmTaskServiceTest.java b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskGcmTaskServiceTest.java index 36018c4..b955ffa1 100644 --- a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskGcmTaskServiceTest.java +++ b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskGcmTaskServiceTest.java
@@ -6,6 +6,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import android.content.Context; import android.os.Build; @@ -38,6 +41,8 @@ static boolean sNeedsRescheduling; @Mock private BackgroundTaskSchedulerDelegate mDelegate; + @Mock + private BackgroundTaskSchedulerUma mBackgroundTaskSchedulerUma; @Before public void setUp() { @@ -45,6 +50,7 @@ ContextUtils.initApplicationContextForTests(RuntimeEnvironment.application); BackgroundTaskSchedulerFactory.setSchedulerForTesting( new BackgroundTaskScheduler(mDelegate)); + BackgroundTaskSchedulerUma.setInstanceForTesting(mBackgroundTaskSchedulerUma); sReturnThroughCallback = false; sNeedsRescheduling = false; sLastTask = null; @@ -87,6 +93,8 @@ assertEquals(parameters.getTaskId(), TaskIds.TEST); assertEquals(parameters.getExtras().getString("foo"), "bar"); + + verify(mBackgroundTaskSchedulerUma, times(1)).reportTaskStarted(eq(TaskIds.TEST)); } @Test
diff --git a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerTest.java b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerTest.java index 269e93f..1fa1b468 100644 --- a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerTest.java +++ b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerTest.java
@@ -49,6 +49,8 @@ @Mock private BackgroundTaskSchedulerDelegate mDelegate; + @Mock + private BackgroundTaskSchedulerUma mBackgroundTaskSchedulerUma; private ShadowGcmNetworkManager mGcmNetworkManager; @Before @@ -57,6 +59,7 @@ ContextUtils.initApplicationContextForTests(RuntimeEnvironment.application); BackgroundTaskSchedulerFactory.setSchedulerForTesting( new BackgroundTaskScheduler(mDelegate)); + BackgroundTaskSchedulerUma.setInstanceForTesting(mBackgroundTaskSchedulerUma); TestBackgroundTask.reset(); // Initialize Google Play Services and GCM Network Manager for upgrade testing. @@ -75,6 +78,8 @@ assertTrue(BackgroundTaskSchedulerPrefs.getScheduledTasks().contains( TASK.getBackgroundTaskClass().getName())); verify(mDelegate, times(1)).schedule(eq(RuntimeEnvironment.application), eq(TASK)); + verify(mBackgroundTaskSchedulerUma, times(1)) + .reportTaskScheduled(eq(TaskIds.TEST), eq(true)); } @Test
diff --git a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java new file mode 100644 index 0000000..405ef75 --- /dev/null +++ b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java
@@ -0,0 +1,184 @@ +// 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.components.background_task_scheduler; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import org.chromium.base.ContextUtils; +import org.chromium.base.test.util.Feature; +import org.chromium.testing.local.LocalRobolectricTestRunner; + +import java.util.Set; + +/** Unit tests for {@link BackgroundTaskSchedulerUma}. */ +@RunWith(LocalRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class BackgroundTaskSchedulerUmaTest { + @Spy + private BackgroundTaskSchedulerUma mUmaSpy; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + ContextUtils.initApplicationContextForTests(RuntimeEnvironment.application); + BackgroundTaskSchedulerUma.setInstanceForTesting(mUmaSpy); + doNothing().when(mUmaSpy).assertNativeIsLoaded(); + } + + @Test + @Feature({"BackgroundTaskScheduler"}) + public void testToUmaEnumValueFromTaskId() { + assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_TEST, + BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(TaskIds.TEST)); + assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_OMAHA, + BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(TaskIds.OMAHA_JOB_ID)); + assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_GCM, + BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId( + TaskIds.GCM_BACKGROUND_TASK_JOB_ID)); + assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_NOTIFICATIONS, + BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId( + TaskIds.NOTIFICATION_SERVICE_JOB_ID)); + assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_WEBVIEW_MINIDUMP, + BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId( + TaskIds.WEBVIEW_MINIDUMP_UPLOADING_JOB_ID)); + assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_CHROME_MINIDUMP, + BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId( + TaskIds.CHROME_MINIDUMP_UPLOADING_JOB_ID)); + assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_OFFLINE_PAGES, + BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId( + TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID)); + assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_OFFLINE_PREFETCH, + BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId( + TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID)); + assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COUNT, 8); + } + + @Test + @Feature({"BackgroundTaskScheduler"}) + public void testCacheEvent() { + String eventName = "event"; + int eventValue = 77; + mUmaSpy.cacheEvent(eventName, eventValue); + + Set<String> cachedUmaEntries = BackgroundTaskSchedulerUma.getCachedUmaEntries( + ContextUtils.getAppSharedPreferences()); + assertTrue(cachedUmaEntries.contains("event:77:1")); + assertEquals(1, cachedUmaEntries.size()); + + mUmaSpy.cacheEvent(eventName, eventValue); + mUmaSpy.cacheEvent(eventName, eventValue); + + cachedUmaEntries = BackgroundTaskSchedulerUma.getCachedUmaEntries( + ContextUtils.getAppSharedPreferences()); + assertTrue(cachedUmaEntries.contains("event:77:3")); + assertEquals(1, cachedUmaEntries.size()); + + int eventValue2 = 50; + mUmaSpy.cacheEvent(eventName, eventValue2); + + cachedUmaEntries = BackgroundTaskSchedulerUma.getCachedUmaEntries( + ContextUtils.getAppSharedPreferences()); + assertTrue(cachedUmaEntries.contains("event:77:3")); + assertTrue(cachedUmaEntries.contains("event:50:1")); + assertEquals(2, cachedUmaEntries.size()); + } + + @Test + @Feature({"BackgroundTaskScheduler"}) + public void testFlushStats() { + doNothing().when(mUmaSpy).recordEnumeratedHistogram(anyString(), anyInt(), anyInt()); + + BackgroundTaskSchedulerUma.getInstance().flushStats(); + verify(mUmaSpy, times(0)).recordEnumeratedHistogram(anyString(), anyInt(), anyInt()); + + String eventName = "event"; + int eventValue = 77; + int eventValue2 = 50; + mUmaSpy.cacheEvent(eventName, eventValue); + mUmaSpy.cacheEvent(eventName, eventValue); + mUmaSpy.cacheEvent(eventName, eventValue); + mUmaSpy.cacheEvent(eventName, eventValue2); + + BackgroundTaskSchedulerUma.getInstance().flushStats(); + + verify(mUmaSpy, times(3)) + .recordEnumeratedHistogram(eq(eventName), eq(eventValue), + eq(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COUNT)); + verify(mUmaSpy, times(1)) + .recordEnumeratedHistogram(eq(eventName), eq(eventValue2), + eq(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COUNT)); + Set<String> cachedUmaEntries = BackgroundTaskSchedulerUma.getCachedUmaEntries( + ContextUtils.getAppSharedPreferences()); + assertTrue(cachedUmaEntries.isEmpty()); + } + + @Test + @Feature({"BackgroundTaskScheduler"}) + public void testReportTaskScheduledSuccess() { + doNothing().when(mUmaSpy).cacheEvent(anyString(), anyInt()); + BackgroundTaskSchedulerUma.getInstance().reportTaskScheduled(TaskIds.TEST, true); + verify(mUmaSpy, times(1)) + .cacheEvent(eq("Android.BackgroundTaskScheduler.TaskScheduled.Success"), + eq(BackgroundTaskSchedulerUma.BACKGROUND_TASK_TEST)); + } + + @Test + @Feature({"BackgroundTaskScheduler"}) + public void testReportTaskScheduledFailure() { + doNothing().when(mUmaSpy).cacheEvent(anyString(), anyInt()); + BackgroundTaskSchedulerUma.getInstance().reportTaskScheduled( + TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID, false); + verify(mUmaSpy, times(1)) + .cacheEvent(eq("Android.BackgroundTaskScheduler.TaskScheduled.Failure"), + eq(BackgroundTaskSchedulerUma.BACKGROUND_TASK_OFFLINE_PAGES)); + } + + @Test + @Feature({"BackgroundTaskScheduler"}) + public void testReportTaskCanceled() { + doNothing().when(mUmaSpy).cacheEvent(anyString(), anyInt()); + BackgroundTaskSchedulerUma.getInstance().reportTaskCanceled( + TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID); + verify(mUmaSpy, times(1)) + .cacheEvent(eq("Android.BackgroundTaskScheduler.TaskCanceled"), + eq(BackgroundTaskSchedulerUma.BACKGROUND_TASK_OFFLINE_PREFETCH)); + } + + @Test + @Feature({"BackgroundTaskScheduler"}) + public void testReportTaskStarted() { + doNothing().when(mUmaSpy).cacheEvent(anyString(), anyInt()); + BackgroundTaskSchedulerUma.getInstance().reportTaskStarted(TaskIds.OMAHA_JOB_ID); + verify(mUmaSpy, times(1)) + .cacheEvent(eq("Android.BackgroundTaskScheduler.TaskStarted"), + eq(BackgroundTaskSchedulerUma.BACKGROUND_TASK_OMAHA)); + } + + @Test + @Feature({"BackgroundTaskScheduler"}) + public void testReportTaskStopped() { + doNothing().when(mUmaSpy).cacheEvent(anyString(), anyInt()); + BackgroundTaskSchedulerUma.getInstance().reportTaskStopped( + TaskIds.GCM_BACKGROUND_TASK_JOB_ID); + verify(mUmaSpy, times(1)) + .cacheEvent(eq("Android.BackgroundTaskScheduler.TaskStopped"), + eq(BackgroundTaskSchedulerUma.BACKGROUND_TASK_GCM)); + } +}
diff --git a/components/feature_engagement_tracker/DEPS b/components/feature_engagement_tracker/DEPS index 925a7c7..48edfa45a 100644 --- a/components/feature_engagement_tracker/DEPS +++ b/components/feature_engagement_tracker/DEPS
@@ -1,6 +1,7 @@ include_rules = [ "-content", "+jni", + "+components/flags_ui", "+components/keyed_service", "+components/leveldb_proto", ]
diff --git a/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc b/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc index 3403c93..9d92884 100644 --- a/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc +++ b/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc
@@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/feature_list.h" #include "base/memory/ptr_util.h" +#include "base/metrics/field_trial_params.h" #include "base/metrics/user_metrics.h" #include "base/threading/thread_task_runner_handle.h" #include "components/feature_engagement_tracker/internal/availability_model_impl.h" @@ -40,6 +41,11 @@ // Creates a FeatureEngagementTrackerImpl that is usable for a demo mode. std::unique_ptr<FeatureEngagementTracker> CreateDemoModeFeatureEngagementTracker() { + // GetFieldTrialParamValueByFeature returns an empty string if the param is + // not set. + std::string chosen_feature_name = base::GetFieldTrialParamValueByFeature( + kIPHDemoMode, kIPHDemoModeFeatureChoiceParam); + std::unique_ptr<EditableConfiguration> configuration = base::MakeUnique<EditableConfiguration>(); @@ -47,8 +53,14 @@ // OnceConditionValidator acknowledges that thet meet conditions once. std::vector<const base::Feature*> features = GetAllFeatures(); for (auto* feature : features) { + // If a particular feature has been chosen to use with demo mode, only + // mark that feature with a valid configuration. + bool valid_config = chosen_feature_name.empty() + ? true + : chosen_feature_name == feature->name; + FeatureConfig feature_config; - feature_config.valid = true; + feature_config.valid = valid_config; feature_config.trigger.name = feature->name + std::string("_trigger"); configuration->SetConfiguration(feature, feature_config); }
diff --git a/components/feature_engagement_tracker/public/BUILD.gn b/components/feature_engagement_tracker/public/BUILD.gn index 3be36c7..2bbd588 100644 --- a/components/feature_engagement_tracker/public/BUILD.gn +++ b/components/feature_engagement_tracker/public/BUILD.gn
@@ -18,6 +18,7 @@ deps = [ "//base", + "//components/flags_ui", "//components/keyed_service/core", ]
diff --git a/components/feature_engagement_tracker/public/feature_list.cc b/components/feature_engagement_tracker/public/feature_list.cc index 6d600c4..73f043c 100644 --- a/components/feature_engagement_tracker/public/feature_list.cc +++ b/components/feature_engagement_tracker/public/feature_list.cc
@@ -6,16 +6,55 @@ #include "base/feature_list.h" #include "components/feature_engagement_tracker/public/feature_constants.h" +#include "components/flags_ui/feature_entry.h" namespace feature_engagement_tracker { namespace { +// Defines a const flags_ui::FeatureEntry::FeatureParam for the given +// base::Feature. The constant name will be on the form +// kFooFeature --> kFooFeatureVariation. This is intended to be used with +// VARIATION_ENTRY below to be able to insert it into an array of +// flags_ui::FeatureEntry::FeatureVariation. +#define DEFINE_VARIATION_PARAM(base_feature) \ + const flags_ui::FeatureEntry::FeatureParam base_feature##Variation[] = { \ + {kIPHDemoModeFeatureChoiceParam, base_feature.name}} + +// Defines a single flags_ui::FeatureEntry::FeatureVariation entry, fully +// enclosed. This is intended to be used with the declaration of +// |kIPHDemoModeChoiceVariations| below. +#define VARIATION_ENTRY(base_feature) \ + { \ + base_feature##Variation[0].param_value, base_feature##Variation, \ + arraysize(base_feature##Variation), nullptr \ + } + +// Whenever a feature is added to |kAllFeatures|, it should also be added as +// DEFINE_VARIATION_PARAM below, and also added to the +// |kIPHDemoModeChoiceVariations| array. const base::Feature* kAllFeatures[] = { &kIPHDataSaverPreview, &kIPHDownloadPageFeature, &kIPHDownloadHomeFeature}; +// Defines a flags_ui::FeatureEntry::FeatureParam for each feature. +DEFINE_VARIATION_PARAM(kIPHDataSaverPreview); +DEFINE_VARIATION_PARAM(kIPHDownloadPageFeature); +DEFINE_VARIATION_PARAM(kIPHDownloadHomeFeature); + } // namespace +const char kIPHDemoModeFeatureChoiceParam[] = "chosen_feature"; + +const flags_ui::FeatureEntry::FeatureVariation kIPHDemoModeChoiceVariations[] = + { + VARIATION_ENTRY(kIPHDataSaverPreview), + VARIATION_ENTRY(kIPHDownloadPageFeature), + VARIATION_ENTRY(kIPHDownloadHomeFeature), + // Note: When changing the number of entries in this array, the constant + // kIPHDemoModeChoiceVariationsLen in feature_list.h must also be + // updated to reflect the real size. +}; + std::vector<const base::Feature*> GetAllFeatures() { return std::vector<const base::Feature*>( kAllFeatures, kAllFeatures + arraysize(kAllFeatures));
diff --git a/components/feature_engagement_tracker/public/feature_list.h b/components/feature_engagement_tracker/public/feature_list.h index ba4a578..0cb72bd2 100644 --- a/components/feature_engagement_tracker/public/feature_list.h +++ b/components/feature_engagement_tracker/public/feature_list.h
@@ -8,10 +8,27 @@ #include <vector> #include "base/feature_list.h" +#include "components/flags_ui/feature_entry.h" namespace feature_engagement_tracker { using FeatureVector = std::vector<const base::Feature*>; +// The param name for the FeatureVariation configuration, which is used by +// chrome://flags to set the variable name for the selected feature. The +// FeatureEngagementTracker backend will then read this to figure out which +// feature (if any) was selected by the end user. +extern const char kIPHDemoModeFeatureChoiceParam[]; + +// The length of kIPHDemoModeChoiceVariations. This must be updated whenever +// new features are added to the demo mode selection. +constexpr size_t kIPHDemoModeChoiceVariationsLen = 3; + +// Defines the array of which features should be listed in the chrome://flags +// UI to be able to select them alone for demo-mode. The features listed here +// are possible to enable on their own in demo mode. +extern const flags_ui::FeatureEntry::FeatureVariation + kIPHDemoModeChoiceVariations[kIPHDemoModeChoiceVariationsLen]; + // Returns all the features that are in use for engagement tracking. FeatureVector GetAllFeatures();
diff --git a/components/history_strings.grdp b/components/history_strings.grdp index f7da1b48..6864dc5 100644 --- a/components/history_strings.grdp +++ b/components/history_strings.grdp
@@ -45,7 +45,7 @@ More from this site </message> <message name="IDS_HISTORY_NO_RESULTS" desc="Text shown when no history entries are found."> - No history entries found + Your browsing history appears here </message> <message name="IDS_HISTORY_NO_SEARCH_RESULTS" desc="Text shown when no history search results have been found"> No search results found
diff --git a/components/nacl/loader/nacl_helper_linux.cc b/components/nacl/loader/nacl_helper_linux.cc index fbce5f9..9de0eb40 100644 --- a/components/nacl/loader/nacl_helper_linux.cc +++ b/components/nacl/loader/nacl_helper_linux.cc
@@ -39,7 +39,6 @@ #include "content/public/common/mojo_channel_switches.h" #include "content/public/common/send_zygote_child_ping_linux.h" #include "content/public/common/zygote_fork_delegate_linux.h" -#include "ipc/ipc_descriptors.h" #include "mojo/edk/embedder/embedder.h" #include "sandbox/linux/services/credentials.h" #include "sandbox/linux/services/namespace_sandbox.h"
diff --git a/components/omnibox/browser/clipboard_url_provider.cc b/components/omnibox/browser/clipboard_url_provider.cc index 3fe1074..f2f12d3 100644 --- a/components/omnibox/browser/clipboard_url_provider.cc +++ b/components/omnibox/browser/clipboard_url_provider.cc
@@ -4,6 +4,8 @@ #include "components/omnibox/browser/clipboard_url_provider.h" +#include <algorithm> + #include "base/feature_list.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" @@ -24,7 +26,8 @@ : AutocompleteProvider(AutocompleteProvider::TYPE_CLIPBOARD_URL), client_(client), clipboard_content_(clipboard_content), - history_url_provider_(history_url_provider) { + history_url_provider_(history_url_provider), + current_url_suggested_times_(0) { DCHECK(clipboard_content_); } @@ -39,10 +42,15 @@ return; GURL url; - // If the clipboard does not contain any URL, or the URL on the page is the - // same as the URL in the clipboard, early return. - if (!clipboard_content_->GetRecentURLFromClipboard(&url) || - url == input.current_url()) + // The clipboard does not contain a URL worth suggesting. + if (!clipboard_content_->GetRecentURLFromClipboard(&url)) { + current_url_suggested_ = GURL(); + current_url_suggested_times_ = 0; + return; + } + // The URL on the page is the same as the URL in the clipboard. Don't + // bother suggesting it. + if (url == input.current_url()) return; DCHECK(url.is_valid()); @@ -62,6 +70,18 @@ !matches_.empty()); UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionShownAge", clipboard_content_->GetClipboardContentAge()); + // Record the number of times the currently-offered URL has been suggested. + // This only works over this run of Chrome; if the URL was in the clipboard + // on a previous run, those offerings will not be counted. + if (url == current_url_suggested_) { + current_url_suggested_times_++; + } else { + current_url_suggested_ = url; + current_url_suggested_times_ = 1; + } + UMA_HISTOGRAM_SPARSE_SLOWLY( + "Omnibox.ClipboardSuggestionShownNumTimes", + std::min(current_url_suggested_times_, static_cast<size_t>(20))); // Add the clipboard match. The relevance is 800 to beat ZeroSuggest results. AutocompleteMatch match(this, 800, false, AutocompleteMatchType::CLIPBOARD); @@ -80,3 +100,17 @@ matches_.push_back(match); } + +void ClipboardURLProvider::AddProviderInfo(ProvidersInfo* provider_info) const { + // If a URL wasn't suggested on this most recent focus event, don't bother + // setting |times_returned_results_in_session|, as in effect this URL has + // never been suggested during the current session. (For the purpose of + // this provider, we define a session as intervals between when a URL + // clipboard suggestion changes.) + if (current_url_suggested_times_ == 0) + return; + provider_info->push_back(metrics::OmniboxEventProto_ProviderInfo()); + metrics::OmniboxEventProto_ProviderInfo& new_entry = provider_info->back(); + new_entry.set_provider(AsOmniboxEventProviderType()); + new_entry.set_times_returned_results_in_session(current_url_suggested_times_); +}
diff --git a/components/omnibox/browser/clipboard_url_provider.h b/components/omnibox/browser/clipboard_url_provider.h index ac2762ac..0b54d3e4 100644 --- a/components/omnibox/browser/clipboard_url_provider.h +++ b/components/omnibox/browser/clipboard_url_provider.h
@@ -22,6 +22,7 @@ // AutocompleteProvider implementation. void Start(const AutocompleteInput& input, bool minimal_changes) override; + void AddProviderInfo(ProvidersInfo* provider_info) const override; private: ~ClipboardURLProvider() override; @@ -32,6 +33,11 @@ // Used for efficiency when creating the verbatim match. Can be NULL. HistoryURLProvider* history_url_provider_; + // The current URL suggested and the number of times it has been offered. + // Used for recording metrics. + GURL current_url_suggested_; + size_t current_url_suggested_times_; + DISALLOW_COPY_AND_ASSIGN(ClipboardURLProvider); };
diff --git a/content/app/android/child_process_service_impl.cc b/content/app/android/child_process_service_impl.cc index 5f5fca29..08be4443 100644 --- a/content/app/android/child_process_service_impl.cc +++ b/content/app/android/child_process_service_impl.cc
@@ -23,7 +23,6 @@ #include "content/public/common/content_switches.h" #include "gpu/ipc/common/android/scoped_surface_request_conduit.h" #include "gpu/ipc/common/gpu_surface_lookup.h" -#include "ipc/ipc_descriptors.h" #include "jni/ChildProcessServiceImpl_jni.h" #include "services/service_manager/embedder/shared_file_util.h" #include "services/service_manager/embedder/switches.h"
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc index a677c9d..7bdb162 100644 --- a/content/app/content_main_runner.cc +++ b/content/app/content_main_runner.cc
@@ -54,7 +54,6 @@ #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" #include "content/public/common/sandbox_init.h" -#include "ipc/ipc_descriptors.h" #include "media/base/media.h" #include "ppapi/features/features.h" #include "services/service_manager/embedder/switches.h"
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc index 3458260a..42c7e04 100644 --- a/content/browser/frame_host/render_frame_host_manager.cc +++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -1262,6 +1262,19 @@ // BrowserContext. DCHECK_EQ(new_instance->GetBrowserContext(), browser_context); + // If |new_instance| is a new SiteInstance for a subframe with an isolated + // origin, set its process reuse policy so that such subframes are + // consolidated into existing processes for that isolated origin. + SiteInstanceImpl* new_instance_impl = + static_cast<SiteInstanceImpl*>(new_instance.get()); + auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); + if (!frame_tree_node_->IsMainFrame() && !new_instance_impl->HasProcess() && + new_instance_impl->HasSite() && + policy->IsIsolatedOrigin(url::Origin(new_instance_impl->GetSiteURL()))) { + new_instance_impl->set_process_reuse_policy( + SiteInstanceImpl::ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE); + } + return new_instance; }
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc index 95dab99..d44a1ba 100644 --- a/content/browser/isolated_origin_browsertest.cc +++ b/content/browser/isolated_origin_browsertest.cc
@@ -255,6 +255,88 @@ EXPECT_FALSE(child->current_frame_host()->IsCrossProcessSubframe()); } +// Check that a new isolated origin subframe will attempt to reuse an existing +// process for that isolated origin, even across BrowsingInstances. Also check +// that main frame navigations to an isolated origin keep using the default +// process model and do not reuse existing processes. +IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, SubframeReusesExistingProcess) { + GURL top_url( + embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html")); + EXPECT_TRUE(NavigateToURL(shell(), top_url)); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + FrameTreeNode* child = root->child_at(0); + + // Open an unrelated tab in a separate BrowsingInstance, and navigate it to + // to an isolated origin. This SiteInstance should have a default process + // reuse policy - only subframes attempt process reuse. + GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com", + "/page_with_iframe.html")); + Shell* second_shell = CreateBrowser(); + EXPECT_TRUE(NavigateToURL(second_shell, isolated_url)); + scoped_refptr<SiteInstanceImpl> second_shell_instance = + static_cast<SiteInstanceImpl*>( + second_shell->web_contents()->GetMainFrame()->GetSiteInstance()); + EXPECT_FALSE(second_shell_instance->IsRelatedSiteInstance( + root->current_frame_host()->GetSiteInstance())); + RenderProcessHost* isolated_process = second_shell_instance->GetProcess(); + EXPECT_EQ(SiteInstanceImpl::ProcessReusePolicy::DEFAULT, + second_shell_instance->process_reuse_policy()); + + // Now navigate the first tab's subframe to an isolated origin. See that it + // reuses the existing |isolated_process|. + NavigateIframeToURL(web_contents(), "test_iframe", isolated_url); + EXPECT_EQ(isolated_url, child->current_url()); + EXPECT_EQ(isolated_process, child->current_frame_host()->GetProcess()); + EXPECT_EQ( + SiteInstanceImpl::ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE, + child->current_frame_host()->GetSiteInstance()->process_reuse_policy()); + + EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe()); + EXPECT_EQ(isolated_url.GetOrigin(), + child->current_frame_host()->GetSiteInstance()->GetSiteURL()); + + // The subframe's SiteInstance should still be different from second_shell's + // SiteInstance, and they should be in separate BrowsingInstances. + EXPECT_NE(second_shell_instance, + child->current_frame_host()->GetSiteInstance()); + EXPECT_FALSE(second_shell_instance->IsRelatedSiteInstance( + child->current_frame_host()->GetSiteInstance())); + + // Navigate the second tab to a normal URL with a same-site subframe. This + // leaves only the first tab's subframe in the isolated origin process. + EXPECT_TRUE(NavigateToURL(second_shell, top_url)); + EXPECT_NE(isolated_process, + second_shell->web_contents()->GetMainFrame()->GetProcess()); + + // Navigate the second tab's subframe to an isolated origin, and check that + // this new subframe reuses the isolated process of the subframe in the first + // tab, even though the two are in separate BrowsingInstances. + NavigateIframeToURL(second_shell->web_contents(), "test_iframe", + isolated_url); + FrameTreeNode* second_subframe = + static_cast<WebContentsImpl*>(second_shell->web_contents()) + ->GetFrameTree() + ->root() + ->child_at(0); + EXPECT_EQ(isolated_process, + second_subframe->current_frame_host()->GetProcess()); + EXPECT_NE(child->current_frame_host()->GetSiteInstance(), + second_subframe->current_frame_host()->GetSiteInstance()); + + // Open a third, unrelated tab, navigate it to an isolated origin, and check + // that its main frame doesn't share a process with the existing isolated + // subframes. + Shell* third_shell = CreateBrowser(); + EXPECT_TRUE(NavigateToURL(third_shell, isolated_url)); + SiteInstanceImpl* third_shell_instance = static_cast<SiteInstanceImpl*>( + third_shell->web_contents()->GetMainFrame()->GetSiteInstance()); + EXPECT_NE(third_shell_instance, + second_subframe->current_frame_host()->GetSiteInstance()); + EXPECT_NE(third_shell_instance, + child->current_frame_host()->GetSiteInstance()); + EXPECT_NE(third_shell_instance->GetProcess(), isolated_process); +} + // Check that isolated origins can access cookies. This requires cookie checks // on the IO thread to be aware of isolated origins. IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, Cookies) {
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc index 8eb3248c..3828cf0 100644 --- a/content/browser/media/media_internals.cc +++ b/content/browser/media/media_internals.cc
@@ -31,6 +31,7 @@ #include "content/public/browser/web_ui.h" #include "media/base/audio_parameters.h" #include "media/base/media_log_event.h" +#include "media/base/watch_time_keys.h" #include "media/filters/gpu_video_decoder.h" #if !defined(OS_ANDROID) @@ -463,20 +464,20 @@ MediaInternals::MediaInternalsUMAHandler::MediaInternalsUMAHandler( content::MediaInternals* media_internals) - : watch_time_keys_(media::MediaLog::GetWatchTimeKeys()), - watch_time_power_keys_(media::MediaLog::GetWatchTimePowerKeys()), - mtbr_keys_({{media::MediaLog::kWatchTimeAudioSrc, - media::MediaLog::kMeanTimeBetweenRebuffersAudioSrc}, - {media::MediaLog::kWatchTimeAudioMse, - media::MediaLog::kMeanTimeBetweenRebuffersAudioMse}, - {media::MediaLog::kWatchTimeAudioEme, - media::MediaLog::kMeanTimeBetweenRebuffersAudioEme}, - {media::MediaLog::kWatchTimeAudioVideoSrc, - media::MediaLog::kMeanTimeBetweenRebuffersAudioVideoSrc}, - {media::MediaLog::kWatchTimeAudioVideoMse, - media::MediaLog::kMeanTimeBetweenRebuffersAudioVideoMse}, - {media::MediaLog::kWatchTimeAudioVideoEme, - media::MediaLog::kMeanTimeBetweenRebuffersAudioVideoEme}}, + : watch_time_keys_(media::GetWatchTimeKeys()), + watch_time_power_keys_(media::GetWatchTimePowerKeys()), + mtbr_keys_({{media::kWatchTimeAudioSrc, + media::kMeanTimeBetweenRebuffersAudioSrc}, + {media::kWatchTimeAudioMse, + media::kMeanTimeBetweenRebuffersAudioMse}, + {media::kWatchTimeAudioEme, + media::kMeanTimeBetweenRebuffersAudioEme}, + {media::kWatchTimeAudioVideoSrc, + media::kMeanTimeBetweenRebuffersAudioVideoSrc}, + {media::kWatchTimeAudioVideoMse, + media::kMeanTimeBetweenRebuffersAudioVideoMse}, + {media::kWatchTimeAudioVideoEme, + media::kMeanTimeBetweenRebuffersAudioVideoEme}}, base::KEEP_FIRST_OF_DUPES), media_internals_(media_internals) {} @@ -574,24 +575,23 @@ base::TimeDelta::FromSecondsD(it.value().GetDouble()); } - if (event.params.HasKey(media::MediaLog::kUnderflowCount)) { - event.params.GetInteger(media::MediaLog::kUnderflowCount, + if (event.params.HasKey(media::kWatchTimeUnderflowCount)) { + event.params.GetInteger(media::kWatchTimeUnderflowCount, &player_info.underflow_count); } - if (event.params.HasKey(media::MediaLog::kWatchTimeFinalize)) { + if (event.params.HasKey(media::kWatchTimeFinalize)) { bool should_finalize; - DCHECK(event.params.GetBoolean(media::MediaLog::kWatchTimeFinalize, + DCHECK(event.params.GetBoolean(media::kWatchTimeFinalize, &should_finalize) && should_finalize); FinalizeWatchTime(player_info.has_video, player_info.origin_url, &player_info.underflow_count, &player_info.watch_time_info, FinalizeType::EVERYTHING); - } else if (event.params.HasKey( - media::MediaLog::kWatchTimeFinalizePower)) { + } else if (event.params.HasKey(media::kWatchTimeFinalizePower)) { bool should_finalize; - DCHECK(event.params.GetBoolean(media::MediaLog::kWatchTimeFinalizePower, + DCHECK(event.params.GetBoolean(media::kWatchTimeFinalizePower, &should_finalize) && should_finalize); FinalizeWatchTime(player_info.has_video, player_info.origin_url,
diff --git a/content/browser/media/media_internals_unittest.cc b/content/browser/media/media_internals_unittest.cc index d6ed6be..1773565 100644 --- a/content/browser/media/media_internals_unittest.cc +++ b/content/browser/media/media_internals_unittest.cc
@@ -21,6 +21,7 @@ #include "media/base/audio_parameters.h" #include "media/base/channel_layout.h" #include "media/base/media_log.h" +#include "media/base/watch_time_keys.h" #include "media/blink/watch_time_reporter.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -344,8 +345,8 @@ media_log_(new DirectMediaLog(render_process_id_)), histogram_tester_(new base::HistogramTester()), test_recorder_(new ukm::TestUkmRecorder()), - watch_time_keys_(media::MediaLog::GetWatchTimeKeys()), - watch_time_power_keys_(media::MediaLog::GetWatchTimePowerKeys()) { + watch_time_keys_(media::GetWatchTimeKeys()), + watch_time_power_keys_(media::GetWatchTimePowerKeys()) { media_log_->AddEvent(media_log_->CreateCreatedEvent(kTestOrigin)); } @@ -438,13 +439,12 @@ CycleWatchTimeReporter(); wtr_.reset(); - ExpectWatchTime( - {media::MediaLog::kWatchTimeAudioAll, media::MediaLog::kWatchTimeAudioMse, - media::MediaLog::kWatchTimeAudioEme, media::MediaLog::kWatchTimeAudioAc, - media::MediaLog::kWatchTimeAudioEmbeddedExperience}, - kWatchTimeLate); - ExpectMtbrTime({media::MediaLog::kMeanTimeBetweenRebuffersAudioMse, - media::MediaLog::kMeanTimeBetweenRebuffersAudioEme}, + ExpectWatchTime({media::kWatchTimeAudioAll, media::kWatchTimeAudioMse, + media::kWatchTimeAudioEme, media::kWatchTimeAudioAc, + media::kWatchTimeAudioEmbeddedExperience}, + kWatchTimeLate); + ExpectMtbrTime({media::kMeanTimeBetweenRebuffersAudioMse, + media::kMeanTimeBetweenRebuffersAudioEme}, kWatchTimeLate / 2); ASSERT_EQ(1U, test_recorder_->sources_count()); @@ -472,14 +472,13 @@ CycleWatchTimeReporter(); wtr_.reset(); - ExpectWatchTime({media::MediaLog::kWatchTimeAudioVideoAll, - media::MediaLog::kWatchTimeAudioVideoSrc, - media::MediaLog::kWatchTimeAudioVideoEme, - media::MediaLog::kWatchTimeAudioVideoAc, - media::MediaLog::kWatchTimeAudioVideoEmbeddedExperience}, - kWatchTimeLate); - ExpectMtbrTime({media::MediaLog::kMeanTimeBetweenRebuffersAudioVideoSrc, - media::MediaLog::kMeanTimeBetweenRebuffersAudioVideoEme}, + ExpectWatchTime( + {media::kWatchTimeAudioVideoAll, media::kWatchTimeAudioVideoSrc, + media::kWatchTimeAudioVideoEme, media::kWatchTimeAudioVideoAc, + media::kWatchTimeAudioVideoEmbeddedExperience}, + kWatchTimeLate); + ExpectMtbrTime({media::kMeanTimeBetweenRebuffersAudioVideoSrc, + media::kMeanTimeBetweenRebuffersAudioVideoEme}, kWatchTimeLate / 2); ASSERT_EQ(1U, test_recorder_->sources_count()); @@ -514,18 +513,17 @@ CycleWatchTimeReporter(); // This should finalize the power watch time on battery. - ExpectWatchTime({media::MediaLog::kWatchTimeAudioVideoBattery}, kWatchTime2); + ExpectWatchTime({media::kWatchTimeAudioVideoBattery}, kWatchTime2); ResetHistogramTester(); wtr_.reset(); std::vector<base::StringPiece> normal_keys = { - media::MediaLog::kWatchTimeAudioVideoAll, - media::MediaLog::kWatchTimeAudioVideoSrc, - media::MediaLog::kWatchTimeAudioVideoEme, - media::MediaLog::kWatchTimeAudioVideoEmbeddedExperience}; + media::kWatchTimeAudioVideoAll, media::kWatchTimeAudioVideoSrc, + media::kWatchTimeAudioVideoEme, + media::kWatchTimeAudioVideoEmbeddedExperience}; for (auto key : watch_time_keys_) { - if (key == media::MediaLog::kWatchTimeAudioVideoAc) { + if (key == media::kWatchTimeAudioVideoAc) { histogram_tester_->ExpectUniqueSample( key.as_string(), (kWatchTime3 - kWatchTime2).InMilliseconds(), 1); continue; @@ -576,13 +574,12 @@ CycleWatchTimeReporter(); wtr_.reset(); - ExpectWatchTime( - {media::MediaLog::kWatchTimeAudioVideoBackgroundAll, - media::MediaLog::kWatchTimeAudioVideoBackgroundSrc, - media::MediaLog::kWatchTimeAudioVideoBackgroundEme, - media::MediaLog::kWatchTimeAudioVideoBackgroundAc, - media::MediaLog::kWatchTimeAudioVideoBackgroundEmbeddedExperience}, - kWatchTimeLate); + ExpectWatchTime({media::kWatchTimeAudioVideoBackgroundAll, + media::kWatchTimeAudioVideoBackgroundSrc, + media::kWatchTimeAudioVideoBackgroundEme, + media::kWatchTimeAudioVideoBackgroundAc, + media::kWatchTimeAudioVideoBackgroundEmbeddedExperience}, + kWatchTimeLate); ASSERT_EQ(1U, test_recorder_->sources_count()); ExpectUkmWatchTime(0, 4, kWatchTimeLate); @@ -609,12 +606,11 @@ media_log_->AddEvent( media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_DESTROYED)); - ExpectWatchTime({media::MediaLog::kWatchTimeAudioVideoAll, - media::MediaLog::kWatchTimeAudioVideoSrc, - media::MediaLog::kWatchTimeAudioVideoEme, - media::MediaLog::kWatchTimeAudioVideoAc, - media::MediaLog::kWatchTimeAudioVideoEmbeddedExperience}, - kWatchTimeLate); + ExpectWatchTime( + {media::kWatchTimeAudioVideoAll, media::kWatchTimeAudioVideoSrc, + media::kWatchTimeAudioVideoEme, media::kWatchTimeAudioVideoAc, + media::kWatchTimeAudioVideoEmbeddedExperience}, + kWatchTimeLate); ASSERT_EQ(1U, test_recorder_->sources_count()); ExpectUkmWatchTime(0, 4, kWatchTimeLate); @@ -641,12 +637,11 @@ // Also verify that if UKM has already been destructed, we don't crash. test_recorder_.reset(); internals_->OnProcessTerminatedForTesting(render_process_id_); - ExpectWatchTime({media::MediaLog::kWatchTimeAudioVideoAll, - media::MediaLog::kWatchTimeAudioVideoSrc, - media::MediaLog::kWatchTimeAudioVideoEme, - media::MediaLog::kWatchTimeAudioVideoAc, - media::MediaLog::kWatchTimeAudioVideoEmbeddedExperience}, - kWatchTimeLate); + ExpectWatchTime( + {media::kWatchTimeAudioVideoAll, media::kWatchTimeAudioVideoSrc, + media::kWatchTimeAudioVideoEme, media::kWatchTimeAudioVideoAc, + media::kWatchTimeAudioVideoEmbeddedExperience}, + kWatchTimeLate); } } // namespace content
diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc index d167547..1155857 100644 --- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc +++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
@@ -72,6 +72,7 @@ MediaStreamType stream_type, const media::VideoCaptureParams& params, base::WeakPtr<media::VideoFrameReceiver> receiver, + base::OnceClosure /* connection_lost_cb */, Callbacks* callbacks, base::OnceClosure done_cb) { DCHECK_CURRENTLY_ON(BrowserThread::IO);
diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h index ad5166f1..8b810b1 100644 --- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h +++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h
@@ -31,6 +31,7 @@ MediaStreamType stream_type, const media::VideoCaptureParams& params, base::WeakPtr<media::VideoFrameReceiver> receiver, + base::OnceClosure connection_lost_cb, Callbacks* callbacks, base::OnceClosure done_cb) override;
diff --git a/content/browser/renderer_host/media/mock_video_capture_provider.h b/content/browser/renderer_host/media/mock_video_capture_provider.h index 7c17595..afb659c 100644 --- a/content/browser/renderer_host/media/mock_video_capture_provider.h +++ b/content/browser/renderer_host/media/mock_video_capture_provider.h
@@ -30,11 +30,12 @@ MockVideoCaptureDeviceLauncher(); ~MockVideoCaptureDeviceLauncher() override; - MOCK_METHOD6(DoLaunchDeviceAsync, + MOCK_METHOD7(DoLaunchDeviceAsync, void(const std::string& device_id, MediaStreamType stream_type, const media::VideoCaptureParams& params, base::WeakPtr<media::VideoFrameReceiver>* receiver, + base::OnceClosure* connection_lost_cb, Callbacks* callbacks, base::OnceClosure* done_cb)); @@ -44,10 +45,11 @@ MediaStreamType stream_type, const media::VideoCaptureParams& params, base::WeakPtr<media::VideoFrameReceiver> receiver, + base::OnceClosure connection_lost_cb, Callbacks* callbacks, base::OnceClosure done_cb) override { - DoLaunchDeviceAsync(device_id, stream_type, params, &receiver, callbacks, - &done_cb); + DoLaunchDeviceAsync(device_id, stream_type, params, &receiver, + &connection_lost_cb, callbacks, &done_cb); } };
diff --git a/content/browser/renderer_host/media/service_launched_video_capture_device.cc b/content/browser/renderer_host/media/service_launched_video_capture_device.cc index af54de3..71b8d14 100644 --- a/content/browser/renderer_host/media/service_launched_video_capture_device.cc +++ b/content/browser/renderer_host/media/service_launched_video_capture_device.cc
@@ -7,8 +7,10 @@ namespace content { ServiceLaunchedVideoCaptureDevice::ServiceLaunchedVideoCaptureDevice( - video_capture::mojom::DevicePtr device) - : device_(std::move(device)) { + video_capture::mojom::DevicePtr device, + base::OnceClosure connection_lost_cb) + : device_(std::move(device)), + connection_lost_cb_(std::move(connection_lost_cb)) { // Unretained |this| is safe, because |this| owns |device_|. device_.set_connection_error_handler( base::Bind(&ServiceLaunchedVideoCaptureDevice::OnLostConnectionToDevice, @@ -71,7 +73,7 @@ void ServiceLaunchedVideoCaptureDevice::OnLostConnectionToDevice() { DCHECK(sequence_checker_.CalledOnValidSequence()); - NOTIMPLEMENTED(); + base::ResetAndReturn(&connection_lost_cb_).Run(); } } // namespace content
diff --git a/content/browser/renderer_host/media/service_launched_video_capture_device.h b/content/browser/renderer_host/media/service_launched_video_capture_device.h index 35acd7b..c01194f2 100644 --- a/content/browser/renderer_host/media/service_launched_video_capture_device.h +++ b/content/browser/renderer_host/media/service_launched_video_capture_device.h
@@ -14,8 +14,8 @@ // service. class ServiceLaunchedVideoCaptureDevice : public LaunchedVideoCaptureDevice { public: - explicit ServiceLaunchedVideoCaptureDevice( - video_capture::mojom::DevicePtr device); + ServiceLaunchedVideoCaptureDevice(video_capture::mojom::DevicePtr device, + base::OnceClosure connection_lost_cb); ~ServiceLaunchedVideoCaptureDevice() override; // LaunchedVideoCaptureDevice implementation. @@ -40,6 +40,7 @@ void OnLostConnectionToDevice(); video_capture::mojom::DevicePtr device_; + base::OnceClosure connection_lost_cb_; base::SequenceChecker sequence_checker_; };
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher.cc b/content/browser/renderer_host/media/service_video_capture_device_launcher.cc index 522570a..cbd9f49 100644 --- a/content/browser/renderer_host/media/service_video_capture_device_launcher.cc +++ b/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
@@ -19,6 +19,7 @@ const media::VideoCaptureParams& params, video_capture::mojom::DevicePtr device, base::WeakPtr<media::VideoFrameReceiver> receiver, + base::OnceClosure connection_lost_cb, VideoCaptureDeviceLauncher::Callbacks* callbacks, base::OnceClosure done_cb) { if (abort_requested) { @@ -38,7 +39,8 @@ std::move(receiver_adapter), mojo::MakeRequest(&receiver_proxy)); device->Start(params, std::move(receiver_proxy)); callbacks->OnDeviceLaunched( - base::MakeUnique<ServiceLaunchedVideoCaptureDevice>(std::move(device))); + base::MakeUnique<ServiceLaunchedVideoCaptureDevice>( + std::move(device), std::move(connection_lost_cb))); base::ResetAndReturn(&done_cb).Run(); } @@ -71,6 +73,7 @@ MediaStreamType stream_type, const media::VideoCaptureParams& params, base::WeakPtr<media::VideoFrameReceiver> receiver, + base::OnceClosure connection_lost_cb, Callbacks* callbacks, base::OnceClosure done_cb) { DCHECK(sequence_checker_.CalledOnValidSequence()); @@ -110,7 +113,7 @@ // that |this| stays alive. &ServiceVideoCaptureDeviceLauncher::OnCreateDeviceCallback, base::Unretained(this), params, base::Passed(&device), - std::move(receiver))); + std::move(receiver), base::Passed(&connection_lost_cb))); state_ = State::DEVICE_START_IN_PROGRESS; } @@ -124,6 +127,7 @@ const media::VideoCaptureParams& params, video_capture::mojom::DevicePtr device, base::WeakPtr<media::VideoFrameReceiver> receiver, + base::OnceClosure connection_lost_cb, video_capture::mojom::DeviceAccessResultCode result_code) { DCHECK(sequence_checker_.CalledOnValidSequence()); DCHECK(callbacks_); @@ -135,9 +139,9 @@ callbacks_ = nullptr; switch (result_code) { case video_capture::mojom::DeviceAccessResultCode::SUCCESS: - ConcludeLaunchDeviceWithSuccess(abort_requested, params, - std::move(device), std::move(receiver), - callbacks, std::move(done_cb_)); + ConcludeLaunchDeviceWithSuccess( + abort_requested, params, std::move(device), std::move(receiver), + std::move(connection_lost_cb), callbacks, std::move(done_cb_)); return; case video_capture::mojom::DeviceAccessResultCode::ERROR_DEVICE_NOT_FOUND: case video_capture::mojom::DeviceAccessResultCode::NOT_INITIALIZED:
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher.h b/content/browser/renderer_host/media/service_video_capture_device_launcher.h index 30fcf31..446d206c 100644 --- a/content/browser/renderer_host/media/service_video_capture_device_launcher.h +++ b/content/browser/renderer_host/media/service_video_capture_device_launcher.h
@@ -25,6 +25,7 @@ MediaStreamType stream_type, const media::VideoCaptureParams& params, base::WeakPtr<media::VideoFrameReceiver> receiver, + base::OnceClosure connection_lost_cb, Callbacks* callbacks, base::OnceClosure done_cb) override; void AbortLaunch() override; @@ -42,6 +43,7 @@ const media::VideoCaptureParams& params, video_capture::mojom::DevicePtr device, base::WeakPtr<media::VideoFrameReceiver> receiver, + base::OnceClosure connection_lost_cb, video_capture::mojom::DeviceAccessResultCode result_code); void OnConnectionLostWhileWaitingForCallback();
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc b/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc index 65dd139b..866eb75d 100644 --- a/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc +++ b/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
@@ -8,6 +8,7 @@ #include "base/test/mock_callback.h" #include "base/test/scoped_task_environment.h" #include "base/threading/thread.h" +#include "content/browser/renderer_host/media/service_launched_video_capture_device.h" #include "mojo/public/cpp/bindings/binding.h" #include "services/video_capture/public/interfaces/device_factory.mojom.h" #include "testing/gmock/include/gmock/gmock.h" @@ -78,7 +79,8 @@ std::unique_ptr<mojo::Binding<video_capture::mojom::DeviceFactory>> factory_binding_; std::unique_ptr<ServiceVideoCaptureDeviceLauncher> launcher_; - base::MockCallback<base::OnceClosure> done_cb; + base::MockCallback<base::OnceClosure> connection_lost_cb_; + base::MockCallback<base::OnceClosure> done_cb_; private: DISALLOW_COPY_AND_ASSIGN(ServiceVideoCaptureDeviceLauncherTest); @@ -109,14 +111,16 @@ EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)).Times(1); EXPECT_CALL(mock_callbacks_, OnDeviceLaunchAborted()).Times(0); EXPECT_CALL(mock_callbacks_, OnDeviceLaunchFailed()).Times(0); - EXPECT_CALL(done_cb, Run()).WillOnce(InvokeWithoutArgs([&run_loop]() { + EXPECT_CALL(connection_lost_cb_, Run()).Times(0); + EXPECT_CALL(done_cb_, Run()).WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); })); // Exercise launcher_->LaunchDeviceAsync( kStubDeviceId, content::MEDIA_DEVICE_VIDEO_CAPTURE, kArbitraryParams, - kNullReceiver, &mock_callbacks_, done_cb.Get()); + kNullReceiver, connection_lost_cb_.Get(), &mock_callbacks_, + done_cb_.Get()); run_loop.Run(); } @@ -165,14 +169,16 @@ EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)).Times(0); EXPECT_CALL(mock_callbacks_, OnDeviceLaunchAborted()).Times(1); EXPECT_CALL(mock_callbacks_, OnDeviceLaunchFailed()).Times(0); - EXPECT_CALL(done_cb, Run()).WillOnce(InvokeWithoutArgs([&step_2_run_loop]() { + EXPECT_CALL(connection_lost_cb_, Run()).Times(0); + EXPECT_CALL(done_cb_, Run()).WillOnce(InvokeWithoutArgs([&step_2_run_loop]() { step_2_run_loop.Quit(); })); // Exercise launcher_->LaunchDeviceAsync( kStubDeviceId, content::MEDIA_DEVICE_VIDEO_CAPTURE, kArbitraryParams, - kNullReceiver, &mock_callbacks_, done_cb.Get()); + kNullReceiver, connection_lost_cb_.Get(), &mock_callbacks_, + done_cb_.Get()); step_1_run_loop.Run(); launcher_->AbortLaunch(); @@ -208,14 +214,16 @@ EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)).Times(0); EXPECT_CALL(mock_callbacks_, OnDeviceLaunchAborted()).Times(0); EXPECT_CALL(mock_callbacks_, OnDeviceLaunchFailed()).Times(1); - EXPECT_CALL(done_cb, Run()).WillOnce(InvokeWithoutArgs([&run_loop]() { + EXPECT_CALL(connection_lost_cb_, Run()).Times(0); + EXPECT_CALL(done_cb_, Run()).WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); })); // Exercise launcher_->LaunchDeviceAsync( kStubDeviceId, content::MEDIA_DEVICE_VIDEO_CAPTURE, kArbitraryParams, - kNullReceiver, &mock_callbacks_, done_cb.Get()); + kNullReceiver, connection_lost_cb_.Get(), &mock_callbacks_, + done_cb_.Get()); run_loop.Run(); } @@ -227,7 +235,8 @@ EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)).Times(0); EXPECT_CALL(mock_callbacks_, OnDeviceLaunchAborted()).Times(0); EXPECT_CALL(mock_callbacks_, OnDeviceLaunchFailed()).Times(1); - EXPECT_CALL(done_cb, Run()).WillOnce(InvokeWithoutArgs([&run_loop]() { + EXPECT_CALL(connection_lost_cb_, Run()).Times(0); + EXPECT_CALL(done_cb_, Run()).WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); })); @@ -235,7 +244,8 @@ device_factory_.reset(); launcher_->LaunchDeviceAsync( kStubDeviceId, content::MEDIA_DEVICE_VIDEO_CAPTURE, kArbitraryParams, - kNullReceiver, &mock_callbacks_, done_cb.Get()); + kNullReceiver, connection_lost_cb_.Get(), &mock_callbacks_, + done_cb_.Get()); run_loop.Run(); } @@ -260,14 +270,18 @@ EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)).Times(0); EXPECT_CALL(mock_callbacks_, OnDeviceLaunchAborted()).Times(0); EXPECT_CALL(mock_callbacks_, OnDeviceLaunchFailed()).Times(1); - EXPECT_CALL(done_cb, Run()).WillOnce(InvokeWithoutArgs([&run_loop]() { + // Note: |connection_lost_cb_| is only meant to be called when the connection + // to a successfully-launched device is lost, which is not the case here. + EXPECT_CALL(connection_lost_cb_, Run()).Times(0); + EXPECT_CALL(done_cb_, Run()).WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); })); // Exercise launcher_->LaunchDeviceAsync( kStubDeviceId, content::MEDIA_DEVICE_VIDEO_CAPTURE, kArbitraryParams, - kNullReceiver, &mock_callbacks_, done_cb.Get()); + kNullReceiver, connection_lost_cb_.Get(), &mock_callbacks_, + done_cb_.Get()); run_loop.Run(); @@ -280,4 +294,54 @@ create_device_cb.Run(arbitrary_result_code); } +TEST_F(ServiceVideoCaptureDeviceLauncherTest, + ConnectionLostAfterSuccessfulLaunch) { + video_capture::mojom::DeviceRequest device_request_owned_by_service; + EXPECT_CALL(mock_device_factory_, DoCreateDevice(kStubDeviceId, _, _)) + .WillOnce(Invoke([&device_request_owned_by_service]( + const std::string& device_id, + video_capture::mojom::DeviceRequest* device_request, + const video_capture::mojom::DeviceFactory:: + CreateDeviceCallback& callback) { + // The service holds on to the |device_request|. + device_request_owned_by_service = std::move(*device_request); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind( + [](const video_capture::mojom::DeviceFactory:: + CreateDeviceCallback& callback) { + callback.Run( + video_capture::mojom::DeviceAccessResultCode::SUCCESS); + }, + callback)); + })); + std::unique_ptr<LaunchedVideoCaptureDevice> launched_device; + EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)) + .WillOnce( + Invoke([&launched_device]( + std::unique_ptr<LaunchedVideoCaptureDevice>* device) { + // We must keep the launched device alive, because otherwise it will + // no longer listen for connection errors. + launched_device = std::move(*device); + })); + base::RunLoop step_1_run_loop; + EXPECT_CALL(done_cb_, Run()).WillOnce(InvokeWithoutArgs([&step_1_run_loop]() { + step_1_run_loop.Quit(); + })); + // Exercise step 1 + launcher_->LaunchDeviceAsync( + kStubDeviceId, content::MEDIA_DEVICE_VIDEO_CAPTURE, kArbitraryParams, + kNullReceiver, connection_lost_cb_.Get(), &mock_callbacks_, + done_cb_.Get()); + step_1_run_loop.Run(); + + base::RunLoop step_2_run_loop; + EXPECT_CALL(connection_lost_cb_, Run()).WillOnce(Invoke([&step_2_run_loop]() { + step_2_run_loop.Quit(); + })); + // Exercise step 2: The service cuts/loses the connection + device_request_owned_by_service = nullptr; + step_2_run_loop.Run(); +} + } // namespace content
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc index d67f4ecf..fa67f31 100644 --- a/content/browser/renderer_host/media/video_capture_controller.cc +++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -498,7 +498,6 @@ entry.set_consumer_feedback_observer(launched_device_.get()); if (device_launch_observer_) { device_launch_observer_->OnDeviceLaunched(this); - device_launch_observer_ = nullptr; } } @@ -518,15 +517,25 @@ } } +void VideoCaptureController::OnDeviceConnectionLost() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (device_launch_observer_) { + device_launch_observer_->OnDeviceConnectionLost(this); + device_launch_observer_ = nullptr; + } +} + void VideoCaptureController::CreateAndStartDeviceAsync( const media::VideoCaptureParams& params, VideoCaptureDeviceLaunchObserver* observer, base::OnceClosure done_cb) { DCHECK_CURRENTLY_ON(BrowserThread::IO); device_launch_observer_ = observer; - device_launcher_->LaunchDeviceAsync(device_id_, stream_type_, params, - GetWeakPtrForIOThread(), this, - std::move(done_cb)); + device_launcher_->LaunchDeviceAsync( + device_id_, stream_type_, params, GetWeakPtrForIOThread(), + base::Bind(&VideoCaptureController::OnDeviceConnectionLost, + GetWeakPtrForIOThread()), + this, std::move(done_cb)); } void VideoCaptureController::ReleaseDeviceAsync(base::OnceClosure done_cb) {
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h index e1224f2..04c5540 100644 --- a/content/browser/renderer_host/media/video_capture_controller.h +++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -120,6 +120,8 @@ void OnDeviceLaunchFailed() override; void OnDeviceLaunchAborted() override; + void OnDeviceConnectionLost(); + void CreateAndStartDeviceAsync(const media::VideoCaptureParams& params, VideoCaptureDeviceLaunchObserver* callbacks, base::OnceClosure done_cb);
diff --git a/content/browser/renderer_host/media/video_capture_device_launch_observer.h b/content/browser/renderer_host/media/video_capture_device_launch_observer.h index 49812ba..33e4b1b 100644 --- a/content/browser/renderer_host/media/video_capture_device_launch_observer.h +++ b/content/browser/renderer_host/media/video_capture_device_launch_observer.h
@@ -17,6 +17,7 @@ virtual void OnDeviceLaunched(VideoCaptureController* controller) = 0; virtual void OnDeviceLaunchFailed(VideoCaptureController* controller) = 0; virtual void OnDeviceLaunchAborted() = 0; + virtual void OnDeviceConnectionLost(VideoCaptureController* controller) = 0; }; } // namespace content
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc index bc60336..cc170527 100644 --- a/content/browser/renderer_host/media/video_capture_manager.cc +++ b/content/browser/renderer_host/media/video_capture_manager.cc
@@ -355,6 +355,15 @@ ProcessDeviceStartRequestQueue(); } +void VideoCaptureManager::OnDeviceConnectionLost( + VideoCaptureController* controller) { + const std::string log_message = base::StringPrintf( + "Lost connection to device %d.", controller->serial_id()); + DLOG(WARNING) << log_message; + controller->OnLog(log_message); + controller->OnError(); +} + void VideoCaptureManager::ConnectClient( media::VideoCaptureSessionId session_id, const media::VideoCaptureParams& params, @@ -430,6 +439,10 @@ // If controller has no more clients, delete controller and device. DestroyControllerIfNoClients(controller); + + if (controllers_.empty()) { + video_capture_provider_->Uninitialize(); + } } void VideoCaptureManager::PauseCaptureForClient(
diff --git a/content/browser/renderer_host/media/video_capture_manager.h b/content/browser/renderer_host/media/video_capture_manager.h index be614cfeb..de07dac 100644 --- a/content/browser/renderer_host/media/video_capture_manager.h +++ b/content/browser/renderer_host/media/video_capture_manager.h
@@ -183,6 +183,7 @@ void OnDeviceLaunched(VideoCaptureController* controller) override; void OnDeviceLaunchFailed(VideoCaptureController* controller) override; void OnDeviceLaunchAborted() override; + void OnDeviceConnectionLost(VideoCaptureController* controller) override; // Retrieves camera calibration information for a particular device. Returns // nullopt_t if the |device_id| is not found or camera calibration information
diff --git a/content/browser/renderer_host/media/video_capture_provider.h b/content/browser/renderer_host/media/video_capture_provider.h index 10135040..81fc0f1 100644 --- a/content/browser/renderer_host/media/video_capture_provider.h +++ b/content/browser/renderer_host/media/video_capture_provider.h
@@ -41,6 +41,7 @@ MediaStreamType stream_type, const media::VideoCaptureParams& params, base::WeakPtr<media::VideoFrameReceiver> receiver, + base::OnceClosure connection_lost_cb, Callbacks* callbacks, base::OnceClosure done_cb) = 0;
diff --git a/content/browser/renderer_host/media/video_capture_provider_switcher.cc b/content/browser/renderer_host/media/video_capture_provider_switcher.cc index eaeb799..3cc09af 100644 --- a/content/browser/renderer_host/media/video_capture_provider_switcher.cc +++ b/content/browser/renderer_host/media/video_capture_provider_switcher.cc
@@ -24,6 +24,7 @@ MediaStreamType stream_type, const media::VideoCaptureParams& params, base::WeakPtr<media::VideoFrameReceiver> receiver, + base::OnceClosure connection_lost_cb, Callbacks* callbacks, base::OnceClosure done_cb) override { if (stream_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) { @@ -33,8 +34,8 @@ base::Bind(&VideoCaptureDeviceLauncher::AbortLaunch, base::Unretained(media_device_launcher_.get())); return media_device_launcher_->LaunchDeviceAsync( - device_id, stream_type, params, std::move(receiver), callbacks, - std::move(done_cb)); + device_id, stream_type, params, std::move(receiver), + std::move(connection_lost_cb), callbacks, std::move(done_cb)); } // Use of Unretained() is safe, because |other_types_launcher_| is owned by // |this|. @@ -42,8 +43,8 @@ base::Bind(&VideoCaptureDeviceLauncher::AbortLaunch, base::Unretained(other_types_launcher_.get())); return other_types_launcher_->LaunchDeviceAsync( - device_id, stream_type, params, std::move(receiver), callbacks, - std::move(done_cb)); + device_id, stream_type, params, std::move(receiver), + std::move(connection_lost_cb), callbacks, std::move(done_cb)); } void AbortLaunch() override {
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h index 1b77386..dd6b845 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -593,6 +593,10 @@ cc::CompositorFrame frame) override; void DidNotProduceFrame(const cc::BeginFrameAck& ack) override; + // Signals that a frame with token |frame_token| was finished processing. If + // there are any queued messages belonging to it, they will be processed. + void DidProcessFrame(uint32_t frame_token); + protected: // --------------------------------------------------------------------------- // The following method is overridden by RenderViewHost to send upwards to @@ -754,10 +758,6 @@ // Used for UMA logging how long the renderer was unresponsive. void LogHangMonitorUnresponsive(); - // Signals that a frame with token |frame_token| was finished processing. If - // there are any queued messages belonging to it, they will be processed. - void DidProcessFrame(uint32_t frame_token); - // Once both the frame and its swap messages arrive, we call this method to // process the messages. Virtual for tests. virtual void ProcessSwapMessages(std::vector<IPC::Message> messages);
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc index df540b7..af376b9 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.cc +++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -1281,6 +1281,9 @@ is_mobile_optimized); } + if (host_ && frame_metadata.frame_token) + host_->DidProcessFrame(frame_metadata.frame_token); + // This is a subset of OnSwapCompositorFrame() used in the synchronous // compositor flow. OnFrameMetadataUpdated(frame_metadata.Clone(), false);
diff --git a/content/browser/webrtc/webrtc_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_image_capture_browsertest.cc index d5641934..ca3751d 100644 --- a/content/browser/webrtc/webrtc_image_capture_browsertest.cc +++ b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
@@ -39,9 +39,10 @@ // platforms where the ImageCaptureCode is landed, https://crbug.com/656810 static struct TargetCamera { bool use_fake; -} const kTestParameters[] = {{true}, -#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_ANDROID) - {false} +} const kTestParameters[] = { + {true}, +#if defined(OS_LINUX) + {false} #endif };
diff --git a/content/common/zygote_commands_linux.h b/content/common/zygote_commands_linux.h index c67593d..24f3bf6 100644 --- a/content/common/zygote_commands_linux.h +++ b/content/common/zygote_commands_linux.h
@@ -8,7 +8,6 @@ #include <stddef.h> #include "base/posix/global_descriptors.h" -#include "ipc/ipc_descriptors.h" namespace content {
diff --git a/content/public/common/content_descriptors.h b/content/public/common/content_descriptors.h index 0e7b01c..35bf7ab 100644 --- a/content/public/common/content_descriptors.h +++ b/content/public/common/content_descriptors.h
@@ -6,12 +6,11 @@ #define CONTENT_PUBLIC_COMMON_CONTENT_DESCRIPTORS_H_ #include "build/build_config.h" -#include "ipc/ipc_descriptors.h" // This is a list of global descriptor keys to be used with the // base::GlobalDescriptors object (see base/posix/global_descriptors.h) enum { - kCrashDumpSignal = kIPCDescriptorMax, + kCrashDumpSignal = 0, kSandboxIPCChannel, // https://chromium.googlesource.com/chromium/src/+/master/docs/linux_sandbox_ipc.md kMojoIPCChannel, kFieldTrialDescriptor,
diff --git a/content/renderer/android/synchronous_compositor_frame_sink.cc b/content/renderer/android/synchronous_compositor_frame_sink.cc index 51e5c73..a5d7fb8 100644 --- a/content/renderer/android/synchronous_compositor_frame_sink.cc +++ b/content/renderer/android/synchronous_compositor_frame_sink.cc
@@ -323,7 +323,6 @@ sync_client_->SubmitCompositorFrame(compositor_frame_sink_id_, std::move(submit_frame)); - DeliverMessages(); did_submit_frame_ = true; } @@ -343,12 +342,14 @@ DCHECK(CalledOnValidThread()); TRACE_EVENT0("renderer", "SynchronousCompositorFrameSink::FallbackTickFired"); base::AutoReset<bool> in_fallback_tick(&fallback_tick_running_, true); + frame_swap_message_queue_->NotifyFramesAreDiscarded(true); SkBitmap bitmap; bitmap.allocN32Pixels(1, 1); bitmap.eraseColor(0); SkCanvas canvas(bitmap); fallback_tick_pending_ = false; DemandDrawSw(&canvas); + frame_swap_message_queue_->NotifyFramesAreDiscarded(false); } void SynchronousCompositorFrameSink::Invalidate() {
diff --git a/content/renderer/gpu/frame_swap_message_queue.cc b/content/renderer/gpu/frame_swap_message_queue.cc index cf3107b8..3f43b95d 100644 --- a/content/renderer/gpu/frame_swap_message_queue.cc +++ b/content/renderer/gpu/frame_swap_message_queue.cc
@@ -112,9 +112,11 @@ } // namespace -FrameSwapMessageQueue::FrameSwapMessageQueue() +FrameSwapMessageQueue::FrameSwapMessageQueue(int32_t routing_id) : visual_state_queue_(new VisualStateQueue()), - swap_queue_(new SwapQueue()) { + swap_queue_(new SwapQueue()), + routing_id_(routing_id) { + DETACH_FROM_THREAD(impl_thread_checker_); } FrameSwapMessageQueue::~FrameSwapMessageQueue() { @@ -210,4 +212,15 @@ return ++last_used_frame_token_; } +void FrameSwapMessageQueue::NotifyFramesAreDiscarded( + bool frames_are_discarded) { + DCHECK_CALLED_ON_VALID_THREAD(impl_thread_checker_); + frames_are_discarded_ = frames_are_discarded; +} + +bool FrameSwapMessageQueue::AreFramesDiscarded() { + DCHECK_CALLED_ON_VALID_THREAD(impl_thread_checker_); + return frames_are_discarded_; +} + } // namespace content
diff --git a/content/renderer/gpu/frame_swap_message_queue.h b/content/renderer/gpu/frame_swap_message_queue.h index 0b9e1c1..1dccf1d 100644 --- a/content/renderer/gpu/frame_swap_message_queue.h +++ b/content/renderer/gpu/frame_swap_message_queue.h
@@ -13,6 +13,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/synchronization/lock.h" +#include "base/threading/thread_checker.h" #include "cc/output/swap_promise.h" #include "content/common/content_export.h" #include "content/renderer/message_delivery_policy.h" @@ -37,7 +38,7 @@ virtual ~SendMessageScope() {} }; - FrameSwapMessageQueue(); + explicit FrameSwapMessageQueue(int32_t routing_id); // Queues message to be returned on a matching DrainMessages call. // @@ -98,6 +99,11 @@ uint32_t AllocateFrameToken(); + int32_t routing_id() const { return routing_id_; } + + void NotifyFramesAreDiscarded(bool frames_are_discarded); + bool AreFramesDiscarded(); + private: friend class base::RefCountedThreadSafe<FrameSwapMessageQueue>; @@ -110,6 +116,9 @@ std::unique_ptr<FrameSwapMessageSubQueue> swap_queue_; std::vector<std::unique_ptr<IPC::Message>> next_drain_messages_; uint32_t last_used_frame_token_ = 0; + int32_t routing_id_ = 0; + bool frames_are_discarded_ = false; + THREAD_CHECKER(impl_thread_checker_); DISALLOW_COPY_AND_ASSIGN(FrameSwapMessageQueue); };
diff --git a/content/renderer/gpu/frame_swap_message_queue_unittest.cc b/content/renderer/gpu/frame_swap_message_queue_unittest.cc index 54e4f7d..75d0e423 100644 --- a/content/renderer/gpu/frame_swap_message_queue_unittest.cc +++ b/content/renderer/gpu/frame_swap_message_queue_unittest.cc
@@ -18,7 +18,7 @@ : first_message_(41, 1, IPC::Message::PRIORITY_NORMAL), second_message_(42, 2, IPC::Message::PRIORITY_NORMAL), third_message_(43, 3, IPC::Message::PRIORITY_NORMAL), - queue_(new FrameSwapMessageQueue()) {} + queue_(new FrameSwapMessageQueue(0)) {} protected: void QueueNextSwapMessage(std::unique_ptr<IPC::Message> msg) {
diff --git a/content/renderer/gpu/queue_message_swap_promise.cc b/content/renderer/gpu/queue_message_swap_promise.cc index 22745027..ad4b91e 100644 --- a/content/renderer/gpu/queue_message_swap_promise.cc +++ b/content/renderer/gpu/queue_message_swap_promise.cc
@@ -5,6 +5,7 @@ #include "content/renderer/gpu/queue_message_swap_promise.h" #include "base/command_line.h" +#include "content/common/view_messages.h" #include "content/public/common/content_switches.h" #include "content/public/renderer/render_thread.h" #include "content/renderer/gpu/frame_swap_message_queue.h" @@ -48,7 +49,22 @@ DCHECK(!completed_); #endif message_queue_->DidSwap(source_frame_number_); - // The OutputSurface will take care of the Drain+Send. + + if (!message_queue_->AreFramesDiscarded()) { + std::unique_ptr<FrameSwapMessageQueue::SendMessageScope> + send_message_scope = message_queue_->AcquireSendMessageScope(); + std::vector<std::unique_ptr<IPC::Message>> messages; + message_queue_->DrainMessages(&messages); + std::vector<IPC::Message> messages_to_send; + FrameSwapMessageQueue::TransferMessages(&messages, &messages_to_send); + if (!messages_to_send.empty()) { + metadata->frame_token = message_queue_->AllocateFrameToken(); + message_sender_->Send(new ViewHostMsg_FrameSwapMessages( + message_queue_->routing_id(), metadata->frame_token, + messages_to_send)); + } + } + PromiseCompleted(); }
diff --git a/content/renderer/gpu/queue_message_swap_promise_unittest.cc b/content/renderer/gpu/queue_message_swap_promise_unittest.cc index 18d3b2c..a02cc38 100644 --- a/content/renderer/gpu/queue_message_swap_promise_unittest.cc +++ b/content/renderer/gpu/queue_message_swap_promise_unittest.cc
@@ -13,6 +13,7 @@ #include "base/memory/ptr_util.h" #include "base/test/scoped_task_environment.h" #include "cc/output/swap_promise.h" +#include "content/common/view_messages.h" #include "content/renderer/gpu/frame_swap_message_queue.h" #include "content/renderer/gpu/render_widget_compositor.h" #include "content/renderer/render_widget.h" @@ -39,16 +40,34 @@ TestSyncMessageFilter() : IPC::SyncMessageFilter(nullptr) {} bool Send(IPC::Message* message) override { - messages_.push_back(base::WrapUnique(message)); + if (message->type() == ViewHostMsg_FrameSwapMessages::ID) { + ViewHostMsg_FrameSwapMessages::Param param; + ViewHostMsg_FrameSwapMessages::Read(message, ¶m); + std::vector<IPC::Message> messages = std::get<1>(param); + last_swap_messages_.clear(); + for (const IPC::Message& message : messages) { + last_swap_messages_.push_back(base::MakeUnique<IPC::Message>(message)); + } + delete message; + } else { + direct_send_messages_.push_back(base::WrapUnique(message)); + } return true; } - std::vector<std::unique_ptr<IPC::Message>>& messages() { return messages_; } + std::vector<std::unique_ptr<IPC::Message>>& last_swap_messages() { + return last_swap_messages_; + } + + const std::vector<std::unique_ptr<IPC::Message>>& direct_send_messages() { + return direct_send_messages_; + } private: ~TestSyncMessageFilter() override {} - std::vector<std::unique_ptr<IPC::Message>> messages_; + std::vector<std::unique_ptr<IPC::Message>> direct_send_messages_; + std::vector<std::unique_ptr<IPC::Message>> last_swap_messages_; DISALLOW_COPY_AND_ASSIGN(TestSyncMessageFilter); }; @@ -61,7 +80,7 @@ class QueueMessageSwapPromiseTest : public testing::Test { public: QueueMessageSwapPromiseTest() - : frame_swap_message_queue_(new FrameSwapMessageQueue()), + : frame_swap_message_queue_(new FrameSwapMessageQueue(0)), sync_message_filter_(new TestSyncMessageFilter()) {} ~QueueMessageSwapPromiseTest() override {} @@ -76,7 +95,11 @@ } const std::vector<std::unique_ptr<IPC::Message>>& DirectSendMessages() { - return sync_message_filter_->messages(); + return sync_message_filter_->direct_send_messages(); + } + + std::vector<std::unique_ptr<IPC::Message>>& LastSwapMessages() { + return sync_message_filter_->last_swap_messages(); } std::vector<std::unique_ptr<IPC::Message>>& NextSwapMessages() { @@ -100,6 +123,10 @@ return false; } + bool LastSwapHasMessage(const IPC::Message& message) { + return ContainsMessage(LastSwapMessages(), message); + } + bool NextSwapHasMessage(const IPC::Message& message) { return ContainsMessage(NextSwapMessages(), message); } @@ -118,7 +145,7 @@ for (const auto& promise : promises_) { if (promise.get()) { promise->DidActivate(); - promise->WillSwap(NULL); + promise->WillSwap(&dummy_metadata_); promise->DidSwap(); } } @@ -133,6 +160,7 @@ scoped_refptr<TestSyncMessageFilter> sync_message_filter_; std::vector<IPC::Message> messages_; std::vector<std::unique_ptr<cc::SwapPromise>> promises_; + cc::CompositorFrameMetadata dummy_metadata_; private: std::vector<std::unique_ptr<IPC::Message>> next_swap_messages_; @@ -149,13 +177,12 @@ ASSERT_TRUE(promises_[0].get()); promises_[0]->DidActivate(); - promises_[0]->WillSwap(NULL); + promises_[0]->WillSwap(&dummy_metadata_); promises_[0]->DidSwap(); EXPECT_TRUE(DirectSendMessages().empty()); - EXPECT_FALSE(frame_swap_message_queue_->Empty()); - // frame_swap_message_queue_->WillSwap(1); - EXPECT_TRUE(NextSwapHasMessage(messages_[0])); + EXPECT_TRUE(frame_swap_message_queue_->Empty()); + EXPECT_TRUE(LastSwapHasMessage(messages_[0])); } TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicyNeedsAtMostOnePromise) { @@ -181,7 +208,7 @@ promises_[0]->DidNotSwap(cc::SwapPromise::COMMIT_NO_UPDATE); EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[0])); - EXPECT_TRUE(NextSwapMessages().empty()); + EXPECT_TRUE(LastSwapMessages().empty()); EXPECT_TRUE(frame_swap_message_queue_->Empty()); } @@ -194,7 +221,7 @@ promises_[0]->DidNotSwap(cc::SwapPromise::SWAP_FAILS); EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[0])); - EXPECT_TRUE(NextSwapMessages().empty()); + EXPECT_TRUE(LastSwapMessages().empty()); EXPECT_TRUE(frame_swap_message_queue_->Empty()); } @@ -207,6 +234,7 @@ promises_[0]->DidNotSwap(cc::SwapPromise::COMMIT_FAILS); EXPECT_TRUE(DirectSendMessages().empty()); + EXPECT_TRUE(LastSwapMessages().empty()); EXPECT_FALSE(frame_swap_message_queue_->Empty()); frame_swap_message_queue_->DidSwap(2); EXPECT_TRUE(NextSwapHasMessage(messages_[0])); @@ -254,11 +282,11 @@ QueueMessages(data, arraysize(data)); promises_[0]->DidActivate(); - promises_[0]->WillSwap(NULL); + promises_[0]->WillSwap(&dummy_metadata_); promises_[0]->DidSwap(); ASSERT_FALSE(promises_[1].get()); std::vector<std::unique_ptr<IPC::Message>> messages; - messages.swap(NextSwapMessages()); + messages.swap(LastSwapMessages()); EXPECT_EQ(2u, messages.size()); EXPECT_TRUE(ContainsMessage(messages, messages_[0])); EXPECT_TRUE(ContainsMessage(messages, messages_[1]));
diff --git a/content/renderer/gpu/renderer_compositor_frame_sink.cc b/content/renderer/gpu/renderer_compositor_frame_sink.cc index 7c14ba38..90a0846 100644 --- a/content/renderer/gpu/renderer_compositor_frame_sink.cc +++ b/content/renderer/gpu/renderer_compositor_frame_sink.cc
@@ -16,7 +16,6 @@ #include "cc/output/managed_memory_policy.h" #include "content/common/view_messages.h" #include "content/public/common/content_switches.h" -#include "content/renderer/gpu/frame_swap_message_queue.h" #include "content/renderer/render_thread_impl.h" #include "gpu/command_buffer/client/context_support.h" #include "gpu/command_buffer/client/gles2_interface.h" @@ -34,8 +33,7 @@ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, cc::SharedBitmapManager* shared_bitmap_manager, cc::mojom::MojoCompositorFrameSinkPtrInfo sink_info, - cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request, - scoped_refptr<FrameSwapMessageQueue> swap_frame_message_queue) + cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request) : ClientCompositorFrameSink(std::move(context_provider), std::move(worker_context_provider), gpu_memory_buffer_manager, @@ -47,10 +45,8 @@ compositor_frame_sink_filter_( RenderThreadImpl::current()->compositor_message_filter()), message_sender_(RenderThreadImpl::current()->sync_message_filter()), - frame_swap_message_queue_(swap_frame_message_queue), routing_id_(routing_id) { DCHECK(compositor_frame_sink_filter_); - DCHECK(frame_swap_message_queue_); DCHECK(message_sender_); } @@ -59,8 +55,7 @@ std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source, scoped_refptr<cc::VulkanContextProvider> vulkan_context_provider, cc::mojom::MojoCompositorFrameSinkPtrInfo sink_info, - cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request, - scoped_refptr<FrameSwapMessageQueue> swap_frame_message_queue) + cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request) : ClientCompositorFrameSink(std::move(vulkan_context_provider), std::move(synthetic_begin_frame_source), std::move(sink_info), @@ -69,10 +64,8 @@ compositor_frame_sink_filter_( RenderThreadImpl::current()->compositor_message_filter()), message_sender_(RenderThreadImpl::current()->sync_message_filter()), - frame_swap_message_queue_(swap_frame_message_queue), routing_id_(routing_id) { DCHECK(compositor_frame_sink_filter_); - DCHECK(frame_swap_message_queue_); DCHECK(message_sender_); } @@ -102,22 +95,6 @@ void RendererCompositorFrameSink::SubmitCompositorFrame( cc::CompositorFrame frame) { - { - std::unique_ptr<FrameSwapMessageQueue::SendMessageScope> - send_message_scope = - frame_swap_message_queue_->AcquireSendMessageScope(); - std::vector<std::unique_ptr<IPC::Message>> messages; - frame_swap_message_queue_->DrainMessages(&messages); - std::vector<IPC::Message> messages_to_send; - FrameSwapMessageQueue::TransferMessages(&messages, &messages_to_send); - if (!messages_to_send.empty()) { - frame.metadata.frame_token = - frame_swap_message_queue_->AllocateFrameToken(); - message_sender_->Send(new ViewHostMsg_FrameSwapMessages( - routing_id_, frame.metadata.frame_token, messages_to_send)); - } - // ~send_message_scope. - } auto new_surface_properties = RenderWidgetSurfaceProperties::FromCompositorFrame(frame); ClientCompositorFrameSink::SubmitCompositorFrame(std::move(frame));
diff --git a/content/renderer/gpu/renderer_compositor_frame_sink.h b/content/renderer/gpu/renderer_compositor_frame_sink.h index 58a20a4..6db24f0b 100644 --- a/content/renderer/gpu/renderer_compositor_frame_sink.h +++ b/content/renderer/gpu/renderer_compositor_frame_sink.h
@@ -41,7 +41,6 @@ } namespace content { -class FrameSwapMessageQueue; // This class can be created only on the main thread, but then becomes pinned // to a fixed thread when BindToClient is called. @@ -57,16 +56,14 @@ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, cc::SharedBitmapManager* shared_bitmap_manager, cc::mojom::MojoCompositorFrameSinkPtrInfo sink_info, - cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request, - scoped_refptr<FrameSwapMessageQueue> swap_frame_message_queue); + cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request); RendererCompositorFrameSink( int32_t routing_id, std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source, scoped_refptr<cc::VulkanContextProvider> vulkan_context_provider, cc::mojom::MojoCompositorFrameSinkPtrInfo sink_info, - cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request, - scoped_refptr<FrameSwapMessageQueue> swap_frame_message_queue); + cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request); ~RendererCompositorFrameSink() override; // Overriden from viz::ClientCompositorFrameSink. @@ -105,7 +102,6 @@ compositor_frame_sink_filter_handler_; scoped_refptr<RendererCompositorFrameSinkProxy> compositor_frame_sink_proxy_; scoped_refptr<IPC::SyncMessageFilter> message_sender_; - scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue_; int routing_id_; RenderWidgetSurfaceProperties current_surface_properties_;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 604b384b..7d56be9 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc
@@ -1912,7 +1912,7 @@ callback.Run(base::MakeUnique<RendererCompositorFrameSink>( routing_id, std::move(synthetic_begin_frame_source), std::move(vulkan_context_provider), std::move(sink_info), - std::move(client_request), std::move(frame_swap_message_queue))); + std::move(client_request))); return; } } @@ -1940,7 +1940,7 @@ callback.Run(base::MakeUnique<RendererCompositorFrameSink>( routing_id, std::move(synthetic_begin_frame_source), nullptr, nullptr, nullptr, shared_bitmap_manager(), std::move(sink_info), - std::move(client_request), std::move(frame_swap_message_queue))); + std::move(client_request))); return; } @@ -2013,7 +2013,7 @@ routing_id, std::move(synthetic_begin_frame_source), std::move(context_provider), std::move(worker_context_provider), GetGpuMemoryBufferManager(), nullptr, std::move(sink_info), - std::move(client_request), std::move(frame_swap_message_queue)))); + std::move(client_request)))); } AssociatedInterfaceRegistry*
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index 40f6f5a..0b676bf3 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc
@@ -370,7 +370,7 @@ device_scale_factor_(screen_info_.device_scale_factor), monitor_composition_info_(false), popup_origin_scale_for_emulation_(0.f), - frame_swap_message_queue_(new FrameSwapMessageQueue()), + frame_swap_message_queue_(new FrameSwapMessageQueue(routing_id_)), resizing_mode_selector_(new ResizingModeSelector()), has_host_context_menu_location_(false), has_added_input_handler_(false),
diff --git a/content/shell/browser/shell_views.cc b/content/shell/browser/shell_views.cc index 6bfe64c..b15581e9 100644 --- a/content/shell/browser/shell_views.cc +++ b/content/shell/browser/shell_views.cc
@@ -113,7 +113,7 @@ private: // Initialize the UI control contained in shell window void InitShellWindow() { - set_background(views::Background::CreateStandardPanelBackground()); + SetBackground(views::CreateStandardPanelBackground()); views::GridLayout* layout = new views::GridLayout(this); SetLayoutManager(layout);
diff --git a/ios/chrome/browser/web/window_open_by_dom_egtest.mm b/ios/chrome/browser/web/window_open_by_dom_egtest.mm index 077be28..631be43 100644 --- a/ios/chrome/browser/web/window_open_by_dom_egtest.mm +++ b/ios/chrome/browser/web/window_open_by_dom_egtest.mm
@@ -200,6 +200,17 @@ assertWithMatcher:grey_notNil()]; } +// Tests opening a child window using the following link +// <a href="data:text/html,<script>window.location='about:newtab';</script>" +// target="_blank"> +// TODO(crbug.com/687863): Enable this test. +- (void)DISABLED_testWindowOpenWithAboutNewTabScript { + TapWebViewElementWithId("webScenarioWindowOpenWithAboutNewTabScript"); + AssertMainTabCount(2); + [[EarlGrey selectElementWithMatcher:OmniboxText("about:newtab")] + assertWithMatcher:grey_notNil()]; +} + // Tests that closing the current window using DOM fails. - (void)testCloseWindowNotOpenByDOM { TapWebViewElementWithId("webScenarioWindowClose");
diff --git a/ios/chrome/test/ocmock/BUILD.gn b/ios/chrome/test/ocmock/BUILD.gn index 1b83041..6c079a2 100644 --- a/ios/chrome/test/ocmock/BUILD.gn +++ b/ios/chrome/test/ocmock/BUILD.gn
@@ -7,8 +7,6 @@ sources = [ "OCMockObject+BreakpadControllerTesting.h", "OCMockObject+BreakpadControllerTesting.mm", - "scoped_mock_object.h", - "scoped_verifying_mock_object.h", ] deps = [ "//base",
diff --git a/ios/chrome/test/ocmock/scoped_mock_object.h b/ios/chrome/test/ocmock/scoped_mock_object.h deleted file mode 100644 index f41ad8d..0000000 --- a/ios/chrome/test/ocmock/scoped_mock_object.h +++ /dev/null
@@ -1,20 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef IOS_CHROME_TEST_OCMOCK_SCOPED_MOCK_OBJECT_H_ -#define IOS_CHROME_TEST_OCMOCK_SCOPED_MOCK_OBJECT_H_ - -#import "base/mac/scoped_nsobject.h" -#import "third_party/ocmock/OCMock/OCMock.h" - -// Helper class that constructs an OCMock and manages ownership of it. -template <typename NST> -class ScopedMockObject : public base::scoped_nsobject<id> { - public: - ScopedMockObject() - : base::scoped_nsobject<id>( - [[OCMockObject mockForClass:[NST class]] retain]) {} -}; - -#endif // IOS_CHROME_TEST_OCMOCK_SCOPED_MOCK_OBJECT_H_
diff --git a/ios/chrome/test/ocmock/scoped_verifying_mock_object.h b/ios/chrome/test/ocmock/scoped_verifying_mock_object.h deleted file mode 100644 index cd0e0e41..0000000 --- a/ios/chrome/test/ocmock/scoped_verifying_mock_object.h +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef IOS_CHROME_TEST_OCMOCK_SCOPED_VERIFYING_MOCK_OBJECT_H_ -#define IOS_CHROME_TEST_OCMOCK_SCOPED_VERIFYING_MOCK_OBJECT_H_ - -#import "ios/chrome/test/ocmock/scoped_mock_object.h" -#include "third_party/ocmock/gtest_support.h" - -// Helper class that constructs an OCMock and automatically verifies the mock -// upon destruction. -template <typename NST> -class ScopedVerifyingMockObject : public ScopedMockObject<NST> { - public: - ScopedVerifyingMockObject() {} - - ~ScopedVerifyingMockObject() { - EXPECT_OCMOCK_VERIFY(ScopedMockObject<NST>::get()); - } -}; - -#endif // IOS_CHROME_TEST_OCMOCK_SCOPED_VERIFYING_MOCK_OBJECT_H_
diff --git a/ios/testing/data/http_server_files/window_open.html b/ios/testing/data/http_server_files/window_open.html index c604f38..77ccf862 100644 --- a/ios/testing/data/http_server_files/window_open.html +++ b/ios/testing/data/http_server_files/window_open.html
@@ -128,6 +128,18 @@ <td>about:blank opened in a new window, with an href and a preventDefault<br></td> </tr> + <tr id="_webScenarioWindowOpenWithAboutNewTabScript"> + <td> + <a href="data:text/html,<script>window.location='about:newtab';</script>" + target="_blank" + name="webScenarioWindowOpenWithAboutNewTabScript" + id="webScenarioWindowOpenWithAboutNewTabScript"> + webScenarioWindowOpenWithAboutNewTabScript + </a> + </td> + <td>about:blank opened in a new window, using "window.location='about:newtab" script<br></td> + </tr> + <tr id="_webScenarioWindowOpenAndSetLocation"> <td> <script>
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm index 08f22bf..314f5b2 100644 --- a/ios/web/web_state/ui/crw_web_controller.mm +++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -302,9 +302,6 @@ // Last URL change reported to webWill/DidStartLoadingURL. Used to detect page // location changes (client redirects) in practice. GURL _lastRegisteredRequestURL; - // Last URL change reported to webDidStartLoadingURL. Used to detect page - // location changes in practice. - GURL _URLOnStartLoading; // Page loading phase. web::LoadPhase _loadPhase; // The web::PageDisplayState recorded when the page starts loading. @@ -1636,7 +1633,6 @@ if (!weakSelf || weakSelf.get()->_isBeingDestroyed) return; base::scoped_nsobject<CRWWebController> strongSelf([weakSelf retain]); - strongSelf.get()->_URLOnStartLoading = URL; strongSelf.get()->_lastRegisteredRequestURL = URL; }]; } @@ -2742,7 +2738,6 @@ return NO; } NSString* stateObject = base::SysUTF8ToNSString(stateObjectJSON); - _URLOnStartLoading = pushURL; _lastRegisteredRequestURL = pushURL; // If the user interacted with the page, categorize it as a link navigation. @@ -2809,7 +2804,6 @@ return NO; } NSString* stateObject = base::SysUTF8ToNSString(stateObjectJSON); - _URLOnStartLoading = replaceURL; _lastRegisteredRequestURL = replaceURL; [self replaceStateWithPageURL:replaceURL stateObject:stateObject]; NSString* replaceStateJS = [self javaScriptToReplaceWebViewURL:replaceURL @@ -2872,7 +2866,6 @@ - (void)didStartLoadingURL:(const GURL&)URL { _loadPhase = web::PAGE_LOADING; - _URLOnStartLoading = URL; _displayStateOnStartLoading = self.pageDisplayState; self.userInteractionRegistered = NO; @@ -4019,8 +4012,6 @@ [_webView addGestureRecognizer:recognizer]; } - _URLOnStartLoading = _defaultURL; - // WKWebViews with invalid or empty frames have exhibited rendering bugs, so // resize the view to match the container view upon creation. [_webView setFrame:[_containerView bounds]];
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn index a3aaaa3..99e93cf 100644 --- a/ipc/BUILD.gn +++ b/ipc/BUILD.gn
@@ -40,7 +40,6 @@ "ipc_channel_proxy.h", "ipc_channel_reader.cc", "ipc_channel_reader.h", - "ipc_descriptors.h", "ipc_export.h", "ipc_listener.h", "ipc_logging.cc",
diff --git a/ipc/ipc_descriptors.h b/ipc/ipc_descriptors.h deleted file mode 100644 index 7a4a7b19..0000000 --- a/ipc/ipc_descriptors.h +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef IPC_IPC_DESCRIPTORS_H_ -#define IPC_IPC_DESCRIPTORS_H_ - -// This is a list of global descriptor keys to be used with the -// base::GlobalDescriptors object (see base/posix/global_descriptors.h) -enum { - // The first key that can be use to register descriptors. - kIPCDescriptorMax = 0 -}; - -#endif // IPC_IPC_DESCRIPTORS_H_
diff --git a/mash/catalog_viewer/catalog_viewer.cc b/mash/catalog_viewer/catalog_viewer.cc index 75abf03a..c3ca399 100644 --- a/mash/catalog_viewer/catalog_viewer.cc +++ b/mash/catalog_viewer/catalog_viewer.cc
@@ -51,7 +51,7 @@ capability_(new views::Textfield) { constexpr int kPadding = 5; SetBorder(views::CreateEmptyBorder(gfx::Insets(kPadding))); - set_background(views::Background::CreateStandardPanelBackground()); + SetBackground(views::CreateStandardPanelBackground()); views::GridLayout* layout = new views::GridLayout(this); SetLayoutManager(layout);
diff --git a/mash/example/window_type_launcher/window_type_launcher.cc b/mash/example/window_type_launcher/window_type_launcher.cc index 558a42c..021135e4 100644 --- a/mash/example/window_type_launcher/window_type_launcher.cc +++ b/mash/example/window_type_launcher/window_type_launcher.cc
@@ -59,7 +59,7 @@ }; explicit WindowDelegateView(uint32_t traits) : traits_(traits) { - set_background(views::Background::CreateSolidBackground(SK_ColorRED)); + SetBackground(views::CreateSolidBackground(SK_ColorRED)); } ~WindowDelegateView() override {}
diff --git a/mash/quick_launch/quick_launch.cc b/mash/quick_launch/quick_launch.cc index cd784e6..9948a895 100644 --- a/mash/quick_launch/quick_launch.cc +++ b/mash/quick_launch/quick_launch.cc
@@ -42,7 +42,7 @@ connector_(connector), prompt_(new views::Textfield), catalog_(std::move(catalog)) { - set_background(views::Background::CreateStandardPanelBackground()); + SetBackground(views::CreateStandardPanelBackground()); prompt_->set_controller(this); AddChildView(prompt_);
diff --git a/mash/task_viewer/task_viewer.cc b/mash/task_viewer/task_viewer.cc index 13e20f6..cdc03e76 100644 --- a/mash/task_viewer/task_viewer.cc +++ b/mash/task_viewer/task_viewer.cc
@@ -65,7 +65,7 @@ table_view_ = new views::TableView(this, GetColumns(), views::TEXT_ONLY, false); - set_background(views::Background::CreateStandardPanelBackground()); + SetBackground(views::CreateStandardPanelBackground()); table_view_parent_ = table_view_->CreateParentIfNecessary(); AddChildView(table_view_parent_);
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn index ee1193f..a4c5a309e 100644 --- a/media/base/BUILD.gn +++ b/media/base/BUILD.gn
@@ -269,6 +269,8 @@ "video_util.h", "wall_clock_time_source.cc", "wall_clock_time_source.h", + "watch_time_keys.cc", + "watch_time_keys.h", ] allow_circular_includes_from = []
diff --git a/media/base/media_log.cc b/media/base/media_log.cc index 7e732c3f..abba917 100644 --- a/media/base/media_log.cc +++ b/media/base/media_log.cc
@@ -17,99 +17,6 @@ // unique IDs. static base::StaticAtomicSequenceNumber g_media_log_count; -// Audio+video watch time metrics. -const char MediaLog::kWatchTimeAudioVideoAll[] = - "Media.WatchTime.AudioVideo.All"; -const char MediaLog::kWatchTimeAudioVideoMse[] = - "Media.WatchTime.AudioVideo.MSE"; -const char MediaLog::kWatchTimeAudioVideoEme[] = - "Media.WatchTime.AudioVideo.EME"; -const char MediaLog::kWatchTimeAudioVideoSrc[] = - "Media.WatchTime.AudioVideo.SRC"; -const char MediaLog::kWatchTimeAudioVideoBattery[] = - "Media.WatchTime.AudioVideo.Battery"; -const char MediaLog::kWatchTimeAudioVideoAc[] = "Media.WatchTime.AudioVideo.AC"; -const char MediaLog::kWatchTimeAudioVideoEmbeddedExperience[] = - "Media.WatchTime.AudioVideo.EmbeddedExperience"; - -// Audio only "watch time" metrics. -const char MediaLog::kWatchTimeAudioAll[] = "Media.WatchTime.Audio.All"; -const char MediaLog::kWatchTimeAudioMse[] = "Media.WatchTime.Audio.MSE"; -const char MediaLog::kWatchTimeAudioEme[] = "Media.WatchTime.Audio.EME"; -const char MediaLog::kWatchTimeAudioSrc[] = "Media.WatchTime.Audio.SRC"; -const char MediaLog::kWatchTimeAudioBattery[] = "Media.WatchTime.Audio.Battery"; -const char MediaLog::kWatchTimeAudioAc[] = "Media.WatchTime.Audio.AC"; -const char MediaLog::kWatchTimeAudioEmbeddedExperience[] = - "Media.WatchTime.Audio.EmbeddedExperience"; - -// Audio+video background watch time metrics. -const char MediaLog::kWatchTimeAudioVideoBackgroundAll[] = - "Media.WatchTime.AudioVideo.Background.All"; -const char MediaLog::kWatchTimeAudioVideoBackgroundMse[] = - "Media.WatchTime.AudioVideo.Background.MSE"; -const char MediaLog::kWatchTimeAudioVideoBackgroundEme[] = - "Media.WatchTime.AudioVideo.Background.EME"; -const char MediaLog::kWatchTimeAudioVideoBackgroundSrc[] = - "Media.WatchTime.AudioVideo.Background.SRC"; -const char MediaLog::kWatchTimeAudioVideoBackgroundBattery[] = - "Media.WatchTime.AudioVideo.Background.Battery"; -const char MediaLog::kWatchTimeAudioVideoBackgroundAc[] = - "Media.WatchTime.AudioVideo.Background.AC"; -const char MediaLog::kWatchTimeAudioVideoBackgroundEmbeddedExperience[] = - "Media.WatchTime.AudioVideo.Background.EmbeddedExperience"; - -const char MediaLog::kWatchTimeFinalize[] = "FinalizeWatchTime"; -const char MediaLog::kWatchTimeFinalizePower[] = "FinalizePowerWatchTime"; - -const char MediaLog::kUnderflowCount[] = "UnderflowCount"; - -const char MediaLog::kMeanTimeBetweenRebuffersAudioSrc[] = - "Media.MeanTimeBetweenRebuffers.Audio.SRC"; -const char MediaLog::kMeanTimeBetweenRebuffersAudioMse[] = - "Media.MeanTimeBetweenRebuffers.Audio.MSE"; -const char MediaLog::kMeanTimeBetweenRebuffersAudioEme[] = - "Media.MeanTimeBetweenRebuffers.Audio.EME"; -const char MediaLog::kMeanTimeBetweenRebuffersAudioVideoSrc[] = - "Media.MeanTimeBetweenRebuffers.AudioVideo.SRC"; -const char MediaLog::kMeanTimeBetweenRebuffersAudioVideoMse[] = - "Media.MeanTimeBetweenRebuffers.AudioVideo.MSE"; -const char MediaLog::kMeanTimeBetweenRebuffersAudioVideoEme[] = - "Media.MeanTimeBetweenRebuffers.AudioVideo.EME"; - -base::flat_set<base::StringPiece> MediaLog::GetWatchTimeKeys() { - return base::flat_set<base::StringPiece>( - {kWatchTimeAudioAll, - kWatchTimeAudioMse, - kWatchTimeAudioEme, - kWatchTimeAudioSrc, - kWatchTimeAudioBattery, - kWatchTimeAudioAc, - kWatchTimeAudioEmbeddedExperience, - kWatchTimeAudioVideoAll, - kWatchTimeAudioVideoMse, - kWatchTimeAudioVideoEme, - kWatchTimeAudioVideoSrc, - kWatchTimeAudioVideoBattery, - kWatchTimeAudioVideoAc, - kWatchTimeAudioVideoEmbeddedExperience, - kWatchTimeAudioVideoBackgroundAll, - kWatchTimeAudioVideoBackgroundMse, - kWatchTimeAudioVideoBackgroundEme, - kWatchTimeAudioVideoBackgroundSrc, - kWatchTimeAudioVideoBackgroundBattery, - kWatchTimeAudioVideoBackgroundAc, - kWatchTimeAudioVideoBackgroundEmbeddedExperience}, - base::KEEP_FIRST_OF_DUPES); -} - -base::flat_set<base::StringPiece> MediaLog::GetWatchTimePowerKeys() { - return base::flat_set<base::StringPiece>( - {kWatchTimeAudioBattery, kWatchTimeAudioAc, kWatchTimeAudioVideoBattery, - kWatchTimeAudioVideoAc, kWatchTimeAudioVideoBackgroundBattery, - kWatchTimeAudioVideoBackgroundAc}, - base::KEEP_FIRST_OF_DUPES); -} - std::string MediaLog::MediaLogLevelToString(MediaLogLevel level) { switch (level) { case MEDIALOG_ERROR:
diff --git a/media/base/media_log.h b/media/base/media_log.h index 9f508159..9899018 100644 --- a/media/base/media_log.h +++ b/media/base/media_log.h
@@ -12,7 +12,6 @@ #include <sstream> #include <string> -#include "base/containers/flat_set.h" #include "base/logging.h" #include "base/macros.h" #include "media/base/buffering_state.h" @@ -107,49 +106,6 @@ void SetDoubleProperty(const std::string& key, double value); void SetBooleanProperty(const std::string& key, bool value); - // Histogram names used for reporting; also double as MediaLog key names. - // NOTE: If you add to this list you must update GetWatchTimeKeys() and if - // necessary, GetWatchTimePowerKeys(). - static const char kWatchTimeAudioAll[]; - static const char kWatchTimeAudioMse[]; - static const char kWatchTimeAudioEme[]; - static const char kWatchTimeAudioSrc[]; - static const char kWatchTimeAudioBattery[]; - static const char kWatchTimeAudioAc[]; - static const char kWatchTimeAudioEmbeddedExperience[]; - static const char kWatchTimeAudioVideoAll[]; - static const char kWatchTimeAudioVideoMse[]; - static const char kWatchTimeAudioVideoEme[]; - static const char kWatchTimeAudioVideoSrc[]; - static const char kWatchTimeAudioVideoBattery[]; - static const char kWatchTimeAudioVideoAc[]; - static const char kWatchTimeAudioVideoEmbeddedExperience[]; - static const char kWatchTimeAudioVideoBackgroundAll[]; - static const char kWatchTimeAudioVideoBackgroundMse[]; - static const char kWatchTimeAudioVideoBackgroundEme[]; - static const char kWatchTimeAudioVideoBackgroundSrc[]; - static const char kWatchTimeAudioVideoBackgroundBattery[]; - static const char kWatchTimeAudioVideoBackgroundAc[]; - static const char kWatchTimeAudioVideoBackgroundEmbeddedExperience[]; - - // Markers which signify the watch time should be finalized immediately. - static const char kWatchTimeFinalize[]; - static const char kWatchTimeFinalizePower[]; - - // Count of the number of underflow events during a media session. - static const char kUnderflowCount[]; - - // UMA keys for MTBR samples. - static const char kMeanTimeBetweenRebuffersAudioSrc[]; - static const char kMeanTimeBetweenRebuffersAudioMse[]; - static const char kMeanTimeBetweenRebuffersAudioEme[]; - static const char kMeanTimeBetweenRebuffersAudioVideoSrc[]; - static const char kMeanTimeBetweenRebuffersAudioVideoMse[]; - static const char kMeanTimeBetweenRebuffersAudioVideoEme[]; - - static base::flat_set<base::StringPiece> GetWatchTimeKeys(); - static base::flat_set<base::StringPiece> GetWatchTimePowerKeys(); - private: // A unique (to this process) id for this MediaLog. int32_t id_;
diff --git a/media/base/watch_time_keys.cc b/media/base/watch_time_keys.cc new file mode 100644 index 0000000..6ead6c2 --- /dev/null +++ b/media/base/watch_time_keys.cc
@@ -0,0 +1,97 @@ +// 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 "media/base/watch_time_keys.h" + +namespace media { + +// Audio+video watch time metrics. +const char kWatchTimeAudioVideoAll[] = "Media.WatchTime.AudioVideo.All"; +const char kWatchTimeAudioVideoMse[] = "Media.WatchTime.AudioVideo.MSE"; +const char kWatchTimeAudioVideoEme[] = "Media.WatchTime.AudioVideo.EME"; +const char kWatchTimeAudioVideoSrc[] = "Media.WatchTime.AudioVideo.SRC"; +const char kWatchTimeAudioVideoBattery[] = "Media.WatchTime.AudioVideo.Battery"; +const char kWatchTimeAudioVideoAc[] = "Media.WatchTime.AudioVideo.AC"; +const char kWatchTimeAudioVideoEmbeddedExperience[] = + "Media.WatchTime.AudioVideo.EmbeddedExperience"; + +// Audio only "watch time" metrics. +const char kWatchTimeAudioAll[] = "Media.WatchTime.Audio.All"; +const char kWatchTimeAudioMse[] = "Media.WatchTime.Audio.MSE"; +const char kWatchTimeAudioEme[] = "Media.WatchTime.Audio.EME"; +const char kWatchTimeAudioSrc[] = "Media.WatchTime.Audio.SRC"; +const char kWatchTimeAudioBattery[] = "Media.WatchTime.Audio.Battery"; +const char kWatchTimeAudioAc[] = "Media.WatchTime.Audio.AC"; +const char kWatchTimeAudioEmbeddedExperience[] = + "Media.WatchTime.Audio.EmbeddedExperience"; + +// Audio+video background watch time metrics. +const char kWatchTimeAudioVideoBackgroundAll[] = + "Media.WatchTime.AudioVideo.Background.All"; +const char kWatchTimeAudioVideoBackgroundMse[] = + "Media.WatchTime.AudioVideo.Background.MSE"; +const char kWatchTimeAudioVideoBackgroundEme[] = + "Media.WatchTime.AudioVideo.Background.EME"; +const char kWatchTimeAudioVideoBackgroundSrc[] = + "Media.WatchTime.AudioVideo.Background.SRC"; +const char kWatchTimeAudioVideoBackgroundBattery[] = + "Media.WatchTime.AudioVideo.Background.Battery"; +const char kWatchTimeAudioVideoBackgroundAc[] = + "Media.WatchTime.AudioVideo.Background.AC"; +const char kWatchTimeAudioVideoBackgroundEmbeddedExperience[] = + "Media.WatchTime.AudioVideo.Background.EmbeddedExperience"; + +const char kWatchTimeFinalize[] = "FinalizeWatchTime"; +const char kWatchTimeFinalizePower[] = "FinalizePowerWatchTime"; + +const char kWatchTimeUnderflowCount[] = "UnderflowCount"; + +const char kMeanTimeBetweenRebuffersAudioSrc[] = + "Media.MeanTimeBetweenRebuffers.Audio.SRC"; +const char kMeanTimeBetweenRebuffersAudioMse[] = + "Media.MeanTimeBetweenRebuffers.Audio.MSE"; +const char kMeanTimeBetweenRebuffersAudioEme[] = + "Media.MeanTimeBetweenRebuffers.Audio.EME"; +const char kMeanTimeBetweenRebuffersAudioVideoSrc[] = + "Media.MeanTimeBetweenRebuffers.AudioVideo.SRC"; +const char kMeanTimeBetweenRebuffersAudioVideoMse[] = + "Media.MeanTimeBetweenRebuffers.AudioVideo.MSE"; +const char kMeanTimeBetweenRebuffersAudioVideoEme[] = + "Media.MeanTimeBetweenRebuffers.AudioVideo.EME"; + +base::flat_set<base::StringPiece> GetWatchTimeKeys() { + return base::flat_set<base::StringPiece>( + {kWatchTimeAudioAll, + kWatchTimeAudioMse, + kWatchTimeAudioEme, + kWatchTimeAudioSrc, + kWatchTimeAudioBattery, + kWatchTimeAudioAc, + kWatchTimeAudioEmbeddedExperience, + kWatchTimeAudioVideoAll, + kWatchTimeAudioVideoMse, + kWatchTimeAudioVideoEme, + kWatchTimeAudioVideoSrc, + kWatchTimeAudioVideoBattery, + kWatchTimeAudioVideoAc, + kWatchTimeAudioVideoEmbeddedExperience, + kWatchTimeAudioVideoBackgroundAll, + kWatchTimeAudioVideoBackgroundMse, + kWatchTimeAudioVideoBackgroundEme, + kWatchTimeAudioVideoBackgroundSrc, + kWatchTimeAudioVideoBackgroundBattery, + kWatchTimeAudioVideoBackgroundAc, + kWatchTimeAudioVideoBackgroundEmbeddedExperience}, + base::KEEP_FIRST_OF_DUPES); +} + +base::flat_set<base::StringPiece> GetWatchTimePowerKeys() { + return base::flat_set<base::StringPiece>( + {kWatchTimeAudioBattery, kWatchTimeAudioAc, kWatchTimeAudioVideoBattery, + kWatchTimeAudioVideoAc, kWatchTimeAudioVideoBackgroundBattery, + kWatchTimeAudioVideoBackgroundAc}, + base::KEEP_FIRST_OF_DUPES); +} + +} // namespace media
diff --git a/media/base/watch_time_keys.h b/media/base/watch_time_keys.h new file mode 100644 index 0000000..2854131 --- /dev/null +++ b/media/base/watch_time_keys.h
@@ -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. + +#ifndef MEDIA_BASE_WATCH_TIME_KEYS_H_ +#define MEDIA_BASE_WATCH_TIME_KEYS_H_ + +#include "base/containers/flat_set.h" +#include "base/strings/string_piece.h" +#include "media/base/media_export.h" + +namespace media { + +// Histogram names used for reporting; also double as MediaLog key names. +// NOTE: If you add to this list you must update GetWatchTimeKeys() and if +// necessary, GetWatchTimePowerKeys(). +MEDIA_EXPORT extern const char kWatchTimeAudioAll[]; +MEDIA_EXPORT extern const char kWatchTimeAudioMse[]; +MEDIA_EXPORT extern const char kWatchTimeAudioEme[]; +MEDIA_EXPORT extern const char kWatchTimeAudioSrc[]; +MEDIA_EXPORT extern const char kWatchTimeAudioBattery[]; +MEDIA_EXPORT extern const char kWatchTimeAudioAc[]; +MEDIA_EXPORT extern const char kWatchTimeAudioEmbeddedExperience[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoAll[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoMse[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoEme[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoSrc[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoBattery[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoAc[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoEmbeddedExperience[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoBackgroundAll[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoBackgroundMse[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoBackgroundEme[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoBackgroundSrc[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoBackgroundBattery[]; +MEDIA_EXPORT extern const char kWatchTimeAudioVideoBackgroundAc[]; +MEDIA_EXPORT extern const char + kWatchTimeAudioVideoBackgroundEmbeddedExperience[]; +// **** If adding any line above this see the toplevel comment! **** + +// Markers which signify the watch time should be finalized immediately. +MEDIA_EXPORT extern const char kWatchTimeFinalize[]; +MEDIA_EXPORT extern const char kWatchTimeFinalizePower[]; + +// Count of the number of underflow events during a media session. +MEDIA_EXPORT extern const char kWatchTimeUnderflowCount[]; + +// UMA keys for MTBR samples. +MEDIA_EXPORT extern const char kMeanTimeBetweenRebuffersAudioSrc[]; +MEDIA_EXPORT extern const char kMeanTimeBetweenRebuffersAudioMse[]; +MEDIA_EXPORT extern const char kMeanTimeBetweenRebuffersAudioEme[]; +MEDIA_EXPORT extern const char kMeanTimeBetweenRebuffersAudioVideoSrc[]; +MEDIA_EXPORT extern const char kMeanTimeBetweenRebuffersAudioVideoMse[]; +MEDIA_EXPORT extern const char kMeanTimeBetweenRebuffersAudioVideoEme[]; + +MEDIA_EXPORT base::flat_set<base::StringPiece> GetWatchTimeKeys(); +MEDIA_EXPORT base::flat_set<base::StringPiece> GetWatchTimePowerKeys(); + +} // namespace media + +#endif // MEDIA_BASE_WATCH_TIME_KEYS_H_
diff --git a/media/blink/watch_time_reporter.cc b/media/blink/watch_time_reporter.cc index 7ca8dc1..7bd76741 100644 --- a/media/blink/watch_time_reporter.cc +++ b/media/blink/watch_time_reporter.cc
@@ -5,6 +5,7 @@ #include "media/blink/watch_time_reporter.h" #include "base/power_monitor/power_monitor.h" +#include "media/base/watch_time_keys.h" namespace media { @@ -271,14 +272,13 @@ std::unique_ptr<MediaLogEvent> log_event = media_log_->CreateEvent(MediaLogEvent::Type::WATCH_TIME_UPDATE); -#define RECORD_WATCH_TIME(key, value) \ - do { \ - log_event->params.SetDoubleWithoutPathExpansion( \ - has_video_ \ - ? MediaLog::kWatchTimeAudioVideo##key \ - : (is_background_ ? MediaLog::kWatchTimeAudioVideoBackground##key \ - : MediaLog::kWatchTimeAudio##key), \ - value.InSecondsF()); \ +#define RECORD_WATCH_TIME(key, value) \ + do { \ + log_event->params.SetDoubleWithoutPathExpansion( \ + has_video_ ? kWatchTimeAudioVideo##key \ + : (is_background_ ? kWatchTimeAudioVideoBackground##key \ + : kWatchTimeAudio##key), \ + value.InSecondsF()); \ } while (0) // Only report watch time after some minimum amount has elapsed. Don't update @@ -339,16 +339,16 @@ } } - log_event->params.SetInteger(MediaLog::kUnderflowCount, underflow_count_); + log_event->params.SetInteger(kWatchTimeUnderflowCount, underflow_count_); pending_underflow_events_.clear(); } // Always send finalize, even if we don't currently have any data, it's // harmless to send since nothing will be logged if we've already finalized. if (is_finalizing) - log_event->params.SetBoolean(MediaLog::kWatchTimeFinalize, true); + log_event->params.SetBoolean(kWatchTimeFinalize, true); else if (is_power_change_pending) - log_event->params.SetBoolean(MediaLog::kWatchTimeFinalizePower, true); + log_event->params.SetBoolean(kWatchTimeFinalizePower, true); if (!log_event->params.empty()) media_log_->AddEvent(std::move(log_event));
diff --git a/media/blink/watch_time_reporter_unittest.cc b/media/blink/watch_time_reporter_unittest.cc index 7464ed0..5a65d106 100644 --- a/media/blink/watch_time_reporter_unittest.cc +++ b/media/blink/watch_time_reporter_unittest.cc
@@ -9,6 +9,7 @@ #include "base/run_loop.h" #include "base/test/test_message_loop.h" #include "media/base/mock_media_log.h" +#include "media/base/watch_time_keys.h" #include "media/blink/watch_time_reporter.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -17,23 +18,21 @@ constexpr gfx::Size kSizeJustRight = gfx::Size(201, 201); -#define EXPECT_WATCH_TIME(key, value) \ - do { \ - EXPECT_CALL( \ - media_log_, \ - OnWatchTimeUpdate(has_video_ ? MediaLog::kWatchTimeAudioVideo##key \ - : MediaLog::kWatchTimeAudio##key, \ - value)) \ - .RetiresOnSaturation(); \ +#define EXPECT_WATCH_TIME(key, value) \ + do { \ + EXPECT_CALL(media_log_, \ + OnWatchTimeUpdate(has_video_ ? kWatchTimeAudioVideo##key \ + : kWatchTimeAudio##key, \ + value)) \ + .RetiresOnSaturation(); \ } while (0) -#define EXPECT_BACKGROUND_WATCH_TIME(key, value) \ - do { \ - DCHECK(has_video_); \ - EXPECT_CALL(media_log_, \ - OnWatchTimeUpdate( \ - MediaLog::kWatchTimeAudioVideoBackground##key, value)) \ - .RetiresOnSaturation(); \ +#define EXPECT_BACKGROUND_WATCH_TIME(key, value) \ + do { \ + DCHECK(has_video_); \ + EXPECT_CALL(media_log_, \ + OnWatchTimeUpdate(kWatchTimeAudioVideoBackground##key, value)) \ + .RetiresOnSaturation(); \ } while (0) #define EXPECT_WATCH_TIME_FINALIZED() \ @@ -59,7 +58,7 @@ it.Advance()) { bool finalize; if (it.value().GetAsBoolean(&finalize)) { - if (it.key() == MediaLog::kWatchTimeFinalize) + if (it.key() == kWatchTimeFinalize) OnWatchTimeFinalized(); else OnPowerWatchTimeFinalized();
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index f9db61f3..b6fc0c97 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc
@@ -534,6 +534,7 @@ } void ChunkDemuxer::SetStreamStatusChangeCB(const StreamStatusChangeCB& cb) { + base::AutoLock auto_lock(lock_); DCHECK(!cb.is_null()); for (const auto& stream : audio_streams_) stream->SetStreamStatusChangeCB(cb);
diff --git a/net/BUILD.gn b/net/BUILD.gn index 6b6dbf8..ac40a7a 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -793,6 +793,8 @@ "http/bidirectional_stream_impl.h", "http/bidirectional_stream_request_info.cc", "http/bidirectional_stream_request_info.h", + "http/broken_alternative_services.cc", + "http/broken_alternative_services.h", "http/des.cc", "http/des.h", "http/failing_http_transaction_factory.cc", @@ -4436,6 +4438,7 @@ "ftp/ftp_network_transaction_unittest.cc", "ftp/ftp_util_unittest.cc", "http/bidirectional_stream_unittest.cc", + "http/broken_alternative_services_unittest.cc", "http/des_unittest.cc", "http/http_auth_cache_unittest.cc", "http/http_auth_challenge_tokenizer_unittest.cc",
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc index c33515e..4a963be 100644 --- a/net/dns/host_resolver_impl_unittest.cc +++ b/net/dns/host_resolver_impl_unittest.cc
@@ -2407,7 +2407,14 @@ EXPECT_TRUE(requests_[2]->HasOneAddress("192.168.0.3", 80)); } -TEST_F(HostResolverImplDnsTest, NoIPv6OnWifi) { +// TODO(crbug.com/728808): This test causes HttpNetworkTransactionTests to crash +// on iOS. +#if defined(OS_IOS) +#define MAYBE_NoIPv6OnWifi DISABLED_NoIPv6OnWifi +#else +#define MAYBE_NoIPv6OnWifi NoIPv6OnWifi +#endif +TEST_F(HostResolverImplDnsTest, MAYBE_NoIPv6OnWifi) { test::ScopedMockNetworkChangeNotifier notifier; CreateSerialResolver(); // To guarantee order of resolutions. resolver_->SetNoIPv6OnWifi(true);
diff --git a/net/http/broken_alternative_services.cc b/net/http/broken_alternative_services.cc new file mode 100644 index 0000000..d660f25 --- /dev/null +++ b/net/http/broken_alternative_services.cc
@@ -0,0 +1,181 @@ +// Copyright (c) 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 "net/http/broken_alternative_services.h" + +#include "base/memory/singleton.h" +#include "base/time/tick_clock.h" +#include "base/time/time.h" +#include "net/http/http_server_properties_impl.h" + +namespace net { + +namespace { + +// Initial delay for broken alternative services. +const uint64_t kBrokenAlternativeProtocolDelaySecs = 300; +// Subsequent failures result in exponential (base 2) backoff. +// Limit binary shift to limit delay to approximately 2 days. +const int kBrokenDelayMaxShift = 9; + +base::TimeDelta ComputeBrokenAlternativeServiceExpirationDelay( + int broken_count) { + DCHECK_GE(broken_count, 0); + if (broken_count > kBrokenDelayMaxShift) + broken_count = kBrokenDelayMaxShift; + return base::TimeDelta::FromSeconds(kBrokenAlternativeProtocolDelaySecs) * + (1 << broken_count); +} + +} // namespace + +BrokenAlternativeServices::BrokenAlternativeServices(Delegate* delegate, + base::TickClock* clock) + : delegate_(delegate), + clock_(clock), + recently_broken_alternative_services_( + RecentlyBrokenAlternativeServices::NO_AUTO_EVICT), + weak_ptr_factory_(this) { + DCHECK(delegate_); + DCHECK(clock_); +} + +BrokenAlternativeServices::~BrokenAlternativeServices() {} + +void BrokenAlternativeServices::MarkAlternativeServiceBroken( + const AlternativeService& alternative_service) { + // Empty host means use host of origin, callers are supposed to substitute. + DCHECK(!alternative_service.host.empty()); + DCHECK_NE(kProtoUnknown, alternative_service.protocol); + + auto it = recently_broken_alternative_services_.Get(alternative_service); + int broken_count = 0; + if (it == recently_broken_alternative_services_.end()) { + recently_broken_alternative_services_.Put(alternative_service, 1); + } else { + broken_count = it->second++; + } + base::TimeTicks expiration = + clock_->NowTicks() + + ComputeBrokenAlternativeServiceExpirationDelay(broken_count); + // Return if alternative service is already in expiration queue. + BrokenAlternativeServiceList::iterator list_it; + if (!AddToBrokenAlternativeServiceListAndMap(alternative_service, expiration, + &list_it)) { + return; + } + + // If this is now the first entry in the list (i.e. |alternative_service| is + // the next alt svc to expire), schedule an expiration task for it. + if (list_it == broken_alternative_service_list_.begin()) { + ScheduleBrokenAlternateProtocolMappingsExpiration(); + } +} + +void BrokenAlternativeServices::MarkAlternativeServiceRecentlyBroken( + const AlternativeService& alternative_service) { + DCHECK_NE(kProtoUnknown, alternative_service.protocol); + if (recently_broken_alternative_services_.Get(alternative_service) == + recently_broken_alternative_services_.end()) { + recently_broken_alternative_services_.Put(alternative_service, 1); + } +} + +bool BrokenAlternativeServices::IsAlternativeServiceBroken( + const AlternativeService& alternative_service) const { + // Empty host means use host of origin, callers are supposed to substitute. + DCHECK(!alternative_service.host.empty()); + return broken_alternative_service_map_.find(alternative_service) != + broken_alternative_service_map_.end(); +} + +bool BrokenAlternativeServices::WasAlternativeServiceRecentlyBroken( + const AlternativeService& alternative_service) { + return recently_broken_alternative_services_.Get(alternative_service) != + recently_broken_alternative_services_.end(); +} + +void BrokenAlternativeServices::ConfirmAlternativeService( + const AlternativeService& alternative_service) { + DCHECK_NE(kProtoUnknown, alternative_service.protocol); + + // Remove |alternative_service| from |alternative_service_list_| and + // |alternative_service_map_|. + auto map_it = broken_alternative_service_map_.find(alternative_service); + if (map_it != broken_alternative_service_map_.end()) { + broken_alternative_service_list_.erase(map_it->second); + broken_alternative_service_map_.erase(map_it); + } + + auto it = recently_broken_alternative_services_.Get(alternative_service); + if (it != recently_broken_alternative_services_.end()) { + recently_broken_alternative_services_.Erase(it); + } +} + +bool BrokenAlternativeServices::AddToBrokenAlternativeServiceListAndMap( + const AlternativeService& alternative_service, + base::TimeTicks expiration, + BrokenAlternativeServiceList::iterator* it) { + DCHECK(it); + + auto map_it = broken_alternative_service_map_.find(alternative_service); + if (map_it != broken_alternative_service_map_.end()) + return false; + + // Iterate from end of |broken_alternative_service_list_| to find where to + // insert it to keep the list sorted by expiration time. + auto list_it = broken_alternative_service_list_.end(); + while (list_it != broken_alternative_service_list_.begin()) { + --list_it; + if (list_it->expiration <= expiration) { + ++list_it; + break; + } + } + + // Insert |alternative_service| into the list and the map + list_it = broken_alternative_service_list_.insert( + list_it, BrokenAltSvcExpireInfo(alternative_service, expiration)); + broken_alternative_service_map_.insert( + std::make_pair(alternative_service, list_it)); + + *it = list_it; + return true; +} + +void BrokenAlternativeServices::ExpireBrokenAlternateProtocolMappings() { + base::TimeTicks now = clock_->NowTicks(); + + while (!broken_alternative_service_list_.empty()) { + auto it = broken_alternative_service_list_.begin(); + if (now < it->expiration) { + break; + } + + delegate_->OnExpireBrokenAlternativeService(it->alternative_service); + + broken_alternative_service_map_.erase(it->alternative_service); + broken_alternative_service_list_.erase(it); + } + + if (!broken_alternative_service_list_.empty()) + ScheduleBrokenAlternateProtocolMappingsExpiration(); +} + +void BrokenAlternativeServices :: + ScheduleBrokenAlternateProtocolMappingsExpiration() { + DCHECK(!broken_alternative_service_list_.empty()); + base::TimeTicks now = clock_->NowTicks(); + base::TimeTicks when = broken_alternative_service_list_.front().expiration; + base::TimeDelta delay = when > now ? when - now : base::TimeDelta(); + expiration_timer_.Stop(); + expiration_timer_.Start( + FROM_HERE, delay, + base::Bind( + &BrokenAlternativeServices ::ExpireBrokenAlternateProtocolMappings, + weak_ptr_factory_.GetWeakPtr())); +} + +} // namespace net
diff --git a/net/http/broken_alternative_services.h b/net/http/broken_alternative_services.h new file mode 100644 index 0000000..6a51222e --- /dev/null +++ b/net/http/broken_alternative_services.h
@@ -0,0 +1,145 @@ +// Copyright (c) 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. + +#ifndef NET_HTTP_BROKEN_ALTERNATIVE_SERVICES_H_ +#define NET_HTTP_BROKEN_ALTERNATIVE_SERVICES_H_ + +#include <list> +#include <unordered_map> + +#include "base/memory/weak_ptr.h" +#include "base/timer/timer.h" +#include "net/http/http_server_properties.h" + +namespace base { +class TickClock; +} + +namespace net { + +struct AlternativeServiceHash { + size_t operator()(const net::AlternativeService& entry) const { + return entry.protocol ^ std::hash<std::string>()(entry.host) ^ entry.port; + } +}; + +// This class tracks HTTP alternative services that have been marked as broken. +// The brokenness of an alt-svc will expire after some time according to an +// exponential back-off formula: each time an alt-svc is marked broken, the +// expiration delay will be some constant multiple of its previous expiration +// delay. This prevents broken alt-svcs from being retried too often by the +// network stack. +class NET_EXPORT_PRIVATE BrokenAlternativeServices { + public: + // Delegate to be used by owner so it can be notified when the brokenness of + // an AlternativeService expires. + class NET_EXPORT Delegate { + public: + // Called when a broken alternative service's expiration time is reached. + virtual void OnExpireBrokenAlternativeService( + const AlternativeService& expired_alternative_service) = 0; + virtual ~Delegate() {} + }; + + // |delegate| will be notified when a broken alternative service expires. It + // must not be null. + // |clock| is used for setting expiration times and scheduling the + // expiration of broken alternative services. It must not be null. + // |delegate| and |clock| are both unowned and must outlive this. + BrokenAlternativeServices(Delegate* delegate, base::TickClock* clock); + + BrokenAlternativeServices(const BrokenAlternativeServices&) = delete; + void operator=(const BrokenAlternativeServices&) = delete; + + ~BrokenAlternativeServices(); + + // Marks |alternative_service| as broken until after some expiration delay + // (determined by how many times it's been marked broken before). Being broken + // will cause IsAlternativeServiceBroken(alternative_service) to return true + // until the expiration time is reached, or until + // ConfirmAlternativeService(alternative_service) is called. + void MarkAlternativeServiceBroken( + const AlternativeService& alternative_service); + + // Marks |alternative_service| as recently broken. Being recently broken will + // cause WasAlternativeServiceRecentlyBroken(alternative_service) to return + // true until ConfirmAlternativeService(alternative_service) is called. + void MarkAlternativeServiceRecentlyBroken( + const AlternativeService& alternative_service); + + // Returns true if MarkAlternativeServiceBroken(alternative_service) has been + // called, the expiration time has not been reached, and + // ConfirmAlternativeService(alternative_service) has not been called + // afterwards. + bool IsAlternativeServiceBroken( + const AlternativeService& alternative_service) const; + + // Returns true if MarkAlternativeServiceRecentlyBroken(alternative_service) + // or MarkAlternativeServiceBroken(alternative_service) has been called and + // ConfirmAlternativeService(alternative_service) has not been called + // afterwards (even if brokenness of |alternative_service| has expired). + bool WasAlternativeServiceRecentlyBroken( + const AlternativeService& alternative_service); + + // Marks |alternative_service| as not broken and not recently broken. + void ConfirmAlternativeService(const AlternativeService& alternative_service); + + private: + // TODO (wangyix): modify HttpServerPropertiesImpl unit tests so this + // friendness is no longer required. + friend class HttpServerPropertiesImplPeer; + + // A pair containing a broken AlternativeService and the expiration time of + // its brokenness. + struct BrokenAltSvcExpireInfo { + BrokenAltSvcExpireInfo(const AlternativeService& alt_svc, + base::TimeTicks expire) + : alternative_service(alt_svc), expiration(expire) {} + + AlternativeService alternative_service; + base::TimeTicks expiration; + }; + + typedef std::list<BrokenAltSvcExpireInfo> BrokenAlternativeServiceList; + + typedef std::unordered_map<AlternativeService, + BrokenAlternativeServiceList::iterator, + AlternativeServiceHash> + BrokenAlternativeServiceMap; + + // Inserts |alternative_service| and its |expiration| time into + // |broken_alternative_service_list_| and |broken_alternative_service_map_|. + // |it| is the position in |broken_alternative_service_list_| where it was + // inserted. + bool AddToBrokenAlternativeServiceListAndMap( + const AlternativeService& alternative_service, + base::TimeTicks expiration, + BrokenAlternativeServiceList::iterator* it); + + void ExpireBrokenAlternateProtocolMappings(); + void ScheduleBrokenAlternateProtocolMappingsExpiration(); + + Delegate* delegate_; // Unowned + base::TickClock* clock_; // Unowned + + // List of <broken alt svc, expiration time> pairs sorted by expiration time. + BrokenAlternativeServiceList broken_alternative_service_list_; + // A map from broken alt-svcs to their iterator pointing to that alt-svc's + // position in |broken_alternative_service_list_|. + BrokenAlternativeServiceMap broken_alternative_service_map_; + + // Maps broken alternative services to how many times they've been marked + // broken. + RecentlyBrokenAlternativeServices recently_broken_alternative_services_; + + // Used for scheduling the task that expires the brokenness of alternative + // services. + base::OneShotTimer expiration_timer_; + + base::WeakPtrFactory<BrokenAlternativeServices> weak_ptr_factory_; +}; + +} // namespace net + +#endif // NET_HTTP_BROKEN_ALTERNATIVE_SERVICES_H_
diff --git a/net/http/broken_alternative_services_unittest.cc b/net/http/broken_alternative_services_unittest.cc new file mode 100644 index 0000000..0c58582 --- /dev/null +++ b/net/http/broken_alternative_services_unittest.cc
@@ -0,0 +1,366 @@ +// Copyright (c) 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 "net/http/broken_alternative_services.h" + +#include <algorithm> +#include <vector> + +#include "base/test/test_mock_time_task_runner.h" +#include "base/time/tick_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace { + +class BrokenAlternativeServicesTest + : public BrokenAlternativeServices::Delegate, + public ::testing::Test { + public: + BrokenAlternativeServicesTest() + : test_task_runner_(new base::TestMockTimeTaskRunner()), + test_task_runner_context_(test_task_runner_), + broken_services_clock_(test_task_runner_->GetMockTickClock()), + broken_services_(this, broken_services_clock_.get()) {} + + // BrokenAlternativeServices::Delegate implementation + void OnExpireBrokenAlternativeService( + const AlternativeService& expired_alternative_service) override { + expired_alt_svcs_.push_back(expired_alternative_service); + } + + // All tests will run inside the scope of |test_task_runner_context_|, which + // means any task posted to the main message loop will run on + // |test_task_runner_|. + scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_; + base::TestMockTimeTaskRunner::ScopedContext test_task_runner_context_; + + std::unique_ptr<base::TickClock> broken_services_clock_; + BrokenAlternativeServices broken_services_; + + std::vector<AlternativeService> expired_alt_svcs_; +}; + +TEST_F(BrokenAlternativeServicesTest, MarkBroken) { + const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443); + const AlternativeService alternative_service2(kProtoHTTP2, "foo", 1234); + + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service2)); + + broken_services_.MarkAlternativeServiceBroken(alternative_service1); + + EXPECT_TRUE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service2)); + + broken_services_.MarkAlternativeServiceBroken(alternative_service2); + + EXPECT_TRUE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_TRUE( + broken_services_.IsAlternativeServiceBroken(alternative_service2)); + + broken_services_.ConfirmAlternativeService(alternative_service1); + + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_TRUE( + broken_services_.IsAlternativeServiceBroken(alternative_service2)); + + broken_services_.ConfirmAlternativeService(alternative_service2); + + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service2)); + + EXPECT_EQ(0u, expired_alt_svcs_.size()); +} + +TEST_F(BrokenAlternativeServicesTest, MarkRecentlyBroken) { + const AlternativeService alternative_service(kProtoHTTP2, "foo", 443); + + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + EXPECT_FALSE(broken_services_.WasAlternativeServiceRecentlyBroken( + alternative_service)); + + broken_services_.MarkAlternativeServiceRecentlyBroken(alternative_service); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + EXPECT_TRUE(broken_services_.WasAlternativeServiceRecentlyBroken( + alternative_service)); + + broken_services_.ConfirmAlternativeService(alternative_service); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + EXPECT_FALSE(broken_services_.WasAlternativeServiceRecentlyBroken( + alternative_service)); +} + +TEST_F(BrokenAlternativeServicesTest, ExpireBrokenAlternateProtocolMappings) { + AlternativeService alternative_service(kProtoQUIC, "foo", 443); + + broken_services_.MarkAlternativeServiceBroken(alternative_service); + + // |broken_services_| should have posted task to expire the brokenness of + // |alternative_service|. + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + + // Advance time until one time quantum before |alternative_service1|'s + // brokenness expires + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(5) - + base::TimeDelta::FromInternalValue(1)); + + // Ensure |alternative_service| is still marked broken. + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + EXPECT_EQ(0u, expired_alt_svcs_.size()); + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + + // Advance time by one time quantum. + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + + // Ensure |alternative_service| brokenness has expired but is still + // considered recently broken + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + EXPECT_FALSE(test_task_runner_->HasPendingTask()); + EXPECT_EQ(1u, expired_alt_svcs_.size()); + EXPECT_EQ(alternative_service, expired_alt_svcs_[0]); + EXPECT_TRUE(broken_services_.WasAlternativeServiceRecentlyBroken( + alternative_service)); +} + +TEST_F(BrokenAlternativeServicesTest, ExponentialBackoff) { + // Tests the exponential backoff of the computed expiration delay when an + // alt svc is marked broken. After being marked broken 10 times, the max + // expiration delay will have been reached and exponential backoff will no + // longer apply. + + AlternativeService alternative_service(kProtoQUIC, "foo", 443); + + broken_services_.MarkAlternativeServiceBroken(alternative_service); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(5) - + base::TimeDelta::FromInternalValue(1)); + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + + broken_services_.MarkAlternativeServiceBroken(alternative_service); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(10) - + base::TimeDelta::FromInternalValue(1)); + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + + broken_services_.MarkAlternativeServiceBroken(alternative_service); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(20) - + base::TimeDelta::FromInternalValue(1)); + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + + broken_services_.MarkAlternativeServiceBroken(alternative_service); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(40) - + base::TimeDelta::FromInternalValue(1)); + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + + broken_services_.MarkAlternativeServiceBroken(alternative_service); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(80) - + base::TimeDelta::FromInternalValue(1)); + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + + broken_services_.MarkAlternativeServiceBroken(alternative_service); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(160) - + base::TimeDelta::FromInternalValue(1)); + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + + broken_services_.MarkAlternativeServiceBroken(alternative_service); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(320) - + base::TimeDelta::FromInternalValue(1)); + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + + broken_services_.MarkAlternativeServiceBroken(alternative_service); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(640) - + base::TimeDelta::FromInternalValue(1)); + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + + broken_services_.MarkAlternativeServiceBroken(alternative_service); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(1280) - + base::TimeDelta::FromInternalValue(1)); + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + + broken_services_.MarkAlternativeServiceBroken(alternative_service); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(2560) - + base::TimeDelta::FromInternalValue(1)); + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); + + // Max expiration delay has been reached; subsequent expiration delays from + // this point forward should not increase further. + broken_services_.MarkAlternativeServiceBroken(alternative_service); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(2560) - + base::TimeDelta::FromInternalValue(1)); + EXPECT_TRUE(broken_services_.IsAlternativeServiceBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service)); +} + +TEST_F(BrokenAlternativeServicesTest, RemoveExpiredBrokenAltSvc) { + // This test will mark broken an alternative service A that has already been + // marked broken many times, then immediately mark another alternative service + // B as broken for the first time. Because A's been marked broken many times + // already, its brokenness will be scheduled to expire much further in the + // future than B, even though it was marked broken before B. This test makes + // sure that even though A was marked broken before B, B's brokenness should + // expire before A. + + AlternativeService alternative_service1(kProtoQUIC, "foo", 443); + AlternativeService alternative_service2(kProtoQUIC, "bar", 443); + + // Repeately mark |alternative_service1| broken and let brokenness expire. + // Do this a few times. + + broken_services_.MarkAlternativeServiceBroken(alternative_service1); + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(5)); + EXPECT_EQ(1u, expired_alt_svcs_.size()); + EXPECT_EQ(alternative_service1, expired_alt_svcs_.back()); + + broken_services_.MarkAlternativeServiceBroken(alternative_service1); + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(10)); + EXPECT_EQ(2u, expired_alt_svcs_.size()); + EXPECT_EQ(alternative_service1, expired_alt_svcs_.back()); + + broken_services_.MarkAlternativeServiceBroken(alternative_service1); + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(20)); + EXPECT_EQ(3u, expired_alt_svcs_.size()); + EXPECT_EQ(alternative_service1, expired_alt_svcs_.back()); + + expired_alt_svcs_.clear(); + + // Mark |alternative_service1| broken (will be given longer expiration delay), + // then mark |alternative_service2| broken (will be given shorter expiration + // delay). + broken_services_.MarkAlternativeServiceBroken(alternative_service1); + broken_services_.MarkAlternativeServiceBroken(alternative_service2); + + EXPECT_TRUE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_TRUE( + broken_services_.IsAlternativeServiceBroken(alternative_service2)); + + // Advance time until one time quantum before |alternative_service2|'s + // brokenness expires. + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(5) - + base::TimeDelta::FromInternalValue(1)); + + EXPECT_TRUE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_TRUE( + broken_services_.IsAlternativeServiceBroken(alternative_service2)); + EXPECT_EQ(0u, expired_alt_svcs_.size()); + + // Advance time by one time quantum. |alternative_service2| should no longer + // be broken. + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + + EXPECT_TRUE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service2)); + EXPECT_EQ(1u, expired_alt_svcs_.size()); + EXPECT_EQ(alternative_service2, expired_alt_svcs_[0]); + + // Advance time until one time quantum before |alternative_service1|'s + // brokenness expires + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(40) - + base::TimeDelta::FromMinutes(5) - + base::TimeDelta::FromInternalValue(1)); + + EXPECT_TRUE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service2)); + EXPECT_EQ(1u, expired_alt_svcs_.size()); + EXPECT_EQ(alternative_service2, expired_alt_svcs_[0]); + + // Advance time by one time quantum. |alternative_service1| should no longer + // be broken. + test_task_runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1)); + + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service2)); + EXPECT_EQ(2u, expired_alt_svcs_.size()); + EXPECT_EQ(alternative_service2, expired_alt_svcs_[0]); + EXPECT_EQ(alternative_service1, expired_alt_svcs_[1]); +} + +TEST_F(BrokenAlternativeServicesTest, ScheduleExpireTaskAfterExpire) { + // This test will check that when a broken alt svc expires, an expiration task + // is scheduled for the next broken alt svc in the expiration queue. + + AlternativeService alternative_service1(kProtoQUIC, "foo", 443); + AlternativeService alternative_service2(kProtoQUIC, "bar", 443); + + // Mark |alternative_service1| broken and let brokenness expire. This will + // increase its expiration delay the next time it's marked broken. + broken_services_.MarkAlternativeServiceBroken(alternative_service1); + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(5)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_FALSE(test_task_runner_->HasPendingTask()); + + // Mark |alternative_service1| and |alternative_service2| broken and + // let |alternative_service2|'s brokenness expire. + broken_services_.MarkAlternativeServiceBroken(alternative_service1); + broken_services_.MarkAlternativeServiceBroken(alternative_service2); + + test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(5)); + EXPECT_FALSE( + broken_services_.IsAlternativeServiceBroken(alternative_service2)); + EXPECT_TRUE( + broken_services_.IsAlternativeServiceBroken(alternative_service1)); + + // Make sure an expiration task has been scheduled for expiring the brokenness + // of |alternative_service1|. + EXPECT_TRUE(test_task_runner_->HasPendingTask()); +} + +} // namespace + +} // namespace net \ No newline at end of file
diff --git a/net/http/http_server_properties_impl.cc b/net/http/http_server_properties_impl.cc index 7cc4a32..abf4bf0 100644 --- a/net/http/http_server_properties_impl.cc +++ b/net/http/http_server_properties_impl.cc
@@ -20,25 +20,17 @@ namespace net { -namespace { - -// Initial delay for broken alternative services. -const uint64_t kBrokenAlternativeProtocolDelaySecs = 300; -// Subsequent failures result in exponential (base 2) backoff. -// Limit binary shift to limit delay to approximately 2 days. -const int kBrokenDelayMaxShift = 9; - -} // namespace - HttpServerPropertiesImpl::HttpServerPropertiesImpl() - : spdy_servers_map_(SpdyServersMap::NO_AUTO_EVICT), + : HttpServerPropertiesImpl(&broken_alternative_services_clock_) {} + +HttpServerPropertiesImpl::HttpServerPropertiesImpl( + base::TickClock* broken_alternative_services_clock) + : broken_alternative_services_(this, broken_alternative_services_clock), + spdy_servers_map_(SpdyServersMap::NO_AUTO_EVICT), alternative_service_map_(AlternativeServiceMap::NO_AUTO_EVICT), - recently_broken_alternative_services_( - RecentlyBrokenAlternativeServices::NO_AUTO_EVICT), server_network_stats_map_(ServerNetworkStatsMap::NO_AUTO_EVICT), quic_server_info_map_(QuicServerInfoMap::NO_AUTO_EVICT), - max_server_configs_stored_in_properties_(kMaxQuicServersToPersist), - weak_ptr_factory_(this) { + max_server_configs_stored_in_properties_(kMaxQuicServersToPersist) { canonical_suffixes_.push_back(".ggpht.com"); canonical_suffixes_.push_back(".c.youtube.com"); canonical_suffixes_.push_back(".googlevideo.com"); @@ -454,71 +446,31 @@ void HttpServerPropertiesImpl::MarkAlternativeServiceBroken( const AlternativeService& alternative_service) { - // Empty host means use host of origin, callers are supposed to substitute. - DCHECK(!alternative_service.host.empty()); - if (alternative_service.protocol == kProtoUnknown) { - LOG(DFATAL) << "Trying to mark unknown alternate protocol broken."; - return; - } - auto it = recently_broken_alternative_services_.Get(alternative_service); - int shift = 0; - if (it == recently_broken_alternative_services_.end()) { - recently_broken_alternative_services_.Put(alternative_service, 1); - } else { - shift = it->second++; - } - if (shift > kBrokenDelayMaxShift) - shift = kBrokenDelayMaxShift; - base::TimeDelta delay = - base::TimeDelta::FromSeconds(kBrokenAlternativeProtocolDelaySecs); - base::TimeTicks when = base::TimeTicks::Now() + delay * (1 << shift); - auto result = broken_alternative_services_.insert( - std::make_pair(alternative_service, when)); - // Return if alternative service is already in expiration queue. - if (!result.second) { - return; - } - - // If this is the only entry in the list, schedule an expiration task. - // Otherwise it will be rescheduled automatically when the pending task runs. - if (broken_alternative_services_.size() == 1) { - ScheduleBrokenAlternateProtocolMappingsExpiration(); - } + broken_alternative_services_.MarkAlternativeServiceBroken( + alternative_service); } void HttpServerPropertiesImpl::MarkAlternativeServiceRecentlyBroken( const AlternativeService& alternative_service) { - if (recently_broken_alternative_services_.Get(alternative_service) == - recently_broken_alternative_services_.end()) { - recently_broken_alternative_services_.Put(alternative_service, 1); - } + broken_alternative_services_.MarkAlternativeServiceRecentlyBroken( + alternative_service); } bool HttpServerPropertiesImpl::IsAlternativeServiceBroken( const AlternativeService& alternative_service) const { - // Empty host means use host of origin, callers are supposed to substitute. - DCHECK(!alternative_service.host.empty()); - return base::ContainsKey(broken_alternative_services_, alternative_service); + return broken_alternative_services_.IsAlternativeServiceBroken( + alternative_service); } bool HttpServerPropertiesImpl::WasAlternativeServiceRecentlyBroken( const AlternativeService& alternative_service) { - if (alternative_service.protocol == kProtoUnknown) - return false; - - return recently_broken_alternative_services_.Get(alternative_service) != - recently_broken_alternative_services_.end(); + return broken_alternative_services_.WasAlternativeServiceRecentlyBroken( + alternative_service); } void HttpServerPropertiesImpl::ConfirmAlternativeService( const AlternativeService& alternative_service) { - if (alternative_service.protocol == kProtoUnknown) - return; - broken_alternative_services_.erase(alternative_service); - auto it = recently_broken_alternative_services_.Get(alternative_service); - if (it != recently_broken_alternative_services_.end()) { - recently_broken_alternative_services_.Erase(it); - } + broken_alternative_services_.ConfirmAlternativeService(alternative_service); } const AlternativeServiceMap& HttpServerPropertiesImpl::alternative_service_map() @@ -712,65 +664,37 @@ canonical_host_to_origin_map_.erase(canonical->first); } -void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() { - base::TimeTicks now = base::TimeTicks::Now(); - while (!broken_alternative_services_.empty()) { - BrokenAlternativeServices::iterator it = - broken_alternative_services_.begin(); - if (now < it->second) { - break; - } - - const AlternativeService expired_alternative_service = it->first; - broken_alternative_services_.erase(it); - - // Remove every occurrence of |expired_alternative_service| from - // |alternative_service_map_|. - for (AlternativeServiceMap::iterator map_it = - alternative_service_map_.begin(); - map_it != alternative_service_map_.end();) { - for (AlternativeServiceInfoVector::iterator it = map_it->second.begin(); - it != map_it->second.end();) { - AlternativeService alternative_service(it->alternative_service); - // Empty hostname in map means hostname of key: substitute before - // comparing to |expired_alternative_service|. - if (alternative_service.host.empty()) { - alternative_service.host = map_it->first.host(); - } - if (alternative_service == expired_alternative_service) { - it = map_it->second.erase(it); - continue; - } - ++it; +void HttpServerPropertiesImpl::OnExpireBrokenAlternativeService( + const AlternativeService& expired_alternative_service) { + // Remove every occurrence of |expired_alternative_service| from + // |alternative_service_map_|. + for (AlternativeServiceMap::iterator map_it = + alternative_service_map_.begin(); + map_it != alternative_service_map_.end();) { + for (AlternativeServiceInfoVector::iterator it = map_it->second.begin(); + it != map_it->second.end();) { + AlternativeService alternative_service(it->alternative_service); + // Empty hostname in map means hostname of key: substitute before + // comparing to |expired_alternative_service|. + if (alternative_service.host.empty()) { + alternative_service.host = map_it->first.host(); } - // If an origin has an empty list of alternative services, then remove it - // from both |canonical_host_to_origin_map_| and - // |alternative_service_map_|. - if (map_it->second.empty()) { - RemoveCanonicalHost(map_it->first); - map_it = alternative_service_map_.Erase(map_it); + if (alternative_service == expired_alternative_service) { + it = map_it->second.erase(it); continue; } - ++map_it; + ++it; } + // If an origin has an empty list of alternative services, then remove it + // from both |canonical_host_to_origin_map_| and + // |alternative_service_map_|. + if (map_it->second.empty()) { + RemoveCanonicalHost(map_it->first); + map_it = alternative_service_map_.Erase(map_it); + continue; + } + ++map_it; } - ScheduleBrokenAlternateProtocolMappingsExpiration(); -} - -void -HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() { - if (broken_alternative_services_.empty()) { - return; - } - base::TimeTicks now = base::TimeTicks::Now(); - base::TimeTicks when = broken_alternative_services_.front().second; - base::TimeDelta delay = when > now ? when - now : base::TimeDelta(); - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, - base::Bind( - &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings, - weak_ptr_factory_.GetWeakPtr()), - delay); } } // namespace net
diff --git a/net/http/http_server_properties_impl.h b/net/http/http_server_properties_impl.h index 598b4474..36e13e6 100644 --- a/net/http/http_server_properties_impl.h +++ b/net/http/http_server_properties_impl.h
@@ -17,11 +17,13 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/threading/non_thread_safe.h" +#include "base/time/default_tick_clock.h" #include "base/values.h" #include "net/base/host_port_pair.h" #include "net/base/ip_address.h" #include "net/base/linked_hash_map.h" #include "net/base/net_export.h" +#include "net/http/broken_alternative_services.h" #include "net/http/http_server_properties.h" namespace base { @@ -30,18 +32,15 @@ namespace net { -struct AlternativeServiceHash { - size_t operator()(const net::AlternativeService& entry) const { - return entry.protocol ^ std::hash<std::string>()(entry.host) ^ entry.port; - } -}; - // The implementation for setting/retrieving the HTTP server properties. class NET_EXPORT HttpServerPropertiesImpl : public HttpServerProperties, + public BrokenAlternativeServices::Delegate, NON_EXPORTED_BASE(public base::NonThreadSafe) { public: HttpServerPropertiesImpl(); + explicit HttpServerPropertiesImpl( + base::TickClock* broken_alternative_services_clock); ~HttpServerPropertiesImpl() override; // Sets |spdy_servers_map_| with the servers (host/port) from @@ -122,7 +121,13 @@ size_t max_server_configs_stored_in_properties) override; bool IsInitialized() const override; + // BrokenAlternativeServices::Delegate method. + void OnExpireBrokenAlternativeService( + const AlternativeService& expired_alternative_service) override; + private: + // TODO (wangyix): modify HttpServerPropertiesImpl unit tests so this + // friendness is no longer required. friend class HttpServerPropertiesImplPeer; // |spdy_servers_map_| has flattened representation of servers @@ -132,14 +137,6 @@ typedef std::vector<std::string> CanonicalSufficList; typedef std::set<HostPortPair> Http11ServerHostPortSet; - // Linked hash map from AlternativeService to expiration time. This container - // is a queue with O(1) enqueue and dequeue, and a hash_map with O(1) lookup - // at the same time. - typedef linked_hash_map<AlternativeService, - base::TimeTicks, - AlternativeServiceHash> - BrokenAlternativeServices; - // Return the iterator for |server|, or for its canonical host, or end. AlternativeServiceMap::const_iterator GetAlternateProtocolIterator( const url::SchemeHostPort& server); @@ -150,17 +147,14 @@ // Remove the cononical host for |server|. void RemoveCanonicalHost(const url::SchemeHostPort& server); - void ExpireBrokenAlternateProtocolMappings(); - void ScheduleBrokenAlternateProtocolMappingsExpiration(); + + base::DefaultTickClock broken_alternative_services_clock_; + BrokenAlternativeServices broken_alternative_services_; SpdyServersMap spdy_servers_map_; Http11ServerHostPortSet http11_servers_; AlternativeServiceMap alternative_service_map_; - BrokenAlternativeServices broken_alternative_services_; - // Class invariant: Every alternative service in broken_alternative_services_ - // must also be in recently_broken_alternative_services_. - RecentlyBrokenAlternativeServices recently_broken_alternative_services_; IPAddress last_quic_address_; ServerNetworkStatsMap server_network_stats_map_; @@ -175,8 +169,6 @@ QuicServerInfoMap quic_server_info_map_; size_t max_server_configs_stored_in_properties_; - base::WeakPtrFactory<HttpServerPropertiesImpl> weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(HttpServerPropertiesImpl); };
diff --git a/net/http/http_server_properties_impl_unittest.cc b/net/http/http_server_properties_impl_unittest.cc index 18edad7..46cd0df 100644 --- a/net/http/http_server_properties_impl_unittest.cc +++ b/net/http/http_server_properties_impl_unittest.cc
@@ -9,6 +9,7 @@ #include <vector> #include "base/logging.h" +#include "base/test/test_mock_time_task_runner.h" #include "base/values.h" #include "net/base/host_port_pair.h" #include "net/base/ip_address.h" @@ -21,18 +22,30 @@ namespace net { +const base::TimeDelta BROKEN_ALT_SVC_EXPIRE_DELAYS[10] = { + base::TimeDelta::FromSeconds(300), base::TimeDelta::FromSeconds(600), + base::TimeDelta::FromSeconds(1200), base::TimeDelta::FromSeconds(2400), + base::TimeDelta::FromSeconds(4800), base::TimeDelta::FromSeconds(9600), + base::TimeDelta::FromSeconds(19200), base::TimeDelta::FromSeconds(38400), + base::TimeDelta::FromSeconds(76800), base::TimeDelta::FromSeconds(153600), +}; + class HttpServerPropertiesImplPeer { public: static void AddBrokenAlternativeServiceWithExpirationTime( HttpServerPropertiesImpl* impl, - AlternativeService alternative_service, + const AlternativeService& alternative_service, base::TimeTicks when) { - impl->broken_alternative_services_.insert( - std::make_pair(alternative_service, when)); + BrokenAlternativeServices::BrokenAlternativeServiceList::iterator unused_it; + impl->broken_alternative_services_.AddToBrokenAlternativeServiceListAndMap( + alternative_service, when, &unused_it); auto it = - impl->recently_broken_alternative_services_.Get(alternative_service); - if (it == impl->recently_broken_alternative_services_.end()) { - impl->recently_broken_alternative_services_.Put(alternative_service, 1); + impl->broken_alternative_services_.recently_broken_alternative_services_ + .Get(alternative_service); + if (it == impl->broken_alternative_services_ + .recently_broken_alternative_services_.end()) { + impl->broken_alternative_services_.recently_broken_alternative_services_ + .Put(alternative_service, 1); } else { it->second++; } @@ -40,7 +53,7 @@ static void ExpireBrokenAlternateProtocolMappings( HttpServerPropertiesImpl* impl) { - impl->ExpireBrokenAlternateProtocolMappings(); + impl->broken_alternative_services_.ExpireBrokenAlternateProtocolMappings(); } }; @@ -50,6 +63,11 @@ class HttpServerPropertiesImplTest : public testing::Test { protected: + HttpServerPropertiesImplTest() + : test_task_runner_(new base::TestMockTimeTaskRunner()), + broken_services_clock_(test_task_runner_->GetMockTickClock()), + impl_(broken_services_clock_.get()) {} + bool HasAlternativeService(const url::SchemeHostPort& origin) { const AlternativeServiceInfoVector alternative_service_info_vector = impl_.GetAlternativeServiceInfos(origin); @@ -63,6 +81,13 @@ return impl_.SetAlternativeService(origin, alternative_service, expiration); } + void MarkBrokenAndLetExpireAlternativeServiceNTimes( + const AlternativeService& alternative_service, + int num_times) {} + + scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_; + + std::unique_ptr<base::TickClock> broken_services_clock_; HttpServerPropertiesImpl impl_; }; @@ -948,7 +973,7 @@ EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(alternative_service)); base::TimeTicks past = - base::TimeTicks::Now() - base::TimeDelta::FromSeconds(42); + broken_services_clock_->NowTicks() - base::TimeDelta::FromSeconds(42); HttpServerPropertiesImplPeer::AddBrokenAlternativeServiceWithExpirationTime( &impl_, alternative_service, past); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service)); @@ -978,7 +1003,7 @@ // Mark "bar:443" as broken. base::TimeTicks past = - base::TimeTicks::Now() - base::TimeDelta::FromSeconds(42); + broken_services_clock_->NowTicks() - base::TimeDelta::FromSeconds(42); HttpServerPropertiesImplPeer::AddBrokenAlternativeServiceWithExpirationTime( &impl_, bar_alternative_service, past); @@ -998,6 +1023,70 @@ impl_.WasAlternativeServiceRecentlyBroken(baz_alternative_service)); } +// Regression test for https://crbug.com/724302 +TEST_F(AlternateProtocolServerPropertiesTest, RemoveExpiredBrokenAltSvc2) { + // This test will mark an alternative service A that has already been marked + // broken many times, then immediately mark another alternative service B as + // broken for the first time. Because A's been marked broken many times + // already, its brokenness will be scheduled to expire much further in the + // future than B, even though it was marked broken before B. This test makes + // sure that even though A was marked broken before B, B's brokenness should + // expire before A. + + url::SchemeHostPort server1("https", "foo", 443); + AlternativeService alternative_service1(kProtoQUIC, "foo", 443); + SetAlternativeService(server1, alternative_service1); + + url::SchemeHostPort server2("https", "bar", 443); + AlternativeService alternative_service2(kProtoQUIC, "bar", 443); + SetAlternativeService(server2, alternative_service2); + + // Repeatedly mark alt svc 1 broken and wait for its brokenness to expire. + // This will increase its time until expiration. + for (int i = 0; i < 3; ++i) { + { + base::TestMockTimeTaskRunner::ScopedContext scoped_context( + test_task_runner_); + impl_.MarkAlternativeServiceBroken(alternative_service1); + } + // |impl_| should have posted task to expire the brokenness of + // |alternative_service1| + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1)); + + // Advance time by just enough so that |alternative_service1|'s brokenness + // expires. + test_task_runner_->FastForwardBy(BROKEN_ALT_SVC_EXPIRE_DELAYS[i]); + + // Ensure brokenness of |alternative_service1| has expired. + EXPECT_FALSE(test_task_runner_->HasPendingTask()); + EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service1)); + } + + { + base::TestMockTimeTaskRunner::ScopedContext scoped_context( + test_task_runner_); + impl_.MarkAlternativeServiceBroken(alternative_service1); + impl_.MarkAlternativeServiceBroken(alternative_service2); + } + + EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service2)); + + // Advance time by just enough so that |alternative_service2|'s brokennness + // expires. + test_task_runner_->FastForwardBy(BROKEN_ALT_SVC_EXPIRE_DELAYS[0]); + + EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service2)); + + // Advance time by enough so that |alternative_service1|'s brokenness expires. + test_task_runner_->FastForwardBy(BROKEN_ALT_SVC_EXPIRE_DELAYS[3] - + BROKEN_ALT_SVC_EXPIRE_DELAYS[0]); + + EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service1)); + EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service2)); +} + typedef HttpServerPropertiesImplTest SupportsQuicServerPropertiesTest; TEST_F(SupportsQuicServerPropertiesTest, Set) {
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl.cc b/net/quic/chromium/bidirectional_stream_quic_impl.cc index 5d2ae044..df82cec1 100644 --- a/net/quic/chromium/bidirectional_stream_quic_impl.cc +++ b/net/quic/chromium/bidirectional_stream_quic_impl.cc
@@ -81,6 +81,10 @@ } void BidirectionalStreamQuicImpl::SendRequestHeaders() { + WriteHeaders(); +} + +bool BidirectionalStreamQuicImpl::WriteHeaders() { DCHECK(!has_sent_headers_); if (!stream_) { LOG(ERROR) @@ -88,7 +92,7 @@ base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&BidirectionalStreamQuicImpl::NotifyError, weak_factory_.GetWeakPtr(), ERR_UNEXPECTED)); - return; + return false; } SpdyHeaderBlock headers; @@ -99,14 +103,17 @@ CreateSpdyHeadersFromHttpRequest( http_request_info, http_request_info.extra_headers, true, &headers); - // Sending the request might result in |this| being deleted. - auto guard = weak_factory_.GetWeakPtr(); + // Sending the request might result in the stream being closed via OnClose + // which will post a task to notify the delegate asynchronously. + // TODO(rch): Clean up this interface when OnClose and OnError are removed. size_t headers_bytes_sent = stream_->WriteHeaders( std::move(headers), request_info_->end_stream_on_headers, nullptr); - if (!guard.get()) - return; + if (!stream_) + return false; + headers_bytes_sent_ += headers_bytes_sent; has_sent_headers_ = true; + return true; } int BidirectionalStreamQuicImpl::ReadData(IOBuffer* buffer, int buffer_len) { @@ -157,7 +164,9 @@ // single data buffer. bundler = session_->CreatePacketBundler(QuicConnection::SEND_ACK_IF_PENDING); - SendRequestHeaders(); + // Sending the request might result in the stream being closed. + if (!WriteHeaders()) + return; } QuicStringPiece string_data(data->data(), length); @@ -191,7 +200,9 @@ session_->CreatePacketBundler(QuicConnection::SEND_ACK_IF_PENDING)); if (!has_sent_headers_) { DCHECK(!send_request_headers_automatically_); - SendRequestHeaders(); + // Sending the request might result in the stream being closed. + if (!WriteHeaders()) + return; } int rv = stream_->WritevStreamData( @@ -242,16 +253,15 @@ if (stream_->connection_error() != QUIC_NO_ERROR || stream_->stream_error() != QUIC_STREAM_NO_ERROR) { - NotifyError(session_->IsCryptoHandshakeConfirmed() - ? ERR_QUIC_PROTOCOL_ERROR - : ERR_QUIC_HANDSHAKE_FAILED); + OnError(session_->IsCryptoHandshakeConfirmed() ? ERR_QUIC_PROTOCOL_ERROR + : ERR_QUIC_HANDSHAKE_FAILED); return; } if (!stream_->fin_sent() || !stream_->fin_received()) { // The connection must have been closed by the peer with QUIC_NO_ERROR, // which is improper. - NotifyError(ERR_UNEXPECTED); + OnError(ERR_UNEXPECTED); return; } @@ -261,7 +271,8 @@ } void BidirectionalStreamQuicImpl::OnError(int error) { - NotifyError(error); + // Avoid reentrancy by notifying the delegate asynchronously. + NotifyErrorImpl(error, /*notify_delegate_later*/ true); } void BidirectionalStreamQuicImpl::OnStreamReady(int rv) { @@ -358,6 +369,11 @@ } void BidirectionalStreamQuicImpl::NotifyError(int error) { + NotifyErrorImpl(error, /*notify_delegate_later*/ false); +} + +void BidirectionalStreamQuicImpl::NotifyErrorImpl(int error, + bool notify_delegate_later) { DCHECK_NE(OK, error); DCHECK_NE(ERR_IO_PENDING, error); @@ -368,19 +384,29 @@ delegate_ = nullptr; // Cancel any pending callback. weak_factory_.InvalidateWeakPtrs(); - delegate->OnFailed(error); - // |this| might be destroyed at this point. + if (notify_delegate_later) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&BidirectionalStreamQuicImpl::NotifyFailure, + weak_factory_.GetWeakPtr(), delegate, error)); + } else { + NotifyFailure(delegate, error); + // |this| might be destroyed at this point. + } } } +void BidirectionalStreamQuicImpl::NotifyFailure( + BidirectionalStreamImpl::Delegate* delegate, + int error) { + delegate->OnFailed(error); + // |this| might be destroyed at this point. +} + void BidirectionalStreamQuicImpl::NotifyStreamReady() { - if (send_request_headers_automatically_) { - // Sending the request might result in |this| being deleted. - auto guard = weak_factory_.GetWeakPtr(); - SendRequestHeaders(); - if (!guard.get()) - return; - } + // Sending the request might result in the stream being closed. + if (send_request_headers_automatically_ && !WriteHeaders()) + return; + if (delegate_) delegate_->OnStreamReady(has_sent_headers_); }
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl.h b/net/quic/chromium/bidirectional_stream_quic_impl.h index 36129e1..af5bcf4 100644 --- a/net/quic/chromium/bidirectional_stream_quic_impl.h +++ b/net/quic/chromium/bidirectional_stream_quic_impl.h
@@ -61,6 +61,10 @@ void OnClose() override; void OnError(int error) override; + // Write headers to the stream and returns true on success. Posts a task to + // notify the delegate asynchronously and returns false on failure + bool WriteHeaders(); + void OnStreamReady(int rv); void OnSendDataComplete(int rv); void ReadInitialHeaders(); @@ -69,12 +73,20 @@ void OnReadTrailingHeadersComplete(int rv); void OnReadDataComplete(int rv); - // Notifies the delegate of an error. + // Notifies the delegate of an error, clears |stream_| and |delegate_|, + // and cancels any pending callbacks. void NotifyError(int error); + // Notifies the delegate of an error, clears |stream_| and |delegate_|, + // and cancels any pending callbacks. If |notify_delegate_later| is true + // then the delegate will be notified asynchronously via a posted task, + // otherwise the notification will be synchronous. + void NotifyErrorImpl(int error, bool notify_delegate_later); // Notifies the delegate that the stream is ready. void NotifyStreamReady(); // Resets the stream and ensures that |delegate_| won't be called back. void ResetStream(); + // Invokes OnFailure(error) on |delegate|. + void NotifyFailure(BidirectionalStreamImpl::Delegate* delegate, int error); const std::unique_ptr<QuicChromiumClientSession::Handle> session_; std::unique_ptr<QuicChromiumClientStream::Handle> stream_;
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc index 94d6009..2efcb02 100644 --- a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc +++ b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
@@ -1248,6 +1248,75 @@ delegate->GetTotalReceivedBytes()); } +// Tests that when request headers are delayed and SendData triggers the +// headers to be sent, if that write fails the stream does not crash. +TEST_P(BidirectionalStreamQuicImplTest, + SendDataWriteErrorCoalesceDataBufferAndHeaderFrame) { + QuicStreamOffset header_stream_offset = 0; + AddWrite(ConstructInitialSettingsPacket(1, &header_stream_offset)); + AddWriteError(SYNCHRONOUS, ERR_CONNECTION_REFUSED); + + Initialize(); + + BidirectionalStreamRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.google.com/"); + request.end_stream_on_headers = false; + request.priority = DEFAULT_PRIORITY; + request.extra_headers.SetHeader("cookie", std::string(2048, 'A')); + + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); + std::unique_ptr<DeleteStreamDelegate> delegate(new DeleteStreamDelegate( + read_buffer.get(), kReadBufferSize, DeleteStreamDelegate::ON_FAILED)); + delegate->DoNotSendRequestHeadersAutomatically(); + delegate->Start(&request, net_log().bound(), session()->CreateHandle()); + ConfirmHandshake(); + delegate->WaitUntilNextCallback(kOnStreamReady); + + // Attempt to send the headers and data. + const char kBody1[] = "here are some data"; + scoped_refptr<StringIOBuffer> buf1(new StringIOBuffer(kBody1)); + delegate->SendData(buf1, buf1->size(), !kFin); + + delegate->WaitUntilNextCallback(kOnFailed); +} + +// Tests that when request headers are delayed and SendvData triggers the +// headers to be sent, if that write fails the stream does not crash. +TEST_P(BidirectionalStreamQuicImplTest, + SendvDataWriteErrorCoalesceDataBufferAndHeaderFrame) { + QuicStreamOffset header_stream_offset = 0; + AddWrite(ConstructInitialSettingsPacket(1, &header_stream_offset)); + AddWriteError(SYNCHRONOUS, ERR_CONNECTION_REFUSED); + + Initialize(); + + BidirectionalStreamRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.google.com/"); + request.end_stream_on_headers = false; + request.priority = DEFAULT_PRIORITY; + request.extra_headers.SetHeader("cookie", std::string(2048, 'A')); + + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); + std::unique_ptr<DeleteStreamDelegate> delegate(new DeleteStreamDelegate( + read_buffer.get(), kReadBufferSize, DeleteStreamDelegate::ON_FAILED)); + delegate->DoNotSendRequestHeadersAutomatically(); + delegate->Start(&request, net_log().bound(), session()->CreateHandle()); + ConfirmHandshake(); + delegate->WaitUntilNextCallback(kOnStreamReady); + + // Attempt to send the headers and data. + const char kBody1[] = "here are some data"; + const char kBody2[] = "data keep coming"; + scoped_refptr<StringIOBuffer> buf1(new StringIOBuffer(kBody1)); + scoped_refptr<StringIOBuffer> buf2(new StringIOBuffer(kBody2)); + std::vector<int> lengths = {buf1->size(), buf2->size()}; + delegate->SendvData({buf1, buf2}, lengths, !kFin); + + delegate->WaitUntilNextCallback(kOnFailed); +} + TEST_P(BidirectionalStreamQuicImplTest, PostRequest) { SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; @@ -1520,7 +1589,7 @@ // Server sends a Rst. ProcessPacket(ConstructServerRstStreamPacket(1)); - EXPECT_TRUE(delegate->on_failed_called()); + delegate->WaitUntilNextCallback(kOnFailed); TestCompletionCallback cb; EXPECT_THAT(delegate->ReadData(cb.callback()), @@ -1584,7 +1653,7 @@ // Server sends a Rst. ProcessPacket(ConstructServerRstStreamPacket(3)); - EXPECT_TRUE(delegate->on_failed_called()); + delegate->WaitUntilNextCallback(kOnFailed); EXPECT_THAT(delegate->ReadData(cb.callback()), IsError(ERR_QUIC_PROTOCOL_ERROR)); @@ -1638,7 +1707,7 @@ EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); session()->connection()->CloseConnection( QUIC_NO_ERROR, "test", ConnectionCloseBehavior::SILENT_CLOSE); - EXPECT_TRUE(delegate->on_failed_called()); + delegate->WaitUntilNextCallback(kOnFailed); // Try to send data after OnFailed(), should not get called back. scoped_refptr<StringIOBuffer> buf(new StringIOBuffer(kUploadData));
diff --git a/remoting/ios/app/remoting_view_controller.mm b/remoting/ios/app/remoting_view_controller.mm index 1f28e5e..443311a 100644 --- a/remoting/ios/app/remoting_view_controller.mm +++ b/remoting/ios/app/remoting_view_controller.mm
@@ -203,6 +203,13 @@ - (void)didSelectCell:(HostCollectionViewCell*)cell completion:(void (^)())completionBlock { + if (![cell.hostInfo isOnline]) { + MDCSnackbarMessage* message = [[MDCSnackbarMessage alloc] init]; + message.text = @"Host is offline."; + [MDCSnackbarManager showMessage:message]; + return; + } + _client = [[RemotingClient alloc] init]; [_remotingService.authentication
diff --git a/services/preferences/persistent_pref_store_factory.cc b/services/preferences/persistent_pref_store_factory.cc index 587ac96..c9145fdc 100644 --- a/services/preferences/persistent_pref_store_factory.cc +++ b/services/preferences/persistent_pref_store_factory.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "components/prefs/in_memory_pref_store.h" #include "components/prefs/json_pref_store.h" #include "components/prefs/pref_filter.h" #include "services/preferences/persistent_pref_store_impl.h" @@ -44,6 +45,10 @@ std::move(configuration->get_tracked_configuration()), worker_pool), std::move(on_initialized)); } + if (configuration->is_incognito_configuration()) { + return base::MakeUnique<PersistentPrefStoreImpl>( + base::MakeRefCounted<InMemoryPrefStore>(), std::move(on_initialized)); + } NOTREACHED(); return nullptr; }
diff --git a/services/preferences/pref_store_manager_impl.cc b/services/preferences/pref_store_manager_impl.cc index 288e649..9803ec5b 100644 --- a/services/preferences/pref_store_manager_impl.cc +++ b/services/preferences/pref_store_manager_impl.cc
@@ -36,9 +36,13 @@ base::ContainsValue(expected_pref_stores_, PrefValueStore::DEFAULT_STORE)) << "expected_pref_stores must always include PrefValueStore::USER_STORE " "and PrefValueStore::DEFAULT_STORE."; - // The user store is not actually connected to in the implementation, but - // accessed directly. + // The user store is not actually registered or connected to in the + // implementation, but accessed directly. expected_pref_stores_.erase(PrefValueStore::USER_STORE); + DVLOG(1) << "Expecting " << expected_pref_stores_.size() + << " pref store(s) to register"; + // This store is done in-process so it's already "registered": + DVLOG(1) << "Registering pref store: " << PrefValueStore::DEFAULT_STORE; registry_.AddInterface<prefs::mojom::PrefStoreConnector>( base::Bind(&PrefStoreManagerImpl::BindPrefStoreConnectorRequest, base::Unretained(this))); @@ -50,7 +54,10 @@ base::Unretained(this))); } -PrefStoreManagerImpl::~PrefStoreManagerImpl() = default; +PrefStoreManagerImpl::~PrefStoreManagerImpl() { + // For logging consistency: + DVLOG(1) << "Deregistering pref store: " << PrefValueStore::DEFAULT_STORE; +} void PrefStoreManagerImpl::Register(PrefValueStore::PrefStoreType type, mojom::PrefStorePtr pref_store_ptr) { @@ -79,6 +86,9 @@ mojom::PrefRegistryPtr pref_registry, const std::vector<PrefValueStore::PrefStoreType>& already_connected_types, ConnectCallback callback) { + DVLOG(1) << "Will connect to " + << expected_pref_stores_.size() - already_connected_types.size() + << " pref store(s)"; std::set<PrefValueStore::PrefStoreType> required_remote_types; for (auto type : expected_pref_stores_) { if (!base::ContainsValue(already_connected_types, type)) { @@ -98,6 +108,19 @@ for (auto type : remaining_remote_types) { pending_connections_[type].push_back(connection); } + if (!Initialized()) { + pending_incognito_connections_.push_back(connection); + } else if (incognito_connector_) { + connection->ProvideIncognitoConnector(incognito_connector_); + } +} + +void PrefStoreManagerImpl::ConnectToUserPrefStore( + const std::vector<std::string>& observed_prefs, + mojom::PrefStoreConnector::ConnectToUserPrefStoreCallback callback) { + std::move(callback).Run(persistent_pref_store_->CreateConnection( + PersistentPrefStoreImpl::ObservedPrefs(observed_prefs.begin(), + observed_prefs.end()))); } void PrefStoreManagerImpl::BindPrefStoreConnectorRequest( @@ -129,6 +152,14 @@ mojom::PersistentPrefStoreConfigurationPtr configuration) { DCHECK(!persistent_pref_store_); + if (configuration->is_incognito_configuration()) { + incognito_connector_ = + std::move(configuration->get_incognito_configuration()->connector); + for (auto connection : pending_incognito_connections_) { + connection->ProvideIncognitoConnector(incognito_connector_); + } + } + pending_incognito_connections_.clear(); persistent_pref_store_ = CreatePersistentPrefStore( std::move(configuration), worker_pool_.get(), base::Bind(&PrefStoreManagerImpl::OnPersistentPrefStoreReady, @@ -162,4 +193,8 @@ pending_persistent_connections_.clear(); } +bool PrefStoreManagerImpl::Initialized() const { + return bool(persistent_pref_store_); +} + } // namespace prefs
diff --git a/services/preferences/pref_store_manager_impl.h b/services/preferences/pref_store_manager_impl.h index c1b0ae83..5e7c4f4 100644 --- a/services/preferences/pref_store_manager_impl.h +++ b/services/preferences/pref_store_manager_impl.h
@@ -54,13 +54,18 @@ void Register(PrefValueStore::PrefStoreType type, mojom::PrefStorePtr pref_store_ptr) override; - // mojom::PrefStoreConnector: |already_connected_types| must not include - // PrefValueStore::DEFAULT_STORE and PrefValueStore::USER_STORE as these must - // always be accessed through the service. + // mojom::PrefStoreConnector: + // |already_connected_types| must not include PrefValueStore::DEFAULT_STORE + // and PrefValueStore::USER_STORE as these must always be accessed through the + // service. void Connect( mojom::PrefRegistryPtr pref_registry, const std::vector<PrefValueStore::PrefStoreType>& already_connected_types, ConnectCallback callback) override; + void ConnectToUserPrefStore( + const std::vector<std::string>& observed_prefs, + mojom::PrefStoreConnector::ConnectToUserPrefStoreCallback callback) + override; void BindPrefStoreConnectorRequest( const service_manager::BindSourceInfo& source_info, @@ -86,6 +91,9 @@ void OnPersistentPrefStoreReady(); + // Has |Init| been called? + bool Initialized() const; + // PrefStores that need to register before replying to any Connect calls. This // does not include the PersistentPrefStore, which is handled separately. std::set<PrefValueStore::PrefStoreType> expected_pref_stores_; @@ -99,6 +107,8 @@ std::unique_ptr<PersistentPrefStoreImpl> persistent_pref_store_; mojo::Binding<mojom::PrefServiceControl> init_binding_; + mojom::PrefStoreConnectorPtr incognito_connector_; + const scoped_refptr<DefaultPrefStore> defaults_; const std::unique_ptr<PrefStoreImpl> defaults_wrapper_; @@ -110,6 +120,8 @@ pending_connections_; std::vector<scoped_refptr<ScopedPrefConnectionBuilder>> pending_persistent_connections_; + std::vector<scoped_refptr<ScopedPrefConnectionBuilder>> + pending_incognito_connections_; const scoped_refptr<base::SequencedWorkerPool> worker_pool_;
diff --git a/services/preferences/public/cpp/persistent_pref_store_client.cc b/services/preferences/public/cpp/persistent_pref_store_client.cc index baf9aa1a..ef9a633 100644 --- a/services/preferences/public/cpp/persistent_pref_store_client.cc +++ b/services/preferences/public/cpp/persistent_pref_store_client.cc
@@ -73,7 +73,7 @@ PersistentPrefStoreClient::PersistentPrefStoreClient( mojom::PersistentPrefStoreConnectionPtr connection) : weak_factory_(this) { - OnConnect(std::move(connection), + OnConnect(std::move(connection), mojom::PersistentPrefStoreConnection::New(), std::unordered_map<PrefValueStore::PrefStoreType, prefs::mojom::PrefStoreConnectionPtr>()); } @@ -139,16 +139,18 @@ PersistentPrefStore::PrefReadError PersistentPrefStoreClient::ReadPrefs() { mojom::PersistentPrefStoreConnectionPtr connection; + mojom::PersistentPrefStoreConnectionPtr incognito_connection; std::unordered_map<PrefValueStore::PrefStoreType, prefs::mojom::PrefStoreConnectionPtr> other_pref_stores; mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_calls; bool success = connector_->Connect(SerializePrefRegistry(*pref_registry_), already_connected_types_, &connection, - &other_pref_stores); + &incognito_connection, &other_pref_stores); DCHECK(success); pref_registry_ = nullptr; - OnConnect(std::move(connection), std::move(other_pref_stores)); + OnConnect(std::move(connection), std::move(incognito_connection), + std::move(other_pref_stores)); return read_error_; } @@ -188,6 +190,7 @@ void PersistentPrefStoreClient::OnConnect( mojom::PersistentPrefStoreConnectionPtr connection, + mojom::PersistentPrefStoreConnectionPtr incognito_connection, std::unordered_map<PrefValueStore::PrefStoreType, prefs::mojom::PrefStoreConnectionPtr> other_pref_stores) {
diff --git a/services/preferences/public/cpp/persistent_pref_store_client.h b/services/preferences/public/cpp/persistent_pref_store_client.h index 67849f42..c6f9de86 100644 --- a/services/preferences/public/cpp/persistent_pref_store_client.h +++ b/services/preferences/public/cpp/persistent_pref_store_client.h
@@ -70,6 +70,7 @@ private: void OnConnect(mojom::PersistentPrefStoreConnectionPtr connection, + mojom::PersistentPrefStoreConnectionPtr incognito_connection, std::unordered_map<PrefValueStore::PrefStoreType, prefs::mojom::PrefStoreConnectionPtr> other_pref_stores);
diff --git a/services/preferences/public/cpp/pref_service_factory.cc b/services/preferences/public/cpp/pref_service_factory.cc index 4c6b4c7..eda3a20d 100644 --- a/services/preferences/public/cpp/pref_service_factory.cc +++ b/services/preferences/public/cpp/pref_service_factory.cc
@@ -5,6 +5,7 @@ #include "services/preferences/public/cpp/pref_service_factory.h" #include "base/callback_helpers.h" +#include "components/prefs/overlay_user_pref_store.h" #include "components/prefs/persistent_pref_store.h" #include "components/prefs/pref_notifier_impl.h" #include "components/prefs/pref_registry.h" @@ -64,6 +65,7 @@ local_layered_pref_stores, ConnectCallback callback, mojom::PersistentPrefStoreConnectionPtr persistent_pref_store_connection, + mojom::PersistentPrefStoreConnectionPtr incognito_connection, std::unordered_map<PrefValueStore::PrefStoreType, mojom::PrefStoreConnectionPtr> connections) { scoped_refptr<PrefStore> managed_prefs = CreatePrefStoreClient( @@ -86,13 +88,22 @@ scoped_refptr<PersistentPrefStore> persistent_pref_store( new PersistentPrefStoreClient( std::move(persistent_pref_store_connection))); + // If in incognito mode, |persistent_pref_store| above will be a connection to + // an in-memory pref store and |incognito_connection| will refer to the + // underlying profile's user pref store. + scoped_refptr<PersistentPrefStore> user_pref_store = + incognito_connection + ? new OverlayUserPrefStore( + persistent_pref_store.get(), + new PersistentPrefStoreClient(std::move(incognito_connection))) + : persistent_pref_store; PrefNotifierImpl* pref_notifier = new PrefNotifierImpl(); auto* pref_value_store = new PrefValueStore( managed_prefs.get(), supervised_user_prefs.get(), extension_prefs.get(), - command_line_prefs.get(), persistent_pref_store.get(), - recommended_prefs.get(), pref_registry->defaults().get(), pref_notifier); + command_line_prefs.get(), user_pref_store.get(), recommended_prefs.get(), + pref_registry->defaults().get(), pref_notifier); callback.Run(base::MakeUnique<::PrefService>( - pref_notifier, pref_value_store, persistent_pref_store.get(), + pref_notifier, pref_value_store, user_pref_store.get(), pref_registry.get(), base::Bind(&DoNothingHandleReadError), true)); connector_ptr->reset(); }
diff --git a/services/preferences/public/cpp/tracked/configuration.h b/services/preferences/public/cpp/tracked/configuration.h index bbb0ebff..2b06c56 100644 --- a/services/preferences/public/cpp/tracked/configuration.h +++ b/services/preferences/public/cpp/tracked/configuration.h
@@ -5,7 +5,7 @@ #ifndef SERVICES_PREFERENCES_PUBLIC_CPP_TRACKED_CONFIGURATION_H_ #define SERVICES_PREFERENCES_PUBLIC_CPP_TRACKED_CONFIGURATION_H_ -#include "services/preferences/public/interfaces/preferences_configuration.mojom.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" namespace prefs {
diff --git a/services/preferences/public/cpp/tracked/mock_validation_delegate.h b/services/preferences/public/cpp/tracked/mock_validation_delegate.h index 5538c693..44f9fb8 100644 --- a/services/preferences/public/cpp/tracked/mock_validation_delegate.h +++ b/services/preferences/public/cpp/tracked/mock_validation_delegate.h
@@ -12,7 +12,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "services/preferences/public/interfaces/preferences_configuration.mojom.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" #include "services/preferences/public/interfaces/tracked_preference_validation_delegate.mojom.h" class MockValidationDelegate;
diff --git a/services/preferences/public/interfaces/BUILD.gn b/services/preferences/public/interfaces/BUILD.gn index 8bc3749..58d8191f 100644 --- a/services/preferences/public/interfaces/BUILD.gn +++ b/services/preferences/public/interfaces/BUILD.gn
@@ -7,7 +7,6 @@ mojom("interfaces") { sources = [ "preferences.mojom", - "preferences_configuration.mojom", "tracked_preference_validation_delegate.mojom", ] public_deps = [
diff --git a/services/preferences/public/interfaces/preferences.mojom b/services/preferences/public/interfaces/preferences.mojom index eb44945..d195e240 100644 --- a/services/preferences/public/interfaces/preferences.mojom +++ b/services/preferences/public/interfaces/preferences.mojom
@@ -4,8 +4,10 @@ module prefs.mojom; +import "mojo/common/file_path.mojom"; +import "mojo/common/string16.mojom"; import "mojo/common/values.mojom"; -import "services/preferences/public/interfaces/preferences_configuration.mojom"; +import "services/preferences/public/interfaces/tracked_preference_validation_delegate.mojom"; const string kServiceName = "preferences"; const string kForwarderServiceName = "preferences_forwarder"; @@ -92,11 +94,24 @@ // be received. The client asserts that it is already connected to the // |already_connected_types| pref stores through some other means, so the // Connect call will not connect to those. + // + // The returned |connection| is the connection to the main writable user pref + // store. If a |underlay| is returned any writes or reads not serviced by + // |connection| should be serviced by |underlay| instead (e.g. by using an + // |OverlayUserPrefStore|). + // + // Calls to |Connect| before |Init| are allowed and will cause the calls to + // queue and connect once |Init| has been called. [Sync] Connect(PrefRegistry pref_registry, array<PrefStoreType> already_connected_types) => (PersistentPrefStoreConnection connection, + PersistentPrefStoreConnection? underlay, map<PrefStoreType, PrefStoreConnection> connections); + + // Connect to the user pref store. Used for incognito. + ConnectToUserPrefStore(array<string> prefs_to_observe) => + (PersistentPrefStoreConnection connection); }; // An update to a subcomponent of a pref. @@ -155,3 +170,65 @@ // be used. Init(PersistentPrefStoreConfiguration configuration); }; + +// --------------------------------------------------------------------- +// Service Configuration + +union PersistentPrefStoreConfiguration { + SimplePersistentPrefStoreConfiguration simple_configuration; + TrackedPersistentPrefStoreConfiguration tracked_configuration; + IncognitoPersistentPrefStoreConfiguration incognito_configuration; +}; + +struct SimplePersistentPrefStoreConfiguration { + mojo.common.mojom.FilePath pref_filename; +}; + +// These parameters are passed to prefs::CreateTrackedPersistentPrefStore() in +// services/preferences/persistent_pref_store_factory.cc. +struct TrackedPersistentPrefStoreConfiguration { + mojo.common.mojom.FilePath unprotected_pref_filename; + mojo.common.mojom.FilePath protected_pref_filename; + array<TrackedPreferenceMetadata> tracking_configuration; + uint64 reporting_ids_count; + string seed; + string legacy_device_id; + string registry_seed; + mojo.common.mojom.String16 registry_path; + TrackedPreferenceValidationDelegate? validation_delegate; + ResetOnLoadObserver? reset_on_load_observer; +}; + +struct TrackedPreferenceMetadata { + enum EnforcementLevel { NO_ENFORCEMENT, ENFORCE_ON_LOAD }; + + enum PrefTrackingStrategy { + // Atomic preferences are tracked as a whole. + ATOMIC, + // Split preferences are dictionaries for which each top-level entry is + // tracked independently. Note: preferences using this strategy must be kept + // in sync with TrackedSplitPreferences in histograms.xml. + SPLIT, + }; + + enum ValueType { + IMPERSONAL, + // The preference value may contain personal information. + PERSONAL, + }; + + uint64 reporting_id; + string name; + EnforcementLevel enforcement_level; + PrefTrackingStrategy strategy; + ValueType value_type; +}; + +interface ResetOnLoadObserver { + OnResetOnLoad(); +}; + +struct IncognitoPersistentPrefStoreConfiguration { + // A connector for the underlying profile's prefs. + PrefStoreConnector connector; +};
diff --git a/services/preferences/public/interfaces/preferences_configuration.mojom b/services/preferences/public/interfaces/preferences_configuration.mojom deleted file mode 100644 index 76f7785..0000000 --- a/services/preferences/public/interfaces/preferences_configuration.mojom +++ /dev/null
@@ -1,62 +0,0 @@ -// 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. - -module prefs.mojom; - -import "mojo/common/file_path.mojom"; -import "mojo/common/string16.mojom"; -import "services/preferences/public/interfaces/tracked_preference_validation_delegate.mojom"; - -union PersistentPrefStoreConfiguration { - SimplePersistentPrefStoreConfiguration simple_configuration; - TrackedPersistentPrefStoreConfiguration tracked_configuration; -}; - -struct SimplePersistentPrefStoreConfiguration { - mojo.common.mojom.FilePath pref_filename; -}; - -// These parameters are passed to prefs::CreateTrackedPersistentPrefStore() in -// services/preferences/persistent_pref_store_factory.cc. -struct TrackedPersistentPrefStoreConfiguration { - mojo.common.mojom.FilePath unprotected_pref_filename; - mojo.common.mojom.FilePath protected_pref_filename; - array<TrackedPreferenceMetadata> tracking_configuration; - uint64 reporting_ids_count; - string seed; - string legacy_device_id; - string registry_seed; - mojo.common.mojom.String16 registry_path; - TrackedPreferenceValidationDelegate? validation_delegate; - ResetOnLoadObserver? reset_on_load_observer; -}; - -struct TrackedPreferenceMetadata { - enum EnforcementLevel { NO_ENFORCEMENT, ENFORCE_ON_LOAD }; - - enum PrefTrackingStrategy { - // Atomic preferences are tracked as a whole. - ATOMIC, - // Split preferences are dictionaries for which each top-level entry is - // tracked independently. Note: preferences using this strategy must be kept - // in sync with TrackedSplitPreferences in histograms.xml. - SPLIT, - }; - - enum ValueType { - IMPERSONAL, - // The preference value may contain personal information. - PERSONAL, - }; - - uint64 reporting_id; - string name; - EnforcementLevel enforcement_level; - PrefTrackingStrategy strategy; - ValueType value_type; -}; - -interface ResetOnLoadObserver { - OnResetOnLoad(); -};
diff --git a/services/preferences/scoped_pref_connection_builder.cc b/services/preferences/scoped_pref_connection_builder.cc index 8e6af17..c279b7c0 100644 --- a/services/preferences/scoped_pref_connection_builder.cc +++ b/services/preferences/scoped_pref_connection_builder.cc
@@ -46,8 +46,16 @@ observed_prefs_.end())); } +void ScopedPrefConnectionBuilder::ProvideIncognitoConnector( + const mojom::PrefStoreConnectorPtr& incognito_connector) { + incognito_connector->ConnectToUserPrefStore( + observed_prefs_, + base::Bind(&ScopedPrefConnectionBuilder::OnIncognitoConnect, this)); +} + ScopedPrefConnectionBuilder::~ScopedPrefConnectionBuilder() { std::move(callback_).Run(std::move(persistent_pref_store_connection_), + std::move(incognito_connection_), std::move(connections_)); } @@ -58,4 +66,10 @@ DCHECK(inserted); } +void ScopedPrefConnectionBuilder::OnIncognitoConnect( + mojom::PersistentPrefStoreConnectionPtr connection_ptr) { + DCHECK(!incognito_connection_); + incognito_connection_ = std::move(connection_ptr); +} + } // namespace prefs
diff --git a/services/preferences/scoped_pref_connection_builder.h b/services/preferences/scoped_pref_connection_builder.h index c73e97e..71d38f41 100644 --- a/services/preferences/scoped_pref_connection_builder.h +++ b/services/preferences/scoped_pref_connection_builder.h
@@ -40,6 +40,9 @@ void ProvidePersistentPrefStore( PersistentPrefStoreImpl* persistent_pref_store); + void ProvideIncognitoConnector( + const mojom::PrefStoreConnectorPtr& incognito_connector); + private: friend class base::RefCounted<ScopedPrefConnectionBuilder>; ~ScopedPrefConnectionBuilder(); @@ -47,6 +50,9 @@ void OnConnect(PrefValueStore::PrefStoreType type, mojom::PrefStoreConnectionPtr connection_ptr); + void OnIncognitoConnect( + mojom::PersistentPrefStoreConnectionPtr connection_ptr); + mojom::PrefStoreConnector::ConnectCallback callback_; std::vector<std::string> observed_prefs_; @@ -57,6 +63,7 @@ connections_; mojom::PersistentPrefStoreConnectionPtr persistent_pref_store_connection_; + mojom::PersistentPrefStoreConnectionPtr incognito_connection_; DISALLOW_COPY_AND_ASSIGN(ScopedPrefConnectionBuilder); };
diff --git a/services/preferences/tracked/pref_hash_filter.h b/services/preferences/tracked/pref_hash_filter.h index 4c3cdaa4..6467eb10 100644 --- a/services/preferences/tracked/pref_hash_filter.h +++ b/services/preferences/tracked/pref_hash_filter.h
@@ -18,7 +18,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/optional.h" -#include "services/preferences/public/interfaces/preferences_configuration.mojom.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" #include "services/preferences/tracked/hash_store_contents.h" #include "services/preferences/tracked/interceptable_pref_filter.h" #include "services/preferences/tracked/tracked_preference.h"
diff --git a/services/preferences/tracked/tracked_persistent_pref_store_factory.h b/services/preferences/tracked/tracked_persistent_pref_store_factory.h index fb426bab..e50d1278 100644 --- a/services/preferences/tracked/tracked_persistent_pref_store_factory.h +++ b/services/preferences/tracked/tracked_persistent_pref_store_factory.h
@@ -6,7 +6,7 @@ #define SERVICES_PREFERENCES_TRACKED_TRACKED_PERSISTENT_PREF_STORE_FACTORY_H_ #include <utility> -#include "services/preferences/public/interfaces/preferences_configuration.mojom.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" namespace base { class DictionaryValue;
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn index 0f93d30..46e503c 100644 --- a/services/ui/ws/BUILD.gn +++ b/services/ui/ws/BUILD.gn
@@ -48,6 +48,7 @@ "event_matcher.h", "event_targeter.cc", "event_targeter.h", + "event_targeter_delegate.h", "focus_controller.cc", "focus_controller.h", "focus_controller_delegate.h",
diff --git a/services/ui/ws/event_dispatcher.cc b/services/ui/ws/event_dispatcher.cc index c6e6897..5634221 100644 --- a/services/ui/ws/event_dispatcher.cc +++ b/services/ui/ws/event_dispatcher.cc
@@ -47,8 +47,7 @@ capture_window_client_id_(kInvalidClientId), modal_window_controller_(this), event_targeter_( - base::MakeUnique<EventTargeter>(delegate_, - &modal_window_controller_)), + base::MakeUnique<EventTargeter>(this, &modal_window_controller_)), mouse_button_down_(false), mouse_cursor_source_window_(nullptr), mouse_cursor_in_non_client_area_(false) {} @@ -317,6 +316,12 @@ return; } +ServerWindow* EventDispatcher::GetRootWindowContaining( + gfx::Point* location_in_display, + int64_t* display_id) { + return delegate_->GetRootWindowContaining(location_in_display, display_id); +} + void EventDispatcher::SetMouseCursorSourceWindow(ServerWindow* window) { if (mouse_cursor_source_window_ == window) return;
diff --git a/services/ui/ws/event_dispatcher.h b/services/ui/ws/event_dispatcher.h index 193786b..1da07968 100644 --- a/services/ui/ws/event_dispatcher.h +++ b/services/ui/ws/event_dispatcher.h
@@ -17,6 +17,7 @@ #include "services/ui/public/interfaces/window_manager.mojom.h" #include "services/ui/ws/drag_cursor_updater.h" #include "services/ui/ws/event_targeter.h" +#include "services/ui/ws/event_targeter_delegate.h" #include "services/ui/ws/modal_window_controller.h" #include "services/ui/ws/server_window_observer.h" #include "ui/gfx/geometry/rect_f.h" @@ -41,7 +42,9 @@ } // Handles dispatching events to the right location as well as updating focus. -class EventDispatcher : public ServerWindowObserver, public DragCursorUpdater { +class EventDispatcher : public ServerWindowObserver, + public DragCursorUpdater, + public EventTargeterDelegate { public: enum class AcceleratorMatchPhase { // Both pre and post should be considered. @@ -148,6 +151,10 @@ const int64_t display_id, AcceleratorMatchPhase match_phase); + // EventTargeterDelegate: + ServerWindow* GetRootWindowContaining(gfx::Point* location_in_display, + int64_t* display_id) override; + private: friend class test::EventDispatcherTestApi;
diff --git a/services/ui/ws/event_targeter.cc b/services/ui/ws/event_targeter.cc index 9329b4b..7e1f3a4 100644 --- a/services/ui/ws/event_targeter.cc +++ b/services/ui/ws/event_targeter.cc
@@ -4,7 +4,7 @@ #include "services/ui/ws/event_targeter.h" -#include "services/ui/ws/event_dispatcher_delegate.h" +#include "services/ui/ws/event_targeter_delegate.h" #include "services/ui/ws/modal_window_controller.h" #include "services/ui/ws/window_finder.h" #include "ui/events/event.h" @@ -13,9 +13,9 @@ namespace ui { namespace ws { -EventTargeter::EventTargeter(EventDispatcherDelegate* event_dispatcher_delegate, +EventTargeter::EventTargeter(EventTargeterDelegate* event_targeter_delegate, ModalWindowController* modal_window_controller) - : event_dispatcher_delegate_(event_dispatcher_delegate), + : event_targeter_delegate_(event_targeter_delegate), modal_window_controller_(modal_window_controller) {} EventTargeter::~EventTargeter() {} @@ -41,7 +41,7 @@ gfx::Point* location, int64_t* display_id) { ServerWindow* root = - event_dispatcher_delegate_->GetRootWindowContaining(location, display_id); + event_targeter_delegate_->GetRootWindowContaining(location, display_id); return root ? ui::ws::FindDeepestVisibleWindowForEvents(root, *location) : DeepestWindow(); }
diff --git a/services/ui/ws/event_targeter.h b/services/ui/ws/event_targeter.h index a59aba2..91dfaf3 100644 --- a/services/ui/ws/event_targeter.h +++ b/services/ui/ws/event_targeter.h
@@ -18,7 +18,7 @@ namespace ws { struct DeepestWindow; -class EventDispatcherDelegate; +class EventTargeterDelegate; class ModalWindowController; class ServerWindow; @@ -46,7 +46,7 @@ // Finds the PointerTarget for an event or the DeepestWindow for a location. class EventTargeter { public: - EventTargeter(EventDispatcherDelegate* event_dispatcher_delegate, + EventTargeter(EventTargeterDelegate* event_targeter_delegate, ModalWindowController* modal_window_controller); ~EventTargeter(); @@ -62,7 +62,7 @@ int64_t* display_id); private: - EventDispatcherDelegate* event_dispatcher_delegate_; + EventTargeterDelegate* event_targeter_delegate_; ModalWindowController* modal_window_controller_; DISALLOW_COPY_AND_ASSIGN(EventTargeter);
diff --git a/services/ui/ws/event_targeter_delegate.h b/services/ui/ws/event_targeter_delegate.h new file mode 100644 index 0000000..2e46860 --- /dev/null +++ b/services/ui/ws/event_targeter_delegate.h
@@ -0,0 +1,33 @@ +// 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. + +#ifndef SERVICES_UI_WS_EVENT_TARGETER_DELEGATE_H_ +#define SERVICES_UI_WS_EVENT_TARGETER_DELEGATE_H_ + +#include <stdint.h> + +namespace gfx { +class Point; +} + +namespace ui { +namespace ws { +class ServerWindow; + +// Used by EventTargeter to talk to WindowManagerState. +class EventTargeterDelegate { + public: + // Calls EventDispatcherDelegate::GetRootWindowContaining, see + // event_dispatcher_delegate.h for details. + virtual ServerWindow* GetRootWindowContaining(gfx::Point* location_in_display, + int64_t* display_id) = 0; + + protected: + virtual ~EventTargeterDelegate() {} +}; + +} // namespace ws +} // namespace ui + +#endif // SERVICES_UI_WS_EVENT_TARGETER_DELEGATE_H_
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index e73c1d63..6cf6bfc 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -1503,6 +1503,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "webkit_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "wm_unittests" } ]
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-item-selection-dialog-filtering.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-item-selection-dialog-filtering.js index 226c642d..7fc09aec 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-item-selection-dialog-filtering.js +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-item-selection-dialog-filtering.js
@@ -33,10 +33,10 @@ function dump() { TestRunner.addResult("Query:" + JSON.stringify(filteredSelectionDialog._value())); - var list = filteredSelectionDialog._list; + var items = filteredSelectionDialog._items; var output = []; - for (var i = 0; i < list.length(); ++i) - output.push(provider.itemKeyAt(list.itemAtIndex(i))); + for (var i = 0; i < items.length(); ++i) + output.push(provider.itemKeyAt(items.itemAtIndex(i))); TestRunner.addResult("Output:" + JSON.stringify(output)); }
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-list-widget-providers.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-list-widget-providers.js index 44774ddb..13b4d98 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-list-widget-providers.js +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-list-widget-providers.js
@@ -42,18 +42,18 @@ } function dump() { - var list = filteredListWidget._list; if (filteredListWidget._bottomElementsContainer.classList.contains('hidden')) { TestRunner.addResult('Output: <hidden>'); return; } - if (list.element.classList.contains('hidden')) { + if (filteredListWidget._list.element.classList.contains('hidden')) { TestRunner.addResult('Output: ' + filteredListWidget._notFoundElement.textContent); return; } + var items = filteredListWidget._items; var output = []; - for (var i = 0; i < list.length(); ++i) - output.push(filteredListWidget._provider.itemKeyAt(list.itemAtIndex(i))); + for (var i = 0; i < items.length(); ++i) + output.push(filteredListWidget._provider.itemKeyAt(items.itemAtIndex(i))); TestRunner.addResult('Output:' + JSON.stringify(output)); } }
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-equal-height-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-equal-height-expected.txt index 5fd0dcd..ec113cad 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-equal-height-expected.txt +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-equal-height-expected.txt
@@ -642,4 +642,34 @@ *[0] 29 *[15] bottom +Replacing model with [5-7] +Creating element for 5 +Creating element for 6 +Creating element for 7 +----list[length=1][height=84]---- + [0] top +*[0] 5 +*[15] 6 +*[30] 7 +*[45] bottom + +Pushing 8 +Creating element for 8 +----list[length=1][height=84]---- + [0] top +*[0] 5 +*[15] 6 +*[30] 7 +*[45] 8 +*[60] bottom + +Pushing 9 to old model +----list[length=2][height=84]---- + [0] top +*[0] 5 +*[15] 6 +*[30] 7 +*[45] 8 +*[60] bottom +
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-equal-height.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-equal-height.js index 189faf0..67b85b7 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-equal-height.js +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-equal-height.js
@@ -31,14 +31,15 @@ } var delegate = new Delegate(); -var list = new UI.ListControl(delegate, UI.ListMode.EqualHeightItems); +var model = new UI.ListModel(); +var list = new UI.ListControl(model, delegate, UI.ListMode.EqualHeightItems); list.element.style.height = '73px'; UI.inspectorView.element.appendChild(list.element); function dumpList() { var height = list.element.offsetHeight; - TestRunner.addResult(`----list[length=${list.length()}][height=${height}]----`); + TestRunner.addResult(`----list[length=${model.length()}][height=${height}]----`); for (var child of list.element.children) { var offsetTop = child.getBoundingClientRect().top - list.element.getBoundingClientRect().top; var offsetBottom = child.getBoundingClientRect().bottom - list.element.getBoundingClientRect().top; @@ -52,7 +53,7 @@ } TestRunner.addResult('Adding 0, 1, 2'); -list.replaceAllItems([0, 1, 2]); +model.replaceAllItems([0, 1, 2]); dumpList(); TestRunner.addResult('Scrolling to 0'); @@ -84,7 +85,7 @@ dumpList(); TestRunner.addResult('Replacing 0 with 5, 6, 7'); -list.replaceItemsInRange(0, 1, [5, 6, 7]); +model.replaceItemsInRange(0, 1, [5, 6, 7]); dumpList(); TestRunner.addResult('ArrowUp'); @@ -92,7 +93,7 @@ dumpList(); TestRunner.addResult('Pushing 10'); -list.pushItem(10); +model.pushItem(10); dumpList(); TestRunner.addResult('Selecting 10'); @@ -100,19 +101,19 @@ dumpList(); TestRunner.addResult('Popping 10'); -list.popItem(); +model.popItem(); dumpList(); TestRunner.addResult('Removing 2'); -list.removeItemAtIndex(4); +model.removeItemAtIndex(4); dumpList(); TestRunner.addResult('Inserting 8'); -list.insertItemAtIndex(1, 8); +model.insertItemAtIndex(1, 8); dumpList(); TestRunner.addResult('Replacing with 0...20'); -list.replaceAllItems([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); +model.replaceAllItems([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); dumpList(); TestRunner.addResult('Resizing'); @@ -146,11 +147,11 @@ dumpList(); TestRunner.addResult('Replacing 7 with 27'); -list.replaceItemsInRange(7, 8, [27]); +model.replaceItemsInRange(7, 8, [27]); dumpList(); TestRunner.addResult('Replacing 18, 19 with 28, 29'); -list.replaceItemsInRange(18, 20, [28, 29]); +model.replaceItemsInRange(18, 20, [28, 29]); dumpList(); TestRunner.addResult('PageDown'); @@ -158,7 +159,7 @@ dumpList(); TestRunner.addResult('Replacing 1, 2, 3 with [31-43]'); -list.replaceItemsInRange(1, 4, [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]); +model.replaceItemsInRange(1, 4, [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]); dumpList(); TestRunner.addResult('Scrolling to 13 (center)'); @@ -194,11 +195,24 @@ dumpList(); TestRunner.addResult('Replacing all but 29 with []'); -list.replaceItemsInRange(0, 29, []); +model.replaceItemsInRange(0, 29, []); dumpList(); TestRunner.addResult('ArrowDown'); list._onKeyDown(TestRunner.createKeyEvent('ArrowDown')); dumpList(); +var newModel = new UI.ListModel([5, 6, 7]); +TestRunner.addResult('Replacing model with [5-7]'); +list.setModel(newModel); +dumpList(); + +TestRunner.addResult('Pushing 8'); +newModel.pushItem(8); +dumpList(); + +TestRunner.addResult('Pushing 9 to old model'); +model.pushItem(9); +dumpList(); + TestRunner.completeTest();
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-non-viewport.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-non-viewport.js index b525b1c..fe6e78e 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-non-viewport.js +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-non-viewport.js
@@ -31,13 +31,14 @@ } var delegate = new Delegate(); -var list = new UI.ListControl(delegate, UI.ListMode.NonViewport); +var model = new UI.ListModel(); +var list = new UI.ListControl(model, delegate, UI.ListMode.NonViewport); UI.inspectorView.element.appendChild(list.element); function dumpList() { var height = list.element.offsetHeight; - TestRunner.addResult(`----list[length=${list.length()}][height=${height}]----`); + TestRunner.addResult(`----list[length=${model.length()}][height=${height}]----`); for (var child of list.element.children) { var offsetTop = child.getBoundingClientRect().top - list.element.getBoundingClientRect().top; var offsetBottom = child.getBoundingClientRect().bottom - list.element.getBoundingClientRect().top; @@ -51,7 +52,7 @@ } TestRunner.addResult('Adding 0, 1, 2'); -list.replaceAllItems([0, 1, 2]); +model.replaceAllItems([0, 1, 2]); dumpList(); TestRunner.addResult('Scrolling to 0'); @@ -63,7 +64,7 @@ dumpList(); TestRunner.addResult('Adding 3-20'); -list.replaceItemsInRange(3, 3, [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); +model.replaceItemsInRange(3, 3, [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); dumpList(); TestRunner.addResult('Scrolling to 19'); @@ -75,7 +76,7 @@ dumpList(); TestRunner.addResult('Replacing 0, 1 with 25-36'); -list.replaceItemsInRange(0, 2, [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36]); +model.replaceItemsInRange(0, 2, [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36]); dumpList(); TestRunner.addResult('Scrolling to 18'); @@ -83,11 +84,11 @@ dumpList(); TestRunner.addResult('Replacing 25-36 with 0-1'); -list.replaceItemsInRange(0, 12, [0, 1]); +model.replaceItemsInRange(0, 12, [0, 1]); dumpList(); TestRunner.addResult('Replacing 16-18 with 45'); -list.replaceItemsInRange(16, 19, [45]); +model.replaceItemsInRange(16, 19, [45]); dumpList(); TestRunner.addResult('Scrolling to 4'); @@ -95,7 +96,7 @@ dumpList(); TestRunner.addResult('Replacing 45 with 16-18'); -list.replaceItemsInRange(16, 17, [16, 17, 18]); +model.replaceItemsInRange(16, 17, [16, 17, 18]); dumpList(); TestRunner.completeTest();
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-various-height.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-various-height.js index 85d24f25..763d01ef 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-various-height.js +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-control-various-height.js
@@ -30,14 +30,15 @@ } var delegate = new Delegate(); -var list = new UI.ListControl(delegate, UI.ListMode.VariousHeightItems); +var model = new UI.ListModel(); +var list = new UI.ListControl(model, delegate, UI.ListMode.VariousHeightItems); list.element.style.height = '73px'; UI.inspectorView.element.appendChild(list.element); function dumpList() { var height = list.element.offsetHeight; - TestRunner.addResult(`----list[length=${list.length()}][height=${height}]----`); + TestRunner.addResult(`----list[length=${model.length()}][height=${height}]----`); for (var child of list.element.children) { var offsetTop = child.getBoundingClientRect().top - list.element.getBoundingClientRect().top; var offsetBottom = child.getBoundingClientRect().bottom - list.element.getBoundingClientRect().top; @@ -52,7 +53,7 @@ } TestRunner.addResult('Adding 0, 1, 2'); -list.replaceAllItems([0, 1, 2]); +model.replaceAllItems([0, 1, 2]); dumpList(); TestRunner.addResult('Scrolling to 0'); @@ -64,7 +65,7 @@ dumpList(); TestRunner.addResult('Adding 3-20'); -list.replaceItemsInRange(3, 3, [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); +model.replaceItemsInRange(3, 3, [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); dumpList(); TestRunner.addResult('Scrolling to 11'); @@ -84,7 +85,7 @@ dumpList(); TestRunner.addResult('Replacing 0, 1 with 25-36'); -list.replaceItemsInRange(0, 2, [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36]); +model.replaceItemsInRange(0, 2, [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36]); dumpList(); TestRunner.addResult('Scrolling to 18'); @@ -92,11 +93,11 @@ dumpList(); TestRunner.addResult('Replacing 25-36 with 0-1'); -list.replaceItemsInRange(0, 12, [0, 1]); +model.replaceItemsInRange(0, 12, [0, 1]); dumpList(); TestRunner.addResult('Replacing 16-18 with 45'); -list.replaceItemsInRange(16, 19, [45]); +model.replaceItemsInRange(16, 19, [45]); dumpList(); TestRunner.addResult('Scrolling to 4'); @@ -104,7 +105,7 @@ dumpList(); TestRunner.addResult('Replacing 45 with 16-18'); -list.replaceItemsInRange(16, 17, [16, 17, 18]); +model.replaceItemsInRange(16, 17, [16, 17, 18]); dumpList(); TestRunner.addResult('Resizing');
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-model-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-model-expected.txt new file mode 100644 index 0000000..a078ea0c --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-model-expected.txt
@@ -0,0 +1,46 @@ +Test ListModel API. +Adding 0, 1, 2 +Replaced [] at index 0 with [0, 1, 2] +Resulting list: [0, 1, 2] + +Replacing 0 with 5, 6, 7 +Replaced [0] at index 0 with [5, 6, 7] +Resulting list: [5, 6, 7, 1, 2] + +Pushing 10 +Replaced [] at index 5 with [10] +Resulting list: [5, 6, 7, 1, 2, 10] + +Popping 10 +Replaced [10] at index 5 with [] +Resulting list: [5, 6, 7, 1, 2] + +Removing 2 +Replaced [2] at index 4 with [] +Resulting list: [5, 6, 7, 1] + +Inserting 8 +Replaced [] at index 1 with [8] +Resulting list: [5, 8, 6, 7, 1] + +Replacing with 0...20 +Replaced [5, 8, 6, 7, 1] at index 0 with [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] +Resulting list: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + +Replacing 7 with 27 +Replaced [7] at index 7 with [27] +Resulting list: [0, 1, 2, 3, 4, 5, 6, 27, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + +Replacing 18, 19 with 28, 29 +Replaced [18, 19] at index 18 with [28, 29] +Resulting list: [0, 1, 2, 3, 4, 5, 6, 27, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 28, 29] + +Replacing 1, 2, 3 with [31-43] +Replaced [1, 2, 3] at index 1 with [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43] +Resulting list: [0, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 4, 5, 6, 27, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 28, 29] + +Replacing all but 29 with [] +Replaced [0, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 4, 5, 6, 27, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 28] at index 0 with [] +Resulting list: [29] + +
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-model.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-model.js new file mode 100644 index 0000000..9fbf6c8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/list-model.js
@@ -0,0 +1,50 @@ +TestRunner.addResult('Test ListModel API.'); + +var list = new UI.ListModel(); +list.addEventListener(UI.ListModel.Events.ItemsReplaced, event => { + var data = event.data; + var inserted = []; + for (var index = data.index; index < data.index + data.inserted; index++) + inserted.push(list.itemAtIndex(index)); + TestRunner.addResult(`Replaced [${data.removed.join(', ')}] at index ${data.index} with [${inserted.join(', ')}]`); + var all = []; + for (var index = 0; index < list.length(); index++) + all.push(list.itemAtIndex(index)); + TestRunner.addResult(`Resulting list: [${all.join(', ')}]`); + TestRunner.addResult(''); +}); + +TestRunner.addResult('Adding 0, 1, 2'); +list.replaceAllItems([0, 1, 2]); + +TestRunner.addResult('Replacing 0 with 5, 6, 7'); +list.replaceItemsInRange(0, 1, [5, 6, 7]); + +TestRunner.addResult('Pushing 10'); +list.pushItem(10); + +TestRunner.addResult('Popping 10'); +list.popItem(); + +TestRunner.addResult('Removing 2'); +list.removeItemAtIndex(4); + +TestRunner.addResult('Inserting 8'); +list.insertItemAtIndex(1, 8); + +TestRunner.addResult('Replacing with 0...20'); +list.replaceAllItems([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); + +TestRunner.addResult('Replacing 7 with 27'); +list.replaceItemsInRange(7, 8, [27]); + +TestRunner.addResult('Replacing 18, 19 with 28, 29'); +list.replaceItemsInRange(18, 20, [28, 29]); + +TestRunner.addResult('Replacing 1, 2, 3 with [31-43]'); +list.replaceItemsInRange(1, 4, [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]); + +TestRunner.addResult('Replacing all but 29 with []'); +list.replaceItemsInRange(0, 29, []); + +TestRunner.completeTest();
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js index d23a82e..9210a99a 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js +++ b/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js
@@ -395,7 +395,7 @@ InspectorTest.changeExecutionContext = function(namePrefix) { var selector = Console.ConsoleView.instance()._consoleContextSelector; - for (var executionContext of selector._list._items) { + for (var executionContext of selector._items._items) { if (selector._titleFor(executionContext).startsWith(namePrefix)) { UI.context.setFlavor(SDK.ExecutionContext, executionContext); return;
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-context-selector.html b/third_party/WebKit/LayoutTests/inspector/console/console-context-selector.html index 864a7e4..f94132f 100644 --- a/third_party/WebKit/LayoutTests/inspector/console/console-context-selector.html +++ b/third_party/WebKit/LayoutTests/inspector/console/console-context-selector.html
@@ -98,7 +98,7 @@ var consoleView = Console.ConsoleView.instance(); var selector = consoleView._consoleContextSelector; InspectorTest.addResult('Console context selector:'); - for (var executionContext of selector._list._items) { + for (var executionContext of selector._items._items) { var selected = UI.context.flavor(SDK.ExecutionContext) === executionContext; var text = '____'.repeat(selector._depthFor(executionContext)) + selector._titleFor(executionContext); var disabled = !selector.isItemSelectable(executionContext);
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-change-variable.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-change-variable.html index 3910f18..4cb7fb0 100644 --- a/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-change-variable.html +++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-change-variable.html
@@ -45,7 +45,7 @@ function step2(callFrames) { var pane = self.runtime.sharedInstance(Sources.CallStackSidebarPane); - pane._list.selectItem(pane._list.itemAtIndex(1)); + pane._list.selectItem(pane._list._model.itemAtIndex(1)); InspectorTest.deprecatedRunAfterPendingDispatches(step3); }
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-eval-on-call-frame-inside-iframe.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-eval-on-call-frame-inside-iframe.html index ba6754f..e4ced82 100644 --- a/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-eval-on-call-frame-inside-iframe.html +++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-eval-on-call-frame-inside-iframe.html
@@ -77,7 +77,7 @@ function step3() { var pane = self.runtime.sharedInstance(Sources.CallStackSidebarPane); - pane._list.selectItem(pane._list.itemAtIndex(1)); + pane._list.selectItem(pane._list._model.itemAtIndex(1)); InspectorTest.deprecatedRunAfterPendingDispatches(step4); }
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-eval-while-paused.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-eval-while-paused.html index 7be8554..a4702a5 100644 --- a/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-eval-while-paused.html +++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-pause/debugger-eval-while-paused.html
@@ -37,7 +37,7 @@ { InspectorTest.addResult("Evaluated script on the top frame: " + result); var pane = self.runtime.sharedInstance(Sources.CallStackSidebarPane); - pane._list.selectItem(pane._list.itemAtIndex(1)); + pane._list.selectItem(pane._list._model.itemAtIndex(1)); InspectorTest.deprecatedRunAfterPendingDispatches(step4); }
diff --git a/third_party/WebKit/Source/core/editing/BUILD.gn b/third_party/WebKit/Source/core/editing/BUILD.gn index f6cb0331..c69f5a0 100644 --- a/third_party/WebKit/Source/core/editing/BUILD.gn +++ b/third_party/WebKit/Source/core/editing/BUILD.gn
@@ -216,6 +216,7 @@ "markers/SpellingMarker.h", "markers/SpellingMarkerListImpl.cpp", "markers/SpellingMarkerListImpl.h", + "markers/TextMatchMarker.cpp", "markers/TextMatchMarker.h", "markers/TextMatchMarkerListImpl.cpp", "markers/TextMatchMarkerListImpl.h",
diff --git a/third_party/WebKit/Source/core/editing/markers/CompositionMarker.cpp b/third_party/WebKit/Source/core/editing/markers/CompositionMarker.cpp index f445f50..f24217d 100644 --- a/third_party/WebKit/Source/core/editing/markers/CompositionMarker.cpp +++ b/third_party/WebKit/Source/core/editing/markers/CompositionMarker.cpp
@@ -11,11 +11,15 @@ Color underline_color, bool thick, Color background_color) - : DocumentMarker(DocumentMarker::kComposition, start_offset, end_offset), + : DocumentMarker(start_offset, end_offset), underline_color_(underline_color), background_color_(background_color), thick_(thick) {} +DocumentMarker::MarkerType CompositionMarker::GetType() const { + return DocumentMarker::kComposition; +} + Color CompositionMarker::UnderlineColor() const { return underline_color_; }
diff --git a/third_party/WebKit/Source/core/editing/markers/CompositionMarker.h b/third_party/WebKit/Source/core/editing/markers/CompositionMarker.h index 0b02e62..fa476cb 100644 --- a/third_party/WebKit/Source/core/editing/markers/CompositionMarker.h +++ b/third_party/WebKit/Source/core/editing/markers/CompositionMarker.h
@@ -22,6 +22,10 @@ bool thick, Color background_color); + // DocumentMarker implementations + MarkerType GetType() const final; + + // CompositionMarker-specific Color UnderlineColor() const; bool Thick() const; Color BackgroundColor() const;
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp index 7ecc322..3a43d7aa 100644 --- a/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp +++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp
@@ -37,10 +37,8 @@ DocumentMarker::~DocumentMarker() = default; -DocumentMarker::DocumentMarker(MarkerType type, - unsigned start_offset, - unsigned end_offset) - : type_(type), start_offset_(start_offset), end_offset_(end_offset) { +DocumentMarker::DocumentMarker(unsigned start_offset, unsigned end_offset) + : start_offset_(start_offset), end_offset_(end_offset) { DCHECK_LT(start_offset_, end_offset_); }
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarker.h b/third_party/WebKit/Source/core/editing/markers/DocumentMarker.h index 0ab8fa70..7440dae 100644 --- a/third_party/WebKit/Source/core/editing/markers/DocumentMarker.h +++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarker.h
@@ -129,7 +129,7 @@ virtual ~DocumentMarker(); - MarkerType GetType() const { return type_; } + virtual MarkerType GetType() const = 0; unsigned StartOffset() const { return start_offset_; } unsigned EndOffset() const { return end_offset_; } @@ -151,10 +151,9 @@ DEFINE_INLINE_VIRTUAL_TRACE() {} protected: - DocumentMarker(MarkerType, unsigned start_offset, unsigned end_offset); + DocumentMarker(unsigned start_offset, unsigned end_offset); private: - const MarkerType type_; unsigned start_offset_; unsigned end_offset_;
diff --git a/third_party/WebKit/Source/core/editing/markers/GrammarMarker.cpp b/third_party/WebKit/Source/core/editing/markers/GrammarMarker.cpp index 32cfbb6..eadeee2f 100644 --- a/third_party/WebKit/Source/core/editing/markers/GrammarMarker.cpp +++ b/third_party/WebKit/Source/core/editing/markers/GrammarMarker.cpp
@@ -9,11 +9,12 @@ GrammarMarker::GrammarMarker(unsigned start_offset, unsigned end_offset, const String& description) - : SpellCheckMarker(DocumentMarker::kGrammar, - start_offset, - end_offset, - description) { + : SpellCheckMarker(start_offset, end_offset, description) { DCHECK_LT(start_offset, end_offset); } +DocumentMarker::MarkerType GrammarMarker::GetType() const { + return DocumentMarker::kGrammar; +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/markers/GrammarMarker.h b/third_party/WebKit/Source/core/editing/markers/GrammarMarker.h index 6bf39c5..17c526a 100644 --- a/third_party/WebKit/Source/core/editing/markers/GrammarMarker.h +++ b/third_party/WebKit/Source/core/editing/markers/GrammarMarker.h
@@ -20,6 +20,9 @@ const String& description); private: + // DocumentMarker implementations + MarkerType GetType() const final; + DISALLOW_COPY_AND_ASSIGN(GrammarMarker); };
diff --git a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarker.cpp b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarker.cpp index 04952394..3a1cb2d 100644 --- a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarker.cpp +++ b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarker.cpp
@@ -6,12 +6,10 @@ namespace blink { -SpellCheckMarker::SpellCheckMarker(DocumentMarker::MarkerType type, - unsigned start_offset, +SpellCheckMarker::SpellCheckMarker(unsigned start_offset, unsigned end_offset, const String& description) - : DocumentMarker(type, start_offset, end_offset), - description_(description) { + : DocumentMarker(start_offset, end_offset), description_(description) { DCHECK_LT(start_offset, end_offset); }
diff --git a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarker.h b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarker.h index 053f7c5..07ca3e6 100644 --- a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarker.h +++ b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarker.h
@@ -15,8 +15,7 @@ // or grammar error. class CORE_EXPORT SpellCheckMarker : public DocumentMarker { public: - SpellCheckMarker(DocumentMarker::MarkerType, - unsigned start_offset, + SpellCheckMarker(unsigned start_offset, unsigned end_offset, const String& description);
diff --git a/third_party/WebKit/Source/core/editing/markers/SpellingMarker.cpp b/third_party/WebKit/Source/core/editing/markers/SpellingMarker.cpp index fa0b1b1b..0d7d1100 100644 --- a/third_party/WebKit/Source/core/editing/markers/SpellingMarker.cpp +++ b/third_party/WebKit/Source/core/editing/markers/SpellingMarker.cpp
@@ -9,11 +9,12 @@ SpellingMarker::SpellingMarker(unsigned start_offset, unsigned end_offset, const String& description) - : SpellCheckMarker(DocumentMarker::kSpelling, - start_offset, - end_offset, - description) { + : SpellCheckMarker(start_offset, end_offset, description) { DCHECK_LT(start_offset, end_offset); } +DocumentMarker::MarkerType SpellingMarker::GetType() const { + return DocumentMarker::kSpelling; +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/markers/SpellingMarker.h b/third_party/WebKit/Source/core/editing/markers/SpellingMarker.h index d4dac785..f657926 100644 --- a/third_party/WebKit/Source/core/editing/markers/SpellingMarker.h +++ b/third_party/WebKit/Source/core/editing/markers/SpellingMarker.h
@@ -20,6 +20,9 @@ const String& description); private: + // DocumentMarker implementations + MarkerType GetType() const final; + DISALLOW_COPY_AND_ASSIGN(SpellingMarker); };
diff --git a/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.cpp b/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.cpp new file mode 100644 index 0000000..096b8a5e --- /dev/null +++ b/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.cpp
@@ -0,0 +1,13 @@ +// 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 "core/editing/markers/TextMatchMarker.h" + +namespace blink { + +DocumentMarker::MarkerType TextMatchMarker::GetType() const { + return DocumentMarker::kTextMatch; +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h b/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h index 0b712782..e4c9fc2 100644 --- a/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h +++ b/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h
@@ -36,7 +36,7 @@ // markers. We store whether or not the match is active, a LayoutRect used for // rendering the marker, and whether or not the LayoutRect is currently // up-to-date. -class TextMatchMarker final : public DocumentMarker { +class CORE_EXPORT TextMatchMarker final : public DocumentMarker { private: enum class State { kInvalid, kValidNull, kValidNotNull }; @@ -46,11 +46,14 @@ TextMatchMarker(unsigned start_offset, unsigned end_offset, MatchStatus status) - : DocumentMarker(DocumentMarker::kTextMatch, start_offset, end_offset), - match_status_(status) { + : DocumentMarker(start_offset, end_offset), match_status_(status) { layout_state_ = State::kInvalid; } + // DocumentMarker implementations + MarkerType GetType() const final; + + // TextMatchMarker-specific bool IsActiveMatch() const { return match_status_ == MatchStatus::kActive; } void SetIsActiveMatch(bool active) { match_status_ = active ? MatchStatus::kActive : MatchStatus::kInactive;
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn index 335b3f4..47296f2 100644 --- a/third_party/WebKit/Source/devtools/BUILD.gn +++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -680,6 +680,7 @@ "front_end/ui/inspectorViewTabbedPane.css", "front_end/ui/KeyboardShortcut.js", "front_end/ui/ListControl.js", + "front_end/ui/ListModel.js", "front_end/ui/listWidget.css", "front_end/ui/ListWidget.js", "front_end/ui/module.json",
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/whatsnew.png b/third_party/WebKit/Source/devtools/front_end/Images/whatsnew.png index 9dbe93d..9bb608b8 100644 --- a/third_party/WebKit/Source/devtools/front_end/Images/whatsnew.png +++ b/third_party/WebKit/Source/devtools/front_end/Images/whatsnew.png Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Tests.js b/third_party/WebKit/Source/devtools/front_end/Tests.js index 5ce03ea..c09309dc 100644 --- a/third_party/WebKit/Source/devtools/front_end/Tests.js +++ b/third_party/WebKit/Source/devtools/front_end/Tests.js
@@ -847,10 +847,10 @@ function onExecutionContexts() { var consoleView = Console.ConsoleView.instance(); - var items = consoleView._consoleContextSelector._list._items; + var items = consoleView._consoleContextSelector._items; var values = []; - for (var i = 0; i < items.length; ++i) - values.push(consoleView._consoleContextSelector._titleFor(items[i])); + for (var i = 0; i < items.length(); ++i) + values.push(consoleView._consoleContextSelector._titleFor(items.itemAtIndex(i))); test.assertEquals('top', values[0]); test.assertEquals('Simple content script', values[1]); test.releaseControl();
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleContextSelector.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleContextSelector.js index f6e003f..665bdda 100644 --- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleContextSelector.js +++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleContextSelector.js
@@ -22,7 +22,10 @@ this._glassPane.setAnchorBehavior(UI.GlassPane.AnchorBehavior.PreferBottom); this._glassPane.setOutsideClickCallback(this._hide.bind(this)); this._glassPane.setPointerEventsBehavior(UI.GlassPane.PointerEventsBehavior.BlockedByGlassPane); - this._list = new UI.ListControl(this, UI.ListMode.EqualHeightItems); + /** @type {!UI.ListModel<!SDK.ExecutionContext>} */ + this._items = new UI.ListModel(); + /** @type {!UI.ListControl<!SDK.ExecutionContext>} */ + this._list = new UI.ListControl(this._items, this, UI.ListMode.EqualHeightItems); this._list.element.classList.add('context-list'); this._rowHeight = 36; UI.createShadowRootWithCoreStyles(this._glassPane.contentElement, 'console/consoleContextSelector.css') @@ -96,7 +99,7 @@ } _updateGlasspaneSize() { - var maxHeight = this._rowHeight * (Math.min(this._list.length(), 9)); + var maxHeight = this._rowHeight * (Math.min(this._items.length(), 9)); this._glassPane.setMaxContentSize(new UI.Size(315, maxHeight)); this._list.viewportResized(); } @@ -127,7 +130,7 @@ var currentExecutionContext = this._list.selectedItem(); if (!currentExecutionContext) break; - var nextExecutionContext = this._list.itemAtIndex(this._list.selectedIndex() + 1); + var nextExecutionContext = this._items.itemAtIndex(this._list.selectedIndex() + 1); if (nextExecutionContext && this._depthFor(currentExecutionContext) < this._depthFor(nextExecutionContext)) handled = this._list.selectNextItem(false, false); break; @@ -137,9 +140,9 @@ break; var depth = this._depthFor(currentExecutionContext); for (var i = this._list.selectedIndex() - 1; i >= 0; i--) { - if (this._depthFor(this._list.itemAtIndex(i)) < depth) { + if (this._depthFor(this._items.itemAtIndex(i)) < depth) { handled = true; - this._list.selectItem(this._list.itemAtIndex(i), false); + this._list.selectItem(this._items.itemAtIndex(i), false); break; } } @@ -151,18 +154,18 @@ handled = this._list.selectItemNextPage(false); break; case 'Home': - for (var i = 0; i < this._list.length(); i++) { - if (this.isItemSelectable(this._list.itemAtIndex(i))) { - this._list.selectItem(this._list.itemAtIndex(i)); + for (var i = 0; i < this._items.length(); i++) { + if (this.isItemSelectable(this._items.itemAtIndex(i))) { + this._list.selectItem(this._items.itemAtIndex(i)); handled = true; break; } } break; case 'End': - for (var i = this._list.length() - 1; i >= 0; i--) { - if (this.isItemSelectable(this._list.itemAtIndex(i))) { - this._list.selectItem(this._list.itemAtIndex(i)); + for (var i = this._items.length() - 1; i >= 0; i--) { + if (this.isItemSelectable(this._items.itemAtIndex(i))) { + this._list.selectItem(this._items.itemAtIndex(i)); handled = true; break; } @@ -192,8 +195,8 @@ if (event.key.length === 1) { var selectedIndex = this._list.selectedIndex(); var letter = event.key.toUpperCase(); - for (var i = 0; i < this._list.length(); i++) { - var context = this._list.itemAtIndex((selectedIndex + i + 1) % this._list.length()); + for (var i = 0; i < this._items.length(); i++) { + var context = this._items.itemAtIndex((selectedIndex + i + 1) % this._items.length()); if (this._titleFor(context).toUpperCase().startsWith(letter)) { this._list.selectItem(context); break; @@ -296,7 +299,7 @@ if (!executionContext.target().hasJSCapability()) return; - this._list.insertItemWithComparator(executionContext, executionContext.runtimeModel.executionContextComparator()); + this._items.insertItemWithComparator(executionContext, executionContext.runtimeModel.executionContextComparator()); if (executionContext === UI.context.flavor(SDK.ExecutionContext)) this._updateSelectionTitle(); @@ -317,7 +320,7 @@ */ _onExecutionContextChanged(event) { var executionContext = /** @type {!SDK.ExecutionContext} */ (event.data); - if (this._list.indexOfItem(executionContext) === -1) + if (this._items.indexOfItem(executionContext) === -1) return; this._executionContextDestroyed(executionContext); this._executionContextCreated(executionContext); @@ -328,10 +331,10 @@ * @param {!SDK.ExecutionContext} executionContext */ _executionContextDestroyed(executionContext) { - if (this._list.indexOfItem(executionContext) === -1) + if (this._items.indexOfItem(executionContext) === -1) return; this._disposeExecutionContextBadge(executionContext); - this._list.removeItem(executionContext); + this._items.removeItem(executionContext); this._updateGlasspaneSize(); } @@ -349,7 +352,7 @@ */ _executionContextChangedExternally(event) { var executionContext = /** @type {?SDK.ExecutionContext} */ (event.data); - if (!executionContext || this._list.indexOfItem(executionContext) === -1) + if (!executionContext || this._items.indexOfItem(executionContext) === -1) return; this._list.selectItem(executionContext); this._updateSelectedContext(); @@ -383,8 +386,8 @@ * @return {boolean} */ _hasTopContext() { - for (var i = 0; i < this._list.length(); i++) { - if (this._isTopContext(this._list.itemAtIndex(i))) + for (var i = 0; i < this._items.length(); i++) { + if (this._isTopContext(this._items.itemAtIndex(i))) return true; } return false; @@ -403,9 +406,9 @@ * @param {!SDK.RuntimeModel} runtimeModel */ modelRemoved(runtimeModel) { - for (var i = 0; i < this._list.length(); i++) { - if (this._list.itemAtIndex(i).runtimeModel === runtimeModel) - this._executionContextDestroyed(this._list.itemAtIndex(i)); + for (var i = 0; i < this._items.length(); i++) { + if (this._items.itemAtIndex(i).runtimeModel === runtimeModel) + this._executionContextDestroyed(this._items.itemAtIndex(i)); } } @@ -520,10 +523,11 @@ */ _callFrameSelectedInModel(event) { var debuggerModel = /** @type {!SDK.DebuggerModel} */ (event.data); - for (var i = 0; i < this._list.length(); i++) { - if (this._list.itemAtIndex(i).debuggerModel === debuggerModel) { - this._disposeExecutionContextBadge(this._list.itemAtIndex(i)); - this._list.refreshItemsInRange(i, i + 1); + for (var i = 0; i < this._items.length(); i++) { + var executionContext = this._items.itemAtIndex(i); + if (executionContext.debuggerModel === debuggerModel) { + this._disposeExecutionContextBadge(executionContext); + this._list.refreshItem(executionContext); } } } @@ -533,10 +537,11 @@ */ _frameNavigated(event) { var frameId = event.data.id; - for (var i = 0; i < this._list.length(); i++) { - if (frameId === this._list.itemAtIndex(i).frameId) { - this._disposeExecutionContextBadge(this._list.itemAtIndex(i)); - this._list.refreshItemsInRange(i, i + 1); + for (var i = 0; i < this._items.length(); i++) { + var executionContext = this._items.itemAtIndex(i); + if (frameId === executionContext.frameId) { + this._disposeExecutionContextBadge(executionContext); + this._list.refreshItem(executionContext); } } }
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js index e038414..9b68900 100644 --- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js +++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
@@ -575,9 +575,9 @@ contextMenu.appendItem(Common.UIString('Save as...'), this._saveConsole.bind(this)); var request = consoleMessage ? consoleMessage.request : null; - if (request && request.resourceType() === Common.resourceTypes.XHR) { + if (request && SDK.NetworkManager.canReplayRequest(request)) { contextMenu.appendSeparator(); - contextMenu.appendItem(Common.UIString('Replay XHR'), request.replayXHR.bind(request)); + contextMenu.appendItem(Common.UIString('Replay XHR'), SDK.NetworkManager.replayRequest.bind(null, request)); } contextMenu.show();
diff --git a/third_party/WebKit/Source/devtools/front_end/devices/devicesView.css b/third_party/WebKit/Source/devtools/front_end/devices/devicesView.css index 1fec4e6..8cad9b6f 100644 --- a/third_party/WebKit/Source/devtools/front_end/devices/devicesView.css +++ b/third_party/WebKit/Source/devtools/front_end/devices/devicesView.css
@@ -137,15 +137,18 @@ flex: auto 1 1; } -.port-forwarding-value { +.list-item .port-forwarding-value { white-space: nowrap; text-overflow: ellipsis; -webkit-user-select: none; color: #222; - flex: 3 1 0; overflow: hidden; } +.port-forwarding-value { + flex: 3 1 0; +} + .port-forwarding-value.port-forwarding-port { flex: 1 1 0; } @@ -224,15 +227,18 @@ flex: auto 1 1; } -.network-discovery-value { +.list-item .network-discovery-value { white-space: nowrap; text-overflow: ellipsis; -webkit-user-select: none; color: #222; - flex: 3 1 0; overflow: hidden; } +.network-discovery-value { + flex: 3 1 0; +} + .network-discovery-edit-row { flex: none; display: flex;
diff --git a/third_party/WebKit/Source/devtools/front_end/emulation/SensorsView.js b/third_party/WebKit/Source/devtools/front_end/emulation/SensorsView.js index 79317b6..294c0ba 100644 --- a/third_party/WebKit/Source/devtools/front_end/emulation/SensorsView.js +++ b/third_party/WebKit/Source/devtools/front_end/emulation/SensorsView.js
@@ -77,6 +77,7 @@ var longitudeGroup = this._fieldsetElement.createChild('div', 'latlong-group'); this._latitudeInput = latitudeGroup.createChild('input'); + this._latitudeInput.setAttribute('step', 'any'); this._latitudeInput.setAttribute('type', 'number'); this._latitudeInput.value = 0; this._latitudeSetter = UI.bindInput( @@ -85,6 +86,7 @@ this._latitudeSetter(String(geolocation.latitude)); this._longitudeInput = longitudeGroup.createChild('input'); + this._longitudeInput.setAttribute('step', 'any'); this._longitudeInput.setAttribute('type', 'number'); this._longitudeInput.value = 0; this._longitudeSetter = UI.bindInput( @@ -311,14 +313,17 @@ var cellElement = fieldsetElement.createChild('td', 'orientation-inputs-cell'); this._alphaElement = createElement('input'); + this._alphaElement.setAttribute('step', 'any'); this._alphaSetter = this._createAxisInput(cellElement, this._alphaElement, Common.UIString('\u03B1 (alpha)')); this._alphaSetter(String(deviceOrientation.alpha)); this._betaElement = createElement('input'); + this._betaElement.setAttribute('step', 'any'); this._betaSetter = this._createAxisInput(cellElement, this._betaElement, Common.UIString('\u03B2 (beta)')); this._betaSetter(String(deviceOrientation.beta)); this._gammaElement = createElement('input'); + this._gammaElement.setAttribute('step', 'any'); this._gammaSetter = this._createAxisInput(cellElement, this._gammaElement, Common.UIString('\u03B3 (gamma)')); this._gammaSetter(String(deviceOrientation.gamma));
diff --git a/third_party/WebKit/Source/devtools/front_end/emulation/devicesSettingsTab.css b/third_party/WebKit/Source/devtools/front_end/emulation/devicesSettingsTab.css index a731ee9f..0f4b71d 100644 --- a/third_party/WebKit/Source/devtools/front_end/emulation/devicesSettingsTab.css +++ b/third_party/WebKit/Source/devtools/front_end/emulation/devicesSettingsTab.css
@@ -74,7 +74,6 @@ height: 22px; border: 1px solid rgb(213, 213, 213); border-radius: 2px; - color: #444444; padding: 3px; }
diff --git a/third_party/WebKit/Source/devtools/front_end/emulation/sensors.css b/third_party/WebKit/Source/devtools/front_end/emulation/sensors.css index 8fec49e..6288dfd 100644 --- a/third_party/WebKit/Source/devtools/front_end/emulation/sensors.css +++ b/third_party/WebKit/Source/devtools/front_end/emulation/sensors.css
@@ -28,11 +28,6 @@ .sensors-view select { border: 1px solid #bfbfbf; border-radius: 2px; - box-sizing: border-box; - color: #444; - font: inherit; - border-width: 1px; - text-align: left; } .sensors-view input {
diff --git a/third_party/WebKit/Source/devtools/front_end/help/ReleaseNoteText.js b/third_party/WebKit/Source/devtools/front_end/help/ReleaseNoteText.js index cac4efb..2cb717fe 100644 --- a/third_party/WebKit/Source/devtools/front_end/help/ReleaseNoteText.js +++ b/third_party/WebKit/Source/devtools/front_end/help/ReleaseNoteText.js
@@ -6,11 +6,52 @@ // be shown in Canary (e.g. make sure the release notes are accurate). // https://github.com/ChromeDevTools/devtools-frontend/wiki/Release-Notes +var continueToHereShortcut = Host.isMac() ? 'Command' : 'Control'; var commandMenuShortcut = Host.isMac() ? 'Command + Shift + P' : 'Control + Shift + P'; /** @type {!Array<!Help.ReleaseNote>} */ Help.releaseNoteText = [ { + version: 3, + header: 'Highlights from the Chrome 60 update', + highlights: [ + { + title: 'New Audits panel, powered by Lighthouse', + subtitle: + 'Find out whether your site qualifies as a Progressive Web App, measure the accessibility and performance of a page, and discover best practices.', + link: 'https://developers.google.com/web/updates/2017/05/devtools-release-notes#lighthouse', + }, + { + title: 'Third-party badges', + subtitle: + 'See what third-party entities are logging to the Console, making network requests, and causing work during performance recordings.', + link: 'https://developers.google.com/web/updates/2017/05/devtools-release-notes#badges', + }, + { + title: 'New "Continue to Here" gesture', + subtitle: 'While paused on a line of code, hold ' + continueToHereShortcut + + ' and then click to continue to another line of code.', + link: 'https://developers.google.com/web/updates/2017/05/devtools-release-notes#continue', + }, + { + title: 'Step into async', + subtitle: 'Predictably step into a promise resolution or other asynchronous code with a single gesture.', + link: 'https://developers.google.com/web/updates/2017/05/devtools-release-notes#step-into-async', + }, + { + title: 'More informative object previews', + subtitle: 'Get a better idea of the contents of objects when logging them to the Console.', + link: 'https://developers.google.com/web/updates/2017/05/devtools-release-notes#object-previews', + }, + { + title: 'Real-time Coverage tab updates', + subtitle: 'See what code is being used in real-time.', + link: 'https://developers.google.com/web/updates/2017/05/devtools-release-notes#coverage', + } + ], + link: 'https://developers.google.com/web/updates/2017/05/devtools-release-notes', + }, + { version: 2, header: 'Highlights from Chrome 59 update', highlights: [
diff --git a/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js b/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js index 35c0f73..9608234 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js +++ b/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js
@@ -153,7 +153,7 @@ var editor = new UI.ListWidget.Editor(); var content = editor.contentElement(); var titles = content.createChild('div', 'blocked-url-edit-row'); - titles.createChild('div', 'blocked-url-edit-value').textContent = + titles.createChild('div').textContent = Common.UIString('Text pattern to block matching requests; use * for wildcard'); var fields = content.createChild('div', 'blocked-url-edit-row'); var urlInput = editor.createInput(
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js index 953a544..16f0bfbb 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js +++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
@@ -1161,6 +1161,12 @@ Common.UIString.capitalize('Unblock ' + croppedDomain), removeBlockedURL.bind(null, domain)); } + if (SDK.NetworkManager.canReplayRequest(request)) { + contextMenu.appendSeparator(); + contextMenu.appendItem(Common.UIString('Replay XHR'), SDK.NetworkManager.replayRequest.bind(null, request)); + contextMenu.appendSeparator(); + } + /** * @param {string} url */ @@ -1180,12 +1186,6 @@ UI.viewManager.showView('network.blocked-urls'); } } - - if (request && request.resourceType() === Common.resourceTypes.XHR) { - contextMenu.appendSeparator(); - contextMenu.appendItem(Common.UIString('Replay XHR'), request.replayXHR.bind(request)); - contextMenu.appendSeparator(); - } } _harRequests() {
diff --git a/third_party/WebKit/Source/devtools/front_end/network/blockedURLsPane.css b/third_party/WebKit/Source/devtools/front_end/network/blockedURLsPane.css index 1ba8d66..e40dafa3 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/blockedURLsPane.css +++ b/third_party/WebKit/Source/devtools/front_end/network/blockedURLsPane.css
@@ -67,12 +67,8 @@ } .blocked-url-edit-value { - white-space: nowrap; - text-overflow: ellipsis; -webkit-user-select: none; - color: #222; flex: 1 1 0px; - overflow: hidden; } .blocked-url-edit-row input {
diff --git a/third_party/WebKit/Source/devtools/front_end/network/networkConfigView.css b/third_party/WebKit/Source/devtools/front_end/network/networkConfigView.css index 5580678..72b179d 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/networkConfigView.css +++ b/third_party/WebKit/Source/devtools/front_end/network/networkConfigView.css
@@ -67,10 +67,6 @@ max-width: 250px; border: 1px solid #bfbfbf; border-radius: 2px; - box-sizing: border-box; - color: #444; - font: inherit; - border-width: 1px; min-height: 2em; padding: 3px; }
diff --git a/third_party/WebKit/Source/devtools/front_end/persistence/editFileSystemView.css b/third_party/WebKit/Source/devtools/front_end/persistence/editFileSystemView.css index 7bf411c..a6de783 100644 --- a/third_party/WebKit/Source/devtools/front_end/persistence/editFileSystemView.css +++ b/third_party/WebKit/Source/devtools/front_end/persistence/editFileSystemView.css
@@ -42,15 +42,17 @@ flex: auto 1 1; } -.file-system-value { +.list-item .file-system-value { white-space: nowrap; text-overflow: ellipsis; -webkit-user-select: none; - color: #222; - flex: 1 1 0px; overflow: hidden; } +.file-system-value { + flex: 1 1 0px; +} + .file-system-separator { flex: 0 0 1px; background-color: rgb(231, 231, 231);
diff --git a/third_party/WebKit/Source/devtools/front_end/persistence/workspaceSettingsTab.css b/third_party/WebKit/Source/devtools/front_end/persistence/workspaceSettingsTab.css index 736c105..a3a121a 100644 --- a/third_party/WebKit/Source/devtools/front_end/persistence/workspaceSettingsTab.css +++ b/third_party/WebKit/Source/devtools/front_end/persistence/workspaceSettingsTab.css
@@ -55,14 +55,6 @@ padding: 0; } -.settings-tab input:not([type]), -.settings-tab input[type="text"] { - border: 1px solid rgb(213, 213, 213); - border-radius: 2px; - color: #444444; - padding: 3px; -} - .settings-tab p { margin: 12px 0; }
diff --git a/third_party/WebKit/Source/devtools/front_end/quick_open/FilteredListWidget.js b/third_party/WebKit/Source/devtools/front_end/quick_open/FilteredListWidget.js index 86152f7..e7deb52 100644 --- a/third_party/WebKit/Source/devtools/front_end/quick_open/FilteredListWidget.js +++ b/third_party/WebKit/Source/devtools/front_end/quick_open/FilteredListWidget.js
@@ -34,8 +34,10 @@ this._progressElement = this._bottomElementsContainer.createChild('div', 'filtered-list-widget-progress'); this._progressBarElement = this._progressElement.createChild('div', 'filtered-list-widget-progress-bar'); + /** @type {!UI.ListModel<number>} */ + this._items = new UI.ListModel(); /** @type {!UI.ListControl<number>} */ - this._list = new UI.ListControl(this, UI.ListMode.EqualHeightItems); + this._list = new UI.ListControl(this._items, this, UI.ListMode.EqualHeightItems); this._itemElementsContainer = this._list.element; this._itemElementsContainer.classList.add('container'); this._bottomElementsContainer.appendChild(this._itemElementsContainer); @@ -150,7 +152,7 @@ } _attachProvider() { - this._list.replaceAllItems([]); + this._items.replaceAllItems([]); this._list.invalidateItemHeight(); if (this._provider) { this._provider.setRefreshCallback(this._itemsLoaded.bind(this, this._provider)); @@ -433,7 +435,7 @@ filteredItems = [].concat(bestItems, overflowItems, filteredItems); this._updateNotFoundMessage(!!filteredItems.length); var oldHeight = this._list.element.offsetHeight; - this._list.replaceAllItems(filteredItems); + this._items.replaceAllItems(filteredItems); if (filteredItems.length) this._list.selectItem(filteredItems[0]); if (this._list.element.offsetHeight !== oldHeight)
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js index 1d0b90c..1e4f6d7 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js
@@ -58,6 +58,22 @@ } /** + * @param {!SDK.NetworkRequest} request + * @return {boolean} + */ + static canReplayRequest(request) { + return request.resourceType() === Common.resourceTypes.XHR; + } + + /** + * @param {!SDK.NetworkRequest} request + */ + static replayRequest(request) { + // TODO(allada) networkAgent() will be removed from NetworkRequest, but in the mean time we extract it from request. + request.networkManager()._networkAgent.replayXHR(request.requestId()); + } + + /** * @param {!SDK.NetworkManager.Conditions} conditions * @return {!Protocol.Network.ConnectionType} * TODO(allada): this belongs to NetworkConditionsSelector, which should hardcode/guess it.
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js index 311f417..12c4ad84 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js
@@ -1100,10 +1100,6 @@ this.dispatchEventToListeners(SDK.NetworkRequest.Events.EventSourceMessageAdded, message); } - replayXHR() { - this._networkManager.target().networkAgent().replayXHR(this._requestId); - } - /** * @return {!SDK.NetworkManager} */
diff --git a/third_party/WebKit/Source/devtools/front_end/settings/frameworkBlackboxSettingsTab.css b/third_party/WebKit/Source/devtools/front_end/settings/frameworkBlackboxSettingsTab.css index e7bb786b..00ca7ba 100644 --- a/third_party/WebKit/Source/devtools/front_end/settings/frameworkBlackboxSettingsTab.css +++ b/third_party/WebKit/Source/devtools/front_end/settings/frameworkBlackboxSettingsTab.css
@@ -51,15 +51,18 @@ flex: auto 1 1; } -.blackbox-pattern { + .blackbox-list-item .blackbox-pattern { white-space: nowrap; text-overflow: ellipsis; -webkit-user-select: none; color: #222; - flex: auto; overflow: hidden; } +.blackbox-pattern { + flex: auto; +} + .blackbox-list-item.blackbox-disabled .blackbox-pattern { text-decoration: line-through; }
diff --git a/third_party/WebKit/Source/devtools/front_end/settings/settingsScreen.css b/third_party/WebKit/Source/devtools/front_end/settings/settingsScreen.css index 6387a4c..707b6383 100644 --- a/third_party/WebKit/Source/devtools/front_end/settings/settingsScreen.css +++ b/third_party/WebKit/Source/devtools/front_end/settings/settingsScreen.css
@@ -164,7 +164,6 @@ .settings-tab input[type="text"] { border: 1px solid rgb(213, 213, 213); border-radius: 2px; - color: #444444; padding: 3px; }
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js index 73503b4..4c30a89 100644 --- a/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js +++ b/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js
@@ -39,8 +39,10 @@ this._notPausedMessageElement = this.contentElement.createChild('div', 'gray-info-message'); this._notPausedMessageElement.textContent = Common.UIString('Not Paused'); + /** @type {!UI.ListModel<!Sources.CallStackSidebarPane.Item>} */ + this._items = new UI.ListModel(); /** @type {!UI.ListControl<!Sources.CallStackSidebarPane.Item>} */ - this._list = new UI.ListControl(this, UI.ListMode.NonViewport); + this._list = new UI.ListControl(this._items, this, UI.ListMode.NonViewport); this.contentElement.appendChild(this._list.element); this._list.element.addEventListener('contextmenu', this._onContextMenu.bind(this), false); this._list.element.addEventListener('click', this._onClick.bind(this), false); @@ -68,7 +70,7 @@ if (!details) { this._notPausedMessageElement.classList.remove('hidden'); this._blackboxedMessageElement.classList.add('hidden'); - this._list.replaceAllItems([]); + this._items.replaceAllItems([]); this._debuggerModel = null; UI.context.setFlavor(SDK.DebuggerModel.CallFrame, null); return; @@ -139,7 +141,7 @@ this._blackboxedMessageElement.classList.remove('hidden'); } - this._list.replaceAllItems(items); + this._items.replaceAllItems(items); this._list.selectNextItem(true /* canWrap */, false /* center */); } @@ -357,8 +359,8 @@ _copyStackTrace() { var text = []; - for (var i = 0; i < this._list.length(); i++) { - var item = this._list.itemAtIndex(i); + for (var i = 0; i < this._items.length(); i++) { + var item = this._items.itemAtIndex(i); if (item.promiseCreationFrame) continue; var itemText = this._itemTitle(item);
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js index c54e0e7..6278274b5 100644 --- a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js +++ b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
@@ -1056,7 +1056,7 @@ this._sidebarPaneStack.showView(jsBreakpoints); this._extensionSidebarPanesContainer = this._sidebarPaneStack; this.sidebarPaneView = vbox; - this._splitWidget.uninstallResizer(this._debugToolbar.element); + this._splitWidget.uninstallResizer(this._debugToolbar.gripElementForResize()); } else { var splitWidget = new UI.SplitWidget(true, true, 'sourcesPanelDebuggerSidebarSplitViewState', 0.5); splitWidget.setMainWidget(vbox); @@ -1068,7 +1068,7 @@ splitWidget.setSidebarWidget(tabbedLocation.tabbedPane()); this._tabbedLocationHeader = tabbedLocation.tabbedPane().headerElement(); this._splitWidget.installResizer(this._tabbedLocationHeader); - this._splitWidget.installResizer(this._debugToolbar.element); + this._splitWidget.installResizer(this._debugToolbar.gripElementForResize()); tabbedLocation.appendView(scopeChainView); tabbedLocation.appendView(this._watchSidebarPane); this._extensionSidebarPanesContainer = tabbedLocation;
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/ThreadsSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/sources/ThreadsSidebarPane.js index 8009585..f193a3f 100644 --- a/third_party/WebKit/Source/devtools/front_end/sources/ThreadsSidebarPane.js +++ b/third_party/WebKit/Source/devtools/front_end/sources/ThreadsSidebarPane.js
@@ -10,8 +10,10 @@ super(true); this.registerRequiredCSS('sources/threadsSidebarPane.css'); + /** @type {!UI.ListModel<!SDK.DebuggerModel>} */ + this._items = new UI.ListModel(); /** @type {!UI.ListControl<!SDK.DebuggerModel>} */ - this._list = new UI.ListControl(this, UI.ListMode.NonViewport); + this._list = new UI.ListControl(this._items, this, UI.ListMode.NonViewport); this.contentElement.appendChild(this._list.element); UI.context.addFlavorChangeListener(SDK.Target, this._targetFlavorChanged, this); @@ -106,7 +108,7 @@ * @param {!SDK.DebuggerModel} debuggerModel */ modelAdded(debuggerModel) { - this._list.pushItem(debuggerModel); + this._items.pushItem(debuggerModel); var currentTarget = UI.context.flavor(SDK.Target); if (currentTarget === debuggerModel.target()) this._list.selectItem(debuggerModel); @@ -117,7 +119,7 @@ * @param {!SDK.DebuggerModel} debuggerModel */ modelRemoved(debuggerModel) { - this._list.removeItem(debuggerModel); + this._items.removeItem(debuggerModel); } /**
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineHistoryManager.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineHistoryManager.js index 68ffb79..8e2522b5 100644 --- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineHistoryManager.js +++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineHistoryManager.js
@@ -253,9 +253,10 @@ UI.createShadowRootWithCoreStyles(this._glassPane.contentElement, 'timeline/timelineHistoryManager.css'); var contentElement = shadowRoot.createChild('div', 'drop-down'); - this._listControl = new UI.ListControl(this, UI.ListMode.NonViewport); + var listModel = new UI.ListModel(); + this._listControl = new UI.ListControl(listModel, this, UI.ListMode.NonViewport); this._listControl.element.addEventListener('mousemove', this._onMouseMove.bind(this), false); - this._listControl.replaceAllItems(models); + listModel.replaceAllItems(models); contentElement.appendChild(this._listControl.element); contentElement.addEventListener('keydown', this._onKeyDown.bind(this), false);
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js b/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js index a2b92c00..39be6fd 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js
@@ -50,10 +50,11 @@ */ UI.ListControl = class { /** + * @param {!UI.ListModel<T>} model * @param {!UI.ListDelegate<T>} delegate * @param {!UI.ListMode=} mode */ - constructor(delegate, mode) { + constructor(model, delegate, mode) { this.element = createElement('div'); this.element.style.overflowY = 'auto'; this._topElement = this.element.createChild('div'); @@ -64,11 +65,13 @@ this._topHeight = 0; this._bottomHeight = 0; - /** @type {!Array<T>} */ - this._items = []; + this._model = model; + this._model.addEventListener(UI.ListModel.Events.ItemsReplaced, this._replacedItemsInRange, this); /** @type {!Map<T, !Element>} */ this._itemToElement = new Map(); this._selectedIndex = -1; + /** @type {?T} */ + this._selectedItem = null; this.element.tabIndex = -1; this.element.addEventListener('click', this._onClick.bind(this), false); @@ -88,106 +91,36 @@ } /** - * @return {number} + * @param {!UI.ListModel<T>} model */ - length() { - return this._items.length; + setModel(model) { + this._itemToElement.clear(); + var length = this._model.length(); + this._model.removeEventListener(UI.ListModel.Events.ItemsReplaced, this._replacedItemsInRange, this); + this._model = model; + this._model.addEventListener(UI.ListModel.Events.ItemsReplaced, this._replacedItemsInRange, this); + this.invalidateRange(0, length); } /** - * @param {number} index - * @return {T} + * @param {!Common.Event} event */ - itemAtIndex(index) { - return this._items[index]; - } + _replacedItemsInRange(event) { + var data = /** @type {{index: number, removed: !Array<T>, inserted: number}} */ (event.data); + var from = data.index; + var to = from + data.removed.length; - /** - * @param {T} item - */ - pushItem(item) { - this.replaceItemsInRange(this._items.length, this._items.length, [item]); - } - - /** - * @return {T} - */ - popItem() { - return this.removeItemAtIndex(this._items.length - 1); - } - - /** - * @param {number} index - * @param {T} item - */ - insertItemAtIndex(index, item) { - this.replaceItemsInRange(index, index, [item]); - } - - /** - * @param {T} item - * @param {function(T, T):number} comparator - */ - insertItemWithComparator(item, comparator) { - var index = this._items.lowerBound(item, comparator); - this.insertItemAtIndex(index, item); - } - - /** - * @param {T} item - * @return {number} - */ - indexOfItem(item) { - return this._items.indexOf(item); - } - - /** - * @param {number} index - * @return {T} - */ - removeItemAtIndex(index) { - var result = this._items[index]; - this.replaceItemsInRange(index, index + 1, []); - return result; - } - - /** - * @param {T} item - */ - removeItem(item) { - var index = this._items.indexOf(item); - if (index === -1) { - console.error('Attempt to remove non-existing item'); - return; - } - this.removeItemAtIndex(index); - } - - /** - * @param {number} from - * @param {number} to - * @param {!Array<T>} items - */ - replaceItemsInRange(from, to, items) { - var oldSelectedItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; + var oldSelectedItem = this._selectedItem; var oldSelectedElement = oldSelectedItem ? (this._itemToElement.get(oldSelectedItem) || null) : null; - - for (var i = from; i < to; i++) - this._itemToElement.delete(this._items[i]); - if (items.length < 10000) { - this._items.splice.bind(this._items, from, to - from).apply(null, items); - } else { - // Splice may fail with too many arguments. - var before = this._items.slice(0, from); - var after = this._items.slice(to); - this._items = [].concat(before, items, after); - } - this._invalidate(from, to, items.length); + for (var i = 0; i < data.removed.length; i++) + this._itemToElement.delete(data.removed[i]); + this._invalidate(from, to, data.inserted); if (this._selectedIndex >= to) { - this._selectedIndex += items.length - (to - from); + this._selectedIndex += data.inserted - (to - from); + this._selectedItem = this._model.itemAtIndex(this._selectedIndex); } else if (this._selectedIndex >= from) { - var index = this._findFirstSelectable(from + items.length, +1, false); + var index = this._findFirstSelectable(from + data.inserted, +1, false); if (index === -1) index = this._findFirstSelectable(from - 1, -1, false); this._select(index, oldSelectedItem, oldSelectedElement); @@ -195,24 +128,16 @@ } /** - * @param {!Array<T>} items + * @param {T} item */ - replaceAllItems(items) { - this.replaceItemsInRange(0, this._items.length, items); - } - - refreshAllItems() { - this.refreshItemsInRange(0, this._items.length); - } - - /** - * @param {number} from - * @param {number} to - */ - refreshItemsInRange(from, to) { - for (var i = from; i < to; i++) - this._itemToElement.delete(this._items[i]); - this.invalidateRange(from, to); + refreshItem(item) { + var index = this._model.indexOfItem(item); + if (index === -1) { + console.error('Item to refresh is not present'); + return; + } + this._itemToElement.delete(item); + this.invalidateRange(index, index + 1); if (this._selectedIndex !== -1) this._select(this._selectedIndex, null, null); } @@ -241,9 +166,9 @@ return; } this._fixedHeight = 0; - if (this._items.length) { + if (this._model.length()) { this._itemToElement.clear(); - this._invalidate(0, this._items.length, this._items.length); + this._invalidate(0, this._model.length(), this._model.length()); } } @@ -257,8 +182,8 @@ if (!node) return null; var element = /** @type {!Element} */ (node); - var index = this._items.findIndex(item => this._itemToElement.get(item) === element); - return index !== -1 ? this._items[index] : null; + var index = this._model.findIndex(item => this._itemToElement.get(item) === element); + return index !== -1 ? this._model.itemAtIndex(index) : null; } /** @@ -266,7 +191,7 @@ * @param {boolean=} center */ scrollItemIntoView(item, center) { - var index = this._items.indexOf(item); + var index = this._model.indexOfItem(item); if (index === -1) { console.error('Attempt to scroll onto missing item'); return; @@ -278,7 +203,7 @@ * @return {?T} */ selectedItem() { - return this._selectedIndex === -1 ? null : this._items[this._selectedIndex]; + return this._selectedItem; } /** @@ -296,7 +221,7 @@ selectItem(item, center, dontScroll) { var index = -1; if (item !== null) { - index = this._items.indexOf(item); + index = this._model.indexOfItem(item); if (index === -1) { console.error('Attempt to select missing item'); return; @@ -320,7 +245,7 @@ selectPreviousItem(canWrap, center) { if (this._selectedIndex === -1 && !canWrap) return false; - var index = this._selectedIndex === -1 ? this._items.length - 1 : this._selectedIndex - 1; + var index = this._selectedIndex === -1 ? this._model.length() - 1 : this._selectedIndex - 1; index = this._findFirstSelectable(index, -1, !!canWrap); if (index !== -1) { this._scrollIntoView(index, center); @@ -355,7 +280,7 @@ selectItemPreviousPage(center) { if (this._mode === UI.ListMode.NonViewport) return false; - var index = this._selectedIndex === -1 ? this._items.length - 1 : this._selectedIndex; + var index = this._selectedIndex === -1 ? this._model.length() - 1 : this._selectedIndex; index = this._findPageSelectable(index, -1); if (index !== -1) { this._scrollIntoView(index, center); @@ -444,7 +369,7 @@ * @return {number} */ _totalHeight() { - return this._offsetAtIndex(this._items.length); + return this._offsetAtIndex(this._model.length()); } /** @@ -454,15 +379,15 @@ _indexAtOffset(offset) { if (this._mode === UI.ListMode.NonViewport) throw 'There should be no offset conversions in non-viewport mode'; - if (!this._items.length || offset < 0) + if (!this._model.length() || offset < 0) return 0; if (this._mode === UI.ListMode.VariousHeightItems) { return Math.min( - this._items.length - 1, this._variableOffsets.lowerBound(offset, undefined, 0, this._items.length)); + this._model.length() - 1, this._variableOffsets.lowerBound(offset, undefined, 0, this._model.length())); } if (!this._fixedHeight) this._measureHeight(); - return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeight)); + return Math.min(this._model.length() - 1, Math.floor(offset / this._fixedHeight)); } /** @@ -470,7 +395,7 @@ * @return {!Element} */ _elementAtIndex(index) { - var item = this._items[index]; + var item = this._model.itemAtIndex(index); var element = this._itemToElement.get(item); if (!element) { element = this._delegate.createElementForItem(item); @@ -486,7 +411,7 @@ _offsetAtIndex(index) { if (this._mode === UI.ListMode.NonViewport) throw 'There should be no offset conversions in non-viewport mode'; - if (!this._items.length) + if (!this._model.length()) return 0; if (this._mode === UI.ListMode.VariousHeightItems) return this._variableOffsets[index]; @@ -496,7 +421,7 @@ } _measureHeight() { - this._fixedHeight = this._delegate.heightForItem(this._items[0]); + this._fixedHeight = this._delegate.heightForItem(this._model.itemAtIndex(0)); if (!this._fixedHeight) this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this.element).height; } @@ -508,11 +433,12 @@ */ _select(index, oldItem, oldElement) { if (oldItem === undefined) - oldItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; + oldItem = this._selectedItem; if (oldElement === undefined) oldElement = this._itemToElement.get(oldItem) || null; this._selectedIndex = index; - var newItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; + this._selectedItem = index === -1 ? null : this._model.itemAtIndex(index); + var newItem = this._selectedItem; var newElement = this._selectedIndex !== -1 ? this._elementAtIndex(index) : null; this._delegate.selectedItemChanged(oldItem, newItem, /** @type {?Element} */ (oldElement), newElement); } @@ -524,7 +450,7 @@ * @return {number} */ _findFirstSelectable(index, direction, canWrap) { - var length = this._items.length; + var length = this._model.length(); if (!length) return -1; for (var step = 0; step <= length; step++) { @@ -533,7 +459,7 @@ return -1; index = (index + length) % length; } - if (this._delegate.isItemSelectable(this._items[index])) + if (this._delegate.isItemSelectable(this._model.itemAtIndex(index))) return index; index += direction; } @@ -550,8 +476,8 @@ var startOffset = this._offsetAtIndex(index); // Compensate for zoom rounding errors with -1. var viewportHeight = this.element.offsetHeight - 1; - while (index >= 0 && index < this._items.length) { - if (this._delegate.isItemSelectable(this._items[index])) { + while (index >= 0 && index < this._model.length()) { + if (this._delegate.isItemSelectable(this._model.itemAtIndex(index))) { if (Math.abs(this._offsetAtIndex(index) - startOffset) >= viewportHeight) return index; lastSelectable = index; @@ -589,9 +515,11 @@ } if (this._mode === UI.ListMode.VariousHeightItems) { - this._reallocateVariableOffsets(this._items.length + 1, from + 1); - for (var i = from + 1; i <= this._items.length; i++) - this._variableOffsets[i] = this._variableOffsets[i - 1] + this._delegate.heightForItem(this._items[i - 1]); + this._reallocateVariableOffsets(this._model.length() + 1, from + 1); + for (var i = from + 1; i <= this._model.length(); i++) { + this._variableOffsets[i] = + this._variableOffsets[i - 1] + this._delegate.heightForItem(this._model.itemAtIndex(i - 1)); + } } var viewportHeight = this.element.offsetHeight;
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/ListModel.js b/third_party/WebKit/Source/devtools/front_end/ui/ListModel.js new file mode 100644 index 0000000..5ebd819 --- /dev/null +++ b/third_party/WebKit/Source/devtools/front_end/ui/ListModel.js
@@ -0,0 +1,140 @@ +// 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. + +/** + * @template T + */ +UI.ListModel = class extends Common.Object { + /** + * @param {!Array<T>=} items + */ + constructor(items) { + super(); + this._items = items || []; + } + + /** + * @return {number} + */ + length() { + return this._items.length; + } + + /** + * @param {number} index + * @return {T} + */ + itemAtIndex(index) { + return this._items[index]; + } + + /** + * @param {T} item + */ + pushItem(item) { + this.replaceItemsInRange(this._items.length, this._items.length, [item]); + } + + /** + * @return {T} + */ + popItem() { + return this.removeItemAtIndex(this._items.length - 1); + } + + /** + * @param {number} index + * @param {T} item + */ + insertItemAtIndex(index, item) { + this.replaceItemsInRange(index, index, [item]); + } + + /** + * @param {T} item + * @param {function(T, T):number} comparator + */ + insertItemWithComparator(item, comparator) { + var index = this._items.lowerBound(item, comparator); + this.insertItemAtIndex(index, item); + } + + /** + * @param {T} item + * @return {number} + */ + indexOfItem(item) { + return this._items.indexOf(item); + } + + /** + * @param {number} index + * @return {T} + */ + removeItemAtIndex(index) { + var result = this._items[index]; + this.replaceItemsInRange(index, index + 1, []); + return result; + } + + /** + * @param {T} item + */ + removeItem(item) { + var index = this._items.indexOf(item); + if (index === -1) { + console.error('Attempt to remove non-existing item'); + return; + } + this.removeItemAtIndex(index); + } + + /** + * @param {!Array<T>} items + */ + replaceAllItems(items) { + this.replaceItemsInRange(0, this._items.length, items); + } + + /** + * @param {number} index + * @param {T} item + */ + replaceItemAtIndex(index, item) { + this.replaceItemsInRange(index, index + 1, [item]); + } + + /** + * @param {function(T):boolean} callback + * @return {number} + */ + findIndex(callback) { + return this._items.findIndex(callback); + } + + /** + * @param {number} from + * @param {number} to + * @param {!Array<T>} items + */ + replaceItemsInRange(from, to, items) { + var removed; + if (items.length < 10000) { + removed = this._items.splice(from, to - from, ...items); + } else { + removed = this._items.slice(from, to); + // Splice may fail with too many arguments. + var before = this._items.slice(0, from); + var after = this._items.slice(to); + this._items = [].concat(before, items, after); + } + this.dispatchEventToListeners( + UI.ListModel.Events.ItemsReplaced, {index: from, removed: removed, inserted: items.length}); + } +}; + +/** @enum {symbol} */ +UI.ListModel.Events = { + ItemsReplaced: Symbol('ItemsReplaced'), +};
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js b/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js index 03e0c12..7c3badc 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
@@ -66,8 +66,10 @@ /** @type {?string} */ this._onlyCompletion = null; + /** @type {!UI.ListModel<!UI.SuggestBox.Suggestion>} */ + this._items = new UI.ListModel(); /** @type {!UI.ListControl<!UI.SuggestBox.Suggestion>} */ - this._list = new UI.ListControl(this, UI.ListMode.EqualHeightItems); + this._list = new UI.ListControl(this._items, this, UI.ListMode.EqualHeightItems); this._element = this._list.element; this._element.classList.add('suggest-box'); this._element.addEventListener('mousedown', event => event.preventDefault(), true); @@ -316,7 +318,7 @@ this._updateMaxSize(completions); this._glassPane.setContentAnchorBox(anchorBox); this._list.invalidateItemHeight(); - this._list.replaceAllItems(completions); + this._items.replaceAllItems(completions); if (selectHighestPriority) { var highestPriorityItem = completions[0];
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Toolbar.js b/third_party/WebKit/Source/devtools/front_end/ui/Toolbar.js index 909d19a..37cc0e2f 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/Toolbar.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/Toolbar.js
@@ -194,6 +194,13 @@ } /** + * @return {!Element} + */ + gripElementForResize() { + return this._contentElement; + } + + /** * @param {boolean=} reverse * @param {boolean=} growVertically */
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/module.json b/third_party/WebKit/Source/devtools/front_end/ui/module.json index 52967e3f..c1144a11 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/module.json +++ b/third_party/WebKit/Source/devtools/front_end/ui/module.json
@@ -28,6 +28,7 @@ "TextEditor.js", "KeyboardShortcut.js", "ListControl.js", + "ListModel.js", "ListWidget.js", "Panel.js", "Popover.js",
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/copy_existing_baselines.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/copy_existing_baselines.py new file mode 100644 index 0000000..2cb949a --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/copy_existing_baselines.py
@@ -0,0 +1,124 @@ +# 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. + +import logging + +from webkitpy.common.memoized import memoized +from webkitpy.layout_tests.models.test_expectations import SKIP +from webkitpy.layout_tests.models.test_expectations import TestExpectations +from webkitpy.tool.commands.rebaseline import AbstractRebaseliningCommand + +_log = logging.getLogger(__name__) + + +class CopyExistingBaselines(AbstractRebaseliningCommand): + name = 'copy-existing-baselines-internal' + help_text = ('Copy existing baselines down one level in the baseline ' + 'order to ensure new baselines don\'t break existing passing ' + 'platforms.') + + def __init__(self): + super(CopyExistingBaselines, self).__init__(options=[ + self.test_option, + self.suffixes_option, + self.port_name_option, + self.results_directory_option, + ]) + + def execute(self, options, args, tool): + self._tool = tool + port_name = options.port_name + for suffix in options.suffixes.split(','): + self._copy_existing_baseline(port_name, options.test, suffix) + + def _copy_existing_baseline(self, port_name, test_name, suffix): + """Copies the baseline for the given builder to all "predecessor" directories.""" + baseline_directory = self._tool.port_factory.get(port_name).baseline_version_dir() + ports = [self._port_for_primary_baseline(baseline) + for baseline in self._immediate_predecessors_in_fallback(baseline_directory)] + + old_baselines = [] + new_baselines = [] + + # Need to gather all the baseline paths before modifying the filesystem since + # the modifications can affect the results of port.expected_filename. + for port in ports: + old_baseline = port.expected_filename(test_name, '.' + suffix) + if not self._tool.filesystem.exists(old_baseline): + _log.debug('No existing baseline for %s.', test_name) + continue + + new_baseline = self._tool.filesystem.join( + port.baseline_version_dir(), + self._file_name_for_expected_result(test_name, suffix)) + if self._tool.filesystem.exists(new_baseline): + _log.debug('Existing baseline at %s, not copying over it.', new_baseline) + continue + + generic_expectations = TestExpectations(port, tests=[test_name], include_overrides=False) + full_expectations = TestExpectations(port, tests=[test_name], include_overrides=True) + # TODO(qyearsley): Change Port.skips_test so that this can be simplified. + if SKIP in full_expectations.get_expectations(test_name): + _log.debug('%s is skipped (perhaps temporarily) on %s.', test_name, port.name()) + continue + if port.skips_test(test_name, generic_expectations, full_expectations): + _log.debug('%s is skipped on %s.', test_name, port.name()) + continue + + old_baselines.append(old_baseline) + new_baselines.append(new_baseline) + + for i in range(len(old_baselines)): + old_baseline = old_baselines[i] + new_baseline = new_baselines[i] + + _log.debug('Copying baseline from %s to %s.', old_baseline, new_baseline) + self._tool.filesystem.maybe_make_directory(self._tool.filesystem.dirname(new_baseline)) + self._tool.filesystem.copyfile(old_baseline, new_baseline) + + def _port_for_primary_baseline(self, baseline): + """Returns a Port object for the given baseline directory base name.""" + for port in [self._tool.port_factory.get(port_name) for port_name in self._tool.port_factory.all_port_names()]: + if self._tool.filesystem.basename(port.baseline_version_dir()) == baseline: + return port + raise Exception('Failed to find port for primary baseline %s.' % baseline) + + @memoized + def _immediate_predecessors_in_fallback(self, path_to_rebaseline): + """Returns the predecessor directories in the baseline fall-back graph. + + The platform-specific fall-back baseline directories form a tree, where + when we search for baselines we normally fall back to parents nodes in + the tree. The "immediate predecessors" are the children nodes of the + given node. + + For example, if the baseline fall-back graph includes: + "mac10.9" -> "mac10.10/" + "mac10.10/" -> "mac/" + "retina/" -> "mac/" + Then, the "immediate predecessors" are: + "mac/": ["mac10.10/", "retina/"] + "mac10.10/": ["mac10.9/"] + "mac10.9/", "retina/": [] + + Args: + path_to_rebaseline: The absolute path to a baseline directory. + + Returns: + A list of directory names (not full paths) of directories that are + "immediate predecessors" of the given baseline path. + """ + port_names = self._tool.port_factory.all_port_names() + immediate_predecessors = [] + for port_name in port_names: + port = self._tool.port_factory.get(port_name) + baseline_search_path = port.baseline_search_path() + try: + index = baseline_search_path.index(path_to_rebaseline) + if index: + immediate_predecessors.append(self._tool.filesystem.basename(baseline_search_path[index - 1])) + except ValueError: + # baseline_search_path.index() throws a ValueError if the item isn't in the list. + pass + return immediate_predecessors
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/copy_existing_baselines_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/copy_existing_baselines_unittest.py new file mode 100644 index 0000000..b01f871 --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/copy_existing_baselines_unittest.py
@@ -0,0 +1,118 @@ +# 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. + +import optparse + +from webkitpy.tool.commands.rebaseline_unittest import BaseTestCase +from webkitpy.tool.commands.copy_existing_baselines import CopyExistingBaselines + + +class TestCopyExistingBaselines(BaseTestCase): + command_constructor = CopyExistingBaselines + + def options(self, **kwargs): + options_dict = { + 'results_directory': None, + 'suffixes': 'txt', + 'verbose': False, + 'port_name': None, + } + options_dict.update(kwargs) + return optparse.Values(options_dict) + + def baseline_path(self, path_from_layout_test_dir): + port = self.tool.port_factory.get() + return self.tool.filesystem.join(port.layout_tests_dir(), path_from_layout_test_dir) + + # The tests in this class all depend on the fall-back path graph + # that is set up in |TestPort.FALLBACK_PATHS|. + + def test_copy_baseline_mac_newer_to_older_version(self): + # The test-mac-mac10.11 baseline is copied over to the test-mac-mac10.10 + # baseline directory because test-mac-mac10.10 is the "immediate + # predecessor" in the fall-back graph. + self._write( + self.baseline_path('platform/test-mac-mac10.11/failures/expected/image-expected.txt'), + 'original test-mac-mac10.11 result') + self.assertFalse(self.tool.filesystem.exists( + self.baseline_path('platform/test-mac-mac10.10/failures/expected/image-expected.txt'))) + + self.command.execute(self.options(port_name='test-mac-mac10.11', test='failures/expected/image.html'), [], self.tool) + + self.assertEqual( + self._read(self.baseline_path('platform/test-mac-mac10.11/failures/expected/image-expected.txt')), + 'original test-mac-mac10.11 result') + self.assertEqual( + self._read(self.baseline_path('platform/test-mac-mac10.10/failures/expected/image-expected.txt')), + 'original test-mac-mac10.11 result') + + def test_copy_baseline_to_multiple_immediate_predecessors(self): + # The test-win-win10 baseline is copied over to the test-linux-trusty + # and test-win-win7 baseline paths, since both of these are "immediate + # predecessors". + self._write( + self.baseline_path('platform/test-win-win10/failures/expected/image-expected.txt'), + 'original test-win-win10 result') + self.assertFalse(self.tool.filesystem.exists( + self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt'))) + + self.command.execute(self.options(port_name='test-win-win10', test='failures/expected/image.html'), [], self.tool) + + self.assertEqual( + self._read(self.baseline_path('platform/test-win-win10/failures/expected/image-expected.txt')), + 'original test-win-win10 result') + self.assertEqual( + self._read(self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt')), + 'original test-win-win10 result') + self.assertEqual( + self._read(self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt')), + 'original test-win-win10 result') + + def test_no_copy_existing_baseline(self): + # If a baseline exists already for an "immediate predecessor" baseline + # directory, (e.g. test-linux-trusty), then no "immediate successor" + # baselines (e.g. test-win-win10) are copied over. + self._write( + self.baseline_path('platform/test-win-win10/failures/expected/image-expected.txt'), + 'original test-win-win10 result') + self._write( + self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt'), + 'original test-linux-trusty result') + + self.command.execute(self.options(port_name='test-win-win10', test='failures/expected/image.html'), [], self.tool) + + self.assertEqual( + self._read(self.baseline_path('platform/test-win-win10/failures/expected/image-expected.txt')), + 'original test-win-win10 result') + self.assertEqual( + self._read(self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt')), + 'original test-linux-trusty result') + + def test_no_copy_skipped_test(self): + # If a test is skipped on some platform, no baselines are copied over + # to that directory. In this example, the test is skipped on linux, + # so the test-win-win10 baseline is not copied over. + port = self.tool.port_factory.get('test-win-win10') + self._write( + self.baseline_path('platform/test-win-win10/failures/expected/image-expected.txt'), + 'original test-win-win10 result') + self._write( + port.path_to_generic_test_expectations_file(), + ('[ Win ] failures/expected/image.html [ Failure ]\n' + '[ Linux ] failures/expected/image.html [ Skip ]\n')) + + self.command.execute(self.options(port_name='test-win-win10', test='failures/expected/image.html'), [], self.tool) + + self.assertFalse( + self.tool.filesystem.exists(self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt'))) + + def test_port_for_primary_baseline(self): + # Testing a protected method - pylint: disable=protected-access + self.assertEqual(self.command._port_for_primary_baseline('test-linux-trusty').name(), 'test-linux-trusty') + self.assertEqual(self.command._port_for_primary_baseline('test-mac-mac10.11').name(), 'test-mac-mac10.11') + + def test_port_for_primary_baseline_not_found(self): + # Testing a protected method - pylint: disable=protected-access + with self.assertRaises(Exception): + self.command._port_for_primary_baseline('test-foo-foo4.7')
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py index 381697a..9745818 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py
@@ -26,24 +26,19 @@ # (INCLUDING NEGLIGENCE OR/ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from __future__ import print_function import collections import json import logging import optparse import re -import sys -import traceback -from webkitpy.common.memoized import memoized from webkitpy.common.net.buildbot import Build -from webkitpy.common.system.executive import ScriptError +from webkitpy.layout_tests.models.test_expectations import BASELINE_SUFFIX_LIST +from webkitpy.layout_tests.models.test_expectations import TestExpectations from webkitpy.layout_tests.models.testharness_results import is_all_pass_testharness_result -from webkitpy.layout_tests.models.test_expectations import TestExpectations, BASELINE_SUFFIX_LIST, SKIP from webkitpy.layout_tests.port import factory from webkitpy.tool.commands.command import Command - _log = logging.getLogger(__name__) @@ -82,10 +77,7 @@ self.expectation_line_changes = ChangeSet() self._tool = None - def _print_expectation_line_changes(self): - print(json.dumps(self.expectation_line_changes.to_dict())) - - def _baseline_directory(self, builder_name): + def baseline_directory(self, builder_name): port = self._tool.port_factory.get_from_builder_name(builder_name) return port.baseline_version_dir() @@ -199,187 +191,6 @@ return self._builder_names -class CopyExistingBaselinesInternal(AbstractRebaseliningCommand): - name = 'copy-existing-baselines-internal' - help_text = ('Copy existing baselines down one level in the baseline order to ensure ' - "new baselines don't break existing passing platforms.") - - def __init__(self): - super(CopyExistingBaselinesInternal, self).__init__(options=[ - self.test_option, - self.suffixes_option, - self.port_name_option, - self.results_directory_option, - ]) - - @memoized - def _immediate_predecessors_in_fallback(self, path_to_rebaseline): - """Returns the predecessor directories in the baseline fall-back graph. - - The platform-specific fall-back baseline directories form a tree, where - when we search for baselines we normally fall back to parents nodes in - the tree. The "immediate predecessors" are the children nodes of the - given node. - - For example, if the baseline fall-back graph includes: - "mac10.9" -> "mac10.10/" - "mac10.10/" -> "mac/" - "retina/" -> "mac/" - Then, the "immediate predecessors" are: - "mac/": ["mac10.10/", "retina/"] - "mac10.10/": ["mac10.9/"] - "mac10.9/", "retina/": [] - - Args: - path_to_rebaseline: The absolute path to a baseline directory. - - Returns: - A list of directory names (not full paths) of directories that are - "immediate predecessors" of the given baseline path. - """ - port_names = self._tool.port_factory.all_port_names() - immediate_predecessors = [] - for port_name in port_names: - port = self._tool.port_factory.get(port_name) - baseline_search_path = port.baseline_search_path() - try: - index = baseline_search_path.index(path_to_rebaseline) - if index: - immediate_predecessors.append(self._tool.filesystem.basename(baseline_search_path[index - 1])) - except ValueError: - # baseline_search_path.index() throws a ValueError if the item isn't in the list. - pass - return immediate_predecessors - - def _port_for_primary_baseline(self, baseline): - """Returns a Port object for the given baseline directory base name.""" - for port in [self._tool.port_factory.get(port_name) for port_name in self._tool.port_factory.all_port_names()]: - if self._tool.filesystem.basename(port.baseline_version_dir()) == baseline: - return port - raise Exception('Failed to find port for primary baseline %s.' % baseline) - - def _copy_existing_baseline(self, port_name, test_name, suffix): - """Copies the baseline for the given builder to all "predecessor" directories.""" - baseline_directory = self._tool.port_factory.get(port_name).baseline_version_dir() - ports = [self._port_for_primary_baseline(baseline) - for baseline in self._immediate_predecessors_in_fallback(baseline_directory)] - - old_baselines = [] - new_baselines = [] - - # Need to gather all the baseline paths before modifying the filesystem since - # the modifications can affect the results of port.expected_filename. - for port in ports: - old_baseline = port.expected_filename(test_name, '.' + suffix) - if not self._tool.filesystem.exists(old_baseline): - _log.debug('No existing baseline for %s.', test_name) - continue - - new_baseline = self._tool.filesystem.join( - port.baseline_version_dir(), - self._file_name_for_expected_result(test_name, suffix)) - if self._tool.filesystem.exists(new_baseline): - _log.debug('Existing baseline at %s, not copying over it.', new_baseline) - continue - - generic_expectations = TestExpectations(port, tests=[test_name], include_overrides=False) - full_expectations = TestExpectations(port, tests=[test_name], include_overrides=True) - # TODO(qyearsley): Change Port.skips_test so that this can be simplified. - if SKIP in full_expectations.get_expectations(test_name): - _log.debug('%s is skipped (perhaps temporarily) on %s.', test_name, port.name()) - continue - if port.skips_test(test_name, generic_expectations, full_expectations): - _log.debug('%s is skipped on %s.', test_name, port.name()) - continue - - old_baselines.append(old_baseline) - new_baselines.append(new_baseline) - - for i in range(len(old_baselines)): - old_baseline = old_baselines[i] - new_baseline = new_baselines[i] - - _log.debug('Copying baseline from %s to %s.', old_baseline, new_baseline) - self._tool.filesystem.maybe_make_directory(self._tool.filesystem.dirname(new_baseline)) - self._tool.filesystem.copyfile(old_baseline, new_baseline) - - def execute(self, options, args, tool): - self._tool = tool - port_name = options.port_name - for suffix in options.suffixes.split(','): - self._copy_existing_baseline(port_name, options.test, suffix) - - -class RebaselineTest(AbstractRebaseliningCommand): - name = 'rebaseline-test-internal' - help_text = 'Rebaseline a single test from a buildbot. Only intended for use by other webkit-patch commands.' - - def __init__(self): - super(RebaselineTest, self).__init__(options=[ - self.test_option, - self.suffixes_option, - self.port_name_option, - self.builder_option, - self.build_number_option, - self.results_directory_option, - ]) - - def _save_baseline(self, data, target_baseline): - if not data: - _log.debug('No baseline data to save.') - return - - filesystem = self._tool.filesystem - filesystem.maybe_make_directory(filesystem.dirname(target_baseline)) - filesystem.write_binary_file(target_baseline, data) - - def _rebaseline_test(self, port_name, test_name, suffix, results_url): - """Downloads a baseline file and saves it to the filesystem. - - Args: - port_name: The port that the baseline is for. This determines - the directory that the baseline is saved to. - test_name: The name of the test being rebaselined. - suffix: The baseline file extension (e.g. png); together with the - test name and results_url this determines what file to download. - results_url: Base URL to download the actual result from. - """ - baseline_directory = self._tool.port_factory.get(port_name).baseline_version_dir() - - source_baseline = '%s/%s' % (results_url, self._file_name_for_actual_result(test_name, suffix)) - target_baseline = self._tool.filesystem.join(baseline_directory, self._file_name_for_expected_result(test_name, suffix)) - - _log.debug('Retrieving source %s for target %s.', source_baseline, target_baseline) - self._save_baseline(self._tool.web.get_binary(source_baseline, return_none_on_404=True), - target_baseline) - - def _rebaseline_test_and_update_expectations(self, options): - self._baseline_suffix_list = options.suffixes.split(',') - - port_name = options.port_name or self._tool.builders.port_name_for_builder_name(options.builder) - port = self._tool.port_factory.get(port_name) - - if port.reference_files(options.test): - if 'png' in self._baseline_suffix_list: - _log.warning('Cannot rebaseline image result for reftest: %s', options.test) - return - assert self._baseline_suffix_list == ['txt'] - - if options.results_directory: - results_url = 'file://' + options.results_directory - else: - results_url = self._tool.buildbot.results_url(options.builder, build_number=options.build_number) - - for suffix in self._baseline_suffix_list: - self._rebaseline_test(port_name, options.test, suffix, results_url) - self.expectation_line_changes.remove_line(test=options.test, port_name=port_name) - - def execute(self, options, args, tool): - self._tool = tool - self._rebaseline_test_and_update_expectations(options) - self._print_expectation_line_changes() - - class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand): """Base class for rebaseline commands that do some tasks in parallel.""" # Not overriding execute() - pylint: disable=abstract-method @@ -644,7 +455,7 @@ baseline_paths = set() for test, build, _ in test_baseline_set: filenames = [self._file_name_for_expected_result(test, suffix) for suffix in BASELINE_SUFFIX_LIST] - port_baseline_dir = self._baseline_directory(build.builder_name) + port_baseline_dir = self.baseline_directory(build.builder_name) baseline_paths.update({filesystem.join(port_baseline_dir, filename) for filename in filenames}) baseline_paths.update({filesystem.join(self._layout_tests_dir(), filename) for filename in filenames}) return sorted(baseline_paths)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_test.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_test.py new file mode 100644 index 0000000..66d0305d --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_test.py
@@ -0,0 +1,85 @@ +# 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. + +import json +import logging + +from webkitpy.tool.commands.rebaseline import AbstractRebaseliningCommand + +_log = logging.getLogger(__name__) + + +class RebaselineTest(AbstractRebaseliningCommand): + name = 'rebaseline-test-internal' + help_text = 'Rebaseline a single test from a single builder.' + + def __init__(self): + super(RebaselineTest, self).__init__(options=[ + self.test_option, + self.suffixes_option, + self.port_name_option, + self.builder_option, + self.build_number_option, + self.results_directory_option, + ]) + + def execute(self, options, args, tool): + self._tool = tool + self._rebaseline_test_and_update_expectations(options) + self._print_expectation_line_changes() + + def _rebaseline_test_and_update_expectations(self, options): + self._baseline_suffix_list = options.suffixes.split(',') + + port_name = options.port_name or self._tool.builders.port_name_for_builder_name(options.builder) + port = self._tool.port_factory.get(port_name) + + if port.reference_files(options.test): + if 'png' in self._baseline_suffix_list: + _log.warning('Cannot rebaseline image result for reftest: %s', options.test) + return + assert self._baseline_suffix_list == ['txt'] + + if options.results_directory: + results_url = 'file://' + options.results_directory + else: + results_url = self._tool.buildbot.results_url(options.builder, build_number=options.build_number) + + for suffix in self._baseline_suffix_list: + self._rebaseline_test(port_name, options.test, suffix, results_url) + self.expectation_line_changes.remove_line( + test=options.test, + port_name=port_name) + + def _save_baseline(self, data, target_baseline): + if not data: + _log.debug('No baseline data to save.') + return + filesystem = self._tool.filesystem + self._tool.filesystem.maybe_make_directory(filesystem.dirname(target_baseline)) + self._tool.filesystem.write_binary_file(target_baseline, data) + + def _rebaseline_test(self, port_name, test_name, suffix, results_url): + """Downloads a baseline file and saves it to the filesystem. + + Args: + port_name: The port that the baseline is for. This determines + the directory that the baseline is saved to. + test_name: The name of the test being rebaselined. + suffix: The baseline file extension (e.g. png); together with the + test name and results_url this determines what file to download. + results_url: Base URL to download the actual result from. + """ + baseline_directory = self._tool.port_factory.get(port_name).baseline_version_dir() + + source_baseline = '%s/%s' % (results_url, self._file_name_for_actual_result(test_name, suffix)) + target_baseline = self._tool.filesystem.join(baseline_directory, self._file_name_for_expected_result(test_name, suffix)) + + _log.debug('Retrieving source %s for target %s.', source_baseline, target_baseline) + self._save_baseline( + self._tool.web.get_binary(source_baseline, return_none_on_404=True), + target_baseline) + + def _print_expectation_line_changes(self): + print json.dumps(self.expectation_line_changes.to_dict())
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_test_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_test_unittest.py new file mode 100644 index 0000000..cb38860 --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_test_unittest.py
@@ -0,0 +1,124 @@ +# 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. + +import optparse + +from webkitpy.common.system.executive_mock import MockExecutive +from webkitpy.common.system.output_capture import OutputCapture +from webkitpy.tool.commands.rebaseline_test import RebaselineTest +from webkitpy.tool.commands.rebaseline_unittest import BaseTestCase + + +class TestRebaselineTest(BaseTestCase): + command_constructor = RebaselineTest + + @staticmethod + def options(**kwargs): + return optparse.Values(dict({ + 'builder': 'MOCK Mac10.11', + 'port_name': None, + 'test': 'userscripts/another-test.html', + 'suffixes': 'txt', + 'results_directory': None, + 'build_number': None + }, **kwargs)) + + def test_rebaseline_test_internal_with_port_that_lacks_buildbot(self): + self.tool.executive = MockExecutive() + + port = self.tool.port_factory.get('test-win-win7') + self._write( + port.host.filesystem.join( + port.layout_tests_dir(), + 'platform/test-win-win10/failures/expected/image-expected.txt'), + 'original win10 result') + + oc = OutputCapture() + try: + options = optparse.Values({ + 'optimize': True, + 'builder': 'MOCK Win10', + 'port_name': None, + 'suffixes': 'txt', + 'verbose': True, + 'test': 'failures/expected/image.html', + 'results_directory': None, + 'build_number': None + }) + oc.capture_output() + self.command.execute(options, [], self.tool) + finally: + out, _, _ = oc.restore_output() + + self.assertMultiLineEqual( + self._read(self.tool.filesystem.join( + port.layout_tests_dir(), + 'platform/test-win-win10/failures/expected/image-expected.txt')), + 'MOCK Web result, convert 404 to None=True') + self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join( + port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt'))) + self.assertMultiLineEqual( + out, '{"remove-lines": [{"test": "failures/expected/image.html", "port_name": "test-win-win10"}]}\n') + + def test_baseline_directory(self): + self.assertMultiLineEqual( + self.command.baseline_directory('MOCK Mac10.11'), + '/test.checkout/LayoutTests/platform/test-mac-mac10.11') + self.assertMultiLineEqual( + self.command.baseline_directory('MOCK Mac10.10'), + '/test.checkout/LayoutTests/platform/test-mac-mac10.10') + self.assertMultiLineEqual( + self.command.baseline_directory('MOCK Trusty'), + '/test.checkout/LayoutTests/platform/test-linux-trusty') + self.assertMultiLineEqual( + self.command.baseline_directory('MOCK Precise'), + '/test.checkout/LayoutTests/platform/test-linux-precise') + + def test_rebaseline_updates_expectations_file_noop(self): + # pylint: disable=protected-access + self._zero_out_test_expectations() + self._write( + self.mac_expectations_path, + ('Bug(B) [ Mac Linux Win7 Debug ] fast/dom/Window/window-postmessage-clone-really-deep-array.html [ Pass ]\n' + 'Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ]\n')) + self._write('fast/dom/Window/window-postmessage-clone-really-deep-array.html', 'Dummy test contents') + self._write('fast/css/large-list-of-rules-crash.html', 'Dummy test contents') + self._write('userscripts/another-test.html', 'Dummy test contents') + + self.command._rebaseline_test_and_update_expectations(self.options(suffixes='png,wav,txt')) + + self.assertItemsEqual(self.tool.web.urls_fetched, + [self.WEB_PREFIX + '/userscripts/another-test-actual.png', + self.WEB_PREFIX + '/userscripts/another-test-actual.wav', + self.WEB_PREFIX + '/userscripts/another-test-actual.txt']) + new_expectations = self._read(self.mac_expectations_path) + self.assertMultiLineEqual( + new_expectations, + ('Bug(B) [ Mac Linux Win7 Debug ] fast/dom/Window/window-postmessage-clone-really-deep-array.html [ Pass ]\n' + 'Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ]\n')) + + def test_rebaseline_test(self): + # pylint: disable=protected-access + self.command._rebaseline_test('test-linux-trusty', 'userscripts/another-test.html', 'txt', self.WEB_PREFIX) + self.assertItemsEqual( + self.tool.web.urls_fetched, [self.WEB_PREFIX + '/userscripts/another-test-actual.txt']) + + def test_rebaseline_test_with_results_directory(self): + # pylint: disable=protected-access + self._write('userscripts/another-test.html', 'test data') + self._write( + self.mac_expectations_path, + ('Bug(x) [ Mac ] userscripts/another-test.html [ Failure ]\n' + 'bug(z) [ Linux ] userscripts/another-test.html [ Failure ]\n')) + self.command._rebaseline_test_and_update_expectations(self.options(results_directory='/tmp')) + self.assertItemsEqual(self.tool.web.urls_fetched, ['file:///tmp/userscripts/another-test-actual.txt']) + + def test_rebaseline_reftest(self): + # pylint: disable=protected-access + self._write('userscripts/another-test.html', 'test data') + self._write('userscripts/another-test-expected.html', 'generic result') + OutputCapture().assert_outputs( + self, self.command._rebaseline_test_and_update_expectations, args=[self.options(suffixes='png')], + expected_logs='Cannot rebaseline image result for reftest: userscripts/another-test.html\n') + self.assertDictEqual(self.command.expectation_line_changes.to_dict(), {'remove-lines': []})
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py index 9819a88..b5b7e1347 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
@@ -9,12 +9,11 @@ from webkitpy.common.net.buildbot import Build from webkitpy.common.net.layout_test_results import LayoutTestResults from webkitpy.common.system.executive_mock import MockExecutive -from webkitpy.common.system.output_capture import OutputCapture from webkitpy.layout_tests.builder_list import BuilderList from webkitpy.layout_tests.port.factory_mock import MockPortFactory from webkitpy.tool.commands.rebaseline import ( - AbstractParallelRebaselineCommand, CopyExistingBaselinesInternal, - Rebaseline, RebaselineExpectations, RebaselineTest, TestBaselineSet + AbstractParallelRebaselineCommand, Rebaseline, RebaselineExpectations, + TestBaselineSet ) from webkitpy.tool.mock_tool import MockWebKitPatch @@ -105,225 +104,6 @@ })) -class TestCopyExistingBaselinesInternal(BaseTestCase): - command_constructor = CopyExistingBaselinesInternal - - def setUp(self): - super(TestCopyExistingBaselinesInternal, self).setUp() - - def options(self, **kwargs): - options_dict = { - 'results_directory': None, - 'suffixes': 'txt', - 'verbose': False, - 'port_name': None, - } - options_dict.update(kwargs) - return optparse.Values(options_dict) - - def baseline_path(self, path_from_layout_test_dir): - port = self.tool.port_factory.get() - return self.tool.filesystem.join(port.layout_tests_dir(), path_from_layout_test_dir) - - # The tests in this class all depend on the fall-back path graph - # that is set up in |TestPort.FALLBACK_PATHS|. - - def test_copy_baseline_mac_newer_to_older_version(self): - # The test-mac-mac10.11 baseline is copied over to the test-mac-mac10.10 - # baseline directory because test-mac-mac10.10 is the "immediate - # predecessor" in the fall-back graph. - self._write( - self.baseline_path('platform/test-mac-mac10.11/failures/expected/image-expected.txt'), - 'original test-mac-mac10.11 result') - self.assertFalse(self.tool.filesystem.exists( - self.baseline_path('platform/test-mac-mac10.10/failures/expected/image-expected.txt'))) - - self.command.execute(self.options(port_name='test-mac-mac10.11', test='failures/expected/image.html'), [], self.tool) - - self.assertEqual( - self._read(self.baseline_path('platform/test-mac-mac10.11/failures/expected/image-expected.txt')), - 'original test-mac-mac10.11 result') - self.assertEqual( - self._read(self.baseline_path('platform/test-mac-mac10.10/failures/expected/image-expected.txt')), - 'original test-mac-mac10.11 result') - - def test_copy_baseline_to_multiple_immediate_predecessors(self): - # The test-win-win10 baseline is copied over to the test-linux-trusty - # and test-win-win7 baseline paths, since both of these are "immediate - # predecessors". - self._write( - self.baseline_path('platform/test-win-win10/failures/expected/image-expected.txt'), - 'original test-win-win10 result') - self.assertFalse(self.tool.filesystem.exists( - self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt'))) - - self.command.execute(self.options(port_name='test-win-win10', test='failures/expected/image.html'), [], self.tool) - - self.assertEqual( - self._read(self.baseline_path('platform/test-win-win10/failures/expected/image-expected.txt')), - 'original test-win-win10 result') - self.assertEqual( - self._read(self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt')), - 'original test-win-win10 result') - self.assertEqual( - self._read(self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt')), - 'original test-win-win10 result') - - def test_no_copy_existing_baseline(self): - # If a baseline exists already for an "immediate prdecessor" baseline - # directory, (e.g. test-linux-trusty), then no "immediate successor" - # baselines (e.g. test-win-win10) are copied over. - self._write( - self.baseline_path('platform/test-win-win10/failures/expected/image-expected.txt'), - 'original test-win-win10 result') - self._write( - self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt'), - 'original test-linux-trusty result') - - self.command.execute(self.options(port_name='test-win-win10', test='failures/expected/image.html'), [], self.tool) - - self.assertEqual( - self._read(self.baseline_path('platform/test-win-win10/failures/expected/image-expected.txt')), - 'original test-win-win10 result') - self.assertEqual( - self._read(self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt')), - 'original test-linux-trusty result') - - def test_no_copy_skipped_test(self): - # If a test is skipped on some platform, no baselines are copied over - # to that directory. In this example, the test is skipped on linux, - # so the test-win-win10 baseline is not copied over. - port = self.tool.port_factory.get('test-win-win10') - self._write( - self.baseline_path('platform/test-win-win10/failures/expected/image-expected.txt'), - 'original test-win-win10 result') - self._write( - port.path_to_generic_test_expectations_file(), - ('[ Win ] failures/expected/image.html [ Failure ]\n' - '[ Linux ] failures/expected/image.html [ Skip ]\n')) - - self.command.execute(self.options(port_name='test-win-win10', test='failures/expected/image.html'), [], self.tool) - - self.assertFalse( - self.tool.filesystem.exists(self.baseline_path('platform/test-linux-trusty/failures/expected/image-expected.txt'))) - - def test_port_for_primary_baseline(self): - self.assertEqual(self.command._port_for_primary_baseline('test-linux-trusty').name(), 'test-linux-trusty') - self.assertEqual(self.command._port_for_primary_baseline('test-mac-mac10.11').name(), 'test-mac-mac10.11') - - def test_port_for_primary_baseline_not_found(self): - with self.assertRaises(Exception): - self.command._port_for_primary_baseline('test-foo-foo4.7') - - -class TestRebaselineTest(BaseTestCase): - command_constructor = RebaselineTest # AKA webkit-patch rebaseline-test-internal - - def setUp(self): - super(TestRebaselineTest, self).setUp() - - @staticmethod - def options(**kwargs): - return optparse.Values(dict({ - 'builder': 'MOCK Mac10.11', - 'port_name': None, - 'test': 'userscripts/another-test.html', - 'suffixes': 'txt', - 'results_directory': None, - 'build_number': None - }, **kwargs)) - - def test_baseline_directory(self): - command = self.command - self.assertMultiLineEqual(command._baseline_directory('MOCK Mac10.11'), - '/test.checkout/LayoutTests/platform/test-mac-mac10.11') - self.assertMultiLineEqual(command._baseline_directory('MOCK Mac10.10'), - '/test.checkout/LayoutTests/platform/test-mac-mac10.10') - self.assertMultiLineEqual(command._baseline_directory('MOCK Trusty'), - '/test.checkout/LayoutTests/platform/test-linux-trusty') - self.assertMultiLineEqual(command._baseline_directory('MOCK Precise'), - '/test.checkout/LayoutTests/platform/test-linux-precise') - - def test_rebaseline_updates_expectations_file_noop(self): - self._zero_out_test_expectations() - self._write( - self.mac_expectations_path, - ('Bug(B) [ Mac Linux Win7 Debug ] fast/dom/Window/window-postmessage-clone-really-deep-array.html [ Pass ]\n' - 'Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ]\n')) - self._write('fast/dom/Window/window-postmessage-clone-really-deep-array.html', 'Dummy test contents') - self._write('fast/css/large-list-of-rules-crash.html', 'Dummy test contents') - self._write('userscripts/another-test.html', 'Dummy test contents') - - self.command._rebaseline_test_and_update_expectations(self.options(suffixes='png,wav,txt')) - - self.assertItemsEqual(self.tool.web.urls_fetched, - [self.WEB_PREFIX + '/userscripts/another-test-actual.png', - self.WEB_PREFIX + '/userscripts/another-test-actual.wav', - self.WEB_PREFIX + '/userscripts/another-test-actual.txt']) - new_expectations = self._read(self.mac_expectations_path) - self.assertMultiLineEqual( - new_expectations, - ('Bug(B) [ Mac Linux Win7 Debug ] fast/dom/Window/window-postmessage-clone-really-deep-array.html [ Pass ]\n' - 'Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ]\n')) - - def test_rebaseline_test(self): - self.command._rebaseline_test('test-linux-trusty', 'userscripts/another-test.html', 'txt', self.WEB_PREFIX) - self.assertItemsEqual(self.tool.web.urls_fetched, [self.WEB_PREFIX + '/userscripts/another-test-actual.txt']) - - def test_rebaseline_test_with_results_directory(self): - self._write('userscripts/another-test.html', 'test data') - self._write( - self.mac_expectations_path, - ('Bug(x) [ Mac ] userscripts/another-test.html [ Failure ]\n' - 'bug(z) [ Linux ] userscripts/another-test.html [ Failure ]\n')) - self.command._rebaseline_test_and_update_expectations(self.options(results_directory='/tmp')) - self.assertItemsEqual(self.tool.web.urls_fetched, ['file:///tmp/userscripts/another-test-actual.txt']) - - def test_rebaseline_reftest(self): - self._write('userscripts/another-test.html', 'test data') - self._write('userscripts/another-test-expected.html', 'generic result') - OutputCapture().assert_outputs( - self, self.command._rebaseline_test_and_update_expectations, args=[self.options(suffixes='png')], - expected_logs='Cannot rebaseline image result for reftest: userscripts/another-test.html\n') - self.assertDictEqual(self.command.expectation_line_changes.to_dict(), {'remove-lines': []}) - - def test_rebaseline_test_internal_with_port_that_lacks_buildbot(self): - self.tool.executive = MockExecutive() - - port = self.tool.port_factory.get('test-win-win7') - self._write( - port.host.filesystem.join( - port.layout_tests_dir(), - 'platform/test-win-win10/failures/expected/image-expected.txt'), - 'original win10 result') - - oc = OutputCapture() - try: - options = optparse.Values({ - 'optimize': True, - 'builder': 'MOCK Win10', - 'port_name': None, - 'suffixes': 'txt', - 'verbose': True, - 'test': 'failures/expected/image.html', - 'results_directory': None, - 'build_number': None - }) - oc.capture_output() - self.command.execute(options, [], self.tool) - finally: - out, _, _ = oc.restore_output() - - self.assertMultiLineEqual( - self._read(self.tool.filesystem.join( - port.layout_tests_dir(), - 'platform/test-win-win10/failures/expected/image-expected.txt')), - 'MOCK Web result, convert 404 to None=True') - self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join( - port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt'))) - self.assertMultiLineEqual( - out, '{"remove-lines": [{"test": "failures/expected/image.html", "port_name": "test-win-win10"}]}\n') - class TestAbstractParallelRebaselineCommand(BaseTestCase): command_constructor = AbstractParallelRebaselineCommand
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/webkit_patch.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/webkit_patch.py index 0968e2c..267ffed 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/webkit_patch.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/webkit_patch.py
@@ -29,10 +29,11 @@ """Webkit-patch is a tool with multiple sub-commands with different purposes. -Historically, it had commands related to dealing with bugzilla, and posting -and comitting patches to WebKit. More recently, it has commands for printing -expectations, fetching new test baselines, starting a commit-announcer IRC bot, -etc. These commands don't necessarily have anything to do with each other. +Historically, it had commands related to dealing with bugzilla and posting +and committing patches to WebKit. More recently, it has commands for printing +expectations, fetching new test baselines, etc. + +These commands don't necessarily have anything to do with each other. """ import logging @@ -43,6 +44,7 @@ from webkitpy.tool.commands.analyze_baselines import AnalyzeBaselines from webkitpy.tool.commands.auto_rebaseline import AutoRebaseline from webkitpy.tool.commands.command import HelpPrintingOptionParser +from webkitpy.tool.commands.copy_existing_baselines import CopyExistingBaselines from webkitpy.tool.commands.flaky_tests import FlakyTests from webkitpy.tool.commands.help_command import HelpCommand from webkitpy.tool.commands.layout_tests_server import LayoutTestsServer @@ -51,12 +53,11 @@ from webkitpy.tool.commands.queries import CrashLog from webkitpy.tool.commands.queries import PrintBaselines from webkitpy.tool.commands.queries import PrintExpectations -from webkitpy.tool.commands.rebaseline import CopyExistingBaselinesInternal from webkitpy.tool.commands.rebaseline import Rebaseline from webkitpy.tool.commands.rebaseline import RebaselineExpectations -from webkitpy.tool.commands.rebaseline import RebaselineTest from webkitpy.tool.commands.rebaseline_cl import RebaselineCL from webkitpy.tool.commands.rebaseline_server import RebaselineServer +from webkitpy.tool.commands.rebaseline_test import RebaselineTest _log = logging.getLogger(__name__) @@ -81,7 +82,7 @@ self.commands = [ AnalyzeBaselines(), AutoRebaseline(), - CopyExistingBaselinesInternal(), + CopyExistingBaselines(), CrashLog(), FlakyTests(), LayoutTestsServer(),
diff --git a/tools/ipc_fuzzer/message_replay/replay_process.cc b/tools/ipc_fuzzer/message_replay/replay_process.cc index 10173728..325382e 100644 --- a/tools/ipc_fuzzer/message_replay/replay_process.cc +++ b/tools/ipc_fuzzer/message_replay/replay_process.cc
@@ -17,7 +17,6 @@ #include "content/public/common/content_switches.h" #include "content/public/common/mojo_channel_switches.h" #include "ipc/ipc_channel_mojo.h" -#include "ipc/ipc_descriptors.h" #include "mojo/edk/embedder/configuration.h" #include "mojo/edk/embedder/embedder.h" #include "mojo/edk/embedder/incoming_broker_client_invitation.h"
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index dcfc5ee0..1bf8fe4 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -2046,6 +2046,17 @@ <int value="5" label="Permission denied"/> </enum> +<enum name="BackgroundTaskId" type="int"> + <int value="0" label="Test task (should not appear)"/> + <int value="1" label="Omaha"/> + <int value="2" label="GCM Background Task"/> + <int value="3" label="Notification service task"/> + <int value="4" label="Webview minidump uploading task"/> + <int value="5" label="Chrome minidump uploading task"/> + <int value="6" label="Offlining pages task"/> + <int value="7" label="Offline page prefetch task"/> +</enum> + <enum name="BackgroundTracingState" type="int"> <int value="0" label="Scenario activation requested"/> <int value="1" label="Scenario successfully activated"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index ceeaeb1..b398515 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -558,6 +558,48 @@ </summary> </histogram> +<histogram name="Android.BackgroundTaskScheduler.TaskCanceled" + enum="BackgroundTaskId"> + <owner>fgorski@chromium.org</owner> + <owner>nyquist@chromium.org</owner> + <summary>Records that a specific background task has been canceled.</summary> +</histogram> + +<histogram name="Android.BackgroundTaskScheduler.TaskScheduled.Failure" + enum="BackgroundTaskId"> + <owner>fgorski@chromium.org</owner> + <owner>nyquist@chromium.org</owner> + <summary> + Records that a specific background task has failed to be scheduled. + </summary> +</histogram> + +<histogram name="Android.BackgroundTaskScheduler.TaskScheduled.Success" + enum="BackgroundTaskId"> + <owner>fgorski@chromium.org</owner> + <owner>nyquist@chromium.org</owner> + <summary> + Records that a specific background task has been successfully scheduled. + </summary> +</histogram> + +<histogram name="Android.BackgroundTaskScheduler.TaskStarted" + enum="BackgroundTaskId"> + <owner>fgorski@chromium.org</owner> + <owner>nyquist@chromium.org</owner> + <summary>Records that a specific background task has been started.</summary> +</histogram> + +<histogram name="Android.BackgroundTaskScheduler.TaskStopped" + enum="BackgroundTaskId"> + <owner>fgorski@chromium.org</owner> + <owner>nyquist@chromium.org</owner> + <summary> + Records that a specific background task has been stopped by Background Task + Scheduler before it was able to complete itself. + </summary> +</histogram> + <histogram name="Android.ChromeHome.DurationOpen" units="ms"> <owner>mdjones@chromium.org</owner> <owner>twellington@chromium.org</owner> @@ -46794,6 +46836,33 @@ </summary> </histogram> +<histogram name="Omnibox.ClipboardSuggestionShownNumTimes"> + <owner>mpearson@chromium.org</owner> + <summary> + Recorded every time the omnibox is focussed and a recent URL from the user's + clipboard is suggested. The number emitted is the number of times the URL + has been suggested within the same session including the current time. Thus, + the third time it is shown, we'll emit a three to this histogram, and this + histogram will have previously seen emits of one and two. If the clipboard + content was the same during a previous run of Chrome and this URL was + suggested during that run, those impressions are not counted. Also, if the + clipboard content changes during a particular run of Chrome to another URL, + the omnibox is focused and that URL is suggested, then content changes back + and Chrome starts suggesting the older URL again, the counts start again + from scratch. Chrome only remembers the number of times the URL was shown + consecutively. + + This value is useful to compare with the number of times a clipboard + suggestion has been shown when it is clicked. This value can be obtained + from OmniboxEvent records in which the selected suggestion is from Clipboard + provider. In those cases, look in the Clipboard provider's ProviderInfo + field for |times_returned_results_in_session|. Note that at the time of + this writing that OmniboxEvent logs aren't recorded in incognito whereas + histograms are. Thus, the total counts will not be comparable, though the + distributions should be. + </summary> +</histogram> + <histogram name="Omnibox.ClipboardSuggestionShownWithCurrentURL" enum="BooleanPresent"> <owner>mpearson@chromium.org</owner> @@ -66228,6 +66297,26 @@ </summary> </histogram> +<histogram name="Search.ContextualSearchTapLongWordSeen" + enum="ContextualSearchResultsSeen"> + <owner>donnd@chromium.org</owner> + <owner>twellington@chromium.org</owner> + <summary> + Whether results were seen for a Tap that was on a word considered long. + Recorded when the UX is hidden. Implemented for Android. + </summary> +</histogram> + +<histogram name="Search.ContextualSearchTapShortWordSeen" + enum="ContextualSearchResultsSeen"> + <owner>donnd@chromium.org</owner> + <owner>twellington@chromium.org</owner> + <summary> + Whether results were seen for a Tap that was on a word considered short. + Recorded when the UX is hidden. Implemented for Android. + </summary> +</histogram> + <histogram name="Search.ContextualSearchTapsSinceOpenDecided" units="taps"> <owner>donnd@chromium.org</owner> <owner>twellington@chromium.org</owner>
diff --git a/tools/perf/benchmarks/dromaeo.py b/tools/perf/benchmarks/dromaeo.py index a9a5fbbb..551db0c 100644 --- a/tools/perf/benchmarks/dromaeo.py +++ b/tools/perf/benchmarks/dromaeo.py
@@ -141,6 +141,11 @@ def Name(cls): return 'dromaeo.domcoreattr' + def GetExpectations(self): + class StoryExpectations(story.expectations.StoryExpectations): + def SetExpectations(self): + pass # http://dromaeo.com?dom-attr not disabled. + return StoryExpectations() @benchmark.Owner(emails=['yukishiino@chromium.org', 'bashi@chromium.org', @@ -157,6 +162,12 @@ def Name(cls): return 'dromaeo.domcoremodify' + def GetExpectations(self): + class StoryExpectations(story.expectations.StoryExpectations): + def SetExpectations(self): + pass # http://dromaeo.com?dom-modify not disabled. + return StoryExpectations() + @benchmark.Owner(emails=['yukishiino@chromium.org', 'bashi@chromium.org', @@ -173,6 +184,12 @@ def Name(cls): return 'dromaeo.domcorequery' + def GetExpectations(self): + class StoryExpectations(story.expectations.StoryExpectations): + def SetExpectations(self): + pass # http://dromaeo.com?dom-query not disabled. + return StoryExpectations() + @benchmark.Owner(emails=['yukishiino@chromium.org', 'bashi@chromium.org', @@ -188,3 +205,9 @@ @classmethod def Name(cls): return 'dromaeo.domcoretraverse' + + def GetExpectations(self): + class StoryExpectations(story.expectations.StoryExpectations): + def SetExpectations(self): + pass # http://dromaeo.com?dom-traverse not disabled. + return StoryExpectations()
diff --git a/ui/app_list/views/app_list_item_view.cc b/ui/app_list/views/app_list_item_view.cc index 488aef3..b3053fb 100644 --- a/ui/app_list/views/app_list_item_view.cc +++ b/ui/app_list/views/app_list_item_view.cc
@@ -476,13 +476,13 @@ title_->SetSubpixelRenderingEnabled(enable_aa); if (enable_aa) { title_->SetBackgroundColor(app_list::kLabelBackgroundColor); - title_->set_background(views::Background::CreateSolidBackground( - app_list::kLabelBackgroundColor)); + title_->SetBackground( + views::CreateSolidBackground(app_list::kLabelBackgroundColor)); } else { // In other cases, keep the background transparent to ensure correct // interactions with animations. This will temporarily disable subpixel AA. title_->SetBackgroundColor(0); - title_->set_background(NULL); + title_->SetBackground(nullptr); } title_->SchedulePaint(); }
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc index c7971ee..420aede 100644 --- a/ui/app_list/views/search_box_view.cc +++ b/ui/app_list/views/search_box_view.cc
@@ -136,7 +136,7 @@ views::ImageButton::ALIGN_MIDDLE); SetBackButtonLabel(false); content_container_->AddChildView(back_button_); - content_container_->set_background(new SearchBoxBackground()); + content_container_->SetBackground(base::MakeUnique<SearchBoxBackground>()); views::BoxLayout* layout = new views::BoxLayout(views::BoxLayout::kHorizontal, kPadding, 0,
diff --git a/ui/app_list/views/search_result_answer_card_view.cc b/ui/app_list/views/search_result_answer_card_view.cc index 5991853..43ac790 100644 --- a/ui/app_list/views/search_result_answer_card_view.cc +++ b/ui/app_list/views/search_result_answer_card_view.cc
@@ -62,15 +62,11 @@ private: void UpdateBackgroundColor() { - views::Background* background = nullptr; + if (selected_) + SetBackground(views::CreateSolidBackground(kSelectedColor)); + else if (state() == STATE_HOVERED || state() == STATE_PRESSED) + SetBackground(views::CreateSolidBackground(kHighlightedColor)); - if (selected_) { - background = views::Background::CreateSolidBackground(kSelectedColor); - } else if (state() == STATE_HOVERED || state() == STATE_PRESSED) { - background = views::Background::CreateSolidBackground(kHighlightedColor); - } - - set_background(background); SchedulePaint(); }
diff --git a/ui/app_list/views/search_result_list_view.cc b/ui/app_list/views/search_result_list_view.cc index 3446a2a..44a58346 100644 --- a/ui/app_list/views/search_result_list_view.cc +++ b/ui/app_list/views/search_result_list_view.cc
@@ -46,8 +46,8 @@ results_container_->AddChildView(new SearchResultView(this)); AddChildView(results_container_); - auto_launch_indicator_->set_background( - views::Background::CreateSolidBackground(kTimeoutIndicatorColor)); + auto_launch_indicator_->SetBackground( + views::CreateSolidBackground(kTimeoutIndicatorColor)); auto_launch_indicator_->SetVisible(false); AddChildView(auto_launch_indicator_);
diff --git a/ui/app_list/views/search_result_page_view.cc b/ui/app_list/views/search_result_page_view.cc index 61d8fa5..20ab5e4 100644 --- a/ui/app_list/views/search_result_page_view.cc +++ b/ui/app_list/views/search_result_page_view.cc
@@ -40,8 +40,8 @@ SetBorder(base::MakeUnique<views::ShadowBorder>( GetShadowForZHeight(kSearchResultZHeight))); SetLayoutManager(new views::FillLayout()); - content_view->set_background( - views::Background::CreateSolidBackground(kCardBackgroundColor)); + content_view->SetBackground( + views::CreateSolidBackground(kCardBackgroundColor)); AddChildView(content_view); }
diff --git a/ui/app_list/views/speech_view.cc b/ui/app_list/views/speech_view.cc index 0625e615..419b9f4 100644 --- a/ui/app_list/views/speech_view.cc +++ b/ui/app_list/views/speech_view.cc
@@ -121,8 +121,7 @@ // actually has a single child of 'container' which has white background and // contains all components. views::View* container = new views::View(); - container->set_background( - views::Background::CreateSolidBackground(SK_ColorWHITE)); + container->SetBackground(views::CreateSolidBackground(SK_ColorWHITE)); const gfx::ImageSkia& logo_image = delegate_->GetSpeechUI()->logo(); if (!logo_image.isNull()) {
diff --git a/ui/app_list/views/start_page_view.cc b/ui/app_list/views/start_page_view.cc index b41a9ffa..7206b6f6 100644 --- a/ui/app_list/views/start_page_view.cc +++ b/ui/app_list/views/start_page_view.cc
@@ -62,7 +62,7 @@ const std::string& custom_launcher_page_name) : selected_(false), custom_launcher_page_name_(custom_launcher_page_name) { - set_background(views::Background::CreateSolidBackground(kSelectedColor)); + SetBackground(views::CreateSolidBackground(kSelectedColor)); } ~CustomLauncherPageBackgroundView() override {} @@ -133,8 +133,7 @@ : contents_view_(contents_view), view_delegate_(view_delegate), all_apps_button_(all_apps_button) { - set_background( - views::Background::CreateSolidBackground(kLabelBackgroundColor)); + SetBackground(views::CreateSolidBackground(kLabelBackgroundColor)); all_apps_button_->SetHoverStyle(TileItemView::HOVER_STYLE_ANIMATE_SHADOW); all_apps_button_->SetParentBackgroundColor(kLabelBackgroundColor); CreateAppsGrid(kNumStartPageTiles);
diff --git a/ui/app_list/views/tile_item_view.cc b/ui/app_list/views/tile_item_view.cc index d2beee07..29da844 100644 --- a/ui/app_list/views/tile_item_view.cc +++ b/ui/app_list/views/tile_item_view.cc
@@ -114,12 +114,12 @@ } void TileItemView::UpdateBackgroundColor() { - views::Background* background = nullptr; + std::unique_ptr<views::Background> background; SkColor background_color = parent_background_color_; if (selected_) { background_color = kSelectedColor; - background = views::Background::CreateSolidBackground(background_color); + background = views::CreateSolidBackground(background_color); } else if (image_shadow_animator_) { if (state() == STATE_HOVERED || state() == STATE_PRESSED) image_shadow_animator_->animation()->Show(); @@ -127,7 +127,7 @@ image_shadow_animator_->animation()->Hide(); } else if (state() == STATE_HOVERED || state() == STATE_PRESSED) { background_color = kHighlightedColor; - background = views::Background::CreateSolidBackground(background_color); + background = views::CreateSolidBackground(background_color); } // Tell the label what color it will be drawn onto. It will use whether the @@ -135,7 +135,7 @@ // rendering. Does not actually set the label's background color. title_->SetBackgroundColor(background_color); - set_background(background); + SetBackground(std::move(background)); SchedulePaint(); }
diff --git a/ui/arc/notification/arc_notification_content_view.cc b/ui/arc/notification/arc_notification_content_view.cc index 312dcb9..a8c8254 100644 --- a/ui/arc/notification/arc_notification_content_view.cc +++ b/ui/arc/notification/arc_notification_content_view.cc
@@ -213,10 +213,10 @@ ArcNotificationContentView* owner) : message_center::PaddedButton(owner), owner_(owner) { if (owner_->item_) { - set_background(views::Background::CreateSolidBackground( + SetBackground(views::CreateSolidBackground( GetControlButtonBackgroundColor(owner_->item_->GetShownContents()))); } else { - set_background(views::Background::CreateSolidBackground( + SetBackground(views::CreateSolidBackground( message_center::kControlButtonBackgroundColor)); } } @@ -720,13 +720,12 @@ const SkColor current_color = gfx::Tween::ColorValueBetween( animation->GetCurrentValue(), start, target); if (settings_button_) { - settings_button_->set_background( - views::Background::CreateSolidBackground(current_color)); + settings_button_->SetBackground( + views::CreateSolidBackground(current_color)); settings_button_->SchedulePaint(); } if (close_button_) { - close_button_->set_background( - views::Background::CreateSolidBackground(current_color)); + close_button_->SetBackground(views::CreateSolidBackground(current_color)); close_button_->SchedulePaint(); } }
diff --git a/ui/arc/notification/arc_notification_view_unittest.cc b/ui/arc/notification/arc_notification_view_unittest.cc index 81f8b477..3c81560 100644 --- a/ui/arc/notification/arc_notification_view_unittest.cc +++ b/ui/arc/notification/arc_notification_view_unittest.cc
@@ -36,7 +36,7 @@ public: TestNotificationContentsView() { SetFocusBehavior(FocusBehavior::ALWAYS); - set_background(views::Background::CreateSolidBackground(kBackgroundColor)); + SetBackground(views::CreateSolidBackground(kBackgroundColor)); SetPreferredSize(gfx::Size(100, 100)); } ~TestNotificationContentsView() override = default;
diff --git a/ui/base/cocoa/hover_button.h b/ui/base/cocoa/hover_button.h index 1c5923f..b25e29aa 100644 --- a/ui/base/cocoa/hover_button.h +++ b/ui/base/cocoa/hover_button.h
@@ -27,10 +27,15 @@ @private // Tracking area for button mouseover states. Nil if not enabled. ui::ScopedCrTrackingArea trackingArea_; + BOOL mouseDown_; } @property(nonatomic) HoverState hoverState; +// Common initialization called from initWithFrame: and awakeFromNib. +// Subclassers should call [super commonInit]. +- (void)commonInit; + // Text that would be announced by screen readers. - (void)setAccessibilityTitle:(NSString*)accessibilityTitle;
diff --git a/ui/base/cocoa/hover_button.mm b/ui/base/cocoa/hover_button.mm index e763c0cd..ebcd582 100644 --- a/ui/base/cocoa/hover_button.mm +++ b/ui/base/cocoa/hover_button.mm
@@ -10,14 +10,16 @@ - (id)initWithFrame:(NSRect)frameRect { if ((self = [super initWithFrame:frameRect])) { - [self setTrackingEnabled:YES]; - hoverState_ = kHoverStateNone; - [self updateTrackingAreas]; + [self commonInit]; } return self; } - (void)awakeFromNib { + [self commonInit]; +} + +- (void)commonInit { [self setTrackingEnabled:YES]; self.hoverState = kHoverStateNone; [self updateTrackingAreas]; @@ -33,30 +35,53 @@ self.hoverState = kHoverStateMouseOver; } +- (void)mouseMoved:(NSEvent*)theEvent { + [self checkImageState]; +} + - (void)mouseExited:(NSEvent*)theEvent { if (trackingArea_.get()) self.hoverState = kHoverStateNone; } -- (void)mouseMoved:(NSEvent*)theEvent { - [self checkImageState]; -} - - (void)mouseDown:(NSEvent*)theEvent { + mouseDown_ = YES; self.hoverState = kHoverStateMouseDown; + // The hover button needs to hold onto itself here for a bit. Otherwise, - // it can be freed while |super mouseDown:| is in its loop, and the - // |checkImageState| call will crash. + // it can be freed while in the tracking loop below. // http://crbug.com/28220 base::scoped_nsobject<HoverButton> myself([self retain]); - [super mouseDown:theEvent]; - // We need to check the image state after the mouseDown event loop finishes. - // It's possible that we won't get a mouseExited event if the button was - // moved under the mouse during tab resize, instead of the mouse moving over - // the button. - // http://crbug.com/31279 - [self checkImageState]; + // Begin tracking the mouse. + if ([theEvent type] == NSLeftMouseDown) { + NSWindow* window = [self window]; + NSEvent* nextEvent = nil; + + // For the tracking loop ignore key events so that they don't pile up in + // the queue and get processed after the user releases the mouse. + const NSEventMask eventMask = (NSLeftMouseDraggedMask | NSLeftMouseUpMask | + NSKeyDownMask | NSKeyUpMask); + + while ((nextEvent = [window nextEventMatchingMask:eventMask])) { + // Update the image state, which will change if the user moves the mouse + // into or out of the button. + [self checkImageState]; + + if ([nextEvent type] == NSLeftMouseUp) { + break; + } + } + } + + // If the mouse is still over the button, it means the user clicked the + // button. + if (self.hoverState == kHoverStateMouseDown) { + [self performClick:nil]; + } + + // Clean up. + mouseDown_ = NO; } - (void)setAccessibilityTitle:(NSString*)accessibilityTitle { @@ -108,8 +133,12 @@ // Update the button's state if the button has moved. NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream]; mouseLoc = [self convertPoint:mouseLoc fromView:nil]; - self.hoverState = NSPointInRect(mouseLoc, [self bounds]) ? - kHoverStateMouseOver : kHoverStateNone; + BOOL mouseInBounds = NSPointInRect(mouseLoc, [self bounds]); + if (mouseDown_ && mouseInBounds) { + self.hoverState = kHoverStateMouseDown; + } else { + self.hoverState = mouseInBounds ? kHoverStateMouseOver : kHoverStateNone; + } } - (void)setHoverState:(HoverState)state {
diff --git a/ui/chromeos/ime/candidate_view.cc b/ui/chromeos/ime/candidate_view.cc index 0544fb7e..26e78c0 100644 --- a/ui/chromeos/ime/candidate_view.cc +++ b/ui/chromeos/ime/candidate_view.cc
@@ -86,8 +86,8 @@ 0x40); SkColor transparent_blakish = color_utils::AlphaBlend( SK_ColorTRANSPARENT, blackish, 0xE0); - shortcut_label->set_background( - views::Background::CreateSolidBackground(transparent_blakish)); + shortcut_label->SetBackground( + views::CreateSolidBackground(transparent_blakish)); } shortcut_label->SetElideBehavior(gfx::NO_ELIDE); @@ -163,9 +163,8 @@ if (orientation == ui::CandidateWindow::VERTICAL) { infolist_icon_ = new views::View; - infolist_icon_->set_background( - views::Background::CreateSolidBackground(theme.GetSystemColor( - ui::NativeTheme::kColorId_FocusedBorderColor))); + infolist_icon_->SetBackground(views::CreateSolidBackground( + theme.GetSystemColor(ui::NativeTheme::kColorId_FocusedBorderColor))); AddChildView(infolist_icon_); } } @@ -204,9 +203,8 @@ highlighted_ = highlighted; if (highlighted) { ui::NativeTheme* theme = GetNativeTheme(); - set_background( - views::Background::CreateSolidBackground(theme->GetSystemColor( - ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused))); + SetBackground(views::CreateSolidBackground(theme->GetSystemColor( + ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused))); SetBorder(views::CreateSolidBorder( 1, theme->GetSystemColor(ui::NativeTheme::kColorId_FocusedBorderColor))); @@ -219,7 +217,7 @@ view->SetHighlighted(false); } } else { - set_background(NULL); + SetBackground(nullptr); SetBorder(views::CreateEmptyBorder(1, 1, 1, 1)); } SchedulePaint();
diff --git a/ui/chromeos/ime/candidate_window_view.cc b/ui/chromeos/ime/candidate_window_view.cc index 9029c6c..77dd05a19 100644 --- a/ui/chromeos/ime/candidate_window_view.cc +++ b/ui/chromeos/ime/candidate_window_view.cc
@@ -107,7 +107,7 @@ SetLayoutManager(new views::FillLayout()); AddChildView(label_); - set_background(views::Background::CreateSolidBackground( + SetBackground(views::CreateSolidBackground( color_utils::AlphaBlend(SK_ColorBLACK, GetNativeTheme()->GetSystemColor( ui::NativeTheme::kColorId_WindowBackground),
diff --git a/ui/chromeos/ime/infolist_window.cc b/ui/chromeos/ime/infolist_window.cc index 3a85d8ed..6ca2381 100644 --- a/ui/chromeos/ime/infolist_window.cc +++ b/ui/chromeos/ime/infolist_window.cc
@@ -152,14 +152,13 @@ void InfolistEntryView::UpdateBackground() { if (entry_.highlighted) { - set_background( - views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused))); + SetBackground(views::CreateSolidBackground(GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused))); SetBorder(views::CreateSolidBorder( 1, GetNativeTheme()->GetSystemColor( ui::NativeTheme::kColorId_FocusedBorderColor))); } else { - set_background(NULL); + SetBackground(nullptr); SetBorder(views::CreateEmptyBorder(1, 1, 1, 1)); } SchedulePaint(); @@ -179,9 +178,8 @@ set_accept_events(false); set_margins(gfx::Insets()); - set_background( - views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_WindowBackground))); + SetBackground(views::CreateSolidBackground(GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_WindowBackground))); SetBorder(views::CreateSolidBorder( 1, GetNativeTheme()->GetSystemColor( ui::NativeTheme::kColorId_MenuBorderColor))); @@ -194,7 +192,7 @@ caption_label->SetEnabledColor(GetNativeTheme()->GetSystemColor( ui::NativeTheme::kColorId_LabelEnabledColor)); caption_label->SetBorder(views::CreateEmptyBorder(2, 2, 2, 2)); - caption_label->set_background(views::Background::CreateSolidBackground( + caption_label->SetBackground(views::CreateSolidBackground( color_utils::AlphaBlend(SK_ColorBLACK, GetNativeTheme()->GetSystemColor( ui::NativeTheme::kColorId_WindowBackground),
diff --git a/ui/login/account_picker/md_user_pod_row.js b/ui/login/account_picker/md_user_pod_row.js index 5ba3286..9316cc9 100644 --- a/ui/login/account_picker/md_user_pod_row.js +++ b/ui/login/account_picker/md_user_pod_row.js
@@ -3122,6 +3122,7 @@ this.activatedPod_ = undefined; this.lastFocusedPod_ = undefined; this.mainPod_ = undefined; + this.smallPodsContainer.innerHTML = ''; // Switch off animation Oobe.getInstance().toggleClass('flying-pods', false);
diff --git a/ui/message_center/views/message_center_button_bar.cc b/ui/message_center/views/message_center_button_bar.cc index 5b8ef31..19538d1 100644 --- a/ui/message_center/views/message_center_button_bar.cc +++ b/ui/message_center/views/message_center_button_bar.cc
@@ -83,8 +83,7 @@ settings_button_(NULL), quiet_mode_button_(NULL) { SetPaintToLayer(); - set_background( - views::Background::CreateSolidBackground(kMessageCenterBackgroundColor)); + SetBackground(views::CreateSolidBackground(kMessageCenterBackgroundColor)); ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
diff --git a/ui/message_center/views/message_center_view.cc b/ui/message_center/views/message_center_view.cc index d760442..5a183bf 100644 --- a/ui/message_center/views/message_center_view.cc +++ b/ui/message_center/views/message_center_view.cc
@@ -80,8 +80,7 @@ focus_manager_(nullptr) { message_center_->AddObserver(this); set_notify_enter_exit_on_child(true); - set_background(views::Background::CreateSolidBackground( - kMessageCenterBackgroundColor)); + SetBackground(views::CreateSolidBackground(kMessageCenterBackgroundColor)); NotifierSettingsProvider* notifier_settings_provider = message_center_->GetNotifierSettingsProvider(); @@ -96,8 +95,8 @@ scroller_->ClipHeightTo(kMinScrollViewHeight, max_height - button_height); scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false)); scroller_->SetHorizontalScrollBar(new views::OverlayScrollBar(true)); - scroller_->set_background( - views::Background::CreateSolidBackground(kMessageCenterBackgroundColor)); + scroller_->SetBackground( + views::CreateSolidBackground(kMessageCenterBackgroundColor)); scroller_->SetPaintToLayer(); scroller_->layer()->SetFillsBoundsOpaquely(false);
diff --git a/ui/message_center/views/message_list_view.cc b/ui/message_center/views/message_list_view.cc index 127f3fd..a5c77aa 100644 --- a/ui/message_center/views/message_list_view.cc +++ b/ui/message_center/views/message_list_view.cc
@@ -44,8 +44,7 @@ // because of the shadow of message view. Use an empty border instead // to provide this margin. gfx::Insets shadow_insets = MessageView::GetShadowInsets(); - set_background( - views::Background::CreateSolidBackground(kMessageCenterBackgroundColor)); + SetBackground(views::CreateSolidBackground(kMessageCenterBackgroundColor)); SetBorder(views::CreateEmptyBorder( kMarginBetweenItems - shadow_insets.top(), /* top */ kMarginBetweenItems - shadow_insets.left(), /* left */
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc index a951c10a..ac2dc30 100644 --- a/ui/message_center/views/message_view.cc +++ b/ui/message_center/views/message_view.cc
@@ -73,8 +73,8 @@ // Create the opaque background that's above the view's shadow. background_view_ = new views::View(); - background_view_->set_background( - views::Background::CreateSolidBackground(kNotificationBackgroundColor)); + background_view_->SetBackground( + views::CreateSolidBackground(kNotificationBackgroundColor)); AddChildView(background_view_); focus_painter_ = views::Painter::CreateSolidFocusPainter(
diff --git a/ui/message_center/views/notification_button.cc b/ui/message_center/views/notification_button.cc index 4fd8ac6a..c42b6d7 100644 --- a/ui/message_center/views/notification_button.cc +++ b/ui/message_center/views/notification_button.cc
@@ -21,8 +21,7 @@ SetFocusForPlatform(); // Create a background so that it does not change when the MessageView // background changes to show touch feedback - set_background(views::Background::CreateSolidBackground( - kNotificationBackgroundColor)); + SetBackground(views::CreateSolidBackground(kNotificationBackgroundColor)); set_notify_enter_exit_on_child(true); SetLayoutManager( new views::BoxLayout(views::BoxLayout::kHorizontal, @@ -95,11 +94,10 @@ void NotificationButton::StateChanged(ButtonState old_state) { if (state() == STATE_HOVERED || state() == STATE_PRESSED) { - set_background(views::Background::CreateSolidBackground( + SetBackground(views::CreateSolidBackground( message_center::kHoveredButtonBackgroundColor)); } else { - set_background(views::Background::CreateSolidBackground( - kNotificationBackgroundColor)); + SetBackground(views::CreateSolidBackground(kNotificationBackgroundColor)); } }
diff --git a/ui/message_center/views/notification_view.cc b/ui/message_center/views/notification_view.cc index 89fec2e..f65b597 100644 --- a/ui/message_center/views/notification_view.cc +++ b/ui/message_center/views/notification_view.cc
@@ -623,8 +623,8 @@ image_container_ = new views::View(); image_container_->SetLayoutManager(new views::FillLayout()); - image_container_->set_background(views::Background::CreateSolidBackground( - message_center::kImageBackgroundColor)); + image_container_->SetBackground( + views::CreateSolidBackground(message_center::kImageBackgroundColor)); image_view_ = new message_center::ProportionalImageView(ideal_size); image_container_->AddChildView(image_view_);
diff --git a/ui/message_center/views/notifier_settings_view.cc b/ui/message_center/views/notifier_settings_view.cc index 3e541c7..f2fc81f 100644 --- a/ui/message_center/views/notifier_settings_view.cc +++ b/ui/message_center/views/notifier_settings_view.cc
@@ -424,8 +424,7 @@ provider_->AddObserver(this); SetFocusBehavior(FocusBehavior::ALWAYS); - set_background( - views::Background::CreateSolidBackground(kMessageCenterBackgroundColor)); + SetBackground(views::CreateSolidBackground(kMessageCenterBackgroundColor)); SetPaintToLayer(); title_label_ = new views::Label(
diff --git a/ui/message_center/views/padded_button.cc b/ui/message_center/views/padded_button.cc index 08e3e6f..b81a3f4 100644 --- a/ui/message_center/views/padded_button.cc +++ b/ui/message_center/views/padded_button.cc
@@ -22,8 +22,7 @@ SetFocusPainter(views::Painter::CreateSolidFocusPainter( kFocusBorderColor, gfx::Insets(1, 2, 2, 2))); - set_background( - views::Background::CreateSolidBackground(kControlButtonBackgroundColor)); + SetBackground(views::CreateSolidBackground(kControlButtonBackgroundColor)); SetBorder(views::CreateEmptyBorder(gfx::Insets(kControlButtonBorderSize))); set_animate_on_state_change(false);
diff --git a/ui/message_center/views/toast_contents_view.cc b/ui/message_center/views/toast_contents_view.cc index 785ffa6..7455eb1d 100644 --- a/ui/message_center/views/toast_contents_view.cc +++ b/ui/message_center/views/toast_contents_view.cc
@@ -65,7 +65,7 @@ // Sets the transparent background. Then, when the message view is slid out, // the whole toast seems to slide although the actual bound of the widget // remains. This is hacky but easier to keep the consistency. - set_background(views::Background::CreateSolidBackground(0, 0, 0, 0)); + SetBackground(views::CreateSolidBackground(SK_ColorTRANSPARENT)); fade_animation_.reset(new gfx::SlideAnimation(this)); fade_animation_->SetSlideDuration(kFadeInOutDuration);
diff --git a/ui/views/background.cc b/ui/views/background.cc index 901aa03b..6cb17f106 100644 --- a/ui/views/background.cc +++ b/ui/views/background.cc
@@ -85,40 +85,32 @@ DISALLOW_COPY_AND_ASSIGN(BackgroundPainter); }; -Background::Background() - : color_(SK_ColorWHITE) -{ -} +Background::Background() : color_(SK_ColorWHITE) {} -Background::~Background() { -} +Background::~Background() {} void Background::SetNativeControlColor(SkColor color) { color_ = color; } -// static -Background* Background::CreateSolidBackground(SkColor color) { - return new SolidBackground(color); +std::unique_ptr<Background> CreateSolidBackground(SkColor color) { + return base::MakeUnique<SolidBackground>(color); } -// static -Background* Background::CreateThemedSolidBackground( +std::unique_ptr<Background> CreateThemedSolidBackground( View* view, ui::NativeTheme::ColorId color_id) { - return new ThemedSolidBackground(view, color_id); + return base::MakeUnique<ThemedSolidBackground>(view, color_id); } -// static -Background* Background::CreateStandardPanelBackground() { +std::unique_ptr<Background> CreateStandardPanelBackground() { // TODO(beng): Should be in NativeTheme. return CreateSolidBackground(SK_ColorWHITE); } -// static -Background* Background::CreateBackgroundPainter( +std::unique_ptr<Background> CreateBackgroundFromPainter( std::unique_ptr<Painter> painter) { - return new BackgroundPainter(std::move(painter)); + return base::MakeUnique<BackgroundPainter>(std::move(painter)); } } // namespace views
diff --git a/ui/views/background.h b/ui/views/background.h index 81bb675..63d7847 100644 --- a/ui/views/background.h +++ b/ui/views/background.h
@@ -45,31 +45,6 @@ Background(); virtual ~Background(); - // Creates a background that fills the canvas in the specified color. - static Background* CreateSolidBackground(SkColor color); - - // Creates a background that fills the canvas in the color specified by the - // view's NativeTheme and the given color identifier. - static Background* CreateThemedSolidBackground( - View* view, - ui::NativeTheme::ColorId color_id); - - // Creates a background that fills the canvas in the specified color. - static Background* CreateSolidBackground(int r, int g, int b) { - return CreateSolidBackground(SkColorSetRGB(r, g, b)); - } - - // Creates a background that fills the canvas in the specified color. - static Background* CreateSolidBackground(int r, int g, int b, int a) { - return CreateSolidBackground(SkColorSetARGB(a, r, g, b)); - } - - // Creates Chrome's standard panel background - static Background* CreateStandardPanelBackground(); - - // Creates a Background from the specified Painter. - static Background* CreateBackgroundPainter(std::unique_ptr<Painter> painter); - // Render the background for the provided view virtual void Paint(gfx::Canvas* canvas, View* view) const = 0; @@ -89,6 +64,22 @@ DISALLOW_COPY_AND_ASSIGN(Background); }; +// Creates a background that fills the canvas in the specified color. +VIEWS_EXPORT std::unique_ptr<Background> CreateSolidBackground(SkColor color); + +// Creates a background that fills the canvas in the color specified by the +// view's NativeTheme and the given color identifier. +VIEWS_EXPORT std::unique_ptr<Background> CreateThemedSolidBackground( + View* view, + ui::NativeTheme::ColorId color_id); + +// Creates Chrome's standard panel background +VIEWS_EXPORT std::unique_ptr<Background> CreateStandardPanelBackground(); + +// Creates a Background from the specified Painter. +VIEWS_EXPORT std::unique_ptr<Background> CreateBackgroundFromPainter( + std::unique_ptr<Painter> painter); + } // namespace views #endif // UI_VIEWS_BACKGROUND_H_
diff --git a/ui/views/bubble/bubble_dialog_delegate.cc b/ui/views/bubble/bubble_dialog_delegate.cc index 6845612..5688543b 100644 --- a/ui/views/bubble/bubble_dialog_delegate.cc +++ b/ui/views/bubble/bubble_dialog_delegate.cc
@@ -303,9 +303,9 @@ // When there's an opaque layer, the bubble border background won't show // through, so explicitly paint a background color. - set_background(layer() && layer()->fills_bounds_opaquely() - ? Background::CreateSolidBackground(color()) - : nullptr); + SetBackground(layer() && layer()->fills_bounds_opaquely() + ? CreateSolidBackground(color()) + : nullptr); } void BubbleDialogDelegateView::HandleVisibilityChanged(Widget* widget,
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc index e3edaf9..17c86ee 100644 --- a/ui/views/bubble/bubble_frame_view.cc +++ b/ui/views/bubble/bubble_frame_view.cc
@@ -400,7 +400,7 @@ SetBorder(std::move(border)); // Update the background, which relies on the border. - set_background(new views::BubbleBackground(bubble_border_)); + SetBackground(base::MakeUnique<views::BubbleBackground>(bubble_border_)); } void BubbleFrameView::SetFootnoteView(View* view) { @@ -412,8 +412,8 @@ footnote_container_->SetLayoutManager( new BoxLayout(BoxLayout::kVertical, content_margins_.left(), content_margins_.top(), 0)); - footnote_container_->set_background( - Background::CreateSolidBackground(kFootnoteBackgroundColor)); + footnote_container_->SetBackground( + CreateSolidBackground(kFootnoteBackgroundColor)); footnote_container_->SetBorder( CreateSolidSidedBorder(1, 0, 0, 0, kFootnoteBorderColor)); footnote_container_->AddChildView(view);
diff --git a/ui/views/button_drag_utils.cc b/ui/views/button_drag_utils.cc index cb6b611..064a133 100644 --- a/ui/views/button_drag_utils.cc +++ b/ui/views/button_drag_utils.cc
@@ -59,7 +59,7 @@ button.SetTextShadows(gfx::ShadowValues( 10, gfx::ShadowValue(gfx::Vector2d(0, 0), 2.0f, bg_color))); } else { - button.set_background(views::Background::CreateSolidBackground(bg_color)); + button.SetBackground(views::CreateSolidBackground(bg_color)); button.SetBorder(button.CreateDefaultBorder()); } button.SetMaxSize(gfx::Size(kLinkDragImageMaxWidth, 0));
diff --git a/ui/views/color_chooser/color_chooser_view.cc b/ui/views/color_chooser/color_chooser_view.cc index a6e1aba7..4daa497 100644 --- a/ui/views/color_chooser/color_chooser_view.cc +++ b/ui/views/color_chooser/color_chooser_view.cc
@@ -347,7 +347,7 @@ void ColorChooserView::SelectedColorPatchView::SetColor(SkColor color) { if (!background()) - set_background(Background::CreateSolidBackground(color)); + SetBackground(CreateSolidBackground(color)); else background()->SetNativeControlColor(color); SchedulePaint(); @@ -362,7 +362,7 @@ : listener_(listener) { DCHECK(listener_); - set_background(Background::CreateSolidBackground(SK_ColorLTGRAY)); + SetBackground(CreateSolidBackground(SK_ColorLTGRAY)); SetLayoutManager(new BoxLayout(BoxLayout::kVertical, kMarginWidth, kMarginWidth, kMarginWidth));
diff --git a/ui/views/controls/button/image_button.cc b/ui/views/controls/button/image_button.cc index 364489c..49e6d5fc 100644 --- a/ui/views/controls/button/image_button.cc +++ b/ui/views/controls/button/image_button.cc
@@ -64,9 +64,9 @@ SchedulePaint(); } -void ImageButton::SetBackground(SkColor color, - const gfx::ImageSkia* image, - const gfx::ImageSkia* mask) { +void ImageButton::SetBackgroundImage(SkColor color, + const gfx::ImageSkia* image, + const gfx::ImageSkia* mask) { if (image == NULL || mask == NULL) { background_image_ = gfx::ImageSkia(); return;
diff --git a/ui/views/controls/button/image_button.h b/ui/views/controls/button/image_button.h index e17a5ce..419a517 100644 --- a/ui/views/controls/button/image_button.h +++ b/ui/views/controls/button/image_button.h
@@ -50,9 +50,9 @@ virtual void SetImage(ButtonState state, const gfx::ImageSkia& image); // Set the background details. - void SetBackground(SkColor color, - const gfx::ImageSkia* image, - const gfx::ImageSkia* mask); + void SetBackgroundImage(SkColor color, + const gfx::ImageSkia* image, + const gfx::ImageSkia* mask); // Sets how the image is laid out within the button's bounds. void SetImageAlignment(HorizontalAlignment h_align,
diff --git a/ui/views/controls/button/label_button.cc b/ui/views/controls/button/label_button.cc index 6fb6e39e..50e17d9 100644 --- a/ui/views/controls/button/label_button.cc +++ b/ui/views/controls/button/label_button.cc
@@ -440,13 +440,13 @@ colors[STATE_NORMAL] = colors[STATE_HOVERED] = colors[STATE_PRESSED] = SK_ColorWHITE; label_->SetBackgroundColor(SK_ColorBLACK); - label_->set_background(Background::CreateSolidBackground(SK_ColorBLACK)); + label_->SetBackground(CreateSolidBackground(SK_ColorBLACK)); label_->SetAutoColorReadabilityEnabled(true); label_->SetShadows(gfx::ShadowValues()); } else { if (style() == STYLE_BUTTON) PlatformStyle::ApplyLabelButtonTextStyle(label_, &colors); - label_->set_background(nullptr); + label_->SetBackground(nullptr); label_->SetAutoColorReadabilityEnabled(false); }
diff --git a/ui/views/controls/button/md_text_button.cc b/ui/views/controls/button/md_text_button.cc index 32773d2..55159d8 100644 --- a/ui/views/controls/button/md_text_button.cc +++ b/ui/views/controls/button/md_text_button.cc
@@ -283,9 +283,9 @@ } DCHECK_EQ(SK_AlphaOPAQUE, static_cast<int>(SkColorGetA(bg_color))); - set_background(Background::CreateBackgroundPainter( - Painter::CreateRoundRectWith1PxBorderPainter(bg_color, stroke_color, - kInkDropSmallCornerRadius))); + SetBackground( + CreateBackgroundFromPainter(Painter::CreateRoundRectWith1PxBorderPainter( + bg_color, stroke_color, kInkDropSmallCornerRadius))); SchedulePaint(); }
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc index b85f07dc..c948d755 100644 --- a/ui/views/controls/combobox/combobox.cc +++ b/ui/views/controls/combobox/combobox.cc
@@ -554,8 +554,8 @@ if (!UseMd()) return; - set_background( - Background::CreateBackgroundPainter(Painter::CreateSolidRoundRectPainter( + SetBackground( + CreateBackgroundFromPainter(Painter::CreateSolidRoundRectPainter( theme->GetSystemColor( ui::NativeTheme::kColorId_TextfieldDefaultBackground), FocusableBorder::kCornerRadiusDp)));
diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc index a0a74eb..08ba198 100644 --- a/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -304,7 +304,7 @@ BubbleBorder::SMALL_SHADOW, SK_ColorWHITE); SetBorder(std::unique_ptr<Border>(bubble_border_)); - set_background(new BubbleBackground(bubble_border_)); + SetBackground(base::MakeUnique<BubbleBackground>(bubble_border_)); } BubbleBorder::Arrow MenuScrollViewContainer::BubbleBorderTypeFromAnchor(
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc index aa480a2..6d97969 100644 --- a/ui/views/controls/scroll_view.cc +++ b/ui/views/controls/scroll_view.cc
@@ -209,8 +209,7 @@ DCHECK(!a_view->layer()); if (ScrollsWithLayers()) { if (!a_view->background() && background_color_ != SK_ColorTRANSPARENT) { - a_view->set_background( - Background::CreateSolidBackground(background_color_)); + a_view->SetBackground(CreateSolidBackground(background_color_)); } a_view->SetPaintToLayer(); a_view->layer()->SetScrollable( @@ -226,12 +225,10 @@ void ScrollView::SetBackgroundColor(SkColor color) { background_color_ = color; - contents_viewport_->set_background( - Background::CreateSolidBackground(background_color_)); + contents_viewport_->SetBackground(CreateSolidBackground(background_color_)); if (contents_ && ScrollsWithLayers() && background_color_ != SK_ColorTRANSPARENT) { - contents_->set_background( - Background::CreateSolidBackground(background_color_)); + contents_->SetBackground(CreateSolidBackground(background_color_)); } } @@ -752,8 +749,7 @@ if (scroll_with_layers_enabled_) { background_color_ = SK_ColorWHITE; - contents_viewport_->set_background( - Background::CreateSolidBackground(background_color_)); + contents_viewport_->SetBackground(CreateSolidBackground(background_color_)); } else { // We may have transparent children who want to blend into the default // background.
diff --git a/ui/views/controls/table/table_header.cc b/ui/views/controls/table/table_header.cc index a4e1117..dd72a41 100644 --- a/ui/views/controls/table/table_header.cc +++ b/ui/views/controls/table/table_header.cc
@@ -227,7 +227,7 @@ } void TableHeader::OnNativeThemeChanged(const ui::NativeTheme* theme) { - set_background(Background::CreateSolidBackground( + SetBackground(CreateSolidBackground( theme->GetSystemColor(ui::NativeTheme::kColorId_TableHeaderBackground))); }
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc index ea4f91a2..70892160 100644 --- a/ui/views/controls/textfield/textfield.cc +++ b/ui/views/controls/textfield/textfield.cc
@@ -1910,11 +1910,11 @@ void Textfield::UpdateBackgroundColor() { const SkColor color = GetBackgroundColor(); if (ui::MaterialDesignController::IsSecondaryUiMaterial()) { - set_background(Background::CreateBackgroundPainter( - Painter::CreateSolidRoundRectPainter( + SetBackground( + CreateBackgroundFromPainter(Painter::CreateSolidRoundRectPainter( color, FocusableBorder::kCornerRadiusDp))); } else { - set_background(Background::CreateSolidBackground(color)); + SetBackground(CreateSolidBackground(color)); } // Disable subpixel rendering when the background color is transparent // because it draws incorrect colors around the glyphs in that case.
diff --git a/ui/views/corewm/tooltip_aura.cc b/ui/views/corewm/tooltip_aura.cc index a9433d4..949c322 100644 --- a/ui/views/corewm/tooltip_aura.cc +++ b/ui/views/corewm/tooltip_aura.cc
@@ -123,13 +123,12 @@ void SetBackgroundColor(SkColor background_color) { // Corner radius of tooltip background. const float kTooltipCornerRadius = 2.f; - views::Background* background = - CanUseTranslucentTooltipWidget() - ? views::Background::CreateBackgroundPainter( - views::Painter::CreateSolidRoundRectPainter( - background_color, kTooltipCornerRadius)) - : views::Background::CreateSolidBackground(background_color); - set_background(background); + SetBackground(CanUseTranslucentTooltipWidget() + ? views::CreateBackgroundFromPainter( + views::Painter::CreateSolidRoundRectPainter( + background_color, kTooltipCornerRadius)) + : views::CreateSolidBackground(background_color)); + // Force the text color to be readable when |background_color| is not // opaque. render_text_->set_subpixel_rendering_suppressed(
diff --git a/ui/views/examples/button_example.cc b/ui/views/examples/button_example.cc index 353718d..bb4790a 100644 --- a/ui/views/examples/button_example.cc +++ b/ui/views/examples/button_example.cc
@@ -37,7 +37,7 @@ } void ButtonExample::CreateExampleView(View* container) { - container->set_background(Background::CreateSolidBackground(SK_ColorWHITE)); + container->SetBackground(CreateSolidBackground(SK_ColorWHITE)); BoxLayout* layout = new BoxLayout(BoxLayout::kVertical, 10, 10, 10); layout->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); container->SetLayoutManager(layout);
diff --git a/ui/views/examples/dialog_example.cc b/ui/views/examples/dialog_example.cc index 895db3c..9ec6b09 100644 --- a/ui/views/examples/dialog_example.cc +++ b/ui/views/examples/dialog_example.cc
@@ -43,7 +43,7 @@ Label* body = new Label(parent_->body_->text()); body->SetMultiLine(true); body->SetHorizontalAlignment(gfx::ALIGN_LEFT); - body->set_background(Background::CreateSolidBackground(0, 255, 255)); + body->SetBackground(CreateSolidBackground(SkColorSetRGB(0, 255, 255))); this->AddChildView(body); // Give the example code a way to change the body text.
diff --git a/ui/views/examples/examples_window.cc b/ui/views/examples/examples_window.cc index 5f869477..d2654b4 100644 --- a/ui/views/examples/examples_window.cc +++ b/ui/views/examples/examples_window.cc
@@ -140,7 +140,7 @@ combobox_model_.SetExamples(std::move(examples)); combobox_->ModelChanged(); - set_background(Background::CreateStandardPanelBackground()); + SetBackground(CreateStandardPanelBackground()); GridLayout* layout = new GridLayout(this); SetLayoutManager(layout); ColumnSet* column_set = layout->AddColumnSet(0);
diff --git a/ui/views/examples/label_example.cc b/ui/views/examples/label_example.cc index 5db8bbf..9507b13 100644 --- a/ui/views/examples/label_example.cc +++ b/ui/views/examples/label_example.cc
@@ -160,8 +160,7 @@ void LabelExample::AddCustomLabel(View* container) { View* control_container = new View(); control_container->SetBorder(CreateSolidBorder(2, SK_ColorGRAY)); - control_container->set_background( - Background::CreateSolidBackground(SK_ColorLTGRAY)); + control_container->SetBackground(CreateSolidBackground(SK_ColorLTGRAY)); GridLayout* layout = GridLayout::CreatePanel(control_container); control_container->SetLayoutManager(layout);
diff --git a/ui/views/examples/widget_example.cc b/ui/views/examples/widget_example.cc index 93a152c..835272f 100644 --- a/ui/views/examples/widget_example.cc +++ b/ui/views/examples/widget_example.cc
@@ -43,7 +43,7 @@ }; DialogExample::DialogExample() { - set_background(Background::CreateSolidBackground(SK_ColorGRAY)); + SetBackground(CreateSolidBackground(SK_ColorGRAY)); SetLayoutManager(new BoxLayout(BoxLayout::kVertical, 10, 10, 10)); AddChildView(new Label(ASCIIToUTF16("Dialog contents label!"))); } @@ -105,7 +105,7 @@ if (!widget->GetContentsView()) { View* contents = new View(); contents->SetLayoutManager(new BoxLayout(BoxLayout::kHorizontal, 0, 0, 0)); - contents->set_background(Background::CreateSolidBackground(SK_ColorGRAY)); + contents->SetBackground(CreateSolidBackground(SK_ColorGRAY)); BuildButton(contents, "Close", CLOSE_WIDGET); widget->SetContentsView(contents); }
diff --git a/ui/views/focus/focus_traversal_unittest.cc b/ui/views/focus/focus_traversal_unittest.cc index 352b4e4..6ca2228 100644 --- a/ui/views/focus/focus_traversal_unittest.cc +++ b/ui/views/focus/focus_traversal_unittest.cc
@@ -300,8 +300,7 @@ // NativeButton * THUMBNAIL_STAR_ID // NativeButton * THUMBNAIL_SUPER_STAR_ID - GetContentsView()->set_background( - Background::CreateSolidBackground(SK_ColorWHITE)); + GetContentsView()->SetBackground(CreateSolidBackground(SK_ColorWHITE)); Checkbox* cb = new Checkbox(ASCIIToUTF16("This is a checkbox")); GetContentsView()->AddChildView(cb); @@ -311,8 +310,8 @@ left_container_ = new PaneView(); left_container_->SetBorder(CreateSolidBorder(1, SK_ColorBLACK)); - left_container_->set_background( - Background::CreateSolidBackground(240, 240, 240)); + left_container_->SetBackground( + CreateSolidBackground(SkColorSetRGB(240, 240, 240))); left_container_->set_id(LEFT_CONTAINER_ID); GetContentsView()->AddChildView(left_container_); left_container_->SetBounds(10, 35, 250, 200); @@ -395,8 +394,8 @@ right_container_ = new PaneView(); right_container_->SetBorder(CreateSolidBorder(1, SK_ColorBLACK)); - right_container_->set_background( - Background::CreateSolidBackground(240, 240, 240)); + right_container_->SetBackground( + CreateSolidBackground(SkColorSetRGB(240, 240, 240))); right_container_->set_id(RIGHT_CONTAINER_ID); GetContentsView()->AddChildView(right_container_); right_container_->SetBounds(270, 35, 300, 200); @@ -426,8 +425,8 @@ View* inner_container = new View(); inner_container->SetBorder(CreateSolidBorder(1, SK_ColorBLACK)); - inner_container->set_background( - Background::CreateSolidBackground(230, 230, 230)); + inner_container->SetBackground( + CreateSolidBackground(SkColorSetRGB(230, 230, 230))); inner_container->set_id(INNER_CONTAINER_ID); right_container_->AddChildView(inner_container); inner_container->SetBounds(100, 10, 150, 180); @@ -439,8 +438,8 @@ View* scroll_content = new View(); scroll_content->SetBounds(0, 0, 200, 200); - scroll_content->set_background( - Background::CreateSolidBackground(200, 200, 200)); + scroll_content->SetBackground( + CreateSolidBackground(SkColorSetRGB(200, 200, 200))); scroll_view->SetContents(scroll_content); static const char* const kTitles[] = { @@ -495,7 +494,7 @@ // Left bottom box with style checkboxes. contents = new View(); - contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE)); + contents->SetBackground(CreateSolidBackground(SK_ColorWHITE)); cb = new Checkbox(ASCIIToUTF16("Bold")); contents->AddChildView(cb); cb->SetBounds(10, 10, 50, 20); @@ -530,7 +529,7 @@ // Right bottom box with search. contents = new View(); - contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE)); + contents->SetBackground(CreateSolidBackground(SK_ColorWHITE)); text_field = new Textfield(); contents->AddChildView(text_field); text_field->SetBounds(10, 10, 100, 20); @@ -557,7 +556,7 @@ contents = new View(); contents->SetFocusBehavior(View::FocusBehavior::ALWAYS); - contents->set_background(Background::CreateSolidBackground(SK_ColorBLUE)); + contents->SetBackground(CreateSolidBackground(SK_ColorBLUE)); contents->set_id(THUMBNAIL_CONTAINER_ID); button = MdTextButton::Create(NULL, ASCIIToUTF16("Star")); contents->AddChildView(button);
diff --git a/ui/views/view.cc b/ui/views/view.cc index 9257c06..8fbd867 100644 --- a/ui/views/view.cc +++ b/ui/views/view.cc
@@ -911,8 +911,8 @@ PaintChildren(context); } -void View::set_background(Background* b) { - background_.reset(b); +void View::SetBackground(std::unique_ptr<Background> b) { + background_ = std::move(b); } void View::SetBorder(std::unique_ptr<Border> b) {
diff --git a/ui/views/view.h b/ui/views/view.h index 9c78e196..0c2c9c2 100644 --- a/ui/views/view.h +++ b/ui/views/view.h
@@ -540,12 +540,12 @@ // the hierarchy beneath it. void Paint(const ui::PaintContext& parent_context); - // The background object is owned by this object and may be NULL. - void set_background(Background* b); + // The background object may be null. + void SetBackground(std::unique_ptr<Background> b); const Background* background() const { return background_.get(); } Background* background() { return background_.get(); } - // The border object is owned by this object and may be NULL. + // The border object may be null. virtual void SetBorder(std::unique_ptr<Border> b); const Border* border() const { return border_.get(); } Border* border() { return border_.get(); }
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc index 1d3510f..e215d7b0 100644 --- a/ui/views/window/dialog_client_view.cc +++ b/ui/views/window/dialog_client_view.cc
@@ -220,8 +220,8 @@ const DialogDelegate* dialog = GetDialogDelegate(); if (dialog && !dialog->ShouldUseCustomFrame()) { - set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> - GetSystemColor(ui::NativeTheme::kColorId_DialogBackground))); + SetBackground(views::CreateSolidBackground(GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_DialogBackground))); } }