diff --git a/DEPS b/DEPS index a7fdb51c9..55e7332 100644 --- a/DEPS +++ b/DEPS
@@ -121,11 +121,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': 'c4745d6e517b2d8d831f98f748e730f64338634c', + 'skia_revision': 'bf515da22a7fa31ccd65c0e36d7559088e52ed58', # 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': 'b346b550dade4120c0461ee3bd59c763e40a37e2', + 'v8_revision': 'f54a0f5b545aed0f9192294607adc91a53c888d4', # 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. @@ -133,7 +133,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': '5df1d281a992fb221f243d532c70ad0398c9410a', + 'angle_revision': 'eb278cb04e970ad823ae09e9d4348f88879de6c0', # 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. @@ -145,7 +145,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': 'b6f0a1af7b64470739cb4dcf51b9b0d6b4cbd029', + 'pdfium_revision': '4ae52355703d27771c30bf6d99dc211083fc86d2', # 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. @@ -229,7 +229,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'spv_tools_revision': '5eab6df648eace6eab69c44ccd17bd0f5e57406d', + 'spv_tools_revision': 'ee95ab15ce7afa034787b38f24d5f59f3769bb0b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -679,7 +679,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ed43a650f0c063e682bab0019ff786498c2347b6', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'd2644cbe7da11efab78ea4db85d60333e70b2cd0', 'condition': 'checkout_linux', }, @@ -704,7 +704,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c0641b8e930496500616b6f7253da869fc4a343e', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd16b51b9bf1795ca337a878288c38728f6d1a1da', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -1036,7 +1036,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + 'c79bb20688a3a41c83a066b50afb036b23c3b1e9', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '70216c261f7337bbe3a7da2955649e3dec05f36f', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78', @@ -1230,7 +1230,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c81e6accde873f927b817e9d6be42a300458992b', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@67cf9c07be0d3c7cdd646f460a24899e8190c813', 'condition': 'checkout_src_internal', }, @@ -2702,6 +2702,7 @@ 'chrome-sdk', '--nogoma', '--use-external-config', + '--require-exact-version', '--nogn-gen', '--download-vm', '--board={cros_board}', @@ -2723,6 +2724,7 @@ 'chrome-sdk', '--nogoma', '--use-external-config', + '--require-exact-version', '--nogn-gen', '--board={cros_board}', '--cache-dir=src/build/cros_cache/',
diff --git a/WATCHLISTS b/WATCHLISTS index fde1d16..0dbc54f 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -1152,11 +1152,6 @@ 'media_win': { 'filepath': 'media/gpu/windows/' }, - 'memory_coordinator': { - 'filepath': 'base/memory/memory_|' \ - 'content/browser/memory/|' \ - 'content/child/memory/' - }, 'message_loop': { 'filepath': 'base/message_' }, @@ -2313,7 +2308,6 @@ 'pthatcher+watch@chromium.org', 'takumif+watch@chromium.org'], 'media_win': ['media-win-reviews@chromium.org'], - 'memory_coordinator': ['chrome-grc-reviews+memory@chromium.org'], 'message_loop': ['sadrul@chromium.org'], 'metrics': ['asvitkine+watch@chromium.org'], 'metrics_xml_files': ['asvitkine+watch@chromium.org'],
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index cd9dba7..c341d521 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -502,8 +502,8 @@ "magnifier/docked_magnifier_controller.cc", "magnifier/docked_magnifier_controller.h", "magnifier/magnification_controller.cc", - "magnifier/magnifier_scale_utils.cc", - "magnifier/magnifier_scale_utils.h", + "magnifier/magnifier_utils.cc", + "magnifier/magnifier_utils.h", "magnifier/partial_magnification_controller.cc", "magnifier/partial_magnification_controller.h", "media/media_notification_constants.cc", @@ -1701,9 +1701,9 @@ "login/ui/note_action_launch_button_unittest.cc", "magnifier/docked_magnifier_controller_unittest.cc", "magnifier/magnification_controller_unittest.cc", - "magnifier/magnifier_scale_utils_unittest.cc", "magnifier/magnifier_test_utils.cc", "magnifier/magnifier_test_utils.h", + "magnifier/magnifier_utils_unittest.cc", "magnifier/partial_magnification_controller_unittest.cc", "media/media_notification_controller_unittest.cc", "media/media_notification_view_unittest.cc",
diff --git a/ash/app_list/BUILD.gn b/ash/app_list/BUILD.gn index 54619ede..ff72324 100644 --- a/ash/app_list/BUILD.gn +++ b/ash/app_list/BUILD.gn
@@ -57,6 +57,8 @@ "views/page_switcher.h", "views/pulsing_block_view.cc", "views/pulsing_block_view.h", + "views/remove_query_confirmation_dialog.cc", + "views/remove_query_confirmation_dialog.h", "views/search_box_view.cc", "views/search_box_view.h", "views/search_result_actions_view.cc",
diff --git a/ash/app_list/app_list_metrics.cc b/ash/app_list/app_list_metrics.cc index bd827eb..e4f9ff1 100644 --- a/ash/app_list/app_list_metrics.cc +++ b/ash/app_list/app_list_metrics.cc
@@ -41,6 +41,16 @@ constexpr char kAppListSearchResultOpenSourceHistogram[] = "Apps.AppListSearchResultOpenedSource"; +// The UMA hisotogram that logs the action user performs on zero state +// search result. +constexpr char kAppListZeroStateSearchResultUserActionHistogram[] = + "Apps.AppListZeroStateSearchResultUserActionType"; + +// The UMA histogram that logs user's decision(remove or cancel) for zero state +// search result removal confirmation. +constexpr char kAppListZeroStateSearchResultRemovalHistogram[] = + "Apps.ZeroStateSearchResutRemovalDecision"; + // The different sources from which a search result is displayed. These values // are written to logs. New enum values can be added, but existing enums must // never be renumbered or deleted and reused. @@ -90,4 +100,16 @@ ApplistSearchResultOpenedSource::kMaxApplistSearchResultOpenedSource); } +void RecordZeroStateSearchResultUserActionHistogram( + ZeroStateSearchResultUserActionType action) { + UMA_HISTOGRAM_ENUMERATION(kAppListZeroStateSearchResultUserActionHistogram, + action); +} + +void RecordZeroStateSearchResultRemovalHistogram( + ZeroStateSearchResutRemovalConfirmation removal_decision) { + UMA_HISTOGRAM_ENUMERATION(kAppListZeroStateSearchResultRemovalHistogram, + removal_decision); +} + } // namespace app_list
diff --git a/ash/app_list/app_list_metrics.h b/ash/app_list/app_list_metrics.h index a205000d..a758b54 100644 --- a/ash/app_list/app_list_metrics.h +++ b/ash/app_list/app_list_metrics.h
@@ -23,6 +23,26 @@ constexpr char kAppListHideInputLatencyHistogram[] = "Apps.AppListHide.InputLatency"; +// These are used in histograms, do not remove/renumber entries. If you're +// adding to this enum with the intention that it will be logged, update the +// AppListZeroStateSearchResultUserActionType enum listing in +// tools/metrics/histograms/enums.xml. +enum class ZeroStateSearchResultUserActionType { + kRemoveResult = 0, + kAppendResult = 1, + kMaxValue = kAppendResult, +}; + +// These are used in histograms, do not remove/renumber entries. If you're +// adding to this enum with the intention that it will be logged, update the +// AppListZeroStateResultRemovalConfirmation enum listing in +// tools/metrics/histograms/enums.xml. +enum class ZeroStateSearchResutRemovalConfirmation { + kRemovalConfirmed = 0, + kRemovalCanceled = 1, + kMaxValue = kRemovalCanceled, +}; + void RecordFolderShowHideAnimationSmoothness(int actual_frames, int ideal_duration_ms, float refresh_rate); @@ -31,6 +51,12 @@ int ideal_duration_ms, float refresh_rate); +void RecordZeroStateSearchResultUserActionHistogram( + ZeroStateSearchResultUserActionType action); + +void RecordZeroStateSearchResultRemovalHistogram( + ZeroStateSearchResutRemovalConfirmation removal_decision); + APP_LIST_EXPORT void RecordSearchResultOpenSource( const SearchResult* result, const AppListModel* model,
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc index 7660a12..5de68ec6 100644 --- a/ash/app_list/views/contents_view.cc +++ b/ash/app_list/views/contents_view.cc
@@ -582,6 +582,8 @@ return (current_state == ash::AppListState::kStateStart && target_state == ash::AppListState::kStateApps) || (current_state == ash::AppListState::kStateApps && + target_state == ash::AppListState::kStateStart) || + (current_state == ash::AppListState::kStateSearchResults && target_state == ash::AppListState::kStateStart); }
diff --git a/ash/app_list/views/horizontal_page_container.cc b/ash/app_list/views/horizontal_page_container.cc index 6cb4c49..36bf49a 100644 --- a/ash/app_list/views/horizontal_page_container.cc +++ b/ash/app_list/views/horizontal_page_container.cc
@@ -76,6 +76,11 @@ gfx::Rect to_rect = page->GetPageBoundsForState(to_state); gfx::Rect from_rect = page->GetPageBoundsForState(from_state); + // Invalidate layout when the state changes to ensure that SetBoundsRect + // below also triggers a layout. + if (from_state != to_state) + page->InvalidateLayout(); + // Animate linearly (the PaginationModel handles easing). gfx::Rect bounds( gfx::Tween::RectValueBetween(progress, from_rect, to_rect));
diff --git a/ash/app_list/views/remove_query_confirmation_dialog.cc b/ash/app_list/views/remove_query_confirmation_dialog.cc new file mode 100644 index 0000000..3f27dc75 --- /dev/null +++ b/ash/app_list/views/remove_query_confirmation_dialog.cc
@@ -0,0 +1,96 @@ +// Copyright (c) 2019 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 "ash/app_list/views/remove_query_confirmation_dialog.h" + +#include "ui/base/l10n/l10n_util.h" +#include "ui/strings/grit/ui_strings.h" +#include "ui/views/controls/label.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/layout/layout_provider.h" + +namespace app_list { + +namespace { + +constexpr int kDialogWidth = 320; +constexpr int kDialogYOffset = 32; + +} // namespace + +RemoveQueryConfirmationDialog::RemoveQueryConfirmationDialog( + RemovalConfirmationCallback confirm_callback, + int event_flags) + : confirm_callback_(std::move(confirm_callback)), + event_flags_(event_flags) { + const views::LayoutProvider* provider = views::LayoutProvider::Get(); + SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::kVertical, + provider->GetDialogInsetsForContentType(views::TEXT, views::TEXT), + provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL))); + + views::Label* label = new views::Label( + l10n_util::GetStringUTF16(IDS_REMOVE_ZERO_STATE_SUGGESTION_DETAILS)); + label->SetMultiLine(true); + label->SetHorizontalAlignment(gfx::ALIGN_LEFT); + label->SetAllowCharacterBreak(true); + AddChildView(label); +} + +RemoveQueryConfirmationDialog::~RemoveQueryConfirmationDialog() {} + +void RemoveQueryConfirmationDialog::Show(gfx::NativeWindow parent, + const gfx::Rect& anchor_rect) { + views::DialogDelegate::CreateDialogWidget(this, nullptr, parent); + + views::Widget* widget = GetWidget(); + DCHECK(widget); + gfx::Rect widget_rect = widget->GetWindowBoundsInScreen(); + gfx::Point origin(anchor_rect.CenterPoint().x() - kDialogWidth / 2, + anchor_rect.y() + kDialogYOffset); + widget_rect.set_origin(origin); + widget->SetBounds(widget_rect); + + widget->Show(); +} + +base::string16 RemoveQueryConfirmationDialog::GetWindowTitle() const { + return l10n_util::GetStringUTF16(IDS_REMOVE_ZERO_STATE_SUGGESTION_TITLE); +} + +ui::ModalType RemoveQueryConfirmationDialog::GetModalType() const { + return ui::MODAL_TYPE_WINDOW; +} + +bool RemoveQueryConfirmationDialog::ShouldShowCloseButton() const { + return false; +} + +base::string16 RemoveQueryConfirmationDialog::GetDialogButtonLabel( + ui::DialogButton button) const { + return button == ui::DIALOG_BUTTON_CANCEL + ? l10n_util::GetStringUTF16(IDS_APP_CANCEL) + : l10n_util::GetStringUTF16(IDS_REMOVE_SUGGESTION_BUTTON_LABEL); +} + +bool RemoveQueryConfirmationDialog::Accept() { + if (confirm_callback_) + std::move(confirm_callback_).Run(true, event_flags_); + + return true; +} + +bool RemoveQueryConfirmationDialog::Cancel() { + if (confirm_callback_) + std::move(confirm_callback_).Run(false, event_flags_); + + return true; +} + +gfx::Size RemoveQueryConfirmationDialog::CalculatePreferredSize() const { + const int default_width = kDialogWidth; + return gfx::Size(default_width, GetHeightForWidth(default_width)); +} + +} // namespace app_list
diff --git a/ash/app_list/views/remove_query_confirmation_dialog.h b/ash/app_list/views/remove_query_confirmation_dialog.h new file mode 100644 index 0000000..18328873f --- /dev/null +++ b/ash/app_list/views/remove_query_confirmation_dialog.h
@@ -0,0 +1,53 @@ +// Copyright (c) 2019 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 ASH_APP_LIST_VIEWS_REMOVE_QUERY_CONFIRMATION_DIALOG_H_ +#define ASH_APP_LIST_VIEWS_REMOVE_QUERY_CONFIRMATION_DIALOG_H_ + +#include "base/callback.h" +#include "ui/views/window/dialog_delegate.h" + +namespace app_list { + +// RemoveQueryConfirmationDialog displays the confirmation dialog for removing +// a recent query suggestion. +class RemoveQueryConfirmationDialog : public views::DialogDelegateView { + public: + // Callback to notify user's confirmation for removing the zero state + // suggestion query. Invoked with true if user confirms removing query + // suggestion; and false for declining the removal. The second parameter is + // the event flags of user action for invoking the removal action on the + // associated result. + using RemovalConfirmationCallback = base::OnceCallback<void(bool, int)>; + + RemoveQueryConfirmationDialog(RemovalConfirmationCallback callback, + int event_flgas); + ~RemoveQueryConfirmationDialog() override; + + // Shows the dialog with |parent| and |anchor_rect| in screen coordinates. + void Show(gfx::NativeWindow parent, const gfx::Rect& anchor_rect); + + private: + // views::WidgetDelegate: + base::string16 GetWindowTitle() const override; + ui::ModalType GetModalType() const override; + bool ShouldShowCloseButton() const override; + + // views::DialogDelegate: + base::string16 GetDialogButtonLabel(ui::DialogButton button) const override; + bool Accept() override; + bool Cancel() override; + + // views::View: + gfx::Size CalculatePreferredSize() const override; + + RemovalConfirmationCallback confirm_callback_; + int event_flags_; + + DISALLOW_COPY_AND_ASSIGN(RemoveQueryConfirmationDialog); +}; + +} // namespace app_list + +#endif // ASH_APP_LIST_VIEWS_REMOVE_QUERY_CONFIRMATION_DIALOG_H_
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc index 200d7f6..3974b92 100644 --- a/ash/app_list/views/search_box_view.cc +++ b/ash/app_list/views/search_box_view.cc
@@ -497,6 +497,11 @@ search_box()->set_controller(this); } +void SearchBoxView::UpdateQuery(const base::string16& new_query) { + search_box()->SetText(new_query); + ContentsChanged(search_box(), new_query); +} + bool SearchBoxView::HandleKeyEvent(views::Textfield* sender, const ui::KeyEvent& key_event) { if (search_box()->HasFocus() && is_search_box_active() &&
diff --git a/ash/app_list/views/search_box_view.h b/ash/app_list/views/search_box_view.h index 354ddb5..4dce11f 100644 --- a/ash/app_list/views/search_box_view.h +++ b/ash/app_list/views/search_box_view.h
@@ -87,6 +87,9 @@ // Sets the autocomplete text if autocomplete conditions are met. void ProcessAutocomplete(); + // Updates the search box with |new_query| and starts a new search. + void UpdateQuery(const base::string16& new_query); + void set_contents_view(ContentsView* contents_view) { contents_view_ = contents_view; }
diff --git a/ash/app_list/views/search_result_actions_view.cc b/ash/app_list/views/search_result_actions_view.cc index 0208ec8..2358ab7 100644 --- a/ash/app_list/views/search_result_actions_view.cc +++ b/ash/app_list/views/search_result_actions_view.cc
@@ -10,34 +10,213 @@ #include <memory> #include "ash/app_list/views/search_result_actions_view_delegate.h" +#include "ash/app_list/views/search_result_view.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/color_palette.h" #include "ui/gfx/geometry/insets.h" +#include "ui/views/animation/flood_fill_ink_drop_ripple.h" +#include "ui/views/animation/ink_drop_impl.h" +#include "ui/views/animation/ink_drop_mask.h" #include "ui/views/border.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/md_text_button.h" +#include "ui/views/focus/focus_manager.h" #include "ui/views/layout/box_layout.h" namespace app_list { +namespace { + +// Image buttons. +constexpr int kImageButtonSizeDip = 40; +constexpr int kActionButtonBetweenSpacing = 8; +// Button hover color, Google Grey 8%. +constexpr SkColor kButtonHoverColor = SkColorSetA(gfx::kGoogleGrey900, 0x14); + +} // namespace + +// SearchResultImageButton renders the button defined by SearchResult::Action. +class SearchResultImageButton : public views::ImageButton { + public: + SearchResultImageButton(SearchResultActionsView* parent, + const SearchResult::Action& action); + ~SearchResultImageButton() override {} + + // views::View overrides: + void OnFocus() override; + void OnBlur() override; + + // ui::EventHandler overrides: + void OnGestureEvent(ui::GestureEvent* event) override; + + // views::Button overrides: + void StateChanged(ButtonState old_state) override; + + // views::InkDropHost overrides: + std::unique_ptr<views::InkDrop> CreateInkDrop() override; + std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override; + std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override; + std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight() + const override; + + // Updates the button visibility upon state change of the button or the + // search result view associated with it. + void UpdateOnStateChanged(); + + private: + // views::View overrides: + void OnPaintBackground(gfx::Canvas* canvas) override; + + int GetInkDropRadius() const; + const char* GetClassName() const override; + + SearchResultActionsView* parent_; + const bool visible_on_hover_; + bool to_be_activate_by_long_press_ = false; + + DISALLOW_COPY_AND_ASSIGN(SearchResultImageButton); +}; + +SearchResultImageButton::SearchResultImageButton( + SearchResultActionsView* parent, + const SearchResult::Action& action) + : ImageButton(parent), + parent_(parent), + visible_on_hover_(action.visible_on_hover) { + SetFocusBehavior(FocusBehavior::ALWAYS); + // Avoid drawing default dashed focus and draw customized focus in + // OnPaintBackground(); + SetFocusPainter(nullptr); + SetInkDropMode(InkDropMode::ON); + + SetPreferredSize({kImageButtonSizeDip, kImageButtonSizeDip}); + SetImageAlignment(HorizontalAlignment::ALIGN_CENTER, + VerticalAlignment::ALIGN_MIDDLE); + SetImage(views::ImageButton::STATE_NORMAL, &action.image); + SetAccessibleName(action.tooltip_text); + + SetVisible(!visible_on_hover_); +} + +void SearchResultImageButton::OnFocus() { + parent_->ActionButtonStateChanged(); + SchedulePaint(); +} + +void SearchResultImageButton::OnBlur() { + parent_->ActionButtonStateChanged(); + SchedulePaint(); +} + +void SearchResultImageButton::OnGestureEvent(ui::GestureEvent* event) { + switch (event->type()) { + case ui::ET_GESTURE_LONG_PRESS: + to_be_activate_by_long_press_ = true; + event->SetHandled(); + break; + case ui::ET_GESTURE_END: + if (to_be_activate_by_long_press_) { + NotifyClick(*event); + SetState(STATE_NORMAL); + to_be_activate_by_long_press_ = false; + event->SetHandled(); + } + break; + default: + break; + } + if (!event->handled()) + Button::OnGestureEvent(event); +} + +void SearchResultImageButton::StateChanged( + views::Button::ButtonState old_state) { + parent_->ActionButtonStateChanged(); +} + +std::unique_ptr<views::InkDrop> SearchResultImageButton::CreateInkDrop() { + return CreateDefaultFloodFillInkDropImpl(); +} + +std::unique_ptr<views::InkDropRipple> +SearchResultImageButton::CreateInkDropRipple() const { + const gfx::Point center = GetLocalBounds().CenterPoint(); + const int ripple_radius = GetInkDropRadius(); + gfx::Rect bounds(center.x() - ripple_radius, center.y() - ripple_radius, + 2 * ripple_radius, 2 * ripple_radius); + constexpr SkColor ripple_color = SkColorSetA(gfx::kGoogleGrey900, 0x17); + + return std::make_unique<views::FloodFillInkDropRipple>( + size(), GetLocalBounds().InsetsFrom(bounds), + GetInkDropCenterBasedOnLastEvent(), ripple_color, 1.0f); +} + +std::unique_ptr<views::InkDropMask> SearchResultImageButton::CreateInkDropMask() + const { + return std::make_unique<views::CircleInkDropMask>( + size(), GetLocalBounds().CenterPoint(), GetInkDropRadius()); +} + +std::unique_ptr<views::InkDropHighlight> +SearchResultImageButton::CreateInkDropHighlight() const { + constexpr SkColor ripple_color = SkColorSetA(gfx::kGoogleGrey900, 0x12); + return std::make_unique<views::InkDropHighlight>( + gfx::PointF(GetLocalBounds().CenterPoint()), + std::make_unique<views::CircleLayerDelegate>(ripple_color, + GetInkDropRadius())); +} + +void SearchResultImageButton::UpdateOnStateChanged() { + if (!visible_on_hover_) + return; + + // Show button if the associated result row is hovered or selected, or one + // of the action buttons is selected. + if (parent_->IsSearchResultHoveredOrSelected() || + parent()->Contains(GetFocusManager()->GetFocusedView())) { + SetVisible(true); + } else { + SetVisible(false); + } +} + +void SearchResultImageButton::OnPaintBackground(gfx::Canvas* canvas) { + if (HasFocus()) { + cc::PaintFlags circle_flags; + circle_flags.setAntiAlias(true); + circle_flags.setColor(kButtonHoverColor); + circle_flags.setStyle(cc::PaintFlags::kFill_Style); + canvas->DrawCircle(GetLocalBounds().CenterPoint(), GetInkDropRadius(), + circle_flags); + } +} + +int SearchResultImageButton::GetInkDropRadius() const { + return width() / 2; +} + +const char* SearchResultImageButton::GetClassName() const { + return "SearchResultImageButton"; +} + SearchResultActionsView::SearchResultActionsView( SearchResultActionsViewDelegate* delegate) : delegate_(delegate), selected_action_(-1) { SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::kHorizontal, gfx::Insets(10, 0), 0)); + views::BoxLayout::kHorizontal, gfx::Insets(), + kActionButtonBetweenSpacing)); } SearchResultActionsView::~SearchResultActionsView() {} void SearchResultActionsView::SetActions(const SearchResult::Actions& actions) { + buttons_.clear(); RemoveAllChildViews(true); for (size_t i = 0; i < actions.size(); ++i) { const SearchResult::Action& action = actions.at(i); - if (action.label_text.empty()) - CreateImageButton(action); - else - CreateBlueButton(action); + CreateImageButton(action); } PreferredSizeChanged(); @@ -64,28 +243,24 @@ return action_index >= 0 && action_index < child_count(); } -void SearchResultActionsView::CreateImageButton( - const SearchResult::Action& action) { - views::ImageButton* button = new views::ImageButton(this); - button->SetBorder(views::CreateEmptyBorder(0, 9, 0, 9)); - button->SetAccessibleName(action.tooltip_text); - button->SetImageAlignment(views::ImageButton::ALIGN_CENTER, - views::ImageButton::ALIGN_MIDDLE); - button->SetImage(views::Button::STATE_NORMAL, &action.base_image); - button->SetImage(views::Button::STATE_HOVERED, &action.hover_image); - button->SetImage(views::Button::STATE_PRESSED, &action.pressed_image); - button->SetTooltipText(action.tooltip_text); - AddChildView(button); +bool SearchResultActionsView::IsSearchResultHoveredOrSelected() const { + return delegate_->IsSearchResultHoveredOrSelected(); } -void SearchResultActionsView::CreateBlueButton( +void SearchResultActionsView::UpdateButtonsOnStateChanged() { + for (SearchResultImageButton* button : buttons_) + button->UpdateOnStateChanged(); +} + +void SearchResultActionsView::ActionButtonStateChanged() { + UpdateButtonsOnStateChanged(); +} + +void SearchResultActionsView::CreateImageButton( const SearchResult::Action& action) { - views::MdTextButton* button = - views::MdTextButton::Create(this, action.label_text); - button->SetProminent(true); - button->SetTooltipText(action.tooltip_text); - button->SetFocusBehavior(FocusBehavior::NEVER); + SearchResultImageButton* button = new SearchResultImageButton(this, action); AddChildView(button); + buttons_.emplace_back(button); } void SearchResultActionsView::OnPaint(gfx::Canvas* canvas) { @@ -97,6 +272,10 @@ canvas->FillRect(active_action_bounds, kActiveActionBackground); } +void SearchResultActionsView::ChildVisibilityChanged(views::View* child) { + PreferredSizeChanged(); +} + void SearchResultActionsView::ButtonPressed(views::Button* sender, const ui::Event& event) { if (!delegate_)
diff --git a/ash/app_list/views/search_result_actions_view.h b/ash/app_list/views/search_result_actions_view.h index c6c3ec8..0e4b291 100644 --- a/ash/app_list/views/search_result_actions_view.h +++ b/ash/app_list/views/search_result_actions_view.h
@@ -5,6 +5,8 @@ #ifndef ASH_APP_LIST_VIEWS_SEARCH_RESULT_ACTIONS_VIEW_H_ #define ASH_APP_LIST_VIEWS_SEARCH_RESULT_ACTIONS_VIEW_H_ +#include <vector> + #include "ash/app_list/model/search/search_result.h" #include "base/macros.h" #include "ui/views/controls/button/button.h" @@ -13,13 +15,15 @@ namespace app_list { class SearchResultActionsViewDelegate; +class SearchResultView; +class SearchResultImageButton; // SearchResultActionsView displays a SearchResult::Actions in a button // strip. Each action is presented as a button and horizontally laid out. class SearchResultActionsView : public views::View, public views::ButtonListener { public: - explicit SearchResultActionsView(SearchResultActionsViewDelegate* delegate); + SearchResultActionsView(SearchResultActionsViewDelegate* delegate); ~SearchResultActionsView() override; void SetActions(const SearchResult::Actions& actions); @@ -29,18 +33,27 @@ bool IsValidActionIndex(int action_index) const; + bool IsSearchResultHoveredOrSelected() const; + + // Updates the button UI upon the SearchResultView's UI state change. + void UpdateButtonsOnStateChanged(); + + // Called when one of the action buttons changes state. + void ActionButtonStateChanged(); + private: void CreateImageButton(const SearchResult::Action& action); - void CreateBlueButton(const SearchResult::Action& action); // views::View overrides: void OnPaint(gfx::Canvas* canvas) override; + void ChildVisibilityChanged(views::View* child) override; // views::ButtonListener overrides: void ButtonPressed(views::Button* sender, const ui::Event& event) override; SearchResultActionsViewDelegate* delegate_; // Not owned. int selected_action_; + std::vector<SearchResultImageButton*> buttons_; DISALLOW_COPY_AND_ASSIGN(SearchResultActionsView); };
diff --git a/ash/app_list/views/search_result_actions_view_delegate.h b/ash/app_list/views/search_result_actions_view_delegate.h index 147cfd6..31b3cf70 100644 --- a/ash/app_list/views/search_result_actions_view_delegate.h +++ b/ash/app_list/views/search_result_actions_view_delegate.h
@@ -15,6 +15,10 @@ // in SearchResultActionsView. virtual void OnSearchResultActionActivated(size_t index, int event_flags) = 0; + // Returns true if the associated search result is hovered by mouse, or + // or selected by keyboard. + virtual bool IsSearchResultHoveredOrSelected() = 0; + protected: virtual ~SearchResultActionsViewDelegate() {} };
diff --git a/ash/app_list/views/search_result_list_view.cc b/ash/app_list/views/search_result_list_view.cc index 86a099f..2a3d4b8 100644 --- a/ash/app_list/views/search_result_list_view.cc +++ b/ash/app_list/views/search_result_list_view.cc
@@ -12,6 +12,7 @@ #include "ash/app_list/app_list_view_delegate.h" #include "ash/app_list/model/search/search_result.h" #include "ash/app_list/views/app_list_main_view.h" +#include "ash/app_list/views/search_box_view.h" #include "ash/app_list/views/search_result_view.h" #include "base/bind.h" #include "base/time/time.h" @@ -128,8 +129,14 @@ size_t action_index, int event_flags) { if (view_delegate_ && view->result()) { - view_delegate_->InvokeSearchResultAction(view->result()->id(), action_index, - event_flags); + ash::OmniBoxZeroStateAction action = + ash::GetOmniBoxZeroStateAction(action_index); + if (action == ash::OmniBoxZeroStateAction::kRemoveSuggestion) { + view_delegate_->InvokeSearchResultAction(view->result()->id(), + action_index, event_flags); + } else if (action == ash::OmniBoxZeroStateAction::kAppendSuggestion) { + main_view_->search_box_view()->UpdateQuery(view->result()->title()); + } } }
diff --git a/ash/app_list/views/search_result_list_view.h b/ash/app_list/views/search_result_list_view.h index 1d444cb0..2e51d9c 100644 --- a/ash/app_list/views/search_result_list_view.h +++ b/ash/app_list/views/search_result_list_view.h
@@ -53,6 +53,8 @@ int GetYSize() override; SearchResultBaseView* GetFirstResultView() override; + AppListMainView* app_list_main_view() const { return main_view_; } + private: friend class test::SearchResultListViewTest;
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc index e9133ec..2a76fcf 100644 --- a/ash/app_list/views/search_result_view.cc +++ b/ash/app_list/views/search_result_view.cc
@@ -7,8 +7,12 @@ #include <algorithm> #include <utility> +#include "ash/app_list/app_list_metrics.h" #include "ash/app_list/app_list_view_delegate.h" #include "ash/app_list/model/search/search_result.h" +#include "ash/app_list/views/app_list_main_view.h" +#include "ash/app_list/views/remove_query_confirmation_dialog.h" +#include "ash/app_list/views/search_box_view.h" #include "ash/app_list/views/search_result_actions_view.h" #include "ash/app_list/views/search_result_list_view.h" #include "ash/public/cpp/app_list/app_list_config.h" @@ -79,6 +83,7 @@ AddChildView(actions_view_); AddChildView(progress_bar_); set_context_menu_controller(this); + set_notify_enter_exit_on_child(true); } SearchResultView::~SearchResultView() { @@ -123,7 +128,6 @@ if (!result_ || result_->details().empty()) details_text_.reset(); else - CreateDetailsRenderText(); UpdateAccessibleName(); @@ -189,6 +193,22 @@ details_text_ = std::move(render_text); } +void SearchResultView::OnQueryRemovalAccepted(bool accepted, int event_flags) { + if (accepted) { + list_view_->SearchResultActionActivated( + this, ash::OmniBoxZeroStateAction::kRemoveSuggestion, event_flags); + } + + if (confirm_remove_by_long_press_) { + confirm_remove_by_long_press_ = false; + SetBackgroundHighlighted(false); + } + + RecordZeroStateSearchResultRemovalHistogram( + accepted ? ZeroStateSearchResutRemovalConfirmation::kRemovalConfirmed + : ZeroStateSearchResutRemovalConfirmation::kRemovalCanceled); +} + const char* SearchResultView::GetClassName() const { return kViewClassName; } @@ -329,16 +349,45 @@ ScrollRectToVisible(GetLocalBounds()); NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true); SetBackgroundHighlighted(true); + selected_ = true; + actions_view_->UpdateButtonsOnStateChanged(); } void SearchResultView::OnBlur() { SetBackgroundHighlighted(false); + selected_ = false; + actions_view_->UpdateButtonsOnStateChanged(); +} + +void SearchResultView::OnMouseEntered(const ui::MouseEvent& event) { + actions_view_->UpdateButtonsOnStateChanged(); +} + +void SearchResultView::OnMouseExited(const ui::MouseEvent& event) { + actions_view_->UpdateButtonsOnStateChanged(); +} + +void SearchResultView::OnGestureEvent(ui::GestureEvent* event) { + switch (event->type()) { + case ui::ET_GESTURE_LONG_PRESS: + ScrollRectToVisible(GetLocalBounds()); + NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true); + SetBackgroundHighlighted(true); + confirm_remove_by_long_press_ = true; + OnSearchResultActionActivated( + ash::OmniBoxZeroStateAction::kRemoveSuggestion, event->flags()); + event->SetHandled(); + break; + default: + break; + } + if (!event->handled()) + Button::OnGestureEvent(event); } void SearchResultView::ButtonPressed(views::Button* sender, const ui::Event& event) { DCHECK(sender == this); - list_view_->SearchResultActivated(this, event.flags()); } @@ -406,7 +455,34 @@ DCHECK_LT(index, result_->actions().size()); - list_view_->SearchResultActionActivated(this, index, event_flags); + if (result_->is_omnibox_search()) { + ash::OmniBoxZeroStateAction button_action = + ash::GetOmniBoxZeroStateAction(index); + + if (button_action == ash::OmniBoxZeroStateAction::kRemoveSuggestion) { + RecordZeroStateSearchResultUserActionHistogram( + ZeroStateSearchResultUserActionType::kRemoveResult); + RemoveQueryConfirmationDialog* dialog = new RemoveQueryConfirmationDialog( + base::BindOnce(&SearchResultView::OnQueryRemovalAccepted, + weak_ptr_factory_.GetWeakPtr()), + event_flags); + + // Calculate confirmation dialog's origin in screen coordinates. + gfx::Rect search_box_rect = list_view_->app_list_main_view() + ->search_box_view() + ->GetBoundsInScreen(); + dialog->Show(GetWidget()->GetNativeWindow(), search_box_rect); + } else if (button_action == + ash::OmniBoxZeroStateAction::kAppendSuggestion) { + RecordZeroStateSearchResultUserActionHistogram( + ZeroStateSearchResultUserActionType::kAppendResult); + list_view_->SearchResultActionActivated(this, index, event_flags); + } + } +} + +bool SearchResultView::IsSearchResultHoveredOrSelected() { + return IsMouseHovered() || selected(); } void SearchResultView::ShowContextMenuForView(views::View* source,
diff --git a/ash/app_list/views/search_result_view.h b/ash/app_list/views/search_result_view.h index 4b2a87c..db9b554 100644 --- a/ash/app_list/views/search_result_view.h +++ b/ash/app_list/views/search_result_view.h
@@ -73,6 +73,8 @@ // AppListMenuModelAdapter::Delegate overrides: void ExecuteCommand(int command_id, int event_flags) override; + bool selected() const { return selected_; } + private: friend class app_list::test::SearchResultListViewTest; @@ -84,6 +86,9 @@ void CreateTitleRenderText(); void CreateDetailsRenderText(); + // Callback for query suggstion removal confirmation. + void OnQueryRemovalAccepted(bool accepted, int event_flags); + // views::View overrides: const char* GetClassName() const override; gfx::Size CalculatePreferredSize() const override; @@ -93,6 +98,11 @@ void PaintButtonContents(gfx::Canvas* canvas) override; void OnFocus() override; void OnBlur() override; + void OnMouseEntered(const ui::MouseEvent& event) override; + void OnMouseExited(const ui::MouseEvent& event) override; + + // ui::EventHandler overrides: + void OnGestureEvent(ui::GestureEvent* event) override; // views::ButtonListener overrides: void ButtonPressed(views::Button* sender, const ui::Event& event) override; @@ -121,6 +131,7 @@ // SearchResultActionsViewDelegate overrides: void OnSearchResultActionActivated(size_t index, int event_flags) override; + bool IsSearchResultHoveredOrSelected() override; SearchResult* result_ = nullptr; // Owned by SearchModel::SearchResults. @@ -142,6 +153,8 @@ // Whether this view is selected. bool selected_ = false; + // Whether the removal confirmation dialog is invoked by long press touch. + bool confirm_remove_by_long_press_ = false; base::WeakPtrFactory<SearchResultView> weak_ptr_factory_;
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index a3bf5e0..35e4b55 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -1792,7 +1792,7 @@ <message name="IDS_ASSISTANT_TIMER_NOTIFICATION_TITLE" desc="Title for Assistant timer notification."> Time's up </message> - <message name="IDS_ASSISTANT_TIMER_NOTIFICATION_MESSAGE" desc="Message for Assistant timer notification. Example: -01:23s. [ICU Syntax]"> + <message name="IDS_ASSISTANT_TIMER_NOTIFICATION_MESSAGE" desc="Message for Assistant timer notification. Example: -01:23s."> <ph name="SIGN">{0}<ex>-</ex></ph><ph name="MINUTES_REMAINING">{1,number,00}<ex>01</ex></ph>:<ph name="SECONDS_REMAINING">{2,number,00}<ex>02</ex></ph>s </message> <message name="IDS_ASSISTANT_TIMER_NOTIFICATION_ADD_1_MIN_BUTTON" desc="Label for button to add 1 minute to timer in Assistant timer notification.">
diff --git a/ash/bluetooth_devices_observer.cc b/ash/bluetooth_devices_observer.cc index 7b7ce1d..97136206 100644 --- a/ash/bluetooth_devices_observer.cc +++ b/ash/bluetooth_devices_observer.cc
@@ -15,8 +15,8 @@ weak_factory_(this) { if (device::BluetoothAdapterFactory::IsBluetoothSupported()) { device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothDevicesObserver::InitializeOnAdapterReady, - weak_factory_.GetWeakPtr())); + base::BindOnce(&BluetoothDevicesObserver::InitializeOnAdapterReady, + weak_factory_.GetWeakPtr())); } else { adapter_or_device_changed_callback_.Run(/*device=*/nullptr); }
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc index 2f724c33..30fc788 100644 --- a/ash/login/ui/lock_contents_view.cc +++ b/ash/login/ui/lock_contents_view.cc
@@ -42,6 +42,7 @@ #include "components/user_manager/user_type.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/ui_base_features.h" #include "ui/display/display.h" #include "ui/display/manager/display_manager.h" #include "ui/display/manager/managed_display_info.h" @@ -388,7 +389,7 @@ system_info_ = new views::View(); auto* system_info_layout = system_info_->SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::kVertical, gfx::Insets(5, 8))); + views::BoxLayout::kVertical, gfx::Insets(6, 8))); system_info_layout->set_cross_axis_alignment( views::BoxLayout::CROSS_AXIS_ALIGNMENT_END); system_info_->SetVisible(false); @@ -879,6 +880,8 @@ if (system_info_->child_count() == 0) { for (int i = 0; i < 3; ++i) system_info_->AddChildView(create_info_label()); + if (::features::IsSingleProcessMash()) + system_info_->AddChildView(create_info_label()); } if (show) @@ -893,6 +896,8 @@ update_label(0, os_version_label_text); update_label(1, enterprise_info_text); update_label(2, bluetooth_name); + if (::features::IsSingleProcessMash()) + update_label(3, "SingleProcessMash enabled"); LayoutTopHeader(); }
diff --git a/ash/magnifier/docked_magnifier_controller.cc b/ash/magnifier/docked_magnifier_controller.cc index eafd4991..2e958f9 100644 --- a/ash/magnifier/docked_magnifier_controller.cc +++ b/ash/magnifier/docked_magnifier_controller.cc
@@ -8,7 +8,7 @@ #include "ash/accessibility/accessibility_controller.h" #include "ash/host/ash_window_tree_host.h" -#include "ash/magnifier/magnifier_scale_utils.h" +#include "ash/magnifier/magnifier_utils.h" #include "ash/public/cpp/ash_pref_names.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/root_window_controller.h" @@ -25,6 +25,7 @@ #include "ui/aura/client/drag_drop_client.h" #include "ui/aura/env.h" #include "ui/aura/window_tree_host.h" +#include "ui/base/ime/ime_bridge.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_input_client.h" #include "ui/compositor/layer.h" @@ -58,15 +59,6 @@ return display::Screen::GetScreen()->GetCursorScreenPoint(); } -// Returns the InputMethod associated with the WindowTreeHost of the given -// window. -ui::InputMethod* GetInputMethodForWindow(aura::Window* window) { - DCHECK(window); - aura::WindowTreeHost* host = window->GetHost(); - DCHECK(host); - return host->GetInputMethod(); -} - // Updates the workarea of the display associated with |window| such that the // given magnifier viewport |height| is allocated at the top of the screen. void SetViewportHeightInWorkArea(aura::Window* window, int height) { @@ -133,9 +125,17 @@ DockedMagnifierController::DockedMagnifierController() : binding_(this) { Shell::Get()->session_controller()->AddObserver(this); + if (ui::IMEBridge::Get()) + ui::IMEBridge::Get()->AddObserver(this); } DockedMagnifierController::~DockedMagnifierController() { + if (input_method_) + input_method_->RemoveObserver(this); + input_method_ = nullptr; + if (ui::IMEBridge::Get()) + ui::IMEBridge::Get()->RemoveObserver(this); + Shell* shell = Shell::Get(); shell->session_controller()->RemoveObserver(this); @@ -194,7 +194,7 @@ } void DockedMagnifierController::StepToNextScaleValue(int delta_index) { - SetScale(magnifier_scale_utils::GetNextMagnifierScaleValue( + SetScale(magnifier_utils::GetNextMagnifierScaleValue( delta_index, GetScale(), kMinMagnifierScale, kMaxMagnifierScale)); } @@ -314,7 +314,7 @@ // Notes: - Clamping of the new scale value happens inside SetScale(). // - Refreshing the viewport happens in the handler of the scale pref // changes. - SetScale(magnifier_scale_utils::GetScaleFromScroll( + SetScale(magnifier_utils::GetScaleFromScroll( event->y_offset() * kScrollScaleFactor, GetScale(), kMaxMagnifierScale, kMinMagnifierScale)); event->StopPropagation(); @@ -331,6 +331,29 @@ CenterOnPoint(event_screen_point); } +void DockedMagnifierController::OnInputContextHandlerChanged() { + if (!GetEnabled()) + return; + + auto* new_input_method = + magnifier_utils::GetInputMethod(current_source_root_window_); + if (new_input_method == input_method_) + return; + + if (input_method_) + input_method_->RemoveObserver(this); + input_method_ = new_input_method; + if (input_method_) + input_method_->AddObserver(this); +} + +void DockedMagnifierController::OnInputMethodDestroyed( + const ui::InputMethod* input_method) { + DCHECK_EQ(input_method, input_method_); + input_method_->RemoveObserver(this); + input_method_ = nullptr; +} + void DockedMagnifierController::OnCaretBoundsChanged( const ui::TextInputClient* client) { if (!GetEnabled()) { @@ -449,10 +472,10 @@ } if (update_old_root_workarea) SetViewportHeightInWorkArea(old_root_window, 0); - ui::InputMethod* old_input_method = - GetInputMethodForWindow(old_root_window); - if (old_input_method) - old_input_method->RemoveObserver(this); + + if (input_method_) + input_method_->RemoveObserver(this); + input_method_ = nullptr; // Reset mouse cursor confinement to default. RootWindowController::ForWindow(old_root_window) @@ -487,10 +510,9 @@ CreateMagnifierViewport(); - ui::InputMethod* new_input_method = - GetInputMethodForWindow(current_source_root_window_); - if (new_input_method) - new_input_method->AddObserver(this); + input_method_ = magnifier_utils::GetInputMethod(current_source_root_window_); + if (input_method_) + input_method_->AddObserver(this); DCHECK(Shell::Get()->aura_env()->context_factory_private()); DCHECK(viewport_widget_);
diff --git a/ash/magnifier/docked_magnifier_controller.h b/ash/magnifier/docked_magnifier_controller.h index da60b7349..bbcd052b 100644 --- a/ash/magnifier/docked_magnifier_controller.h +++ b/ash/magnifier/docked_magnifier_controller.h
@@ -12,6 +12,7 @@ #include "ash/public/interfaces/docked_magnifier_controller.mojom.h" #include "ash/session/session_observer.h" #include "mojo/public/cpp/bindings/binding.h" +#include "ui/base/ime/ime_bridge_observer.h" #include "ui/base/ime/input_method_observer.h" #include "ui/events/event_handler.h" #include "ui/views/widget/widget_observer.h" @@ -46,6 +47,7 @@ : public mojom::DockedMagnifierController, public SessionObserver, public ui::EventHandler, + public ui::IMEBridgeObserver, public ui::InputMethodObserver, public views::WidgetObserver, public WindowTreeHostManager::Observer { @@ -90,11 +92,15 @@ void OnScrollEvent(ui::ScrollEvent* event) override; void OnTouchEvent(ui::TouchEvent* event) override; + // ui::IMEBridgeObserver: + void OnRequestSwitchEngine() override {} + void OnInputContextHandlerChanged() override; + // ui::InputMethodObserver: void OnFocus() override {} void OnBlur() override {} void OnTextInputStateChanged(const ui::TextInputClient* client) override {} - void OnInputMethodDestroyed(const ui::InputMethod* input_method) override {} + void OnInputMethodDestroyed(const ui::InputMethod* input_method) override; void OnShowVirtualKeyboardIfEnabled() override {} void OnCaretBoundsChanged(const ui::TextInputClient* client) override; @@ -196,6 +202,9 @@ // ash_unittests. PrefService* active_user_pref_service_ = nullptr; + // The currently active input method, observed for caret bounds changes. + ui::InputMethod* input_method_ = nullptr; + std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_; mojo::Binding<mojom::DockedMagnifierController> binding_;
diff --git a/ash/magnifier/magnification_controller.cc b/ash/magnifier/magnification_controller.cc index 361de42..601b19f6 100644 --- a/ash/magnifier/magnification_controller.cc +++ b/ash/magnifier/magnification_controller.cc
@@ -13,7 +13,7 @@ #include "ash/display/root_window_transformers.h" #include "ash/host/ash_window_tree_host.h" #include "ash/host/root_window_transformer.h" -#include "ash/magnifier/magnifier_scale_utils.h" +#include "ash/magnifier/magnifier_utils.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/root_window_controller.h" #include "ash/shell.h" @@ -83,18 +83,6 @@ gfx::ToCeiledPoint(host_location_3f.AsPointF())); } -ui::InputMethod* GetInputMethod(aura::Window* root_window) { - ui::IMEBridge* bridge = ui::IMEBridge::Get(); - if (bridge && bridge->GetInputContextHandler()) - return bridge->GetInputContextHandler()->GetInputMethod(); - - if (root_window->GetHost()) - return root_window->GetHost()->GetInputMethod(); - - // Needed by a handful of browser tests that use MockInputMethod. - return Shell::GetRootWindowForNewWindows()->GetHost()->GetInputMethod(); -} - } // namespace class MagnificationController::GestureProviderClient @@ -154,7 +142,7 @@ void MagnificationController::SetEnabled(bool enabled) { if (enabled) { if (!is_enabled_) { - input_method_ = GetInputMethod(root_window_); + input_method_ = magnifier_utils::GetInputMethod(root_window_); if (input_method_) input_method_->AddObserver(this); } @@ -217,7 +205,7 @@ } void MagnificationController::StepToNextScaleValue(int delta_index) { - SetScale(magnifier_scale_utils::GetNextMagnifierScaleValue( + SetScale(magnifier_utils::GetNextMagnifierScaleValue( delta_index, GetScale(), kNonMagnifiedScale, kMaxMagnifiedScale), true /* animate */); } @@ -315,8 +303,11 @@ } void MagnificationController::OnInputContextHandlerChanged() { - auto* new_input_method = GetInputMethod(root_window_); - if (!is_enabled_ || new_input_method == input_method_) + if (!is_enabled_) + return; + + auto* new_input_method = magnifier_utils::GetInputMethod(root_window_); + if (new_input_method == input_method_) return; if (input_method_) @@ -475,7 +466,7 @@ } if (event->type() == ui::ET_SCROLL) { - SetScale(magnifier_scale_utils::GetScaleFromScroll( + SetScale(magnifier_utils::GetScaleFromScroll( event->y_offset() * kScrollScaleChangeFactor, GetScale(), kMaxMagnifiedScale, kNonMagnifiedScale), false /* animate */);
diff --git a/ash/magnifier/magnification_controller_unittest.cc b/ash/magnifier/magnification_controller_unittest.cc index af67c43..86aef3e 100644 --- a/ash/magnifier/magnification_controller_unittest.cc +++ b/ash/magnifier/magnification_controller_unittest.cc
@@ -4,8 +4,8 @@ #include "ash/magnifier/magnification_controller.h" -#include "ash/magnifier/magnifier_scale_utils.h" #include "ash/magnifier/magnifier_test_utils.h" +#include "ash/magnifier/magnifier_utils.h" #include "ash/public/cpp/ash_switches.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" @@ -450,7 +450,7 @@ GetMagnificationController()->SetEnabled(true); EXPECT_FLOAT_EQ(2.f, GetMagnificationController()->GetScale()); - scale *= magnifier_scale_utils::kMagnificationScaleFactor; + scale *= magnifier_utils::kMagnificationScaleFactor; GetMagnificationController()->SetScale(scale, false); EXPECT_FLOAT_EQ(2.3784142, GetMagnificationController()->GetScale()); event_generator->MoveMouseToInHost(gfx::Point(400, 300)); @@ -459,21 +459,21 @@ EXPECT_EQ("566,299", env->last_mouse_location().ToString()); EXPECT_EQ("705,300", GetHostMouseLocation()); - scale *= magnifier_scale_utils::kMagnificationScaleFactor; + scale *= magnifier_utils::kMagnificationScaleFactor; GetMagnificationController()->SetScale(scale, false); EXPECT_FLOAT_EQ(2.8284268, GetMagnificationController()->GetScale()); event_generator->MoveMouseToInHost(gfx::Point(799, 300)); EXPECT_EQ("599,299", env->last_mouse_location().ToString()); EXPECT_EQ("702,300", GetHostMouseLocation()); - scale *= magnifier_scale_utils::kMagnificationScaleFactor; + scale *= magnifier_utils::kMagnificationScaleFactor; GetMagnificationController()->SetScale(scale, false); EXPECT_FLOAT_EQ(3.3635852, GetMagnificationController()->GetScale()); event_generator->MoveMouseToInHost(gfx::Point(799, 300)); EXPECT_EQ("627,298", env->last_mouse_location().ToString()); EXPECT_EQ("707,300", GetHostMouseLocation()); - scale *= magnifier_scale_utils::kMagnificationScaleFactor; + scale *= magnifier_utils::kMagnificationScaleFactor; GetMagnificationController()->SetScale(scale, false); EXPECT_FLOAT_EQ(4.f, GetMagnificationController()->GetScale()); event_generator->MoveMouseToInHost(gfx::Point(799, 300)); @@ -496,7 +496,7 @@ GetMagnificationController()->SetEnabled(true); EXPECT_FLOAT_EQ(2.f, GetMagnificationController()->GetScale()); - scale *= magnifier_scale_utils::kMagnificationScaleFactor; + scale *= magnifier_utils::kMagnificationScaleFactor; GetMagnificationController()->SetScale(scale, false); EXPECT_FLOAT_EQ(2.3784142, GetMagnificationController()->GetScale()); event_generator->MoveMouseToInHost(gfx::Point(400, 300)); @@ -505,21 +505,21 @@ EXPECT_EQ("231,299", env->last_mouse_location().ToString()); EXPECT_EQ("100,300", GetHostMouseLocation()); - scale *= magnifier_scale_utils::kMagnificationScaleFactor; + scale *= magnifier_utils::kMagnificationScaleFactor; GetMagnificationController()->SetScale(scale, false); EXPECT_FLOAT_EQ(2.8284268, GetMagnificationController()->GetScale()); event_generator->MoveMouseToInHost(gfx::Point(0, 300)); EXPECT_EQ("194,299", env->last_mouse_location().ToString()); EXPECT_EQ("99,300", GetHostMouseLocation()); - scale *= magnifier_scale_utils::kMagnificationScaleFactor; + scale *= magnifier_utils::kMagnificationScaleFactor; GetMagnificationController()->SetScale(scale, false); EXPECT_FLOAT_EQ(3.3635852, GetMagnificationController()->GetScale()); event_generator->MoveMouseToInHost(gfx::Point(0, 300)); EXPECT_EQ("164,298", env->last_mouse_location().ToString()); EXPECT_EQ("98,300", GetHostMouseLocation()); - scale *= magnifier_scale_utils::kMagnificationScaleFactor; + scale *= magnifier_utils::kMagnificationScaleFactor; GetMagnificationController()->SetScale(scale, false); EXPECT_FLOAT_EQ(4.f, GetMagnificationController()->GetScale()); event_generator->MoveMouseToInHost(gfx::Point(0, 300));
diff --git a/ash/magnifier/magnifier_scale_utils.cc b/ash/magnifier/magnifier_utils.cc similarity index 72% rename from ash/magnifier/magnifier_scale_utils.cc rename to ash/magnifier/magnifier_utils.cc index 3289620..1267844 100644 --- a/ash/magnifier/magnifier_scale_utils.cc +++ b/ash/magnifier/magnifier_utils.cc
@@ -2,16 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ash/magnifier/magnifier_scale_utils.h" +#include "ash/magnifier/magnifier_utils.h" #include <algorithm> #include <cmath> +#include "ash/shell.h" #include "base/logging.h" #include "base/numerics/ranges.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/ime/ime_bridge.h" namespace ash { -namespace magnifier_scale_utils { +namespace magnifier_utils { namespace { @@ -54,5 +58,17 @@ return base::ClampToRange(new_scale, min_scale, max_scale); } -} // namespace magnifier_scale_utils +ui::InputMethod* GetInputMethod(aura::Window* root_window) { + ui::IMEBridge* bridge = ui::IMEBridge::Get(); + if (bridge && bridge->GetInputContextHandler()) + return bridge->GetInputContextHandler()->GetInputMethod(); + + if (root_window && root_window->GetHost()) + return root_window->GetHost()->GetInputMethod(); + + // Needed by a handful of browser tests that use MockInputMethod. + return Shell::GetRootWindowForNewWindows()->GetHost()->GetInputMethod(); +} + +} // namespace magnifier_utils } // namespace ash
diff --git a/ash/magnifier/magnifier_scale_utils.h b/ash/magnifier/magnifier_utils.h similarity index 84% rename from ash/magnifier/magnifier_scale_utils.h rename to ash/magnifier/magnifier_utils.h index 3a9065a9..b4422c9 100644 --- a/ash/magnifier/magnifier_scale_utils.h +++ b/ash/magnifier/magnifier_utils.h
@@ -2,13 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef ASH_MAGNIFIER_MAGNIFIER_SCALE_UTILS_H_ -#define ASH_MAGNIFIER_MAGNIFIER_SCALE_UTILS_H_ +#ifndef ASH_MAGNIFIER_MAGNIFIER_UTILS_H_ +#define ASH_MAGNIFIER_MAGNIFIER_UTILS_H_ #include "ash/ash_export.h" +namespace aura { +class Window; +} + +namespace ui { +class InputMethod; +} + namespace ash { -namespace magnifier_scale_utils { +namespace magnifier_utils { // Factor of magnification scale. For example, when this value is 1.189, scale // value will be changed x1.000, x1.189, x1.414, x1.681, x2.000, ... @@ -43,7 +51,10 @@ float min_scale, float max_scale); -} // namespace magnifier_scale_utils +// Returns the active InputMethod, or that associated with |root_window|. +ui::InputMethod* GetInputMethod(aura::Window* root_window); + +} // namespace magnifier_utils } // namespace ash -#endif // ASH_MAGNIFIER_MAGNIFIER_SCALE_UTILS_H_ +#endif // ASH_MAGNIFIER_MAGNIFIER_UTILS_H_
diff --git a/ash/magnifier/magnifier_scale_utils_unittest.cc b/ash/magnifier/magnifier_utils_unittest.cc similarity index 94% rename from ash/magnifier/magnifier_scale_utils_unittest.cc rename to ash/magnifier/magnifier_utils_unittest.cc index 51dacd7e..a004cb7 100644 --- a/ash/magnifier/magnifier_scale_utils_unittest.cc +++ b/ash/magnifier/magnifier_utils_unittest.cc
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ash/magnifier/magnifier_scale_utils.h" +#include "ash/magnifier/magnifier_utils.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash { -namespace magnifier_scale_utils { +namespace magnifier_utils { namespace { TEST(MagnifierScaleUtilsTest, AdjustScaleFromScroll) { @@ -64,5 +64,5 @@ } } // namespace -} // namespace magnifier_scale_utils +} // namespace magnifier_utils } // namespace ash
diff --git a/ash/public/cpp/app_list/app_list_struct_traits.cc b/ash/public/cpp/app_list/app_list_struct_traits.cc index ba8972d..3f740d8f 100644 --- a/ash/public/cpp/app_list/app_list_struct_traits.cc +++ b/ash/public/cpp/app_list/app_list_struct_traits.cc
@@ -23,46 +23,6 @@ } //////////////////////////////////////////////////////////////////////////////// -// SearchResultActionLabel: - -// static -ash::mojom::SearchResultActionLabelDataView::Tag UnionTraits< - ash::mojom::SearchResultActionLabelDataView, - ash::SearchResultAction>::GetTag(const ash::SearchResultAction& action) { - if (action.label_text.empty()) - return ash::mojom::SearchResultActionLabelDataView::Tag::IMAGE_LABEL; - else - return ash::mojom::SearchResultActionLabelDataView::Tag::TEXT_LABEL; -} - -// static -bool UnionTraits<ash::mojom::SearchResultActionLabelDataView, - ash::SearchResultAction>:: - Read(ash::mojom::SearchResultActionLabelDataView data, - ash::SearchResultAction* out) { - switch (data.tag()) { - case ash::mojom::SearchResultActionLabelDataView::Tag::IMAGE_LABEL: { - ash::mojom::SearchResultActionImageLabelDataView image_label_data_view; - data.GetImageLabelDataView(&image_label_data_view); - if (!image_label_data_view.ReadBaseImage(&out->base_image)) - return false; - if (!image_label_data_view.ReadHoverImage(&out->hover_image)) - return false; - if (!image_label_data_view.ReadPressedImage(&out->pressed_image)) - return false; - return true; - } - case ash::mojom::SearchResultActionLabelDataView::Tag::TEXT_LABEL: { - if (!data.ReadTextLabel(&out->label_text)) - return false; - return true; - } - } - NOTREACHED(); - return false; -} - -//////////////////////////////////////////////////////////////////////////////// // SearchResultAction: // static @@ -72,8 +32,9 @@ ash::SearchResultAction* out) { if (!data.ReadTooltipText(&out->tooltip_text)) return false; - if (!data.ReadLabel(out)) + if (!data.ReadImage(&out->image)) return false; + out->visible_on_hover = data.visible_on_hover(); return true; }
diff --git a/ash/public/cpp/app_list/app_list_struct_traits.h b/ash/public/cpp/app_list/app_list_struct_traits.h index 297b02cf..499e767 100644 --- a/ash/public/cpp/app_list/app_list_struct_traits.h +++ b/ash/public/cpp/app_list/app_list_struct_traits.h
@@ -216,60 +216,9 @@ }; //////////////////////////////////////////////////////////////////////////////// -// SearchResultActionLabel: - -template <> -struct UnionTraits<ash::mojom::SearchResultActionLabelDataView, - ash::SearchResultAction> { - static ash::mojom::SearchResultActionLabelDataView::Tag GetTag( - const ash::SearchResultAction& action); - - static bool Read(ash::mojom::SearchResultActionLabelDataView data, - ash::SearchResultAction* out); - - static const ash::SearchResultAction& image_label( - const ash::SearchResultAction& action) { - return action; - } - - static const ash::SearchResultAction& text_label( - const ash::SearchResultAction& action) { - return action; - } -}; - -//////////////////////////////////////////////////////////////////////////////// // SearchResultAction: template <> -struct StructTraits<ash::mojom::SearchResultActionImageLabelDataView, - ash::SearchResultAction> { - static const gfx::ImageSkia& base_image( - const ash::SearchResultAction& action) { - return action.base_image; - } - static const gfx::ImageSkia& hover_image( - const ash::SearchResultAction& action) { - return action.hover_image; - } - static const gfx::ImageSkia& pressed_image( - const ash::SearchResultAction& action) { - return action.pressed_image; - } -}; - -template <> -struct StructTraits<mojo_base::mojom::String16DataView, - ash::SearchResultAction> { - static base::span<const uint16_t> data( - const ash::SearchResultAction& action) { - return base::make_span( - reinterpret_cast<const uint16_t*>(action.label_text.data()), - action.label_text.size()); - } -}; - -template <> struct StructTraits<ash::mojom::SearchResultActionDataView, ash::SearchResultAction> { static bool Read(ash::mojom::SearchResultActionDataView data, @@ -280,9 +229,12 @@ return action.tooltip_text; } - static const ash::SearchResultAction& label( - const ash::SearchResultAction& action) { - return action; + static const gfx::ImageSkia& image(const ash::SearchResultAction& action) { + return action.image; + } + + static bool visible_on_hover(const ash::SearchResultAction& action) { + return action.visible_on_hover; } };
diff --git a/ash/public/cpp/app_list/app_list_types.cc b/ash/public/cpp/app_list/app_list_types.cc index cdcc68a..a16d517 100644 --- a/ash/public/cpp/app_list/app_list_types.cc +++ b/ash/public/cpp/app_list/app_list_types.cc
@@ -8,6 +8,16 @@ const char kOemFolderId[] = "ddb1da55-d478-4243-8642-56d3041f0263"; +OmniBoxZeroStateAction GetOmniBoxZeroStateAction(int button_index) { + if (button_index < 0 || + button_index >= + static_cast<int>(ash::OmniBoxZeroStateAction::kZeroStateActionMax)) { + return ash::OmniBoxZeroStateAction::kZeroStateActionMax; + } + + return static_cast<ash::OmniBoxZeroStateAction>(button_index); +} + //////////////////////////////////////////////////////////////////////////////// // SearchResultTag: @@ -21,18 +31,12 @@ SearchResultAction::SearchResultAction() {} -SearchResultAction::SearchResultAction(const gfx::ImageSkia& base_image, - const gfx::ImageSkia& hover_image, - const gfx::ImageSkia& pressed_image, - const base::string16& tooltip_text) - : base_image(base_image), - hover_image(hover_image), - pressed_image(pressed_image), - tooltip_text(tooltip_text) {} - -SearchResultAction::SearchResultAction(const base::string16& label_text, - const base::string16& tooltip_text) - : tooltip_text(tooltip_text), label_text(label_text) {} +SearchResultAction::SearchResultAction(const gfx::ImageSkia& image, + const base::string16& tooltip_text, + bool visible_on_hover) + : image(image), + tooltip_text(tooltip_text), + visible_on_hover(visible_on_hover) {} SearchResultAction::SearchResultAction(const SearchResultAction& other) = default;
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h index ba75c5a4..bba4c9a 100644 --- a/ash/public/cpp/app_list/app_list_types.h +++ b/ash/public/cpp/app_list/app_list_types.h
@@ -63,6 +63,20 @@ kLast, // Don't use over IPC }; +// Actions for OmniBox zero state suggestion. +enum OmniBoxZeroStateAction { + // Removes the zero state suggestion. + kRemoveSuggestion = 0, + // Appends the suggestion to search box query. + kAppendSuggestion, + // kZeroStateActionMax is always last. + kZeroStateActionMax +}; + +// Returns OmniBoxZeroStateAction mapped for |button_index|. +ASH_PUBLIC_EXPORT OmniBoxZeroStateAction +GetOmniBoxZeroStateAction(int button_index); + // A tagged range in search result text. struct ASH_PUBLIC_EXPORT SearchResultTag { // Similar to ACMatchClassification::Style, the style values are not @@ -88,20 +102,16 @@ // button with the label text will be used. struct ASH_PUBLIC_EXPORT SearchResultAction { SearchResultAction(); - SearchResultAction(const gfx::ImageSkia& base_image, - const gfx::ImageSkia& hover_image, - const gfx::ImageSkia& pressed_image, - const base::string16& tooltip_text); - SearchResultAction(const base::string16& label_text, - const base::string16& tooltip_text); + SearchResultAction(const gfx::ImageSkia& image, + const base::string16& tooltip_text, + bool visible_on_hover); SearchResultAction(const SearchResultAction& other); ~SearchResultAction(); - gfx::ImageSkia base_image; - gfx::ImageSkia hover_image; - gfx::ImageSkia pressed_image; + gfx::ImageSkia image; base::string16 tooltip_text; - base::string16 label_text; + // Visible when button or its parent row in hover state. + bool visible_on_hover; }; using SearchResultActions = std::vector<SearchResultAction>;
diff --git a/ash/public/cpp/app_list/vector_icons/BUILD.gn b/ash/public/cpp/app_list/vector_icons/BUILD.gn index 0b1625c..52cb6a11 100644 --- a/ash/public/cpp/app_list/vector_icons/BUILD.gn +++ b/ash/public/cpp/app_list/vector_icons/BUILD.gn
@@ -21,6 +21,8 @@ "mic_black.icon", "search.icon", "search_engine_not_google.icon", + "search_result_append.icon", + "search_result_remove.icon", ] }
diff --git a/ash/public/cpp/app_list/vector_icons/search_result_append.icon b/ash/public/cpp/app_list/vector_icons/search_result_append.icon new file mode 100644 index 0000000..32f576a --- /dev/null +++ b/ash/public/cpp/app_list/vector_icons/search_result_append.icon
@@ -0,0 +1,12 @@ +CANVAS_DIMENSIONS, 20, +MOVE_TO, 14, 4, +R_V_LINE_TO, 2, +H_LINE_TO, 7.5f, +R_LINE_TO, 8.5f, 8.5f, +R_LINE_TO, -1.5f, 1.5f, +LINE_TO, 6, 7.5f, +V_LINE_TO, 14, +H_LINE_TO, 4, +V_LINE_TO, 4, +CLOSE +
diff --git a/ash/public/cpp/app_list/vector_icons/search_result_remove.icon b/ash/public/cpp/app_list/vector_icons/search_result_remove.icon new file mode 100644 index 0000000..2e23df1 --- /dev/null +++ b/ash/public/cpp/app_list/vector_icons/search_result_remove.icon
@@ -0,0 +1,23 @@ +CANVAS_DIMENSIONS, 20, +MOVE_TO, 10, 18, +ARC_TO, 8, 8, 0, 1, 1, 10, 2, +R_ARC_TO, 8, 8, 0, 0, 1, 0, 16, +CLOSE, +R_MOVE_TO, 0, -2, +ARC_TO, 6, 6, 0, 1, 0, 10, 4, +R_ARC_TO, 6, 6, 0, 0, 0, 0, 12, +CLOSE, +R_MOVE_TO, 3, -8.03f, +LINE_TO, 10.97f, 10, +LINE_TO, 13, 12.03f, +R_LINE_TO, -0.97f, 0.97f, +LINE_TO, 10, 10.97f, +LINE_TO, 7.97f, 13, +LINE_TO, 7, 12.03f, +LINE_TO, 9.03f, 10, +LINE_TO, 7, 7.97f, +LINE_TO, 7.97f, 7, +LINE_TO, 10, 9.03f, +LINE_TO, 12.03f, 7, +R_LINE_TO, 0.97f, 0.97f, +CLOSE
diff --git a/ash/public/interfaces/app_list.mojom b/ash/public/interfaces/app_list.mojom index c11efda8..b85f786 100644 --- a/ash/public/interfaces/app_list.mojom +++ b/ash/public/interfaces/app_list.mojom
@@ -130,21 +130,13 @@ }; // Data representing an action that can be performed on a search result. -// An action could be represented as an icon set or as a blue button with -// a label. Icon set is chosen if label text is empty. Otherwise, a blue -// button with the label text will be used. -struct SearchResultActionImageLabel { - gfx.mojom.ImageSkia base_image; - gfx.mojom.ImageSkia hover_image; - gfx.mojom.ImageSkia pressed_image; -}; -union SearchResultActionLabel { - SearchResultActionImageLabel image_label; - mojo_base.mojom.String16 text_label; -}; +// An action could be represented as an image button, it will be always visibe +// if |visible_on_hover| is false; otherwise how it is visible only when +// when its associated search result is hovered. struct SearchResultAction { mojo_base.mojom.String16 tooltip_text; - SearchResultActionLabel label; + gfx.mojom.ImageSkia image; + bool visible_on_hover; }; // The Chrome app list (aka Launcher), is the place where user can find and
diff --git a/ash/public/interfaces/shell_test_api.test-mojom b/ash/public/interfaces/shell_test_api.test-mojom index bab9b461..12dbdbc 100644 --- a/ash/public/interfaces/shell_test_api.test-mojom +++ b/ash/public/interfaces/shell_test_api.test-mojom
@@ -26,4 +26,8 @@ // Enters or exits overview mode. ToggleOverviewMode() => (); + + // Used to emulate display change when run in a desktop environment instead + // of on a device. + AddRemoveDisplay(); };
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc index 91483180..79ee1406d 100644 --- a/ash/shell_test_api.cc +++ b/ash/shell_test_api.cc
@@ -117,4 +117,8 @@ std::move(cb).Run(); } +void ShellTestApi::AddRemoveDisplay() { + shell_->display_manager()->AddRemoveDisplay(); +} + } // namespace ash
diff --git a/ash/shell_test_api.h b/ash/shell_test_api.h index 1dcfb91b..f613191 100644 --- a/ash/shell_test_api.h +++ b/ash/shell_test_api.h
@@ -61,6 +61,7 @@ SnapWindowInSplitViewCallback cb) override; void ToggleFullscreen(ToggleFullscreenCallback cb) override; void ToggleOverviewMode(ToggleOverviewModeCallback cb) override; + void AddRemoveDisplay() override; private: Shell* shell_; // not owned
diff --git a/ash/system/bluetooth/bluetooth_notification_controller.cc b/ash/system/bluetooth/bluetooth_notification_controller.cc index f3472cec..1996722 100644 --- a/ash/system/bluetooth/bluetooth_notification_controller.cc +++ b/ash/system/bluetooth/bluetooth_notification_controller.cc
@@ -134,8 +134,8 @@ BluetoothNotificationController::BluetoothNotificationController() : weak_ptr_factory_(this) { BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothNotificationController::OnGetAdapter, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce(&BluetoothNotificationController::OnGetAdapter, + weak_ptr_factory_.GetWeakPtr())); } BluetoothNotificationController::~BluetoothNotificationController() {
diff --git a/ash/system/bluetooth/bluetooth_power_controller.cc b/ash/system/bluetooth/bluetooth_power_controller.cc index 89060a59..7752c53 100644 --- a/ash/system/bluetooth/bluetooth_power_controller.cc +++ b/ash/system/bluetooth/bluetooth_power_controller.cc
@@ -24,8 +24,8 @@ BluetoothPowerController::BluetoothPowerController() : weak_ptr_factory_(this) { device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothPowerController::InitializeOnAdapterReady, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce(&BluetoothPowerController::InitializeOnAdapterReady, + weak_ptr_factory_.GetWeakPtr())); Shell::Get()->AddShellObserver(this); Shell::Get()->session_controller()->AddObserver(this); }
diff --git a/ash/system/bluetooth/tray_bluetooth_helper_legacy.cc b/ash/system/bluetooth/tray_bluetooth_helper_legacy.cc index 985009a..72f60a2 100644 --- a/ash/system/bluetooth/tray_bluetooth_helper_legacy.cc +++ b/ash/system/bluetooth/tray_bluetooth_helper_legacy.cc
@@ -153,8 +153,8 @@ void TrayBluetoothHelperLegacy::Initialize() { device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&TrayBluetoothHelperLegacy::InitializeOnAdapterReady, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce(&TrayBluetoothHelperLegacy::InitializeOnAdapterReady, + weak_ptr_factory_.GetWeakPtr())); } void TrayBluetoothHelperLegacy::StartBluetoothDiscovering() {
diff --git a/ash/system/power/peripheral_battery_notifier.cc b/ash/system/power/peripheral_battery_notifier.cc index 1d77aae..6605cbe 100644 --- a/ash/system/power/peripheral_battery_notifier.cc +++ b/ash/system/power/peripheral_battery_notifier.cc
@@ -167,8 +167,8 @@ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver( this); device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&PeripheralBatteryNotifier::InitializeOnBluetoothReady, - weakptr_factory_->GetWeakPtr())); + base::BindOnce(&PeripheralBatteryNotifier::InitializeOnBluetoothReady, + weakptr_factory_->GetWeakPtr())); } PeripheralBatteryNotifier::~PeripheralBatteryNotifier() {
diff --git a/base/third_party/symbolize/README.chromium b/base/third_party/symbolize/README.chromium index 1e28438e..152b5f7f 100644 --- a/base/third_party/symbolize/README.chromium +++ b/base/third_party/symbolize/README.chromium
@@ -3,7 +3,7 @@ License: BSD The following files are copied AS-IS from: -https://github.com/google/glog/tree/781096619d3dd368cfebd33889e417a168493ce7 +https://github.com/google/glog/tree/5d46e1bcfc92bf06a9ca3b3f1c5bb1dc024d9ecd - demangle.cc - demangle.h
diff --git a/base/third_party/symbolize/demangle.cc b/base/third_party/symbolize/demangle.cc index d7b3af18..9369f9a 100644 --- a/base/third_party/symbolize/demangle.cc +++ b/base/third_party/symbolize/demangle.cc
@@ -30,7 +30,7 @@ // Author: Satoru Takabayashi // // For reference check out: -// http://www.codesourcery.com/public/cxx-abi/abi.html#mangling +// http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling // // Note that we only have partial C++0x support yet. @@ -423,6 +423,8 @@ static bool ParseFloatNumber(State *state); static bool ParseSeqId(State *state); static bool ParseIdentifier(State *state, int length); +static bool ParseAbiTags(State *state); +static bool ParseAbiTag(State *state); static bool ParseOperatorName(State *state); static bool ParseSpecialName(State *state); static bool ParseCallOffset(State *state); @@ -594,13 +596,13 @@ // <unqualified-name> ::= <operator-name> // ::= <ctor-dtor-name> -// ::= <source-name> -// ::= <local-source-name> +// ::= <source-name> [<abi-tags>] +// ::= <local-source-name> [<abi-tags>] static bool ParseUnqualifiedName(State *state) { return (ParseOperatorName(state) || ParseCtorDtorName(state) || - ParseSourceName(state) || - ParseLocalSourceName(state)); + (ParseSourceName(state) && Optional(ParseAbiTags(state))) || + (ParseLocalSourceName(state) && Optional(ParseAbiTags(state)))); } // <source-name> ::= <positive length number> <identifier> @@ -703,6 +705,23 @@ return true; } +// <abi-tags> ::= <abi-tag> [<abi-tags>] +static bool ParseAbiTags(State *state) { + State copy = *state; + DisableAppend(state); + if (OneOrMore(ParseAbiTag, state)) { + RestoreAppend(state, copy.append); + return true; + } + *state = copy; + return false; +} + +// <abi-tag> ::= B <source-name> +static bool ParseAbiTag(State *state) { + return ParseOneCharToken(state, 'B') && ParseSourceName(state); +} + // <operator-name> ::= nw, and other two letters cases // ::= cv <type> # (cast) // ::= v <digit> <source-name> # vendor extended operator @@ -1097,10 +1116,11 @@ // <template-arg> ::= <type> // ::= <expr-primary> // ::= I <template-arg>* E # argument pack +// ::= J <template-arg>* E # argument pack // ::= X <expression> E static bool ParseTemplateArg(State *state) { State copy = *state; - if (ParseOneCharToken(state, 'I') && + if ((ParseOneCharToken(state, 'I') || ParseOneCharToken(state, 'J')) && ZeroOrMore(ParseTemplateArg, state) && ParseOneCharToken(state, 'E')) { return true;
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h index 43e8b8a0..fbe58b6 100644 --- a/base/trace_event/builtin_categories.h +++ b/base/trace_event/builtin_categories.h
@@ -179,7 +179,6 @@ X(TRACE_DISABLED_BY_DEFAULT("layer-element")) \ X(TRACE_DISABLED_BY_DEFAULT("lighthouse")) \ X(TRACE_DISABLED_BY_DEFAULT("loading")) \ - X(TRACE_DISABLED_BY_DEFAULT("memory_coordinator")) \ X(TRACE_DISABLED_BY_DEFAULT("memory-infra")) \ X(TRACE_DISABLED_BY_DEFAULT("memory-infra.v8.code_stats")) \ X(TRACE_DISABLED_BY_DEFAULT("net")) \
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml index 2c3649f..569c92b 100644 --- a/build/android/lint/suppressions.xml +++ b/build/android/lint/suppressions.xml
@@ -347,6 +347,8 @@ <!-- 2 resource sets used by clank widgets for each channel --> <ignore regexp="The resource `R.string.bookmark_widget_title.*` appears to be unused"/> <ignore regexp="The resource `R.string.search_widget_title.*` appears to be unused"/> + <!-- 1 resource used by android tv to generate resources.zip file --> + <ignore regexp="chromecast/internal/shell/browser/android/java/res/drawable-hdpi/ic_settings_cast.png"/> <!-- Endnote: Please specify number of suppressions when adding more --> </issue> <issue id="UseCompoundDrawables">
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index 952fc66..c24af32 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -36d38e319887c19d1e974b639f0a9b33263edbdf \ No newline at end of file +9e9e61c1dddc4ca1bf8fb7c41c07c810295a4699 \ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index 8d41e18..640d4da 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -32d827f51f434ab3285f72db8c24809e1a7fb779 \ No newline at end of file +a65778bcb0c393f7779ddfc684fe494108009a16 \ No newline at end of file
diff --git a/build/win/copy_cdb_to_output.py b/build/win/copy_cdb_to_output.py index 2107188..327c71b 100755 --- a/build/win/copy_cdb_to_output.py +++ b/build/win/copy_cdb_to_output.py
@@ -54,7 +54,7 @@ directory, which is created if it does not exist. The output directory, and target architecture that should be copied, are passed. Supported values for the target architecture are the GYP - values "ia32" and "x64" and the GN values "x86" and "x64". + values "ia32", "x64", "arm64" and the GN values "x86", "x64", "arm64". """ _ConditionalMkdir(output_dir) vs_toolchain.SetEnvironmentAndGetRuntimeDllDirs() @@ -66,8 +66,8 @@ '\\Windows Kits\\10'))) if target_arch == 'ia32' or target_arch == 'x86': src_arch = 'x86' - elif target_arch == 'x64': - src_arch = 'x64' + elif target_arch in ['x64', 'arm64']: + src_arch = target_arch else: print 'copy_cdb_to_output.py: unknown target_arch %s' % target_arch sys.exit(1) @@ -99,10 +99,11 @@ _CopyImpl('uext.dll', dst_winext_dir, src_winext_dir) _CopyImpl('exts.dll', dst_winxp_dir, src_winxp_dir) _CopyImpl('ntsdexts.dll', dst_winxp_dir, src_winxp_dir) - _CopyImpl('api-ms-win-eventing-provider-l1-1-0.dll', output_dir, src_dir) + if src_arch in ['x64', 'x86']: + _CopyImpl('api-ms-win-eventing-provider-l1-1-0.dll', output_dir, src_dir) + _CopyImpl('ucrtbase.dll', output_dir, src_crt_dir) for dll_path in glob.glob(os.path.join(src_crt_dir, 'api-ms-win-*.dll')): _CopyImpl(os.path.split(dll_path)[1], output_dir, src_crt_dir) - _CopyImpl('ucrtbase.dll', output_dir, src_crt_dir) return 0
diff --git a/cc/input/scrollbar_animation_controller_unittest.cc b/cc/input/scrollbar_animation_controller_unittest.cc index e9f336f..9ffac7a 100644 --- a/cc/input/scrollbar_animation_controller_unittest.cc +++ b/cc/input/scrollbar_animation_controller_unittest.cc
@@ -108,12 +108,12 @@ host_impl_.active_tree()->SetRootLayerForTesting(std::move(clip)); v_scrollbar_layer_->SetBounds(gfx::Size(kThumbThickness, kTrackLength)); - v_scrollbar_layer_->SetPosition(gfx::PointF(90, 0)); + v_scrollbar_layer_->test_properties()->position = gfx::PointF(90, 0); v_scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id()); v_scrollbar_layer_->test_properties()->opacity_can_animate = true; h_scrollbar_layer_->SetBounds(gfx::Size(kTrackLength, kThumbThickness)); - h_scrollbar_layer_->SetPosition(gfx::PointF(0, 90)); + h_scrollbar_layer_->test_properties()->position = gfx::PointF(0, 90); h_scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id()); h_scrollbar_layer_->test_properties()->opacity_can_animate = true;
diff --git a/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc b/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc index 08ba96d..c1c0723 100644 --- a/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc +++ b/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
@@ -90,7 +90,7 @@ host_impl_.active_tree()->SetRootLayerForTesting(std::move(clip)); scrollbar_layer_->SetBounds(gfx::Size(kThumbThickness, kTrackLength)); - scrollbar_layer_->SetPosition(gfx::PointF(90, 0)); + scrollbar_layer_->test_properties()->position = gfx::PointF(90, 0); scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id()); scrollbar_layer_->test_properties()->opacity_can_animate = true; clip_layer_->SetBounds(gfx::Size(100, 100));
diff --git a/cc/layers/effect_tree_layer_list_iterator_unittest.cc b/cc/layers/effect_tree_layer_list_iterator_unittest.cc index 7f79eb9..26bd15d 100644 --- a/cc/layers/effect_tree_layer_list_iterator_unittest.cc +++ b/cc/layers/effect_tree_layer_list_iterator_unittest.cc
@@ -32,7 +32,6 @@ explicit TestLayerImpl(LayerTreeImpl* tree, int id) : LayerImpl(tree, id), count_(-1) { SetBounds(gfx::Size(100, 100)); - SetPosition(gfx::PointF()); SetDrawsContent(true); } };
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc index 1af78ae..cb5219b 100644 --- a/cc/layers/layer.cc +++ b/cc/layers/layer.cc
@@ -1359,7 +1359,6 @@ layer->SetWheelEventHandlerRegion(Region()); } layer->SetContentsOpaque(inputs_.contents_opaque); - layer->SetPosition(inputs_.position); layer->SetShouldFlattenScreenSpaceTransformFromPropertyTree( should_flatten_screen_space_transform_from_property_tree_); layer->SetUseParentBackfaceVisibility(inputs_.use_parent_backface_visibility);
diff --git a/cc/layers/layer.h b/cc/layers/layer.h index 2eed5e9e53..0d4fa2a 100644 --- a/cc/layers/layer.h +++ b/cc/layers/layer.h
@@ -163,7 +163,8 @@ // specified in layer space, which excludes device scale and page scale // factors, and ignoring transforms for this layer or ancestor layers. The // root layer's position is not used as it always appears at the origin of - // the viewport. + // the viewport. When property trees are built by cc (when IsUsingLayerLists + // is false), position is used to update |offset_to_transform_parent|. void SetPosition(const gfx::PointF& position); const gfx::PointF& position() const { return inputs_.position; } @@ -708,10 +709,9 @@ void SetEffectTreeIndex(int index); void SetScrollTreeIndex(int index); - // Internal to property tree construction. Set or get the position of this - // layer relative to the origin after transforming according to this layer's - // index into the transform tree. This translation is appended to the - // transform that comes from the transform tree for this layer. + // The position of this layer after transforming by the layer's transform + // node. When property trees are built by cc (when IsUsingLayerLists is false) + // this is set by property_tree_builder.cc. void SetOffsetToTransformParent(gfx::Vector2dF offset); gfx::Vector2dF offset_to_transform_parent() const { return offset_to_transform_parent_;
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index 43c529e..3fe2260 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc
@@ -321,7 +321,6 @@ layer->wheel_event_handler_region_ = wheel_event_handler_region_; layer->background_color_ = background_color_; layer->safe_opaque_background_color_ = safe_opaque_background_color_; - layer->position_ = position_; layer->transform_tree_index_ = transform_tree_index_; layer->effect_tree_index_ = effect_tree_index_; layer->clip_tree_index_ = clip_tree_index_; @@ -387,11 +386,6 @@ list->AppendInteger(bounds().height()); result->Set("Bounds", std::move(list)); - list = std::make_unique<base::ListValue>(); - list->AppendDouble(position_.x()); - list->AppendDouble(position_.y()); - result->Set("Position", std::move(list)); - const gfx::Transform& gfx_transform = const_cast<LayerImpl*>(this)->test_properties()->transform; double transform[16]; @@ -647,10 +641,6 @@ layer_tree_impl_->AddToElementLayerList(element_id_, this); } -void LayerImpl::SetPosition(const gfx::PointF& position) { - position_ = position; -} - void LayerImpl::SetUpdateRect(const gfx::Rect& update_rect) { update_rect_ = update_rect; } @@ -731,8 +721,6 @@ state->SetDouble("opacity", Opacity()); - MathUtil::AddToTracedValue("position", position_, state); - state->SetInteger("transform_tree_index", transform_tree_index()); state->SetInteger("clip_tree_index", clip_tree_index()); state->SetInteger("effect_tree_index", effect_tree_index());
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h index 3002f25..f4b63cce 100644 --- a/cc/layers/layer_impl.h +++ b/cc/layers/layer_impl.h
@@ -196,9 +196,6 @@ void SetElementId(ElementId element_id); ElementId element_id() const { return element_id_; } - void SetPosition(const gfx::PointF& position); - gfx::PointF position() const { return position_; } - bool IsAffectedByPageScale() const; bool Is3dSorted() const { return GetSortingContextId() != 0; } @@ -545,8 +542,6 @@ SkColor background_color_; SkColor safe_opaque_background_color_; - gfx::PointF position_; - int transform_tree_index_; int effect_tree_index_; int clip_tree_index_;
diff --git a/cc/layers/layer_impl_test_properties.h b/cc/layers/layer_impl_test_properties.h index 9ec06b0..9f0d688a 100644 --- a/cc/layers/layer_impl_test_properties.h +++ b/cc/layers/layer_impl_test_properties.h
@@ -56,6 +56,7 @@ LayerStickyPositionConstraint sticky_position_constraint; gfx::Point3F transform_origin; gfx::Transform transform; + gfx::PointF position; LayerImpl* scroll_parent; LayerImpl* clip_parent; std::unique_ptr<std::set<LayerImpl*>> clip_children;
diff --git a/cc/layers/layer_impl_unittest.cc b/cc/layers/layer_impl_unittest.cc index cca256cf..9200f7a 100644 --- a/cc/layers/layer_impl_unittest.cc +++ b/cc/layers/layer_impl_unittest.cc
@@ -115,7 +115,6 @@ EXPECT_FALSE(child->LayerPropertyChanged()); EXPECT_FALSE(grand_child->LayerPropertyChanged()); - gfx::PointF arbitrary_point_f = gfx::PointF(0.125f, 0.25f); float arbitrary_number = 0.352f; gfx::Size arbitrary_size = gfx::Size(111, 222); gfx::Point arbitrary_point = gfx::Point(333, 444); @@ -159,8 +158,6 @@ // After setting all these properties already, setting to the exact same // values again should not cause any change. EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetMasksToBounds(true)); - EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE( - root->SetPosition(arbitrary_point_f)); EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetContentsOpaque(true)); EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetDrawsContent(true)); EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetBounds(root->bounds())); @@ -266,7 +263,6 @@ host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); DCHECK(host_impl.CanDraw()); - gfx::PointF arbitrary_point_f = gfx::PointF(0.125f, 0.25f); float arbitrary_number = 0.352f; gfx::Size arbitrary_size = gfx::Size(111, 222); gfx::Vector2d arbitrary_vector2d = gfx::Vector2d(111, 222); @@ -325,8 +321,6 @@ layer->NoteLayerPropertyChanged()); VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetContentsOpaque(true); layer->NoteLayerPropertyChanged()); - VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer2->SetPosition(arbitrary_point_f); - layer->NoteLayerPropertyChanged()); VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES( layer->SetBackgroundColor(arbitrary_color)); VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES( @@ -346,8 +340,6 @@ arbitrary_filters)); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetMasksToBounds(true)); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetContentsOpaque(true)); - VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES( - layer2->SetPosition(arbitrary_point_f)); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetDrawsContent(true)); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES( layer->SetBackgroundColor(arbitrary_color));
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc index 0bf913bf..00cbffe 100644 --- a/cc/layers/picture_layer_impl_unittest.cc +++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -4052,7 +4052,7 @@ layer1->SetBounds(layer_bounds); layer1->SetDrawsContent(true); layer1->SetContentsOpaque(true); - layer1->SetPosition(occluding_layer_position); + layer1->test_properties()->position = occluding_layer_position; RebuildPropertyTreesOnPendingTree(); host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(200)); @@ -4076,7 +4076,7 @@ EXPECT_EQ(20, unoccluded_tile_count); // Full occlusion. - layer1->SetPosition(gfx::PointF()); + layer1->test_properties()->position = gfx::PointF(); layer1->NoteLayerPropertyChanged(); RebuildPropertyTreesOnPendingTree(); @@ -4147,7 +4147,7 @@ layer1->SetBounds(layer_bounds); layer1->SetDrawsContent(true); layer1->SetContentsOpaque(true); - layer1->SetPosition(occluding_layer_position); + layer1->test_properties()->position = occluding_layer_position; RebuildPropertyTreesOnPendingTree(); host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(200)); @@ -4184,7 +4184,7 @@ } // Full occlusion. - layer1->SetPosition(gfx::PointF()); + layer1->test_properties()->position = gfx::PointF(); layer1->NoteLayerPropertyChanged(); RebuildPropertyTreesOnPendingTree(); @@ -4244,7 +4244,7 @@ layer1->SetBounds(layer_bounds); layer1->SetDrawsContent(true); layer1->SetContentsOpaque(true); - layer1->SetPosition(occluding_layer_position); + layer1->test_properties()->position = occluding_layer_position; pending_layer()->tilings()->RemoveAllTilings(); float low_res_factor = host_impl()->settings().low_res_contents_scale_factor; @@ -4329,7 +4329,7 @@ layer1->SetBounds(layer_bounds); layer1->SetDrawsContent(true); layer1->SetContentsOpaque(true); - layer1->SetPosition(occluding_layer_position); + layer1->test_properties()->position = occluding_layer_position; ActivateTree(); @@ -4425,7 +4425,8 @@ active_occluding_layer->SetBounds(layer_bounds); active_occluding_layer->SetDrawsContent(true); active_occluding_layer->SetContentsOpaque(true); - active_occluding_layer->SetPosition(active_occluding_layer_position); + active_occluding_layer->test_properties()->position = + active_occluding_layer_position; ActivateTree(); // Partially invalidate the pending layer. Tiles inside the invalidation rect @@ -4441,7 +4442,8 @@ pending_occluding_layer->SetBounds(layer_bounds); pending_occluding_layer->SetDrawsContent(true); pending_occluding_layer->SetContentsOpaque(true); - pending_occluding_layer->SetPosition(pending_occluding_layer_position); + pending_occluding_layer->test_properties()->position = + pending_occluding_layer_position; EXPECT_EQ(1u, pending_layer()->num_tilings()); EXPECT_EQ(2u, active_layer()->num_tilings());
diff --git a/cc/paint/paint_flags.cc b/cc/paint/paint_flags.cc index a54051f..a86da2a4 100644 --- a/cc/paint/paint_flags.cc +++ b/cc/paint/paint_flags.cc
@@ -24,7 +24,6 @@ bitfields_.cap_type_ = SkPaint::kDefault_Cap; bitfields_.join_type_ = SkPaint::kDefault_Join; bitfields_.style_ = SkPaint::kFill_Style; - bitfields_.text_encoding_ = static_cast<unsigned>(kUTF8_SkTextEncoding); bitfields_.hinting_ = static_cast<unsigned>(SkFontHinting::kNormal); bitfields_.filter_quality_ = SkFilterQuality::kNone_SkFilterQuality; @@ -145,7 +144,6 @@ paint.setStrokeCap(static_cast<SkPaint::Cap>(getStrokeCap())); paint.setStrokeJoin(static_cast<SkPaint::Join>(getStrokeJoin())); paint.setStyle(static_cast<SkPaint::Style>(getStyle())); - paint.setTextEncoding(static_cast<SkTextEncoding>(getTextEncoding())); paint.setHinting(static_cast<SkFontHinting>(getHinting())); paint.setFilterQuality(getFilterQuality()); return paint; @@ -192,8 +190,6 @@ return false; if (getStyle() != other.getStyle()) return false; - if (getTextEncoding() != other.getTextEncoding()) - return false; if (getHinting() != other.getHinting()) return false; if (getFilterQuality() != other.getFilterQuality())
diff --git a/cc/paint/paint_flags.h b/cc/paint/paint_flags.h index 28339d6f..b0192444 100644 --- a/cc/paint/paint_flags.h +++ b/cc/paint/paint_flags.h
@@ -99,18 +99,6 @@ ALWAYS_INLINE void setDither(bool dither) { SetInternalFlag(dither, SkPaint::kDither_Flag); } - enum TextEncoding { - kUTF8_TextEncoding = static_cast<unsigned>(kUTF8_SkTextEncoding), - kUTF16_TextEncoding = static_cast<unsigned>(kUTF16_SkTextEncoding), - kUTF32_TextEncoding = static_cast<unsigned>(kUTF32_SkTextEncoding), - kGlyphID_TextEncoding = static_cast<unsigned>(kGlyphID_SkTextEncoding) - }; - ALWAYS_INLINE TextEncoding getTextEncoding() const { - return static_cast<TextEncoding>(bitfields_.text_encoding_); - } - ALWAYS_INLINE void setTextEncoding(TextEncoding encoding) { - bitfields_.text_encoding_ = encoding; - } ALWAYS_INLINE SkScalar getTextSize() const { return text_size_; } ALWAYS_INLINE void setTextSize(SkScalar text_size) { text_size_ = text_size; } ALWAYS_INLINE void setFilterQuality(SkFilterQuality quality) { @@ -251,7 +239,6 @@ uint32_t cap_type_ : 2; uint32_t join_type_ : 2; uint32_t style_ : 2; - uint32_t text_encoding_ : 2; uint32_t hinting_ : 2; uint32_t filter_quality_ : 2; };
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc index 1b61f75..e14d939 100644 --- a/cc/paint/paint_op_buffer_unittest.cc +++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -1050,7 +1050,6 @@ flags.setStrokeCap(PaintFlags::kSquare_Cap); flags.setStrokeJoin(PaintFlags::kBevel_Join); flags.setStyle(PaintFlags::kStrokeAndFill_Style); - flags.setTextEncoding(PaintFlags::kGlyphID_TextEncoding); flags.setHinting(PaintFlags::kNormal_Hinting); flags.setFilterQuality(SkFilterQuality::kMedium_SkFilterQuality); flags.setShader(PaintShader::MakeColor(SkColorSetARGB(1, 2, 3, 4))); @@ -1067,7 +1066,6 @@ flags.setStrokeCap(PaintFlags::kRound_Cap); flags.setStrokeJoin(PaintFlags::kRound_Join); flags.setStyle(PaintFlags::kFill_Style); - flags.setTextEncoding(PaintFlags::kUTF32_TextEncoding); flags.setHinting(PaintFlags::kSlight_Hinting); flags.setFilterQuality(SkFilterQuality::kHigh_SkFilterQuality);
diff --git a/cc/paint/paint_op_helper_unittest.cc b/cc/paint/paint_op_helper_unittest.cc index 50cffc3..2c5ee73d 100644 --- a/cc/paint/paint_op_helper_unittest.cc +++ b/cc/paint/paint_op_helper_unittest.cc
@@ -67,7 +67,7 @@ "by 5.000,6.000 7.000x8.000], flags=[color=rgba(0, 0, 0, 255), " "blendMode=kSrcOver, isAntiAlias=false, " "isSubpixelText=false, isLCDRenderText=false, hinting=kNormal_Hinting, " - "isAutohinted=false, isDither=false, textEncoding=kUTF8_TextEncoding, " + "isAutohinted=false, isDither=false, " "textSize=12.000, filterQuality=kNone_SkFilterQuality, " "strokeWidth=0.000, strokeMiter=4.000, strokeCap=kButt_Cap, " "strokeJoin=kMiter_Join, typeface=(nil), colorFilter=(nil), " @@ -86,7 +86,7 @@ "flags=[color=rgba(0, 0, 0, 255), blendMode=kSrcOver, isAntiAlias=false, " "isSubpixelText=false, isLCDRenderText=false, " "hinting=kNormal_Hinting, isAutohinted=false, isDither=false, " - "textEncoding=kUTF8_TextEncoding, textSize=12.000, " + "textSize=12.000, " "filterQuality=kNone_SkFilterQuality, strokeWidth=0.000, " "strokeMiter=4.000, strokeCap=kButt_Cap, strokeJoin=kMiter_Join, " "typeface=(nil), colorFilter=(nil), maskFilter=(nil), shader=(nil), " @@ -107,7 +107,7 @@ "flags=[color=rgba(0, 0, 0, 255), blendMode=kSrcOver, isAntiAlias=false, " "isSubpixelText=false, isLCDRenderText=false, " "hinting=kNormal_Hinting, isAutohinted=false, isDither=false, " - "textEncoding=kUTF8_TextEncoding, textSize=12.000, " + "textSize=12.000, " "filterQuality=kNone_SkFilterQuality, strokeWidth=0.000, " "strokeMiter=4.000, strokeCap=kButt_Cap, strokeJoin=kMiter_Join, " "typeface=(nil), colorFilter=(nil), maskFilter=(nil), shader=(nil), " @@ -124,7 +124,7 @@ "blendMode=kSrcOver, isAntiAlias=false, " "isSubpixelText=false, isLCDRenderText=false, " "hinting=kNormal_Hinting, isAutohinted=false, isDither=false, " - "textEncoding=kUTF8_TextEncoding, textSize=12.000, " + "textSize=12.000, " "filterQuality=kNone_SkFilterQuality, strokeWidth=0.000, " "strokeMiter=4.000, strokeCap=kButt_Cap, strokeJoin=kMiter_Join, " "typeface=(nil), colorFilter=(nil), maskFilter=(nil), " @@ -143,7 +143,7 @@ "0, 0, 255), blendMode=kSrcOver, isAntiAlias=false, " "isSubpixelText=false, isLCDRenderText=false, " "hinting=kNormal_Hinting, isAutohinted=false, isDither=false, " - "textEncoding=kUTF8_TextEncoding, textSize=12.000, " + "textSize=12.000, " "filterQuality=kNone_SkFilterQuality, strokeWidth=0.000, " "strokeMiter=4.000, strokeCap=kButt_Cap, strokeJoin=kMiter_Join, " "typeface=(nil), colorFilter=(nil), maskFilter=(nil), shader=(nil), " @@ -161,7 +161,7 @@ "0, 0, 255), blendMode=kSrcOver, isAntiAlias=false, " "isSubpixelText=false, isLCDRenderText=false, " "hinting=kNormal_Hinting, isAutohinted=false, isDither=false, " - "textEncoding=kUTF8_TextEncoding, textSize=12.000, " + "textSize=12.000, " "filterQuality=kNone_SkFilterQuality, strokeWidth=0.000, " "strokeMiter=4.000, strokeCap=kButt_Cap, strokeJoin=kMiter_Join, " "typeface=(nil), colorFilter=(nil), maskFilter=(nil), shader=(nil), " @@ -179,7 +179,7 @@ "blendMode=kSrcOver, isAntiAlias=false, " "isSubpixelText=false, isLCDRenderText=false, " "hinting=kNormal_Hinting, isAutohinted=false, isDither=false, " - "textEncoding=kUTF8_TextEncoding, textSize=12.000, " + "textSize=12.000, " "filterQuality=kNone_SkFilterQuality, strokeWidth=0.000, " "strokeMiter=4.000, strokeCap=kButt_Cap, strokeJoin=kMiter_Join, " "typeface=(nil), colorFilter=(nil), maskFilter=(nil), " @@ -203,7 +203,7 @@ "DrawRectOp(rect=[-1.000,-2.000 -3.000x-4.000], flags=[color=rgba(0, 0, " "0, 255), blendMode=kSrcOver, isAntiAlias=false, " "isSubpixelText=false, isLCDRenderText=false, hinting=kNormal_Hinting, " - "isAutohinted=false, isDither=false, textEncoding=kUTF8_TextEncoding, " + "isAutohinted=false, isDither=false, " "textSize=12.000, filterQuality=kNone_SkFilterQuality, " "strokeWidth=0.000, strokeMiter=4.000, strokeCap=kButt_Cap, " "strokeJoin=kMiter_Join, typeface=(nil), colorFilter=(nil), " @@ -223,7 +223,7 @@ "flags=[color=rgba(0, 0, 0, 255), blendMode=kSrcOver, isAntiAlias=false, " "isSubpixelText=false, isLCDRenderText=false, " "hinting=kNormal_Hinting, isAutohinted=false, isDither=false, " - "textEncoding=kUTF8_TextEncoding, textSize=12.000, " + "textSize=12.000, " "filterQuality=kNone_SkFilterQuality, strokeWidth=0.000, " "strokeMiter=4.000, strokeCap=kButt_Cap, strokeJoin=kMiter_Join, " "typeface=(nil), colorFilter=(nil), maskFilter=(nil), shader=(nil), " @@ -241,7 +241,7 @@ "0, 0, 255), blendMode=kSrcOver, isAntiAlias=false, " "isSubpixelText=false, isLCDRenderText=false, " "hinting=kNormal_Hinting, isAutohinted=false, isDither=false, " - "textEncoding=kUTF8_TextEncoding, textSize=12.000, " + "textSize=12.000, " "filterQuality=kNone_SkFilterQuality, strokeWidth=0.000, " "strokeMiter=4.000, strokeCap=kButt_Cap, strokeJoin=kMiter_Join, " "typeface=(nil), colorFilter=(nil), maskFilter=(nil), shader=(nil), " @@ -283,7 +283,7 @@ "SaveLayerOp(bounds=[1.000,2.000 3.000x4.000], flags=[color=rgba(0, 0, " "0, 255), blendMode=kSrcOver, isAntiAlias=false, " "isSubpixelText=false, isLCDRenderText=false, hinting=kNormal_Hinting, " - "isAutohinted=false, isDither=false, textEncoding=kUTF8_TextEncoding, " + "isAutohinted=false, isDither=false, " "textSize=12.000, filterQuality=kNone_SkFilterQuality, " "strokeWidth=0.000, strokeMiter=4.000, strokeCap=kButt_Cap, " "strokeJoin=kMiter_Join, typeface=(nil), colorFilter=(nil), "
diff --git a/cc/test/layer_test_common.cc b/cc/test/layer_test_common.cc index 1f9de8a..8f0ef18c 100644 --- a/cc/test/layer_test_common.cc +++ b/cc/test/layer_test_common.cc
@@ -259,7 +259,6 @@ root->SetScrollable(inner_viewport_size); root->SetElementId(LayerIdToElementIdForTesting(root->id())); root->SetBounds(outer_viewport_size); - root->SetPosition(gfx::PointF()); root->SetDrawsContent(false); root_clip->test_properties()->force_render_surface = true; root->test_properties()->is_container_for_fixed_position_layers = true; @@ -267,7 +266,6 @@ outer_scroll->SetScrollable(outer_viewport_size); outer_scroll->SetElementId(LayerIdToElementIdForTesting(outer_scroll->id())); outer_scroll->SetBounds(scroll_layer_size); - outer_scroll->SetPosition(gfx::PointF()); outer_scroll->SetDrawsContent(false); outer_scroll->test_properties()->is_container_for_fixed_position_layers = true;
diff --git a/cc/test/layer_tree_json_parser.cc b/cc/test/layer_tree_json_parser.cc index 4ab48653..d23dfb4 100644 --- a/cc/test/layer_tree_json_parser.cc +++ b/cc/test/layer_tree_json_parser.cc
@@ -31,10 +31,6 @@ int width, height; success &= list->GetInteger(0, &width); success &= list->GetInteger(1, &height); - success &= dict->GetList("Position", &list); - double position_x, position_y; - success &= list->GetDouble(0, &position_x); - success &= list->GetDouble(1, &position_y); bool draws_content; success &= dict->GetBoolean("DrawsContent", &draws_content); @@ -86,7 +82,6 @@ } else { // Type "Layer" or "unknown" new_layer = Layer::Create(); } - new_layer->SetPosition(gfx::PointF(position_x, position_y)); new_layer->SetBounds(gfx::Size(width, height)); new_layer->SetIsDrawable(draws_content);
diff --git a/cc/test/layer_tree_json_parser_unittest.cc b/cc/test/layer_tree_json_parser_unittest.cc index 22a64d1..9471974 100644 --- a/cc/test/layer_tree_json_parser_unittest.cc +++ b/cc/test/layer_tree_json_parser_unittest.cc
@@ -32,8 +32,6 @@ EXPECT_EQ(layer_impl->test_properties()->children.size(), layer->children().size())); RETURN_IF_EXPECTATION_FAILS(EXPECT_EQ(layer_impl->bounds(), layer->bounds())); - RETURN_IF_EXPECTATION_FAILS( - EXPECT_EQ(layer_impl->position(), layer->position())); RETURN_IF_EXPECTATION_FAILS(EXPECT_TRANSFORMATION_MATRIX_EQ( layer_impl->test_properties()->transform, layer->transform())); RETURN_IF_EXPECTATION_FAILS(EXPECT_EQ(layer_impl->contents_opaque(), @@ -79,7 +77,7 @@ translate.Translate(10, 15); child->test_properties()->transform = translate; - parent->SetPosition(gfx::PointF(25.f, 25.f)); + parent->test_properties()->position = gfx::PointF(25.f, 25.f); parent->test_properties()->AddChild(std::move(child)); root_impl->test_properties()->AddChild(std::move(parent));
diff --git a/cc/test/paint_op_helper.h b/cc/test/paint_op_helper.h index 20330bbe..61a974a 100644 --- a/cc/test/paint_op_helper.h +++ b/cc/test/paint_op_helper.h
@@ -368,20 +368,6 @@ return "<unknown PaintFlags::Hinting>"; } - static std::string SkiaTypeToString(PaintFlags::TextEncoding encoding) { - switch (encoding) { - case PaintFlags::kUTF8_TextEncoding: - return "kUTF8_TextEncoding"; - case PaintFlags::kUTF16_TextEncoding: - return "kUTF16_TextEncoding"; - case PaintFlags::kUTF32_TextEncoding: - return "kUTF32_TextEncoding"; - case PaintFlags::kGlyphID_TextEncoding: - return "kGlyphID_TextEncoding"; - } - return "<unknown PaintFlags::TextEncoding>"; - } - static std::string SkiaTypeToString(SkFilterQuality quality) { switch (quality) { case kNone_SkFilterQuality: @@ -612,8 +598,6 @@ str << ", hinting=" << PaintOpHelper::SkiaTypeToString(flags.getHinting()); str << ", isAutohinted=" << flags.isAutohinted(); str << ", isDither=" << flags.isDither(); - str << ", textEncoding=" - << PaintOpHelper::SkiaTypeToString(flags.getTextEncoding()); str << ", textSize=" << PaintOpHelper::SkiaTypeToString(flags.getTextSize()); str << ", filterQuality="
diff --git a/cc/trees/damage_tracker_unittest.cc b/cc/trees/damage_tracker_unittest.cc index 03d1e9716..a96cc8c 100644 --- a/cc/trees/damage_tracker_unittest.cc +++ b/cc/trees/damage_tracker_unittest.cc
@@ -73,7 +73,6 @@ std::unique_ptr<LayerImpl> root = LayerImpl::Create(host_impl_.active_tree(), 1); - root->SetPosition(gfx::PointF()); root->SetBounds(gfx::Size(500, 500)); root->SetDrawsContent(true); root->test_properties()->force_render_surface = true; @@ -81,7 +80,7 @@ for (int i = 0; i < number_of_children; ++i) { std::unique_ptr<LayerImpl> child = LayerImpl::Create(host_impl_.active_tree(), 2 + i); - child->SetPosition(gfx::PointF(100.f, 100.f)); + child->test_properties()->position = gfx::PointF(100.f, 100.f); child->SetBounds(gfx::Size(30, 30)); child->SetDrawsContent(true); root->test_properties()->AddChild(std::move(child)); @@ -109,12 +108,11 @@ std::unique_ptr<LayerImpl> grand_child2 = LayerImpl::Create(host_impl_.active_tree(), 5); - root->SetPosition(gfx::PointF()); root->SetBounds(gfx::Size(500, 500)); root->SetDrawsContent(true); root->test_properties()->force_render_surface = true; - child1->SetPosition(gfx::PointF(100.f, 100.f)); + child1->test_properties()->position = gfx::PointF(100.f, 100.f); child1->SetBounds(gfx::Size(30, 30)); // With a child that draws_content, opacity will cause the layer to create // its own RenderSurface. This layer does not draw, but is intended to @@ -122,15 +120,15 @@ child1->SetDrawsContent(false); child1->test_properties()->force_render_surface = true; - child2->SetPosition(gfx::PointF(11.f, 11.f)); + child2->test_properties()->position = gfx::PointF(11.f, 11.f); child2->SetBounds(gfx::Size(18, 18)); child2->SetDrawsContent(true); - grand_child1->SetPosition(gfx::PointF(200.f, 200.f)); + grand_child1->test_properties()->position = gfx::PointF(200.f, 200.f); grand_child1->SetBounds(gfx::Size(6, 8)); grand_child1->SetDrawsContent(true); - grand_child2->SetPosition(gfx::PointF(190.f, 190.f)); + grand_child2->test_properties()->position = gfx::PointF(190.f, 190.f); grand_child2->SetBounds(gfx::Size(6, 8)); grand_child2->SetDrawsContent(true); @@ -582,7 +580,7 @@ ClearDamageForAllSurfaces(root); child->test_properties()->transform_origin = gfx::Point3F( child->bounds().width() * 0.5f, child->bounds().height() * 0.5f, 0.f); - child->SetPosition(gfx::PointF(85.f, 85.f)); + child->test_properties()->position = gfx::PointF(85.f, 85.f); child->NoteLayerPropertyChanged(); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); @@ -653,7 +651,7 @@ transform.Translate3d(-50.0, -50.0, 0.0); // Set up the child - child->SetPosition(gfx::PointF(0.f, 0.f)); + child->test_properties()->position = gfx::PointF(0.f, 0.f); child->SetBounds(gfx::Size(100, 100)); child->test_properties()->transform = transform; root->layer_tree_impl()->property_trees()->needs_rebuild = true; @@ -661,7 +659,8 @@ // Sanity check that the child layer's bounds would actually get clipped by // w < 0, otherwise this test is not actually testing the intended scenario. - gfx::RectF test_rect(child->position(), gfx::SizeF(child->bounds())); + gfx::RectF test_rect(child->test_properties()->position, + gfx::SizeF(child->bounds())); bool clipped = false; MathUtil::MapQuad(transform, gfx::QuadF(test_rect), &clipped); EXPECT_TRUE(clipped); @@ -1057,7 +1056,7 @@ // to get expanded. We position child1 so that an expansion of the empty rect // would have non-empty intersection with child1 in its target space (root // space). - child1->SetPosition(gfx::PointF()); + child1->test_properties()->position = gfx::PointF(); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); @@ -1082,7 +1081,7 @@ { std::unique_ptr<LayerImpl> child2 = LayerImpl::Create(host_impl_.active_tree(), 3); - child2->SetPosition(gfx::PointF(400.f, 380.f)); + child2->test_properties()->position = gfx::PointF(400.f, 380.f); child2->SetBounds(gfx::Size(6, 8)); child2->SetDrawsContent(true); root->test_properties()->AddChild(std::move(child2)); @@ -1140,7 +1139,7 @@ { std::unique_ptr<LayerImpl> child2 = LayerImpl::Create(host_impl_.active_tree(), 3); - child2->SetPosition(gfx::PointF(400.f, 380.f)); + child2->test_properties()->position = gfx::PointF(400.f, 380.f); child2->SetBounds(gfx::Size(6, 8)); child2->SetDrawsContent(true); root->test_properties()->AddChild(std::move(child2)); @@ -1179,7 +1178,7 @@ { std::unique_ptr<LayerImpl> child2 = LayerImpl::Create(host_impl_.active_tree(), 3); - child2->SetPosition(gfx::PointF(400.f, 380.f)); + child2->test_properties()->position = gfx::PointF(400.f, 380.f); child2->SetBounds(gfx::Size(6, 8)); child2->SetDrawsContent(true); root->test_properties()->AddChild(std::move(child2)); @@ -1290,7 +1289,7 @@ gfx::Rect root_damage_rect; ClearDamageForAllSurfaces(root); - grand_child1->SetPosition(gfx::PointF(195.f, 205.f)); + grand_child1->test_properties()->position = gfx::PointF(195.f, 205.f); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); EXPECT_TRUE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( @@ -1501,7 +1500,8 @@ { std::unique_ptr<LayerImpl> mask_layer = LayerImpl::Create(host_impl_.active_tree(), 3); - mask_layer->SetPosition(child->position()); + mask_layer->test_properties()->position = + child->test_properties()->position; mask_layer->SetBounds(child->bounds()); child->test_properties()->SetMaskLayer(std::move(mask_layer)); child->test_properties()->force_render_surface = true; @@ -1513,7 +1513,7 @@ { std::unique_ptr<LayerImpl> grand_child = LayerImpl::Create(host_impl_.active_tree(), 4); - grand_child->SetPosition(gfx::PointF(2.f, 2.f)); + grand_child->test_properties()->position = gfx::PointF(2.f, 2.f); grand_child->SetBounds(gfx::Size(2, 2)); grand_child->SetDrawsContent(true); child->test_properties()->AddChild(std::move(grand_child)); @@ -1730,7 +1730,6 @@ // The child layer covers (0, 0, i, i) of the viewport, // but has a huge negative position. - child->SetPosition(gfx::PointF()); child->SetBounds(gfx::Size(kBigNumber + i, kBigNumber + i)); child->test_properties()->transform = transform; root->layer_tree_impl()->property_trees()->needs_rebuild = true; @@ -1760,11 +1759,13 @@ LayerImpl* child2 = root->test_properties()->children[1]; // Really far left. - child1->SetPosition(gfx::PointF(std::numeric_limits<int>::min() + 100, 0)); + child1->test_properties()->position = + gfx::PointF(std::numeric_limits<int>::min() + 100, 0); child1->SetBounds(gfx::Size(1, 1)); // Really far right. - child2->SetPosition(gfx::PointF(std::numeric_limits<int>::max() - 100, 0)); + child2->test_properties()->position = + gfx::PointF(std::numeric_limits<int>::max() - 100, 0); child2->SetBounds(gfx::Size(1, 1)); root->layer_tree_impl()->property_trees()->needs_rebuild = true; @@ -1794,11 +1795,13 @@ root->test_properties()->backdrop_filters = filters; // Really far left. - child1->SetPosition(gfx::PointF(std::numeric_limits<int>::min() + 100, 0)); + child1->test_properties()->position = + gfx::PointF(std::numeric_limits<int>::min() + 100, 0); child1->SetBounds(gfx::Size(1, 1)); // Really far right. - child2->SetPosition(gfx::PointF(std::numeric_limits<int>::max() - 100, 0)); + child2->test_properties()->position = + gfx::PointF(std::numeric_limits<int>::max() - 100, 0); child2->SetBounds(gfx::Size(1, 1)); root->layer_tree_impl()->property_trees()->needs_rebuild = true; @@ -1824,14 +1827,14 @@ LayerImpl* grandchild2 = child1->test_properties()->children[1]; // Really far left. - grandchild1->SetPosition( - gfx::PointF(std::numeric_limits<int>::min() + 500, 0)); + grandchild1->test_properties()->position = + gfx::PointF(std::numeric_limits<int>::min() + 500, 0); grandchild1->SetBounds(gfx::Size(1, 1)); grandchild1->SetDrawsContent(true); // Really far right. - grandchild2->SetPosition( - gfx::PointF(std::numeric_limits<int>::max() - 500, 0)); + grandchild2->test_properties()->position = + gfx::PointF(std::numeric_limits<int>::max() - 500, 0); grandchild2->SetBounds(gfx::Size(1, 1)); grandchild2->SetDrawsContent(true); @@ -1920,14 +1923,14 @@ child1->test_properties()->backdrop_filters = filters; // Really far left. - grandchild1->SetPosition( - gfx::PointF(std::numeric_limits<int>::min() + 500, 0)); + grandchild1->test_properties()->position = + gfx::PointF(std::numeric_limits<int>::min() + 500, 0); grandchild1->SetBounds(gfx::Size(1, 1)); grandchild1->SetDrawsContent(true); // Really far right. - grandchild2->SetPosition( - gfx::PointF(std::numeric_limits<int>::max() - 500, 0)); + grandchild2->test_properties()->position = + gfx::PointF(std::numeric_limits<int>::max() - 500, 0); grandchild2->SetBounds(gfx::Size(1, 1)); grandchild2->SetDrawsContent(true);
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc index def5a7c..e2f1c0d 100644 --- a/cc/trees/layer_tree_host_common.cc +++ b/cc/trees/layer_tree_host_common.cc
@@ -572,9 +572,6 @@ gfx::RectF(gfx::SizeF(inputs->device_viewport_size))); float page_scale_factor_for_root = combine_dsf_and_psf ? inputs->page_scale_factor : 1.f; - // SetRootTransformsAndScales will be incorrect if the root layer has - // non-zero position, so ensure it is zero. - DCHECK(inputs->root_layer->position().IsOrigin()); property_trees->transform_tree.SetRootTransformsAndScales( inputs->device_scale_factor, page_scale_factor_for_root, inputs->device_transform);
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc index b2a7a579..05a2290e 100644 --- a/cc/trees/layer_tree_host_common_unittest.cc +++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -350,11 +350,12 @@ root->SetBounds(gfx::Size(500, 500)); root->test_properties()->force_render_surface = true; - target->SetPosition(gfx::PointF(target_rect.origin())); + target->test_properties()->position = gfx::PointF(target_rect.origin()); target->SetBounds(target_rect.size()); target->test_properties()->force_render_surface = true; drawing_layer->test_properties()->transform = layer_transform; - drawing_layer->SetPosition(gfx::PointF(layer_rect.origin())); + drawing_layer->test_properties()->position = + gfx::PointF(layer_rect.origin()); drawing_layer->SetBounds(layer_rect.size()); drawing_layer->test_properties()->should_flatten_transform = false; @@ -396,7 +397,7 @@ child->SetDrawsContent(true); parent->SetBounds(gfx::Size(100, 100)); - child->SetPosition(gfx::PointF(10, 10)); + child->test_properties()->position = gfx::PointF(10, 10); child->SetBounds(gfx::Size(100, 100)); child->test_properties()->opacity = 0.f; ExecuteCalculateDrawProperties(parent); @@ -448,7 +449,7 @@ // screen space transform. gfx::Transform position_transform; position_transform.Translate(0.f, 1.2f); - layer->SetPosition(gfx::PointF(0.f, 1.2f)); + layer->test_properties()->position = gfx::PointF(0.f, 1.2f); host_impl()->active_tree()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -465,7 +466,7 @@ layer_transform.Scale3d(2.0, 2.0, 1.0); layer->test_properties()->transform = layer_transform; layer->test_properties()->transform_origin = gfx::Point3F(); - layer->SetPosition(gfx::PointF()); + layer->test_properties()->position = gfx::PointF(); host_impl()->active_tree()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -495,7 +496,7 @@ // it is still worth testing to detect accidental regressions. expected_result = position_transform * translation_to_anchor * layer_transform * Inverse(translation_to_anchor); - layer->SetPosition(gfx::PointF(0.f, 1.2f)); + layer->test_properties()->position = gfx::PointF(0.f, 1.2f); host_impl()->active_tree()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -645,7 +646,7 @@ // Case 2: parent's position affects child and grand_child. gfx::Transform parent_position_transform; parent_position_transform.Translate(0.f, 1.2f); - parent->SetPosition(gfx::PointF(0.f, 1.2f)); + parent->test_properties()->position = gfx::PointF(0.f, 1.2f); host_impl()->active_tree()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -670,7 +671,7 @@ parent_translation_to_anchor * parent_layer_transform * Inverse(parent_translation_to_anchor); parent->test_properties()->transform = parent_layer_transform; - parent->SetPosition(gfx::PointF()); + parent->test_properties()->position = gfx::PointF(); host_impl()->active_tree()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -1367,7 +1368,7 @@ render_surface1->SetBounds(gfx::Size(10, 10)); render_surface1->test_properties()->force_render_surface = true; child->SetDrawsContent(true); - child->SetPosition(gfx::PointF(30.f, 30.f)); + child->test_properties()->position = gfx::PointF(30.f, 30.f); child->SetBounds(gfx::Size(10, 10)); ExecuteCalculateDrawProperties(root); @@ -1481,7 +1482,7 @@ child1->SetBounds(gfx::Size(25, 25)); child1->SetDrawsContent(true); child1->test_properties()->force_render_surface = true; - child2->SetPosition(gfx::PointF(25, 25)); + child2->test_properties()->position = gfx::PointF(25, 25); child2->SetBounds(gfx::Size(25, 25)); child2->SetDrawsContent(true); child2->test_properties()->force_render_surface = true; @@ -1699,7 +1700,7 @@ child->SetBounds(gfx::Size(20, 20)); child->SetMasksToBounds(true); child->test_properties()->force_render_surface = true; - grand_child->SetPosition(gfx::PointF(45.f, 45.f)); + grand_child->test_properties()->position = gfx::PointF(45.f, 45.f); grand_child->SetBounds(gfx::Size(10, 10)); great_grand_child->SetBounds(gfx::Size(10, 10)); leaf_node1->SetBounds(gfx::Size(500, 500)); @@ -1739,7 +1740,7 @@ root->SetBounds(gfx::Size(100, 100)); child->SetBounds(gfx::Size(20, 20)); child->test_properties()->force_render_surface = true; - grand_child->SetPosition(gfx::PointF(200.f, 200.f)); + grand_child->test_properties()->position = gfx::PointF(200.f, 200.f); grand_child->SetBounds(gfx::Size(10, 10)); grand_child->test_properties()->force_render_surface = true; leaf_node->SetBounds(gfx::Size(10, 10)); @@ -1874,7 +1875,7 @@ EXPECT_EQ(gfx::Rect(100, 100), child->clip_rect()); parent->SetMasksToBounds(true); - child->SetPosition(gfx::PointF(100.f, 100.f)); + child->test_properties()->position = gfx::PointF(100.f, 100.f); host_impl()->active_tree()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); @@ -1910,17 +1911,17 @@ child->SetMasksToBounds(true); child->SetBounds(gfx::Size(20, 20)); child->test_properties()->force_render_surface = true; - grand_child1->SetPosition(gfx::PointF(5.f, 5.f)); + grand_child1->test_properties()->position = gfx::PointF(5.f, 5.f); grand_child1->SetBounds(gfx::Size(10, 10)); grand_child1->SetDrawsContent(true); - grand_child2->SetPosition(gfx::PointF(15.f, 15.f)); + grand_child2->test_properties()->position = gfx::PointF(15.f, 15.f); grand_child2->SetBounds(gfx::Size(10, 10)); grand_child2->SetDrawsContent(true); - grand_child3->SetPosition(gfx::PointF(15.f, 15.f)); + grand_child3->test_properties()->position = gfx::PointF(15.f, 15.f); grand_child3->SetMasksToBounds(true); grand_child3->SetBounds(gfx::Size(10, 10)); grand_child3->SetDrawsContent(true); - grand_child4->SetPosition(gfx::PointF(45.f, 45.f)); + grand_child4->test_properties()->position = gfx::PointF(45.f, 45.f); grand_child4->SetBounds(gfx::Size(10, 10)); grand_child4->SetDrawsContent(true); ExecuteCalculateDrawProperties(parent); @@ -1956,17 +1957,17 @@ child->SetBounds(gfx::Size(20, 20)); child->SetMasksToBounds(true); child->test_properties()->force_render_surface = true; - grand_child1->SetPosition(gfx::PointF(5.f, 5.f)); + grand_child1->test_properties()->position = gfx::PointF(5.f, 5.f); grand_child1->SetBounds(gfx::Size(10, 10)); grand_child1->test_properties()->force_render_surface = true; - grand_child2->SetPosition(gfx::PointF(15.f, 15.f)); + grand_child2->test_properties()->position = gfx::PointF(15.f, 15.f); grand_child2->SetBounds(gfx::Size(10, 10)); grand_child2->test_properties()->force_render_surface = true; - grand_child3->SetPosition(gfx::PointF(15.f, 15.f)); + grand_child3->test_properties()->position = gfx::PointF(15.f, 15.f); grand_child3->SetBounds(gfx::Size(10, 10)); grand_child3->SetMasksToBounds(true); grand_child3->test_properties()->force_render_surface = true; - grand_child4->SetPosition(gfx::PointF(45.f, 45.f)); + grand_child4->test_properties()->position = gfx::PointF(45.f, 45.f); grand_child4->SetBounds(gfx::Size(10, 10)); grand_child4->SetMasksToBounds(true); grand_child4->test_properties()->force_render_surface = true; @@ -2022,44 +2023,44 @@ root->SetBounds(gfx::Size(10, 10)); root->test_properties()->transform_origin = gfx::Point3F(0.25f, 0.f, 0.f); render_surface1->test_properties()->transform = layer_transform; - render_surface1->SetPosition(gfx::PointF(2.5f, 0.f)); + render_surface1->test_properties()->position = gfx::PointF(2.5f, 0.f); render_surface1->SetBounds(gfx::Size(10, 10)); render_surface1->test_properties()->transform_origin = gfx::Point3F(0.25f, 0.f, 0.f); render_surface1->test_properties()->force_render_surface = true; render_surface2->test_properties()->transform = layer_transform; - render_surface2->SetPosition(gfx::PointF(2.5f, 0.f)); + render_surface2->test_properties()->position = gfx::PointF(2.5f, 0.f); render_surface2->SetBounds(gfx::Size(10, 10)); render_surface2->test_properties()->transform_origin = gfx::Point3F(0.25f, 0.f, 0.f); render_surface2->test_properties()->force_render_surface = true; child_of_root->test_properties()->transform = layer_transform; - child_of_root->SetPosition(gfx::PointF(2.5f, 0.f)); + child_of_root->test_properties()->position = gfx::PointF(2.5f, 0.f); child_of_root->SetBounds(gfx::Size(10, 10)); child_of_root->test_properties()->transform_origin = gfx::Point3F(0.25f, 0.f, 0.f); child_of_rs1->test_properties()->transform = layer_transform; - child_of_rs1->SetPosition(gfx::PointF(2.5f, 0.f)); + child_of_rs1->test_properties()->position = gfx::PointF(2.5f, 0.f); child_of_rs1->SetBounds(gfx::Size(10, 10)); child_of_rs1->test_properties()->transform_origin = gfx::Point3F(0.25f, 0.f, 0.f); child_of_rs2->test_properties()->transform = layer_transform; - child_of_rs2->SetPosition(gfx::PointF(2.5f, 0.f)); + child_of_rs2->test_properties()->position = gfx::PointF(2.5f, 0.f); child_of_rs2->SetBounds(gfx::Size(10, 10)); child_of_rs2->test_properties()->transform_origin = gfx::Point3F(0.25f, 0.f, 0.f); grand_child_of_root->test_properties()->transform = layer_transform; - grand_child_of_root->SetPosition(gfx::PointF(2.5f, 0.f)); + grand_child_of_root->test_properties()->position = gfx::PointF(2.5f, 0.f); grand_child_of_root->SetBounds(gfx::Size(10, 10)); grand_child_of_root->test_properties()->transform_origin = gfx::Point3F(0.25f, 0.f, 0.f); grand_child_of_rs1->test_properties()->transform = layer_transform; - grand_child_of_rs1->SetPosition(gfx::PointF(2.5f, 0.f)); + grand_child_of_rs1->test_properties()->position = gfx::PointF(2.5f, 0.f); grand_child_of_rs1->SetBounds(gfx::Size(10, 10)); grand_child_of_rs1->test_properties()->transform_origin = gfx::Point3F(0.25f, 0.f, 0.f); grand_child_of_rs2->test_properties()->transform = layer_transform; - grand_child_of_rs2->SetPosition(gfx::PointF(2.5f, 0.f)); + grand_child_of_rs2->test_properties()->position = gfx::PointF(2.5f, 0.f); grand_child_of_rs2->SetBounds(gfx::Size(10, 10)); grand_child_of_rs2->test_properties()->transform_origin = gfx::Point3F(0.25f, 0.f, 0.f); @@ -2556,10 +2557,10 @@ root->SetBounds(gfx::Size(100, 100)); child1_layer->SetBounds(gfx::Size(50, 50)); child1_layer->SetDrawsContent(true); - child2_layer->SetPosition(gfx::PointF(75.f, 75.f)); + child2_layer->test_properties()->position = gfx::PointF(75.f, 75.f); child2_layer->SetBounds(gfx::Size(50, 50)); child2_layer->SetDrawsContent(true); - child3_layer->SetPosition(gfx::PointF(125.f, 125.f)); + child3_layer->test_properties()->position = gfx::PointF(125.f, 125.f); child3_layer->SetBounds(gfx::Size(50, 50)); child3_layer->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); @@ -2592,13 +2593,13 @@ root->SetBounds(gfx::Size(100, 100)); child->SetBounds(gfx::Size(100, 100)); child->SetMasksToBounds(true); - grand_child1->SetPosition(gfx::PointF(5.f, 5.f)); + grand_child1->test_properties()->position = gfx::PointF(5.f, 5.f); grand_child1->SetBounds(gfx::Size(50, 50)); grand_child1->SetDrawsContent(true); - grand_child2->SetPosition(gfx::PointF(75.f, 75.f)); + grand_child2->test_properties()->position = gfx::PointF(75.f, 75.f); grand_child2->SetBounds(gfx::Size(50, 50)); grand_child2->SetDrawsContent(true); - grand_child3->SetPosition(gfx::PointF(125.f, 125.f)); + grand_child3->test_properties()->position = gfx::PointF(125.f, 125.f); grand_child3->SetBounds(gfx::Size(50, 50)); grand_child3->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); @@ -2654,7 +2655,7 @@ clip->SetBounds(gfx::Size(10, 10)); filter->test_properties()->force_render_surface = true; filter_child->SetBounds(gfx::Size(2000, 2000)); - filter_child->SetPosition(gfx::PointF(-50, -50)); + filter_child->test_properties()->position = gfx::PointF(-50, -50); filter_child->SetDrawsContent(true); clip->SetMasksToBounds(true); @@ -2703,7 +2704,7 @@ clip->SetBounds(gfx::Size(10, 10)); filter->test_properties()->force_render_surface = true; filter_child->SetBounds(gfx::Size(2000, 2000)); - filter_child->SetPosition(gfx::PointF(-50, -50)); + filter_child->test_properties()->position = gfx::PointF(-50, -50); filter_child->SetDrawsContent(true); clip->SetMasksToBounds(true); @@ -2829,13 +2830,13 @@ root->SetBounds(gfx::Size(100, 100)); render_surface->SetBounds(gfx::Size(3, 4)); render_surface->test_properties()->force_render_surface = true; - child1->SetPosition(gfx::PointF(5.f, 5.f)); + child1->test_properties()->position = gfx::PointF(5.f, 5.f); child1->SetBounds(gfx::Size(50, 50)); child1->SetDrawsContent(true); - child2->SetPosition(gfx::PointF(75.f, 75.f)); + child2->test_properties()->position = gfx::PointF(75.f, 75.f); child2->SetBounds(gfx::Size(50, 50)); child2->SetDrawsContent(true); - child3->SetPosition(gfx::PointF(125.f, 125.f)); + child3->test_properties()->position = gfx::PointF(125.f, 125.f); child3->SetBounds(gfx::Size(50, 50)); child3->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); @@ -2873,13 +2874,13 @@ LayerImpl* child3 = AddChild<LayerImpl>(root); root->SetBounds(gfx::Size(100, 100)); - child1->SetPosition(gfx::PointF(5.f, 5.f)); + child1->test_properties()->position = gfx::PointF(5.f, 5.f); child1->SetBounds(gfx::Size(50, 50)); child1->SetDrawsContent(true); - child2->SetPosition(gfx::PointF(75.f, 75.f)); + child2->test_properties()->position = gfx::PointF(75.f, 75.f); child2->SetBounds(gfx::Size(50, 50)); child2->SetDrawsContent(true); - child3->SetPosition(gfx::PointF(125.f, 125.f)); + child3->test_properties()->position = gfx::PointF(125.f, 125.f); child3->SetBounds(gfx::Size(50, 50)); child3->SetDrawsContent(true); @@ -2910,7 +2911,7 @@ LayerImpl* child = AddChildToRoot<LayerImpl>(); root->SetBounds(gfx::Size(100, 100)); - child->SetPosition(gfx::PointF(5.f, 5.f)); + child->test_properties()->position = gfx::PointF(5.f, 5.f); child->SetBounds(gfx::Size(50, 50)); child->SetDrawsContent(true); @@ -2963,7 +2964,7 @@ root->SetBounds(gfx::Size(100, 100)); child->test_properties()->transform = perspective; - child->SetPosition(gfx::PointF(10.f, 10.f)); + child->test_properties()->position = gfx::PointF(10.f, 10.f); child->SetBounds(gfx::Size(100, 100)); child->SetDrawsContent(true); child->test_properties()->sorting_context_id = 1; @@ -3127,7 +3128,7 @@ root->SetBounds(gfx::Size(1000, 1000)); child->test_properties()->transform = perspective; - child->SetPosition(gfx::PointF(10.f, 10.f)); + child->test_properties()->position = gfx::PointF(10.f, 10.f); child->SetBounds(gfx::Size(300, 300)); child->test_properties()->should_flatten_transform = false; child->test_properties()->sorting_context_id = 1; @@ -3191,13 +3192,13 @@ root->SetMasksToBounds(true); render_surface->SetBounds(gfx::Size(3, 4)); render_surface->test_properties()->force_render_surface = true; - child1->SetPosition(gfx::PointF(5.f, 5.f)); + child1->test_properties()->position = gfx::PointF(5.f, 5.f); child1->SetBounds(gfx::Size(50, 50)); child1->SetDrawsContent(true); - child2->SetPosition(gfx::PointF(75.f, 75.f)); + child2->test_properties()->position = gfx::PointF(75.f, 75.f); child2->SetBounds(gfx::Size(50, 50)); child2->SetDrawsContent(true); - child3->SetPosition(gfx::PointF(125.f, 125.f)); + child3->test_properties()->position = gfx::PointF(125.f, 125.f); child3->SetBounds(gfx::Size(50, 50)); child3->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); @@ -3244,13 +3245,13 @@ render_surface1->test_properties()->force_render_surface = true; render_surface2->SetBounds(gfx::Size(7, 13)); render_surface2->test_properties()->force_render_surface = true; - child1->SetPosition(gfx::PointF(5.f, 5.f)); + child1->test_properties()->position = gfx::PointF(5.f, 5.f); child1->SetBounds(gfx::Size(50, 50)); child1->SetDrawsContent(true); - child2->SetPosition(gfx::PointF(75.f, 75.f)); + child2->test_properties()->position = gfx::PointF(75.f, 75.f); child2->SetBounds(gfx::Size(50, 50)); child2->SetDrawsContent(true); - child3->SetPosition(gfx::PointF(125.f, 125.f)); + child3->test_properties()->position = gfx::PointF(125.f, 125.f); child3->SetBounds(gfx::Size(50, 50)); child3->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); @@ -3360,7 +3361,7 @@ root->SetBounds(gfx::Size(100, 100)); - clip_parent->SetPosition(gfx::PointF(2.f, 2.f)); + clip_parent->test_properties()->position = gfx::PointF(2.f, 2.f); clip_parent->SetBounds(gfx::Size(50, 50)); clip_parent->test_properties()->clip_children = std::make_unique<std::set<LayerImpl*>>(); @@ -3512,7 +3513,7 @@ render_surface->SetBounds(gfx::Size(3, 4)); render_surface->test_properties()->force_render_surface = true; child1->test_properties()->transform = child_rotation; - child1->SetPosition(gfx::PointF(25.f, 25.f)); + child1->test_properties()->position = gfx::PointF(25.f, 25.f); child1->SetBounds(gfx::Size(50, 50)); child1->SetDrawsContent(true); child1->test_properties()->transform_origin = gfx::Point3F(25.f, 25.f, 0.f); @@ -3558,7 +3559,7 @@ root->SetMasksToBounds(true); render_surface->SetBounds(gfx::Size(3, 4)); render_surface->test_properties()->force_render_surface = true; - child1->SetPosition(gfx::PointF(25.f, 25.f)); + child1->test_properties()->position = gfx::PointF(25.f, 25.f); child1->SetBounds(gfx::Size(50, 50)); child1->SetDrawsContent(true); child1->test_properties()->transform = child_rotation; @@ -3604,21 +3605,21 @@ root->SetBounds(gfx::Size(100, 100)); root->SetMasksToBounds(true); render_surface1->SetBounds(gfx::Size(3, 4)); - render_surface1->SetPosition(gfx::PointF(5.f, 5.f)); + render_surface1->test_properties()->position = gfx::PointF(5.f, 5.f); render_surface1->SetDrawsContent(true); render_surface1->test_properties()->force_render_surface = true; render_surface2->SetBounds(gfx::Size(7, 13)); - render_surface2->SetPosition(gfx::PointF(5.f, 5.f)); + render_surface2->test_properties()->position = gfx::PointF(5.f, 5.f); render_surface2->SetDrawsContent(true); render_surface2->test_properties()->force_render_surface = true; child1->SetBounds(gfx::Size(50, 50)); - child1->SetPosition(gfx::PointF(5.f, 5.f)); + child1->test_properties()->position = gfx::PointF(5.f, 5.f); child1->SetDrawsContent(true); child2->SetBounds(gfx::Size(50, 50)); - child2->SetPosition(gfx::PointF(75.f, 75.f)); + child2->test_properties()->position = gfx::PointF(75.f, 75.f); child2->SetDrawsContent(true); child3->SetBounds(gfx::Size(50, 50)); - child3->SetPosition(gfx::PointF(125.f, 125.f)); + child3->test_properties()->position = gfx::PointF(125.f, 125.f); child3->SetDrawsContent(true); float device_scale_factor = 2.f; ExecuteCalculateDrawProperties(root, device_scale_factor); @@ -4002,12 +4003,12 @@ root->SetDrawsContent(true); LayerImpl* child = AddChildToRoot<LayerImpl>(); - child->SetPosition(gfx::PointF(2.f, 2.f)); + child->test_properties()->position = gfx::PointF(2.f, 2.f); child->SetBounds(gfx::Size(10, 10)); child->SetDrawsContent(true); LayerImpl* child2 = AddChildToRoot<LayerImpl>(); - child2->SetPosition(gfx::PointF(2.f, 2.f)); + child2->test_properties()->position = gfx::PointF(2.f, 2.f); child2->SetBounds(gfx::Size(5, 5)); child2->SetDrawsContent(true); @@ -4044,8 +4045,8 @@ // Verify child and child2 transforms. They should match. gfx::Transform expected_child_transform; expected_child_transform.Scale(device_scale_factor, device_scale_factor); - expected_child_transform.Translate(child->position().x(), - child->position().y()); + expected_child_transform.Translate(child->test_properties()->position.x(), + child->test_properties()->position.y()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, @@ -4069,7 +4070,7 @@ gfx::RectF child2_screen_space_rect = MathUtil::MapClippedRect(child2->ScreenSpaceTransform(), child_bounds); - gfx::RectF expected_child_draw_rect(child->position(), + gfx::RectF expected_child_draw_rect(child->test_properties()->position, gfx::SizeF(child->bounds())); expected_child_draw_rect.Scale(device_scale_factor); EXPECT_FLOAT_RECT_EQ(expected_child_draw_rect, child_draw_rect); @@ -4097,7 +4098,7 @@ parent->SetDrawsContent(true); LayerImpl* perspective_surface = AddChild<LayerImpl>(parent); - perspective_surface->SetPosition(gfx::PointF(2.f, 2.f)); + perspective_surface->test_properties()->position = gfx::PointF(2.f, 2.f); perspective_surface->SetBounds(gfx::Size(10, 10)); perspective_surface->test_properties()->transform = perspective_matrix * scale_small_matrix; @@ -4105,7 +4106,7 @@ perspective_surface->test_properties()->force_render_surface = true; LayerImpl* scale_surface = AddChild<LayerImpl>(parent); - scale_surface->SetPosition(gfx::PointF(2.f, 2.f)); + scale_surface->test_properties()->position = gfx::PointF(2.f, 2.f); scale_surface->SetBounds(gfx::Size(10, 10)); scale_surface->test_properties()->transform = scale_small_matrix; scale_surface->SetDrawsContent(true); @@ -4158,9 +4159,9 @@ gfx::Transform expected_perspective_surface_draw_transform; expected_perspective_surface_draw_transform.Translate( device_scale_factor * page_scale_factor * - perspective_surface->position().x(), + perspective_surface->test_properties()->position.x(), device_scale_factor * page_scale_factor * - perspective_surface->position().y()); + perspective_surface->test_properties()->position.y()); expected_perspective_surface_draw_transform.PreconcatTransform( perspective_matrix); expected_perspective_surface_draw_transform.PreconcatTransform( @@ -4195,7 +4196,7 @@ parent->SetDrawsContent(true); LayerImpl* child_scale = AddChild<LayerImpl>(parent); - child_scale->SetPosition(gfx::PointF(2.f, 2.f)); + child_scale->test_properties()->position = gfx::PointF(2.f, 2.f); child_scale->SetBounds(gfx::Size(10, 10)); child_scale->test_properties()->transform = child_scale_matrix; child_scale->SetDrawsContent(true); @@ -4239,7 +4240,7 @@ LayerImpl* child_scale = AddChild<LayerImpl>(parent); child_scale->SetBounds(gfx::Size(10, 10)); - child_scale->SetPosition(gfx::PointF(2.f, 2.f)); + child_scale->test_properties()->position = gfx::PointF(2.f, 2.f); child_scale->test_properties()->transform = child_scale_matrix; child_scale->SetDrawsContent(true); @@ -4261,7 +4262,7 @@ LayerImpl* child = AddChildToRoot<LayerImpl>(); child->SetBounds(gfx::Size(10, 10)); - child->SetPosition(gfx::PointF(2.f, 2.f)); + child->test_properties()->position = gfx::PointF(2.f, 2.f); child->SetDrawsContent(true); child->test_properties()->force_render_surface = true; @@ -4293,8 +4294,9 @@ gfx::Transform expected_screen_space_transform; expected_screen_space_transform.Scale(device_scale_factor, device_scale_factor); - expected_screen_space_transform.Translate(child->position().x(), - child->position().y()); + expected_screen_space_transform.Translate( + child->test_properties()->position.x(), + child->test_properties()->position.y()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_screen_space_transform, child->ScreenSpaceTransform()); @@ -4311,8 +4313,8 @@ gfx::Transform expected_render_surface_draw_transform; expected_render_surface_draw_transform.Translate( - device_scale_factor * child->position().x(), - device_scale_factor * child->position().y()); + device_scale_factor * child->test_properties()->position.x(), + device_scale_factor * child->test_properties()->position.y()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_render_surface_draw_transform, GetRenderSurface(child)->draw_transform()); @@ -5057,7 +5059,7 @@ copy_layer->test_properties()->force_render_surface = true; LayerImpl* copy_child = AddChild<LayerImpl>(copy_layer); - copy_child->SetPosition(gfx::PointF(40.f, 40.f)); + copy_child->test_properties()->position = gfx::PointF(40.f, 40.f); copy_child->SetBounds(gfx::Size(20, 20)); copy_child->SetDrawsContent(true); @@ -5066,12 +5068,12 @@ copy_clip->SetMasksToBounds(true); LayerImpl* copy_clipped_child = AddChild<LayerImpl>(copy_clip); - copy_clipped_child->SetPosition(gfx::PointF(40.f, 40.f)); + copy_clipped_child->test_properties()->position = gfx::PointF(40.f, 40.f); copy_clipped_child->SetBounds(gfx::Size(20, 20)); copy_clipped_child->SetDrawsContent(true); LayerImpl* copy_surface = AddChild<LayerImpl>(copy_clip); - copy_surface->SetPosition(gfx::PointF(45.f, 45.f)); + copy_surface->test_properties()->position = gfx::PointF(45.f, 45.f); copy_surface->SetBounds(gfx::Size(20, 20)); copy_surface->SetDrawsContent(true); copy_surface->test_properties()->force_render_surface = true; @@ -5154,11 +5156,11 @@ render_surface->SetBounds(gfx::Size(10, 10)); render_surface->test_properties()->force_render_surface = true; clip_parent->test_properties()->transform = scale_transform; - clip_parent->SetPosition(gfx::PointF(1.f, 1.f)); + clip_parent->test_properties()->position = gfx::PointF(1.f, 1.f); clip_parent->SetBounds(gfx::Size(10, 10)); - intervening->SetPosition(gfx::PointF(1.f, 1.f)); + intervening->test_properties()->position = gfx::PointF(1.f, 1.f); intervening->SetBounds(gfx::Size(5, 5)); - clip_child->SetPosition(gfx::PointF(1.f, 1.f)); + clip_child->test_properties()->position = gfx::PointF(1.f, 1.f); clip_child->SetBounds(gfx::Size(10, 10)); ExecuteCalculateDrawProperties(root); @@ -5215,15 +5217,15 @@ translation_transform.Translate(2, 2); root->SetBounds(gfx::Size(50, 50)); - clip_parent->SetPosition(gfx::PointF(1.f, 1.f)); + clip_parent->test_properties()->position = gfx::PointF(1.f, 1.f); clip_parent->SetBounds(gfx::Size(40, 40)); render_surface1->SetBounds(gfx::Size(10, 10)); render_surface1->test_properties()->force_render_surface = true; - intervening->SetPosition(gfx::PointF(1.f, 1.f)); + intervening->test_properties()->position = gfx::PointF(1.f, 1.f); intervening->SetBounds(gfx::Size(5, 5)); render_surface2->SetBounds(gfx::Size(10, 10)); render_surface2->test_properties()->force_render_surface = true; - clip_child->SetPosition(gfx::PointF(-10.f, -10.f)); + clip_child->test_properties()->position = gfx::PointF(-10.f, -10.f); clip_child->SetBounds(gfx::Size(60, 60)); ExecuteCalculateDrawProperties(root); @@ -5301,15 +5303,15 @@ root->SetBounds(gfx::Size(50, 50)); clip_parent->test_properties()->transform = translation_transform; - clip_parent->SetPosition(gfx::PointF(1.f, 1.f)); + clip_parent->test_properties()->position = gfx::PointF(1.f, 1.f); clip_parent->SetBounds(gfx::Size(40, 40)); render_surface1->SetBounds(gfx::Size(10, 10)); render_surface1->test_properties()->force_render_surface = true; - intervening->SetPosition(gfx::PointF(1.f, 1.f)); + intervening->test_properties()->position = gfx::PointF(1.f, 1.f); intervening->SetBounds(gfx::Size(5, 5)); render_surface2->SetBounds(gfx::Size(10, 10)); render_surface2->test_properties()->force_render_surface = true; - clip_child->SetPosition(gfx::PointF(-10.f, -10.f)); + clip_child->test_properties()->position = gfx::PointF(-10.f, -10.f); clip_child->SetBounds(gfx::Size(60, 60)); BuildPropertyTreesForTesting(); intervening->SetCurrentScrollOffset(gfx::ScrollOffset(3, 3)); @@ -5434,12 +5436,12 @@ root->SetBounds(gfx::Size(15, 15)); clip_parent->SetBounds(gfx::Size(10, 10)); clip_layer->SetBounds(gfx::Size(10, 10)); - render_surface1->SetPosition(gfx::PointF(5, 5)); + render_surface1->test_properties()->position = gfx::PointF(5, 5); render_surface1->SetBounds(gfx::Size(5, 5)); render_surface1->test_properties()->force_render_surface = true; render_surface2->SetBounds(gfx::Size(5, 5)); render_surface2->test_properties()->force_render_surface = true; - clip_child->SetPosition(gfx::PointF(-1, 1)); + clip_child->test_properties()->position = gfx::PointF(-1, 1); clip_child->SetBounds(gfx::Size(10, 10)); non_clip_child->SetBounds(gfx::Size(5, 5)); @@ -5726,7 +5728,7 @@ scroll_child_target->SetBounds(gfx::Size(50, 50)); scroll_child_target->test_properties()->force_render_surface = true; scroll_child->SetBounds(gfx::Size(50, 50)); - scroll_parent_target->SetPosition(gfx::PointF(10, 10)); + scroll_parent_target->test_properties()->position = gfx::PointF(10, 10); scroll_parent_target->SetBounds(gfx::Size(50, 50)); scroll_parent_target->SetMasksToBounds(true); scroll_parent_target->test_properties()->force_render_surface = true; @@ -5983,13 +5985,13 @@ fixed->test_properties()->position_constraint = constraint; root->SetBounds(gfx::Size(50, 50)); - render_surface->SetPosition(gfx::PointF(7.f, 9.f)); + render_surface->test_properties()->position = gfx::PointF(7.f, 9.f); render_surface->SetBounds(gfx::Size(50, 50)); render_surface->SetDrawsContent(true); - fixed->SetPosition(gfx::PointF(10.f, 15.f)); + fixed->test_properties()->position = gfx::PointF(10.f, 15.f); fixed->SetBounds(gfx::Size(50, 50)); fixed->SetDrawsContent(true); - child->SetPosition(gfx::PointF(1.f, 2.f)); + child->test_properties()->position = gfx::PointF(1.f, 2.f); child->SetBounds(gfx::Size(50, 50)); child->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); @@ -8404,9 +8406,9 @@ root->SetBounds(gfx::Size(50, 50)); root->SetMasksToBounds(true); root->test_properties()->is_container_for_fixed_position_layers = true; - child->SetPosition(gfx::PointF(1000, 1000)); + child->test_properties()->position = gfx::PointF(1000, 1000); child->SetBounds(gfx::Size(50, 50)); - grandchild->SetPosition(gfx::PointF(-1000, -1000)); + grandchild->test_properties()->position = gfx::PointF(-1000, -1000); grandchild->SetBounds(gfx::Size(50, 50)); grandchild->SetDrawsContent(true); @@ -8432,10 +8434,10 @@ root->SetBounds(gfx::Size(50, 50)); root->SetMasksToBounds(true); root->test_properties()->is_container_for_fixed_position_layers = true; - child->SetPosition(gfx::PointF(1000, 1000)); + child->test_properties()->position = gfx::PointF(1000, 1000); child->SetBounds(gfx::Size(50, 50)); child->test_properties()->is_container_for_fixed_position_layers = true; - grandchild->SetPosition(gfx::PointF(-1000, -1000)); + grandchild->test_properties()->position = gfx::PointF(-1000, -1000); grandchild->SetBounds(gfx::Size(50, 50)); grandchild->SetDrawsContent(true); @@ -8458,7 +8460,7 @@ root->SetBounds(gfx::Size(800, 800)); root->test_properties()->is_container_for_fixed_position_layers = true; - frame_clip->SetPosition(gfx::PointF(500, 100)); + frame_clip->test_properties()->position = gfx::PointF(500, 100); frame_clip->SetBounds(gfx::Size(100, 100)); frame_clip->SetMasksToBounds(true); @@ -8484,7 +8486,7 @@ root->SetBounds(gfx::Size(800, 800)); root->SetDrawsContent(true); root->test_properties()->is_container_for_fixed_position_layers = true; - frame_clip->SetPosition(gfx::PointF(500, 100)); + frame_clip->test_properties()->position = gfx::PointF(500, 100); frame_clip->SetBounds(gfx::Size(100, 100)); frame_clip->SetMasksToBounds(true); frame_clip->SetDrawsContent(true); @@ -8493,7 +8495,7 @@ scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id())); scroller->SetScrollable(frame_clip->bounds()); scroller->SetDrawsContent(true); - fixed->SetPosition(gfx::PointF(100, 100)); + fixed->test_properties()->position = gfx::PointF(100, 100); fixed->SetBounds(gfx::Size(50, 50)); fixed->SetMasksToBounds(true); fixed->SetDrawsContent(true); @@ -8581,7 +8583,7 @@ ExecuteCalculateDrawProperties(root); EXPECT_EQ(gfx::Rect(25, 25), scroll_child->visible_layer_rect()); - scroll_child->SetPosition(gfx::PointF(0, -10.f)); + scroll_child->test_properties()->position = gfx::PointF(0, -10.f); scroll_parent->SetCurrentScrollOffset(gfx::ScrollOffset(0.f, 10.f)); ExecuteCalculateDrawProperties(root); EXPECT_EQ(gfx::Rect(0, 5, 25, 25), scroll_child->visible_layer_rect()); @@ -9551,7 +9553,7 @@ root->SetMasksToBounds(true); clip_layer->SetBounds(gfx::Size(30, 30)); clip_layer->SetMasksToBounds(true); - render_surface1->SetPosition(gfx::PointF(10, 10)); + render_surface1->test_properties()->position = gfx::PointF(10, 10); render_surface1->SetBounds(gfx::Size(30, 30)); render_surface1->SetDrawsContent(true); render_surface1->test_properties()->force_render_surface = true; @@ -10336,7 +10338,7 @@ copy_layer->test_properties()->force_render_surface = true; LayerImpl* copy_child = AddChild<LayerImpl>(copy_layer); - copy_child->SetPosition(gfx::PointF(40.f, 40.f)); + copy_child->test_properties()->position = gfx::PointF(40.f, 40.f); copy_child->SetBounds(gfx::Size(20, 20)); copy_child->SetDrawsContent(true); @@ -10345,12 +10347,12 @@ copy_clip->SetMasksToBounds(true); LayerImpl* copy_clipped_child = AddChild<LayerImpl>(copy_clip); - copy_clipped_child->SetPosition(gfx::PointF(40.f, 40.f)); + copy_clipped_child->test_properties()->position = gfx::PointF(40.f, 40.f); copy_clipped_child->SetBounds(gfx::Size(20, 20)); copy_clipped_child->SetDrawsContent(true); LayerImpl* cache_surface = AddChild<LayerImpl>(copy_clip); - cache_surface->SetPosition(gfx::PointF(45.f, 45.f)); + cache_surface->test_properties()->position = gfx::PointF(45.f, 45.f); cache_surface->SetBounds(gfx::Size(20, 20)); cache_surface->SetDrawsContent(true); @@ -10477,7 +10479,7 @@ child1->SetBounds(gfx::Size(50, 50)); child1->SetDrawsContent(true); child1->test_properties()->force_render_surface = true; - child2->SetPosition(gfx::PointF(50, 50)); + child2->test_properties()->position = gfx::PointF(50, 50); child2->SetBounds(gfx::Size(50, 50)); child2->SetDrawsContent(true); child2->test_properties()->force_render_surface = true;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index c686e42..8ca47ac 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -276,7 +276,7 @@ } void SetupRootLayerImpl(std::unique_ptr<LayerImpl> root) { - root->SetPosition(gfx::PointF()); + root->test_properties()->position = gfx::PointF(); root->SetBounds(gfx::Size(10, 10)); root->SetDrawsContent(true); root->draw_properties().visible_layer_rect = gfx::Rect(0, 0, 10, 10); @@ -359,7 +359,7 @@ std::unique_ptr<LayerImpl> root = LayerImpl::Create(layer_tree_impl, 1); root->SetBounds(content_size); - root->SetPosition(gfx::PointF()); + root->test_properties()->position = gfx::PointF(); root->test_properties()->force_render_surface = true; std::unique_ptr<LayerImpl> inner_scroll = @@ -384,7 +384,7 @@ inner_scroll->SetElementId( LayerIdToElementIdForTesting(inner_scroll->id())); inner_scroll->SetBounds(content_size); - inner_scroll->SetPosition(gfx::PointF()); + inner_scroll->test_properties()->position = gfx::PointF(); std::unique_ptr<LayerImpl> outer_clip = LayerImpl::Create(layer_tree_impl, kOuterViewportClipLayerId); @@ -402,13 +402,13 @@ ->scroll_tree.UpdateScrollOffsetBaseForTesting( outer_scroll->element_id(), gfx::ScrollOffset()); outer_scroll->SetBounds(content_size); - outer_scroll->SetPosition(gfx::PointF()); + outer_scroll->test_properties()->position = gfx::PointF(); std::unique_ptr<LayerImpl> contents = LayerImpl::Create(layer_tree_impl, kContentLayerId); contents->SetDrawsContent(true); contents->SetBounds(content_size); - contents->SetPosition(gfx::PointF()); + contents->test_properties()->position = gfx::PointF(); outer_scroll->test_properties()->AddChild(std::move(contents)); outer_clip->test_properties()->AddChild(std::move(outer_scroll)); @@ -447,7 +447,7 @@ host_impl_->active_tree()->SetDeviceViewportSize(content_size); std::unique_ptr<LayerImpl> root = LayerImpl::Create(layer_tree_impl, 1); root->SetBounds(content_size); - root->SetPosition(gfx::PointF()); + root->test_properties()->position = gfx::PointF(); std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 3); scroll->SetBounds(scroll_content_size); @@ -459,14 +459,14 @@ SolidColorScrollbarLayerImpl::Create(layer_tree_impl, 4, VERTICAL, 10, 0, false, true); scrollbar->SetBounds(scrollbar_size); - scrollbar->SetPosition(gfx::PointF(345, 0)); + scrollbar->test_properties()->position = gfx::PointF(345, 0); scrollbar->SetScrollElementId(scroll->element_id()); scrollbar->SetDrawsContent(true); scrollbar->test_properties()->opacity = 1.f; std::unique_ptr<LayerImpl> squash1 = LayerImpl::Create(layer_tree_impl, 5); squash1->SetBounds(gfx::Size(140, 300)); - squash1->SetPosition(gfx::PointF(220, 0)); + squash1->test_properties()->position = gfx::PointF(220, 0); if (transparent_layer) { squash1->test_properties()->opacity = 0.0f; // The transparent layer should still participate in hit testing even @@ -478,7 +478,7 @@ std::unique_ptr<LayerImpl> squash2 = LayerImpl::Create(layer_tree_impl, 6); squash2->SetBounds(gfx::Size(140, 300)); - squash2->SetPosition(gfx::PointF(220, 300)); + squash2->test_properties()->position = gfx::PointF(220, 300); squash2->SetDrawsContent(true); scroll->test_properties()->AddChild(std::move(squash2)); @@ -653,7 +653,7 @@ LayerImpl::Create(host_impl_->active_tree(), 6); LayerImpl* child = child_layer.get(); child_layer->SetDrawsContent(true); - child_layer->SetPosition(gfx::PointF(0, 0)); + child_layer->test_properties()->position = gfx::PointF(0, 0); child_layer->SetBounds(gfx::Size(25, 25)); scroll->test_properties()->AddChild(std::move(child_layer)); host_impl_->active_tree()->BuildPropertyTreesForTesting(); @@ -702,7 +702,7 @@ ->property_trees() ->scroll_tree.UpdateScrollOffsetBaseForTesting(overflow->element_id(), gfx::ScrollOffset()); - overflow->SetPosition(gfx::PointF(0, 0)); + overflow->test_properties()->position = gfx::PointF(0, 0); SnapContainerData container_data( ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), @@ -1184,7 +1184,7 @@ LayerImpl::Create(host_impl_->active_tree(), 6); child = child_layer.get(); child_layer->SetDrawsContent(true); - child_layer->SetPosition(gfx::PointF(0, 20)); + child_layer->test_properties()->position = gfx::PointF(0, 20); child_layer->SetBounds(gfx::Size(50, 50)); scroll->test_properties()->AddChild(std::move(child_layer)); host_impl_->active_tree()->BuildPropertyTreesForTesting(); @@ -1267,7 +1267,7 @@ host_impl_->active_tree()->SetDeviceViewportSize(content_size); std::unique_ptr<LayerImpl> root = LayerImpl::Create(layer_tree_impl, 1); root->SetBounds(content_size); - root->SetPosition(gfx::PointF()); + root->test_properties()->position = gfx::PointF(); std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 3); scroll->SetBounds(scroll_content_size); @@ -1279,14 +1279,14 @@ SolidColorScrollbarLayerImpl::Create(layer_tree_impl, 4, VERTICAL, 10, 0, false, true); drawn_scrollbar->SetBounds(scrollbar_size); - drawn_scrollbar->SetPosition(gfx::PointF(345, 0)); + drawn_scrollbar->test_properties()->position = gfx::PointF(345, 0); drawn_scrollbar->SetScrollElementId(scroll->element_id()); drawn_scrollbar->SetDrawsContent(true); drawn_scrollbar->test_properties()->opacity = 1.f; std::unique_ptr<LayerImpl> squash = LayerImpl::Create(layer_tree_impl, 5); squash->SetBounds(gfx::Size(140, 300)); - squash->SetPosition(gfx::PointF(220, 0)); + squash->test_properties()->position = gfx::PointF(220, 0); squash->SetDrawsContent(true); scroll->test_properties()->AddChild(std::move(drawn_scrollbar)); @@ -1376,7 +1376,7 @@ LayerImpl* outer_scroll = host_impl_->OuterViewportScrollLayer(); outer_scroll->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 50)); - outer_scroll->SetPosition(gfx::PointF(-25.f, 0.f)); + outer_scroll->test_properties()->position = gfx::PointF(-25.f, 0.f); outer_scroll->SetDrawsContent(true); host_impl_->active_tree()->BuildPropertyTreesForTesting(); @@ -1718,7 +1718,7 @@ ->property_trees() ->scroll_tree.UpdateScrollOffsetBaseForTesting(overflow->element_id(), gfx::ScrollOffset()); - overflow->SetPosition(gfx::PointF(40, 40)); + overflow->test_properties()->position = gfx::PointF(40, 40); host_impl_->active_tree()->BuildPropertyTreesForTesting(); scroll_layer->SetCurrentScrollOffset(gfx::ScrollOffset(30, 30)); @@ -1887,7 +1887,7 @@ ->property_trees() ->scroll_tree.UpdateScrollOffsetBaseForTesting(overflow->element_id(), gfx::ScrollOffset()); - overflow->SetPosition(gfx::PointF()); + overflow->test_properties()->position = gfx::PointF(); host_impl_->active_tree()->BuildPropertyTreesForTesting(); DrawFrame(); @@ -2687,7 +2687,7 @@ std::unique_ptr<LayerImpl> scroll_child = CreateScrollableLayer(9, gfx::Size(10, 10)); child = scroll_child.get(); - scroll_child->SetPosition(gfx::PointF(20.f, 20.f)); + scroll_child->test_properties()->position = gfx::PointF(20.f, 20.f); scroll_child_clip->test_properties()->AddChild(std::move(scroll_child)); child_clip = scroll_child_clip.get(); @@ -3797,7 +3797,7 @@ host_impl_->pending_tree()->InnerViewportContainerLayer(); scrollbar->SetScrollElementId(scroll->element_id()); scrollbar->SetBounds(gfx::Size(10, 100)); - scrollbar->SetPosition(gfx::PointF(90, 0)); + scrollbar->test_properties()->position = gfx::PointF(90, 0); scrollbar->SetNeedsPushProperties(); container->test_properties()->AddChild(std::move(scrollbar)); @@ -3912,7 +3912,7 @@ touch_action_region.Union(kTouchActionNone, gfx::Rect(scrollbar_size_1)); scrollbar_1->SetTouchActionRegion(touch_action_region); scrollbar_1->SetCurrentPos(0); - scrollbar_1->SetPosition(gfx::PointF(0, 0)); + scrollbar_1->test_properties()->position = gfx::PointF(0, 0); host_impl_->active_tree() ->InnerViewportContainerLayer() ->test_properties() @@ -3926,7 +3926,7 @@ scrollbar_2_ = scrollbar_2.get(); std::unique_ptr<LayerImpl> child = LayerImpl::Create(host_impl_->active_tree(), child_scroll_id); - child->SetPosition(gfx::PointF(50, 50)); + child->test_properties()->position = gfx::PointF(50, 50); child->SetBounds(child_layer_size); child->SetDrawsContent(true); child->SetScrollable(gfx::Size(100, 100)); @@ -3937,7 +3937,7 @@ scrollbar_2->SetDrawsContent(true); scrollbar_2->SetBounds(scrollbar_size_2); scrollbar_2->SetCurrentPos(0); - scrollbar_2->SetPosition(gfx::PointF(0, 0)); + scrollbar_2->test_properties()->position = gfx::PointF(0, 0); child->test_properties()->AddChild(std::move(scrollbar_2)); root_scroll->test_properties()->AddChild(std::move(child)); @@ -4089,7 +4089,7 @@ host_impl_->pending_tree()->InnerViewportContainerLayer(); scrollbar->SetScrollElementId(scroll->element_id()); scrollbar->SetBounds(gfx::Size(10, 100)); - scrollbar->SetPosition(gfx::PointF(90, 0)); + scrollbar->test_properties()->position = gfx::PointF(90, 0); scrollbar->SetNeedsPushProperties(); container->test_properties()->AddChild(std::move(scrollbar)); @@ -4330,7 +4330,7 @@ vert_scrollbar->SetScrollElementId(root_scroll->element_id()); vert_scrollbar->SetBounds(gfx::Size(10, 200)); - vert_scrollbar->SetPosition(gfx::PointF(300, 0)); + vert_scrollbar->test_properties()->position = gfx::PointF(300, 0); vert_scrollbar->test_properties()->opacity_can_animate = true; vert_scrollbar->SetCurrentPos(0); @@ -4488,7 +4488,7 @@ for (size_t i = 0; i < primary_surfaces.size(); ++i) { std::unique_ptr<SurfaceLayerImpl> child = SurfaceLayerImpl::Create(host_impl_->active_tree(), i + 6); - child->SetPosition(gfx::PointF(25.f * i, 0.f)); + child->test_properties()->position = gfx::PointF(25.f * i, 0.f); child->SetBounds(gfx::Size(1, 1)); child->SetDrawsContent(true); child->SetRange( @@ -4930,7 +4930,7 @@ auto* layer = static_cast<DidDrawCheckLayer*>(root->test_properties()->children[0]); // Ensure visible_layer_rect for layer is empty. - layer->SetPosition(gfx::PointF(100.f, 100.f)); + layer->test_properties()->position = gfx::PointF(100.f, 100.f); layer->SetBounds(gfx::Size(10, 10)); host_impl_->active_tree()->BuildPropertyTreesForTesting(); @@ -4949,7 +4949,7 @@ EXPECT_TRUE(layer->visible_layer_rect().IsEmpty()); // Ensure visible_layer_rect for layer is not empty - layer->SetPosition(gfx::PointF()); + layer->test_properties()->position = gfx::PointF(); layer->NoteLayerPropertyChanged(); host_impl_->active_tree()->BuildPropertyTreesForTesting(); @@ -5548,7 +5548,7 @@ touch_action_region.Union(kTouchActionNone, gfx::Rect(scrollbar_size)); scrollbar->SetTouchActionRegion(touch_action_region); scrollbar->SetCurrentPos(0); - scrollbar->SetPosition(gfx::PointF(0, 35)); + scrollbar->test_properties()->position = gfx::PointF(0, 35); host_impl_->active_tree() ->InnerViewportContainerLayer() ->test_properties() @@ -5859,7 +5859,7 @@ child->SetScrollable(sub_content_layer_size); child->SetElementId(LayerIdToElementIdForTesting(child->id())); child->SetBounds(sub_content_size); - child->SetPosition(gfx::PointF()); + child->test_properties()->position = gfx::PointF(); child->SetDrawsContent(true); child->test_properties()->is_container_for_fixed_position_layers = true; @@ -6402,7 +6402,7 @@ std::unique_ptr<LayerImpl> content_layer = LayerImpl::Create(host_impl_->active_tree(), 11); content_layer->SetDrawsContent(true); - content_layer->SetPosition(gfx::PointF()); + content_layer->test_properties()->position = gfx::PointF(); content_layer->SetBounds(contents_size); LayerImpl* scroll_container_layer = @@ -6413,7 +6413,7 @@ scroll_layer->SetScrollable(surface_size); scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id())); scroll_layer->SetBounds(contents_size); - scroll_layer->SetPosition(gfx::PointF()); + scroll_layer->test_properties()->position = gfx::PointF(); scroll_layer->test_properties()->AddChild(std::move(content_layer)); scroll_container_layer->test_properties()->AddChild(std::move(scroll_layer)); @@ -8198,8 +8198,10 @@ inner_scroll_layer->test_properties()->AddChild(std::move(scroll)); // Move the outer viewport layer away so that scrolls won't target it. - host_impl_->active_tree()->OuterViewportContainerLayer()->SetPosition( - gfx::PointF(400, 400)); + host_impl_->active_tree() + ->OuterViewportContainerLayer() + ->test_properties() + ->position = gfx::PointF(400, 400); layer_tree_impl->BuildPropertyTreesForTesting(); @@ -8683,7 +8685,7 @@ host_impl_->active_tree(), 2, host_impl_->resource_provider())); auto* layer1 = static_cast<BlendStateCheckLayer*>(root->test_properties()->children[0]); - layer1->SetPosition(gfx::PointF(2.f, 2.f)); + layer1->test_properties()->position = gfx::PointF(2.f, 2.f); TestFrameData frame; @@ -8736,7 +8738,7 @@ host_impl_->active_tree(), 3, host_impl_->resource_provider())); auto* layer2 = static_cast<BlendStateCheckLayer*>( layer1->test_properties()->children[0]); - layer2->SetPosition(gfx::PointF(4.f, 4.f)); + layer2->test_properties()->position = gfx::PointF(4.f, 4.f); // 2 opaque layers, drawn without blending. layer1->SetContentsOpaque(true); @@ -8963,10 +8965,10 @@ EXPECT_TRUE(MayContainVideoBitSetOnFrameData(host_impl_.get())); // Move the video layer so it goes beyond the root. - video_layer->SetPosition(gfx::PointF(100.f, 100.f)); + video_layer->test_properties()->position = gfx::PointF(100.f, 100.f); EXPECT_FALSE(MayContainVideoBitSetOnFrameData(host_impl_.get())); - video_layer->SetPosition(gfx::PointF(0.f, 0.f)); + video_layer->test_properties()->position = gfx::PointF(0.f, 0.f); video_layer->NoteLayerPropertyChanged(); EXPECT_TRUE(MayContainVideoBitSetOnFrameData(host_impl_.get())); } @@ -9010,7 +9012,7 @@ // Expect no gutter rects. void TestLayerCoversFullViewport() { gfx::Rect layer_rect(viewport_size_); - child_->SetPosition(gfx::PointF(layer_rect.origin())); + child_->test_properties()->position = gfx::PointF(layer_rect.origin()); child_->SetBounds(layer_rect.size()); child_->SetQuadRect(gfx::Rect(layer_rect.size())); child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size())); @@ -9031,7 +9033,7 @@ // Expect fullscreen gutter rect. void SetUpEmptylayer() { gfx::Rect layer_rect(0, 0, 0, 0); - child_->SetPosition(gfx::PointF(layer_rect.origin())); + child_->test_properties()->position = gfx::PointF(layer_rect.origin()); child_->SetBounds(layer_rect.size()); child_->SetQuadRect(gfx::Rect(layer_rect.size())); child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size())); @@ -9068,7 +9070,7 @@ // Expect four surrounding gutter rects. void SetUpLayerInMiddleOfViewport() { gfx::Rect layer_rect(500, 500, 200, 200); - child_->SetPosition(gfx::PointF(layer_rect.origin())); + child_->test_properties()->position = gfx::PointF(layer_rect.origin()); child_->SetBounds(layer_rect.size()); child_->SetQuadRect(gfx::Rect(layer_rect.size())); child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size())); @@ -9106,7 +9108,7 @@ void SetUpLayerIsLargerThanViewport() { gfx::Rect layer_rect(viewport_size_.width() + 10, viewport_size_.height() + 10); - child_->SetPosition(gfx::PointF(layer_rect.origin())); + child_->test_properties()->position = gfx::PointF(layer_rect.origin()); child_->SetBounds(layer_rect.size()); child_->SetQuadRect(gfx::Rect(layer_rect.size())); child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size())); @@ -9320,7 +9322,7 @@ root->test_properties()->force_render_surface = true; std::unique_ptr<LayerImpl> child = FakeDrawableLayerImpl::Create(layer_tree_host_impl->active_tree(), 2); - child->SetPosition(gfx::PointF(12.f, 13.f)); + child->test_properties()->position = gfx::PointF(12.f, 13.f); child->SetBounds(gfx::Size(14, 15)); child->SetDrawsContent(true); root->SetBounds(gfx::Size(500, 500)); @@ -9349,7 +9351,8 @@ ->root_layer_for_testing() ->test_properties() ->children[0] - ->SetPosition(gfx::PointF()); + ->test_properties() + ->position = gfx::PointF(); layer_tree_host_impl->active_tree() ->root_layer_for_testing() ->test_properties() @@ -9566,7 +9569,7 @@ TEST_F(LayerTreeHostImplTestDrawAndTestDamage, FrameIncludesDamageRect) { std::unique_ptr<SolidColorLayerImpl> root = SolidColorLayerImpl::Create(host_impl_->active_tree(), 1); - root->SetPosition(gfx::PointF()); + root->test_properties()->position = gfx::PointF(); root->SetBounds(gfx::Size(10, 10)); root->SetDrawsContent(true); root->SetBackgroundColor(SK_ColorRED); @@ -9575,7 +9578,7 @@ // Child layer is in the bottom right corner. std::unique_ptr<SolidColorLayerImpl> child = SolidColorLayerImpl::Create(host_impl_->active_tree(), 2); - child->SetPosition(gfx::PointF(9.f, 9.f)); + child->test_properties()->position = gfx::PointF(9.f, 9.f); child->SetBounds(gfx::Size(1, 1)); child->SetDrawsContent(true); child->SetBackgroundColor(SK_ColorRED); @@ -10158,7 +10161,7 @@ LayerImpl::Create(host_impl_->active_tree(), occluder_layer_id); occluder_layer->SetDrawsContent(true); occluder_layer->SetBounds(content_size); - occluder_layer->SetPosition(gfx::PointF()); + occluder_layer->test_properties()->position = gfx::PointF(); // The parent of the occluder is *above* the scroller. page_scale_layer->test_properties()->AddChild(std::move(occluder_layer)); @@ -10190,7 +10193,7 @@ LayerImpl::Create(host_impl_->active_tree(), occluder_layer_id); occluder_layer->SetDrawsContent(true); occluder_layer->SetBounds(content_size); - occluder_layer->SetPosition(gfx::PointF(-10.f, -10.f)); + occluder_layer->test_properties()->position = gfx::PointF(-10.f, -10.f); int child_scroll_clip_layer_id = 7; std::unique_ptr<LayerImpl> child_scroll_clip = @@ -10200,7 +10203,7 @@ std::unique_ptr<LayerImpl> child_scroll = CreateScrollableLayer(child_scroll_layer_id, content_size); - child_scroll->SetPosition(gfx::PointF(10.f, 10.f)); + child_scroll->test_properties()->position = gfx::PointF(10.f, 10.f); child_scroll->test_properties()->AddChild(std::move(occluder_layer)); child_scroll_clip->test_properties()->AddChild(std::move(child_scroll)); @@ -10256,7 +10259,7 @@ std::unique_ptr<SolidColorLayerImpl> root = SolidColorLayerImpl::Create(host_impl_->active_tree(), 1); - root->SetPosition(gfx::PointF()); + root->test_properties()->position = gfx::PointF(); root->SetBounds(gfx::Size(10, 10)); root->SetDrawsContent(true); root->test_properties()->force_render_surface = true; @@ -10381,7 +10384,7 @@ int root_layer_id = 1; std::unique_ptr<SolidColorLayerImpl> root = SolidColorLayerImpl::Create(host_impl_->active_tree(), root_layer_id); - root->SetPosition(gfx::PointF()); + root->test_properties()->position = gfx::PointF(); root->SetBounds(gfx::Size(10, 10)); root->SetDrawsContent(true); root->test_properties()->force_render_surface = true; @@ -10429,7 +10432,7 @@ int root_layer_id = 1; std::unique_ptr<SolidColorLayerImpl> root = SolidColorLayerImpl::Create(host_impl_->active_tree(), root_layer_id); - root->SetPosition(gfx::PointF()); + root->test_properties()->position = gfx::PointF(); root->SetBounds(gfx::Size(10, 10)); root->SetDrawsContent(true); root->test_properties()->force_render_surface = true; @@ -11052,7 +11055,7 @@ { std::unique_ptr<LayerImpl> clip = LayerImpl::Create(layer_tree_impl, 10); clip->SetBounds(root_layer_size); - clip->SetPosition(gfx::PointF()); + clip->test_properties()->position = gfx::PointF(); std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 11); scroll->SetBounds(scroll_content_size); @@ -11128,7 +11131,7 @@ inner_scroll->SetElementId( LayerIdToElementIdForTesting(inner_scroll->id())); inner_scroll->SetBounds(outer_viewport); - inner_scroll->SetPosition(gfx::PointF()); + inner_scroll->test_properties()->position = gfx::PointF(); std::unique_ptr<LayerImpl> outer_clip = LayerImpl::Create(layer_tree_impl, kOuterViewportClipLayerId); @@ -11146,12 +11149,12 @@ ->scroll_tree.UpdateScrollOffsetBaseForTesting( outer_scroll->element_id(), gfx::ScrollOffset()); outer_scroll->SetBounds(content_size); - outer_scroll->SetPosition(gfx::PointF()); + outer_scroll->test_properties()->position = gfx::PointF(); std::unique_ptr<LayerImpl> contents = LayerImpl::Create(layer_tree_impl, 8); contents->SetDrawsContent(true); contents->SetBounds(content_size); - contents->SetPosition(gfx::PointF()); + contents->test_properties()->position = gfx::PointF(); outer_scroll->test_properties()->AddChild(std::move(contents)); outer_clip->test_properties()->AddChild(std::move(outer_scroll)); @@ -13243,7 +13246,7 @@ touch_action_region.Union(kTouchActionNone, gfx::Rect(scrollbar_size_1)); scrollbar_1->SetTouchActionRegion(touch_action_region); scrollbar_1->SetCurrentPos(0); - scrollbar_1->SetPosition(gfx::PointF(0, 0)); + scrollbar_1->test_properties()->position = gfx::PointF(0, 0); host_impl_->active_tree() ->InnerViewportContainerLayer() ->test_properties() @@ -13334,7 +13337,7 @@ true, true); std::unique_ptr<LayerImpl> child = LayerImpl::Create(host_impl_->active_tree(), child_scroll_id); - child->SetPosition(gfx::PointF(50, 50)); + child->test_properties()->position = gfx::PointF(50, 50); child->SetBounds(child_layer_size); child->SetDrawsContent(true); child->SetScrollable(gfx::Size(100, 100)); @@ -13350,7 +13353,7 @@ scrollbar_2->SetDrawsContent(true); scrollbar_2->SetBounds(scrollbar_size_2); scrollbar_2->SetCurrentPos(0); - scrollbar_2->SetPosition(gfx::PointF(0, 0)); + scrollbar_2->test_properties()->position = gfx::PointF(0, 0); child->test_properties()->AddChild(std::move(scrollbar_2)); root_scroll->test_properties()->AddChild(std::move(child)); @@ -13807,7 +13810,7 @@ const int root_layer_id = 1; std::unique_ptr<SolidColorLayerImpl> root = SolidColorLayerImpl::Create(host_impl_->active_tree(), root_layer_id); - root->SetPosition(gfx::PointF()); + root->test_properties()->position = gfx::PointF(); root->SetBounds(gfx::Size(10, 10)); root->SetDrawsContent(true); root->test_properties()->force_render_surface = true; @@ -13944,7 +13947,7 @@ std::unique_ptr<SurfaceLayerImpl> surface_child = SurfaceLayerImpl::Create(host_impl_->active_tree(), 3); - surface_child->SetPosition(gfx::PointF(50, 50)); + surface_child->test_properties()->position = gfx::PointF(50, 50); surface_child->SetBounds(gfx::Size(100, 100)); surface_child->SetDrawsContent(true); surface_child->SetSurfaceHitTestable(true); @@ -13983,10 +13986,10 @@ host_impl_->active_tree()->SetDeviceViewportSize(gfx::Size(1024, 768)); - intermediate_layer->SetPosition(gfx::PointF(200, 300)); + intermediate_layer->test_properties()->position = gfx::PointF(200, 300); intermediate_layer->SetBounds(gfx::Size(200, 200)); - surface_child1->SetPosition(gfx::PointF(50, 50)); + surface_child1->test_properties()->position = gfx::PointF(50, 50); surface_child1->SetBounds(gfx::Size(100, 100)); gfx::Transform rotate; rotate.Rotate(45); @@ -13994,12 +13997,12 @@ surface_child1->SetDrawsContent(true); surface_child1->SetSurfaceHitTestable(true); - surface_child2->SetPosition(gfx::PointF(450, 300)); + surface_child2->test_properties()->position = gfx::PointF(450, 300); surface_child2->SetBounds(gfx::Size(100, 100)); surface_child2->SetDrawsContent(true); surface_child2->SetSurfaceHitTestable(true); - overlapping_layer->SetPosition(gfx::PointF(500, 350)); + overlapping_layer->test_properties()->position = gfx::PointF(500, 350); overlapping_layer->SetBounds(gfx::Size(200, 200)); overlapping_layer->SetDrawsContent(true);
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index cde6927..aedef8053 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc
@@ -3412,10 +3412,6 @@ FakePictureLayerImpl* child = static_cast<FakePictureLayerImpl*>( impl->active_tree()->LayerById(child_layer_->id())); - // Positions remain in layout pixels. - EXPECT_EQ(gfx::PointF(), root->position()); - EXPECT_EQ(gfx::PointF(2.f, 2.f), child->position()); - // Compute all the layer transforms for the frame. LayerTreeHostImpl::FrameData frame_data; impl->PrepareToDraw(&frame_data);
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc index cef1be49..5ec2bfc 100644 --- a/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -695,9 +695,9 @@ case 0: { // GESTURE scroll on impl thread. Also tests that the last scrolled // layer id is stored even after the scrolling ends. - gfx::Point scroll_point = - gfx::ToCeiledPoint(expected_scroll_layer_impl->position() - - gfx::Vector2dF(0.5f, 0.5f)); + gfx::Point scroll_point = gfx::ToCeiledPoint( + expected_scroll_layer_impl->test_properties()->position - + gfx::Vector2dF(0.5f, 0.5f)); InputHandler::ScrollStatus status = impl->ScrollBegin( BeginState(scroll_point).get(), InputHandler::TOUCHSCREEN); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); @@ -720,9 +720,9 @@ } case 1: { // WHEEL scroll on impl thread. - gfx::Point scroll_point = - gfx::ToCeiledPoint(expected_scroll_layer_impl->position() + - gfx::Vector2dF(0.5f, 0.5f)); + gfx::Point scroll_point = gfx::ToCeiledPoint( + expected_scroll_layer_impl->test_properties()->position + + gfx::Vector2dF(0.5f, 0.5f)); InputHandler::ScrollStatus status = impl->ScrollBegin( BeginState(scroll_point).get(), InputHandler::WHEEL); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc index 2cea29e7..83bfdb9 100644 --- a/cc/trees/layer_tree_impl_unittest.cc +++ b/cc/trees/layer_tree_impl_unittest.cc
@@ -291,7 +291,7 @@ { std::unique_ptr<LayerImpl> test_layer = LayerImpl::Create(host_impl().active_tree(), 12345); - test_layer->SetPosition(gfx::PointF(50.f, 50.f)); + test_layer->test_properties()->position = gfx::PointF(50.f, 50.f); test_layer->SetBounds(gfx::Size(100, 100)); test_layer->SetDrawsContent(true); root_layer()->test_properties()->AddChild(std::move(test_layer)); @@ -525,13 +525,13 @@ LayerImpl::Create(host_impl().active_tree(), 123); // this layer is positioned, and hit testing should correctly know where the // layer is located. - clipping_layer->SetPosition(gfx::PointF(25.f, 25.f)); + clipping_layer->test_properties()->position = gfx::PointF(25.f, 25.f); clipping_layer->SetBounds(gfx::Size(50, 50)); clipping_layer->SetMasksToBounds(true); std::unique_ptr<LayerImpl> child = LayerImpl::Create(host_impl().active_tree(), 456); - child->SetPosition(gfx::PointF(-50.f, -50.f)); + child->test_properties()->position = gfx::PointF(-50.f, -50.f); child->SetBounds(gfx::Size(300, 300)); child->SetDrawsContent(true); clipping_layer->test_properties()->AddChild(std::move(child)); @@ -602,7 +602,7 @@ std::unique_ptr<LayerImpl> rotated_leaf = LayerImpl::Create(host_impl().active_tree(), 2468); - child->SetPosition(gfx::PointF(10.f, 10.f)); + child->test_properties()->position = gfx::PointF(10.f, 10.f); child->SetBounds(gfx::Size(80, 80)); child->SetMasksToBounds(true); @@ -696,7 +696,7 @@ LayerImpl::Create(host_impl().active_tree(), 123); // this layer is positioned, and hit testing should correctly know where the // layer is located. - intermediate_layer->SetPosition(gfx::PointF(10.f, 10.f)); + intermediate_layer->test_properties()->position = gfx::PointF(10.f, 10.f); intermediate_layer->SetBounds(gfx::Size(50, 50)); // Sanity check the intermediate layer should not clip. ASSERT_FALSE(intermediate_layer->masks_to_bounds()); @@ -707,7 +707,8 @@ // would not be able to hit it successfully. std::unique_ptr<LayerImpl> child = LayerImpl::Create(host_impl().active_tree(), 456); - child->SetPosition(gfx::PointF(60.f, 60.f)); // 70, 70 in screen space + child->test_properties()->position = + gfx::PointF(60.f, 60.f); // 70, 70 in screen spae child->SetBounds(gfx::Size(20, 20)); child->SetDrawsContent(true); intermediate_layer->test_properties()->AddChild(std::move(child)); @@ -766,18 +767,18 @@ std::unique_ptr<LayerImpl> grand_child1 = LayerImpl::Create(host_impl().active_tree(), 4); - child1->SetPosition(gfx::PointF(10.f, 10.f)); + child1->test_properties()->position = gfx::PointF(10.f, 10.f); child1->SetBounds(gfx::Size(50, 50)); child1->SetDrawsContent(true); - child2->SetPosition(gfx::PointF(50.f, 10.f)); + child2->test_properties()->position = gfx::PointF(50.f, 10.f); child2->SetBounds(gfx::Size(50, 50)); child2->SetDrawsContent(true); // Remember that grand_child is positioned with respect to its parent (i.e. // child1). In screen space, the intended position is (10, 50), with size // 100 x 50. - grand_child1->SetPosition(gfx::PointF(0.f, 40.f)); + grand_child1->test_properties()->position = gfx::PointF(0.f, 40.f); grand_child1->SetBounds(gfx::Size(100, 50)); grand_child1->SetDrawsContent(true); @@ -912,13 +913,13 @@ std::unique_ptr<LayerImpl> grand_child1 = LayerImpl::Create(host_impl().active_tree(), 4); - child1->SetPosition(gfx::PointF(10.f, 10.f)); + child1->test_properties()->position = gfx::PointF(10.f, 10.f); child1->SetBounds(gfx::Size(50, 50)); child1->SetDrawsContent(true); child1->test_properties()->should_flatten_transform = false; child1->test_properties()->sorting_context_id = 1; - child2->SetPosition(gfx::PointF(50.f, 10.f)); + child2->test_properties()->position = gfx::PointF(50.f, 10.f); child2->SetBounds(gfx::Size(50, 50)); gfx::Transform translate_z; translate_z.Translate3d(0, 0, 10.f); @@ -930,7 +931,7 @@ // Remember that grand_child is positioned with respect to its parent (i.e. // child1). In screen space, the intended position is (10, 50), with size // 100 x 50. - grand_child1->SetPosition(gfx::PointF(0.f, 40.f)); + grand_child1->test_properties()->position = gfx::PointF(0.f, 40.f); grand_child1->SetBounds(gfx::Size(100, 50)); grand_child1->SetDrawsContent(true); grand_child1->test_properties()->should_flatten_transform = false; @@ -1012,12 +1013,12 @@ std::unique_ptr<LayerImpl> grand_child = LayerImpl::Create(host_impl().active_tree(), 4); - child->SetPosition(gfx::PointF(10.f, 10.f)); + child->test_properties()->position = gfx::PointF(10.f, 10.f); child->SetBounds(gfx::Size(1, 1)); child->SetDrawsContent(true); child->SetMasksToBounds(true); - grand_child->SetPosition(gfx::PointF(0.f, 40.f)); + grand_child->test_properties()->position = gfx::PointF(0.f, 40.f); grand_child->SetBounds(gfx::Size(100, 50)); grand_child->SetDrawsContent(true); grand_child->test_properties()->force_render_surface = true; @@ -1055,7 +1056,7 @@ std::unique_ptr<LayerImpl> grand_child = LayerImpl::Create(host_impl().active_tree(), 4); - child->SetPosition(gfx::PointF(10.f, 10.f)); + child->test_properties()->position = gfx::PointF(10.f, 10.f); child->SetBounds(gfx::Size(1, 1)); child->SetDrawsContent(true); child->SetMasksToBounds(true); @@ -1109,12 +1110,12 @@ std::unique_ptr<LayerImpl> grand_child1 = LayerImpl::Create(host_impl().active_tree(), 4); - child1->SetPosition(gfx::PointF(10.f, 10.f)); + child1->test_properties()->position = gfx::PointF(10.f, 10.f); child1->SetBounds(gfx::Size(50, 50)); child1->SetDrawsContent(true); child1->test_properties()->force_render_surface = true; - child2->SetPosition(gfx::PointF(50.f, 10.f)); + child2->test_properties()->position = gfx::PointF(50.f, 10.f); child2->SetBounds(gfx::Size(50, 50)); child2->SetDrawsContent(true); child2->test_properties()->force_render_surface = true; @@ -1122,7 +1123,7 @@ // Remember that grand_child is positioned with respect to its parent (i.e. // child1). In screen space, the intended position is (10, 50), with size // 100 x 50. - grand_child1->SetPosition(gfx::PointF(0.f, 40.f)); + grand_child1->test_properties()->position = gfx::PointF(0.f, 40.f); grand_child1->SetBounds(gfx::Size(100, 50)); grand_child1->SetDrawsContent(true); grand_child1->test_properties()->force_render_surface = true; @@ -1360,7 +1361,7 @@ { std::unique_ptr<LayerImpl> test_layer = LayerImpl::Create(host_impl().active_tree(), 12345); - test_layer->SetPosition(gfx::PointF(50.f, 50.f)); + test_layer->test_properties()->position = gfx::PointF(50.f, 50.f); test_layer->SetBounds(gfx::Size(100, 100)); test_layer->SetDrawsContent(true); test_layer->SetTouchActionRegion(touch_action_region); @@ -1429,7 +1430,7 @@ touch_action_region.Union(kTouchActionNone, gfx::Rect(10, 10, 30, 30)); std::unique_ptr<LayerImpl> test_layer = LayerImpl::Create(host_impl().active_tree(), 12345); - test_layer->SetPosition(gfx::PointF(25.f, 25.f)); + test_layer->test_properties()->position = gfx::PointF(25.f, 25.f); test_layer->SetBounds(gfx::Size(50, 50)); test_layer->SetDrawsContent(true); test_layer->SetTouchActionRegion(touch_action_region); @@ -1565,7 +1566,7 @@ LayerImpl::Create(host_impl().active_tree(), 123); // this layer is positioned, and hit testing should correctly know where the // layer is located. - clipping_layer->SetPosition(gfx::PointF(25.f, 25.f)); + clipping_layer->test_properties()->position = gfx::PointF(25.f, 25.f); clipping_layer->SetBounds(gfx::Size(50, 50)); clipping_layer->SetMasksToBounds(true); @@ -1574,7 +1575,7 @@ std::unique_ptr<LayerImpl> child = LayerImpl::Create(host_impl().active_tree(), 456); - child->SetPosition(gfx::PointF(-50.f, -50.f)); + child->test_properties()->position = gfx::PointF(-50.f, -50.f); child->SetBounds(gfx::Size(300, 300)); child->SetDrawsContent(true); child->SetTouchActionRegion(touch_action_region); @@ -1649,7 +1650,7 @@ LayerImpl::Create(host_impl().active_tree(), 123); // This layer is positioned, and hit testing should correctly know where the // layer is located. - clipping_layer->SetPosition(gfx::PointF(25.f, 20.f)); + clipping_layer->test_properties()->position = gfx::PointF(25.f, 20.f); clipping_layer->SetBounds(gfx::Size(50, 50)); clipping_layer->SetMasksToBounds(true); @@ -1658,7 +1659,7 @@ std::unique_ptr<LayerImpl> child = LayerImpl::Create(host_impl().active_tree(), 456); - child->SetPosition(gfx::PointF(-50.f, -50.f)); + child->test_properties()->position = gfx::PointF(-50.f, -50.f); child->SetBounds(gfx::Size(300, 300)); child->SetDrawsContent(true); child->SetTouchActionRegion(touch_action_region); @@ -1732,7 +1733,7 @@ LayerImpl::Create(host_impl().active_tree(), 1234); // this layer is positioned, and hit testing should correctly know where the // layer is located. - notouch_layer->SetPosition(gfx::PointF(0, 25)); + notouch_layer->test_properties()->position = gfx::PointF(0, 25); notouch_layer->SetBounds(gfx::Size(50, 50)); notouch_layer->SetDrawsContent(true); root->test_properties()->AddChild(std::move(notouch_layer)); @@ -1812,7 +1813,7 @@ // hit testing (becuase the point is inside test_layer with respect to the old // screen space transform). gfx::PointF test_point(24.f, 24.f); - test_layer->SetPosition(gfx::PointF(25.f, 25.f)); + test_layer->test_properties()->position = gfx::PointF(25.f, 25.f); gfx::Transform expected_screen_space_transform; expected_screen_space_transform.Translate(25.f, 25.f); @@ -1832,7 +1833,7 @@ // We change the position of the test layer such that the test point is now // inside the test_layer. test_layer = root->test_properties()->children[0]; - test_layer->SetPosition(gfx::PointF(10.f, 10.f)); + test_layer->test_properties()->position = gfx::PointF(10.f, 10.f); test_layer->NoteLayerPropertyChanged(); expected_screen_space_transform.MakeIdentity(); expected_screen_space_transform.Translate(10.f, 10.f); @@ -1926,7 +1927,8 @@ std::unique_ptr<LayerImpl> clipping_layer = LayerImpl::Create(host_impl().active_tree(), clip_layer_id); // The clipping layer should occlude the right selection bound. - clipping_layer->SetPosition(gfx::PointF() + clipping_offset); + clipping_layer->test_properties()->position = + gfx::PointF() + clipping_offset; clipping_layer->SetBounds(gfx::Size(50, 50)); clipping_layer->SetMasksToBounds(true); @@ -2009,7 +2011,7 @@ { std::unique_ptr<LayerImpl> sub_layer = LayerImpl::Create(host_impl().active_tree(), sub_layer_id); - sub_layer->SetPosition(gfx::PointF() + sub_layer_offset); + sub_layer->test_properties()->position = gfx::PointF() + sub_layer_offset; sub_layer->SetBounds(gfx::Size(50, 50)); sub_layer->SetDrawsContent(true); root->test_properties()->AddChild(std::move(sub_layer)); @@ -2087,7 +2089,7 @@ { std::unique_ptr<LayerImpl> sub_layer = LayerImpl::Create(host_impl().active_tree(), sub_layer_id); - sub_layer->SetPosition(gfx::PointF() + sub_layer_offset); + sub_layer->test_properties()->position = gfx::PointF() + sub_layer_offset; sub_layer->SetBounds(gfx::Size(50, 50)); sub_layer->SetDrawsContent(true); root->test_properties()->AddChild(std::move(sub_layer));
diff --git a/cc/trees/occlusion_tracker_unittest.cc b/cc/trees/occlusion_tracker_unittest.cc index 0ba135b2..aca25d8 100644 --- a/cc/trees/occlusion_tracker_unittest.cc +++ b/cc/trees/occlusion_tracker_unittest.cc
@@ -287,7 +287,7 @@ const gfx::PointF& position, const gfx::Size& bounds) { layer->test_properties()->transform = transform; - layer->SetPosition(position); + layer->test_properties()->position = position; layer->SetBounds(bounds); }
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc index 674f77f..d0ab4c7 100644 --- a/cc/trees/property_tree_builder.cc +++ b/cc/trees/property_tree_builder.cc
@@ -202,6 +202,14 @@ return layer->test_properties()->transform; } +static const gfx::PointF& Position(Layer* layer) { + return layer->position(); +} + +static const gfx::PointF& Position(LayerImpl* layer) { + return layer->test_properties()->position; +} + // Methods to query state from the AnimationHost ---------------------- template <typename LayerType> bool OpacityIsAnimating(const MutatorHost& host, LayerType* layer) { @@ -472,8 +480,8 @@ if (!requires_node) { data_for_children->should_flatten |= ShouldFlattenTransform(layer); - gfx::Vector2dF local_offset = layer->position().OffsetFromOrigin() + - Transform(layer).To2dTranslation(); + gfx::Vector2dF local_offset = + Position(layer).OffsetFromOrigin() + Transform(layer).To2dTranslation(); gfx::Vector2dF source_to_parent; if (source_index != parent_index) { gfx::Transform to_parent; @@ -539,14 +547,13 @@ is_page_scale_layer ? page_scale_factor_ : 1.f; // SetRootTransformsAndScales will be incorrect if the root layer has // non-zero position, so ensure it is zero. - DCHECK(layer->position().IsOrigin()); + DCHECK(Position(layer).IsOrigin()); transform_tree_.SetRootTransformsAndScales( transform_tree_.device_scale_factor(), page_scale_factor_for_root, device_transform_); } else { node->source_offset = source_offset; - node->update_post_local_transform(layer->position(), - TransformOrigin(layer)); + node->update_post_local_transform(Position(layer), TransformOrigin(layer)); } if (is_overscroll_elasticity_layer) { @@ -1368,7 +1375,7 @@ page_scale_is_root_layer ? page_scale_factor_ : 1.f; // SetRootTransformsAndScales will be incorrect if the root layer has // non-zero position, so ensure it is zero. - DCHECK(root_layer_->position().IsOrigin()); + DCHECK(Position(root_layer_).IsOrigin()); transform_tree_.SetRootTransformsAndScales( device_scale_factor, page_scale_factor_for_root, device_transform_); return;
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml index 7c62de1..ba25f8f 100644 --- a/chrome/android/java/AndroidManifest.xml +++ b/chrome/android/java/AndroidManifest.xml
@@ -699,7 +699,7 @@ </activity-alias> {% endfor %} <!-- Activities for WebAPKs. --> - <activity android:name="org.chromium.chrome.browser.webapps.TransparentSplashWebApkActivity" + <activity android:name="org.chromium.chrome.browser.webapps.SameTaskWebApkActivity" android:theme="@style/WebappTheme" android:label="@string/webapp_activity_title" android:exported="false"
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml index 6599f5a..24cc8c9 100644 --- a/chrome/android/java/res/values-v17/styles.xml +++ b/chrome/android/java/res/values-v17/styles.xml
@@ -102,6 +102,9 @@ <style name="WebappTheme" parent="MainThemeBase"> <item name="android:windowBackground">@null</item> <item name="android:windowDisablePreview">true</item> + <!-- Translucency is necessary for a jank-free transition when launching both + WebApkActivity and a splash screen activity with Intent.FLAG_ACTIVITY_NO_ANIMATION. --> + <item name="android:windowIsTranslucent">true</item> </style> <style name="AlertDialogTheme" parent="Theme.AppCompat.Light.Dialog.Alert">
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java index 3550fb9b..26aa391 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java
@@ -221,6 +221,14 @@ public static final String ALLOW_INCOGNITO_CUSTOM_TABS_FROM_THIRD_PARTY = "allow-incognito-custom-tabs-from-third-party"; + /** Prevents use of first-party Google Play Services. */ + public static final String DISABLE_FIRST_PARTY_GOOGLE_PLAY_SERVICES_FOR_TESTING = + "disable-first-party-google-play-services-for-testing"; + + /** Prevents use of Google Play Services. */ + public static final String DISABLE_GOOGLE_PLAY_SERVICES_FOR_TESTING = + "disable-google-play-services-for-testing"; + // Prevent instantiation. private ChromeSwitches() {} }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/RepostFormWarningDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/RepostFormWarningDialog.java deleted file mode 100644 index 4cf72f4..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/RepostFormWarningDialog.java +++ /dev/null
@@ -1,153 +0,0 @@ -// Copyright 2013 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; - -import android.annotation.SuppressLint; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.os.Bundle; -import android.support.v7.app.AlertDialog; - -import org.chromium.base.VisibleForTesting; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.tab.EmptyTabObserver; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.browser.tab.TabObserver; -import org.chromium.chrome.browser.vr.VrModuleProvider; -import org.chromium.ui.widget.UiWidgetFactory; - -/** - * Form resubmission warning dialog. Presents the cancel/continue choice and fires one of two - * callbacks accordingly. - */ -public class RepostFormWarningDialog extends DialogFragment { - // Warning dialog currently being shown, stored for testing. - private static Dialog sCurrentDialog; - - private final Tab mTab; - private final TabObserver mTabObserver; - - /** Empty constructor required for DialogFragments. */ - public RepostFormWarningDialog() { - mTab = null; - mTabObserver = null; - } - - /** - * Handles the repost form warning for the given Tab. - * @param tab The tab waiting for confirmation on a repost form warning. - */ - @SuppressLint("ValidFragment") - public RepostFormWarningDialog(Tab tab) { - mTab = tab; - mTabObserver = new EmptyTabObserver() { - @Override - public void onDestroyed(Tab tab) { - dismissAllowingStateLoss(); - } - }; - mTab.addObserver(mTabObserver); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - // If there is savedInstanceState, then the dialog is being recreated by android - // and will lack the necessary callbacks. Dismiss immediately as the tab will - // need to be recreated anyway. - if (savedInstanceState != null) { - dismiss(); - } - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - DialogInterface.OnClickListener negativeButtonListener = (dialog, id) -> { - if (!mTab.isInitialized()) return; - mTab.getWebContents().getNavigationController().cancelPendingReload(); - }; - DialogInterface.OnClickListener positiveButtonListener = (dialog, id) -> { - if (!mTab.isInitialized()) return; - mTab.getWebContents().getNavigationController().continuePendingReload(); - }; - Dialog dialog; - if (VrModuleProvider.getDelegate().isInVr()) { - android.app.AlertDialog alertDialog = - UiWidgetFactory.getInstance().createAlertDialog(getActivity()); - alertDialog.setMessage(alertDialog.getContext().getString(R.string.http_post_warning)); - alertDialog.setCancelable(true); - if (savedInstanceState == null) { - assert mTab != null; - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, - alertDialog.getContext().getString(R.string.cancel), - negativeButtonListener); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, - alertDialog.getContext().getString(R.string.http_post_warning_resend), - positiveButtonListener); - } - dialog = alertDialog; - } else { - AlertDialog.Builder builder = - new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme) - .setMessage(R.string.http_post_warning); - - if (savedInstanceState == null) { - assert mTab != null; - builder.setNegativeButton(R.string.cancel, negativeButtonListener); - builder.setPositiveButton( - R.string.http_post_warning_resend, positiveButtonListener); - } - dialog = builder.create(); - } - setCurrentDialogForTesting(dialog); - - return dialog; - } - - @Override - public void dismiss() { - handleDimissCleanup(); - if (getFragmentManager() == null) return; - super.dismiss(); - } - - @Override - public void dismissAllowingStateLoss() { - handleDimissCleanup(); - if (getFragmentManager() == null) return; - super.dismissAllowingStateLoss(); - } - - @Override - public void onDismiss(DialogInterface dialog) { - super.onDismiss(dialog); - handleDimissCleanup(); - } - - private void handleDimissCleanup() { - setCurrentDialogForTesting(null); - - if (mTab != null && mTabObserver != null) { - mTab.removeObserver(mTabObserver); - } - } - - /** - * Sets the currently displayed dialog in sCurrentDialog. This is required by findbugs, which - * allows static fields only to be set from static methods. - */ - private static void setCurrentDialogForTesting(Dialog dialog) { - sCurrentDialog = dialog; - } - - /** - * @return dialog currently being displayed. - */ - @VisibleForTesting - public static Dialog getCurrentDialogForTesting() { - return sCurrentDialog; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java index aee2fbe..d66be61 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java
@@ -18,6 +18,7 @@ import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; +import org.chromium.base.CommandLine; import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.base.ThreadUtils; @@ -25,6 +26,7 @@ import org.chromium.base.metrics.CachedMetrics.SparseHistogramSample; import org.chromium.base.metrics.CachedMetrics.TimesHistogramSample; import org.chromium.chrome.browser.AppHooks; +import org.chromium.chrome.browser.ChromeSwitches; import java.util.concurrent.TimeUnit; @@ -181,6 +183,11 @@ * @return true if and only if Google Play Services can be used */ public boolean canUseGooglePlayServices(final UserRecoverableErrorHandler errorHandler) { + if (CommandLine.getInstance().hasSwitch( + ChromeSwitches.DISABLE_GOOGLE_PLAY_SERVICES_FOR_TESTING)) { + return false; + } + Context context = ContextUtils.getApplicationContext(); final int resultCode = checkGooglePlayServicesAvailable(context); recordConnectionResult(resultCode); @@ -227,6 +234,10 @@ @WorkerThread public boolean canUseFirstPartyGooglePlayServices( UserRecoverableErrorHandler userRecoverableErrorHandler) { + if (CommandLine.getInstance().hasSwitch( + ChromeSwitches.DISABLE_FIRST_PARTY_GOOGLE_PLAY_SERVICES_FOR_TESTING)) { + return false; + } return canUseGooglePlayServices(userRecoverableErrorHandler) && isChromeGoogleSigned(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java index 12402c2..62541c4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java
@@ -9,6 +9,7 @@ import android.app.ActivityManager; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; import android.graphics.Rect; import android.graphics.RectF; import android.media.AudioManager; @@ -28,7 +29,6 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.AppHooks; import org.chromium.chrome.browser.ChromeActivity; -import org.chromium.chrome.browser.RepostFormWarningDialog; import org.chromium.chrome.browser.SwipeRefreshHandler; import org.chromium.chrome.browser.document.DocumentUtils; import org.chromium.chrome.browser.document.DocumentWebContentsDelegate; @@ -48,6 +48,10 @@ import org.chromium.content_public.browser.InvalidateTypes; import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.common.ResourceRequestBody; +import org.chromium.ui.modaldialog.DialogDismissalCause; +import org.chromium.ui.modaldialog.ModalDialogManager; +import org.chromium.ui.modaldialog.ModalDialogProperties; +import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.mojom.WindowOpenDisposition; /** @@ -220,9 +224,7 @@ SwipeRefreshHandler handler = SwipeRefreshHandler.get(mTab); if (handler != null) handler.reset(); - if (mTab.getActivity() == null) return; - RepostFormWarningDialog warningDialog = new RepostFormWarningDialog(mTab); - warningDialog.show(mTab.getActivity().getFragmentManager(), null); + new RepostFormWarningHelper().show(); } @Override @@ -558,6 +560,72 @@ nativeShowFramebustBlockInfoBar(mTab.getWebContents(), url); } + private class RepostFormWarningHelper extends EmptyTabObserver { + private ModalDialogManager mModalDialogManager; + private PropertyModel mDialogModel; + + void show() { + if (mTab.getActivity() == null) return; + mTab.addObserver(this); + mModalDialogManager = mTab.getActivity().getModalDialogManager(); + + ModalDialogProperties + .Controller dialogController = new ModalDialogProperties.Controller() { + @Override + public void onClick(PropertyModel model, int buttonType) { + if (buttonType == ModalDialogProperties.ButtonType.POSITIVE) { + mModalDialogManager.dismissDialog( + model, DialogDismissalCause.POSITIVE_BUTTON_CLICKED); + } else if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) { + mModalDialogManager.dismissDialog( + model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED); + } + } + + @Override + public void onDismiss(PropertyModel model, int dismissalCause) { + mTab.removeObserver(RepostFormWarningHelper.this); + if (!mTab.isInitialized()) return; + switch (dismissalCause) { + case DialogDismissalCause.POSITIVE_BUTTON_CLICKED: + mTab.getWebContents().getNavigationController().continuePendingReload(); + break; + case DialogDismissalCause.ACTIVITY_DESTROYED: + case DialogDismissalCause.TAB_DESTROYED: + // Intentionally ignored as the tab object is gone. + break; + default: + mTab.getWebContents().getNavigationController().cancelPendingReload(); + break; + } + } + }; + + Resources resources = mTab.getActivity().getResources(); + mDialogModel = new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS) + .with(ModalDialogProperties.CONTROLLER, dialogController) + .with(ModalDialogProperties.TITLE, resources, + R.string.http_post_warning_title) + .with(ModalDialogProperties.MESSAGE, resources, + R.string.http_post_warning) + .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources, + R.string.http_post_warning_resend) + .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources, + R.string.cancel) + .with(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true) + .build(); + + mModalDialogManager.showDialog( + mDialogModel, ModalDialogManager.ModalDialogType.TAB, true); + } + + @Override + public void onDestroyed(Tab tab) { + super.onDestroyed(tab); + mModalDialogManager.dismissDialog(mDialogModel, DialogDismissalCause.TAB_DESTROYED); + } + } + private static native void nativeOnRendererUnresponsive(WebContents webContents); private static native void nativeOnRendererResponsive(WebContents webContents); private static native boolean nativeIsCapturingAudio(WebContents webContents);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/SameTaskWebApkActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/SameTaskWebApkActivity.java new file mode 100644 index 0000000..548d344 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/SameTaskWebApkActivity.java
@@ -0,0 +1,8 @@ +// Copyright 2018 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.webapps; + +/** WebApkActivity variant with documentLaunchMode="none". */ +public class SameTaskWebApkActivity extends WebApkActivity {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/TransparentSplashWebApkActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/TransparentSplashWebApkActivity.java deleted file mode 100644 index 508a729..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/TransparentSplashWebApkActivity.java +++ /dev/null
@@ -1,14 +0,0 @@ -// Copyright 2018 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.webapps; - -/** - * WebApkActivity variant to use when ShellAPK displays a splash screen. - * TransparentSplashWebApkActivity is fully transparent while the page is - * loading, enabling the ShellAPK's splash screen to show from underneath the - * WebApkActivity. Once the page is loaded, the activity becomes opaque hiding - * the splash screen underneath. - */ -public class TransparentSplashWebApkActivity extends WebApkActivity {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java index fee8d9eb..bbaa8be6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
@@ -67,11 +67,10 @@ */ public static FreParams slowGenerateFreParamsIfIntentIsForWebApkActivity(Intent fromIntent) { // Check for intents targeted at WebApkActivity, WebApkActivity0-9 and - // TransparentSplashWebApkActivity. + // SameTaskWebApkActivity. String targetActivityClassName = fromIntent.getComponent().getClassName(); if (!targetActivityClassName.startsWith(WebApkActivity.class.getName()) - && !targetActivityClassName.equals( - TransparentSplashWebApkActivity.class.getName())) { + && !targetActivityClassName.equals(SameTaskWebApkActivity.class.getName())) { return null; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java index 47b6d632..4360c60 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
@@ -12,6 +12,7 @@ import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.support.annotation.IntDef; import android.text.TextUtils; @@ -123,7 +124,7 @@ private @WebApkDistributor int mDistributor; private ShareTarget mShareTarget; private Map<String, String> mIconUrlToMurmur2HashMap; - private boolean mUseTransparentSplash; + private boolean mIsSplashProvidedByWebApk; private ShareData mShareData; @@ -180,12 +181,12 @@ } } } - boolean useTransparentSplash = !IntentUtils.isIntentForNewTaskOrNewDocument(intent) + boolean isSplashProvidedByWebApk = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && IntentUtils.safeGetBooleanExtra( - intent, WebApkConstants.EXTRA_USE_TRANSPARENT_SPLASH, false); + intent, WebApkConstants.EXTRA_SPLASH_PROVIDED_BY_WEBAPK, false); - return create( - webApkPackageName, url, source, forceNavigation, useTransparentSplash, shareData); + return create(webApkPackageName, url, source, forceNavigation, isSplashProvidedByWebApk, + shareData); } private static @WebApkDistributor int getDistributor(Bundle bundle, String packageName) { @@ -291,34 +292,35 @@ /** * Construct a {@link WebApkInfo} instance. * - * @param id ID for the WebAPK. - * @param url URL that the WebAPK should navigate to when launched. - * @param scope Scope for the WebAPK. - * @param primaryIcon Primary icon to show for the WebAPK. - * @param badgeIcon Badge icon to use for notifications. - * @param splashIcon Splash icon to use for the splash screen. - * @param name Name of the WebAPK. - * @param shortName The short name of the WebAPK. - * @param displayMode Display mode of the WebAPK. - * @param orientation Orientation of the WebAPK. - * @param source Source that the WebAPK was launched from. - * @param themeColor The theme color of the WebAPK. - * @param backgroundColor The background color of the WebAPK. - * @param webApkPackageName The package of the WebAPK. - * @param shellApkVersion Version of the code in //chrome/android/webapk/shell_apk. - * @param manifestUrl URL of the Web Manifest. - * @param manifestStartUrl URL that the WebAPK should navigate to when launched from the - * homescreen. Different from the {@link url} parameter if the - * WebAPK is launched from a deep link. - * @param distributor The source from where the WebAPK is installed. - * @param iconUrlToMurmur2HashMap Map of the WebAPK's icon URLs to Murmur2 hashes of the - * icon untransformed bytes. - * @param shareTarget Data about WebAPK's share intent handlers. - * @param forceNavigation Whether the WebAPK should navigate to {@link url} if the - * WebAPK is already open. - * @param useTransparentSplash Whether the WebApkActivity should be fully transparent while - * the page is loading. - * @param shareData Shared information from the share intent. + * @param id ID for the WebAPK. + * @param url URL that the WebAPK should navigate to when launched. + * @param scope Scope for the WebAPK. + * @param primaryIcon Primary icon to show for the WebAPK. + * @param badgeIcon Badge icon to use for notifications. + * @param splashIcon Splash icon to use for the splash screen. + * @param name Name of the WebAPK. + * @param shortName The short name of the WebAPK. + * @param displayMode Display mode of the WebAPK. + * @param orientation Orientation of the WebAPK. + * @param source Source that the WebAPK was launched from. + * @param themeColor The theme color of the WebAPK. + * @param backgroundColor The background color of the WebAPK. + * @param webApkPackageName The package of the WebAPK. + * @param shellApkVersion Version of the code in //chrome/android/webapk/shell_apk. + * @param manifestUrl URL of the Web Manifest. + * @param manifestStartUrl URL that the WebAPK should navigate to when launched from + * the homescreen. Different from the {@link url} parameter if + * the WebAPK is launched from a deep link. + * @param distributor The source from where the WebAPK is installed. + * @param iconUrlToMurmur2HashMap Map of the WebAPK's icon URLs to Murmur2 hashes of the + * icon untransformed bytes. + * @param shareTarget Data about WebAPK's share intent handlers. + * @param forceNavigation Whether the WebAPK should navigate to {@link url} if the + * WebAPK is already open. + * @param isSplashProvidedByWebApk Whether the WebAPK provides a splash screen activity which + * should be launched to hide the web contents while the page is + * loading. + * @param shareData Shared information from the share intent. */ public static WebApkInfo create(String id, String url, String scope, Icon primaryIcon, Icon badgeIcon, Icon splashIcon, String name, String shortName, @@ -326,7 +328,7 @@ long backgroundColor, String webApkPackageName, int shellApkVersion, String manifestUrl, String manifestStartUrl, @WebApkDistributor int distributor, Map<String, String> iconUrlToMurmur2HashMap, ShareTarget shareTarget, - boolean forceNavigation, boolean useTransparentSplash, ShareData shareData) { + boolean forceNavigation, boolean isSplashProvidedByWebApk, ShareData shareData) { if (id == null || url == null || manifestStartUrl == null || webApkPackageName == null) { Log.e(TAG, "Incomplete data provided: " + id + ", " + url + ", " + manifestStartUrl + ", " @@ -344,7 +346,7 @@ return new WebApkInfo(id, url, scope, primaryIcon, badgeIcon, splashIcon, name, shortName, displayMode, orientation, source, themeColor, backgroundColor, webApkPackageName, shellApkVersion, manifestUrl, manifestStartUrl, distributor, - iconUrlToMurmur2HashMap, shareTarget, forceNavigation, useTransparentSplash, + iconUrlToMurmur2HashMap, shareTarget, forceNavigation, isSplashProvidedByWebApk, shareData); } @@ -354,7 +356,7 @@ String webApkPackageName, int shellApkVersion, String manifestUrl, String manifestStartUrl, @WebApkDistributor int distributor, Map<String, String> iconUrlToMurmur2HashMap, ShareTarget shareTarget, - boolean forceNavigation, boolean useTransparentSplash, ShareData shareData) { + boolean forceNavigation, boolean isSplashProvidedByWebApk, ShareData shareData) { super(id, url, scope, primaryIcon, name, shortName, displayMode, orientation, source, themeColor, backgroundColor, null /* splash_screen_url */, false /* isIconGenerated */, false /* isIconAdaptive */, forceNavigation); @@ -366,7 +368,7 @@ mManifestStartUrl = manifestStartUrl; mDistributor = distributor; mIconUrlToMurmur2HashMap = iconUrlToMurmur2HashMap; - mUseTransparentSplash = useTransparentSplash; + mIsSplashProvidedByWebApk = isSplashProvidedByWebApk; mShareData = shareData; mShareTarget = shareTarget; @@ -407,8 +409,8 @@ } @Override - public boolean useTransparentSplash() { - return mUseTransparentSplash; + public boolean isSplashProvidedByWebApk() { + return mIsSplashProvidedByWebApk; } public int shellApkVersion() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java index 4536174..75ec95d8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
@@ -121,7 +121,7 @@ backgroundColor, mOldInfo.webApkPackageName(), mOldInfo.shellApkVersion(), mOldInfo.manifestUrl(), manifestStartUrl, WebApkInfo.WebApkDistributor.BROWSER, iconUrlToMurmur2HashMap, shareTarget, mOldInfo.shouldForceNavigation(), - mOldInfo.useTransparentSplash(), null); + mOldInfo.isSplashProvidedByWebApk(), null); mObserver.onGotManifestData(info, primaryIconUrl, badgeIconUrl); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java index f8ad842..5224351 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
@@ -379,8 +379,12 @@ return mIsIconGenerated; } - /** Returns whether the WebappActivity should be transparent while the page is loading. */ - public boolean useTransparentSplash() { + /** + * Returns whether the WebappInfo is for a WebAPK and the WebAPK provides a splash screen + * activity that should be launched on top of the web contents in order to hide the web + * contents while the page is loading. + */ + public boolean isSplashProvidedByWebApk() { return false; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLauncherActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLauncherActivity.java index 4eafa53..aab0849 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLauncherActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLauncherActivity.java
@@ -147,14 +147,11 @@ // WebappActivity and the user selects the WebappActivity from "Android Recents" the // WebappActivity is launched without going through WebappLauncherActivity first. WebappActivity.addWebappInfo(webappInfo.id(), webappInfo); - Intent launchIntent = createWebappLaunchIntent(webappInfo); - IntentHandler.addTimestampToIntent(launchIntent, createTimestamp); - // Pass through WebAPK shell launch timestamp to the new intent. - long shellLaunchTimestamp = IntentHandler.getWebApkShellLaunchTimestampFromIntent(intent); - IntentHandler.addShellLaunchTimestampToIntent(launchIntent, shellLaunchTimestamp); - IntentUtils.safeStartActivity(launchingActivity, launchIntent); - if (IntentUtils.isIntentForNewTaskOrNewDocument(launchIntent)) { + Intent[] launchIntents = + createIntentsToLaunchForWebapp(intent, webappInfo, createTimestamp); + launchingActivity.startActivities(launchIntents); + if (IntentUtils.isIntentForNewTaskOrNewDocument(launchIntents[0])) { ApiCompatibilityUtils.finishAndRemoveTask(launchingActivity); } else { launchingActivity.finish(); @@ -244,14 +241,13 @@ return IntentHandler.wasIntentSenderChrome(intent); } - /** - * Creates an Intent to launch the web app. - * @param info Information about the web app. - */ - private static Intent createWebappLaunchIntent(WebappInfo info) { + /** Returns the class name of the {@link WebappActivity} subclass to launch. */ + private static String selectWebappActivitySubclass(@NonNull WebappInfo info) { + if (info.isSplashProvidedByWebApk()) { + return SameTaskWebApkActivity.class.getName(); + } String activityName = info.isForWebApk() ? WebApkActivity.class.getName() : WebappActivity.class.getName(); - boolean newTask = true; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { // Specifically assign the app to a particular WebappActivity instance. int namespace = info.isForWebApk() @@ -259,37 +255,72 @@ : ActivityAssigner.ActivityAssignerNamespace.WEBAPP_NAMESPACE; int activityIndex = ActivityAssigner.instance(namespace).assign(info.id()); activityName += String.valueOf(activityIndex); + } + return activityName; + } - // Finishes the old activity if it has been assigned to a different WebappActivity. See - // crbug.com/702998. - for (WeakReference<Activity> activityRef : ApplicationStatus.getRunningActivities()) { - Activity activity = activityRef.get(); - if (!(activity instanceof WebappActivity) - || !activity.getClass().getName().equals(activityName)) { - continue; - } - WebappActivity webappActivity = (WebappActivity) activity; - if (!TextUtils.equals(webappActivity.getWebappInfo().id(), info.id())) { - activity.finish(); - } - break; + /** + * Finds instance of {@link webappActivitySubclass}. Finishes the activity if launching the + * webapp will: + * 1) Reuse the currently running activity (activity is singleTask) + * 2) The currently running activity is for a different webapp than the one being launched (The + * {@link ActivityAssigner} has wrapped around.) + * @param webappActivitySubclass WebappActivity subclass to look for. + * @param launchWebappId The ID of the webapp being launched. + */ + private static void finishIfReusingActivity( + String webappActivitySubclass, String launchWebappId) { + // {@link #selectWebappActivitySubclass()} does not select singleTask activities on L+. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) return; + + for (WeakReference<Activity> activityRef : ApplicationStatus.getRunningActivities()) { + Activity activity = activityRef.get(); + if (!activity.getClass().getName().equals(webappActivitySubclass)) { + continue; } - } else { - if (info.isForWebApk() && info.useTransparentSplash()) { - activityName = TransparentSplashWebApkActivity.class.getName(); - newTask = false; + WebappActivity webappActivity = (WebappActivity) activity; + if (!TextUtils.equals(webappActivity.getWebappInfo().id(), launchWebappId)) { + activity.finish(); } + break; + } + } + + /** + * Returns intents to launch for the web app. The output array should be sorted in the order + * that the intents should be dispatched with the intent to be dispatched first at index 0. + */ + private static Intent[] createIntentsToLaunchForWebapp( + Intent intent, @NonNull WebappInfo webappInfo, long createTimestamp) { + String launchActivityClassName = selectWebappActivitySubclass(webappInfo); + + // Finishes the old activity if it has been assigned to a different WebappActivity. See + // crbug.com/702998. + finishIfReusingActivity(launchActivityClassName, webappInfo.id()); + + Intent showSplashIntent = null; + if (webappInfo.isSplashProvidedByWebApk()) { + showSplashIntent = new Intent(WebApkConstants.ACTION_SHOW_SPLASH); + showSplashIntent.setPackage(webappInfo.webApkPackageName()); + showSplashIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); } - // Create an intent to launch the Webapp in an unmapped WebappActivity. - Intent launchIntent = new Intent(); - launchIntent.setClassName(ContextUtils.getApplicationContext(), activityName); - info.setWebappIntentExtras(launchIntent); + Intent webappActivityLaunchIntent = new Intent(); + webappActivityLaunchIntent.setClassName( + ContextUtils.getApplicationContext(), launchActivityClassName); + webappInfo.setWebappIntentExtras(webappActivityLaunchIntent); + webappActivityLaunchIntent.setAction(Intent.ACTION_VIEW); // On L+, firing intents with the exact same data should relaunch a particular // Activity. - launchIntent.setAction(Intent.ACTION_VIEW); - launchIntent.setData(Uri.parse(WebappActivity.WEBAPP_SCHEME + "://" + info.id())); + webappActivityLaunchIntent.setData( + Uri.parse(WebappActivity.WEBAPP_SCHEME + "://" + webappInfo.id())); + + IntentHandler.addTimestampToIntent(webappActivityLaunchIntent, createTimestamp); + // Pass through WebAPK shell launch timestamp to the new intent. + long shellLaunchTimestamp = IntentHandler.getWebApkShellLaunchTimestampFromIntent(intent); + IntentHandler.addShellLaunchTimestampToIntent( + webappActivityLaunchIntent, shellLaunchTimestamp); // Setting FLAG_ACTIVITY_CLEAR_TOP handles 2 edge cases: // - If a legacy PWA is launching from a notification, we want to ensure that the URL being @@ -308,14 +339,18 @@ // onNewIntent of the existing WebappActivity being called. // TODO(pkotwicz): Route Webapp Actions Notification actions through new intent filter // instead of WebappLauncherActivity. http://crbug.com/894610 - if (newTask) { - launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + if (webappInfo.isSplashProvidedByWebApk()) { + webappActivityLaunchIntent.setFlags( + Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NO_ANIMATION); + } else { + webappActivityLaunchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | ApiCompatibilityUtils.getActivityNewDocumentFlag() | Intent.FLAG_ACTIVITY_CLEAR_TOP); - } else { - launchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); } - return launchIntent; + + return (showSplashIntent == null) + ? new Intent[] {webappActivityLaunchIntent} + : new Intent[] {webappActivityLaunchIntent, showSplashIntent}; } /** Tries to create WebappInfo/WebApkInfo for the intent. */
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index 30db8249e..bfdc8dd 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -60,7 +60,6 @@ "java/src/org/chromium/chrome/browser/NearOomMonitor.java", "java/src/org/chromium/chrome/browser/NoTouchActivity.java", "java/src/org/chromium/chrome/browser/PowerBroadcastReceiver.java", - "java/src/org/chromium/chrome/browser/RepostFormWarningDialog.java", "java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java", "java/src/org/chromium/chrome/browser/ReturnToChromeExperimentsUtil.java", "java/src/org/chromium/chrome/browser/SearchGeolocationDisclosureTabHelper.java", @@ -1668,8 +1667,8 @@ "java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHost.java", "java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHostSignature.java", "java/src/org/chromium/chrome/browser/webapps/GooglePlayWebApkInstallDelegate.java", + "java/src/org/chromium/chrome/browser/webapps/SameTaskWebApkActivity.java", "java/src/org/chromium/chrome/browser/webapps/SplashscreenObserver.java", - "java/src/org/chromium/chrome/browser/webapps/TransparentSplashWebApkActivity.java", "java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java", "java/src/org/chromium/chrome/browser/webapps/WebApkActivity0.java", "java/src/org/chromium/chrome/browser/webapps/WebApkActivity1.java", @@ -1877,7 +1876,6 @@ "javatests/src/org/chromium/chrome/browser/PowerBroadcastReceiverTest.java", "javatests/src/org/chromium/chrome/browser/PrerenderTest.java", "javatests/src/org/chromium/chrome/browser/ProcessIsolationTest.java", - "javatests/src/org/chromium/chrome/browser/RepostFormWarningTest.java", "javatests/src/org/chromium/chrome/browser/RestoreHistogramTest.java", "javatests/src/org/chromium/chrome/browser/SafeBrowsingTest.java", "javatests/src/org/chromium/chrome/browser/SelectFileDialogTest.java", @@ -2279,6 +2277,7 @@ "javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseActivityTest.java", "javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java", "javatests/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateTest.java", + "javatests/src/org/chromium/chrome/browser/tab/RepostFormWarningTest.java", "javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java", "javatests/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java", "javatests/src/org/chromium/chrome/browser/tab/TabRedirectHandlerTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/RepostFormWarningTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/RepostFormWarningTest.java similarity index 72% rename from chrome/android/javatests/src/org/chromium/chrome/browser/RepostFormWarningTest.java rename to chrome/android/javatests/src/org/chromium/chrome/browser/tab/RepostFormWarningTest.java index ac36d72..e743552 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/RepostFormWarningTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/RepostFormWarningTest.java
@@ -2,12 +2,12 @@ // 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; +package org.chromium.chrome.browser.tab; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.filters.SmallTest; -import android.support.v7.app.AlertDialog; +import android.text.TextUtils; import org.junit.After; import org.junit.Assert; @@ -20,7 +20,9 @@ import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.RetryOnFailure; -import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.content_public.browser.LoadUrlParams; @@ -28,6 +30,9 @@ import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer; import org.chromium.net.test.EmbeddedTestServer; +import org.chromium.ui.modaldialog.ModalDialogProperties; +import org.chromium.ui.modaldialog.ModalDialogProperties.ButtonType; +import org.chromium.ui.modelutil.PropertyModel; import java.util.concurrent.TimeoutException; @@ -89,10 +94,10 @@ // Trigger a reload and wait for the warning to be displayed. reload(); - AlertDialog dialog = waitForRepostFormWarningDialog(); + PropertyModel dialog = waitForRepostFormWarningDialog(); // Click "Continue" and verify that the page is reloaded. - clickButton(dialog, AlertDialog.BUTTON_POSITIVE); + clickButton(dialog, ModalDialogProperties.ButtonType.POSITIVE); mCallbackHelper.getOnPageFinishedHelper().waitForCallback(1); // Verify that the reference to the dialog in RepostFormWarningDialog was cleared. @@ -114,10 +119,10 @@ // Trigger a reload and wait for the warning to be displayed. reload(); - AlertDialog dialog = waitForRepostFormWarningDialog(); + PropertyModel dialog = waitForRepostFormWarningDialog(); // Click "Cancel" and verify that the page is not reloaded. - clickButton(dialog, AlertDialog.BUTTON_NEGATIVE); + clickButton(dialog, ModalDialogProperties.ButtonType.NEGATIVE); boolean timedOut = false; try { mCallbackHelper.getOnPageFinishedHelper().waitForCallback(1); @@ -146,42 +151,58 @@ waitForRepostFormWarningDialog(); ThreadUtils.runOnUiThreadBlocking( - (Runnable) () -> mActivityTestRule.getActivity().getCurrentTabModel().closeTab( - mTab)); + (Runnable) () + -> mActivityTestRule.getActivity().getCurrentTabModel().closeTab(mTab)); waitForNoReportFormWarningDialog(); } + private PropertyModel getCurrentModalDialog() { + return ThreadUtils.runOnUiThreadBlockingNoException( + () + -> mActivityTestRule.getActivity() + .getModalDialogManager() + .getCurrentDialogForTest()); + } + private void waitForNoReportFormWarningDialog() { CriteriaHelper.pollUiThread( new Criteria("Form resubmission dialog not dismissed correctly") { @Override public boolean isSatisfied() { - return RepostFormWarningDialog.getCurrentDialogForTesting() == null; + return getCurrentModalDialog() == null; } }); } - private AlertDialog waitForRepostFormWarningDialog() { - CriteriaHelper.pollUiThread( - new Criteria("Form resubmission warning not shown") { - @Override - public boolean isSatisfied() { - return RepostFormWarningDialog.getCurrentDialogForTesting() != null; - } - }); - return ThreadUtils.runOnUiThreadBlockingNoException( - () -> (AlertDialog) RepostFormWarningDialog.getCurrentDialogForTesting()); + private PropertyModel waitForRepostFormWarningDialog() { + CriteriaHelper.pollUiThread(new Criteria("Form resubmission warning not shown") { + @Override + public boolean isSatisfied() { + PropertyModel dialogModel = getCurrentModalDialog(); + if (dialogModel == null) { + updateFailureReason("No modal dialog shown"); + return false; + } + + updateFailureReason("Modal dialog is not a HTTP post dialog"); + return TextUtils.equals( + mActivityTestRule.getActivity().getString(R.string.http_post_warning_title), + dialogModel.get(ModalDialogProperties.TITLE)); + } + }); + return getCurrentModalDialog(); } /** Performs a POST navigation in mTab. */ private void postNavigation() throws Throwable { final String url = "/chrome/test/data/android/test.html"; - final byte[] postData = new byte[] { 42 }; + final byte[] postData = new byte[] {42}; InstrumentationRegistry.getInstrumentation().runOnMainSync( - () -> mTab.loadUrl(LoadUrlParams.createLoadHttpPostParams( - mTestServer.getURL(url), postData))); + () + -> mTab.loadUrl(LoadUrlParams.createLoadHttpPostParams( + mTestServer.getURL(url), postData))); } /** Reloads mTab. */ @@ -190,8 +211,9 @@ } /** Clicks the given button in the given dialog. */ - private void clickButton(final AlertDialog dialog, final int buttonId) throws Throwable { + private void clickButton(final PropertyModel dialog, final @ButtonType int type) + throws Throwable { InstrumentationRegistry.getInstrumentation().runOnMainSync( - () -> dialog.getButton(buttonId).performClick()); + () -> dialog.get(ModalDialogProperties.CONTROLLER).onClick(dialog, type)); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java index 092bd8a..88907d3 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java
@@ -838,11 +838,13 @@ "generic_webxr_permission_page")), PAGE_LOAD_TIMEOUT_S); mWebXrVrTestFramework.enterSessionWithUserGestureOrFail(mTestRule.getWebContents()); + NativeUiUtils.enableMockedInput(); NativeUiUtils.performActionAndWaitForVisibilityStatus( UserFriendlyElementName.WEB_XR_HOSTED_CONTENT, true /* visible */, () -> { WebXrVrTestFramework.runJavaScriptOrFail("requestPermission({audio:true})", POLL_TIMEOUT_SHORT_MS, mTestRule.getWebContents()); }); + NativeUiUtils.waitForUiQuiescence(); // Click outside the prompt and ensure that it gets dismissed. NativeUiUtils.clickElement( UserFriendlyElementName.WEB_XR_HOSTED_CONTENT, new PointF(0.55f, 0.0f));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java index 4c8e900b..e33017af 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java
@@ -12,6 +12,7 @@ import android.os.Bundle; import android.os.UserManager; import android.support.customtabs.CustomTabsIntent; +import android.text.TextUtils; import org.junit.Assert; import org.junit.Before; @@ -32,7 +33,7 @@ import org.chromium.chrome.browser.ShortcutHelper; import org.chromium.chrome.browser.document.ChromeLauncherActivity; import org.chromium.chrome.browser.searchwidget.SearchActivity; -import org.chromium.chrome.browser.webapps.TransparentSplashWebApkActivity; +import org.chromium.chrome.browser.webapps.SameTaskWebApkActivity; import org.chromium.chrome.browser.webapps.WebApkActivity; import org.chromium.chrome.browser.webapps.WebApkActivity0; import org.chromium.chrome.browser.webapps.WebappLauncherActivity; @@ -75,6 +76,11 @@ return false; } + /** Checks whether the intent has the provided action. */ + private boolean checkIntentHasAction(Intent intent, String action) { + return intent != null && TextUtils.equals(intent.getAction(), action); + } + /** * Checks that intent is either for {@link FirstRunActivity} or * {@link TabbedModeFirstRunActivity}. @@ -176,15 +182,20 @@ Intent intent = new Intent(); intent.putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, webApkPackageName); intent.putExtra(ShortcutHelper.EXTRA_URL, startUrl); - intent.putExtra(WebApkConstants.EXTRA_USE_TRANSPARENT_SPLASH, true); + intent.putExtra(WebApkConstants.EXTRA_SPLASH_PROVIDED_BY_WEBAPK, true); Robolectric.buildActivity(WebappLauncherActivity.class, intent).create(); Intent launchedIntent = mShadowApplication.getNextStartedActivity(); - while (checkIntentComponentClassOneOf(launchedIntent, - new Class[] {WebApkActivity.class, WebApkActivity0.class, - TransparentSplashWebApkActivity.class})) { - buildActivityWithClassNameFromIntent(launchedIntent); + while (checkIntentHasAction(launchedIntent, WebApkConstants.ACTION_SHOW_SPLASH) + || checkIntentComponentClassOneOf(launchedIntent, + new Class[] {WebApkActivity.class, WebApkActivity0.class, + SameTaskWebApkActivity.class})) { + if (launchedIntent.getComponent() != null) { + buildActivityWithClassNameFromIntent(launchedIntent); + } + // Get next intent even if we did not build an activity to deal with the case of + // several activities having been started simultaneously. launchedIntent = mShadowApplication.getNextStartedActivity(); }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java index 2a805d0..0308db5 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java
@@ -7,6 +7,7 @@ import android.content.Intent; import android.content.res.AssetManager; import android.content.res.Resources; +import android.os.Build; import android.os.Bundle; import android.provider.Browser; @@ -128,7 +129,7 @@ intent.putExtra(ShortcutHelper.EXTRA_FORCE_NAVIGATION, true); intent.putExtra(ShortcutHelper.EXTRA_URL, START_URL); intent.putExtra(ShortcutHelper.EXTRA_SOURCE, ShortcutSource.NOTIFICATION); - intent.putExtra(WebApkConstants.EXTRA_USE_TRANSPARENT_SPLASH, true); + intent.putExtra(WebApkConstants.EXTRA_SPLASH_PROVIDED_BY_WEBAPK, true); WebApkInfo info = WebApkInfo.create(intent); @@ -155,7 +156,8 @@ Assert.assertEquals(ICON_MURMUR2_HASH, info.iconUrlToMurmur2HashMap().get(ICON_URL)); Assert.assertEquals(SOURCE, info.source()); - Assert.assertTrue(info.useTransparentSplash()); + Assert.assertEquals( + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M), info.isSplashProvidedByWebApk()); Assert.assertEquals(null, info.icon()); Assert.assertEquals(null, info.badgeIcon());
diff --git a/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkConstants.java b/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkConstants.java index 4c4bfe1..65a3ac3 100644 --- a/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkConstants.java +++ b/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkConstants.java
@@ -27,11 +27,10 @@ // Activity launch time for uma tracking of Chrome web apk startup public static final String EXTRA_WEBAPK_LAUNCH_TIME = "org.chromium.chrome.browser.webapk_launch_time"; - // Whether the host browser's activity should be completely transparent till the page - // has loaded. This enables the ShellAPK's splash screen to show through the host browser's - // activity. - public static final String EXTRA_USE_TRANSPARENT_SPLASH = - "org.chromium.chrome.browser.webapk.transparent_splash"; + // Whether the WebAPK provides a splash screen activity which should be launched by the host + // browser to hide the web contents while the page is loading. + public static final String EXTRA_SPLASH_PROVIDED_BY_WEBAPK = + "org.chromium.chrome.browser.webapk.splash_provided_by_webapk"; // Tells the host browser to relaunch the WebAPK. public static final String EXTRA_RELAUNCH = "org.chromium.webapk.relaunch"; @@ -42,4 +41,11 @@ /** Name of the shared preferences file. */ public static final String PREF_PACKAGE = "org.chromium.webapk.shell_apk"; + + /** + * Action used by host browser to launch splash screen activity to hide web contents while the + * page is loading. + */ + public static final String ACTION_SHOW_SPLASH = + "org.chromium.webapk.shell_apk.ACTION_SHOW_SPLASH"; }
diff --git a/chrome/android/webapk/shell_apk/AndroidManifest.xml b/chrome/android/webapk/shell_apk/AndroidManifest.xml index d92430c..dbd5f5ac 100644 --- a/chrome/android/webapk/shell_apk/AndroidManifest.xml +++ b/chrome/android/webapk/shell_apk/AndroidManifest.xml
@@ -50,7 +50,7 @@ [[#use_new_splash]] <activity android:name="org.chromium.webapk.shell_apk.h2o.H2OOpaqueMainActivity" - android:theme="@style/SplashTheme" + android:theme="@style/EarlySplashTheme" android:relinquishTaskIdentity="true" android:excludeFromRecents="true" android:enabled="false" @@ -61,9 +61,17 @@ </intent-filter> </activity> <activity android:name="org.chromium.webapk.shell_apk.h2o.SplashActivity" - android:theme="@style/SplashTheme" + android:theme="@style/EarlySplashTheme" android:launchMode="singleTask"> </activity> + <activity android:name="org.chromium.webapk.shell_apk.h2o.OverlaySplashActivity" + android:theme="@style/OverlaySplashTheme" + android:exported="true"> + <intent-filter> + <action android:name="org.chromium.webapk.shell_apk.ACTION_SHOW_SPLASH"></action> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> [[/use_new_splash]] <activity android:name="org.chromium.webapk.shell_apk.[[#use_new_splash]]h2o.H2O[[/use_new_splash]]MainActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar"
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn index 692ea8ee..893a605 100644 --- a/chrome/android/webapk/shell_apk/BUILD.gn +++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -68,6 +68,7 @@ "src/org/chromium/webapk/shell_apk/h2o/H2OMainActivity.java", "src/org/chromium/webapk/shell_apk/h2o/H2OOpaqueMainActivity.java", "src/org/chromium/webapk/shell_apk/h2o/H2OTransparentLauncherActivity.java", + "src/org/chromium/webapk/shell_apk/h2o/OverlaySplashActivity.java", "src/org/chromium/webapk/shell_apk/h2o/SplashActivity.java", ] deps += [ "//chrome/android/webapk/libs/common:splash_java" ]
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni index 59f229b..e820661 100644 --- a/chrome/android/webapk/shell_apk/current_version/current_version.gni +++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@ # //chrome/android/webapk/shell_apk:webapk is changed. This includes # Java files, Android resource files and AndroidManifest.xml. Does not affect # Chrome.apk -current_shell_apk_version = 81 +current_shell_apk_version = 82
diff --git a/chrome/android/webapk/shell_apk/res_h2o/values-v17/styles.xml b/chrome/android/webapk/shell_apk/res_h2o/values-v17/styles.xml index cf80bab7..d99aaaf 100644 --- a/chrome/android/webapk/shell_apk/res_h2o/values-v17/styles.xml +++ b/chrome/android/webapk/shell_apk/res_h2o/values-v17/styles.xml
@@ -9,4 +9,16 @@ <item name="android:windowNoTitle">true</item> <item name="android:windowBackground">@color/background_color</item> </style> + + <!-- Must not be translucent so that background color is shown right when the + WebAPK is launched. --> + <style name="EarlySplashTheme" parent="SplashTheme"> + <item name="android:windowIsTranslucent">false</item> + </style> + + <!-- Must be translucent so that host browser loads page while splash screen + is up. --> + <style name="OverlaySplashTheme" parent="SplashTheme"> + <item name="android:windowIsTranslucent">true</item> + </style> </resources>
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/H2OLauncher.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/H2OLauncher.java index e218612..821902a 100644 --- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/H2OLauncher.java +++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/H2OLauncher.java
@@ -71,8 +71,9 @@ Log.v(TAG, "WebAPK Launch URL: " + params.getStartUrl()); Bundle extraExtras = new Bundle(); - extraExtras.putBoolean(WebApkConstants.EXTRA_USE_TRANSPARENT_SPLASH, true); - HostBrowserLauncher.launchBrowserInWebApkMode(splashActivity, params, extraExtras, 0); + extraExtras.putBoolean(WebApkConstants.EXTRA_SPLASH_PROVIDED_BY_WEBAPK, true); + HostBrowserLauncher.launchBrowserInWebApkMode( + splashActivity, params, extraExtras, Intent.FLAG_ACTIVITY_NO_ANIMATION); } /** Launches the given component, passing extras from the given intent. */
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/OverlaySplashActivity.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/OverlaySplashActivity.java new file mode 100644 index 0000000..4959c858 --- /dev/null +++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/OverlaySplashActivity.java
@@ -0,0 +1,54 @@ +// Copyright 2018 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.webapk.shell_apk.h2o; + +import android.app.Activity; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.os.Bundle; +import android.widget.FrameLayout; + +import org.chromium.webapk.lib.common.WebApkMetaDataKeys; +import org.chromium.webapk.lib.common.WebApkMetaDataUtils; +import org.chromium.webapk.lib.common.splash.SplashLayout; +import org.chromium.webapk.shell_apk.R; +import org.chromium.webapk.shell_apk.WebApkUtils; + +/** + * Splash screen activity which is launched on top of host browser and stays + * up till the host browser draws a non empty frame. + */ +public class OverlaySplashActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + showSplashScreen(); + } + + // TODO(pkotwicz): Make SplashActivity and OverlaySplashActivity share code. + private void showSplashScreen() { + Bundle metadata = WebApkUtils.readMetaData(this); + Resources resources = getResources(); + + Bitmap icon = WebApkUtils.decodeBitmapFromDrawable(resources, R.drawable.splash_icon); + @SplashLayout.IconClassification + int iconClassification = SplashLayout.classifyIcon(resources, icon, false); + + FrameLayout layout = new FrameLayout(this); + setContentView(layout); + + int backgroundColor = WebApkUtils.getColor(resources, R.color.background_color); + SplashLayout.createLayout(this, layout, icon, false /* isIconAdaptive */, + iconClassification, resources.getString(R.string.name), + WebApkUtils.shouldUseLightForegroundOnBackground(backgroundColor)); + + int themeColor = (int) WebApkMetaDataUtils.getLongFromMetaData( + metadata, WebApkMetaDataKeys.THEME_COLOR, Color.BLACK); + WebApkUtils.setStatusBarColor( + getWindow(), WebApkUtils.getDarkenedColorForStatusBar(themeColor)); + } +}
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index af16784..ecd71a4 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -147,12 +147,6 @@ <message name="IDS_MULTIDEVICE_SETUP_BACK_LABEL" desc="Label for button to navigate back in MultiDevice setup workflow."> Back </message> - <message name="IDS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_HEADER" desc="Header for failed setup page."> - Better Together setup couldn't complete - </message> - <message name="IDS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_MESSAGE" desc="Message for failed setup page."> - Make sure your Chromebook is connected to the internet and your phone’s Bluetooth is on - </message> <message name="IDS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_PAGE_HEADER" desc="Header for successful setup page."> All set! </message>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 21db56e..c57c1bf 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -8290,6 +8290,12 @@ <message name="IDS_APP_ACTION_SHORTCUT_ACCESSIBILITY_NAME" desc="Hint text for the search result that performs a specific action with an app."> <ph name="ACTION_NAME">$1<ex>Compose</ex></ph> with <ph name="APP_NAME">$2<ex>GMail</ex></ph>. </message> + <message name="IDS_APP_LIST_REMOVE_SUGGESTION_ACCESSIBILITY_NAME" desc="The spoken feedback for removing suggestion button"> + Remove this suggestion + </message> + <message name="IDS_APP_LIST_APPEND_SUGGESTION_ACCESSIBILITY_NAME" desc="The spoken feedback for appending suggestion button"> + Append this suggestion to search box + </message> <if expr="not use_titlecase"> <message name="IDS_APP_LIST_CONTEXT_MENU_NEW_TAB" desc="Title text for the 'open new' context menu item of an app list item configured to open in a tab"> New tab @@ -8819,6 +8825,12 @@ <ph name="DEVICE_NAME">$1<ex>device name</ex></ph> - Paired </message> </if> + <message name="IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce serial port chooser details to the user in a popup when it is from a website."> + <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to connect + </message> + <message name="IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce serial port chooser details to the user in a popup when it is from a Chrome extension."> + "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to connect + </message> <message name="IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce USB chooser details to the user in a popup when it is from a website."> <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to connect </message> @@ -8831,6 +8843,9 @@ <message name="IDS_DEVICE_CHOOSER_NO_DEVICES_FOUND_PROMPT" desc="The label shown to the user to inform them that no USB devices were found matching the requirements that the application provided."> No compatible devices found. </message> + <message name="IDS_SERIAL_PORT_CHOOSER_CONNECT_BUTTON_TEXT" desc="Label on the button that closes the serial port chooser popup and allows the requesting site access to the selected port."> + Connect + </message> <message name="IDS_USB_DEVICE_CHOOSER_CONNECT_BUTTON_TEXT" desc="Label on the button that closes the USB chooser popup and connects the selected device."> Connect </message>
diff --git a/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_CONNECT_BUTTON_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_CONNECT_BUTTON_TEXT.png.sha1 new file mode 100644 index 0000000..f07fbfed --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_CONNECT_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@ +ecb627167d8389660aeae4792061b5eae21770d2 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1 new file mode 100644 index 0000000..7aca7e4c0 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1
@@ -0,0 +1 @@ +89677bfb410ecac828410ee5f349d03816b2860a \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN.png.sha1 b/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN.png.sha1 new file mode 100644 index 0000000..f07fbfed --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN.png.sha1
@@ -0,0 +1 @@ +ecb627167d8389660aeae4792061b5eae21770d2 \ No newline at end of file
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn index 05dcbdc..4dbf942 100644 --- a/chrome/app/vector_icons/BUILD.gn +++ b/chrome/app/vector_icons/BUILD.gn
@@ -4,6 +4,7 @@ import("//build/util/branding.gni") import("//components/vector_icons/vector_icons.gni") +import("//device/vr/buildflags/buildflags.gni") aggregate_vector_icons("chrome_vector_icons") { icon_directory = "." @@ -142,6 +143,11 @@ if (is_chromeos) { icons += [ "warning_badge_circle.icon" ] } + + if (enable_vr && !is_android) { + # Used for permission prompts on desktop VR headsets and Linux unit tests. + icons += [ "open_in_browser.icon" ] + } } source_set("vector_icons") {
diff --git a/chrome/app/vector_icons/open_in_browser.icon b/chrome/app/vector_icons/open_in_browser.icon new file mode 100644 index 0000000..748db51 --- /dev/null +++ b/chrome/app/vector_icons/open_in_browser.icon
@@ -0,0 +1,32 @@ +// Copyright 2018 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. + +CANVAS_DIMENSIONS, 24, +MOVE_TO, 19, 4, +H_LINE_TO, 5, +R_ARC_TO, 2, 2, 0, 0, 0, -2, 2, +R_V_LINE_TO, 12, +R_ARC_TO, 2, 2, 0, 0, 0, 2, 2, +R_H_LINE_TO, 4, +R_V_LINE_TO, -2, +H_LINE_TO, 5, +V_LINE_TO, 8, +R_H_LINE_TO, 14, +R_V_LINE_TO, 10, +R_H_LINE_TO, -4, +R_V_LINE_TO, 2, +R_H_LINE_TO, 4, +R_CUBIC_TO, 1.1f, 0, 2, -0.9f, 2, -2, +V_LINE_TO, 6, +R_ARC_TO, 2, 2, 0, 0, 0, -2, -2, +CLOSE, +R_MOVE_TO, -7, 6, +R_LINE_TO, -4, 4, +R_H_LINE_TO, 3, +R_V_LINE_TO, 6, +R_H_LINE_TO, 2, +R_V_LINE_TO, -6, +R_H_LINE_TO, 3, +R_LINE_TO, -4, -4, +CLOSE
diff --git a/chrome/app/vr_strings.grdp b/chrome/app/vr_strings.grdp index 2b9f2ce..53a303d2 100644 --- a/chrome/app/vr_strings.grdp +++ b/chrome/app/vr_strings.grdp
@@ -57,10 +57,10 @@ <!-- Desktop-specific permission prompts --> <if expr="not is_android"> <message name="IDS_DESKTOP_PROMPT_DOFF_HEADSET" desc="Text displayed in a dialog in a VR headset to notify the user to take the headset off and take some action on the desktop monitor."> - Remove headset to block or allow on the desktop display + Remove headset to block or allow. </message> - <message name="IDS_VR_DESKTOP_BLUETOOTH_PROMPT" desc="Text displayed in a dialog in VR headset to notify the user that the current webpage is requesting bluetooth permissions."> - <ph name="URL_HOST">$1<ex>google.com</ex></ph> wants to pair to devices + <message name="IDS_VR_DESKTOP_GENERIC_PERMISSION_PROMPT" desc="Text displayed in a dialog in VR headset to notify the user that the current webpage is requesting a new permission, but we don't know which one specifically."> + <ph name="URL_HOST">$1<ex>google.com</ex></ph> has requested additional permissions. </message> </if>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 60fb452..da87ebc 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -1820,10 +1820,6 @@ flag_descriptions::kDeviceDiscoveryNotificationsDescription, kOsDesktop, ENABLE_DISABLE_VALUE_TYPE(switches::kEnableDeviceDiscoveryNotifications, switches::kDisableDeviceDiscoveryNotifications)}, - {"enable-print-preview-register-promos", - flag_descriptions::kPrintPreviewRegisterPromosName, - flag_descriptions::kPrintPreviewRegisterPromosDescription, kOsDesktop, - SINGLE_VALUE_TYPE(switches::kEnablePrintPreviewRegisterPromos)}, #endif // BUILDFLAG(ENABLE_SERVICE_DISCOVERY) #if defined(OS_WIN) {"enable-cloud-print-xps", flag_descriptions::kCloudPrintXpsName, @@ -2926,6 +2922,13 @@ kOsAll, FEATURE_VALUE_TYPE( autofill::features::kAutofillSaveCreditCardUsesStrikeSystem)}, + {"enable-autofill-save-credit-card-uses-strike-system-v2", + flag_descriptions::kEnableAutofillSaveCreditCardUsesStrikeSystemV2Name, + flag_descriptions:: + kEnableAutofillSaveCreditCardUsesStrikeSystemV2Description, + kOsAll, + FEATURE_VALUE_TYPE( + autofill::features::kAutofillSaveCreditCardUsesStrikeSystemV2)}, {"enable-autofill-send-experiment-ids-in-payments-rpcs", flag_descriptions::kEnableAutofillSendExperimentIdsInPaymentsRPCsName, flag_descriptions::
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index 9733b70c..3123425 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc
@@ -56,8 +56,8 @@ #include "chrome/browser/media/webrtc/webrtc_event_log_manager.h" #include "chrome/browser/media/webrtc/webrtc_log_uploader.h" #include "chrome/browser/metrics/chrome_feature_list_creator.h" -#include "chrome/browser/metrics/chrome_metrics_service_accessor.h" #include "chrome/browser/metrics/chrome_metrics_services_manager_client.h" +#include "chrome/browser/metrics/metrics_reporting_state.h" #include "chrome/browser/metrics/thread_watcher.h" #include "chrome/browser/net/chrome_net_log_helper.h" #include "chrome/browser/net/system_network_context_manager.h" @@ -307,10 +307,8 @@ #if !defined(OS_ANDROID) // This preference must be kept in sync with external values; update them // whenever the preference or its controlling policy changes. - pref_change_registrar_.Add( - metrics::prefs::kMetricsReportingEnabled, - base::Bind(&BrowserProcessImpl::ApplyMetricsReportingPolicy, - base::Unretained(this))); + pref_change_registrar_.Add(metrics::prefs::kMetricsReportingEnabled, + base::Bind(&ApplyMetricsReportingPolicy)); #endif int max_per_proxy = local_state_->GetInteger(prefs::kMaxConnectionsPerProxy); @@ -1245,7 +1243,7 @@ void BrowserProcessImpl::CreateNotificationPlatformBridge() { #if BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS) DCHECK(!notification_bridge_); - notification_bridge_.reset(NotificationPlatformBridge::Create()); + notification_bridge_ = NotificationPlatformBridge::Create(); created_notification_bridge_ = true; #endif } @@ -1255,7 +1253,7 @@ // All notification traffic is routed through NotificationPlatformBridge. #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) DCHECK(!notification_ui_manager_); - notification_ui_manager_.reset(NotificationUIManager::Create()); + notification_ui_manager_ = NotificationUIManager::Create(); created_notification_ui_manager_ = !!notification_ui_manager_; #endif } @@ -1271,7 +1269,7 @@ void BrowserProcessImpl::CreateStatusTray() { DCHECK(!status_tray_); - status_tray_.reset(StatusTray::Create()); + status_tray_ = StatusTray::Create(); } void BrowserProcessImpl::CreatePrintPreviewDialogController() { @@ -1402,16 +1400,6 @@ ResourceDispatcherHost::Get()->SetAllowCrossOriginAuthPrompt(value); } -#if !defined(OS_ANDROID) -void BrowserProcessImpl::ApplyMetricsReportingPolicy() { - GoogleUpdateSettings::CollectStatsConsentTaskRunner()->PostTask( - FROM_HERE, - base::BindOnce( - base::IgnoreResult(&GoogleUpdateSettings::SetCollectStatsConsent), - ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled())); -} -#endif - void BrowserProcessImpl::CacheDefaultWebClientState() { #if defined(OS_CHROMEOS) cached_default_web_client_state_ = shell_integration::IS_DEFAULT;
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h index 20aa225..d70dd33f 100644 --- a/chrome/browser/browser_process_impl.h +++ b/chrome/browser/browser_process_impl.h
@@ -231,9 +231,6 @@ void ApplyAllowCrossOriginAuthPromptPolicy(); void ApplyDefaultBrowserPolicy(); -#if !defined(OS_ANDROID) - void ApplyMetricsReportingPolicy(); -#endif void CacheDefaultWebClientState();
diff --git a/chrome/browser/browsing_data/cookies_tree_model.cc b/chrome/browser/browsing_data/cookies_tree_model.cc index fd369f17..e2e4e921 100644 --- a/chrome/browser/browsing_data/cookies_tree_model.cc +++ b/chrome/browser/browsing_data/cookies_tree_model.cc
@@ -15,12 +15,15 @@ #include "base/memory/ptr_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" #include "chrome/browser/browsing_data/browsing_data_cookie_helper.h" #include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h" #include "chrome/browser/content_settings/cookie_settings_factory.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/theme_resources.h" #include "components/content_settings/core/browser/cookie_settings.h" +#include "content/public/browser/storage_partition.h" #include "content/public/browser/storage_usage_info.h" #include "content/public/common/url_constants.h" #include "extensions/buildflags/buildflags.h" @@ -297,6 +300,8 @@ return nullptr; } +void CookieTreeNode::RetrieveSize(const SizeRetrievalCallback& callback) {} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeCookieNode, public: @@ -346,6 +351,12 @@ return DetailedInfo().InitAppCache(origin_.GetURL(), &*appcache_info_); } +void CookieTreeAppCacheNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + callback.Run(this->GetDetailedInfo().origin, + this->GetDetailedInfo().appcache_info->size); +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeDatabaseNode, public: @@ -373,6 +384,12 @@ return DetailedInfo().InitDatabase(&*database_info_); } +void CookieTreeDatabaseNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + callback.Run(this->GetDetailedInfo().origin, + this->GetDetailedInfo().database_info->size); +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeLocalStorageNode, public: @@ -401,6 +418,12 @@ &*local_storage_info_); } +void CookieTreeLocalStorageNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + callback.Run(this->GetDetailedInfo().origin, + this->GetDetailedInfo().local_storage_info->size); +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeSessionStorageNode, public: @@ -455,6 +478,12 @@ return DetailedInfo().InitIndexedDB(&*indexed_db_info_); } +void CookieTreeIndexedDBNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + callback.Run(this->GetDetailedInfo().origin, + this->GetDetailedInfo().indexed_db_info->total_size_bytes); +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeFileSystemNode, public: @@ -482,6 +511,16 @@ return DetailedInfo().InitFileSystem(&*file_system_info_); } +void CookieTreeFileSystemNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + int64_t size = 0; + for (auto const& usage : + this->GetDetailedInfo().file_system_info->usage_map) { + size += usage.second; + } + callback.Run(this->GetDetailedInfo().origin, size); +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeQuotaNode, public: @@ -535,6 +574,12 @@ return DetailedInfo().InitServiceWorker(&*service_worker_info_); } +void CookieTreeServiceWorkerNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + callback.Run(this->GetDetailedInfo().origin, + this->GetDetailedInfo().service_worker_info->total_size_bytes); +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeSharedWorkerNode, public: @@ -587,6 +632,12 @@ return DetailedInfo().InitCacheStorage(&*cache_storage_info_); } +void CookieTreeCacheStorageNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + callback.Run(this->GetDetailedInfo().origin, + this->GetDetailedInfo().cache_storage_info->total_size_bytes); +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeMediaLicenseNode, public: @@ -613,6 +664,12 @@ return DetailedInfo().InitMediaLicense(&*media_license_info_); } +void CookieTreeMediaLicenseNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + callback.Run(this->GetDetailedInfo().origin, + this->GetDetailedInfo().media_license_info->size); +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeRootNode, public: @@ -647,6 +704,12 @@ return DetailedInfo().Init(DetailedInfo::TYPE_ROOT); } +void CookieTreeRootNode::RetrieveSize(const SizeRetrievalCallback& callback) { + for (int i = 0; i < this->child_count(); ++i) { + this->GetChild(i)->RetrieveSize(callback); + } +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeHostNode, public: @@ -805,6 +868,12 @@ return !url_.SchemeIsFile(); } +void CookieTreeHostNode::RetrieveSize(const SizeRetrievalCallback& callback) { + for (int i = 0; i < this->child_count(); ++i) { + this->GetChild(i)->RetrieveSize(callback); + } +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeCookiesNode, public: @@ -833,6 +902,13 @@ return DetailedInfo().Init(DetailedInfo::TYPE_APPCACHES); } +void CookieTreeAppCachesNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + for (int i = 0; i < this->child_count(); ++i) { + this->GetChild(i)->RetrieveSize(callback); + } +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeDatabasesNode, public: @@ -846,6 +922,13 @@ return DetailedInfo().Init(DetailedInfo::TYPE_DATABASES); } +void CookieTreeDatabasesNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + for (int i = 0; i < this->child_count(); ++i) { + this->GetChild(i)->RetrieveSize(callback); + } +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeLocalStoragesNode, public: @@ -860,6 +943,13 @@ return DetailedInfo().Init(DetailedInfo::TYPE_LOCAL_STORAGES); } +void CookieTreeLocalStoragesNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + for (int i = 0; i < this->child_count(); ++i) { + this->GetChild(i)->RetrieveSize(callback); + } +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeSessionStoragesNode, public: @@ -888,6 +978,13 @@ return DetailedInfo().Init(DetailedInfo::TYPE_INDEXED_DBS); } +void CookieTreeIndexedDBsNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + for (int i = 0; i < this->child_count(); ++i) { + this->GetChild(i)->RetrieveSize(callback); + } +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeFileSystemsNode, public: @@ -902,6 +999,13 @@ return DetailedInfo().Init(DetailedInfo::TYPE_FILE_SYSTEMS); } +void CookieTreeFileSystemsNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + for (int i = 0; i < this->child_count(); ++i) { + this->GetChild(i)->RetrieveSize(callback); + } +} + void CookieTreeNode::AddChildSortedByTitle( std::unique_ptr<CookieTreeNode> new_child) { DCHECK(new_child); @@ -925,6 +1029,13 @@ return DetailedInfo().Init(DetailedInfo::TYPE_SERVICE_WORKERS); } +void CookieTreeServiceWorkersNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + for (int i = 0; i < this->child_count(); ++i) { + this->GetChild(i)->RetrieveSize(callback); + } +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeSharedWorkersNode, public: @@ -951,6 +1062,13 @@ return DetailedInfo().Init(DetailedInfo::TYPE_CACHE_STORAGES); } +void CookieTreeCacheStoragesNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + for (int i = 0; i < this->child_count(); ++i) { + this->GetChild(i)->RetrieveSize(callback); + } +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeFlashLSONode CookieTreeFlashLSONode::CookieTreeFlashLSONode( @@ -987,6 +1105,13 @@ return DetailedInfo().Init(DetailedInfo::TYPE_MEDIA_LICENSES); } +void CookieTreeMediaLicensesNode::RetrieveSize( + const SizeRetrievalCallback& callback) { + for (int i = 0; i < this->child_count(); ++i) { + this->GetChild(i)->RetrieveSize(callback); + } +} + /////////////////////////////////////////////////////////////////////////////// // ScopedBatchUpdateNotifier CookiesTreeModel::ScopedBatchUpdateNotifier::ScopedBatchUpdateNotifier( @@ -1610,3 +1735,40 @@ SetBatchExpectation(0, true); } } +// static +std::unique_ptr<CookiesTreeModel> CookiesTreeModel::CreateForProfile( + Profile* profile) { + content::StoragePartition* storage_partition = + content::BrowserContext::GetDefaultStoragePartition(profile); + content::IndexedDBContext* indexed_db_context = + storage_partition->GetIndexedDBContext(); + content::ServiceWorkerContext* service_worker_context = + storage_partition->GetServiceWorkerContext(); + content::CacheStorageContext* cache_storage_context = + storage_partition->GetCacheStorageContext(); + storage::FileSystemContext* file_system_context = + storage_partition->GetFileSystemContext(); + auto container = std::make_unique<LocalDataContainer>( + new BrowsingDataCookieHelper(storage_partition), + new BrowsingDataDatabaseHelper(profile), + new BrowsingDataLocalStorageHelper(profile), + /*session_storage_helper=*/nullptr, + new BrowsingDataAppCacheHelper(profile), + new BrowsingDataIndexedDBHelper(indexed_db_context), + BrowsingDataFileSystemHelper::Create(file_system_context), + BrowsingDataQuotaHelper::Create(profile), + new BrowsingDataServiceWorkerHelper(service_worker_context), + new BrowsingDataSharedWorkerHelper(storage_partition, + profile->GetResourceContext()), + new BrowsingDataCacheStorageHelper(cache_storage_context), +#if defined(OS_ANDROID) + // Android doesn't have flash LSO hence it cannot be created for + // android build. + nullptr, +#else + BrowsingDataFlashLSOHelper::Create(profile), +#endif + BrowsingDataMediaLicenseHelper::Create(file_system_context)); + return std::make_unique<CookiesTreeModel>( + std::move(container), profile->GetExtensionSpecialStoragePolicy()); +}
diff --git a/chrome/browser/browsing_data/cookies_tree_model.h b/chrome/browser/browsing_data/cookies_tree_model.h index bc07f665..56041697 100644 --- a/chrome/browser/browsing_data/cookies_tree_model.h +++ b/chrome/browser/browsing_data/cookies_tree_model.h
@@ -172,11 +172,18 @@ nullptr; }; + using SizeRetrievalCallback = + base::RepeatingCallback<void(const url::Origin& origin, int64_t size)>; + CookieTreeNode() {} explicit CookieTreeNode(const base::string16& title) : ui::TreeNode<CookieTreeNode>(title) {} ~CookieTreeNode() override {} + // Recursively traverse the child nodes of this node and synchronously run + // |callback| on each one that has storage data size. + virtual void RetrieveSize(const SizeRetrievalCallback& callback); + // Delete backend storage for this node, and any children nodes. (E.g. delete // the cookie from CookieMonster, clear the database, and so forth.) virtual void DeleteStoredObjects(); @@ -207,6 +214,7 @@ // CookieTreeNode methods: CookiesTreeModel* GetModel() const override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; private: CookiesTreeModel* model_; @@ -225,6 +233,7 @@ // CookieTreeNode methods: DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; // CookieTreeHostNode methods: CookieTreeCookiesNode* GetOrCreateCookiesNode(); @@ -334,6 +343,7 @@ void DeleteStoredObjects() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; private: url::Origin origin_; @@ -347,6 +357,7 @@ ~CookieTreeAppCachesNode() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; void AddAppCacheNode(std::unique_ptr<CookieTreeAppCacheNode> child) { AddChildSortedByTitle(std::move(child)); @@ -370,6 +381,7 @@ void DeleteStoredObjects() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; private: // database_info_ is expected to remain valid as long as the @@ -386,6 +398,7 @@ ~CookieTreeDatabasesNode() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; void AddDatabaseNode(std::unique_ptr<CookieTreeDatabaseNode> child) { AddChildSortedByTitle(std::move(child)); @@ -409,6 +422,7 @@ void DeleteStoredObjects() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; private: // file_system_info_ expected to remain valid as long as the @@ -425,6 +439,7 @@ ~CookieTreeFileSystemsNode() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; void AddFileSystemNode(std::unique_ptr<CookieTreeFileSystemNode> child) { AddChildSortedByTitle(std::move(child)); @@ -447,6 +462,7 @@ // CookieTreeNode methods: void DeleteStoredObjects() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; private: // local_storage_info_ is expected to remain valid as long as the @@ -463,6 +479,7 @@ ~CookieTreeLocalStoragesNode() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; void AddLocalStorageNode(std::unique_ptr<CookieTreeLocalStorageNode> child) { AddChildSortedByTitle(std::move(child)); @@ -524,6 +541,7 @@ // CookieTreeNode methods: void DeleteStoredObjects() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; private: // indexed_db_info_ is expected to remain valid as long as the @@ -539,6 +557,7 @@ ~CookieTreeIndexedDBsNode() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; void AddIndexedDBNode(std::unique_ptr<CookieTreeIndexedDBNode> child) { AddChildSortedByTitle(std::move(child)); @@ -580,6 +599,7 @@ // CookieTreeNode methods: void DeleteStoredObjects() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; private: // service_worker_info_ is expected to remain valid as long as the @@ -595,6 +615,7 @@ ~CookieTreeServiceWorkersNode() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; void AddServiceWorkerNode( std::unique_ptr<CookieTreeServiceWorkerNode> child) { @@ -655,6 +676,7 @@ // CookieTreeNode methods: void DeleteStoredObjects() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; private: // cache_storage_info_ is expected to remain valid as long as the @@ -670,6 +692,7 @@ ~CookieTreeCacheStoragesNode() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; void AddCacheStorageNode(std::unique_ptr<CookieTreeCacheStorageNode> child) { AddChildSortedByTitle(std::move(child)); @@ -709,6 +732,7 @@ void DeleteStoredObjects() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; private: // |media_license_info_| is expected to remain valid as long as the @@ -725,6 +749,7 @@ ~CookieTreeMediaLicensesNode() override; DetailedInfo GetDetailedInfo() const override; + void RetrieveSize(const SizeRetrievalCallback& callback) override; void AddMediaLicenseNode(std::unique_ptr<CookieTreeMediaLicenseNode> child) { AddChildSortedByTitle(std::move(child)); @@ -836,6 +861,9 @@ // expected). void SetBatchExpectation(int batches_expected, bool reset); + // Create CookiesTreeModel by profile info. + static std::unique_ptr<CookiesTreeModel> CreateForProfile(Profile* profile); + private: enum CookieIconIndex { ORIGIN = 0,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index d964a7e..05834d8 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -315,6 +315,7 @@ #include "services/service_manager/sandbox/sandbox_type.h" #include "services/viz/public/interfaces/constants.mojom.h" #include "storage/browser/fileapi/external_mount_points.h" +#include "third_party/blink/public/common/service_worker/service_worker_utils.h" #include "third_party/blink/public/platform/modules/installedapp/installed_app_provider.mojom.h" #include "third_party/blink/public/platform/modules/webshare/webshare.mojom.h" #include "third_party/widevine/cdm/buildflags.h" @@ -4553,7 +4554,7 @@ } #if BUILDFLAG(ENABLE_PLUGINS) - if (network_service_enabled) { + if (blink::ServiceWorkerUtils::IsServicificationEnabled()) { result.push_back( std::make_unique<PluginResponseInterceptorURLLoaderThrottle>( resource_context, request.resource_type, frame_tree_node_id));
diff --git a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc index 613695b..0365d08 100644 --- a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc +++ b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc
@@ -449,7 +449,7 @@ if (BluetoothAdapterFactory::IsBluetoothSupported()) { VLOG(1) << "Registering bluetooth adapter."; - BluetoothAdapterFactory::GetAdapter(base::Bind( + BluetoothAdapterFactory::GetAdapter(base::BindOnce( &ArcBluetoothBridge::OnAdapterInitialized, weak_factory_.GetWeakPtr())); } else { VLOG(1) << "Bluetooth not supported.";
diff --git a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc index aea458a..bc19dc8 100644 --- a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc +++ b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
@@ -112,7 +112,7 @@ "CHROMEOS_ARC_ANDROID_SDK_VERSION=28", base::Time::Now()); WaitForInstanceReady(arc_bridge_service_->bluetooth()); - device::BluetoothAdapterFactory::GetAdapter(base::Bind( + device::BluetoothAdapterFactory::GetAdapter(base::BindOnce( &ArcBluetoothBridgeTest::OnAdapterInitialized, base::Unretained(this))); // We will quit the loop once we get the adapter. get_adapter_run_loop_.Run();
diff --git a/chrome/browser/chromeos/file_manager/path_util.cc b/chrome/browser/chromeos/file_manager/path_util.cc index e87c2d5..1bbf32c0 100644 --- a/chrome/browser/chromeos/file_manager/path_util.cc +++ b/chrome/browser/chromeos/file_manager/path_util.cc
@@ -365,7 +365,8 @@ // Convert paths under /special. GURL external_file_url = - chromeos::CreateExternalFileURLFromPath(primary_profile, path); + chromeos::CreateExternalFileURLFromPath(primary_profile, path, + /* allow_drivefs = */ true); if (!external_file_url.is_empty()) { *arc_url_out = arc::EncodeToChromeContentProviderUrl(external_file_url); return true;
diff --git a/chrome/browser/chromeos/fileapi/external_file_url_util.cc b/chrome/browser/chromeos/fileapi/external_file_url_util.cc index d422a98..c71ee94 100644 --- a/chrome/browser/chromeos/fileapi/external_file_url_util.cc +++ b/chrome/browser/chromeos/fileapi/external_file_url_util.cc
@@ -24,17 +24,19 @@ namespace chromeos { -bool IsExternalFileURLType(storage::FileSystemType type) { +bool IsExternalFileURLType(storage::FileSystemType type, bool allow_drivefs) { return type == storage::kFileSystemTypeDrive || type == storage::kFileSystemTypeDeviceMediaAsFileStorage || type == storage::kFileSystemTypeProvided || - type == storage::kFileSystemTypeArcContent; + type == storage::kFileSystemTypeArcContent || + (allow_drivefs && type == storage::kFileSystemTypeDriveFs); } GURL FileSystemURLToExternalFileURL( - const storage::FileSystemURL& file_system_url) { + const storage::FileSystemURL& file_system_url, + bool allow_drivefs) { if (file_system_url.mount_type() != storage::kFileSystemTypeExternal || - !IsExternalFileURLType(file_system_url.type())) { + !IsExternalFileURLType(file_system_url.type(), allow_drivefs)) { return GURL(); } @@ -56,7 +58,8 @@ } GURL CreateExternalFileURLFromPath(Profile* profile, - const base::FilePath& path) { + const base::FilePath& path, + bool allow_drivefs) { DCHECK_CURRENTLY_ON(BrowserThread::UI); GURL raw_file_system_url; @@ -75,7 +78,7 @@ if (!file_system_url.is_valid()) return GURL(); - return FileSystemURLToExternalFileURL(file_system_url); + return FileSystemURLToExternalFileURL(file_system_url, allow_drivefs); } } // namespace chromeos
diff --git a/chrome/browser/chromeos/fileapi/external_file_url_util.h b/chrome/browser/chromeos/fileapi/external_file_url_util.h index 117e12a0..9bb60608 100644 --- a/chrome/browser/chromeos/fileapi/external_file_url_util.h +++ b/chrome/browser/chromeos/fileapi/external_file_url_util.h
@@ -22,13 +22,17 @@ namespace chromeos { // Returns whether the external file URL is provided for the |type| or not. -bool IsExternalFileURLType(storage::FileSystemType type); +// TODO(b/119597913): Remove |allow_drivefs| from all functions in this file +// once ARC++ can access FUSE-mounted filesystems directly. +bool IsExternalFileURLType(storage::FileSystemType type, + bool allow_drivefs = false); // Obtains the external file url formatted as "externalfile:<path>" from file // path. Returns empty URL if the file system does not provide the external file // URL. GURL FileSystemURLToExternalFileURL( - const storage::FileSystemURL& file_system_url); + const storage::FileSystemURL& file_system_url, + bool allow_drivefs = false); // Converts a externalfile: URL back to a virtual path of FileSystemURL. base::FilePath ExternalFileURLToVirtualPath(const GURL& url); @@ -40,7 +44,8 @@ // path (e.g. /special/drive-xxx/root/sample.txt), if the |path| points an // external location (drive, MTP, or FSP). Otherwise, it returns empty URL. GURL CreateExternalFileURLFromPath(Profile* profile, - const base::FilePath& path); + const base::FilePath& path, + bool allow_drivefs = false); } // namespace chromeos
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc index 8462d3d..01378ea 100644 --- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc +++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
@@ -101,8 +101,8 @@ return; device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothDetector::OnAdapterInitialized, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce(&BluetoothDetector::OnAdapterInitialized, + weak_ptr_factory_.GetWeakPtr())); } bool IsPresent() const { return adapter_.get() && adapter_->IsPresent(); }
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_screen.cc b/chrome/browser/chromeos/login/screens/hid_detection_screen.cc index 1e0a474..21a27f6 100644 --- a/chrome/browser/chromeos/login/screens/hid_detection_screen.cc +++ b/chrome/browser/chromeos/login/screens/hid_detection_screen.cc
@@ -79,7 +79,7 @@ if (view_) view_->Bind(this); - device::BluetoothAdapterFactory::GetAdapter(base::Bind( + device::BluetoothAdapterFactory::GetAdapter(base::BindOnce( &HIDDetectionScreen::InitializeAdapter, weak_ptr_factory_.GetWeakPtr())); ConnectToInputDeviceManager(); }
diff --git a/chrome/browser/chromeos/login/version_info_updater.cc b/chrome/browser/chromeos/login/version_info_updater.cc index 1ec0834..c183b64 100644 --- a/chrome/browser/chromeos/login/version_info_updater.cc +++ b/chrome/browser/chromeos/login/version_info_updater.cc
@@ -73,7 +73,7 @@ base::Bind(&VersionInfoUpdater::OnVersion, weak_pointer_factory_.GetWeakPtr())); } else { - UpdateVersionLabel(); + OnVersion("linux-chromeos"); } policy::BrowserPolicyConnectorChromeOS* connector = @@ -97,7 +97,7 @@ } // Update device bluetooth info. - device::BluetoothAdapterFactory::GetAdapter(base::Bind( + device::BluetoothAdapterFactory::GetAdapter(base::BindOnce( &VersionInfoUpdater::OnGetAdapter, weak_pointer_factory_.GetWeakPtr())); } @@ -113,10 +113,6 @@ base::UTF8ToUTF16(version_info::GetVersionNumber()), base::UTF8ToUTF16(version_text_), base::UTF8ToUTF16(serial_number_text_)); - // Workaround over incorrect width calculation in old fonts. - // TODO(glotov): remove the following line when new fonts are used. - label_text += ' '; - if (delegate_) delegate_->OnOSVersionLabelTextUpdated(label_text); }
diff --git a/chrome/browser/chromeos/policy/bluetooth_policy_handler.cc b/chrome/browser/chromeos/policy/bluetooth_policy_handler.cc index 4635a99..439faddc 100644 --- a/chrome/browser/chromeos/policy/bluetooth_policy_handler.cc +++ b/chrome/browser/chromeos/policy/bluetooth_policy_handler.cc
@@ -34,7 +34,7 @@ if (status != chromeos::CrosSettingsProvider::TRUSTED) return; - device::BluetoothAdapterFactory::GetAdapter(base::Bind( + device::BluetoothAdapterFactory::GetAdapter(base::BindOnce( &BluetoothPolicyHandler::SetBluetoothPolicy, weak_factory_.GetWeakPtr())); }
diff --git a/chrome/browser/chromeos/tether/tether_service.cc b/chrome/browser/chromeos/tether/tether_service.cc index 022f83d..a60a377 100644 --- a/chrome/browser/chromeos/tether/tether_service.cc +++ b/chrome/browser/chromeos/tether/tether_service.cc
@@ -643,11 +643,8 @@ kUnavailableNoVerifiedHost: // Note that because of the early return above after // !HasSyncedTetherHosts, if this point is hit, there are synced tether - // hosts available, but the multidevice state is unverified. This switch - // case can only occur for legacy Magic Tether hosts, in which case the - // service should be enabled. - // TODO(crbug.com/894585): Remove this legacy special case after M71. - return ENABLED; + // hosts available, but the multidevice state is unverified. + FALLTHROUGH; case chromeos::multidevice_setup::mojom::FeatureState:: kNotSupportedByChromebook: // CryptAuth may not yet know that this device supports
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc index b2bb8790..fc5c46d0 100644 --- a/chrome/browser/devtools/devtools_ui_bindings.cc +++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -725,6 +725,9 @@ auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = gurl; + // TODO(caseq): this preserves behavior of URLFetcher-based implementation. + // We really need to pass proper first party origin from the front-end. + resource_request->site_for_cookies = gurl; resource_request->headers.AddHeadersFromString(headers); auto* partition = content::BrowserContext::GetStoragePartitionForSite(
diff --git a/chrome/browser/engagement/site_engagement_service.h b/chrome/browser/engagement/site_engagement_service.h index d9d7168..add9b596 100644 --- a/chrome/browser/engagement/site_engagement_service.h +++ b/chrome/browser/engagement/site_engagement_service.h
@@ -179,6 +179,7 @@ void AddPointsForTesting(const GURL& url, double points); private: + friend class BookmarkAppTest; friend class SiteEngagementObserver; friend class SiteEngagementServiceTest; FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, CheckHistograms);
diff --git a/chrome/browser/extensions/browsertest_util.cc b/chrome/browser/extensions/browsertest_util.cc index e9bbffb..687930ce 100644 --- a/chrome/browser/extensions/browsertest_util.cc +++ b/chrome/browser/extensions/browsertest_util.cc
@@ -14,7 +14,9 @@ #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/extensions/app_launch_params.h" #include "chrome/browser/ui/extensions/application_launch.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/components/web_app_helpers.h" +#include "chrome/browser/web_applications/components/web_app_tab_helper_base.h" #include "chrome/common/web_application_info.h" #include "content/public/browser/notification_service.h" #include "content/public/test/test_utils.h" @@ -88,5 +90,23 @@ return is_correct_app_browser ? browser : nullptr; } +Browser* LaunchBrowserForAppInTab(Profile* profile, + const Extension* extension_app) { + content::WebContents* web_contents = OpenApplication( + AppLaunchParams(profile, extension_app, LAUNCH_CONTAINER_TAB, + WindowOpenDisposition::NEW_FOREGROUND_TAB, SOURCE_TEST)); + DCHECK(web_contents); + + web_app::WebAppTabHelperBase* tab_helper = + web_app::WebAppTabHelperBase::FromWebContents(web_contents); + DCHECK(tab_helper); + DCHECK_EQ(extension_app->id(), tab_helper->app_id()); + + Browser* browser = chrome::FindBrowserWithWebContents(web_contents); + DCHECK_EQ(browser, chrome::FindLastActive()); + DCHECK_EQ(web_contents, browser->tab_strip_model()->GetActiveWebContents()); + return browser; +} + } // namespace browsertest_util } // namespace extensions
diff --git a/chrome/browser/extensions/browsertest_util.h b/chrome/browser/extensions/browsertest_util.h index 98486cfd..7ef60c2 100644 --- a/chrome/browser/extensions/browsertest_util.h +++ b/chrome/browser/extensions/browsertest_util.h
@@ -27,6 +27,10 @@ // Launches a new app window for |app| in |profile|. Browser* LaunchAppBrowser(Profile* profile, const Extension* app); +// Launches a new tab for |app| in |profile|. +Browser* LaunchBrowserForAppInTab(Profile* profile, + const Extension* extension_app); + } // namespace browsertest_util } // namespace extensions
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index 2385fe3e..0a7df34 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -304,6 +304,11 @@ return browsertest_util::LaunchAppBrowser(profile(), extension); } +Browser* ExtensionBrowserTest::LaunchBrowserForAppInTab( + const Extension* extension) { + return browsertest_util::LaunchBrowserForAppInTab(profile(), extension); +} + base::FilePath ExtensionBrowserTest::PackExtension( const base::FilePath& dir_path, int extra_run_flags) {
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h index 088b33f..1a8cdad 100644 --- a/chrome/browser/extensions/extension_browsertest.h +++ b/chrome/browser/extensions/extension_browsertest.h
@@ -132,6 +132,8 @@ // Launches |extension| as a window and returns the browser. Browser* LaunchAppBrowser(const Extension* extension); + // Launches |extension| as a tab and returns the browser. + Browser* LaunchBrowserForAppInTab(const Extension* extension); // Pack the extension in |dir_path| into a crx file and return its path. // Return an empty FilePath if there were errors.
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index e19c905..10fba59 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -889,6 +889,11 @@ "expiry_milestone": 76 }, { + "name": "enable-autofill-save-credit-card-uses-strike-system-v2", + "owners": [ "annelim@google.com", "jsaul@google.com" ], + "expiry_milestone": 76 + }, + { "name": "enable-autofill-send-experiment-ids-in-payments-rpcs", "owners": [ "dlkumar@google.com" ], "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 68c173a9..db80c18 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -420,6 +420,14 @@ "If enabled, prevents popping up the credit card offer-to-save prompt if " "it has repeatedly been ignored, declined, or failed."; +const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Name[] = + "Enable limit on offering to save the same credit card repeatedly using the" + "updated strike system implementation"; +const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Description[] = + "If enabled, uses the updated strike system implementation to prevent" + "popping up the credit card offer-to-save prompt if it has repeatedly been" + "ignored, declined, or failed."; + const char kEnableAutofillSendExperimentIdsInPaymentsRPCsName[] = "Send experiment flag IDs in calls to Google Payments"; const char kEnableAutofillSendExperimentIdsInPaymentsRPCsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index f44ed0a38..610dcced 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -272,6 +272,9 @@ extern const char kEnableAutofillSaveCreditCardUsesStrikeSystemName[]; extern const char kEnableAutofillSaveCreditCardUsesStrikeSystemDescription[]; +extern const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Name[]; +extern const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Description[]; + extern const char kEnableAutofillToolkitViewsCreditCardDialogsMac[]; extern const char kEnableAutofillToolkitViewsCreditCardDialogsMacDescription[];
diff --git a/chrome/browser/media/android/cdm/media_drm_storage_factory.cc b/chrome/browser/media/android/cdm/media_drm_storage_factory.cc index 37cfd33..3f44c6b5 100644 --- a/chrome/browser/media/android/cdm/media_drm_storage_factory.cc +++ b/chrome/browser/media/android/cdm/media_drm_storage_factory.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/callback.h" #include "base/logging.h" #include "chrome/browser/profiles/profile.h" #include "components/cdm/browser/media_drm_storage_impl.h" @@ -16,6 +17,17 @@ #include "content/public/browser/web_contents.h" #include "mojo/public/cpp/bindings/strong_binding.h" +namespace { + +void CreateOriginId( + base::OnceCallback<void(const base::UnguessableToken&)> callback) { + // TODO(crbug.com/917527): Update this to actually get a pre-provisioned + // origin ID. + std::move(callback).Run(base::UnguessableToken::Create()); +} + +} // namespace + void CreateMediaDrmStorage(content::RenderFrameHost* render_frame_host, media::mojom::MediaDrmStorageRequest request) { DVLOG(1) << __func__; @@ -43,5 +55,6 @@ // The object will be deleted on connection error, or when the frame navigates // away. See FrameServiceBase for details. new cdm::MediaDrmStorageImpl(render_frame_host, pref_service, + base::BindRepeating(&CreateOriginId), std::move(request)); }
diff --git a/chrome/browser/metrics/chrome_metrics_service_accessor.h b/chrome/browser/metrics/chrome_metrics_service_accessor.h index 9a921d5d..a8b24ba 100644 --- a/chrome/browser/metrics/chrome_metrics_service_accessor.h +++ b/chrome/browser/metrics/chrome_metrics_service_accessor.h
@@ -14,10 +14,8 @@ #include "chrome/browser/metrics/metrics_reporting_state.h" #include "components/metrics/metrics_service_accessor.h" -class BrowserProcessImpl; class ChromeMetricsServiceClient; class ChromePasswordManagerClient; -class NavigationMetricsRecorder; class PrefService; class Profile; @@ -26,10 +24,6 @@ class FlashDOMHandler; } -namespace android { -class ExternalDataUseObserverBridge; -} - namespace chrome { void AttemptRestart(); } @@ -44,7 +38,6 @@ } namespace extensions { -class ChromeExtensionWebContentsObserver; class ChromeGuestViewManagerDelegate; class ChromeMetricsPrivateDelegate; class FileManagerPrivateIsUMAEnabledFunction; @@ -54,14 +47,6 @@ class UkmConsentParamBrowserTest; } -namespace options { -class BrowserOptionsHandler; -} - -namespace prerender { -bool IsOmniboxEnabled(Profile* profile); -} - namespace heap_profiling { class BackgroundProfilingTriggers; } @@ -77,21 +62,12 @@ class ReporterRunner; class SafeBrowsingService; class SafeBrowsingUIManager; -class SRTGlobalError; } namespace settings { class MetricsReportingHandler; } -namespace speech { -class ChromeSpeechRecognitionManagerDelegate; -} - -namespace system_logs { -class ChromeInternalLogSource; -} - // This class limits and documents access to metrics service helper methods. // Since these methods are private, each user has to be explicitly declared // as a 'friend' below. @@ -107,9 +83,7 @@ private: friend class ::CrashesDOMHandler; friend class ::FlashDOMHandler; - friend class BrowserProcessImpl; friend void chrome::AttemptRestart(); - friend class ::android::ExternalDataUseObserverBridge; // For ChromeWinClang. friend class ChromeBrowserMainExtraPartsMetrics; // For StackSamplingConfiguration. @@ -120,30 +94,24 @@ const contextual_suggestions::ContextualSuggestionsResult& result); friend class DataReductionProxyChromeSettings; friend class domain_reliability::DomainReliabilityServiceFactory; - friend class extensions::ChromeExtensionWebContentsObserver; friend class extensions::ChromeGuestViewManagerDelegate; friend class extensions::ChromeMetricsPrivateDelegate; friend class extensions::FileManagerPrivateIsUMAEnabledFunction; friend void ChangeMetricsReportingStateWithReply( bool, const OnMetricsReportingCallbackType&); - friend class options::BrowserOptionsHandler; - friend bool prerender::IsOmniboxEnabled(Profile* profile); + friend void ApplyMetricsReportingPolicy(); friend class heap_profiling::BackgroundProfilingTriggers; friend class settings::MetricsReportingHandler; - friend class speech::ChromeSpeechRecognitionManagerDelegate; - friend class system_logs::ChromeInternalLogSource; friend class UmaSessionStats; friend class safe_browsing::ChromeCleanerControllerDelegate; friend class safe_browsing::DownloadUrlSBClient; friend class safe_browsing::IncidentReportingService; friend class safe_browsing::ReporterRunner; - friend class safe_browsing::SRTGlobalError; friend class safe_browsing::SafeBrowsingService; friend class safe_browsing::SafeBrowsingUIManager; friend class ChromeMetricsServiceClient; friend class ChromePasswordManagerClient; - friend class NavigationMetricsRecorder; friend class ChromeUnifiedConsentServiceClient; friend bool nux::IsNuxOnboardingEnabled(Profile* profile);
diff --git a/chrome/browser/metrics/chromeos_metrics_provider.cc b/chrome/browser/metrics/chromeos_metrics_provider.cc index f06e72c0..7f417aa 100644 --- a/chrome/browser/metrics/chromeos_metrics_provider.cc +++ b/chrome/browser/metrics/chromeos_metrics_provider.cc
@@ -186,8 +186,8 @@ void ChromeOSMetricsProvider::InitTaskGetBluetoothAdapter( const base::Closure& callback) { device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&ChromeOSMetricsProvider::SetBluetoothAdapter, - weak_ptr_factory_.GetWeakPtr(), callback)); + base::BindOnce(&ChromeOSMetricsProvider::SetBluetoothAdapter, + weak_ptr_factory_.GetWeakPtr(), callback)); } void ChromeOSMetricsProvider::ProvideSystemProfileMetrics(
diff --git a/chrome/browser/metrics/metrics_reporting_state.cc b/chrome/browser/metrics/metrics_reporting_state.cc index 2592dea8..1b7b06b 100644 --- a/chrome/browser/metrics/metrics_reporting_state.cc +++ b/chrome/browser/metrics/metrics_reporting_state.cc
@@ -7,7 +7,6 @@ #include "base/callback.h" #include "base/metrics/histogram_macros.h" #include "base/task_runner_util.h" -#include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" #include "chrome/common/pref_names.h" @@ -120,6 +119,16 @@ } } +#if !defined(OS_ANDROID) +void ApplyMetricsReportingPolicy() { + GoogleUpdateSettings::CollectStatsConsentTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce( + base::IgnoreResult(&GoogleUpdateSettings::SetCollectStatsConsent), + ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled())); +} +#endif + bool IsMetricsReportingPolicyManaged() { const PrefService* pref_service = g_browser_process->local_state(); const PrefService::Preference* pref =
diff --git a/chrome/browser/metrics/metrics_reporting_state.h b/chrome/browser/metrics/metrics_reporting_state.h index 944c713..53551b6 100644 --- a/chrome/browser/metrics/metrics_reporting_state.h +++ b/chrome/browser/metrics/metrics_reporting_state.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_METRICS_METRICS_REPORTING_STATE_H_ #include "base/callback.h" +#include "build/build_config.h" #include "components/metrics/metrics_service_client.h" typedef base::Callback<void(bool)> OnMetricsReportingCallbackType; @@ -28,6 +29,12 @@ // prefs, so as not to trigger upload of various stale data. void UpdateMetricsPrefsOnPermissionChange(bool metrics_enabled); +#if !defined(OS_ANDROID) +// Propagates the state of metrics reporting pref (which may be policy +// managed) to GoogleUpdateSettings. +void ApplyMetricsReportingPolicy(); +#endif + // Returns whether MetricsReporting can be modified by the user (except // Android). bool IsMetricsReportingPolicyManaged();
diff --git a/chrome/browser/notifications/notification_platform_bridge.h b/chrome/browser/notifications/notification_platform_bridge.h index eeda639..64f07b2 100644 --- a/chrome/browser/notifications/notification_platform_bridge.h +++ b/chrome/browser/notifications/notification_platform_bridge.h
@@ -28,7 +28,7 @@ using NotificationBridgeReadyCallback = base::OnceCallback<void(bool /* success */)>; - static NotificationPlatformBridge* Create(); + static std::unique_ptr<NotificationPlatformBridge> Create(); // Returns whether a native bridge can handle a notification of the given // type. Ideally, this would always return true, but for now some platforms
diff --git a/chrome/browser/notifications/notification_platform_bridge_android.cc b/chrome/browser/notifications/notification_platform_bridge_android.cc index 95fddc4e..5c5d57ed 100644 --- a/chrome/browser/notifications/notification_platform_bridge_android.cc +++ b/chrome/browser/notifications/notification_platform_bridge_android.cc
@@ -113,8 +113,9 @@ } // static -NotificationPlatformBridge* NotificationPlatformBridge::Create() { - return new NotificationPlatformBridgeAndroid(); +std::unique_ptr<NotificationPlatformBridge> +NotificationPlatformBridge::Create() { + return std::make_unique<NotificationPlatformBridgeAndroid>(); } // static
diff --git a/chrome/browser/notifications/notification_platform_bridge_chromeos.cc b/chrome/browser/notifications/notification_platform_bridge_chromeos.cc index 10909f7..1a7b07d2 100644 --- a/chrome/browser/notifications/notification_platform_bridge_chromeos.cc +++ b/chrome/browser/notifications/notification_platform_bridge_chromeos.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/notifications/notification_platform_bridge_chromeos.h" +#include <memory> + #include "base/callback_list.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" @@ -16,8 +18,9 @@ #include "ui/gfx/image/image.h" // static -NotificationPlatformBridge* NotificationPlatformBridge::Create() { - return new NotificationPlatformBridgeChromeOs(); +std::unique_ptr<NotificationPlatformBridge> +NotificationPlatformBridge::Create() { + return std::make_unique<NotificationPlatformBridgeChromeOs>(); } // static
diff --git a/chrome/browser/notifications/notification_platform_bridge_linux.cc b/chrome/browser/notifications/notification_platform_bridge_linux.cc index c3f2584..909acc30 100644 --- a/chrome/browser/notifications/notification_platform_bridge_linux.cc +++ b/chrome/browser/notifications/notification_platform_bridge_linux.cc
@@ -243,8 +243,9 @@ } // namespace // static -NotificationPlatformBridge* NotificationPlatformBridge::Create() { - return new NotificationPlatformBridgeLinux(); +std::unique_ptr<NotificationPlatformBridge> +NotificationPlatformBridge::Create() { + return std::make_unique<NotificationPlatformBridgeLinux>(); } // static
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm index a1a6464..2ad35ce5 100644 --- a/chrome/browser/notifications/notification_platform_bridge_mac.mm +++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -200,10 +200,11 @@ } // static -NotificationPlatformBridge* NotificationPlatformBridge::Create() { +std::unique_ptr<NotificationPlatformBridge> +NotificationPlatformBridge::Create() { base::scoped_nsobject<AlertDispatcherImpl> alert_dispatcher( [[AlertDispatcherImpl alloc] init]); - return new NotificationPlatformBridgeMac( + return std::make_unique<NotificationPlatformBridgeMac>( [NSUserNotificationCenter defaultUserNotificationCenter], alert_dispatcher.get()); }
diff --git a/chrome/browser/notifications/notification_platform_bridge_win.cc b/chrome/browser/notifications/notification_platform_bridge_win.cc index 43332a8f..c847753 100644 --- a/chrome/browser/notifications/notification_platform_bridge_win.cc +++ b/chrome/browser/notifications/notification_platform_bridge_win.cc
@@ -123,8 +123,9 @@ } // namespace // static -NotificationPlatformBridge* NotificationPlatformBridge::Create() { - return new NotificationPlatformBridgeWin(); +std::unique_ptr<NotificationPlatformBridge> +NotificationPlatformBridge::Create() { + return std::make_unique<NotificationPlatformBridgeWin>(); } // static
diff --git a/chrome/browser/notifications/notification_ui_manager.h b/chrome/browser/notifications/notification_ui_manager.h index da82dd7..0cb2737 100644 --- a/chrome/browser/notifications/notification_ui_manager.h +++ b/chrome/browser/notifications/notification_ui_manager.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_UI_MANAGER_H_ #define CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_UI_MANAGER_H_ +#include <memory> #include <set> #include <string> #include <vector> @@ -45,7 +46,7 @@ virtual ~NotificationUIManager() {} // Creates an initialized UI manager. - static NotificationUIManager* Create(); + static std::unique_ptr<NotificationUIManager> Create(); // Adds a notification to be displayed. virtual void Add(const message_center::Notification& notification,
diff --git a/chrome/browser/notifications/notification_ui_manager_impl.cc b/chrome/browser/notifications/notification_ui_manager_impl.cc index a3a085e..eb40715b 100644 --- a/chrome/browser/notifications/notification_ui_manager_impl.cc +++ b/chrome/browser/notifications/notification_ui_manager_impl.cc
@@ -30,13 +30,13 @@ using message_center::NotifierId; // static -NotificationUIManager* NotificationUIManager::Create() { +std::unique_ptr<NotificationUIManager> NotificationUIManager::Create() { // If there's no MessageCenter, there should be no NotificationUIManager to // manage it. if (!message_center::MessageCenter::Get()) return nullptr; - return new NotificationUIManagerImpl(); + return std::make_unique<NotificationUIManagerImpl>(); } NotificationUIManagerImpl::NotificationUIManagerImpl()
diff --git a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc index 79e3712..3a8a828 100644 --- a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc +++ b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
@@ -97,6 +97,7 @@ WebFeature::kDownloadInAdFrameWithoutUserGesture, WebFeature::kOpenWebDatabase, WebFeature::kV8MediaCapabilities_DecodingInfo_Method, + WebFeature::kOpenerNavigationDownloadCrossOriginNoGesture, })); return *opt_in_features; }
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc index 373e2e2e..17c41cd 100644 --- a/chrome/browser/pdf/pdf_extension_test.cc +++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -360,6 +360,23 @@ base::FilePath(ChromeContentClient::kPDFPluginPath), browser()->profile()->GetPath()); } + + // Installs the specified service worker and tests navigating to a PDF in its + // scope. + void RunServiceWorkerTest(const std::string& worker_path) { + // Install the service worker. + ui_test_utils::NavigateToURL( + browser(), embedded_test_server()->GetURL( + "/service_worker/create_service_worker.html")); + EXPECT_EQ("DONE", + EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), + "register('" + worker_path + "', '/pdf');")); + + // Navigate to a PDF in the service worker's scope. It should load. + RunTestsInFile("basic_test.js", "test.pdf"); + // Ensure it loaded in a PPAPI process. + EXPECT_EQ(1, CountPDFProcesses()); + } }; class PDFExtensionLoadTest : public PDFExtensionTest, @@ -1987,3 +2004,24 @@ content::ExecuteScriptAndExtractString(guest_contents, script, &inner)); EXPECT_EQ(inner, outer); } + +// Service worker tests are regression tests for +// https://crbug.com/916514. + +// Test navigating to a PDF in the scope of a service worker with no fetch event +// handler. +IN_PROC_BROWSER_TEST_F(PDFExtensionTest, ServiceWorkerNoFetchHandler) { + RunServiceWorkerTest("empty.js"); +} + +// Test navigating to a PDF when a service worker intercepts the request and +// then falls back to network by not calling FetchEvent.respondWith(). +IN_PROC_BROWSER_TEST_F(PDFExtensionTest, ServiceWorkerNetworkFallback) { + RunServiceWorkerTest("network_fallback_worker.js"); +} + +// Test navigating to a PDF when a service worker intercepts the request and +// provides a response. +IN_PROC_BROWSER_TEST_F(PDFExtensionTest, ServiceWorkerInterception) { + RunServiceWorkerTest("respond_with_fetch_worker.js"); +}
diff --git a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc index d5c3099..67c7f8c 100644 --- a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc +++ b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h" +#include "base/feature_list.h" #include "base/guid.h" #include "base/task/post_task.h" #include "chrome/browser/extensions/api/streams_private/streams_private_api.h" @@ -16,6 +17,7 @@ #include "content/public/common/transferrable_url_loader.mojom.h" #include "extensions/common/extension.h" #include "mojo/public/cpp/system/data_pipe.h" +#include "services/network/public/cpp/features.h" PluginResponseInterceptorURLLoaderThrottle:: PluginResponseInterceptorURLLoaderThrottle( @@ -39,6 +41,17 @@ return; } + // Don't intercept if the request went through the legacy resource loading + // path, i.e., ResourceDispatcherHost, since that path doesn't need response + // interception. ResourceDispatcherHost is only used if network service is + // disabled (in which case this throttle was created because + // ServiceWorkerServicification was enabled) and a service worker didn't + // handle the request. + if (!base::FeatureList::IsEnabled(network::features::kNetworkService) && + !response_head->was_fetched_via_service_worker) { + return; + } + std::string extension_id = PluginUtils::GetExtensionIdForMimeType( resource_context_, response_head->mime_type); if (extension_id.empty())
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc index 6ea8e31..dd8253d0 100644 --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -149,6 +149,7 @@ #include "chrome/browser/extensions/browser_context_keyed_service_factories.h" #include "chrome/browser/extensions/extension_management.h" #include "chrome/browser/ui/bookmarks/enhanced_bookmark_key_service_factory.h" +#include "chrome/browser/ui/web_applications/web_app_metrics_factory.h" #include "chrome/browser/web_applications/web_app_provider_factory.h" #include "extensions/browser/api/networking_private/networking_private_delegate_factory.h" #include "extensions/browser/browser_context_keyed_service_factories.h" @@ -400,6 +401,7 @@ #endif #if BUILDFLAG(ENABLE_EXTENSIONS) web_app::WebAppProviderFactory::GetInstance(); + web_app::WebAppMetricsFactory::GetInstance(); #endif WebDataServiceFactory::GetInstance(); webrtc_event_logging::WebRtcEventLogManagerKeyedServiceFactory::GetInstance();
diff --git a/chrome/browser/resources/local_ntp/doodles.js b/chrome/browser/resources/local_ntp/doodles.js index 870a6e8..98e6c2b 100644 --- a/chrome/browser/resources/local_ntp/doodles.js +++ b/chrome/browser/resources/local_ntp/doodles.js
@@ -61,6 +61,20 @@ /** + * Subset of gws.plugins.doodle.SharingLightbox.LogType in + * googledata/html/templates/gws/head/xjs/plugins/doodle/sharing_lightbox.js. + * @enum {number} + * @const + */ +doodles.SHARE_TYPE = { + FACEBOOK: 2, + TWITTER: 3, + EMAIL: 5, + LINK_COPY: 6, +}; + + +/** * The ID of the doodle app for Facebook. Used to share doodles to Facebook. * @type {number} */ @@ -266,6 +280,29 @@ /** + * TODO(896461): Add more click tracking parameters. + * Logs a doodle sharing event. + * Uses the ct param provided in metadata.onClickUrl to track the doodle. + * + * @param {string} platform Social media platform the doodle will be shared to. + */ +doodles.logDoodleShare = function(platform) { + if (doodles.targetDoodle.metadata.onClickUrl) { + const onClickUrl = new URL(doodles.targetDoodle.metadata.onClickUrl); + const ct = onClickUrl.searchParams.get('ct'); + if (ct && ct != '') { + const url = new URL('/gen_204', configData.googleBaseUrl); + url.searchParams.append('atyp', 'i'); + url.searchParams.append('ct', 'doodle'); + url.searchParams.append('cad', 'sh,' + platform + ',ct:' + ct); + url.searchParams.append('ntp', 1); + navigator.sendBeacon(url.toString()); + } + } +}; + + +/** * Returns true if the target doodle is currently visible. If |image| is null, * returns true when the default logo is visible; if non-null, checks that it * matches the doodle that is currently visible. Here, "visible" means @@ -619,6 +656,7 @@ '&href=' + encodeURIComponent(shortLink) + '&hashtag=' + encodeURIComponent('#GoogleDoodle'); window.open(url); + doodles.logDoodleShare(doodles.SHARE_TYPE.FACEBOOK); }; facebookButton.title = configData.translatedStrings.shareFacebook; @@ -626,6 +664,7 @@ var url = 'https://twitter.com/intent/tweet' + '?text=' + encodeURIComponent(title + '\n' + shortLink); window.open(url); + doodles.logDoodleShare(doodles.SHARE_TYPE.TWITTER); }; twitterButton.title = configData.translatedStrings.shareTwitter; @@ -633,6 +672,7 @@ var url = 'mailto:?subject=' + encodeURIComponent(title) + '&body=' + encodeURIComponent(shortLink); document.location.href = url; + doodles.logDoodleShare(doodles.SHARE_TYPE.EMAIL); }; mailButton.title = configData.translatedStrings.shareMail; @@ -645,6 +685,7 @@ copyButton.onclick = function() { linkText.select(); document.execCommand('copy'); + doodles.logDoodleShare(doodles.SHARE_TYPE.LINK_COPY); }; copyButton.title = configData.translatedStrings.copyLink; };
diff --git a/chrome/browser/resources/md_downloads/item.html b/chrome/browser/resources/md_downloads/item.html index 30031f3d..c2c9079 100644 --- a/chrome/browser/resources/md_downloads/item.html +++ b/chrome/browser/resources/md_downloads/item.html
@@ -252,8 +252,7 @@ right: initial; } - paper-button.action-button, - paper-button.pause-button { + #pauseOrResume { margin-inline-end: 8px; } </style> @@ -308,8 +307,7 @@ </paper-button> </template> <template is="dom-if" if="[[pauseOrResumeText_]]"> - <paper-button class$="[[pauseOrResumeClass_]]" - on-click="onPauseOrResumeTap_"> + <paper-button on-click="onPauseOrResumeTap_" id="pauseOrResume"> [[pauseOrResumeText_]] </paper-button> </template>
diff --git a/chrome/browser/resources/md_downloads/item.js b/chrome/browser/resources/md_downloads/item.js index cb96d21..c850f9c3 100644 --- a/chrome/browser/resources/md_downloads/item.js +++ b/chrome/browser/resources/md_downloads/item.js
@@ -61,15 +61,10 @@ }, /** @private */ - pauseOrResumeClass_: { - computed: 'computePauseOrResumeClass_(isInProgress_, data.resume)', - type: String, - }, - - /** @private */ pauseOrResumeText_: { computed: 'computePauseOrResumeText_(isInProgress_, data.resume)', type: String, + observer: 'updatePauseOrResumeClass_', }, /** @private */ @@ -269,18 +264,31 @@ this.data.dangerType == downloads.DangerType.POTENTIALLY_UNWANTED); }, - /** - * @return {string} 'action-button' for a resume button, 'pause-button' - * otherwise. - * @private - */ - computePauseOrResumeClass_: function() { - if (this.data === undefined) { - return ''; + /** @private */ + toggleButtonClass_: function() { + this.$$('#pauseOrResume') + .classList.toggle( + 'action-button', + this.pauseOrResumeText_ === + loadTimeData.getString('controlResume')); + }, + + /** @private */ + updatePauseOrResumeClass_: function() { + if (!this.pauseOrResumeText_) { + return; } - return !this.isInProgress_ && this.data.resume ? 'action-button' : - 'pause-button'; + // Wait for dom-if to switch to true, in case the text has just changed + // from empty. + // TODO (rbpotter): Remove this conditional when Polymer 2 migration is + // complete. + if (Polymer.DomIf) { + Polymer.RenderStatus.beforeNextRender( + this, () => this.toggleButtonClass_()); + } else { + this.async(() => this.toggleButtonClass_()); + } }, /**
diff --git a/chrome/browser/resources/md_extensions/BUILD.gn b/chrome/browser/resources/md_extensions/BUILD.gn index 2b0c124..be90cd8 100644 --- a/chrome/browser/resources/md_extensions/BUILD.gn +++ b/chrome/browser/resources/md_extensions/BUILD.gn
@@ -274,7 +274,6 @@ "//ui/webui/resources/cr_elements/cr_view_manager:cr_view_manager", "//ui/webui/resources/js:assert", "//ui/webui/resources/js:cr", - "//ui/webui/resources/js:find_shortcut_behavior", ] externs_list = [ "$externs_path/developer_private.js" ] } @@ -381,7 +380,6 @@ deps = [ "//third_party/polymer/v1_0/components-chromium/iron-a11y-announcer:iron-a11y-announcer-extracted", "//ui/webui/resources/cr_elements/cr_toast:cr_toast", - "//ui/webui/resources/cr_elements/cr_toolbar:cr_toolbar", "//ui/webui/resources/js:cr", "//ui/webui/resources/js:i18n_behavior", "//ui/webui/resources/js:util",
diff --git a/chrome/browser/resources/md_extensions/manager.html b/chrome/browser/resources/md_extensions/manager.html index dc94e46..a8d153e 100644 --- a/chrome/browser/resources/md_extensions/manager.html +++ b/chrome/browser/resources/md_extensions/manager.html
@@ -7,7 +7,6 @@ <link rel="import" href="chrome://resources/cr_elements/cr_view_manager/cr_view_manager.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> -<link rel="import" href="chrome://resources/html/find_shortcut_behavior.html"> <link rel="import" href="activity_log.html"> <link rel="import" href="detail_view.html"> <link rel="import" href="drop_overlay.html">
diff --git a/chrome/browser/resources/md_extensions/manager.js b/chrome/browser/resources/md_extensions/manager.js index 323636d..aa7e05b1 100644 --- a/chrome/browser/resources/md_extensions/manager.js +++ b/chrome/browser/resources/md_extensions/manager.js
@@ -35,10 +35,6 @@ const Manager = Polymer({ is: 'extensions-manager', - behaviors: [ - FindShortcutBehavior, - ], - properties: { canLoadUnpacked: { type: Boolean, @@ -586,20 +582,6 @@ this.showKioskDialog_ = false; }, // </if> - - // Override FindShortcutBehavior methods. - handleFindShortcut: function(modalContextOpen) { - if (modalContextOpen) { - return false; - } - this.$$('extensions-toolbar').focusOnSearchInput(); - return true; - }, - - // Override FindShortcutBehavior methods. - searchInputHasFocus: function() { - return this.$$('extensions-toolbar').getSearchField().isSearchFocused(); - }, }); return {Manager: Manager};
diff --git a/chrome/browser/resources/md_extensions/toolbar.js b/chrome/browser/resources/md_extensions/toolbar.js index cde60f0..4fc7895 100644 --- a/chrome/browser/resources/md_extensions/toolbar.js +++ b/chrome/browser/resources/md_extensions/toolbar.js
@@ -184,10 +184,6 @@ this.isUpdating_ = false; }); }, - - focusOnSearchInput: function() { - this.$$('cr-toolbar').getSearchField().showAndFocus(); - }, }); return {
diff --git a/chrome/browser/resources/omnibox/omnibox.css b/chrome/browser/resources/omnibox/omnibox.css index 1314695..e70be12 100644 --- a/chrome/browser/resources/omnibox/omnibox.css +++ b/chrome/browser/resources/omnibox/omnibox.css
@@ -4,9 +4,17 @@ @import url(omnibox_column_widths.css); -output-results-group:not([hidden]) { +[hidden] { + display: none !important; +} + +output-results-group { display: inline-block; - margin-bottom: 60px; + margin-top: 30px; +} + +output-results-details { + margin-bottom: 15px; } .check-mark, @@ -28,6 +36,9 @@ } body { + --action-color: rgb(66, 133, 244); + --border-color: #ddd; + background-color: #fcfcfc; overflow-y: scroll; } @@ -36,14 +47,16 @@ transition-duration: 300ms; } -table { - --border-color: #ccc; - --header-bg-color: #f8f8f8; +omnibox-output { --header-color: #555; - --row-hover-color: #f6f6f6; +} + +table { + --header-bg-color: #fafafa; + --row-hover-color: #fafafa; background-color: white; - border: 2px solid var(--border-color); + border: 1px solid var(--border-color); border-spacing: 0; font-size: 12px; min-width: 900px; @@ -51,58 +64,58 @@ width: 100%; } -thead tr { +.head tr { background-color: var(--header-bg-color); color: var(--header-color); font-size: 15px; text-align: left; } -tbody tr { +.body tr { height: 66px; } -tbody td > * { +.body td > * { display: block; max-height: 56px; } -tbody td * { +.body td * { overflow: hidden; word-break: break-all; } -tbody td:not(:hover) * { +.body td:not(:hover) * { text-overflow: ellipsis; white-space: nowrap; } -tbody td:hover * { +.body td:hover * { overflow-y: auto; } -tbody tr td pre.json, -tbody tr td pre.json * { +.body tr td pre.json, +.body tr td pre.json * { white-space: pre-wrap; } -tbody tr:hover td { +.body tr:hover td { background-color: var(--row-hover-color); } -th, td { padding: 0 5px; } th { - padding-bottom: 6px; - padding-top: 12px; + padding: 11px 5px 5px; } .header-container { display: block; overflow: hidden; + padding-bottom: 1px; + padding-top: 1px; text-overflow: ellipsis; white-space: nowrap; } @@ -150,7 +163,7 @@ font-weight: bold; } -tbody:not(:first-of-type) td.cell-provider-and-type .pair-item:first-child { +.body:not(:first-of-type) td.cell-provider-and-type .pair-item:first-child { font-size: 0; } @@ -165,11 +178,11 @@ } .cell-contents-and-description .pair-container { - margin-right: -15px; + margin-inline-end: -15px; } .cell-contents-and-description .pair-item { - margin-right: 15px; + margin-inline-end: 15px; } .cell-contents-and-description img.pair-item { @@ -186,14 +199,14 @@ } .cell-fill-and-inline .pair-container { - margin-right: -1px; + margin-inline-end: -1px; } .cell-fill-and-inline .pair-item { - margin-right: 1px; + margin-inline-end: 1px; } -.cell-fill-and-inline .pair-item.inline:not(:empty) { +.cell-fill-and-inline .pair-item:nth-child(2):not(:empty) { border: 1px solid; font-weight: bold; } @@ -203,13 +216,12 @@ } .cell-destination-url img { - margin-right: 5px; + margin-inline-end: 5px; vertical-align: middle; } .accesskey { text-decoration: underline; - text-transform: capitalize; } .header-provider-and-type .header-container :first-child { @@ -230,9 +242,10 @@ } omnibox-input { - --action-color: rgb(66, 133, 244); - --hover-color: #f2f2f2; + --hover-color: #f5f5f5; --input-alignment-indentation: 9px; + --row-icon-height: 20px; + --row-height: 26px; --text-active-color: #fff; --text-hover-color: #eee; --text-inactive-color: #f5f5f5; @@ -245,7 +258,7 @@ background-color: var(--text-hover-color); } -.checkbox-container:hover input ~ .checkbox-mark, +.checkbox-container:hover, .button:hover { background-color: var(--hover-color); } @@ -281,7 +294,7 @@ background-position-x: 5px; background-position-y: center; background-repeat: no-repeat; - background-size: 20px; + background-size: var(--row-icon-height); padding-left: 30px; } @@ -289,6 +302,7 @@ select { cursor: pointer; + height: var(--row-height); max-width: 250px; /* padded so that its text aligns with other elements and its height is the same as input elements' height */ @@ -298,42 +312,41 @@ /* stylized checkbox */ -.checkbox-container { +.checkbox-container, +.button { + align-items: center; + border-radius: 5px; cursor: pointer; - display: flex; - position: relative; + display: inline-flex; + min-height: var(--row-height); + padding-inline-end: var(--input-alignment-indentation); + padding-inline-start: var(--input-alignment-indentation); user-select: none; } input[type=checkbox] { - margin-left: 0; + margin-inline-start: 0; } -/* icon buttons */ - -.icon-button * { - vertical-align: middle; +.checkbox-container span { + align-self: center; } /* button */ .button { - border-radius: 5px; color: var(--action-color); - cursor: pointer; - display: inline-block; - padding: 3px var(--input-alignment-indentation); - user-select: none; } /* icons */ .icon { background-position: -2px center; - background-size: 20px; + background-size: var(--row-icon-height); display: inline-block; - height: 20px; - width: 20px; + height: var(--row-icon-height); + margin-inline-end: 3px; + width: var(--row-icon-height); } .search-icon { @@ -363,6 +376,8 @@ /* layout */ .top { + background-color: white; + border: 1px solid var(--border-color); display: flex; padding: 5px 0; } @@ -375,8 +390,9 @@ padding: 0 15px; } -.text, -.text-input { +omnibox-input, +input, +select { font-size: 14px; } @@ -385,11 +401,8 @@ margin-top: 3px; } -.unindented { - margin-left: calc(-1 * var(--input-alignment-indentation)); -} - .section-header { color: gray; font-size: 12px; + margin-inline-start: var(--input-alignment-indentation); }
diff --git a/chrome/browser/resources/omnibox/omnibox.html b/chrome/browser/resources/omnibox/omnibox.html index c39d5db..fb330a1 100644 --- a/chrome/browser/resources/omnibox/omnibox.html +++ b/chrome/browser/resources/omnibox/omnibox.html
@@ -19,16 +19,20 @@ <template id="omnibox-input-template"> <link rel="stylesheet" href="omnibox.css"> - <div class="top text"> + <div class="top"> <div class="top-column"> - <input id="input-text" type="text" class="row unindented input-icon-container search-icon" accesskey="l" autofocus autocomplete="off" placeholder="Enter omnibox input [Alt+L]"> + <input id="input-text" type="text" + class="row input-icon-container search-icon" accesskey="l" + autofocus autocomplete="off" + placeholder="Enter omnibox input [Alt+L]"> </div> <div class="top-column"> <p class="row section-header">Input parameters</p> <div class="row"> <label class="checkbox-container"> - <input id="reset-autocomplete-controller" type="checkbox" accesskey="r"> + <input id="reset-autocomplete-controller" type="checkbox" + accesskey="r"> <span> <span class="accesskey">R</span>eset autocomplete controller </span> @@ -38,7 +42,8 @@ <label class="checkbox-container"> <input id="lock-cursor-position" type="checkbox" accesskey="c"> <span> - Lock <span class="accesskey">c</span>ursor position to end of input + Lock <span class="accesskey">c</span>ursor + position to end of input </span> </label> </div> @@ -50,9 +55,10 @@ </span> </label> </div> - <div class="row indented"> + <div class="row"> <label class="checkbox-container"> - <input id="prevent-inline-autocomplete" type="checkbox" accesskey="a"> + <input id="prevent-inline-autocomplete" type="checkbox" + accesskey="a"> <span> Prevent inline <span class="accesskey">a</span>utocomplete </span> @@ -69,18 +75,26 @@ </div> <div class="top-column"> - <p class="row section-header">Current page c<span class="accesskey">o</span>ntext:</p> - <input id="current-url" type="text" class="row unindented" accesskey="u" placeholder="Enter current url (e.g. 'https://www.google.com') [Alt+U]" title="Mostly useful when zero-suggest is enabled, but may also affect clipboard and search providers."> - <select id="page-classification" class="row unindented" accesskey="o"> + <p class="row section-header"> + Current page c<span class="accesskey">o</span>ntext: + </p> + <input id="current-url" type="text" class="row" accesskey="u" + placeholder="Enter current url (e.g. 'https://www.google.com') [Alt+U]" + title="Mostly useful when zero-suggest is enabled, but may also affect clipboard and search providers."> + <select id="page-classification" class="row" accesskey="o"> <option value="0">Invalid spec</option> <option value="2">about:blank</option> <option value="3">user's home page</option> <option value="4" selected>arbitrary URL</option> - <option value="9">search result page not doing search term replacement</option> + <option value="9"> + search result page not doing search term replacement + </option> <option value="7">new tab page omnibox</option> <option value="8">new tab page fakebox</option> <option value="1">(OBSOLETE) new tab page</option> - <option value="6">(OBSOLETE) search result page doing search term replacement</option> + <option value="6"> + (OBSOLETE) search result page doing search term replacement + </option> </select> </div> @@ -114,15 +128,21 @@ <div class="top-column"> <p class="row section-header">Output controls</p> - <input id="filter-text" type="text" class="row unindented input-icon-container filter-icon" accesskey="g" autocomplete="off" placeholder="Enter filter (e.g. 'google', 'is:star', 'not:del') [Alt+G]" title="Checks each cell individually; i.e. filter text should not span multiple columns. Supports fuzzyness; each character of filter text must be present in the cell, either adjacent to the previous matched character, or at the start of a new word. Words are defined as being delimited by either capital letters, groups of digits, or non alpha characters. E.g. 'abc' matches 'abc', 'a big cat', 'a-bigCat', 'a very big cat', and 'an amBer cat'; but does not match 'abigcat' or 'an amber cat'. 'green rainbow' is matched by 'gre rain', but not by 'gre bow'. One exception is the first character, which may be matched mid-word. E.g. 'een rain' can also match 'green rainbow'. Boolean properties can be searched for via the property name prefixed by 'is:' or 'not:'. Boolean property names are: 'Can Be Default', 'Starred', 'Has Tab Match', 'Del', 'Prev', and 'Done'."> - <div class="row unindented"> - <span id="copy-text" class="icon-button button" title="Copy visible table in text format. This is affected by the visibility of ouput; i.e. toggling 'Show all details' affects what will be copied."> + <input id="filter-text" type="text" + class="row input-icon-container filter-icon" accesskey="g" + autocomplete="off" + placeholder="Enter filter (e.g. 'google', 'is:star', 'not:del') [Alt+G]" + title="Checks each cell individually; i.e. filter text should not span multiple columns. Supports fuzzyness; each character of filter text must be present in the cell, either adjacent to the previous matched character, or at the start of a new word. Words are defined as being delimited by either capital letters, groups of digits, or non alpha characters. E.g. 'abc' matches 'abc', 'a big cat', 'a-bigCat', 'a very big cat', and 'an amBer cat'; but does not match 'abigcat' or 'an amber cat'. 'green rainbow' is matched by 'gre rain', but not by 'gre bow'. One exception is the first character, which may be matched mid-word. E.g. 'een rain' can also match 'green rainbow'. Boolean properties can be searched for via the property name prefixed by 'is:' or 'not:'. Boolean property names are: 'Can Be Default', 'Starred', 'Has Tab Match', 'Del', 'Prev', and 'Done'."> + <div class="row"> + <span id="copy-text" class="button" + title="Copy visible table in text format. This is affected by the visibility of ouput; i.e. toggling 'Show all details' affects what will be copied."> <i class="icon copy-white-icon"></i> <span>Copy as text</span> </span> </div> - <div class="row unindented"> - <span id="copy-json" class="icon-button button" title="Copy responses in JSON format. This is not affected by the visibility of output and will copy responses in their entirety."> + <div class="row"> + <span id="copy-json" class="button" + title="Copy responses in JSON format. This is not affected by the visibility of output and will copy responses in their entirety."> <i class="icon copy-black-icon"></i> <span>Copy as JSON</span> </span> @@ -137,17 +157,45 @@ <div id="contents"></div> </template> - <template id="details-and-table-template"> + <template id="output-results-group-template"> <link rel="stylesheet" href="omnibox.css"> - <div id="details"></div> + <output-results-details></output-results-details> <table id="table"></table> </template> - <template id="details-template"> - <p>cursor position = <span class="cursor-position"></span></p> - <p>elapsed time = <span class="time"></span>ms</p> - <p>all providers done = <span class="done"></span></p> - <p>host = <span class="host"></span>, has isTypedHost = <span class="is-typed-host"></span></p> + <template id="output-results-details-template"> + <style> + :host { + background-color: white; + border: 1px solid var(--border-color); + display: block; + padding: 5px; + } + + .label { + color: var(--header-color); + margin-inline-end: 30px; + } + + .label > span { + color: var(--action-color); + font-weight: bold; + } + </style> + + <span class="label"> + cursor position = <span id="cursor-position"></span> + </span> + <span class="label"> + elapsed time = <span><span id="time"></span>ms</span> + </span> + <span class="label"> + all providers done = <span id="done"></span> + </span> + <span class="label">host = <span id="host"></span></span> + <span class="label"> + has isTypedHost = <span id="is-typed-host"></span> + </span> </template> <omnibox-input id="omnibox-input"></omnibox-input>
diff --git a/chrome/browser/resources/omnibox/omnibox_element.js b/chrome/browser/resources/omnibox/omnibox_element.js index 1e6a2749..2ca4b55f 100644 --- a/chrome/browser/resources/omnibox/omnibox_element.js +++ b/chrome/browser/resources/omnibox/omnibox_element.js
@@ -20,12 +20,12 @@ /** * Get an element that's known to exist within this OmniboxElement. - * Searches local shadow root for element by id. - * @param {string} id + * Searches local shadow root for element by query. + * @param {string} query * @return {!Element} */ - $$(id) { - return OmniboxElement.getById_(id, this.shadowRoot); + $$(query) { + return OmniboxElement.getByQuery_(query, this.shadowRoot); } /** @@ -34,21 +34,21 @@ * @return {!Element} */ static getTemplate(templateId) { - return OmniboxElement.getById_(templateId).content.cloneNode(true); + return OmniboxElement.getByQuery_('#' + templateId).content.cloneNode(true); } /** - * Get an element that's known to exist by its ID. We use this instead of just - * calling getElementById because this lets us satisfy the JSCompiler type + * Get an element that's known to exist by query. We use this instead of just + * calling querySelector because this lets us satisfy the JSCompiler type * system. * @private - * @param {string} id + * @param {string} query * @param {!Node=} context * @return {!Element} */ - static getById_(id, context) { + static getByQuery_(query, context) { return assertInstanceof( - (context || document).getElementById(id), Element, - `Missing required element: ${id}`); + (context || document).querySelector(query), Element, + `Missing required element: ${query}`); } }
diff --git a/chrome/browser/resources/omnibox/omnibox_input.js b/chrome/browser/resources/omnibox/omnibox_input.js index 85f5f95..f259d10 100644 --- a/chrome/browser/resources/omnibox/omnibox_input.js +++ b/chrome/browser/resources/omnibox/omnibox_input.js
@@ -28,10 +28,10 @@ super('omnibox-input-template'); const displayInputs = OmniboxInput.defaultDisplayInputs; - this.$$('show-incomplete-results').checked = + this.$$('#show-incomplete-results').checked = displayInputs.showIncompleteResults; - this.$$('show-details').checked = displayInputs.showDetails; - this.$$('show-all-providers').checked = displayInputs.showAllProviders; + this.$$('#show-details').checked = displayInputs.showDetails; + this.$$('#show-all-providers').checked = displayInputs.showAllProviders; } /** @override */ @@ -41,55 +41,56 @@ /** @private */ setupElementListeners_() { - ['input-text', - 'reset-autocomplete-controller', - 'lock-cursor-position', - 'zero-suggest', - 'prevent-inline-autocomplete', - 'prefer-keyword', - 'current-url', - 'page-classification', + ['#input-text', + '#reset-autocomplete-controller', + '#lock-cursor-position', + '#zero-suggest', + '#prevent-inline-autocomplete', + '#prefer-keyword', + '#current-url', + '#page-classification', ] .forEach( - id => this.$$(id).addEventListener( + query => this.$$(query).addEventListener( 'input', this.onQueryInputsChanged_.bind(this))); - ['show-incomplete-results', - 'show-details', - 'show-all-providers', + ['#show-incomplete-results', + '#show-details', + '#show-all-providers', ] .forEach( - id => this.$$(id).addEventListener( + query => this.$$(query).addEventListener( 'input', this.onDisplayInputsChanged_.bind(this))); - this.$$('copy-text') + this.$$('#copy-text') .addEventListener('click', () => this.onCopyOutput_('text')); - this.$$('copy-json') + this.$$('#copy-json') .addEventListener('click', () => this.onCopyOutput_('json')); - this.$$('filter-text') + this.$$('#filter-text') .addEventListener('input', this.onFilterInputsChanged_.bind(this)); } /** @private */ onQueryInputsChanged_() { - const zeroSuggest = this.$$('zero-suggest').checked; - this.$$('current-url').disabled = zeroSuggest; + const zeroSuggest = this.$$('#zero-suggest').checked; + this.$$('#current-url').disabled = zeroSuggest; if (zeroSuggest) { - this.$$('current-url').value = this.$$('input-text').value; + this.$$('#current-url').value = this.$$('#input-text').value; } /** @type {!QueryInputs} */ const queryInputs = { - inputText: this.$$('input-text').value, + inputText: this.$$('#input-text').value, resetAutocompleteController: - this.$$('reset-autocomplete-controller').checked, + this.$$('#reset-autocomplete-controller').checked, cursorPosition: this.cursorPosition_, zeroSuggest: zeroSuggest, - preventInlineAutocomplete: this.$$('prevent-inline-autocomplete').checked, - preferKeyword: this.$$('prefer-keyword').checked, - currentUrl: this.$$('current-url').value, - pageClassification: this.$$('page-classification').value, + preventInlineAutocomplete: + this.$$('#prevent-inline-autocomplete').checked, + preferKeyword: this.$$('#prefer-keyword').checked, + currentUrl: this.$$('#current-url').value, + pageClassification: this.$$('#page-classification').value, }; this.dispatchEvent( new CustomEvent('query-inputs-changed', {detail: queryInputs})); @@ -99,9 +100,9 @@ onDisplayInputsChanged_() { /** @type {!DisplayInputs} */ const displayInputs = { - showIncompleteResults: this.$$('show-incomplete-results').checked, - showDetails: this.$$('show-details').checked, - showAllProviders: this.$$('show-all-providers').checked, + showIncompleteResults: this.$$('#show-incomplete-results').checked, + showDetails: this.$$('#show-details').checked, + showAllProviders: this.$$('#show-all-providers').checked, }; this.dispatchEvent( new CustomEvent('display-inputs-changed', {detail: displayInputs})); @@ -110,7 +111,7 @@ /** @private */ onFilterInputsChanged_() { this.dispatchEvent(new CustomEvent( - 'filter-input-changed', {detail: this.$$('filter-text').value})); + 'filter-input-changed', {detail: this.$$('#filter-text').value})); } /** @private @param {string} format Either 'text' or 'json'. */ @@ -120,9 +121,9 @@ /** @private @return {number} */ get cursorPosition_() { - return this.$$('lock-cursor-position').checked ? - this.$$('input-text').value.length : - this.$$('input-text').selectionEnd; + return this.$$('#lock-cursor-position').checked ? + this.$$('#input-text').value.length : + this.$$('#input-text').selectionEnd; } /** @return {DisplayInputs} */
diff --git a/chrome/browser/resources/omnibox/omnibox_output.js b/chrome/browser/resources/omnibox/omnibox_output.js index 2d11acc1..9524d14 100644 --- a/chrome/browser/resources/omnibox/omnibox_output.js +++ b/chrome/browser/resources/omnibox/omnibox_output.js
@@ -3,6 +3,17 @@ // found in the LICENSE file. cr.define('omnibox_output', function() { + /** + * @typedef {{ + * cursorPosition: number, + * time: number, + * done: boolean, + * host: string, + * isTypedHost: boolean, + * }} + */ + let ResultsDetails; + /** @param {!Element} element*/ function clearChildren(element) { while (element.firstChild) { @@ -49,7 +60,7 @@ clearAutocompleteResponses() { this.responses = []; this.resultsGroups_ = []; - clearChildren(this.$$('contents')); + clearChildren(this.$$('#contents')); } /** @param {!mojom.OmniboxResult} response */ @@ -59,7 +70,7 @@ const resultsGroup = OutputResultsGroup.create(response, this.queryInputs_.cursorPosition); this.resultsGroups_.push(resultsGroup); - this.$$('contents').appendChild(resultsGroup); + this.$$('#contents').appendChild(resultsGroup); this.updateVisibility_(); this.updateFilterHighlights_(); @@ -137,7 +148,7 @@ } constructor() { - super('details-and-table-template'); + super('output-results-group-template'); } /** @@ -145,11 +156,8 @@ * @param {number} cursorPosition */ setResultsGroup(resultsGroup, cursorPosition) { - /** - * @type {{cursorPosition: number, time: number, done: boolean, host: - * string, isTypedHost: boolean}} - */ - this.details = { + /** @private {ResultsDetails} */ + this.details_ = { cursorPosition: cursorPosition, time: resultsGroup.timeSinceOmniboxStartedMs, done: resultsGroup.done, @@ -183,33 +191,25 @@ /** @private {!Array<!Element>} */ this.innerHeaders_ = []; - this.$$('details').appendChild(this.renderDetails_()); - this.$$('table').appendChild(this.renderHeader_()); - this.$$('table').appendChild(this.combinedResults); + customElements.whenDefined(this.$$('output-results-details').localName) + .then( + () => + this.$$('output-results-details').setDetails(this.details_)); + + this.$$('#table').appendChild(this.renderHeader_()); + this.$$('#table').appendChild(this.combinedResults); this.individualResultsList.forEach(results => { const innerHeader = this.renderInnerHeader_(results); this.innerHeaders_.push(innerHeader); - this.$$('table').appendChild(innerHeader); - this.$$('table').appendChild(results); + this.$$('#table').appendChild(innerHeader); + this.$$('#table').appendChild(results); }); } /** @private @return {!Element} */ - renderDetails_() { - const details = OmniboxElement.getTemplate('details-template'); - details.querySelector('.cursor-position').textContent = - this.details.cursorPosition; - details.querySelector('.time').textContent = this.details.time; - details.querySelector('.done').textContent = this.details.done; - details.querySelector('.host').textContent = this.details.host; - details.querySelector('.is-typed-host').textContent = - this.details.isTypedHost; - return details; - } - - /** @private @return {!Element} */ renderHeader_() { const head = document.createElement('thead'); + head.classList.add('head'); const row = document.createElement('tr'); this.headers.forEach(cell => row.appendChild(cell)); head.appendChild(row); @@ -222,7 +222,8 @@ * @return {!Element} */ renderInnerHeader_(results) { - const head = document.createElement('thead'); + const head = document.createElement('tbody'); + head.classList.add('head'); const row = document.createElement('tr'); const cell = document.createElement('th'); // Reserve 1 more column for showing the additional properties column. @@ -241,7 +242,8 @@ updateVisibility(showIncompleteResults, showDetails, showAllProviders) { // Show the details section above each table if showDetails or // showIncompleteResults are true. - this.$$('details').hidden = !showDetails && !showIncompleteResults; + this.$$('output-results-details').hidden = + !showDetails && !showIncompleteResults; // Show individual results when showAllProviders is true. this.individualResultsList.forEach( @@ -282,6 +284,21 @@ } } + class OutputResultsDetails extends OmniboxElement { + constructor() { + super('output-results-details-template'); + } + + /** @param {ResultsDetails} details */ + setDetails(details) { + this.$$('#cursor-position').textContent = details.cursorPosition; + this.$$('#time').textContent = details.time; + this.$$('#done').textContent = details.done; + this.$$('#host').textContent = details.host; + this.$$('#is-typed-host').textContent = details.isTypedHost; + } + } + /** * Helps track and render a list of results. Each result is tracked and * rendered by OutputMatch below. @@ -299,6 +316,7 @@ constructor() { super(); + this.classList.add('body'); /** @type {!Array<!OutputMatch>} */ this.matches = []; } @@ -957,10 +975,11 @@ customElements.define('omnibox-output', OmniboxOutput); customElements.define('output-results-group', OutputResultsGroup); + customElements.define('output-results-details', OutputResultsDetails); customElements.define( 'output-results-table', OutputResultsTable, {extends: 'tbody'}); customElements.define('output-match', OutputMatch, {extends: 'tr'}); - customElements.define('output-haeder', OutputHeader, {extends: 'th'}); + customElements.define('output-header', OutputHeader, {extends: 'th'}); customElements.define( 'output-pair-property', OutputPairProperty, {extends: 'td'}); customElements.define(
diff --git a/chrome/browser/resources/print_preview/BUILD.gn b/chrome/browser/resources/print_preview/BUILD.gn index 21b63b9e..6e21770 100644 --- a/chrome/browser/resources/print_preview/BUILD.gn +++ b/chrome/browser/resources/print_preview/BUILD.gn
@@ -91,6 +91,7 @@ js_library("cloud_print_interface") { deps = [ "//ui/webui/resources/js:cr", + "//ui/webui/resources/js/cr:event_target", ] } @@ -100,7 +101,6 @@ ":cloud_print_interface_js", ":cloud_print_interface_native", ":native_layer", - "data:user_info", ] } @@ -112,8 +112,8 @@ "data:destination", "data:document_info", "data:invitation", - "data:user_info", "//ui/webui/resources/js:cr", + "//ui/webui/resources/js/cr:event_target", ] }
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface.js b/chrome/browser/resources/print_preview/cloud_print_interface.js index 3b8c9d44..319b8e7 100644 --- a/chrome/browser/resources/print_preview/cloud_print_interface.js +++ b/chrome/browser/resources/print_preview/cloud_print_interface.js
@@ -18,6 +18,7 @@ SEARCH_FAILED: 'cloudprint.CloudPrintInterface.SEARCH_FAILED', SUBMIT_DONE: 'cloudprint.CloudPrintInterface.SUBMIT_DONE', SUBMIT_FAILED: 'cloudprint.CloudPrintInterface.SUBMIT_FAILED', + UPDATE_USERS: 'cloudprint.CloudPrintInterface.UPDATE_USERS', }; /**
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface_js.js b/chrome/browser/resources/print_preview/cloud_print_interface_js.js index d6d9bd6f..f3b30a6 100644 --- a/chrome/browser/resources/print_preview/cloud_print_interface_js.js +++ b/chrome/browser/resources/print_preview/cloud_print_interface_js.js
@@ -16,11 +16,10 @@ * 'https://www.google.com/cloudprint'. * @param {!print_preview.NativeLayer} nativeLayer Native layer used to get * Auth2 tokens. - * @param {!print_preview.UserInfo} userInfo User information repository. * @param {boolean} isInAppKioskMode Whether the print preview is in App * Kiosk mode. */ - constructor(baseUrl, nativeLayer, userInfo, isInAppKioskMode) { + constructor(baseUrl, nativeLayer, isInAppKioskMode) { /** * The base URL of the Google Cloud Print API. * @private {string} @@ -34,12 +33,6 @@ this.nativeLayer_ = nativeLayer; /** - * User information repository. - * @private {!print_preview.UserInfo} - */ - this.userInfo_ = userInfo; - - /** * Whether Print Preview is in App Kiosk mode, basically, use only * printers available for the device. * @private {boolean} @@ -309,6 +302,20 @@ } /** + * Fires an event with information about the new active user and logged in + * users. + * @param {string} activeUser The active user account. + * @param {Array<string>=} users The currently logged in users. Omitted + * if the list of users has not changed. + * @private + */ + dispatchUserUpdateEvent_(activeUser, users) { + this.eventTarget_.dispatchEvent(new CustomEvent( + CloudPrintInterfaceEventType.UPDATE_USERS, + {detail: {activeUser: activeUser, users: users}})); + } + + /** * Updates user info and session index from the {@code request} response. * @param {!cloudprint.CloudPrintRequest} request Request to extract user * info from. @@ -321,7 +328,7 @@ for (let i = 0; i < users.length; i++) { this.userSessionIndex_[users[i]] = i; } - this.userInfo_.setUsers(request.result['request']['user'], users); + this.dispatchUserUpdateEvent_(request.result['request']['user'], users); } } @@ -547,7 +554,7 @@ // In case the user account is known, but not the primary one, // activate it. if (this.userSessionIndex_[request.account] > 0) { - this.userInfo_.activeUser = request.account; + this.dispatchUserUpdateEvent_(request.result['request']['user']); // Repeat the request for the newly activated account. this.printer( request.result['request']['params']['printerid'], request.origin,
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface_manager.html b/chrome/browser/resources/print_preview/cloud_print_interface_manager.html index c633da23..31eac5d 100644 --- a/chrome/browser/resources/print_preview/cloud_print_interface_manager.html +++ b/chrome/browser/resources/print_preview/cloud_print_interface_manager.html
@@ -5,7 +5,6 @@ <link rel="import" href="data/destination.html"> <link rel="import" href="data/document_info.html"> <link rel="import" href="data/invitation.html"> -<link rel="import" href="data/user_info.html"> <script src="cloud_print_interface_js.js"></script> <script src="cloud_print_interface_native.js"></script>
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface_manager.js b/chrome/browser/resources/print_preview/cloud_print_interface_manager.js index c4522f1..fcf7923 100644 --- a/chrome/browser/resources/print_preview/cloud_print_interface_manager.js +++ b/chrome/browser/resources/print_preview/cloud_print_interface_manager.js
@@ -13,19 +13,17 @@ * with no trailing slash. For example, * 'https://www.google.com/cloudprint'. * @param {!print_preview.NativeLayer} nativeLayer Native layer instance. - * @param {!print_preview.UserInfo} userInfo User information repository. * @param {boolean} isInAppKioskMode Whether the print preview is in App * Kiosk mode. * @return {!cloudprint.CloudPrintInterface} */ - function getCloudPrintInterface( - baseUrl, nativeLayer, userInfo, isInAppKioskMode) { + function getCloudPrintInterface(baseUrl, nativeLayer, isInAppKioskMode) { if (instance === null) { if (loadTimeData.getBoolean('cloudPrinterHandlerEnabled')) { instance = new cloudprint.CloudPrintInterfaceNative(); } else { instance = new cloudprint.CloudPrintInterfaceJS( - baseUrl, nativeLayer, userInfo, isInAppKioskMode); + baseUrl, nativeLayer, isInAppKioskMode); } } return instance;
diff --git a/chrome/browser/resources/print_preview/data/BUILD.gn b/chrome/browser/resources/print_preview/data/BUILD.gn index 080d53e..3d9fdb7d 100644 --- a/chrome/browser/resources/print_preview/data/BUILD.gn +++ b/chrome/browser/resources/print_preview/data/BUILD.gn
@@ -19,7 +19,6 @@ ":measurement_system", ":printable_area", ":size", - ":user_info", ] } @@ -28,7 +27,6 @@ ":destination", ":destination_match", ":local_parsers", - ":user_info", "..:cloud_print_interface", "..:metrics", "..:native_layer", @@ -41,7 +39,6 @@ js_library("invitation_store") { deps = [ ":invitation", - ":user_info", "..:cloud_print_interface", "//ui/webui/resources/js:event_tracker", "//ui/webui/resources/js/cr:event_target", @@ -132,7 +129,9 @@ js_library("user_info") { deps = [ - "//ui/webui/resources/js:cr", - "//ui/webui/resources/js/cr:event_target", + ":destination_store", + ":invitation_store", + "..:cloud_print_interface", + "//ui/webui/resources/js:event_tracker", ] }
diff --git a/chrome/browser/resources/print_preview/data/destination_store.html b/chrome/browser/resources/print_preview/data/destination_store.html index a62c21e7..e66bea9 100644 --- a/chrome/browser/resources/print_preview/data/destination_store.html +++ b/chrome/browser/resources/print_preview/data/destination_store.html
@@ -5,6 +5,5 @@ <link rel="import" href="destination.html"> <link rel="import" href="destination_match.html"> <link rel="import" href="local_parsers.html"> -<link rel="import" href="user_info.html"> <script src="destination_store.js"></script>
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js index e67d3ba..8831d42 100644 --- a/chrome/browser/resources/print_preview/data/destination_store.js +++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -129,11 +129,10 @@ /** * A data store that stores destinations and dispatches events when the * data store changes. - * @param {!print_preview.UserInfo} userInfo User information repository. * @param {function(string, !Function):void} addListenerCallback Function * to call to add Web UI listeners in DestinationStore constructor. */ - constructor(userInfo, addListenerCallback) { + constructor(addListenerCallback) { super(); /** @@ -143,12 +142,6 @@ this.nativeLayer_ = print_preview.NativeLayer.getInstance(); /** - * User information repository. - * @private {!print_preview.UserInfo} - */ - this.userInfo_ = userInfo; - - /** * Used to track metrics. * @private {!print_preview.MetricsContext} */ @@ -212,6 +205,12 @@ this.cloudPrintInterface_ = null; /** + * Currently active user. + * @private {string} + */ + this.activeUser_ = ''; + + /** * Maps user account to the list of origins for which destinations are * already loaded. * @private {!Object<Array<!print_preview.DestinationOrigin>>} @@ -856,25 +855,34 @@ } /** + * Updates the current active user account. + * @param {string} activeUser + */ + setActiveUser(activeUser) { + this.activeUser_ = activeUser; + } + + /** * Initiates loading of cloud destinations. * @param {print_preview.DestinationOrigin=} opt_origin Search destinations * for the specified origin only. */ startLoadCloudDestinations(opt_origin) { if (this.cloudPrintInterface_ != null) { - const origins = - this.loadedCloudOrigins_[this.userInfo_.activeUser] || []; + const origins = this.loadedCloudOrigins_[this.activeUser_] || []; if (origins.length == 0 || (opt_origin && origins.indexOf(opt_origin) < 0)) { - this.cloudPrintInterface_.search( - this.userInfo_.activeUser, opt_origin); + this.cloudPrintInterface_.search(this.activeUser_, opt_origin); } } } - /** Requests load of COOKIE based cloud destinations. */ - reloadUserCookieBasedDestinations() { - const origins = this.loadedCloudOrigins_[this.userInfo_.activeUser] || []; + /** + * Requests load of COOKIE based cloud destinations for |account|. + * @param {string} account + */ + reloadUserCookieBasedDestinations(account) { + const origins = this.loadedCloudOrigins_[account] || []; if (origins.indexOf(print_preview.DestinationOrigin.COOKIES) >= 0) { this.dispatchEvent(new CustomEvent( DestinationStore.EventType.DESTINATION_SEARCH_DONE)); @@ -1169,8 +1177,10 @@ assert(origin === print_preview.DestinationOrigin.EXTENSION); return; } - dest = print_preview.parseDestination( - print_preview.originToType(origin), assert(settingsInfo.printer)); + dest = /** @type {!print_preview.Destination} */ ( + print_preview.parseDestination( + print_preview.originToType(origin), + assert(settingsInfo.printer))); } if (dest) { if ((origin === print_preview.DestinationOrigin.LOCAL || @@ -1181,15 +1191,8 @@ // as the user does not change to a new non-recent destination. return; } - const updateDestination = destination => { - destination.capabilities = settingsInfo.capabilities; - this.updateDestination_(destination); - }; - if (Array.isArray(dest)) { - dest.forEach(updateDestination); - } else { - updateDestination(dest); - } + dest.capabilities = settingsInfo.capabilities; + this.updateDestination_(dest); } } @@ -1321,15 +1324,15 @@ if (type == print_preview.PrinterType.PRIVET_PRINTER) { const printer = /** !print_preview.PrivetPrinterDescription */ (printers[0]); - if (printer.serviceName == this.waitForRegisterDestination_ && - !printer.isUnregistered) { + if (printer.serviceName == this.waitForRegisterDestination_) { this.waitForRegisterDestination_ = null; this.onDestinationsReload(); return; } } this.insertDestinations_(printers.map( - printer => print_preview.parseDestination(type, printer))); + printer => /** @type {!print_preview.Destination} */ ( + print_preview.parseDestination(type, printer)))); if (this.selectFirstDestination_) { this.selectDestination(this.destinations_[0]);
diff --git a/chrome/browser/resources/print_preview/data/invitation_store.html b/chrome/browser/resources/print_preview/data/invitation_store.html index ce123196..c2dd17bd 100644 --- a/chrome/browser/resources/print_preview/data/invitation_store.html +++ b/chrome/browser/resources/print_preview/data/invitation_store.html
@@ -1,7 +1,6 @@ <link rel="import" href="chrome://resources/html/cr/event_target.html"> <link rel="import" href="chrome://resources/html/event_tracker.html"> <link rel="import" href="invitation.html"> -<link rel="import" href="user_info.html"> <link rel="import" href="../cloud_print_interface.html"> <script src="invitation_store.js"></script>
diff --git a/chrome/browser/resources/print_preview/data/invitation_store.js b/chrome/browser/resources/print_preview/data/invitation_store.js index dd6d613..014ceeae 100644 --- a/chrome/browser/resources/print_preview/data/invitation_store.js +++ b/chrome/browser/resources/print_preview/data/invitation_store.js
@@ -18,20 +18,11 @@ 'use strict'; class InvitationStore extends cr.EventTarget { - /** - * Printer sharing invitations data store. - * @param {!print_preview.UserInfo} userInfo User information repository. - */ - constructor(userInfo) { + /** Printer sharing invitations data store. */ + constructor() { super(); /** - * User information repository. - * @private {!print_preview.UserInfo} - */ - this.userInfo_ = userInfo; - - /** * Maps user account to the list of invitations for this account. * @private {!Object<!Array<!print_preview.Invitation>>} */ @@ -103,16 +94,17 @@ this.onCloudPrintProcessInviteDone_.bind(this)); } - /** Initiates loading of cloud printer sharing invitations. */ - startLoadingInvitations() { + /** + * Initiates loading of cloud printer sharing invitations for the user + * account given by |user|. + * @param {string} user The user to load invitations for. + */ + startLoadingInvitations(user) { if (!this.cloudPrintInterface_) { return; } - if (!this.userInfo_.activeUser) { - return; - } - if (this.loadStatus_.hasOwnProperty(this.userInfo_.activeUser)) { - if (this.loadStatus_[this.userInfo_.activeUser] == + if (this.loadStatus_.hasOwnProperty(user)) { + if (this.loadStatus_[user] == print_preview.InvitationStoreLoadStatus.DONE) { this.dispatchEvent(new CustomEvent( InvitationStore.EventType.INVITATION_SEARCH_DONE)); @@ -120,9 +112,9 @@ return; } - this.loadStatus_[this.userInfo_.activeUser] = + this.loadStatus_[user] = print_preview.InvitationStoreLoadStatus.IN_PROGRESS; - this.cloudPrintInterface_.invites(this.userInfo_.activeUser); + this.cloudPrintInterface_.invites(user); } /**
diff --git a/chrome/browser/resources/print_preview/data/local_parsers.js b/chrome/browser/resources/print_preview/data/local_parsers.js index cfc2b69..e986f7c 100644 --- a/chrome/browser/resources/print_preview/data/local_parsers.js +++ b/chrome/browser/resources/print_preview/data/local_parsers.js
@@ -14,7 +14,8 @@ * For LOCAL_PRINTER => print_preview.LocalDestinationInfo * For PRIVET_PRINTER => print_preview.PrivetPrinterDescription * For EXTENSION_PRINTER => print_preview.ProvisionalDestinationInfo - * @return {!Array<!print_preview.Destination> | !print_preview.Destination} + * @return {?print_preview.Destination} Only returns null if an invalid value + * is provided for |type|. */ function parseDestination(type, printer) { if (type === print_preview.PrinterType.LOCAL_PRINTER) { @@ -30,7 +31,7 @@ /** @type {!print_preview.ProvisionalDestinationInfo} */ (printer)); } assertNotReached('Unknown printer type ' + type); - return []; + return null; } /** @@ -61,33 +62,17 @@ } /** - * Parses a privet destination as one or more local printers. + * Parses a privet destination as a local printer. * @param {!print_preview.PrivetPrinterDescription} destinationInfo Object * that describes a privet printer. - * @return {!print_preview.Destination | - * !Array<!print_preview.Destination>} Parsed destination info. + * @return {!print_preview.Destination} Parsed destination info. */ function parsePrivetDestination(destinationInfo) { - const returnedPrinters = []; - - if (destinationInfo.hasLocalPrinting) { - returnedPrinters.push(new print_preview.Destination( - destinationInfo.serviceName, print_preview.DestinationType.LOCAL, - print_preview.DestinationOrigin.PRIVET, destinationInfo.name, - false /*isRecent*/, print_preview.DestinationConnectionStatus.ONLINE, - {cloudID: destinationInfo.cloudID})); - } - - if (destinationInfo.isUnregistered) { - returnedPrinters.push(new print_preview.Destination( - destinationInfo.serviceName, print_preview.DestinationType.GOOGLE, - print_preview.DestinationOrigin.PRIVET, destinationInfo.name, - false /*isRecent*/, - print_preview.DestinationConnectionStatus.UNREGISTERED)); - } - - return returnedPrinters.length === 1 ? returnedPrinters[0] : - returnedPrinters; + return new print_preview.Destination( + destinationInfo.serviceName, print_preview.DestinationType.LOCAL, + print_preview.DestinationOrigin.PRIVET, destinationInfo.name, + false /*isRecent*/, print_preview.DestinationConnectionStatus.ONLINE, + {cloudID: destinationInfo.cloudID}); } /**
diff --git a/chrome/browser/resources/print_preview/data/user_info.html b/chrome/browser/resources/print_preview/data/user_info.html index e9ea0b3..7b18056 100644 --- a/chrome/browser/resources/print_preview/data/user_info.html +++ b/chrome/browser/resources/print_preview/data/user_info.html
@@ -1,4 +1,8 @@ -<link rel="import" href="chrome://resources/html/cr.html"> -<link rel="import" href="chrome://resources/html/cr/event_target.html"> +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/html/event_tracker.html"> +<link rel="import" href="../cloud_print_interface.html"> +<link rel="import" href="destination_store.html"> +<link rel="import" href="invitation_store.html"> <script src="user_info.js"></script>
diff --git a/chrome/browser/resources/print_preview/data/user_info.js b/chrome/browser/resources/print_preview/data/user_info.js index 46d62bb7..6fce2b9 100644 --- a/chrome/browser/resources/print_preview/data/user_info.js +++ b/chrome/browser/resources/print_preview/data/user_info.js
@@ -2,90 +2,88 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -cr.define('print_preview', function() { - 'use strict'; +(function() { +'use strict'; - class UserInfo extends cr.EventTarget { - /** - * Repository which stores information about the user. Events are dispatched - * when the information changes. - */ - constructor() { - super(); +/** + * @typedef {{ activeUser: string, + * users: (!Array<string> | undefined) }} + */ +let UpdateUsersPayload; - /** - * Email address of the logged in user or {@code null} if no user is - * logged in. In case of Google multilogin, can be changed by the user. - * @private {?string} - */ - this.activeUser_ = null; +Polymer({ + is: 'print-preview-user-info', - /** - * Email addresses of the logged in users or empty array if no user is - * logged in. {@code null} if not known yet. - * @private {?Array<string>} - */ - this.users_ = null; + properties: { + activeUser: { + type: String, + notify: true, + value: '', + }, + + /** @type {?cloudprint.CloudPrintInterface} */ + cloudPrintInterface: { + type: Object, + observer: 'onCloudPrintInterfaceSet_', + }, + + /** @type {?print_preview.DestinationStore} */ + destinationStore: Object, + + /** @type {?print_preview.InvitationStore} */ + invitationStore: Object, + + /** @type {!Array<string>} */ + users: { + type: Array, + notify: true, + value: function() { + return []; + }, + }, + }, + + /** @private {!EventTracker} */ + tracker_: new EventTracker(), + + /** @private */ + onCloudPrintInterfaceSet_: function() { + if (!this.cloudPrintInterface) { + return; } - /** @return {boolean} Whether user accounts are already retrieved. */ - get initialized() { - return this.users_ != null; - } - - /** @return {boolean} Whether user is logged in or not. */ - get loggedIn() { - return !!this.activeUser; - } - - /** - * @return {?string} Email address of the logged in user or {@code null} if - * no user is logged. - */ - get activeUser() { - return this.activeUser_; - } - - /** - * Changes active user. - * @param {?string} activeUser Email address for the user to be set as - * active. - */ - set activeUser(activeUser) { - if (!!activeUser && this.activeUser_ != activeUser) { - this.activeUser_ = activeUser; - cr.dispatchSimpleEvent(this, UserInfo.EventType.ACTIVE_USER_CHANGED); - } - } - - /** - * @return {?Array<string>} Email addresses of the logged in users or - * empty array if no user is logged in. {@code null} if not known yet. - */ - get users() { - return this.users_; - } - - /** - * Sets logged in user accounts info. - * @param {string} activeUser Active user account (email). - * @param {!Array<string>} users List of currently logged in accounts. - */ - setUsers(activeUser, users) { - this.activeUser_ = activeUser; - this.users_ = users || []; - cr.dispatchSimpleEvent(this, UserInfo.EventType.USERS_CHANGED); - } - } + this.tracker_.add( + assert(this.cloudPrintInterface).getEventTarget(), + cloudprint.CloudPrintInterfaceEventType.UPDATE_USERS, + this.updateUsers_.bind(this)); + }, /** - * Enumeration of event types dispatched by the user info. - * @enum {string} + * @param {string} user The new active user. + * @private */ - UserInfo.EventType = { - ACTIVE_USER_CHANGED: 'print_preview.UserInfo.ACTIVE_USER_CHANGED', - USERS_CHANGED: 'print_preview.UserInfo.USERS_CHANGED' - }; + setActiveUser_: function(user) { + this.destinationStore.setActiveUser(user); + this.activeUser = user; + }, - return {UserInfo: UserInfo}; + /** + * @param {!CustomEvent<!UpdateUsersPayload>} e Event containing the new + * active user and users. + * @private + */ + updateUsers_: function(e) { + this.setActiveUser_(e.detail.activeUser); + if (e.detail.users) { + this.users = e.detail.users; + } + }, + + /** @param {string} user The new active user. */ + updateActiveUser: function(user) { + this.setActiveUser_(user); + this.destinationStore.reloadUserCookieBasedDestinations(user); + this.invitationStore.startLoadingInvitations(user); + }, }); +})();
diff --git a/chrome/browser/resources/print_preview/new/BUILD.gn b/chrome/browser/resources/print_preview/new/BUILD.gn index 3ab301f..9c71426 100644 --- a/chrome/browser/resources/print_preview/new/BUILD.gn +++ b/chrome/browser/resources/print_preview/new/BUILD.gn
@@ -99,7 +99,6 @@ "../data:destination", "../data:destination_store", "../data:invitation_store", - "../data:user_info", "//ui/webui/resources/cr_elements/cr_lazy_render:cr_lazy_render", "//ui/webui/resources/js:i18n_behavior", ] @@ -296,7 +295,6 @@ "../data:destination_store", "../data:invitation", "../data:invitation_store", - "../data:user_info", "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", "//ui/webui/resources/js:event_tracker", "//ui/webui/resources/js:i18n_behavior",
diff --git a/chrome/browser/resources/print_preview/new/app.html b/chrome/browser/resources/print_preview/new/app.html index adf6563..eb96401c 100644 --- a/chrome/browser/resources/print_preview/new/app.html +++ b/chrome/browser/resources/print_preview/new/app.html
@@ -6,7 +6,6 @@ <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/load_time_data.html"> <link rel="import" href="chrome://resources/html/cr/ui/focus_outline_manager.html"> -<link rel="import" href="chrome://resources/html/event_tracker.html"> <link rel="import" href="chrome://resources/html/util.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> @@ -91,6 +90,11 @@ recent-destinations="{{recentDestinations_}}" on-save-sticky-settings="onSaveStickySettings_"> </print-preview-model> + <print-preview-user-info id="userInfo" active-user="{{activeUser_}}" + users="{{users_}}" cloud-print-interface="[[cloudPrintInterface_]]" + destination-store="[[destinationStore_]]" + invitation-store="[[invitationStore_]]"> + </print-preview-user-info> <print-preview-document-info id="documentInfo" document-settings="{{documentSettings_}}" margins="{{margins_}}" page-size="{{pageSize_}}"> @@ -108,7 +112,9 @@ invitation-store="[[invitationStore_]]" disabled="[[controlsDisabled_]]" state="[[state]]" recent-destinations="[[recentDestinations_]]" - user-info="{{userInfo_}}" available class="settings-section"> + active-user="[[activeUser_]]" users="[[users_]]" + available class="settings-section" + on-account-change="onAccountChange_"> </print-preview-destination-settings> <print-preview-pages-settings settings="{{settings}}" page-count="[[documentSettings_.pageCount]]"
diff --git a/chrome/browser/resources/print_preview/new/app.js b/chrome/browser/resources/print_preview/new/app.js index cbb88a2..5364d44 100644 --- a/chrome/browser/resources/print_preview/new/app.js +++ b/chrome/browser/resources/print_preview/new/app.js
@@ -31,6 +31,22 @@ notify: true, }, + /** @type {!print_preview_new.State} */ + state: { + type: Number, + observer: 'onStateChanged_', + }, + + /** @private {string} */ + activeUser_: String, + + /** @private {boolean} */ + controlsDisabled_: { + type: Boolean, + notify: true, + computed: 'computeControlsDisabled_(state)', + }, + /** @private {print_preview.Destination} */ destination_: { type: Object, @@ -56,32 +72,6 @@ /** @private {!print_preview.PrintableArea} */ printableArea_: Object, - /** @private {?print_preview.InvitationStore} */ - invitationStore_: { - type: Object, - notify: true, - value: null, - }, - - /** @private {!Array<print_preview.RecentDestination>} */ - recentDestinations_: { - type: Array, - notify: true, - }, - - /** @type {!print_preview_new.State} */ - state: { - type: Number, - observer: 'onStateChanged_', - }, - - /** @private {?print_preview.UserInfo} */ - userInfo_: { - type: Object, - notify: true, - value: null, - }, - /** @private {string} */ errorMessage_: { type: String, @@ -89,15 +79,8 @@ value: '', }, - /** @private {boolean} */ - controlsDisabled_: { - type: Boolean, - notify: true, - computed: 'computeControlsDisabled_(state)', - }, - - /** @private {?print_preview.MeasurementSystem} */ - measurementSystem_: { + /** @private {?print_preview.InvitationStore} */ + invitationStore_: { type: Object, notify: true, value: null, @@ -110,12 +93,25 @@ value: false, }, + /** @private {?print_preview.MeasurementSystem} */ + measurementSystem_: { + type: Object, + notify: true, + value: null, + }, + /** @private {!print_preview_new.PreviewAreaState} */ previewState_: { type: String, observer: 'onPreviewAreaStateChanged_', }, + /** @private {!Array<print_preview.RecentDestination>} */ + recentDestinations_: { + type: Array, + notify: true, + }, + /** @private {boolean} */ settingsExpandedByUser_: { type: Boolean, @@ -145,6 +141,9 @@ return loadTimeData.getBoolean('pagesPerSheetEnabled'); }, }, + + /** @private {!Array<string>} */ + users_: Array, }, listeners: { @@ -184,15 +183,14 @@ /** @override */ attached: function() { this.nativeLayer_ = print_preview.NativeLayer.getInstance(); - this.userInfo_ = new print_preview.UserInfo(); this.addWebUIListener( 'use-cloud-print', this.onCloudPrintEnable_.bind(this)); this.addWebUIListener('print-failed', this.onPrintFailed_.bind(this)); this.addWebUIListener( 'print-preset-options', this.onPrintPresetOptions_.bind(this)); - this.destinationStore_ = new print_preview.DestinationStore( - this.userInfo_, this.addWebUIListener.bind(this)); - this.invitationStore_ = new print_preview.InvitationStore(this.userInfo_); + this.destinationStore_ = + new print_preview.DestinationStore(this.addWebUIListener.bind(this)); + this.invitationStore_ = new print_preview.InvitationStore(); this.tracker_.add(window, 'keydown', this.onKeyDown_.bind(this)); this.$.previewArea.setPluginKeyEventCallback(this.onKeyDown_.bind(this)); this.tracker_.add( @@ -380,8 +378,7 @@ onCloudPrintEnable_: function(cloudPrintUrl, appKioskMode) { assert(!this.cloudPrintInterface_); this.cloudPrintInterface_ = cloudprint.getCloudPrintInterface( - cloudPrintUrl, assert(this.nativeLayer_), assert(this.userInfo_), - appKioskMode); + cloudPrintUrl, assert(this.nativeLayer_), appKioskMode); this.tracker_.add( assert(this.cloudPrintInterface_).getEventTarget(), cloudprint.CloudPrintInterfaceEventType.SUBMIT_DONE, @@ -399,7 +396,9 @@ this.invitationStore_.setCloudPrintInterface(this.cloudPrintInterface_); if (this.$.destinationSettings.isDialogOpen()) { this.destinationStore_.startLoadCloudDestinations(); - this.invitationStore_.startLoadingInvitations(); + if (this.activeUser_) { + this.invitationStore_.startLoadingInvitations(this.activeUser_); + } } }, @@ -706,5 +705,14 @@ close_: function() { this.$.state.transitTo(print_preview_new.State.CLOSING); }, + + /** + * @param {!CustomEvent<string>} e Event containing the new active user + * account. + * @private + */ + onAccountChange_: function(e) { + this.$.userInfo.updateActiveUser(e.detail); + }, }); })();
diff --git a/chrome/browser/resources/print_preview/new/destination_dialog.html b/chrome/browser/resources/print_preview/new/destination_dialog.html index be40d8e..ea6b1e1 100644 --- a/chrome/browser/resources/print_preview/new/destination_dialog.html +++ b/chrome/browser/resources/print_preview/new/destination_dialog.html
@@ -153,14 +153,14 @@ <cr-dialog id="dialog" on-close="onCloseOrCancel_"> <div slot="title" id="header"> <div>$i18n{destinationSearchTitle}</div> - <div class="user-info" hidden$="[[!userInfo.loggedIn]]"> + <div class="user-info" hidden$="[[!activeUser]]"> <label class="account-select-label" id="accountSelectLabel"> $i18n{accountSelectTitle} </label> <select class="md-select account-select" aria-labelledby="accountSelectLabel" on-change="onUserChange_"> - <template is="dom-repeat" items="[[userInfo.users]]"> - <option selected="[[isSelected_(item, userInfo.activeUser)]]" + <template is="dom-repeat" items="[[users]]"> + <option selected="[[isSelected_(item, activeUser)]]" value="[[item]]"> [[item]] </option>
diff --git a/chrome/browser/resources/print_preview/new/destination_dialog.js b/chrome/browser/resources/print_preview/new/destination_dialog.js index cccb854..0b369c6 100644 --- a/chrome/browser/resources/print_preview/new/destination_dialog.js +++ b/chrome/browser/resources/print_preview/new/destination_dialog.js
@@ -20,18 +20,17 @@ observer: 'onInvitationStoreSet_', }, + activeUser: String, + + /** @type {!Array<string>} */ + users: Array, + /** @private {?print_preview.Invitation} */ invitation_: { type: Object, value: null, }, - /** @type {!print_preview.UserInfo} */ - userInfo: { - type: Object, - notify: true, - }, - /** @type {boolean} */ showCloudPrintPromo: { type: Boolean, @@ -61,7 +60,7 @@ notify: true, computed: 'computeRecentDestinationList_(' + 'destinationStore, recentDestinations, recentDestinations.*, ' + - 'userInfo, destinations_.*)', + 'activeUser, destinations_.*)', observer: 'onRecentDestinationListChange_', }, @@ -155,7 +154,9 @@ /** @private */ onDestinationSearchDone_: function() { this.updateDestinations_(); - this.invitationStore.startLoadingInvitations(); + if (this.activeUser) { + this.invitationStore.startLoadingInvitations(this.activeUser); + } }, /** @private */ @@ -164,21 +165,14 @@ return; } - this.notifyPath('userInfo.users'); - this.notifyPath('userInfo.activeUser'); - this.notifyPath('userInfo.loggedIn'); - if (this.userInfo.loggedIn) { + if (this.activeUser) { this.showCloudPrintPromo = false; } - if (this.userInfo) { - this.updateList( - 'destinations_', - destination => destination.origin + '/' + destination.id, - this.destinationStore.destinations(this.userInfo.activeUser)); - } else { - this.destinations_ = []; - } + this.updateList( + 'destinations_', + destination => destination.origin + '/' + destination.id, + this.destinationStore.destinations(this.activeUser)); this.loadingDestinations_ = this.destinationStore.isPrintDestinationSearchInProgress; @@ -194,7 +188,7 @@ } const recentDestinations = []; - const filterAccount = this.userInfo.activeUser; + const filterAccount = this.activeUser; this.recentDestinations.forEach((recentDestination) => { const destination = this.destinationStore.getDestination( recentDestination.origin, recentDestination.id, @@ -323,7 +317,7 @@ /** @private */ isSelected_: function(account) { - return account == this.userInfo.activeUser; + return account == this.activeUser; }, /** @private */ @@ -345,8 +339,8 @@ * @private */ updateInvitations_: function() { - const invitations = this.userInfo.activeUser ? - this.invitationStore.invitations(this.userInfo.activeUser) : + const invitations = this.activeUser ? + this.invitationStore.invitations(this.activeUser) : []; if (this.invitation_ != invitations[0]) { this.metrics_.record( @@ -416,11 +410,7 @@ const account = select.value; if (account) { this.showCloudPrintPromo = false; - this.userInfo.activeUser = account; - this.notifyPath('userInfo.activeUser'); - this.notifyPath('userInfo.loggedIn'); - this.destinationStore.reloadUserCookieBasedDestinations(); - this.invitationStore.startLoadingInvitations(); + this.fire('account-change', account); this.metrics_.record( print_preview.Metrics.DestinationSearchBucket.ACCOUNT_CHANGED); } else { @@ -429,7 +419,7 @@ this.destinationStore)); const options = select.querySelectorAll('option'); for (let i = 0; i < options.length; i++) { - if (options[i].value == this.userInfo.activeUser) { + if (options[i].value == this.activeUser) { select.selectedIndex = i; break; }
diff --git a/chrome/browser/resources/print_preview/new/destination_settings.html b/chrome/browser/resources/print_preview/new/destination_settings.html index f23b7bb..40486cd0 100644 --- a/chrome/browser/resources/print_preview/new/destination_settings.html +++ b/chrome/browser/resources/print_preview/new/destination_settings.html
@@ -13,7 +13,6 @@ <link rel="import" href="../data/destination.html"> <link rel="import" href="../data/destination_store.html"> <link rel="import" href="../data/invitation_store.html"> -<link rel="import" href="../data/user_info.html"> <link rel="import" href="destination_dialog.html"> <link rel="import" href="print_preview_shared_css.html"> <link rel="import" href="throbber_css.html"> @@ -128,7 +127,7 @@ destination-store="[[destinationStore]]" invitation-store="[[invitationStore]]" recent-destinations="[[recentDestinations]]" - user-info="{{userInfo}}" + active-user="[[activeUser]]" users="[[users]]" show-cloud-print-promo="{{showCloudPrintPromo_}}" on-close="onDialogClose_"> </print-preview-destination-dialog>
diff --git a/chrome/browser/resources/print_preview/new/destination_settings.js b/chrome/browser/resources/print_preview/new/destination_settings.js index 2da04fc..927e9971 100644 --- a/chrome/browser/resources/print_preview/new/destination_settings.js +++ b/chrome/browser/resources/print_preview/new/destination_settings.js
@@ -8,39 +8,32 @@ behaviors: [I18nBehavior], properties: { + activeUser: String, + /** @type {!print_preview.Destination} */ destination: Object, /** @type {?print_preview.DestinationStore} */ destinationStore: Object, - /** @type {?print_preview.InvitationStore} */ - invitationStore: Object, - - /** @type {!Array<!print_preview.RecentDestination>} */ - recentDestinations: Array, - - /** @type {!print_preview.UserInfo} */ - userInfo: { - type: Object, - notify: true, - }, - disabled: Boolean, - /** @type {!print_preview_new.State} */ - state: Number, + /** @type {?print_preview.InvitationStore} */ + invitationStore: Object, noDestinationsFound: { type: Boolean, value: false, }, - /** @private {boolean} */ - showCloudPrintPromo_: { - type: Boolean, - value: false, - }, + /** @type {!Array<!print_preview.RecentDestination>} */ + recentDestinations: Array, + + /** @type {!print_preview_new.State} */ + state: Number, + + /** @type {!Array<string>} */ + users: Array, /** @private {boolean} */ loadingDestination_: { @@ -49,6 +42,12 @@ }, /** @private {boolean} */ + showCloudPrintPromo_: { + type: Boolean, + value: false, + }, + + /** @private {boolean} */ stale_: { type: Boolean, computed: 'computeStale_(destination)', @@ -99,7 +98,9 @@ /** @private */ onChangeButtonClick_: function() { this.destinationStore.startLoadAllDestinations(); - this.invitationStore.startLoadingInvitations(); + if (this.activeUser) { + this.invitationStore.startLoadingInvitations(this.activeUser); + } const dialog = this.$.destinationDialog.get(); dialog.show(); },
diff --git a/chrome/browser/resources/settings/site_settings/all_sites.html b/chrome/browser/resources/settings/site_settings/all_sites.html index f6ec471..44a40cc1 100644 --- a/chrome/browser/resources/settings/site_settings/all_sites.html +++ b/chrome/browser/resources/settings/site_settings/all_sites.html
@@ -64,7 +64,11 @@ scroll-target="[[subpageScrollTarget]]"> <template> <site-entry site-group="[[item]]" list-index="[[index]]" - tabindex$="[[tabIndex]]"></site-entry> + iron-list-tab-index="[[tabIndex]]" + tabindex$="[[tabIndex]]" + last-focused="{{lastFocused_}}" + list-blurred="{{listBlurred_}}"> + </site-entry> </template> </iron-list> </div>
diff --git a/chrome/browser/resources/settings/site_settings/all_sites.js b/chrome/browser/resources/settings/site_settings/all_sites.js index 8d73343..de06f800 100644 --- a/chrome/browser/resources/settings/site_settings/all_sites.js +++ b/chrome/browser/resources/settings/site_settings/all_sites.js
@@ -91,6 +91,20 @@ type: Object, observer: 'focusConfigChanged_', }, + + /** + * @private + * Used to track the last-focused element across rows for the + * focusRowBehavior. + */ + lastFocused_: Object, + + /** + * @private + * Used to track whether the list of row items has been blurred for the + * focusRowBehavior. + */ + listBlurred_: Boolean, }, /** @private {?settings.LocalDataBrowserProxy} */ @@ -105,7 +119,7 @@ /** @override */ ready: function() { this.addWebUIListener( - 'onLocalStorageListFetched', this.onLocalStorageListFetched.bind(this)); + 'onStorageListFetched', this.onStorageListFetched.bind(this)); this.addWebUIListener( 'contentSettingSitePermissionChanged', this.populateList_.bind(this)); this.addEventListener( @@ -158,11 +172,11 @@ }, /** - * Integrate sites using local storage into the existing sites map, as there + * Integrate sites using storage into the existing sites map, as there * may be overlap between the existing sites. - * @param {!Array<!SiteGroup>} list The list of sites using local storage. + * @param {!Array<!SiteGroup>} list The list of sites using storage. */ - onLocalStorageListFetched: function(list) { + onStorageListFetched: function(list) { // Create a new map to make an observable change. const newMap = /** @type {!Map<string, !SiteGroup>} */ (new Map(this.siteGroupMap)); @@ -170,6 +184,8 @@ if (newMap.has(storageSiteGroup.etldPlus1)) { const siteGroup = newMap.get(storageSiteGroup.etldPlus1); const storageOriginInfoMap = new Map(); + + siteGroup.numCookies = storageSiteGroup.numCookies; storageSiteGroup.origins.forEach( originInfo => storageOriginInfoMap.set(originInfo.origin, originInfo)); @@ -177,11 +193,11 @@ // If there is an overlapping origin, update the original // |originInfo|. siteGroup.origins.forEach(originInfo => { - if (!storageOriginInfoMap.has(originInfo.origin)) { - return; + if (storageOriginInfoMap.has(originInfo.origin)) { + Object.assign( + originInfo, storageOriginInfoMap.get(originInfo.origin)); + storageOriginInfoMap.delete(originInfo.origin); } - Object.apply(originInfo, storageOriginInfoMap.get(originInfo.origin)); - storageOriginInfoMap.delete(originInfo.origin); }); // Otherwise, add it to the list. storageOriginInfoMap.forEach( @@ -226,29 +242,7 @@ if (sortMethod == this.sortMethods_.mostVisited) { siteGroupList.sort(this.mostVisitedComparator_); } else if (sortMethod == this.sortMethods_.storage) { - // Storage is loaded asynchronously, so make sure it's updated for every - // item in the list to ensure the sorting is correct. - const etldPlus1List = siteGroupList.reduce((list, siteGroup) => { - if (siteGroup.origins.length > 1 && siteGroup.etldPlus1.length > 0) { - list.push(siteGroup.etldPlus1); - } - return list; - }, []); - - this.localDataBrowserProxy_.getNumCookiesList(etldPlus1List) - .then(numCookiesList => { - assert(etldPlus1List.length == numCookiesList.length); - numCookiesList.forEach(cookiesPerEtldPlus1 => { - this.siteGroupMap.get(cookiesPerEtldPlus1.etldPlus1).numCookies = - cookiesPerEtldPlus1.numCookies; - }); - - // |siteGroupList| by this point should have already been provided - // to the iron list, so just sort in-place here and make sure to - // re-render the item order. - siteGroupList.sort(this.storageComparator_); - this.$.allSitesList.fire('iron-resize'); - }); + siteGroupList.sort(this.storageComparator_); } else if (sortMethod == this.sortMethods_.name) { siteGroupList.sort(this.nameComparator_); }
diff --git a/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js b/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js index 19db72fa..dee64f7 100644 --- a/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js +++ b/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js
@@ -78,15 +78,6 @@ getCookieDetails(site) {} /** - * Gets a list containing the number of cookies for each domain (eTLD+1 - * names) given in |siteList|. This will always return a result array the - * same length and in the same order as |siteList|. - * @param {!Array<string>} siteList The list of sites to count cookies for. - * @return {!Promise<!Array<!EtldPlus1CookieNumber>>} - */ - getNumCookiesList(siteList) {} - - /** * Gets the plural string for a given number of cookies. * @param {number} numCookies The number of cookies. * @return {!Promise<string>} @@ -138,11 +129,6 @@ } /** @override */ - getNumCookiesList(siteList) { - return cr.sendWithPromise('localData.getNumCookiesList', siteList); - } - - /** @override */ getNumCookiesString(numCookies) { return cr.sendWithPromise('localData.getNumCookiesString', numCookies); }
diff --git a/chrome/browser/resources/settings/site_settings/site_entry.html b/chrome/browser/resources/settings/site_settings/site_entry.html index fffb85e..e85a45c 100644 --- a/chrome/browser/resources/settings/site_settings/site_entry.html +++ b/chrome/browser/resources/settings/site_settings/site_entry.html
@@ -2,6 +2,7 @@ <link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html"> <link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html"> +<link rel="import" href="chrome://resources/html/cr/ui/focus_row_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../route.html"> @@ -45,7 +46,7 @@ padding-inline-end: 0; } </style> - <div id="collapseParent"> + <div id="collapseParent" focus-row-container> <div class$="settings-box list-item [[getClassForIndex_(listIndex)]]"> <div id="toggleButton" class="start row-aligned two-line" on-click="onSiteEntryTap_" actionable aria-expanded="false"> @@ -70,19 +71,24 @@ <paper-icon-button-light id="expandIcon" class="icon-expand-more" hidden$="[[!grouped_(siteGroup)]]"> <button aria-label$="[[displayName_]]" - aria-describedby="displayName"></button> + aria-describedby="displayName" focus-row-control + focus-type="expand"> + </button> </paper-icon-button-light> <paper-icon-button-light class="subpage-arrow" hidden$="[[grouped_(siteGroup)]]"> <button aria-label$="[[displayName_]]" - aria-describedby="displayName"></button> + aria-describedby="displayName" + focus-row-control focus-type="show-detail"> + </button> </paper-icon-button-light> </div> <div class="row-aligned" hidden$="[[!grouped_(siteGroup)]]"> <div class="separator"></div> <paper-icon-button-light class="icon-more-vert"> <button id="overflowMenuButton" title="$i18n{moreActions}" - on-click="showOverflowMenu_"> + on-click="showOverflowMenu_" focus-row-control + focus-type="more-actions"> </button> </paper-icon-button-light> </div> @@ -109,9 +115,15 @@ <span class="secondary data-unit" hidden$="[[!item.usage]]"> [[originUsagesItem_(originUsages_.*, index)]] </span> + <span class="secondary data-unit" + hidden$="[[!item.numCookies]]"> + · [[originCookiesItem_(cookiesNum_.*, index)]] + </span> </div> <paper-icon-button-light class="subpage-arrow"> - <button aria-labelledby$="originSiteRepresentation"></button> + <button aria-labelledby$="originSiteRepresentation" + focus-row-control focus-type="detailed-sites"> + </button> </paper-icon-button-light> </div> </template>
diff --git a/chrome/browser/resources/settings/site_settings/site_entry.js b/chrome/browser/resources/settings/site_settings/site_entry.js index cb02d97ab..e183e23 100644 --- a/chrome/browser/resources/settings/site_settings/site_entry.js +++ b/chrome/browser/resources/settings/site_settings/site_entry.js
@@ -9,7 +9,7 @@ Polymer({ is: 'site-entry', - behaviors: [SiteSettingsBehavior], + behaviors: [SiteSettingsBehavior, cr.ui.FocusRowBehavior], properties: { /** @@ -60,6 +60,19 @@ return []; }, }, + + /** + * An array containing the strings to display showing the individual cookies + * number for each origin in |siteGroup|. + * @type {!Array<string>} + * @private + */ + cookiesNum_: { + type: Array, + value: function() { + return []; + } + } }, listeners: { @@ -153,39 +166,12 @@ if (this.$.collapseChild.opened) { this.toggleCollapsible_(); } - // Ungrouped site-entries should not show cookies. - if (this.cookieString_) { - this.cookieString_ = ''; - } } if (!siteGroup) { return; } this.calculateUsageInfo_(siteGroup); - - if (!this.grouped_(siteGroup)) { - return; - } - - const siteList = [this.displayName_]; - this.localDataBrowserProxy_.getNumCookiesList(siteList) - .then(numCookiesList => { - assert(siteList.length == numCookiesList.length); - - const numCookies = numCookiesList[0].numCookies; - if (siteGroup.numCookies != numCookies) { - this.fire('site-entry-storage-updated'); - } - siteGroup.numCookies = numCookies; - this.notifyPath('siteGroup.numCookies'); - - return numCookies == 0 ? - Promise.resolve('') : - this.localDataBrowserProxy_.getNumCookiesString(numCookies); - }) - .then(string => { - this.cookieString_ = string; - }); + this.calculateNumberOfCookies_(siteGroup); }, /** @@ -258,6 +244,34 @@ }, /** + * Calculates the number of cookies set on the given group of origins + * and eTLD+1. Also updates the corresponding display strings. + * @param {SiteGroup} siteGroup The eTLD+1 group of origins. + * @private + */ + calculateNumberOfCookies_: function(siteGroup) { + const getCookieNumString = (numCookies) => { + if (numCookies == 0) { + return Promise.resolve(''); + } + return this.localDataBrowserProxy_.getNumCookiesString(numCookies); + }; + + this.cookiesNum_ = new Array(siteGroup.origins.length); + siteGroup.origins.forEach((originInfo, i) => { + if (this.grouped_(siteGroup)) { + getCookieNumString(originInfo.numCookies).then((string) => { + this.set(`cookiesNum_.${i}`, string); + }); + } + }); + + getCookieNumString(siteGroup.numCookies).then(string => { + this.cookieString_ = string; + }); + }, + + /** * Array binding for the |originUsages_| array for use in the HTML. * @param {!{base: !Array<string>}} change The change record for the array. * @param {number} index The index of the array item. @@ -269,6 +283,17 @@ }, /** + * Array binding for the |cookiesNum_| array for use in the HTML. + * @param {!{base: !Array<string>}} change The change record for the array. + * @param {number} index The index of the array item. + * @return {string} + * @private + */ + originCookiesItem_: function(change, index) { + return change.base[index]; + }, + + /** * Navigates to the corresponding Site Details page for the given origin. * @param {string} origin The origin to navigate to the Site Details page for * it.
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js index 8ff27b0..ec82082 100644 --- a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js +++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
@@ -29,7 +29,8 @@ * Stores origin information. * @typedef {{origin: string, * engagement: number, - * usage: number}} + * usage: number, + numCookies: number}} */ let OriginInfo;
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc index d19a1b4..e0baf701 100644 --- a/chrome/browser/search/local_ntp_source.cc +++ b/chrome/browser/search/local_ntp_source.cc
@@ -85,8 +85,6 @@ // Signifies a locally constructed resource, i.e. not from grit/. const int kLocalResource = -1; -const char kAnimatedShareDoodleUrl[] = - "https://www.gstatic.com/logo/dev/ddljson_animated_share_button.json"; const char kConfigDataFilename[] = "config.js"; const char kDoodleScriptFilename[] = "doodle.js"; const char kGoogleUrl[] = "https://www.google.com/"; @@ -97,8 +95,6 @@ const char kNtpBackgroundImageScriptFilename[] = "ntp-background-images.js"; const char kOneGoogleBarScriptFilename[] = "one-google.js"; const char kPromoScriptFilename[] = "promo.js"; -const char kSimpleShareDoodleUrl[] = - "https://www.gstatic.com/logo/dev/ddljson_simple_share_button.json"; const char kThemeCSSFilename[] = "theme.css"; const struct Resource{ @@ -954,22 +950,10 @@ &force_doodle_param)) { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - // TODO(crbug.com/896461): Add share button to ddljson_desktop0.json and - // ddljson_desktop1.json then update links below. - if (force_doodle_param == "0") { - command_line->AppendSwitchASCII( - search_provider_logos::switches::kGoogleDoodleUrl, - kSimpleShareDoodleUrl); - } else if (force_doodle_param == "1") { - command_line->AppendSwitchASCII( - search_provider_logos::switches::kGoogleDoodleUrl, - kAnimatedShareDoodleUrl); - } else { - command_line->AppendSwitchASCII( - search_provider_logos::switches::kGoogleDoodleUrl, - "https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_desktop" + - force_doodle_param + ".json"); - } + command_line->AppendSwitchASCII( + search_provider_logos::switches::kGoogleDoodleUrl, + "https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_desktop" + + force_doodle_param + ".json"); } callback.Run(base::RefCountedString::TakeString(&html));
diff --git a/chrome/browser/status_icons/status_tray.h b/chrome/browser/status_icons/status_tray.h index a127a83..20f528b 100644 --- a/chrome/browser/status_icons/status_tray.h +++ b/chrome/browser/status_icons/status_tray.h
@@ -32,7 +32,7 @@ // Static factory method that is implemented separately for each platform to // produce the appropriate platform-specific instance. Returns NULL if this // platform does not support status icons. - static StatusTray* Create(); + static std::unique_ptr<StatusTray> Create(); virtual ~StatusTray();
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 2930c51..6344436 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -973,6 +973,10 @@ "search/search_ipc_router_policy_impl.h", "search/search_tab_helper.cc", "search/search_tab_helper.h", + "serial/serial_chooser.cc", + "serial/serial_chooser.h", + "serial/serial_chooser_controller.cc", + "serial/serial_chooser_controller.h", "singleton_tabs.cc", "singleton_tabs.h", "startup/automation_infobar_delegate.cc", @@ -3427,6 +3431,10 @@ "views/extensions/media_galleries_dialog_views.h", "views/extensions/media_gallery_checkbox_view.cc", "views/extensions/media_gallery_checkbox_view.h", + "web_applications/web_app_metrics.cc", + "web_applications/web_app_metrics.h", + "web_applications/web_app_metrics_factory.cc", + "web_applications/web_app_metrics_factory.h", "webui/extensions/extension_basic_info.cc", "webui/extensions/extension_basic_info.h", "webui/extensions/extension_icon_source.cc",
diff --git a/chrome/browser/ui/android/status_tray_android.cc b/chrome/browser/ui/android/status_tray_android.cc index d01287a..4e111542 100644 --- a/chrome/browser/ui/android/status_tray_android.cc +++ b/chrome/browser/ui/android/status_tray_android.cc
@@ -6,7 +6,7 @@ #include "base/logging.h" -StatusTray* StatusTray::Create() { +std::unique_ptr<StatusTray> StatusTray::Create() { NOTIMPLEMENTED(); return NULL; }
diff --git a/chrome/browser/ui/app_list/search/omnibox_provider.cc b/chrome/browser/ui/app_list/search/omnibox_provider.cc index 1e51b2a01..d63ee64 100644 --- a/chrome/browser/ui/app_list/search/omnibox_provider.cc +++ b/chrome/browser/ui/app_list/search/omnibox_provider.cc
@@ -51,8 +51,12 @@ // Sets the |from_omnibox_focus| flag to enable ZeroSuggestProvider to process // the requests from app_list. - if (is_zero_state_enabled_ && input.text().empty()) + if (is_zero_state_enabled_ && input.text().empty()) { input.set_from_omnibox_focus(true); + is_zero_state_input_ = true; + } else { + is_zero_state_input_ = false; + } controller_->Start(input); } @@ -62,9 +66,9 @@ for (const AutocompleteMatch& match : result) { if (!match.destination_url.is_valid()) continue; - new_results.emplace_back(std::make_unique<OmniboxResult>( - profile_, list_controller_, controller_.get(), match)); + profile_, list_controller_, controller_.get(), match, + is_zero_state_input_)); } SwapResults(&new_results); }
diff --git a/chrome/browser/ui/app_list/search/omnibox_provider.h b/chrome/browser/ui/app_list/search/omnibox_provider.h index 161fe51..70d8b09 100644 --- a/chrome/browser/ui/app_list/search/omnibox_provider.h +++ b/chrome/browser/ui/app_list/search/omnibox_provider.h
@@ -37,7 +37,9 @@ void OnResultChanged(bool default_match_changed) override; Profile* profile_; - bool is_zero_state_enabled_; + bool is_zero_state_enabled_ = false; + // True if the input is empty for zero state suggestion. + bool is_zero_state_input_ = false; AppListControllerDelegate* list_controller_; // The omnibox AutocompleteController that collects/sorts/dup-
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.cc b/chrome/browser/ui/app_list/search/omnibox_result.cc index f52437e..64d8065 100644 --- a/chrome/browser/ui/app_list/search/omnibox_result.cc +++ b/chrome/browser/ui/app_list/search/omnibox_result.cc
@@ -9,16 +9,20 @@ #include "ash/public/cpp/app_list/app_list_config.h" #include "ash/public/cpp/app_list/app_list_features.h" #include "ash/public/cpp/app_list/vector_icons/vector_icons.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/bookmarks/bookmark_model_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/app_list/app_list_controller_delegate.h" #include "chrome/browser/ui/app_list/search/search_util.h" +#include "chrome/grit/generated_resources.h" #include "components/bookmarks/browser/bookmark_model.h" #include "components/omnibox/browser/autocomplete_controller.h" #include "components/omnibox/browser/autocomplete_match_type.h" #include "components/omnibox/browser/vector_icons.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/color_palette.h" #include "ui/gfx/paint_vector_icon.h" #include "url/gurl.h" #include "url/url_canon.h" @@ -32,6 +36,9 @@ // #000 at 87% opacity. constexpr SkColor kListIconColor = SkColorSetARGB(0xDE, 0x00, 0x00, 0x00); +constexpr int kImageButtonIconSize = 20; +constexpr SkColor kImageButtonColor = gfx::kGoogleGrey700; + int ACMatchStyleToTagStyle(int styles) { int tag_styles = 0; if (styles & ACMatchClassification::URL) @@ -123,11 +130,13 @@ OmniboxResult::OmniboxResult(Profile* profile, AppListControllerDelegate* list_controller, AutocompleteController* autocomplete_controller, - const AutocompleteMatch& match) + const AutocompleteMatch& match, + bool is_zero_suggestion) : profile_(profile), list_controller_(list_controller), autocomplete_controller_(autocomplete_controller), - match_(match) { + match_(match), + is_zero_suggestion_(is_zero_suggestion) { if (match_.search_terms_args && autocomplete_controller_) { match_.search_terms_args->from_app_list = true; autocomplete_controller_->UpdateMatchDestinationURL( @@ -143,19 +152,38 @@ if (AutocompleteMatch::IsSearchType(match_.type)) SetIsOmniboxSearch(true); - UpdateIcon(); UpdateTitleAndDetails(); + + if (is_zero_suggestion_) + SetZeroSuggestionActions(); } OmniboxResult::~OmniboxResult() = default; void OmniboxResult::Open(int event_flags) { RecordHistogram(OMNIBOX_SEARCH_RESULT); + RecordOmniboxResultHistogram(); list_controller_->OpenURL(profile_, match_.destination_url, match_.transition, ui::DispositionFromEventFlags(event_flags)); } +void OmniboxResult::Remove() { + // TODO(jennyz): add RecordHistogram. + autocomplete_controller_->DeleteMatch(match_); +} + +void OmniboxResult::InvokeAction(int action_index, int event_flags) { + DCHECK(is_zero_suggestion_); + switch (ash::GetOmniBoxZeroStateAction(action_index)) { + case ash::OmniBoxZeroStateAction::kRemoveSuggestion: + Remove(); + break; + default: + NOTREACHED(); + } +} + void OmniboxResult::UpdateIcon() { BookmarkModel* bookmark_model = BookmarkModelFactory::GetForBrowserContext(profile_); @@ -204,4 +232,47 @@ !match_.description.empty(); } +void OmniboxResult::SetZeroSuggestionActions() { + Actions zero_suggestion_actions; + + constexpr int kMaxButtons = ash::OmniBoxZeroStateAction::kZeroStateActionMax; + for (int i = 0; i < kMaxButtons; ++i) { + ash::OmniBoxZeroStateAction button_action = + ash::GetOmniBoxZeroStateAction(i); + gfx::ImageSkia button_image; + base::string16 button_tooltip; + bool visible_on_hover = false; + + switch (button_action) { + case ash::OmniBoxZeroStateAction::kRemoveSuggestion: + button_image = gfx::CreateVectorIcon( + kSearchResultRemoveIcon, kImageButtonIconSize, kImageButtonColor); + button_tooltip = l10n_util::GetStringUTF16( + IDS_APP_LIST_REMOVE_SUGGESTION_ACCESSIBILITY_NAME); + visible_on_hover = true; // visible upon hovering + break; + case ash::OmniBoxZeroStateAction::kAppendSuggestion: + button_image = gfx::CreateVectorIcon( + kSearchResultAppendIcon, kImageButtonIconSize, kImageButtonColor); + button_tooltip = l10n_util::GetStringUTF16( + IDS_APP_LIST_APPEND_SUGGESTION_ACCESSIBILITY_NAME); + visible_on_hover = false; // always visible + break; + default: + NOTREACHED(); + } + Action search_action(button_image, button_tooltip, visible_on_hover); + zero_suggestion_actions.emplace_back(search_action); + } + + SetActions(zero_suggestion_actions); +} + +void OmniboxResult::RecordOmniboxResultHistogram() { + UMA_HISTOGRAM_ENUMERATION("Apps.AppListSearchOmniboxResultOpenType", + is_zero_suggestion_ + ? OmniboxResultType::kZeroStateSuggestion + : OmniboxResultType::kQuerySuggestion); +} + } // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.h b/chrome/browser/ui/app_list/search/omnibox_result.h index 1853270..72ad46a 100644 --- a/chrome/browser/ui/app_list/search/omnibox_result.h +++ b/chrome/browser/ui/app_list/search/omnibox_result.h
@@ -15,6 +15,15 @@ class AutocompleteController; class Profile; +// These are used in histograms, do not remove/renumber entries. If you're +// adding to this enum with the intention that it will be logged, update the +// AppListOmniboxResult enum listing in tools/metrics/histograms/enums.xml. +enum class OmniboxResultType { + kQuerySuggestion = 0, + kZeroStateSuggestion = 1, + kMaxValue = kZeroStateSuggestion, +}; + namespace app_list { class OmniboxResult : public ChromeSearchResult { @@ -22,25 +31,33 @@ OmniboxResult(Profile* profile, AppListControllerDelegate* list_controller, AutocompleteController* autocomplete_controller, - const AutocompleteMatch& match); + const AutocompleteMatch& match, + bool is_zero_suggestion); ~OmniboxResult() override; // ChromeSearchResult overrides: void Open(int event_flags) override; + void InvokeAction(int action_index, int event_flags) override; private: void UpdateIcon(); - void UpdateTitleAndDetails(); + void Remove(); + // Returns true if |match_| indicates a url search result with non-empty // description. bool IsUrlResultWithDescription() const; + void SetZeroSuggestionActions(); + + void RecordOmniboxResultHistogram(); + Profile* profile_; AppListControllerDelegate* list_controller_; AutocompleteController* autocomplete_controller_; AutocompleteMatch match_; + const bool is_zero_suggestion_; DISALLOW_COPY_AND_ASSIGN(OmniboxResult); };
diff --git a/chrome/browser/ui/app_list/search/tests/omnibox_result_unittest.cc b/chrome/browser/ui/app_list/search/tests/omnibox_result_unittest.cc index 9eec775..0aaf5f7 100644 --- a/chrome/browser/ui/app_list/search/tests/omnibox_result_unittest.cc +++ b/chrome/browser/ui/app_list/search/tests/omnibox_result_unittest.cc
@@ -63,8 +63,9 @@ match.type = type; match.keyword = base::UTF8ToUTF16(keyword); - return std::make_unique<OmniboxResult>( - profile_.get(), app_list_controller_delegate_.get(), nullptr, match); + return std::make_unique<OmniboxResult>(profile_.get(), + app_list_controller_delegate_.get(), + nullptr, match, false); } const GURL& GetLastOpenedUrl() const {
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index 83f18db..2870b5f 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc
@@ -134,6 +134,8 @@ #include "chrome/browser/ui/location_bar/location_bar.h" #include "chrome/browser/ui/permission_bubble/chooser_bubble_delegate.h" #include "chrome/browser/ui/search/search_tab_helper.h" +#include "chrome/browser/ui/serial/serial_chooser.h" +#include "chrome/browser/ui/serial/serial_chooser_controller.h" #include "chrome/browser/ui/singleton_tabs.h" #include "chrome/browser/ui/status_bubble.h" #include "chrome/browser/ui/sync/browser_synced_window_delegate.h" @@ -1221,6 +1223,19 @@ return std::move(bluetooth_chooser_desktop); } +std::unique_ptr<content::SerialChooser> Browser::RunSerialChooser( + content::RenderFrameHost* frame, + std::vector<blink::mojom::SerialPortFilterPtr> filters, + content::SerialChooser::Callback callback) { + auto chooser_controller = std::make_unique<SerialChooserController>( + frame, std::move(filters), std::move(callback)); + auto chooser_bubble_delegate = std::make_unique<ChooserBubbleDelegate>( + frame, std::move(chooser_controller)); + BubbleReference bubble_reference = + GetBubbleManager()->ShowBubble(std::move(chooser_bubble_delegate)); + return std::make_unique<SerialChooser>(std::move(bubble_reference)); +} + void Browser::RequestAppBannerFromDevTools(content::WebContents* web_contents) { banners::AppBannerManagerDesktop::CreateForWebContents(web_contents); banners::AppBannerManagerDesktop* manager =
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h index 70d32c5f..d331ec3 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h
@@ -494,6 +494,10 @@ std::unique_ptr<content::BluetoothChooser> RunBluetoothChooser( content::RenderFrameHost* frame, const content::BluetoothChooser::EventHandler& event_handler) override; + std::unique_ptr<content::SerialChooser> RunSerialChooser( + content::RenderFrameHost* frame, + std::vector<blink::mojom::SerialPortFilterPtr> filters, + content::SerialChooser::Callback callback) override; void RequestAppBannerFromDevTools( content::WebContents* web_contents) override; void PassiveInsecureContentFound(const GURL& resource_url) override;
diff --git a/chrome/browser/ui/cocoa/status_icons/status_tray_mac.mm b/chrome/browser/ui/cocoa/status_icons/status_tray_mac.mm index a358a44..1345592 100644 --- a/chrome/browser/ui/cocoa/status_icons/status_tray_mac.mm +++ b/chrome/browser/ui/cocoa/status_icons/status_tray_mac.mm
@@ -9,8 +9,8 @@ #include "chrome/browser/ui/cocoa/status_icons/status_icon_mac.h" -StatusTray* StatusTray::Create() { - return new StatusTrayMac(); +std::unique_ptr<StatusTray> StatusTray::Create() { + return std::make_unique<StatusTrayMac>(); } StatusTrayMac::StatusTrayMac() {
diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc index 9bec118..bcf93e4 100644 --- a/chrome/browser/ui/extensions/application_launch.cc +++ b/chrome/browser/ui/extensions/application_launch.cc
@@ -32,6 +32,7 @@ #include "chrome/browser/ui/extensions/hosted_app_browser_controller.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/components/web_app_helpers.h" +#include "chrome/browser/web_applications/components/web_app_tab_helper_base.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "chrome/common/url_constants.h" #include "content/public/browser/web_contents.h" @@ -178,7 +179,7 @@ } WebContents* OpenApplicationTab(const AppLaunchParams& launch_params, - const GURL& url) { + const GURL& url) { const Extension* extension = GetExtension(launch_params); CHECK(extension); Profile* const profile = launch_params.profile; @@ -244,6 +245,9 @@ contents = params.navigated_or_inserted_contents; } + web_app::WebAppTabHelperBase::FromWebContents(contents)->SetAppId( + extension->id()); + #if defined(OS_CHROMEOS) // In ash, LAUNCH_FULLSCREEN launches in the OpenApplicationWindow function // i.e. it should not reach here. @@ -384,8 +388,14 @@ Navigate(&nav_params); WebContents* web_contents = nav_params.navigated_or_inserted_contents; + extensions::HostedAppBrowserController::SetAppPrefsForWebContents( browser->hosted_app_controller(), web_contents); + if (extension) { + web_app::WebAppTabHelperBase::FromWebContents(web_contents) + ->SetAppId(extension->id()); + } + browser->window()->Show(); // TODO(jcampan): http://crbug.com/8123 we should not need to set the initial
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc index edec546d..a25e163 100644 --- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc +++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -112,6 +112,7 @@ profile, page_url, extensions::LAUNCH_CONTAINER_WINDOW); } +// TODO(loyso): Erase this histogram. crbug.com/918089. const char kPwaWindowEngagementTypeHistogram[] = "Webapp.Engagement.EngagementType";
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.h b/chrome/browser/ui/extensions/hosted_app_browser_controller.h index 34e4b6a..a572eed8 100644 --- a/chrome/browser/ui/extensions/hosted_app_browser_controller.h +++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.h
@@ -30,6 +30,7 @@ const GURL& page_url, content::BrowserContext* profile); +// TODO(loyso): Erase this histogram. crbug.com/918089. extern const char kPwaWindowEngagementTypeHistogram[]; class Extension;
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc index 77dfcdd..da3e667 100644 --- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc +++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -1541,6 +1541,9 @@ NavigateAndCheckForToolbar(app_browser_, app_url, false); } +// TODO(loyso): crbug.com/918089. This test is deprecated in favor of +// BookmarkAppTest.EngagementHistogramForAppInWindow and +// BookmarkAppTest.EngagementHistogramForAppInTab. IN_PROC_BROWSER_TEST_P(HostedAppPWAOnlyTest, EngagementHistogram) { base::HistogramTester histograms; WebApplicationInfo web_app_info; @@ -1585,6 +1588,9 @@ SiteEngagementService::ENGAGEMENT_MOUSE, 1); } +// TODO(loyso): crbug.com/918089. This test is deprecated in favor of +// BookmarkAppTest.EngagementHistogramAppWithoutScope and +// BookmarkAppTest.EngagementHistogramRecordedForNonApps. IN_PROC_BROWSER_TEST_P(HostedAppPWAOnlyTest, EngagementHistogramNotRecordedIfNoScope) { base::HistogramTester histograms; @@ -1601,6 +1607,8 @@ histograms.ExpectTotalCount(extensions::kPwaWindowEngagementTypeHistogram, 0); } +// TODO(loyso): crbug.com/918089. This test is deprecated in favor of +// BookmarkAppTest.EngagementHistogramTwoApps. IN_PROC_BROWSER_TEST_P(HostedAppPWAOnlyTest, EngagementHistogramTwoApps) { base::HistogramTester histograms; const extensions::Extension *app1, *app2;
diff --git a/chrome/browser/ui/serial/serial_chooser.cc b/chrome/browser/ui/serial/serial_chooser.cc new file mode 100644 index 0000000..b780970f --- /dev/null +++ b/chrome/browser/ui/serial/serial_chooser.cc
@@ -0,0 +1,17 @@ +// Copyright 2019 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 "chrome/browser/ui/serial/serial_chooser.h" + +#include <utility> + +#include "components/bubble/bubble_controller.h" + +SerialChooser::SerialChooser(BubbleReference bubble) + : bubble_(std::move(bubble)) {} + +SerialChooser::~SerialChooser() { + if (bubble_) + bubble_->CloseBubble(BUBBLE_CLOSE_FORCED); +}
diff --git a/chrome/browser/ui/serial/serial_chooser.h b/chrome/browser/ui/serial/serial_chooser.h new file mode 100644 index 0000000..9492bd6 --- /dev/null +++ b/chrome/browser/ui/serial/serial_chooser.h
@@ -0,0 +1,24 @@ +// Copyright 2019 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 CHROME_BROWSER_UI_SERIAL_SERIAL_CHOOSER_H_ +#define CHROME_BROWSER_UI_SERIAL_SERIAL_CHOOSER_H_ + +#include "base/macros.h" +#include "components/bubble/bubble_reference.h" +#include "content/public/browser/serial_chooser.h" + +// Owns a serial port chooser dialog and closes it when destroyed. +class SerialChooser : public content::SerialChooser { + public: + explicit SerialChooser(BubbleReference bubble); + ~SerialChooser() override; + + private: + BubbleReference bubble_; + + DISALLOW_COPY_AND_ASSIGN(SerialChooser); +}; + +#endif // CHROME_BROWSER_UI_SERIAL_SERIAL_CHOOSER_H_
diff --git a/chrome/browser/ui/serial/serial_chooser_controller.cc b/chrome/browser/ui/serial/serial_chooser_controller.cc new file mode 100644 index 0000000..c1c0f5f --- /dev/null +++ b/chrome/browser/ui/serial/serial_chooser_controller.cc
@@ -0,0 +1,131 @@ +// Copyright 2019 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 "chrome/browser/ui/serial/serial_chooser_controller.h" + +#include <utility> + +#include "base/bind.h" +#include "base/strings/utf_string_conversions.h" +#include "base/unguessable_token.h" +#include "chrome/grit/generated_resources.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/service_manager_connection.h" +#include "services/device/public/mojom/constants.mojom.h" +#include "services/service_manager/public/cpp/connector.h" +#include "ui/base/l10n/l10n_util.h" + +namespace { + +blink::mojom::SerialPortInfoPtr ToBlinkType( + const device::mojom::SerialPortInfo& port) { + auto info = blink::mojom::SerialPortInfo::New(); + info->token = port.token; + info->has_vendor_id = port.has_vendor_id; + if (port.has_vendor_id) + info->vendor_id = port.vendor_id; + info->has_product_id = port.has_product_id; + if (port.has_product_id) + info->product_id = port.product_id; + return info; +} + +} // namespace + +SerialChooserController::SerialChooserController( + content::RenderFrameHost* render_frame_host, + std::vector<blink::mojom::SerialPortFilterPtr> filters, + content::SerialChooser::Callback callback) + : ChooserController(render_frame_host, + IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN, + IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME), + filters_(std::move(filters)), + callback_(std::move(callback)) { + DCHECK(content::ServiceManagerConnection::GetForProcess()); + content::ServiceManagerConnection::GetForProcess() + ->GetConnector() + ->BindInterface(device::mojom::kServiceName, + mojo::MakeRequest(&port_manager_)); + port_manager_.set_connection_error_handler(base::BindOnce( + &SerialChooserController::OnGetDevices, base::Unretained(this), + std::vector<device::mojom::SerialPortInfoPtr>())); + port_manager_->GetDevices(base::BindOnce( + &SerialChooserController::OnGetDevices, base::Unretained(this))); +} + +SerialChooserController::~SerialChooserController() { + if (callback_) + std::move(callback_).Run(nullptr); +} + +base::string16 SerialChooserController::GetNoOptionsText() const { + return l10n_util::GetStringUTF16(IDS_DEVICE_CHOOSER_NO_DEVICES_FOUND_PROMPT); +} + +base::string16 SerialChooserController::GetOkButtonLabel() const { + return l10n_util::GetStringUTF16(IDS_SERIAL_PORT_CHOOSER_CONNECT_BUTTON_TEXT); +} + +size_t SerialChooserController::NumOptions() const { + return ports_.size(); +} + +base::string16 SerialChooserController::GetOption(size_t index) const { + DCHECK_LT(index, ports_.size()); + + const device::mojom::SerialPortInfo& port = *ports_[index]; + return base::UTF8ToUTF16(port.display_name.value_or(port.path)); +} + +bool SerialChooserController::IsPaired(size_t index) const { + return false; +} + +void SerialChooserController::Select(const std::vector<size_t>& indices) { + DCHECK_EQ(1u, indices.size()); + size_t index = indices[0]; + DCHECK_LT(index, ports_.size()); + + const device::mojom::SerialPortInfo& port = *ports_[index]; + std::move(callback_).Run(ToBlinkType(port)); +} + +void SerialChooserController::Cancel() {} + +void SerialChooserController::Close() {} + +void SerialChooserController::OpenHelpCenterUrl() const { + NOTIMPLEMENTED(); +} + +void SerialChooserController::OnGetDevices( + std::vector<device::mojom::SerialPortInfoPtr> ports) { + for (auto& port : ports) { + if (FilterMatchesAny(*port)) + ports_.push_back(std::move(port)); + } + + if (view()) + view()->OnOptionsInitialized(); +} + +bool SerialChooserController::FilterMatchesAny( + const device::mojom::SerialPortInfo& port) const { + if (filters_.empty()) + return true; + + for (const auto& filter : filters_) { + if (filter->has_vendor_id && + (!port.has_vendor_id || filter->vendor_id != port.vendor_id)) { + continue; + } + if (filter->has_product_id && + (!port.has_product_id || filter->product_id != port.product_id)) { + continue; + } + return true; + } + + return false; +}
diff --git a/chrome/browser/ui/serial/serial_chooser_controller.h b/chrome/browser/ui/serial/serial_chooser_controller.h new file mode 100644 index 0000000..92c44d7 --- /dev/null +++ b/chrome/browser/ui/serial/serial_chooser_controller.h
@@ -0,0 +1,58 @@ +// Copyright 2019 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 CHROME_BROWSER_UI_SERIAL_SERIAL_CHOOSER_CONTROLLER_H_ +#define CHROME_BROWSER_UI_SERIAL_SERIAL_CHOOSER_CONTROLLER_H_ + +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/strings/string16.h" +#include "chrome/browser/chooser_controller/chooser_controller.h" +#include "content/public/browser/serial_chooser.h" +#include "services/device/public/mojom/serial.mojom.h" +#include "third_party/blink/public/mojom/serial/serial.mojom.h" +#include "url/gurl.h" + +namespace content { +class RenderFrameHost; +} // namespace content + +// SerialChooserController provides data for the Serial API permission prompt. +// It is owned by ChooserBubbleDelegate. +class SerialChooserController : public ChooserController { + public: + SerialChooserController( + content::RenderFrameHost* render_frame_host, + std::vector<blink::mojom::SerialPortFilterPtr> filters, + content::SerialChooser::Callback callback); + ~SerialChooserController() override; + + // ChooserController: + base::string16 GetNoOptionsText() const override; + base::string16 GetOkButtonLabel() const override; + size_t NumOptions() const override; + base::string16 GetOption(size_t index) const override; + bool IsPaired(size_t index) const override; + void Select(const std::vector<size_t>& indices) override; + void Cancel() override; + void Close() override; + void OpenHelpCenterUrl() const override; + + private: + void OnGetDevices(std::vector<device::mojom::SerialPortInfoPtr> ports); + bool FilterMatchesAny(const device::mojom::SerialPortInfo& port) const; + + std::vector<blink::mojom::SerialPortFilterPtr> filters_; + content::SerialChooser::Callback callback_; + + device::mojom::SerialPortManagerPtr port_manager_; + std::vector<device::mojom::SerialPortInfoPtr> ports_; + + DISALLOW_COPY_AND_ASSIGN(SerialChooserController); +}; + +#endif // CHROME_BROWSER_UI_SERIAL_SERIAL_CHOOSER_CONTROLLER_H_
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc index 4ba26e26..9f404f1 100644 --- a/chrome/browser/ui/tab_helpers.cc +++ b/chrome/browser/ui/tab_helpers.cc
@@ -71,6 +71,7 @@ #include "chrome/browser/ui/tab_contents/core_tab_helper.h" #include "chrome/browser/ui/tab_dialogs.h" #include "chrome/browser/ui/tab_ui_helper.h" +#include "chrome/browser/ui/web_applications/web_app_metrics.h" #include "chrome/browser/vr/vr_tab_helper.h" #include "chrome/common/buildflags.h" #include "chrome/common/chrome_features.h" @@ -336,6 +337,8 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) extensions::TabHelper::CreateForWebContents(web_contents); web_app::WebAppProvider::CreateTabHelper(web_contents); + if (SiteEngagementService::IsEnabled()) + web_app::WebAppMetrics::Get(profile); #endif #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
diff --git a/chrome/browser/ui/views/badge_service_delegate_impl.cc b/chrome/browser/ui/views/badge_service_delegate_impl.cc index b616e68..5b790e9 100644 --- a/chrome/browser/ui/views/badge_service_delegate_impl.cc +++ b/chrome/browser/ui/views/badge_service_delegate_impl.cc
@@ -15,7 +15,7 @@ #include "ui/strings/grit/ui_strings.h" #if defined(OS_WIN) -#include "chrome/browser/ui/views/frame/taskbar_decorator_win.cc" +#include "chrome/browser/ui/views/frame/taskbar_decorator_win.h" #elif defined(OS_MACOSX) #include "chrome/browser/apps/app_shim/app_shim_host_mac.h" #include "chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h"
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc index 123fb73..b4e993d5 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -82,6 +82,7 @@ #include "ui/aura/client/aura_constants.h" #include "ui/aura/event_injector.h" #include "ui/aura/test/env_test_helper.h" +#include "ui/aura/test/mus/change_completion_waiter.h" #include "ui/base/class_property.h" #include "ui/base/hit_test.h" #include "ui/base/test/material_design_controller_test_api.h" @@ -145,6 +146,7 @@ base::RunLoop run_loop; shell_test_api->ToggleOverviewMode(run_loop.QuitClosure()); run_loop.Run(); + aura::test::WaitForAllChangesToComplete(); } else { ash::Shell::Get()->window_selector_controller()->ToggleOverview(); }
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 77e81306..498feed 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -613,11 +613,7 @@ } bool BrowserView::IsIncognito() const { - // TODO(pbos): This is confusing or incorrect as IsIncognito() returns true - // for Guest sessions. We should probably rename this function and audit - // callers to make sure that's actually what was intended, or make this return - // false for Guest sessions. - return browser_->profile()->IsOffTheRecord(); + return browser_->profile()->GetProfileType() == Profile::INCOGNITO_PROFILE; } bool BrowserView::IsGuestSession() const {
diff --git a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc index 106d4bfd..7d331e3 100644 --- a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc +++ b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc
@@ -944,6 +944,10 @@ event_generator.MoveMouseTo(54, 300); event_generator.ClickLeftButton(); + // Evaluate an empty sentence to make sure that the event processing is done + // in the content. + ignore_result(content::EvalJs(contents, ";")); + // Verify that the selected option has changed and the forth option is // selected. EXPECT_EQ(true, content::EvalJs(contents, "selectChanged;"));
diff --git a/chrome/browser/ui/views/ime/ime_window_browsertest.cc b/chrome/browser/ui/views/ime/ime_window_browsertest.cc index 651e57a6..b9e57cd 100644 --- a/chrome/browser/ui/views/ime/ime_window_browsertest.cc +++ b/chrome/browser/ui/views/ime/ime_window_browsertest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/run_loop.h" +#include "build/build_config.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/ime/ime_native_window.h" #include "chrome/browser/ui/ime/ime_window.h" @@ -61,13 +62,22 @@ DISALLOW_COPY_AND_ASSIGN(ImeWindowBrowserTest); }; -IN_PROC_BROWSER_TEST_F(ImeWindowBrowserTest, CreateNormalWindow) { +// https://crbug.com/919624 : Flaky on Win7 bots +#if defined(OS_WIN) +#define MAYBE_CreateNormalWindow DISABLED_CreateNormalWindow +#define MAYBE_CreateFollowCursorWindow DISABLED_CreateFollowCursorWindow +#else +#define MAYBE_CreateNormalWindow CreateNormalWindow +#define MAYBE_CreateFollowCursorWindow CreateFollowCursorWindow +#endif + +IN_PROC_BROWSER_TEST_F(ImeWindowBrowserTest, MAYBE_CreateNormalWindow) { gfx::Rect expected_bounds(100, 200, 300, 400); CreateImeWindow(expected_bounds, false); VerifyImeWindow(expected_bounds); } -IN_PROC_BROWSER_TEST_F(ImeWindowBrowserTest, CreateFollowCursorWindow) { +IN_PROC_BROWSER_TEST_F(ImeWindowBrowserTest, MAYBE_CreateFollowCursorWindow) { gfx::Rect expected_bounds(100, 200, 300, 400); CreateImeWindow(expected_bounds, true); VerifyImeWindow(expected_bounds);
diff --git a/chrome/browser/ui/views/status_icons/status_tray_linux.cc b/chrome/browser/ui/views/status_icons/status_tray_linux.cc index a06731f5..c09220ea 100644 --- a/chrome/browser/ui/views/status_icons/status_tray_linux.cc +++ b/chrome/browser/ui/views/status_icons/status_tray_linux.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/ui/views/status_icons/status_tray_linux.h" +#include <memory> + #include "build/build_config.h" #if !defined(OS_CHROMEOS) @@ -23,16 +25,16 @@ return StatusIconLinuxWrapper::CreateWrappedStatusIcon(image, tool_tip); } -StatusTray* StatusTray::Create() { +std::unique_ptr<StatusTray> StatusTray::Create() { const views::LinuxUI* linux_ui = views::LinuxUI::instance(); // Only create a status tray if we can actually create status icons. if (linux_ui && linux_ui->IsStatusIconSupported()) - return new StatusTrayLinux(); + return std::make_unique<StatusTrayLinux>(); return nullptr; } #else // defined(OS_CHROMEOS) -StatusTray* StatusTray::Create() { +std::unique_ptr<StatusTray> StatusTray::Create() { return nullptr; } #endif
diff --git a/chrome/browser/ui/views/status_icons/status_tray_win.cc b/chrome/browser/ui/views/status_icons/status_tray_win.cc index c0c672e..9d0e5b0 100644 --- a/chrome/browser/ui/views/status_icons/status_tray_win.cc +++ b/chrome/browser/ui/views/status_icons/status_tray_win.cc
@@ -243,6 +243,6 @@ state_changer_proxy_ = std::move(proxy); } -StatusTray* StatusTray::Create() { - return new StatusTrayWin(); +std::unique_ptr<StatusTray> StatusTray::Create() { + return std::make_unique<StatusTrayWin>(); }
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc index 40f24c9..55229f5 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -70,15 +70,16 @@ #include "ash/public/cpp/ash_switches.h" #include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h" #include "ash/public/cpp/window_properties.h" -#include "ash/shell.h" -#include "ash/wm/cursor_manager_test_api.h" -#include "ash/wm/root_window_finder.h" +#include "ash/public/interfaces/constants.mojom.h" +#include "ash/public/interfaces/shell_test_api.test-mojom.h" #include "ash/wm/window_state.h" -#include "ash/wm/window_util.h" #include "base/test/simple_test_tick_clock.h" #include "chrome/browser/ui/ash/tablet_mode_client_test_util.h" #include "chrome/browser/ui/views/frame/immersive_mode_controller.h" #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h" +#include "content/public/common/service_manager_connection.h" +#include "services/service_manager/public/cpp/connector.h" +#include "ui/aura/client/cursor_client.h" #include "ui/aura/client/screen_position_client.h" #include "ui/aura/test/mus/change_completion_waiter.h" #include "ui/aura/window_event_dispatcher.h" @@ -138,19 +139,25 @@ registrar_.Add(this, chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE, content::NotificationService::AllSources()); } + ~QuitDraggingObserver() override {} void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) override { DCHECK_EQ(chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE, type); - base::RunLoop::QuitCurrentWhenIdleDeprecated(); - delete this; + run_loop_.QuitWhenIdle(); + } + + void Wait() { + run_loop_.Run(); +#if defined(OS_CHROMEOS) + aura::test::WaitForAllChangesToComplete(); +#endif } private: - ~QuitDraggingObserver() override {} - content::NotificationRegistrar registrar_; + base::RunLoop run_loop_; DISALLOW_COPY_AND_ASSIGN(QuitDraggingObserver); }; @@ -184,11 +191,6 @@ return result; } -// Creates a listener that quits the message loop when no longer dragging. -void QuitWhenNotDraggingImpl() { - new QuitDraggingObserver(); // QuitDraggingObserver deletes itself. -} - TabStrip* GetTabStripForBrowser(Browser* browser) { BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); return browser_view->tabstrip(); @@ -216,6 +218,9 @@ AddBlankTabAndShow(browser); StopAnimating(GetTabStripForBrowser(browser)); ResetIDs(browser->tab_strip_model(), 0); +#if defined(OS_CHROMEOS) + aura::test::WaitForAllChangesToComplete(); +#endif } Browser* TabDragControllerTest::CreateAnotherBrowserAndResize() { @@ -232,6 +237,9 @@ browser()->window()->SetBounds(browser_rect); browser_rect.set_x(browser_rect.right()); browser2->window()->SetBounds(browser_rect); +#if defined(OS_CHROMEOS) + aura::test::WaitForAllChangesToComplete(); +#endif return browser2; } @@ -277,11 +285,13 @@ bool GetIsDragged(Browser* browser) { #if defined(OS_CHROMEOS) - return ash::wm::GetWindowState(browser->window()->GetNativeWindow())-> - is_dragged(); -#else - return false; + if (!features::IsUsingWindowService()) { + return ash::wm::GetWindowState(browser->window()->GetNativeWindow()) + ->is_dragged(); + } + // TODO(mukai): support for Mash. #endif + return false; } } // namespace @@ -406,6 +416,7 @@ // tests' touch events. ui::GestureConfiguration::GetInstance()->set_min_fling_velocity( std::numeric_limits<float>::max()); + aura::test::WaitForAllChangesToComplete(); #endif #if defined(OS_MACOSX) // Currently MacViews' browser windows are shown in the background and could @@ -530,21 +541,6 @@ return true; } - void QuitWhenNotDragging() { - if (input_source() == INPUT_SOURCE_MOUSE) { - // Schedule observer to quit message loop when done dragging. This has to - // be async so the message loop can run. - test::QuitWhenNotDraggingImpl(); - base::RunLoop().Run(); - } else { - // Touch events are sync, so we know we're not in a drag session. But some - // tests rely on the browser fully closing, which is async. So, run all - // pending tasks. - base::RunLoop run_loop; - run_loop.RunUntilIdle(); - } - } - void AddBlankTabAndShow(Browser* browser) { InProcessBrowserTest::AddBlankTabAndShow(browser); } @@ -595,7 +591,7 @@ ASSERT_TRUE(DragInputToNotifyWhenDone( tab_0_center + gfx::Vector2d(drag_x_offset, GetDetachY(tab_strip)), std::move(task))); - QuitWhenNotDragging(); + test::QuitDraggingObserver().Wait(); } Browser* browser() const { return InProcessBrowserTest::browser(); } @@ -825,7 +821,10 @@ ash::kWindowPositionManagedTypeKey); } bool HasUserChangedWindowPositionOrSize(aura::Window* window) { - return ash::wm::GetWindowState(window)->bounds_changed_by_user(); + if (!features::IsUsingWindowService()) + return ash::wm::GetWindowState(window)->bounds_changed_by_user(); + // TODO(mukai): support Mash. + return false; } #else bool IsWindowPositionManaged(gfx::NativeWindow window) { @@ -2533,9 +2532,10 @@ } float GetCursorDeviceScaleFactor() const { - ash::CursorManagerTestApi cursor_test_api( - ash::Shell::Get()->cursor_manager()); - return cursor_test_api.GetCurrentCursor().device_scale_factor(); + return aura::client::GetCursorClient( + browser()->window()->GetNativeWindow()->GetRootWindow()) + ->GetCursor() + .device_scale_factor(); } private: @@ -2572,7 +2572,7 @@ if (index > 0) { EXPECT_EQ(kDragPoints[index - 1], - ash::Shell::Get()->aura_env()->last_mouse_location()); + aura::Env::GetInstance()->last_mouse_location()); EXPECT_EQ(kDeviceScaleFactorExpectations[index - 1], test->GetCursorDeviceScaleFactor()); } @@ -2639,9 +2639,11 @@ ASSERT_EQ(2u, browser_list->size()); // Switching display mode should cancel the drag operation. - display::DisplayManager* display_manager = - ash::Shell::Get()->display_manager(); - display_manager->AddRemoveDisplay(); + ash::mojom::ShellTestApiPtr shell_test_api; + content::ServiceManagerConnection::GetForProcess() + ->GetConnector() + ->BindInterface(ash::mojom::kServiceName, &shell_test_api); + shell_test_api->AddRemoveDisplay(); } // Invoked from the nested run loop. @@ -2938,7 +2940,7 @@ ASSERT_TRUE(ReleaseInput()); }))); }))); - QuitWhenNotDragging(); + test::QuitDraggingObserver().Wait(); ASSERT_FALSE(tab_strip->IsDragSessionActive()); ASSERT_FALSE(TabDragController::IsActive());
diff --git a/chrome/browser/ui/web_applications/OWNERS b/chrome/browser/ui/web_applications/OWNERS new file mode 100644 index 0000000..1255a2c --- /dev/null +++ b/chrome/browser/ui/web_applications/OWNERS
@@ -0,0 +1 @@ +file://chrome/browser/web_applications/OWNERS
diff --git a/chrome/browser/ui/web_applications/bookmark_app_browsertest.cc b/chrome/browser/ui/web_applications/bookmark_app_browsertest.cc new file mode 100644 index 0000000..3b4bef9 --- /dev/null +++ b/chrome/browser/ui/web_applications/bookmark_app_browsertest.cc
@@ -0,0 +1,473 @@ +// Copyright 2018 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 "chrome/browser/ui/web_applications/web_app_metrics.h" + +#include <bitset> + +#include "base/test/bind_test_util.h" +#include "base/test/metrics/histogram_tester.h" +#include "chrome/browser/engagement/site_engagement_service.h" +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extension_util.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/web_applications/web_app_metrics.h" +#include "chrome/browser/web_applications/components/pending_app_manager.h" +#include "chrome/browser/web_applications/components/web_app_constants.h" +#include "chrome/browser/web_applications/components/web_app_helpers.h" +#include "chrome/browser/web_applications/web_app_provider.h" +#include "chrome/common/web_application_info.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/test_navigation_observer.h" +#include "extensions/common/extension.h" +#include "url/gurl.h" + +namespace { + +void NavigateToURLAndWait(Browser* browser, const GURL& url) { + content::WebContents* web_contents = + browser->tab_strip_model()->GetActiveWebContents(); + + content::TestNavigationObserver observer( + web_contents, content::MessageLoopRunner::QuitMode::DEFERRED); + NavigateParams params(browser, url, ui::PAGE_TRANSITION_LINK); + ui_test_utils::NavigateToURL(¶ms); + observer.WaitForNavigationFinished(); +} + +// TODO(loyso): Merge this with PendingBookmarkAppManagerBrowserTest's +// implementation in some test_support library. +web_app::PendingAppManager::AppInfo CreateAppInfo(const GURL& url) { + web_app::PendingAppManager::AppInfo app_info( + url, web_app::LaunchContainer::kWindow, + web_app::InstallSource::kInternal); + // Avoid creating real shortcuts in tests. + app_info.create_shortcuts = false; + return app_info; +} + +GURL GetUrlForSuffix(const std::string& prefix, int suffix) { + return GURL(prefix + base::IntToString(suffix) + ".com/"); +} + +// Must be zero-based as this will be stored in a bitset. +enum HistogramIndex { + kHistogramInTab = 0, + kHistogramInWindow, + kHistogramDefaultInstalled_InTab, + kHistogramDefaultInstalled_InWindow, + kHistogramUserInstalled_InTab, + kHistogramUserInstalled_InWindow, + kHistogramUserInstalled_FromInstallButton_InTab, + kHistogramUserInstalled_FromInstallButton_InWindow, + kHistogramUserInstalled_FromCreateShortcutButton_InTab, + kHistogramUserInstalled_FromCreateShortcutButton_InWindow, + kHistogramMoreThanThreeUserInstalledApps, + kHistogramUpToThreeUserInstalledApps, + kHistogramNoUserInstalledApps, + kHistogramMaxValue +}; + +// The order (indices) must match HistogramIndex enum above: +const char* kHistogramNames[] = { + "WebApp.Engagement.InTab", + "WebApp.Engagement.InWindow", + "WebApp.Engagement.DefaultInstalled.InTab", + "WebApp.Engagement.DefaultInstalled.InWindow", + "WebApp.Engagement.UserInstalled.InTab", + "WebApp.Engagement.UserInstalled.InWindow", + "WebApp.Engagement.UserInstalled.FromInstallButton.InTab", + "WebApp.Engagement.UserInstalled.FromInstallButton.InWindow", + "WebApp.Engagement.UserInstalled.FromCreateShortcutButton.InTab", + "WebApp.Engagement.UserInstalled.FromCreateShortcutButton.InWindow", + "WebApp.Engagement.MoreThanThreeUserInstalledApps", + "WebApp.Engagement.UpToThreeUserInstalledApps", + "WebApp.Engagement.NoUserInstalledApps"}; + +const char* HistogramEnumIndexToStr(int histogram_index) { + DCHECK_GE(histogram_index, 0); + DCHECK_LT(histogram_index, kHistogramMaxValue); + return kHistogramNames[histogram_index]; +} + +using Histograms = std::bitset<kHistogramMaxValue>; + +void ExpectUniqueSamples(const base::HistogramTester& tester, + const Histograms& histograms_mask, + SiteEngagementService::EngagementType type, + base::HistogramBase::Count count) { + for (int h = 0; h < kHistogramMaxValue; ++h) { + if (histograms_mask[h]) { + const char* histogram_name = HistogramEnumIndexToStr(h); + tester.ExpectUniqueSample(histogram_name, type, count); + } + } +} + +void ExpectBucketCounts(const base::HistogramTester& tester, + const Histograms& histograms_mask, + SiteEngagementService::EngagementType type, + base::HistogramBase::Count count) { + for (int h = 0; h < kHistogramMaxValue; ++h) { + if (histograms_mask[h]) { + const char* histogram_name = HistogramEnumIndexToStr(h); + tester.ExpectBucketCount(histogram_name, type, count); + } + } +} + +void ExpectTotalCounts(const base::HistogramTester& tester, + const Histograms& histograms_mask, + base::HistogramBase::Count count) { + for (int h = 0; h < kHistogramMaxValue; ++h) { + if (histograms_mask[h]) { + const char* histogram_name = HistogramEnumIndexToStr(h); + tester.ExpectTotalCount(histogram_name, count); + } + } +} + +} // namespace + +class BookmarkAppTest : public extensions::ExtensionBrowserTest { + public: + BookmarkAppTest() = default; + ~BookmarkAppTest() override = default; + + void TestEngagementEventWebAppLaunch(const base::HistogramTester& tester, + const Histograms& histograms) { + ExpectUniqueSamples( + tester, histograms, + SiteEngagementService::ENGAGEMENT_WEBAPP_SHORTCUT_LAUNCH, 1); + ExpectTotalCounts(tester, ~histograms, 0); + } + + // Test some other engagement events by directly calling into + // SiteEngagementService. + void TestEngagementEventsAfterLaunch(const Histograms& histograms, + Browser* browser) { + base::HistogramTester tester; + + content::WebContents* web_contents = + browser->tab_strip_model()->GetActiveWebContents(); + SiteEngagementService* site_engagement_service = + SiteEngagementService::Get(browser->profile()); + + // Simulate 4 events of various types. + site_engagement_service->HandleMediaPlaying(web_contents, false); + site_engagement_service->HandleMediaPlaying(web_contents, true); + site_engagement_service->HandleNavigation(web_contents, + ui::PAGE_TRANSITION_TYPED); + site_engagement_service->HandleUserInput( + web_contents, SiteEngagementService::ENGAGEMENT_MOUSE); + + ExpectTotalCounts(tester, histograms, 4); + ExpectTotalCounts(tester, ~histograms, 0); + + ExpectBucketCounts(tester, histograms, + SiteEngagementService::ENGAGEMENT_MEDIA_VISIBLE, 1); + ExpectBucketCounts(tester, histograms, + SiteEngagementService::ENGAGEMENT_MEDIA_HIDDEN, 1); + ExpectBucketCounts(tester, histograms, + SiteEngagementService::ENGAGEMENT_NAVIGATION, 1); + ExpectBucketCounts(tester, histograms, + SiteEngagementService::ENGAGEMENT_MOUSE, 1); + } + + protected: + void CountUserInstalledApps() { + web_app::WebAppMetrics* web_app_metrics = + web_app::WebAppMetrics::Get(profile()); + web_app_metrics->CountUserInstalledAppsForTesting(); + } + + const extensions::Extension* InstallBookmarkAppAndCountApps( + WebApplicationInfo info) { + const extensions::Extension* app = + ExtensionBrowserTest::InstallBookmarkApp(std::move(info)); + CountUserInstalledApps(); + return app; + } + + // TODO(loyso): Merge this method with + // PendingBookmarkAppManagerBrowserTest::InstallApp in some + // test_support library. + void InstallDefaultAppAndCountApps( + web_app::PendingAppManager::AppInfo app_info) { + base::RunLoop run_loop; + + web_app::WebAppProvider::Get(browser()->profile()) + ->pending_app_manager() + .Install(std::move(app_info), + base::BindLambdaForTesting( + [this, &run_loop](const GURL& provided_url, + web_app::InstallResultCode code) { + result_code_ = code; + run_loop.QuitClosure().Run(); + })); + run_loop.Run(); + ASSERT_TRUE(result_code_.has_value()); + CountUserInstalledApps(); + } + + base::Optional<web_app::InstallResultCode> result_code_; + + private: + DISALLOW_COPY_AND_ASSIGN(BookmarkAppTest); +}; + +IN_PROC_BROWSER_TEST_F(BookmarkAppTest, EngagementHistogramForAppInWindow) { + base::HistogramTester tester; + + const GURL example_url = GURL("http://example.org/"); + + WebApplicationInfo web_app_info; + web_app_info.app_url = example_url; + web_app_info.scope = example_url; + web_app_info.open_as_window = true; + const extensions::Extension* app = + InstallBookmarkAppAndCountApps(web_app_info); + + Browser* app_browser = LaunchAppBrowser(app); + NavigateToURLAndWait(app_browser, example_url); + + EXPECT_EQ(web_app::GetAppIdFromApplicationName(app_browser->app_name()), + app->id()); + + Histograms histograms; + histograms[kHistogramInWindow] = true; + histograms[kHistogramUserInstalled_InWindow] = true; + histograms[kHistogramUserInstalled_FromInstallButton_InWindow] = true; + histograms[kHistogramUpToThreeUserInstalledApps] = true; + + TestEngagementEventWebAppLaunch(tester, histograms); + TestEngagementEventsAfterLaunch(histograms, app_browser); +} + +IN_PROC_BROWSER_TEST_F(BookmarkAppTest, EngagementHistogramForAppInTab) { + base::HistogramTester tester; + + const GURL example_url = GURL("http://example.org/"); + + WebApplicationInfo web_app_info; + web_app_info.app_url = example_url; + web_app_info.scope = example_url; + web_app_info.open_as_window = false; + const extensions::Extension* app = + InstallBookmarkAppAndCountApps(web_app_info); + + Browser* browser = LaunchBrowserForAppInTab(app); + EXPECT_FALSE(browser->hosted_app_controller()); + NavigateToURLAndWait(browser, example_url); + + Histograms histograms; + histograms[kHistogramInTab] = true; + histograms[kHistogramUserInstalled_InTab] = true; + histograms[kHistogramUserInstalled_FromInstallButton_InTab] = true; + histograms[kHistogramUpToThreeUserInstalledApps] = true; + + TestEngagementEventWebAppLaunch(tester, histograms); + TestEngagementEventsAfterLaunch(histograms, browser); +} + +IN_PROC_BROWSER_TEST_F(BookmarkAppTest, EngagementHistogramAppWithoutScope) { + base::HistogramTester tester; + + const GURL example_url = GURL("http://example.org/"); + + WebApplicationInfo web_app_info; + web_app_info.app_url = example_url; + // If app has no scope then UrlHandlers::GetUrlHandlers are empty. Therefore, + // the app is counted as installed via the Create Shortcut button. + web_app_info.scope = GURL(); + web_app_info.open_as_window = true; + const extensions::Extension* app = + InstallBookmarkAppAndCountApps(web_app_info); + + Browser* browser = LaunchAppBrowser(app); + + EXPECT_EQ(web_app::GetAppIdFromApplicationName(browser->app_name()), + app->id()); + EXPECT_TRUE(browser->hosted_app_controller()); + NavigateToURLAndWait(browser, example_url); + + Histograms histograms; + histograms[kHistogramInWindow] = true; + histograms[kHistogramUserInstalled_InWindow] = true; + histograms[kHistogramUserInstalled_FromCreateShortcutButton_InWindow] = true; + histograms[kHistogramUpToThreeUserInstalledApps] = true; + + TestEngagementEventWebAppLaunch(tester, histograms); + TestEngagementEventsAfterLaunch(histograms, browser); +} + +IN_PROC_BROWSER_TEST_F(BookmarkAppTest, EngagementHistogramTwoApps) { + base::HistogramTester tester; + + const GURL example_url1 = GURL("http://example.org/"); + const GURL example_url2 = GURL("http://example.com/"); + + const extensions::Extension *app1, *app2; + + // Install two apps. + { + WebApplicationInfo web_app_info; + web_app_info.app_url = example_url1; + web_app_info.scope = example_url1; + app1 = InstallBookmarkAppAndCountApps(web_app_info); + } + { + WebApplicationInfo web_app_info; + web_app_info.app_url = example_url2; + web_app_info.scope = example_url2; + app2 = InstallBookmarkAppAndCountApps(web_app_info); + } + + // Launch them three times. This ensures that each launch only logs once. + // (Since all apps receive the notification on launch, there is a danger that + // we might log too many times.) + Browser* app_browser1 = LaunchAppBrowser(app1); + Browser* app_browser2 = LaunchAppBrowser(app1); + Browser* app_browser3 = LaunchAppBrowser(app2); + + EXPECT_EQ(web_app::GetAppIdFromApplicationName(app_browser1->app_name()), + app1->id()); + EXPECT_EQ(web_app::GetAppIdFromApplicationName(app_browser2->app_name()), + app1->id()); + EXPECT_EQ(web_app::GetAppIdFromApplicationName(app_browser3->app_name()), + app2->id()); + + Histograms histograms; + histograms[kHistogramInWindow] = true; + histograms[kHistogramUserInstalled_InWindow] = true; + histograms[kHistogramUserInstalled_FromInstallButton_InWindow] = true; + histograms[kHistogramUpToThreeUserInstalledApps] = true; + + ExpectUniqueSamples(tester, histograms, + SiteEngagementService::ENGAGEMENT_WEBAPP_SHORTCUT_LAUNCH, + 3); + ExpectTotalCounts(tester, ~histograms, 0); +} + +IN_PROC_BROWSER_TEST_F(BookmarkAppTest, EngagementHistogramManyUserApps) { + base::HistogramTester tester; + + // More than 3 user-installed apps: + const int num_user_apps = 4; + + std::vector<const extensions::Extension*> apps; + + // Install apps. + const std::string base_url = "http://example"; + for (int i = 0; i < num_user_apps; ++i) { + const GURL url = GetUrlForSuffix(base_url, i); + + WebApplicationInfo web_app_info; + web_app_info.app_url = url; + web_app_info.scope = url; + const extensions::Extension* app = + InstallBookmarkAppAndCountApps(web_app_info); + apps.push_back(app); + } + + // Launch apps in windows. + for (int i = 0; i < num_user_apps; ++i) { + Browser* browser = LaunchAppBrowser(apps[i]); + + const GURL url = GetUrlForSuffix(base_url, i); + NavigateToURLAndWait(browser, url); + } + + Histograms histograms; + histograms[kHistogramInWindow] = true; + histograms[kHistogramUserInstalled_InWindow] = true; + histograms[kHistogramUserInstalled_FromInstallButton_InWindow] = true; + histograms[kHistogramMoreThanThreeUserInstalledApps] = true; + + ExpectUniqueSamples(tester, histograms, + SiteEngagementService::ENGAGEMENT_WEBAPP_SHORTCUT_LAUNCH, + 4); + ExpectTotalCounts(tester, ~histograms, 0); +} + +IN_PROC_BROWSER_TEST_F(BookmarkAppTest, EngagementHistogramDefaultApp) { + base::HistogramTester tester; + + ASSERT_TRUE(embedded_test_server()->Start()); + GURL example_url( + embedded_test_server()->GetURL("/banners/manifest_test_page.html")); + InstallDefaultAppAndCountApps(CreateAppInfo(example_url)); + ASSERT_EQ(web_app::InstallResultCode::kSuccess, result_code_.value()); + + const extensions::Extension* app = extensions::util::GetInstalledPwaForUrl( + browser()->profile(), example_url); + ASSERT_TRUE(app); + EXPECT_TRUE(app->was_installed_by_default()); + + Browser* browser = LaunchAppBrowser(app); + NavigateToURLAndWait(browser, example_url); + + Histograms histograms; + histograms[kHistogramInWindow] = true; + histograms[kHistogramDefaultInstalled_InWindow] = true; + histograms[kHistogramNoUserInstalledApps] = true; + + TestEngagementEventWebAppLaunch(tester, histograms); + TestEngagementEventsAfterLaunch(histograms, browser); +} + +IN_PROC_BROWSER_TEST_F(BookmarkAppTest, + EngagementHistogramNavigateAwayFromAppTab) { + const GURL app_url = GURL("http://example.org/app/"); + const GURL outer_url = GURL("http://example.org/"); + + WebApplicationInfo web_app_info; + web_app_info.app_url = app_url; + web_app_info.scope = app_url; + web_app_info.open_as_window = false; + const extensions::Extension* app = + InstallBookmarkAppAndCountApps(web_app_info); + + Browser* browser = LaunchBrowserForAppInTab(app); + EXPECT_FALSE(browser->hosted_app_controller()); + + NavigateToURLAndWait(browser, app_url); + { + Histograms histograms; + histograms[kHistogramInTab] = true; + histograms[kHistogramUserInstalled_InTab] = true; + histograms[kHistogramUserInstalled_FromInstallButton_InTab] = true; + histograms[kHistogramUpToThreeUserInstalledApps] = true; + TestEngagementEventsAfterLaunch(histograms, browser); + } + + // Navigate away from the web app to an outer simple web site: + NavigateToURLAndWait(browser, outer_url); + { + Histograms histograms; + histograms[kHistogramUpToThreeUserInstalledApps] = true; + TestEngagementEventsAfterLaunch(histograms, browser); + } +} + +IN_PROC_BROWSER_TEST_F(BookmarkAppTest, EngagementHistogramRecordedForNonApps) { + base::HistogramTester tester; + CountUserInstalledApps(); + + // Launch a non-app tab in default browser. + const GURL example_url = GURL("http://example.org/"); + NavigateToURLAndWait(browser(), example_url); + + // Check that no histograms recorded, e.g. no + // SiteEngagementService::ENGAGEMENT_WEBAPP_SHORTCUT_LAUNCH. + Histograms histograms; + ExpectTotalCounts(tester, ~histograms, 0); + + // The engagement broken down by the number of apps installed must be recorded + // for all engagement events. + histograms[kHistogramNoUserInstalledApps] = true; + TestEngagementEventsAfterLaunch(histograms, browser()); +}
diff --git a/chrome/browser/ui/web_applications/web_app_metrics.cc b/chrome/browser/ui/web_applications/web_app_metrics.cc new file mode 100644 index 0000000..b3e8f40 --- /dev/null +++ b/chrome/browser/ui/web_applications/web_app_metrics.cc
@@ -0,0 +1,137 @@ +// Copyright 2018 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 "chrome/browser/ui/web_applications/web_app_metrics.h" + +#include "base/metrics/histogram_functions.h" +#include "chrome/browser/engagement/site_engagement_service.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/web_applications/web_app_metrics_factory.h" +#include "chrome/browser/web_applications/components/web_app_tab_helper_base.h" +#include "chrome/browser/web_applications/web_app_provider.h" +#include "content/public/browser/web_contents.h" + +namespace web_app { + +namespace { + +void RecordEngagementHistogram( + const std::string& histogram_name, + SiteEngagementService::EngagementType engagement_type) { + base::UmaHistogramEnumeration(histogram_name, engagement_type, + SiteEngagementService::ENGAGEMENT_LAST); +} + +void RecordTabOrWindowHistogram( + const std::string& histogram_prefix, + bool in_window, + SiteEngagementService::EngagementType engagement_type) { + RecordEngagementHistogram( + histogram_prefix + (in_window ? ".InWindow" : ".InTab"), engagement_type); +} + +void RecordUserInstalledHistogram( + bool from_install_button, + bool in_window, + SiteEngagementService::EngagementType engagement_type) { + const std::string histogram_prefix = "WebApp.Engagement.UserInstalled"; + RecordTabOrWindowHistogram(histogram_prefix, in_window, engagement_type); + + // Record it into more specific buckets: + RecordTabOrWindowHistogram( + histogram_prefix + (from_install_button ? ".FromInstallButton" + : ".FromCreateShortcutButton"), + in_window, engagement_type); +} + +} // namespace + +// static +WebAppMetrics* WebAppMetrics::Get(Profile* profile) { + return WebAppMetricsFactory::GetForProfile(profile); +} + +WebAppMetrics::WebAppMetrics(Profile* profile) + : SiteEngagementObserver(SiteEngagementService::Get(profile)), + profile_(profile) { + WebAppProvider* provider = WebAppProvider::Get(profile_); + DCHECK(provider); + + provider->SetRegistryReadyCallback(base::BindOnce( + &WebAppMetrics::CountUserInstalledApps, weak_ptr_factory_.GetWeakPtr())); +} + +WebAppMetrics::~WebAppMetrics() = default; + +void WebAppMetrics::OnEngagementEvent( + content::WebContents* web_contents, + const GURL& url, + double score, + SiteEngagementService::EngagementType engagement_type) { + if (!web_contents) + return; + + Browser* browser = chrome::FindBrowserWithWebContents(web_contents); + if (!browser) + return; + + // Number of apps is not yet counted. + if (num_user_installed_apps_ == kNumUserInstalledAppsNotCounted) + return; + + // The engagement broken down by the number of apps installed must be recorded + // for all engagement events, not just web apps. + if (num_user_installed_apps_ > 3) { + RecordEngagementHistogram( + "WebApp.Engagement.MoreThanThreeUserInstalledApps", engagement_type); + } else if (num_user_installed_apps_ > 0) { + RecordEngagementHistogram("WebApp.Engagement.UpToThreeUserInstalledApps", + engagement_type); + } else { + RecordEngagementHistogram("WebApp.Engagement.NoUserInstalledApps", + engagement_type); + } + + // A presence of WebAppTabHelperBase with valid app_id indicates a web app. + WebAppTabHelperBase* tab_helper = + WebAppTabHelperBase::FromWebContents(web_contents); + if (!tab_helper || tab_helper->app_id().empty()) + return; + + // No HostedAppBrowserController if app is running as a tab in common browser. + const bool in_window = !!browser->hosted_app_controller(); + const bool from_install_button = tab_helper->IsFromInstallButton(); + const bool user_installed = tab_helper->IsUserInstalled(); + + // Record all web apps: + RecordTabOrWindowHistogram("WebApp.Engagement", in_window, engagement_type); + + if (user_installed) { + RecordUserInstalledHistogram(from_install_button, in_window, + engagement_type); + } else { + // Record this app into more specific bucket if was installed by default: + RecordTabOrWindowHistogram("WebApp.Engagement.DefaultInstalled", in_window, + engagement_type); + } +} + +void WebAppMetrics::CountUserInstalledAppsForTesting() { + // Reset and re-count. + num_user_installed_apps_ = kNumUserInstalledAppsNotCounted; + CountUserInstalledApps(); +} + +void WebAppMetrics::CountUserInstalledApps() { + DCHECK_EQ(kNumUserInstalledAppsNotCounted, num_user_installed_apps_); + + WebAppProvider* provider = WebAppProvider::Get(profile_); + + num_user_installed_apps_ = provider->CountUserInstalledApps(); + DCHECK_NE(kNumUserInstalledAppsNotCounted, num_user_installed_apps_); + DCHECK_GE(num_user_installed_apps_, 0); +} + +} // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_metrics.h b/chrome/browser/ui/web_applications/web_app_metrics.h new file mode 100644 index 0000000..30646298 --- /dev/null +++ b/chrome/browser/ui/web_applications/web_app_metrics.h
@@ -0,0 +1,57 @@ +// Copyright 2018 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 CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_METRICS_H_ +#define CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_METRICS_H_ + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/engagement/site_engagement_observer.h" +#include "chrome/browser/web_applications/components/web_app_helpers.h" +#include "components/keyed_service/core/keyed_service.h" + +class Profile; + +namespace content { +class WebContents; +} // namespace content + +namespace web_app { + +// A per-profile keyed service, responsible for all Web Applications-related +// metrics recording (UMA histograms). +class WebAppMetrics : public KeyedService, public SiteEngagementObserver { + public: + static WebAppMetrics* Get(Profile* profile); + + explicit WebAppMetrics(Profile* profile); + ~WebAppMetrics() override; + + // SiteEngagementObserver: + void OnEngagementEvent( + content::WebContents* web_contents, + const GURL& url, + double score, + SiteEngagementService::EngagementType engagement_type) override; + + void CountUserInstalledAppsForTesting(); + + private: + void CountUserInstalledApps(); + + // Calculate number of user installed apps once on start to avoid cpu costs + // in OnEngagementEvent: sacrifice histograms accuracy for speed. + static constexpr int kNumUserInstalledAppsNotCounted = -1; + int num_user_installed_apps_ = kNumUserInstalledAppsNotCounted; + + Profile* profile_; + + base::WeakPtrFactory<WebAppMetrics> weak_ptr_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(WebAppMetrics); +}; + +} // namespace web_app + +#endif // CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_METRICS_H_
diff --git a/chrome/browser/ui/web_applications/web_app_metrics_factory.cc b/chrome/browser/ui/web_applications/web_app_metrics_factory.cc new file mode 100644 index 0000000..249b98e6 --- /dev/null +++ b/chrome/browser/ui/web_applications/web_app_metrics_factory.cc
@@ -0,0 +1,47 @@ +// Copyright 2018 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 "chrome/browser/ui/web_applications/web_app_metrics_factory.h" + +#include "chrome/browser/engagement/site_engagement_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/web_applications/web_app_metrics.h" +#include "chrome/browser/web_applications/web_app_provider_factory.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +namespace web_app { + +// static +WebAppMetrics* WebAppMetricsFactory::GetForProfile(Profile* profile) { + return static_cast<WebAppMetrics*>( + WebAppMetricsFactory::GetInstance()->GetServiceForBrowserContext( + profile, /*create_service=*/true)); +} + +// static +WebAppMetricsFactory* WebAppMetricsFactory::GetInstance() { + return base::Singleton<WebAppMetricsFactory>::get(); +} + +WebAppMetricsFactory::WebAppMetricsFactory() + : BrowserContextKeyedServiceFactory( + "WebAppMetrics", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(SiteEngagementServiceFactory::GetInstance()); + DependsOn(WebAppProviderFactory::GetInstance()); +} + +WebAppMetricsFactory::~WebAppMetricsFactory() = default; + +KeyedService* WebAppMetricsFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + Profile* profile = Profile::FromBrowserContext(context); + return new WebAppMetrics(profile); +} + +bool WebAppMetricsFactory::ServiceIsCreatedWithBrowserContext() const { + return false; +} + +} // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_metrics_factory.h b/chrome/browser/ui/web_applications/web_app_metrics_factory.h new file mode 100644 index 0000000..87dbc5e --- /dev/null +++ b/chrome/browser/ui/web_applications/web_app_metrics_factory.h
@@ -0,0 +1,46 @@ +// Copyright 2018 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 CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_METRICS_FACTORY_H_ +#define CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_METRICS_FACTORY_H_ + +#include "base/macros.h" +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace content { +class BrowserContext; +} + +class Profile; + +namespace web_app { + +class WebAppMetrics; + +// A factory to create WebAppMetrics. Its KeyedService shouldn't be created with +// browser context (forces the creation of SiteEngagementService otherwise). +class WebAppMetricsFactory : public BrowserContextKeyedServiceFactory { + public: + static WebAppMetrics* GetForProfile(Profile* profile); + + static WebAppMetricsFactory* GetInstance(); + + private: + friend struct base::DefaultSingletonTraits<WebAppMetricsFactory>; + + WebAppMetricsFactory(); + ~WebAppMetricsFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + bool ServiceIsCreatedWithBrowserContext() const override; + + DISALLOW_COPY_AND_ASSIGN(WebAppMetricsFactory); +}; + +} // namespace web_app + +#endif // CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_METRICS_FACTORY_H_
diff --git a/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_handler.cc b/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_handler.cc index 4721488d..e95abc1 100644 --- a/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_handler.cc +++ b/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_handler.cc
@@ -21,8 +21,8 @@ void BluetoothInternalsHandler::GetAdapter(GetAdapterCallback callback) { if (device::BluetoothAdapterFactory::IsBluetoothSupported()) { device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothInternalsHandler::OnGetAdapter, - weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback))); + base::BindOnce(&BluetoothInternalsHandler::OnGetAdapter, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } else { std::move(callback).Run(nullptr /* AdapterPtr */); }
diff --git a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc index c88d7153..b8b8f19 100644 --- a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc +++ b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
@@ -206,8 +206,8 @@ chromeos::DBusThreadManager::Get()->GetPowerManagerClient())), weak_ptr_factory_(this) { device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&DeviceEmulatorMessageHandler::BluetoothDeviceAdapterReady, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce(&DeviceEmulatorMessageHandler::BluetoothDeviceAdapterReady, + weak_ptr_factory_.GetWeakPtr())); } DeviceEmulatorMessageHandler::~DeviceEmulatorMessageHandler() {}
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc index 916c7c9e..7d3874c5 100644 --- a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc
@@ -53,8 +53,6 @@ IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_INSTALL_APPS_DESCRIPTION}, {"startSetupPageFeatureListAddFeatures", IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_ADD_FEATURES}, - {"setupFailedPageHeader", IDS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_HEADER}, - {"setupFailedPageMessage", IDS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_MESSAGE}, {"setupSucceededPageHeader", IDS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_PAGE_HEADER}, {"setupSucceededPageMessage",
diff --git a/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc b/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc index dceb17b1..6674ed4 100644 --- a/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc +++ b/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc
@@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/command_line.h" #include "base/memory/ref_counted.h" #include "base/memory/ref_counted_memory.h" #include "base/memory/weak_ptr.h" @@ -21,7 +20,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/ui/webui/print_preview/print_preview_utils.h" -#include "chrome/common/chrome_switches.h" #include "services/identity/public/cpp/identity_manager.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "ui/gfx/geometry/size.h" @@ -36,15 +34,9 @@ const cloud_print::DeviceDescription& description, bool has_local_printing, base::DictionaryValue* printer_value) { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - printer_value->SetString("serviceName", name); printer_value->SetString("name", description.name); printer_value->SetBoolean("hasLocalPrinting", has_local_printing); - printer_value->SetBoolean( - "isUnregistered", - description.id.empty() && - command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos)); printer_value->SetString("cloudID", description.id); } } // namespace @@ -103,14 +95,9 @@ const std::string& name, bool has_local_printing, const cloud_print::DeviceDescription& description) { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (!added_printers_callback_ || - (!has_local_printing && - !command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos))) { - // If Print Preview is not expecting this printer (callback reset or no - // registration promos and not a local printer), return early. + if (!added_printers_callback_ || !has_local_printing) return; - } + auto printer_info = std::make_unique<base::DictionaryValue>(); FillPrinterDescription(name, description, has_local_printing, printer_info.get());
diff --git a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc index 2af0d2c1..67db5ae 100644 --- a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc +++ b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
@@ -15,18 +15,6 @@ #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" -#include "chrome/browser/browsing_data/browsing_data_appcache_helper.h" -#include "chrome/browser/browsing_data/browsing_data_cache_storage_helper.h" -#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h" -#include "chrome/browser/browsing_data/browsing_data_database_helper.h" -#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h" -#include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h" -#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h" -#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h" -#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h" -#include "chrome/browser/browsing_data/browsing_data_quota_helper.h" -#include "chrome/browser/browsing_data/browsing_data_service_worker_helper.h" -#include "chrome/browser/browsing_data/browsing_data_shared_worker_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/cookies_tree_model_util.h" #include "chrome/grit/generated_resources.h" @@ -44,9 +32,6 @@ namespace { -constexpr char kEffectiveTopLevelDomainPlus1Name[] = "etldPlus1"; -constexpr char kNumCookies[] = "numCookies"; - int GetCategoryLabelID(CookieTreeNode::DetailedInfo::NodeType node_type) { constexpr struct { CookieTreeNode::DetailedInfo::NodeType node_type; @@ -171,10 +156,6 @@ base::BindRepeating(&CookiesViewHandler::HandleGetCookieDetails, base::Unretained(this))); web_ui()->RegisterMessageCallback( - "localData.getNumCookiesList", - base::BindRepeating(&CookiesViewHandler::HandleGetNumCookiesList, - base::Unretained(this))); - web_ui()->RegisterMessageCallback( "localData.getNumCookiesString", base::BindRepeating(&CookiesViewHandler::HandleGetNumCookiesString, base::Unretained(this))); @@ -258,33 +239,7 @@ void CookiesViewHandler::EnsureCookiesTreeModelCreated() { if (!cookies_tree_model_.get()) { Profile* profile = Profile::FromWebUI(web_ui()); - content::StoragePartition* storage_partition = - content::BrowserContext::GetDefaultStoragePartition(profile); - content::IndexedDBContext* indexed_db_context = - storage_partition->GetIndexedDBContext(); - content::ServiceWorkerContext* service_worker_context = - storage_partition->GetServiceWorkerContext(); - content::CacheStorageContext* cache_storage_context = - storage_partition->GetCacheStorageContext(); - storage::FileSystemContext* file_system_context = - storage_partition->GetFileSystemContext(); - auto container = std::make_unique<LocalDataContainer>( - new BrowsingDataCookieHelper(storage_partition), - new BrowsingDataDatabaseHelper(profile), - new BrowsingDataLocalStorageHelper(profile), - /*session_storage_helper=*/nullptr, - new BrowsingDataAppCacheHelper(profile), - new BrowsingDataIndexedDBHelper(indexed_db_context), - BrowsingDataFileSystemHelper::Create(file_system_context), - BrowsingDataQuotaHelper::Create(profile), - new BrowsingDataServiceWorkerHelper(service_worker_context), - new BrowsingDataSharedWorkerHelper(storage_partition, - profile->GetResourceContext()), - new BrowsingDataCacheStorageHelper(cache_storage_context), - BrowsingDataFlashLSOHelper::Create(profile), - BrowsingDataMediaLicenseHelper::Create(file_system_context)); - cookies_tree_model_ = std::make_unique<CookiesTreeModel>( - std::move(container), profile->GetExtensionSpecialStoragePolicy()); + cookies_tree_model_ = CookiesTreeModel::CreateForProfile(profile); cookies_tree_model_->AddCookiesTreeObserver(this); } } @@ -309,64 +264,6 @@ SendCookieDetails(node); } -void CookiesViewHandler::HandleGetNumCookiesList(const base::ListValue* args) { - CHECK_EQ(2U, args->GetSize()); - std::string callback_id; - CHECK(args->GetString(0, &callback_id)); - const base::ListValue* etld_plus1_list; - CHECK(args->GetList(1, &etld_plus1_list)); - - AllowJavascript(); - CHECK(cookies_tree_model_.get()); - - base::string16 etld_plus1; - base::Value result(base::Value::Type::LIST); - for (size_t i = 0; i < etld_plus1_list->GetSize(); ++i) { - etld_plus1_list->GetString(i, &etld_plus1); - // This method is only interested in the number of cookies, so don't save - // |etld_plus1| as a new filter and keep the existing |sorted_sites_| list. - cookies_tree_model_->UpdateSearchResults(etld_plus1); - - int num_cookies = 0; - const CookieTreeNode* root = cookies_tree_model_->GetRoot(); - for (int i = 0; i < root->child_count(); ++i) { - const CookieTreeNode* site = root->GetChild(i); - const base::string16& title = site->GetTitle(); - if (!base::EndsWith(title, etld_plus1, - base::CompareCase::INSENSITIVE_ASCII)) { - continue; - } - - for (int j = 0; j < site->child_count(); ++j) { - const CookieTreeNode* category = site->GetChild(j); - if (category->GetDetailedInfo().node_type != - CookieTreeNode::DetailedInfo::TYPE_COOKIES) { - continue; - } - - for (int k = 0; k < category->child_count(); ++k) { - if (category->GetChild(k)->GetDetailedInfo().node_type != - CookieTreeNode::DetailedInfo::TYPE_COOKIE) { - continue; - } - - ++num_cookies; - } - } - } - - base::Value cookies_per_etld_plus1(base::Value::Type::DICTIONARY); - cookies_per_etld_plus1.SetKey(kEffectiveTopLevelDomainPlus1Name, - base::Value(etld_plus1)); - cookies_per_etld_plus1.SetKey(kNumCookies, base::Value(num_cookies)); - result.GetList().emplace_back(std::move(cookies_per_etld_plus1)); - } - ResolveJavascriptCallback(base::Value(callback_id), result); - - // Restore the original |filter_|. - cookies_tree_model_->UpdateSearchResults(filter_); -} - void CookiesViewHandler::HandleGetNumCookiesString( const base::ListValue* args) { CHECK_EQ(2U, args->GetSize());
diff --git a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h index bba06ea..d8debac0 100644 --- a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h +++ b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h
@@ -61,11 +61,6 @@ // Retrieve cookie details for a specific site. void HandleGetCookieDetails(const base::ListValue* args); - // Gets a list containing the number of cookies for each domain (eTLD+1 names) - // given in |siteList|. This will always return a result array the same length - // and in the same order as |siteList|. - void HandleGetNumCookiesList(const base::ListValue* args); - // Gets a plural string for the given number of cookies. void HandleGetNumCookiesString(const base::ListValue* args);
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc index e6bc8dc..4404625 100644 --- a/chrome/browser/ui/webui/settings/site_settings_handler.cc +++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -16,6 +16,7 @@ #include "base/i18n/number_formatting.h" #include "base/macros.h" #include "base/metrics/user_metrics.h" +#include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" @@ -46,6 +47,7 @@ #include "components/prefs/pref_service.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" +#include "content/public/browser/storage_usage_info.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "content/public/common/origin_util.h" @@ -142,7 +144,7 @@ // Groups |url| into sets of eTLD+1s in |site_group_map|, assuming |url| is an // origin. -void CreateOrAppendSiteGroupEntry( +void CreateOrAppendSiteGroupEntryForUrl( std::map<std::string, std::set<std::string>>* site_group_map, const GURL& url) { std::string etld_plus1_string = @@ -152,9 +154,65 @@ if (entry == site_group_map->end()) { site_group_map->emplace(etld_plus1_string, std::set<std::string>({url.spec()})); - } else { - entry->second.insert(url.spec()); + return; } + entry->second.insert(url.spec()); +} + +// Update the storage data in |origin_size_map|. +void UpdateDataForOrigin(const GURL& url, + const int size, + std::map<std::string, int>* origin_size_map) { + if (size > 0) + (*origin_size_map)[url.spec()] += size; +} + +// Groups |host| into sets of eTLD+1s in |site_group_map|, |host| can either +// be an eTLD+1 or an origin. +// There are three cases: +// 1. The ETLD+1 of |host| is not yet in |site_group_map|. We add the ETLD+1 +// to |site_group_map| +// 2. The ETLD+1 of |host| is in |site_group_map|, and is equal to |host|. +// This means case 1 has already happened and nothing more needs to be +// done. +// 3. The ETLD+1 of |host| is in |site_group_map| and is different to |host|. +// In case 3, we try to add |host| to the set of origins for the ETLD+1. +// This requires adding a scheme. We initially check if a HTTPS scheme has +// been added for the host, and fall back to HTTP if not. +void CreateOrAppendSiteGroupEntryForCookieHost( + const std::string& host, + std::map<std::string, std::set<std::string>>* site_group_map) { + std::string etld_plus1_string = + net::registry_controlled_domains::GetDomainAndRegistry( + host, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); + auto entry = site_group_map->find(etld_plus1_string); + if (entry == site_group_map->end()) { + site_group_map->emplace( + etld_plus1_string, + // Add etld+1 origin as place holder. + std::set<std::string>({std::string(url::kHttpScheme) + + url::kStandardSchemeSeparator + host + "/"})); + return; + } + // If |host| equals to etld_plus1_string that is already exists in + // |site_group_map|, nothing need to be done. + if (host == etld_plus1_string) + return; + // Cookies ignore schemes, so try and see if a https schemed version + // already exists in the origin list, if not, then add the http schemed + // version into the map. + std::string https_url = std::string(url::kHttpsScheme) + + url::kStandardSchemeSeparator + host + "/"; + if (entry->second.find(https_url) != entry->second.end()) + return; + entry->second.insert(std::string(url::kHttpScheme) + + url::kStandardSchemeSeparator + host + "/"); + // Check if we have an etld_plus1 url place holder, if there is, delete + // that entry. + std::string etld_plus1_url = std::string(url::kHttpScheme) + + url::kStandardSchemeSeparator + + etld_plus1_string + "/"; + entry->second.erase(etld_plus1_url); } // Converts a given |site_group_map| to a list of base::DictionaryValues, adding @@ -180,6 +238,7 @@ "engagement", base::Value(engagement_service->GetScore(GURL(origin)))); origin_object.SetKey("usage", base::Value(0)); + origin_object.SetKey(kNumCookies, base::Value(0)); origin_list.GetList().emplace_back(std::move(origin_object)); } site_group.SetKey(kNumCookies, base::Value(0)); @@ -222,18 +281,29 @@ return true; } +void UpdateDataFromCookiesTree( + std::map<std::string, std::set<std::string>>* all_sites_map, + std::map<std::string, int>* origin_size_map, + const url::Origin& origin, + int64_t size) { + GURL origin_gurl = origin.GetURL(); + UpdateDataForOrigin(origin_gurl, size, origin_size_map); + CreateOrAppendSiteGroupEntryForUrl(all_sites_map, origin_gurl); +} + } // namespace SiteSettingsHandler::SiteSettingsHandler(Profile* profile) - : profile_(profile), - observer_(this), - pref_change_registrar_(nullptr), - local_storage_helper_(nullptr) {} + : profile_(profile), observer_(this), pref_change_registrar_(nullptr) {} SiteSettingsHandler::~SiteSettingsHandler() { + if (cookies_tree_model_) + cookies_tree_model_->RemoveCookiesTreeObserver(this); } void SiteSettingsHandler::RegisterMessages() { + EnsureCookiesTreeModelCreated(); + web_ui()->RegisterMessageCallback( "fetchUsageTotal", base::BindRepeating(&SiteSettingsHandler::HandleFetchUsageTotal, @@ -645,6 +715,8 @@ const base::ListValue* types; CHECK(args->GetList(1, &types)); + all_sites_map_.clear(); + // Convert |types| to a list of ContentSettingsTypes. std::vector<ContentSettingsType> content_types; for (size_t i = 0; i < types->GetSize(); ++i) { @@ -664,14 +736,6 @@ DCHECK(profile); HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile); - std::map<std::string, std::set<std::string>> all_sites_map; - - // TODO(https://crbug.com/835712): Assess performance of this method for - // unusually large numbers of stored content settings. - - // Add sites that are using any local storage to the list. - GetLocalStorageHelper()->StartFetching(base::BindRepeating( - &SiteSettingsHandler::OnLocalStorageFetched, base::Unretained(this))); // Retrieve a list of embargoed settings to check separately. This ensures // that only settings included in |content_types| will be listed in all sites. @@ -688,7 +752,7 @@ permission_manager->GetPermissionStatus(content_type, url, url); if (result.source == PermissionStatusSource::MULTIPLE_DISMISSALS || result.source == PermissionStatusSource::MULTIPLE_IGNORES) { - CreateOrAppendSiteGroupEntry(&all_sites_map, url); + CreateOrAppendSiteGroupEntryForUrl(&all_sites_map_, url); break; } } @@ -701,43 +765,74 @@ map->GetSettingsForOneType(content_type, std::string(), &entries); for (const ContentSettingPatternSource& e : entries) { if (PatternAppliesToSingleOrigin(e)) - CreateOrAppendSiteGroupEntry(&all_sites_map, - GURL(e.primary_pattern.ToString())); + CreateOrAppendSiteGroupEntryForUrl(&all_sites_map_, + GURL(e.primary_pattern.ToString())); } } + // Recreate the cookies tree model to refresh the usage information. + // This happens in the background and will call TreeModelEndBatch() when + // finished. At that point we send usage data to the page. + if (cookies_tree_model_) + cookies_tree_model_->RemoveCookiesTreeObserver(this); + cookies_tree_model_.reset(); + EnsureCookiesTreeModelCreated(); + base::Value result(base::Value::Type::LIST); - ConvertSiteGroupMapToListValue(all_sites_map, &result, profile); + + // Respond with currently available data. + ConvertSiteGroupMapToListValue(all_sites_map_, &result, profile); + should_send_list_ = true; + ResolveJavascriptCallback(*callback_id, result); } -void SiteSettingsHandler::OnLocalStorageFetched( - const std::list<BrowsingDataLocalStorageHelper::LocalStorageInfo>& - local_storage_info) { +base::Value SiteSettingsHandler::PopulateCookiesAndUsageData(Profile* profile) { std::map<std::string, int> origin_size_map; - std::map<std::string, std::set<std::string>> all_sites_map; - for (const BrowsingDataLocalStorageHelper::LocalStorageInfo& info : - local_storage_info) { - origin_size_map.emplace(info.origin_url.spec(), info.size); - CreateOrAppendSiteGroupEntry(&all_sites_map, info.origin_url); - } - base::Value result(base::Value::Type::LIST); - ConvertSiteGroupMapToListValue(all_sites_map, &result, profile_); + std::map<std::string, int> origin_cookie_map; + base::Value list_value(base::Value::Type::LIST); - // Merge the origin usage number into |result|. - for (size_t i = 0; i < result.GetList().size(); ++i) { - base::Value* site_group = &result.GetList()[i]; - base::Value* origin_list = site_group->FindKey(kOriginList); + GetOriginStorage(&all_sites_map_, &origin_size_map); + GetOriginCookies(&all_sites_map_, &origin_cookie_map); + ConvertSiteGroupMapToListValue(all_sites_map_, &list_value, profile); - for (size_t i = 0; i < origin_list->GetList().size(); ++i) { - base::Value* origin_info = &origin_list->GetList()[i]; - const std::string& origin = origin_info->FindKey("origin")->GetString(); - const auto& size_info = origin_size_map.find(origin); - if (size_info != origin_size_map.end()) - origin_info->SetKey("usage", base::Value(size_info->second)); + // Merge the origin usage and cookies number into |list_value|. + for (base::Value& site_group : list_value.GetList()) { + base::Value* origin_list = site_group.FindKey(kOriginList); + int cookie_num = 0; + + const std::string& etld_plus1 = + site_group.FindKey(kEffectiveTopLevelDomainPlus1Name)->GetString(); + const auto& etld_plus1_cookie_num_it = origin_cookie_map.find(etld_plus1); + // Add the number of eTLD+1 scoped cookies. + if (etld_plus1_cookie_num_it != origin_cookie_map.end()) + cookie_num = etld_plus1_cookie_num_it->second; + // Iterate over the origins for the ETLD+1, and set their usage and cookie + // numbers. + for (base::Value& origin_info : origin_list->GetList()) { + const std::string& origin = origin_info.FindKey("origin")->GetString(); + const auto& size_info_it = origin_size_map.find(origin); + if (size_info_it != origin_size_map.end()) + origin_info.SetKey("usage", base::Value(size_info_it->second)); + const auto& origin_cookie_num_it = + origin_cookie_map.find(GURL(origin).host()); + // Add cookies numbers for origins that isn't an eTLD+1. + if (GURL(origin).host() != etld_plus1 && + origin_cookie_num_it != origin_cookie_map.end()) { + origin_info.SetKey(kNumCookies, + base::Value(origin_cookie_num_it->second)); + cookie_num += origin_cookie_num_it->second; + } } + site_group.SetKey(kNumCookies, base::Value(cookie_num)); } - FireWebUIListener("onLocalStorageListFetched", result); + return list_value; +} + +void SiteSettingsHandler::OnStorageFetched() { + AllowJavascript(); + FireWebUIListener("onStorageListFetched", + PopulateCookiesAndUsageData(profile_)); } void SiteSettingsHandler::HandleGetFormattedBytes(const base::ListValue* args) { @@ -1286,10 +1381,70 @@ profile_->GetPrefs()->SetBoolean(prefs::kBlockAutoplayEnabled, value); } -void SiteSettingsHandler::SetBrowsingDataLocalStorageHelperForTesting( - scoped_refptr<BrowsingDataLocalStorageHelper> helper) { - DCHECK(!local_storage_helper_); - local_storage_helper_ = helper; +void SiteSettingsHandler::EnsureCookiesTreeModelCreated() { + if (cookies_tree_model_.get()) + return; + cookies_tree_model_ = CookiesTreeModel::CreateForProfile(profile_); + cookies_tree_model_->AddCookiesTreeObserver(this); +} + +void SiteSettingsHandler::TreeNodesAdded(ui::TreeModel* model, + ui::TreeModelNode* parent, + int start, + int count) {} + +void SiteSettingsHandler::TreeNodesRemoved(ui::TreeModel* model, + ui::TreeModelNode* parent, + int start, + int count) {} + +void SiteSettingsHandler::TreeNodeChanged(ui::TreeModel* model, + ui::TreeModelNode* node) {} + +void SiteSettingsHandler::TreeModelEndBatch(CookiesTreeModel* model) { + if (!should_send_list_) + return; + should_send_list_ = false; + // The WebUI may have shut down before we get the data. + if (IsJavascriptAllowed()) + OnStorageFetched(); +} + +void SiteSettingsHandler::GetOriginStorage( + std::map<std::string, std::set<std::string>>* all_sites_map, + std::map<std::string, int>* origin_size_map) { + CHECK(cookies_tree_model_.get()); + + // RetrieveSize runs its callback synchronously so binding pointers to members + // is safe. + cookies_tree_model_->GetRoot()->RetrieveSize(base::BindRepeating( + &UpdateDataFromCookiesTree, all_sites_map, origin_size_map)); +} + +void SiteSettingsHandler::GetOriginCookies( + std::map<std::string, std::set<std::string>>* all_sites_map, + std::map<std::string, int>* origin_cookie_map) { + CHECK(cookies_tree_model_.get()); + // Get sites that don't have data but have cookies. + const CookieTreeNode* root = cookies_tree_model_->GetRoot(); + for (int i = 0; i < root->child_count(); ++i) { + const CookieTreeNode* site = root->GetChild(i); + std::string title = base::UTF16ToUTF8(site->GetTitle()); + for (int j = 0; j < site->child_count(); ++j) { + const CookieTreeNode* category = site->GetChild(j); + if (category->GetDetailedInfo().node_type != + CookieTreeNode::DetailedInfo::TYPE_COOKIES) + continue; + for (int k = 0; k < category->child_count(); ++k) { + const CookieTreeNode::DetailedInfo& detailedInfo = + category->GetChild(k)->GetDetailedInfo(); + if (detailedInfo.node_type != CookieTreeNode::DetailedInfo::TYPE_COOKIE) + continue; + (*origin_cookie_map)[title] += 1; + CreateOrAppendSiteGroupEntryForCookieHost(title, all_sites_map); + } + } + } } BrowsingDataLocalStorageHelper* SiteSettingsHandler::GetLocalStorageHelper() { @@ -1297,5 +1452,4 @@ local_storage_helper_ = new BrowsingDataLocalStorageHelper(profile_); return local_storage_helper_.get(); } - } // namespace settings
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.h b/chrome/browser/ui/webui/settings/site_settings_handler.h index 717eb27..275e3b96 100644 --- a/chrome/browser/ui/webui/settings/site_settings_handler.h +++ b/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -11,7 +11,7 @@ #include <vector> #include "base/scoped_observer.h" -#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h" +#include "chrome/browser/browsing_data/cookies_tree_model.h" #include "chrome/browser/storage/storage_info_fetcher.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" #include "components/content_settings/core/browser/content_settings_observer.h" @@ -35,13 +35,15 @@ // Chrome "ContentSettings" settings page UI handler. class SiteSettingsHandler : public SettingsPageUIHandler, public content_settings::Observer, - public content::NotificationObserver { + public content::NotificationObserver, + public CookiesTreeModel::Observer { public: explicit SiteSettingsHandler(Profile* profile); ~SiteSettingsHandler() override; // SettingsPageUIHandler: void RegisterMessages() override; + void OnJavascriptAllowed() override; void OnJavascriptDisallowed() override; @@ -51,6 +53,20 @@ blink::mojom::QuotaStatusCode code); void OnUsageCleared(); + // CookiesTreeModel::Observer: + // TODO(https://crbug.com/835712): Listen for backend data changes and notify + // WebUI + void TreeNodesAdded(ui::TreeModel* model, + ui::TreeModelNode* parent, + int start, + int count) override; + void TreeNodesRemoved(ui::TreeModel* model, + ui::TreeModelNode* parent, + int start, + int count) override; + void TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* node) override; + void TreeModelEndBatch(CookiesTreeModel* model) override; + #if defined(OS_CHROMEOS) // Alert the Javascript that the |kEnableDRM| pref has changed. void OnPrefEnableDrmChanged(); @@ -102,6 +118,21 @@ FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, SessionOnlyException); FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ZoomLevels); + // Creates the CookiesTreeModel if necessary. + void EnsureCookiesTreeModelCreated(); + + // Calculates the data storage that has been used for each origin, and + // stores the information in the |all_sites_map| and |origin_size_map|. + void GetOriginStorage( + std::map<std::string, std::set<std::string>>* all_sites_map, + std::map<std::string, int>* origin_size_map); + + // Calculates the number of cookies for each etld+1 and each origin, and + // stores the information in the |all_sites_map| and |origin_cookie_map|. + void GetOriginCookies( + std::map<std::string, std::set<std::string>>* all_sites_map, + std::map<std::string, int>* origin_cookie_map); + // Asynchronously fetches the usage for a given origin. Replies back with // OnGetUsageInfo above. void HandleFetchUsageTotal(const base::ListValue* args); @@ -119,15 +150,19 @@ void HandleSetDefaultValueForContentType(const base::ListValue* args); void HandleGetDefaultValueForContentType(const base::ListValue* args); - // Returns a list of sites, grouped by their effective top level domain plus - // 1, affected by any of the content settings specified in |args|. + // Returns a list of sites with permissions settings, grouped by their + // eTLD+1. Recreates the cookies tree model to fetch the cookie and usage + // data, which will send the list of sites with cookies or usage data to + // the front end when fetching finished. void HandleGetAllSites(const base::ListValue* args); - // Called when the list of origins using local storage has been fetched, and - // sends this list back to the front end. - void OnLocalStorageFetched( - const std::list<BrowsingDataLocalStorageHelper::LocalStorageInfo>& - local_storage_info); + // Called when the list of origins using storage has been fetched, and sends + // this list back to the front end. + void OnStorageFetched(); + + // Returns a list of sites, grouped by their effective top level domain plus + // 1, with their cookies number and data usage information. + base::Value PopulateCookiesAndUsageData(Profile* profile); // Converts a given number of bytes into a human-readable format, with data // units. @@ -186,9 +221,6 @@ // Updates the block autoplay enabled pref when the UI is toggled. void HandleSetBlockAutoplayEnabled(const base::ListValue* args); - void SetBrowsingDataLocalStorageHelperForTesting( - scoped_refptr<BrowsingDataLocalStorageHelper> helper); - BrowsingDataLocalStorageHelper* GetLocalStorageHelper(); Profile* profile_; @@ -213,6 +245,14 @@ scoped_refptr<BrowsingDataLocalStorageHelper> local_storage_helper_; + std::unique_ptr<CookiesTreeModel> cookies_tree_model_; + + // Whether to send all sites list on cookie tree model update. + bool should_send_list_ = false; + + // Populated every time the user reloads the All Sites page. + std::map<std::string, std::set<std::string>> all_sites_map_; + DISALLOW_COPY_AND_ASSIGN(SiteSettingsHandler); };
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc index 9bd15c4..fe08163 100644 --- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc +++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -616,60 +616,7 @@ run_loop.RunUntilIdle(); } -TEST_F(SiteSettingsHandlerTest, GetAllSitesLocalStorage) { - scoped_refptr<MockBrowsingDataLocalStorageHelper> - mock_browsing_data_local_storage_helper = - new MockBrowsingDataLocalStorageHelper(profile()); - handler()->SetBrowsingDataLocalStorageHelperForTesting( - mock_browsing_data_local_storage_helper); - - // Add local storage for |origin|. - const GURL origin("https://example.com:12378"); - mock_browsing_data_local_storage_helper->AddLocalStorageForOrigin(origin, 1); - - // Check these sites are included in the callback. - base::ListValue get_all_sites_args; - get_all_sites_args.AppendString(kCallbackId); - base::Value category_list(base::Value::Type::LIST); - get_all_sites_args.GetList().push_back(std::move(category_list)); - - // Wait for the fetch handler to finish, then check it includes |origin| in - // its result. - handler()->HandleGetAllSites(&get_all_sites_args); - EXPECT_EQ(1U, web_ui()->call_data().size()); - mock_browsing_data_local_storage_helper->Notify(); - EXPECT_EQ(2U, web_ui()->call_data().size()); - base::RunLoop run_loop; - run_loop.RunUntilIdle(); - - const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); - EXPECT_EQ("cr.webUIListenerCallback", data.function_name()); - - std::string callback_id; - ASSERT_TRUE(data.arg1()->GetAsString(&callback_id)); - EXPECT_EQ("onLocalStorageListFetched", callback_id); - - const base::ListValue* local_storage_list; - ASSERT_TRUE(data.arg2()->GetAsList(&local_storage_list)); - EXPECT_EQ(1U, local_storage_list->GetSize()); - - const base::DictionaryValue* site_group; - ASSERT_TRUE(local_storage_list->GetDictionary(0, &site_group)); - - std::string etld_plus1_string; - ASSERT_TRUE(site_group->GetString("etldPlus1", &etld_plus1_string)); - ASSERT_EQ("example.com", etld_plus1_string); - - const base::ListValue* origin_list; - ASSERT_TRUE(site_group->GetList("origins", &origin_list)); - EXPECT_EQ(1U, origin_list->GetSize()); - - const base::DictionaryValue* origin_info; - ASSERT_TRUE(origin_list->GetDictionary(0, &origin_info)); - EXPECT_EQ(origin.spec(), origin_info->FindKey("origin")->GetString()); - EXPECT_EQ(0, origin_info->FindKey("engagement")->GetDouble()); - EXPECT_EQ(1, origin_info->FindKey("usage")->GetDouble()); -} +// TODO(https://crbug.com/835712): Cookie Tree Model Data fetching calculation. TEST_F(SiteSettingsHandlerTest, Origins) { const std::string google("https://www.google.com:443");
diff --git a/chrome/browser/ui/webui/welcome/nux_helper.cc b/chrome/browser/ui/webui/welcome/nux_helper.cc index e74a933..39f1d9c 100644 --- a/chrome/browser/ui/webui/welcome/nux_helper.cc +++ b/chrome/browser/ui/webui/welcome/nux_helper.cc
@@ -64,10 +64,8 @@ if (onboard_group.empty()) return false; - // User will be tied to their original onboarding group, even after - // experiment ends. - ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial( - "NaviOnboardingSynthetic", onboard_group); + // TODO(hcarmona): Re-enable synthetic trial reporting when we know more + // about https://crbug.com/919705 return base::FeatureList::IsEnabled(nux::kNuxOnboardingFeature); #else
diff --git a/chrome/browser/vr/model/web_vr_model.h b/chrome/browser/vr/model/web_vr_model.h index 43dae76..65d551df 100644 --- a/chrome/browser/vr/model/web_vr_model.h +++ b/chrome/browser/vr/model/web_vr_model.h
@@ -28,11 +28,11 @@ }; // Type of permission prompt visible out-of-headset on a desktop display that -// the user may want to respond to. +// the user may want to respond to. Currently this can't differentiate between +// specific permission requests, in the future we may add kPromptBluetooth etc. enum class ExternalPromptNotificationType { kPromptNone = 0, - kPromptAudio, - kPromptBluetooth, + kPromptGenericPermission, }; struct VR_BASE_EXPORT WebVrModel {
diff --git a/chrome/browser/vr/ui_host/vr_ui_host_impl.cc b/chrome/browser/vr/ui_host/vr_ui_host_impl.cc index c8d108b..2c3a45d 100644 --- a/chrome/browser/vr/ui_host/vr_ui_host_impl.cc +++ b/chrome/browser/vr/ui_host/vr_ui_host_impl.cc
@@ -4,9 +4,18 @@ #include "chrome/browser/vr/ui_host/vr_ui_host_impl.h" +#include "chrome/browser/permissions/permission_request.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ssl/security_state_tab_helper.h" #include "chrome/browser/vr/service/browser_xr_runtime.h" #include "chrome/browser/vr/service/xr_runtime_manager.h" #include "chrome/browser/vr/win/vr_browser_renderer_thread_win.h" +#include "components/strings/grit/components_strings.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "ui/base/l10n/l10n_util.h" namespace vr { @@ -32,6 +41,11 @@ // XRRuntimeManager, and the BrowserXRRuntime has been destroyed. StopUiRendering(); + + // Clean up permission observer. + if (permission_request_manager_) { + permission_request_manager_->RemoveObserver(this); + } } // static @@ -47,10 +61,40 @@ // Eventually the contents will be used to poll for permissions, or determine // what overlays should show. - if (contents) + + // permission_request_manager_ is an unowned pointer; it's owned by + // WebContents. If the WebContents change, make sure we unregister any + // pre-existing observers. We only have a non-null permission_request_manager_ + // if we successfully added an observer. + if (permission_request_manager_) { + permission_request_manager_->RemoveObserver(this); + permission_request_manager_ = nullptr; + } + + web_contents_ = contents; + if (contents) { StartUiRendering(); - else + + PermissionRequestManager::CreateForWebContents(contents); + permission_request_manager_ = + PermissionRequestManager::FromWebContents(contents); + // Attaching a permission request manager to WebContents can fail, so a + // DCHECK would be inappropriate here. If it fails, the user won't get + // notified about permission prompts, but other than that the session would + // work normally. + if (permission_request_manager_) { + permission_request_manager_->AddObserver(this); + + // There might already be a visible permission bubble from before + // we registered the observer, show the HMD message now in that case. + if (permission_request_manager_->IsBubbleVisible()) + OnBubbleAdded(); + } else { + DVLOG(1) << __func__ << ": No PermissionRequestManager"; + } + } else { StopUiRendering(); + } } void VRUiHostImpl::SetVRDisplayInfo( @@ -68,15 +112,9 @@ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DVLOG(1) << __func__; -// Only used for testing currently. To see an example overlay, enable the -// following few lines. TODO(https://crbug.com/911734): show browser UI for -// permission prompts in VR here. -#if 0 ui_rendering_thread_ = std::make_unique<VRBrowserRendererThreadWin>(); ui_rendering_thread_->Start(); ui_rendering_thread_->SetVRDisplayInfo(info_.Clone()); - ui_rendering_thread_->StartOverlay(compositor_.get()); -#endif } void VRUiHostImpl::StopUiRendering() { @@ -86,4 +124,31 @@ ui_rendering_thread_ = nullptr; } +void VRUiHostImpl::OnBubbleAdded() { + if (!ui_rendering_thread_) { + DVLOG(1) << __func__ << ": no ui_rendering_thread_"; + return; + } + + ui_rendering_thread_->StartOverlay(compositor_.get()); + + if (web_contents_) { + content::NavigationEntry* entry = + web_contents_->GetController().GetVisibleEntry(); + if (entry) { + GURL gurl = entry->GetVirtualURL(); + ui_rendering_thread_->SetLocationInfo(gurl); + } + } + + ui_rendering_thread_->SetVisibleExternalPromptNotification( + ExternalPromptNotificationType::kPromptGenericPermission); +} + +void VRUiHostImpl::OnBubbleRemoved() { + ui_rendering_thread_->SetVisibleExternalPromptNotification( + ExternalPromptNotificationType::kPromptNone); + ui_rendering_thread_->StopOverlay(); +} + } // namespace vr
diff --git a/chrome/browser/vr/ui_host/vr_ui_host_impl.h b/chrome/browser/vr/ui_host/vr_ui_host_impl.h index 8724f1f7..4d4e3e48 100644 --- a/chrome/browser/vr/ui_host/vr_ui_host_impl.h +++ b/chrome/browser/vr/ui_host/vr_ui_host_impl.h
@@ -7,6 +7,7 @@ #include "base/macros.h" #include "base/threading/thread_checker.h" +#include "chrome/browser/permissions/permission_request_manager.h" #include "chrome/browser/vr/service/browser_xr_runtime.h" #include "chrome/browser/vr/service/vr_ui_host.h" #include "content/public/browser/web_contents.h" @@ -17,7 +18,9 @@ // Concrete implementation of VRBrowserRendererHost, part of the "browser" // component. Used on the browser's main thread. -class VRUiHostImpl : public VRUiHost, public BrowserXRRuntimeObserver { +class VRUiHostImpl : public VRUiHost, + public PermissionRequestManager::Observer, + public BrowserXRRuntimeObserver { public: VRUiHostImpl(device::mojom::VRDisplayInfoPtr info, device::mojom::XRCompositorHostPtr compositor); @@ -38,9 +41,15 @@ void StartUiRendering(); void StopUiRendering(); + // PermissionRequestManager::Observer + void OnBubbleAdded() override; + void OnBubbleRemoved() override; + device::mojom::XRCompositorHostPtr compositor_; std::unique_ptr<VRBrowserRendererThreadWin> ui_rendering_thread_; device::mojom::VRDisplayInfoPtr info_; + content::WebContents* web_contents_ = nullptr; + PermissionRequestManager* permission_request_manager_ = nullptr; THREAD_CHECKER(thread_checker_);
diff --git a/chrome/browser/vr/ui_scene_creator.cc b/chrome/browser/vr/ui_scene_creator.cc index 7b623877..a86b5b2 100644 --- a/chrome/browser/vr/ui_scene_creator.cc +++ b/chrome/browser/vr/ui_scene_creator.cc
@@ -1465,13 +1465,9 @@ int message_id = 0; const gfx::VectorIcon* icon = nullptr; switch (prompt) { - case ExternalPromptNotificationType::kPromptAudio: - message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY_INFOBAR_TEXT; - icon = &vector_icons::kMicIcon; - break; - case ExternalPromptNotificationType::kPromptBluetooth: - message_id = IDS_VR_DESKTOP_BLUETOOTH_PROMPT; - icon = &kBluetoothIcon; + case ExternalPromptNotificationType::kPromptGenericPermission: + message_id = IDS_VR_DESKTOP_GENERIC_PERMISSION_PROMPT; + icon = &kOpenInBrowserIcon; break; case ExternalPromptNotificationType::kPromptNone: NOTREACHED();
diff --git a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc index f8fe647bc..c238577 100644 --- a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc +++ b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
@@ -39,8 +39,9 @@ void VRBrowserRendererThreadWin::SetLocationInfo(GURL gurl) { task_runner()->PostTask( - FROM_HERE, base::BindOnce(&VRBrowserRendererThreadWin::SetLocationInfo, - base::Unretained(this), std::move(gurl))); + FROM_HERE, + base::BindOnce(&VRBrowserRendererThreadWin::SetLocationInfoOnRenderThread, + base::Unretained(this), std::move(gurl))); } void VRBrowserRendererThreadWin::SetVisibleExternalPromptNotification( @@ -62,6 +63,7 @@ void VRBrowserRendererThreadWin::SetLocationInfoOnRenderThread(GURL gurl) { // TODO(https://crbug.com/905375): Set more of this state. Only the GURL is // currently used, so its the only thing we are setting correctly. + DCHECK(ui_) << "Must be called after StartOverlay"; LocationBarState state(gurl, security_state::SecurityLevel::SECURE, nullptr /* vector icon */, true /* display url */, false /* offline */); @@ -82,7 +84,13 @@ overlay_->SetOverlayAndWebXRVisibility(false, true); } else if (!currently_showing_ui && show_ui) { // Draw UI instead of WebXr. - overlay_->SetOverlayAndWebXRVisibility(true, false); + // + // TODO(https://crbug.com/912765): This is supposed to use (true, false), + // but setting WebXRVisibility to false doesn't currently work right. The + // content is frozen on the desktop tab while the prompt is showing in the + // headset. After dismissing the prompt, the headset view also stops + // updating, switching to SteamVR's default fallback for unresponsive apps. + overlay_->SetOverlayAndWebXRVisibility(true, true); overlay_->RequestNextOverlayPose(base::BindOnce( &VRBrowserRendererThreadWin::OnPose, base::Unretained(this))); } @@ -105,6 +113,14 @@ base::Unretained(this), std::move(overlay_info))); } +void VRBrowserRendererThreadWin::StopOverlay() { + // Post a task to the thread to stop the overlay. + task_runner()->PostTask( + FROM_HERE, + base::BindOnce(&VRBrowserRendererThreadWin::StopOverlayOnRenderThread, + base::Unretained(this))); +} + void VRBrowserRendererThreadWin::CleanUp() { browser_renderer_ = nullptr; initializing_graphics_ = nullptr; @@ -202,6 +218,11 @@ graphics_->ClearContext(); } +void VRBrowserRendererThreadWin::StopOverlayOnRenderThread() { + overlay_->SetOverlayAndWebXRVisibility(false, true); + CleanUp(); +} + void VRBrowserRendererThreadWin::OnPose(device::mojom::XRFrameDataPtr data) { if (!ShouldPauseWebXrAndDrawUI()) { // We shouldn't be showing UI. @@ -255,8 +276,10 @@ if (!success) { graphics_->ResetMemoryBuffer(); } - overlay_->RequestNextOverlayPose(base::BindOnce( - &VRBrowserRendererThreadWin::OnPose, base::Unretained(this))); + if (overlay_) { + overlay_->RequestNextOverlayPose(base::BindOnce( + &VRBrowserRendererThreadWin::OnPose, base::Unretained(this))); + } } bool VRBrowserRendererThreadWin::ShouldPauseWebXrAndDrawUI() {
diff --git a/chrome/browser/vr/win/vr_browser_renderer_thread_win.h b/chrome/browser/vr/win/vr_browser_renderer_thread_win.h index a8c0eca2..695180e 100644 --- a/chrome/browser/vr/win/vr_browser_renderer_thread_win.h +++ b/chrome/browser/vr/win/vr_browser_renderer_thread_win.h
@@ -55,6 +55,7 @@ // Methods called on the browser's main thread. void StartOverlay(device::mojom::XRCompositorHost* host); + void StopOverlay(); void SetVRDisplayInfo(device::mojom::VRDisplayInfoPtr display_info); void SetLocationInfo(GURL gurl); void SetVisibleExternalPromptNotification( @@ -67,6 +68,7 @@ // Methods called on render thread. void StartOverlayOnRenderThread( device::mojom::ImmersiveOverlayPtrInfo overlay); + void StopOverlayOnRenderThread(); void SetDisplayInfoOnRenderThread( device::mojom::VRDisplayInfoPtr display_info); void SetLocationInfoOnRenderThread(GURL gurl);
diff --git a/chrome/browser/web_applications/components/web_app_tab_helper_base.h b/chrome/browser/web_applications/components/web_app_tab_helper_base.h index d372420..c8da080 100644 --- a/chrome/browser/web_applications/components/web_app_tab_helper_base.h +++ b/chrome/browser/web_applications/components/web_app_tab_helper_base.h
@@ -46,6 +46,15 @@ content::WebContents* old_web_contents, content::WebContents* new_web_contents) override; + // These methods require an app associated with the tab (valid app_id()). + // + // Returns true if the app was installed by user, false if default installed. + virtual bool IsUserInstalled() const = 0; + // For user-installed apps: + // Returns true if the app was installed through the install button. + // Returns false if the app was installed through the create shortcut button. + virtual bool IsFromInstallButton() const = 0; + protected: // See documentation in WebContentsUserData class comment. explicit WebAppTabHelperBase(content::WebContents* web_contents);
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_tab_helper.cc b/chrome/browser/web_applications/extensions/bookmark_app_tab_helper.cc index 2389015..7577c2b0 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_tab_helper.cc +++ b/chrome/browser/web_applications/extensions/bookmark_app_tab_helper.cc
@@ -9,6 +9,8 @@ #include "chrome/browser/web_applications/extensions/bookmark_app_util.h" #include "chrome/common/chrome_features.h" #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h" +#include "content/public/browser/web_contents.h" +#include "extensions/browser/extension_registry.h" #include "extensions/common/extension.h" #include "url/gurl.h" @@ -60,4 +62,29 @@ return util::IsWebContentsInAppWindow(web_contents()); } +bool BookmarkAppTabHelper::IsUserInstalled() const { + const Extension* app = GetExtension(); + return app && !app->was_installed_by_default(); +} + +bool BookmarkAppTabHelper::IsFromInstallButton() const { + const bool pwa_windowing = + base::FeatureList::IsEnabled(::features::kDesktopPWAWindowing); + const Extension* app = GetExtension(); + // TODO(loyso): Use something better to record apps installed from promoted + // UIs. crbug.com/774918. + return app && app->is_hosted_app() && pwa_windowing && + UrlHandlers::GetUrlHandlers(app); +} + +const Extension* BookmarkAppTabHelper::GetExtension() const { + DCHECK(!app_id().empty()); + content::BrowserContext* browser_context = + web_contents()->GetBrowserContext(); + const Extension* app = + ExtensionRegistry::Get(browser_context) + ->GetExtensionById(app_id(), ExtensionRegistry::EVERYTHING); + return app; +} + } // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_tab_helper.h b/chrome/browser/web_applications/extensions/bookmark_app_tab_helper.h index 1a95fc81..a7527c0f 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_tab_helper.h +++ b/chrome/browser/web_applications/extensions/bookmark_app_tab_helper.h
@@ -14,6 +14,8 @@ namespace extensions { +class Extension; + // Allows to associate a tab with bookmark app. class BookmarkAppTabHelper : public web_app::WebAppTabHelperBase { public: @@ -26,13 +28,20 @@ static BookmarkAppTabHelper* CreateForWebContents( content::WebContents* web_contents); - // TabHelper: + // WebAppTabHelperBase: web_app::WebAppTabHelperBase* CloneForWebContents( content::WebContents* web_contents) const override; web_app::AppId GetAppId(const GURL& url) override; bool IsInAppWindow() const override; + bool IsUserInstalled() const override; + bool IsFromInstallButton() const override; private: + // Get a pointer from app_id_. Semantically, we use app_id_ as a weak + // reference. It might become nullptr in unforeseen circumstances (Uninstall). + // TODO(loyso): Provide guarantees for app_id_. crbug.com/915034 + const Extension* GetExtension() const; + DISALLOW_COPY_AND_ASSIGN(BookmarkAppTabHelper); };
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_util.cc b/chrome/browser/web_applications/extensions/bookmark_app_util.cc index 57fdcc8..93715fb 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_util.cc +++ b/chrome/browser/web_applications/extensions/bookmark_app_util.cc
@@ -6,6 +6,7 @@ #include "base/strings/string_piece.h" #include "base/values.h" +#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/web_applications/extensions/bookmark_app_util.h" #include "chrome/common/chrome_features.h" #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h" @@ -13,6 +14,7 @@ #include "content/public/browser/browser_context.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_system.h" #include "extensions/common/extension.h" #include "extensions/common/url_pattern_set.h" #include "url/gurl.h" @@ -104,4 +106,27 @@ return nullptr; } +int CountUserInstalledBookmarkApps(content::BrowserContext* browser_context) { + // To avoid data races and inaccurate counting, ensure that ExtensionSystem is + // always ready at this point. + DCHECK(extensions::ExtensionSystem::Get(browser_context) + ->extension_service() + ->is_ready()); + + int num_user_installed = 0; + + const ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context); + for (scoped_refptr<const Extension> app : + ExtensionRegistry::Get(browser_context)->enabled_extensions()) { + if (!app->from_bookmark()) + continue; + if (!BookmarkAppIsLocallyInstalled(prefs, app.get())) + continue; + if (!app->was_installed_by_default()) + ++num_user_installed; + } + + return num_user_installed; +} + } // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_util.h b/chrome/browser/web_applications/extensions/bookmark_app_util.h index 95b49e8..af9c20a 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_util.h +++ b/chrome/browser/web_applications/extensions/bookmark_app_util.h
@@ -49,6 +49,10 @@ content::BrowserContext* browser_context, const GURL& url); +// Count a number of all bookmark apps which are installed by user +// (non default-installed apps). +int CountUserInstalledBookmarkApps(content::BrowserContext* browser_context); + } // namespace extensions #endif // CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_UTIL_H_
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc index 3dabd94..533fc40 100644 --- a/chrome/browser/web_applications/web_app_provider.cc +++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/feature_list.h" +#include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h" @@ -16,6 +17,7 @@ #include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/browser/web_applications/components/web_app_helpers.h" #include "chrome/browser/web_applications/extensions/bookmark_app_tab_helper.h" +#include "chrome/browser/web_applications/extensions/bookmark_app_util.h" #include "chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h" #include "chrome/browser/web_applications/extensions/web_app_extension_ids_map.h" #include "chrome/browser/web_applications/external_web_apps.h" @@ -34,6 +36,8 @@ #include "chrome/common/chrome_features.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/web_contents.h" +#include "extensions/browser/extension_system.h" +#include "extensions/common/one_shot_event.h" namespace web_app { @@ -51,7 +55,7 @@ return WebAppProvider::Get(profile); } -WebAppProvider::WebAppProvider(Profile* profile) { +WebAppProvider::WebAppProvider(Profile* profile) : profile_(profile) { audio_focus_id_map_ = std::make_unique<WebAppAudioFocusIdMap>(); if (base::FeatureList::IsEnabled(features::kDesktopPWAsWithoutExtensions)) @@ -80,7 +84,8 @@ install_manager_ = std::make_unique<WebAppInstallManager>( profile, std::move(install_finalizer)); - registrar_->Init(base::DoNothing()); + registrar_->Init(base::BindOnce(&WebAppProvider::OnRegistryReady, + weak_ptr_factory_.GetWeakPtr())); } void WebAppProvider::CreateBookmarkAppsSubsystems(Profile* profile) { @@ -100,6 +105,18 @@ web_app::ScanForExternalWebApps( profile, base::BindOnce(&WebAppProvider::OnScanForExternalWebApps, weak_ptr_factory_.GetWeakPtr())); + + extensions::ExtensionSystem::Get(profile)->ready().Post( + FROM_HERE, base::BindRepeating(&WebAppProvider::OnRegistryReady, + weak_ptr_factory_.GetWeakPtr())); +} + +void WebAppProvider::OnRegistryReady() { + DCHECK(!registry_is_ready_); + registry_is_ready_ = true; + + if (registry_ready_callback_) + std::move(registry_ready_callback_).Run(); } // static @@ -183,6 +200,24 @@ Reset(); } +void WebAppProvider::SetRegistryReadyCallback(base::OnceClosure callback) { + DCHECK(!registry_ready_callback_); + if (registry_is_ready_) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + std::move(callback)); + } else { + registry_ready_callback_ = std::move(callback); + } +} + +int WebAppProvider::CountUserInstalledApps() const { + // TODO: Implement for new Web Apps system. crbug.com/918986. + if (base::FeatureList::IsEnabled(features::kDesktopPWAsWithoutExtensions)) + return 0; + + return extensions::CountUserInstalledBookmarkApps(profile_); +} + void WebAppProvider::OnScanForExternalWebApps( std::vector<web_app::PendingAppManager::AppInfo> app_infos) { pending_app_manager_->SynchronizeInstalledApps(
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h index f53c6841..ad6a7dbd 100644 --- a/chrome/browser/web_applications/web_app_provider.h +++ b/chrome/browser/web_applications/web_app_provider.h
@@ -8,6 +8,7 @@ #include <memory> #include <vector> +#include "base/callback.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/web_applications/components/pending_app_manager.h" @@ -79,12 +80,23 @@ const content::NotificationSource& source, const content::NotificationDetails& details) override; + // Fires when app registry becomes ready. + // Consider to use base::ObserverList or extensions::OneShotEvent if many + // subscribers needed. + void SetRegistryReadyCallback(base::OnceClosure callback); + + // Count a number of all apps which are installed by user (non-default). + // Requires app registry to be in a ready state. + int CountUserInstalledApps() const; + private: // Create extension-independent subsystems. void CreateWebAppsSubsystems(Profile* profile); // ... or create legacy extension-based subsystems. void CreateBookmarkAppsSubsystems(Profile* profile); + void OnRegistryReady(); + void OnScanForExternalWebApps( std::vector<web_app::PendingAppManager::AppInfo>); @@ -105,6 +117,11 @@ content::NotificationRegistrar notification_registrar_; + base::OnceClosure registry_ready_callback_; + bool registry_is_ready_ = false; + + Profile* profile_; + base::WeakPtrFactory<WebAppProvider> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(WebAppProvider);
diff --git a/chrome/browser/web_applications/web_app_tab_helper.cc b/chrome/browser/web_applications/web_app_tab_helper.cc index 8ce555ee..d60225f 100644 --- a/chrome/browser/web_applications/web_app_tab_helper.cc +++ b/chrome/browser/web_applications/web_app_tab_helper.cc
@@ -41,4 +41,14 @@ return false; } +bool WebAppTabHelper::IsUserInstalled() const { + // TODO(loyso): Implement it. + return false; +} + +bool WebAppTabHelper::IsFromInstallButton() const { + // TODO(loyso): Implement it. Plumb |force_shortcut_app| into Registry. + return true; +} + } // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_tab_helper.h b/chrome/browser/web_applications/web_app_tab_helper.h index 1e6a55a..8d05261 100644 --- a/chrome/browser/web_applications/web_app_tab_helper.h +++ b/chrome/browser/web_applications/web_app_tab_helper.h
@@ -26,11 +26,13 @@ static WebAppTabHelper* CreateForWebContents( content::WebContents* web_contents); - // TabHelper: + // WebAppTabHelperBase: WebAppTabHelperBase* CloneForWebContents( content::WebContents* web_contents) const override; AppId GetAppId(const GURL& url) override; bool IsInAppWindow() const override; + bool IsUserInstalled() const override; + bool IsFromInstallButton() const override; private: DISALLOW_COPY_AND_ASSIGN(WebAppTabHelper);
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 4915fee..fd63ea0 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc
@@ -325,10 +325,6 @@ // Enables the Power overlay in Settings. const char kEnablePowerOverlay[] = "enable-power-overlay"; -// Enables showing unregistered printers in print preview -const char kEnablePrintPreviewRegisterPromos[] = - "enable-print-preview-register-promos"; - // Name of the command line flag to force content verification to be on in one // of various modes. const char kExtensionContentVerification[] = "extension-content-verification";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 1bb2a9c..ac3483d 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h
@@ -104,7 +104,6 @@ extern const char kEnableOfflineAutoReloadVisibleOnly[]; extern const char kEnablePotentiallyAnnoyingSecurityFeatures[]; extern const char kEnablePowerOverlay[]; -extern const char kEnablePrintPreviewRegisterPromos[]; extern const char kExtensionContentVerification[]; extern const char kExtensionContentVerificationBootstrap[]; extern const char kExtensionContentVerificationEnforce[];
diff --git a/chrome/common/page_load_metrics/page_load_metrics.mojom b/chrome/common/page_load_metrics/page_load_metrics.mojom index 86d566e..ef7ac788 100644 --- a/chrome/common/page_load_metrics/page_load_metrics.mojom +++ b/chrome/common/page_load_metrics/page_load_metrics.mojom
@@ -36,14 +36,28 @@ // (Experimental) Time when the page's largest image is painted. mojo_base.mojom.TimeDelta? largest_image_paint; + // (Experimental) Size of the largest image of the largest image paint, by + // Size = Height * Width. + uint64 largest_image_paint_size; + // (Experimental) Time when the page's last image is painted. mojo_base.mojom.TimeDelta? last_image_paint; + // (Experimental) Size of the last image of the last image paint. + uint64 last_image_paint_size; + // (Experimental) Time when the page's largest text is painted. mojo_base.mojom.TimeDelta? largest_text_paint; + // (Experimental) Size of the largest text of the largest text paint, by + // Size = Height * Width. + uint64 largest_text_paint_size; + // (Experimental) Time when the page's last text is painted. mojo_base.mojom.TimeDelta? last_text_paint; + + // (Experimental) Size of the last text of the last text paint. + uint64 last_text_paint_size; }; // TimeDeltas below represent durations of time during the page load.
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index 3b6a46d25..73052b8b 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -353,9 +353,6 @@ for (const char* origin : kPredefinedAllowedCompositorOrigins) allowed_compositor_origins_.insert(origin); #endif - - heap_profiling::SetGCHeapAllocationHookFunctions( - &blink::WebHeap::SetAllocationHook, &blink::WebHeap::SetFreeHook); } ChromeContentRendererClient::~ChromeContentRendererClient() = default;
diff --git a/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc b/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc index c504098..0239e2f 100644 --- a/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc +++ b/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
@@ -322,18 +322,27 @@ if (perf.LargestImagePaint() > 0.0) { timing->paint_timing->largest_image_paint = ClampDelta(perf.LargestImagePaint(), start); + DCHECK(perf.LargestImagePaintSize() > 0); + timing->paint_timing->largest_image_paint_size = + perf.LargestImagePaintSize(); } if (perf.LastImagePaint() > 0.0) { timing->paint_timing->last_image_paint = ClampDelta(perf.LastImagePaint(), start); + DCHECK(perf.LastImagePaintSize() > 0); + timing->paint_timing->last_image_paint_size = perf.LastImagePaintSize(); } if (perf.LargestTextPaint() > 0.0) { timing->paint_timing->largest_text_paint = ClampDelta(perf.LargestTextPaint(), start); + DCHECK(perf.LargestTextPaintSize() > 0); + timing->paint_timing->largest_text_paint_size = perf.LargestTextPaintSize(); } if (perf.LastTextPaint() > 0.0) { timing->paint_timing->last_text_paint = ClampDelta(perf.LastTextPaint(), start); + DCHECK(perf.LastTextPaintSize() > 0); + timing->paint_timing->last_text_paint_size = perf.LastTextPaintSize(); } if (perf.ParseStart() > 0.0) timing->parse_timing->parse_start = ClampDelta(perf.ParseStart(), start);
diff --git a/chrome/service/service_utility_process_host.cc b/chrome/service/service_utility_process_host.cc index 44252ce..e1d80f3a 100644 --- a/chrome/service/service_utility_process_host.cc +++ b/chrome/service/service_utility_process_host.cc
@@ -276,7 +276,7 @@ client_task_runner_(client_task_runner), waiting_for_reply_(false), weak_ptr_factory_(this) { - child_process_host_.reset(ChildProcessHost::Create(this)); + child_process_host_ = ChildProcessHost::Create(this); } ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index e5c5bd5..50f4a07 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -942,6 +942,7 @@ "../browser/ui/views/hats/hats_browsertest.cc", "../browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc", "../browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc", + "../browser/ui/web_applications/bookmark_app_browsertest.cc", "../browser/ui/webauthn/authenticator_dialog_browsertest.cc", "../browser/ui/webui/chrome_url_data_manager_browsertest.cc", "../browser/ui/webui/chromeos/bluetooth_pairing_dialog_browsertest-inl.h", @@ -5197,9 +5198,6 @@ "../browser/extensions/updater/extension_cache_fake.cc", "../browser/extensions/updater/extension_cache_fake.h", "base/browser_perf_tests_main.cc", - "base/chrome_render_view_test.cc", - "base/chrome_render_view_test.h", - "perf/mach_ports_performancetest.cc", "perf/url_parse_perftest.cc", ] @@ -5244,9 +5242,6 @@ } else { sources -= [ "../app/chrome_version.rc.version" ] } - if (!is_mac) { - sources -= [ "perf/mach_ports_performancetest.cc" ] - } if (is_linux || is_win) { data += [
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc index f1cd4dec..22230a55 100644 --- a/chrome/test/base/in_process_browser_test.cc +++ b/chrome/test/base/in_process_browser_test.cc
@@ -94,6 +94,7 @@ #include "chrome/test/base/default_ash_event_generator_delegate.h" #include "chromeos/services/device_sync/device_sync_impl.h" #include "chromeos/services/device_sync/fake_device_sync.h" +#include "ui/aura/test/mus/change_completion_waiter.h" #include "ui/aura/test/ui_controls_factory_aura.h" #include "ui/aura/window.h" #include "ui/base/test/ui_controls.h" @@ -351,6 +352,9 @@ content::Source<Browser>(browser)); CloseBrowserAsynchronously(browser); observer.Wait(); +#if defined(OS_CHROMEOS) + aura::test::WaitForAllChangesToComplete(); +#endif } void InProcessBrowserTest::CloseBrowserAsynchronously(Browser* browser) {
diff --git a/chrome/test/base/testing_browser_process.cc b/chrome/test/base/testing_browser_process.cc index 44d0889..507ddbe 100644 --- a/chrome/test/base/testing_browser_process.cc +++ b/chrome/test/base/testing_browser_process.cc
@@ -291,7 +291,7 @@ NotificationUIManager* TestingBrowserProcess::notification_ui_manager() { #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) if (!notification_ui_manager_.get()) - notification_ui_manager_.reset(NotificationUIManager::Create()); + notification_ui_manager_ = NotificationUIManager::Create(); return notification_ui_manager_.get(); #else NOTIMPLEMENTED();
diff --git a/chrome/test/data/service_worker/create_service_worker.html b/chrome/test/data/service_worker/create_service_worker.html index 0f2c75e7..42ec374 100644 --- a/chrome/test/data/service_worker/create_service_worker.html +++ b/chrome/test/data/service_worker/create_service_worker.html
@@ -2,11 +2,30 @@ <title>create service worker</title> <script> // Copy of //content/test/data/service_worker/create_service_worker.html. + +// Simulates navigator.serviceWorker.ready, which can't be used since this +// document may not be in-scope. +async function whenReady(registration) { + if (registration.active) + return; + // If there's no .active, .waiting will activate before .installing. + const nextActiveWorker = registration.waiting || registration.installing; + return new Promise((resolve, reject) => { + nextActiveWorker.addEventListener('statechange', event => { + if (nextActiveWorker.state == 'activated') + resolve(); + if (nextActiveWorker.state == 'redundant') + reject('worker became redundant'); + }); + }); +} + async function register(worker_url, scope) { try { const init = scope ? {scope} : {}; - await navigator.serviceWorker.register(worker_url, init); - await navigator.serviceWorker.ready; + const registration = + await navigator.serviceWorker.register(worker_url, init); + await whenReady(registration); return 'DONE'; } catch (error) { return `${error}`;
diff --git a/chrome/test/data/service_worker/empty.js b/chrome/test/data/service_worker/empty.js new file mode 100644 index 0000000..b8e3cd3 --- /dev/null +++ b/chrome/test/data/service_worker/empty.js
@@ -0,0 +1,5 @@ +// Copyright 2018 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. + +// empty
diff --git a/chrome/test/data/service_worker/empty.js.mock-http-headers b/chrome/test/data/service_worker/empty.js.mock-http-headers new file mode 100644 index 0000000..603dac83 --- /dev/null +++ b/chrome/test/data/service_worker/empty.js.mock-http-headers
@@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Content-Type: text/javascript +Service-Worker-Allowed: /
diff --git a/chrome/test/data/service_worker/network_fallback_worker.js b/chrome/test/data/service_worker/network_fallback_worker.js new file mode 100644 index 0000000..d29fe13 --- /dev/null +++ b/chrome/test/data/service_worker/network_fallback_worker.js
@@ -0,0 +1,7 @@ +// Copyright 2018 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. + +self.addEventListener('fetch', event => { + // Fall back to network. +});
diff --git a/chrome/test/data/service_worker/network_fallback_worker.js.mock-http-headers b/chrome/test/data/service_worker/network_fallback_worker.js.mock-http-headers new file mode 100644 index 0000000..603dac83 --- /dev/null +++ b/chrome/test/data/service_worker/network_fallback_worker.js.mock-http-headers
@@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Content-Type: text/javascript +Service-Worker-Allowed: /
diff --git a/chrome/test/data/service_worker/respond_with_fetch_worker.js b/chrome/test/data/service_worker/respond_with_fetch_worker.js new file mode 100644 index 0000000..dedd3c3 --- /dev/null +++ b/chrome/test/data/service_worker/respond_with_fetch_worker.js
@@ -0,0 +1,7 @@ +// Copyright 2018 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. + +self.addEventListener('fetch', event => { + event.respondWith(fetch(event.request)); +});
diff --git a/chrome/test/data/service_worker/respond_with_fetch_worker.js.mock-http-headers b/chrome/test/data/service_worker/respond_with_fetch_worker.js.mock-http-headers new file mode 100644 index 0000000..603dac83 --- /dev/null +++ b/chrome/test/data/service_worker/respond_with_fetch_worker.js.mock-http-headers
@@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Content-Type: text/javascript +Service-Worker-Allowed: /
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js index 8057ee7..9e327c6 100644 --- a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js +++ b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js
@@ -31,9 +31,7 @@ // Create destinations. nativeLayer = new print_preview.NativeLayerStub(); print_preview.NativeLayer.setInstance(nativeLayer); - const userInfo = new print_preview.UserInfo(); - destinationStore = - print_preview_test_utils.createDestinationStore(userInfo); + destinationStore = print_preview_test_utils.createDestinationStore(); const localDestinations = []; const destinations = print_preview_test_utils.getDestinations( nativeLayer, localDestinations); @@ -47,9 +45,10 @@ // Set up dialog dialog = document.createElement('print-preview-destination-dialog'); - dialog.userInfo = userInfo; + dialog.activeUser = ''; + dialog.users = []; dialog.destinationStore = destinationStore; - dialog.invitationStore = new print_preview.InvitationStore(userInfo); + dialog.invitationStore = new print_preview.InvitationStore(); dialog.recentDestinations = recentDestinations; document.body.appendChild(dialog); return nativeLayer.whenCalled('getPrinterCapabilities');
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_test.js b/chrome/test/data/webui/print_preview/destination_dialog_test.js index 81493ada..d4ce0094 100644 --- a/chrome/test/data/webui/print_preview/destination_dialog_test.js +++ b/chrome/test/data/webui/print_preview/destination_dialog_test.js
@@ -18,9 +18,6 @@ /** @type {?print_preview.DestinationStore} */ let destinationStore = null; - /** @type {?print_preview.UserInfo} */ - let userInfo = null; - /** @type {?print_preview.NativeLayer} */ let nativeLayer = null; @@ -43,9 +40,7 @@ // Create data classes nativeLayer = new print_preview.NativeLayerStub(); print_preview.NativeLayer.setInstance(nativeLayer); - userInfo = new print_preview.UserInfo(); - destinationStore = - print_preview_test_utils.createDestinationStore(userInfo); + destinationStore = print_preview_test_utils.createDestinationStore(); destinations = print_preview_test_utils.getDestinations( nativeLayer, localDestinations); recentDestinations = @@ -58,9 +53,10 @@ // Set up dialog dialog = document.createElement('print-preview-destination-dialog'); - dialog.userInfo = userInfo; + dialog.activeUser = ''; + dialog.users = []; dialog.destinationStore = destinationStore; - dialog.invitationStore = new print_preview.InvitationStore(userInfo); + dialog.invitationStore = new print_preview.InvitationStore(); dialog.recentDestinations = recentDestinations; document.body.appendChild(dialog); return nativeLayer.whenCalled('getPrinterCapabilities')
diff --git a/chrome/test/data/webui/print_preview/destination_search_test.js b/chrome/test/data/webui/print_preview/destination_search_test.js index 114ec68..d2817c6 100644 --- a/chrome/test/data/webui/print_preview/destination_search_test.js +++ b/chrome/test/data/webui/print_preview/destination_search_test.js
@@ -22,9 +22,6 @@ /** @type {?print_preview.DestinationStore} */ let destinationStore = null; - /** @type {?print_preview.UserInfo} */ - let userInfo = null; - /** @type {?print_preview.NativeLayer} */ let nativeLayer = null; @@ -38,9 +35,7 @@ // Create data classes nativeLayer = new print_preview.NativeLayerStub(); print_preview.NativeLayer.setInstance(nativeLayer); - userInfo = new print_preview.UserInfo(); - destinationStore = - print_preview_test_utils.createDestinationStore(userInfo); + destinationStore = print_preview_test_utils.createDestinationStore(); nativeLayer.setLocalDestinationCapabilities( print_preview_test_utils.getCddTemplate('FooDevice', 'FooName')); destinationStore.init( @@ -50,9 +45,10 @@ // Set up dialog dialog = document.createElement('print-preview-destination-dialog'); - dialog.userInfo = userInfo; + dialog.users = []; + dialog.activeUser = ''; dialog.destinationStore = destinationStore; - dialog.invitationStore = new print_preview.InvitationStore(userInfo); + dialog.invitationStore = new print_preview.InvitationStore(); dialog.recentDestinations = []; PolymerTest.clearBody(); document.body.appendChild(dialog);
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.js b/chrome/test/data/webui/print_preview/destination_settings_test.js index 67ece56..84fbaa23 100644 --- a/chrome/test/data/webui/print_preview/destination_settings_test.js +++ b/chrome/test/data/webui/print_preview/destination_settings_test.js
@@ -41,9 +41,8 @@ // Set up the destination store, but no destination yet. Button is // disabled. - const userInfo = new print_preview.UserInfo(); const destinationStore = - print_preview_test_utils.createDestinationStore(userInfo); + print_preview_test_utils.createDestinationStore(); destinationStore.init( false /* isInAppKioskMode */, 'FooDevice' /* printerName */, '' /* serializedDefaultDestinationSelectionRulesStr */,
diff --git a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js index 297fc78..74748a3 100644 --- a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js +++ b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
@@ -100,7 +100,8 @@ createPage(true); - page.userInfo_.setUsers('foo@chromium.org', ['foo@chromium.org']); + page.activeUser = 'foo@chromium.org'; + page.users = [page.activeUser]; cr.webUIListenerCallback('use-cloud-print', 'cloudprint url', false); printers.forEach(printer => { cloudPrintInterface.setPrinter(printer.id, printer);
diff --git a/chrome/test/data/webui/print_preview/print_preview_test_utils.js b/chrome/test/data/webui/print_preview/print_preview_test_utils.js index a6a03d53..b5df66c6 100644 --- a/chrome/test/data/webui/print_preview/print_preview_test_utils.js +++ b/chrome/test/data/webui/print_preview/print_preview_test_utils.js
@@ -305,15 +305,11 @@ }); } - /** - * @param {!print_preview.UserInfo} userInfo - * @return {!print_preview.DestinationStore} - */ - function createDestinationStore(userInfo) { + /** @return {!print_preview.DestinationStore} */ + function createDestinationStore() { const testListenerElement = document.createElement('test-listener-element'); document.body.appendChild(testListenerElement); return new print_preview.DestinationStore( - userInfo, testListenerElement.addWebUIListener.bind(testListenerElement)); }
diff --git a/chrome/test/data/webui/settings/all_sites_tests.js b/chrome/test/data/webui/settings/all_sites_tests.js index c9ef41d2..ca79780 100644 --- a/chrome/test/data/webui/settings/all_sites_tests.js +++ b/chrome/test/data/webui/settings/all_sites_tests.js
@@ -214,7 +214,7 @@ testElement.$.listContainer.querySelectorAll('site-entry'); // Add additional origins to SiteGroups with cookies to simulate their // being grouped entries, plus add local storage. - siteEntries[0].siteGroup.origins[0].usage = 1000; + siteEntries[0].siteGroup.origins[0].usage = 900; siteEntries[1].siteGroup.origins.push( test_util.createOriginInfo('http://bar.com')); siteEntries[1].siteGroup.origins[0].usage = 500; @@ -238,11 +238,10 @@ // name. testElement.root.querySelector('select').value = 'data-stored'; testElement.onSortMethodChanged_(); - return localDataBrowserProxy.whenCalled('getNumCookiesList'); - }) - .then(() => { + + Polymer.dom.flush(); - let siteEntries = + siteEntries = testElement.$.listContainer.querySelectorAll('site-entry'); assertEquals( 'bar.com', @@ -301,7 +300,7 @@ const addEtldPlus1 = 'additional-site.net'; const fooOrigin = 'https://login.foo.com'; const addOrigin = 'http://www.additional-site.net'; - const LOCAL_STORAGE_SITE_GROUP_LIST = /** @type {!Array{!SiteGroup}} */ ([ + const STORAGE_SITE_GROUP_LIST = /** @type {!Array{!SiteGroup}}*/ ([ { // Test merging an existing site works, with overlapping origin lists. 'etldPlus1': fooEtldPlus1, @@ -316,7 +315,7 @@ 'origins': [test_util.createOriginInfo(addOrigin)], } ]); - testElement.onLocalStorageListFetched(LOCAL_STORAGE_SITE_GROUP_LIST); + testElement.onStorageListFetched(STORAGE_SITE_GROUP_LIST); Polymer.dom.flush(); siteEntries = testElement.$.listContainer.querySelectorAll('site-entry');
diff --git a/chrome/test/data/webui/settings/site_entry_tests.js b/chrome/test/data/webui/settings/site_entry_tests.js index 0b8e4446..62d9518 100644 --- a/chrome/test/data/webui/settings/site_entry_tests.js +++ b/chrome/test/data/webui/settings/site_entry_tests.js
@@ -196,21 +196,21 @@ assertFalse(testElement.$.collapseChild.opened); }); - test('cookies only show when non-zero for grouped entries', function() { + test('cookies show correctly for grouped entries', function() { localDataBrowserProxy.setCookieDetails(TEST_COOKIE_LIST); testElement.siteGroup = TEST_MULTIPLE_SITE_GROUP; Polymer.dom.flush(); const cookiesLabel = testElement.$.cookies; assertTrue(cookiesLabel.hidden); - // When the number of cookies is more than zero, the label appears. - testElement.onSiteGroupChanged_(TEST_MULTIPLE_SITE_GROUP); - return localDataBrowserProxy.whenCalled('getNumCookiesList') - .then((args) => { - assertEquals(1, args.length); - assertEquals('example.com', args[0]); - return localDataBrowserProxy.whenCalled('getNumCookiesString'); - }) + const testSiteGroup = JSON.parse(JSON.stringify(TEST_MULTIPLE_SITE_GROUP)); + const numCookies = 3; + testSiteGroup.numCookies = numCookies; + + testElement.siteGroup = testSiteGroup; + + Polymer.dom.flush(); + return localDataBrowserProxy.whenCalled('getNumCookiesString') .then((args) => { assertEquals(3, args); assertFalse(cookiesLabel.hidden); @@ -218,19 +218,30 @@ }); }); - test('cookies do not show for ungrouped entries', function() { + test('cookies show for ungrouped entries', function() { testElement.siteGroup = TEST_SINGLE_SITE_GROUP; Polymer.dom.flush(); const cookiesLabel = testElement.$.cookies; assertTrue(cookiesLabel.hidden); - testElement.onSiteGroupChanged_(TEST_SINGLE_SITE_GROUP); - // Make sure there was never any call to the back end to retrieve cookies. - assertEquals(0, localDataBrowserProxy.getCallCount('getNumCookiesList')); - assertTrue(cookiesLabel.hidden); + + const testSiteGroup = JSON.parse(JSON.stringify(TEST_SINGLE_SITE_GROUP)); + const numCookies = 3; + + testSiteGroup.numCookies = numCookies; + + testElement.siteGroup = testSiteGroup; + + Polymer.dom.flush(); + return localDataBrowserProxy.whenCalled('getNumCookiesString') + .then((args) => { + assertEquals(3, args); + assertFalse(cookiesLabel.hidden); + assertEquals('· 3 cookies', cookiesLabel.textContent.trim()); + }); }); - test.only('data usage shown correctly for grouped entries', function() { + test('data usage shown correctly for grouped entries', function() { // Clone this object to avoid propogating changes made in this test. const testSiteGroup = JSON.parse(JSON.stringify(TEST_MULTIPLE_SITE_GROUP)); const numBytes1 = 74622;
diff --git a/chrome/test/data/webui/settings/test_local_data_browser_proxy.js b/chrome/test/data/webui/settings/test_local_data_browser_proxy.js index 52b98c6..293e133 100644 --- a/chrome/test/data/webui/settings/test_local_data_browser_proxy.js +++ b/chrome/test/data/webui/settings/test_local_data_browser_proxy.js
@@ -17,7 +17,6 @@ 'removeShownItems', 'removeItem', 'getCookieDetails', - 'getNumCookiesList', 'getNumCookiesString', 'reloadCookies', 'removeCookie', @@ -84,28 +83,6 @@ } /** @override */ - getNumCookiesList(siteList) { - this.methodCalled('getNumCookiesList', siteList); - const numCookiesMap = new Map(); - if (this.cookieDetails_) { - this.cookieDetails_.children.forEach(cookie => { - let numCookies = numCookiesMap.get(cookie.domain); - numCookies = numCookies == null ? 1 : ++numCookies; - numCookiesMap.set(cookie.domain, numCookies); - }); - } - - const numCookiesList = siteList.map(site => { - const numCookies = numCookiesMap.get(site); - return { - etldPlus1: site, - numCookies: numCookies == null ? 0 : numCookies, - }; - }); - return Promise.resolve(numCookiesList); - } - - /** @override */ getNumCookiesString(numCookies) { this.methodCalled('getNumCookiesString', numCookies); return Promise.resolve(
diff --git a/chrome/test/data/webui/settings/test_util.js b/chrome/test/data/webui/settings/test_util.js index c7dab2ac..91b8723d83 100644 --- a/chrome/test/data/webui/settings/test_util.js +++ b/chrome/test/data/webui/settings/test_util.js
@@ -212,6 +212,7 @@ origin: origin, engagement: 0, usage: 0, + numCookies: 0, }, override); }
diff --git a/chrome/test/perf/OWNERS b/chrome/test/perf/OWNERS index 402f64b..28794d4 100644 --- a/chrome/test/perf/OWNERS +++ b/chrome/test/perf/OWNERS
@@ -1,3 +1,4 @@ cmp@chromium.org +miu@chromium.org nduca@chromium.org zmo@chromium.org
diff --git a/chrome/test/perf/mach_ports_performancetest.cc b/chrome/test/perf/mach_ports_performancetest.cc deleted file mode 100644 index 27794ccf..0000000 --- a/chrome/test/perf/mach_ports_performancetest.cc +++ /dev/null
@@ -1,110 +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. - -#include <mach/mach.h> - -#include <vector> - -#include "base/memory/ref_counted.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_window.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "net/test/embedded_test_server/embedded_test_server.h" -#include "testing/perf/perf_test.h" - -namespace { - -// This test spawns a new browser and counts the number of open Mach ports in -// the browser process. It navigates tabs and closes them, repeatedly measuring -// the number of open ports. This is used to protect against leaking Mach ports, -// which was the source of <http://crbug.com/105513>. -class MachPortsTest : public InProcessBrowserTest { - public: - MachPortsTest() { - embedded_test_server()->ServeFilesFromSourceDirectory( - "data/mach_ports/moz"); - } - - void SetUpOnMainThread() override { - ASSERT_TRUE(embedded_test_server()->Start()); - } - - void TearDown() override { - std::string ports; - for (int port : port_counts_) - base::StringAppendF(&ports, "%d,", port); - perf_test::PrintResultList("mach_ports", "", "", ports, "ports", true); - - InProcessBrowserTest::TearDown(); - } - - // Gets the browser's current number of Mach ports and records it. - void RecordPortCount() { - mach_port_name_array_t names; - mach_msg_type_number_t names_count = 0; - mach_port_type_array_t types; - mach_msg_type_number_t types_count = 0; - - mach_port_t self = mach_task_self(); - - // A friendlier interface would allow NULL buffers to only get the counts. - kern_return_t kr = mach_port_names(self, - &names, &names_count, - &types, &types_count); - ASSERT_EQ(KERN_SUCCESS, kr) << "Failed to get mach_port_names(): " - << mach_error_string(kr); - ASSERT_EQ(names_count, types_count); // Documented kernel invariant. - - port_counts_.push_back(names_count); - - vm_deallocate(self, reinterpret_cast<vm_address_t>(names), - names_count * sizeof(mach_port_name_array_t)); - vm_deallocate(self, reinterpret_cast<vm_address_t>(types), - types_count * sizeof(mach_port_type_array_t)); - } - - // Adds a tab from the page cycler data at the specified domain. - void AddTab(const std::string& domain) { - AddTabAtIndex( - 0, embedded_test_server()->GetURL("/" + domain + "/").Resolve("?skip"), - ui::PAGE_TRANSITION_TYPED); - } - - private: - std::vector<int> port_counts_; -}; - -IN_PROC_BROWSER_TEST_F(MachPortsTest, GetCounts) { - browser()->window()->Show(); - - // Record startup number. - RecordPortCount(); - - // Create a browser and open a few tabs. - AddTab("www.google.com"); - RecordPortCount(); - - AddTab("www.cnn.com"); - RecordPortCount(); - - AddTab("www.nytimes.com"); - RecordPortCount(); - - TabStripModel* tab_strip_model = browser()->tab_strip_model(); - int tab_count = tab_strip_model->count(); - ASSERT_EQ(4, tab_count); // Also count about:blank. - - // Close each tab, recording the number of ports after each. Do not close the - // last tab, which is about:blank. - for (int i = 0; i < tab_count - 1; ++i) { - EXPECT_TRUE( - tab_strip_model->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE)); - RecordPortCount(); - } -} - -} // namespace
diff --git a/chromecast/device/bluetooth/le/gatt_client_manager.h b/chromecast/device/bluetooth/le/gatt_client_manager.h index e6a2ff9a..49f9b01 100644 --- a/chromecast/device/bluetooth/le/gatt_client_manager.h +++ b/chromecast/device/bluetooth/le/gatt_client_manager.h
@@ -30,6 +30,10 @@ virtual void OnConnectChanged(scoped_refptr<RemoteDevice> device, bool connected) {} + // Called when the bond state changes for |device|. + virtual void OnBondChanged(scoped_refptr<RemoteDevice> device, + bool bonded) {} + // Called when the connection MTU changes for |device|. virtual void OnMtuChanged(scoped_refptr<RemoteDevice> device, int mtu) {} @@ -76,8 +80,13 @@ // TODO(bcf): Deprecated in favor of |GetConnectedDevices|. virtual void GetNumConnected(base::OnceCallback<void(size_t)> cb) const = 0; + // Called when we initiate connection to a remote device. virtual void NotifyConnect(const bluetooth_v2_shlib::Addr& addr) = 0; + // Used to notify |this| of currently bonded devices on initialization. + // Note that these devices might not be connected. + virtual void NotifyBonded(const bluetooth_v2_shlib::Addr& addr) = 0; + // TODO(bcf): Deprecated. Should be removed now that this class may be used // from any thread. virtual scoped_refptr<base::SingleThreadTaskRunner> task_runner() = 0;
diff --git a/chromecast/device/bluetooth/le/gatt_client_manager_impl.cc b/chromecast/device/bluetooth/le/gatt_client_manager_impl.cc index 356ecbb..f7965a4d 100644 --- a/chromecast/device/bluetooth/le/gatt_client_manager_impl.cc +++ b/chromecast/device/bluetooth/le/gatt_client_manager_impl.cc
@@ -127,6 +127,13 @@ observers_->Notify(FROM_HERE, &Observer::OnConnectInitated, addr); } +void GattClientManagerImpl::NotifyBonded(const bluetooth_v2_shlib::Addr& addr) { + MAKE_SURE_IO_THREAD(NotifyBonded, addr); + auto device = GetDeviceSync(addr); + static_cast<RemoteDeviceImpl*>(device.get())->SetBonded(true); + observers_->Notify(FROM_HERE, &Observer::OnBondChanged, device, true); +} + void GattClientManagerImpl::EnqueueConnectRequest( const bluetooth_v2_shlib::Addr& addr) { DCHECK(io_task_runner_->BelongsToCurrentThread()); @@ -198,6 +205,22 @@ } } +void GattClientManagerImpl::OnBondChanged(const bluetooth_v2_shlib::Addr& addr, + bool status, + bool bonded) { + MAKE_SURE_IO_THREAD(OnBondChanged, addr, status, bonded); + auto it = addr_to_device_.find(addr); + + // Silently ignore devices we aren't keeping track of. + if (it == addr_to_device_.end()) { + return; + } + + it->second->SetBonded(bonded); + + observers_->Notify(FROM_HERE, &Observer::OnBondChanged, it->second, bonded); +} + void GattClientManagerImpl::OnNotification(const bluetooth_v2_shlib::Addr& addr, uint16_t handle, const std::vector<uint8_t>& value) {
diff --git a/chromecast/device/bluetooth/le/gatt_client_manager_impl.h b/chromecast/device/bluetooth/le/gatt_client_manager_impl.h index ecece829..a45553aa 100644 --- a/chromecast/device/bluetooth/le/gatt_client_manager_impl.h +++ b/chromecast/device/bluetooth/le/gatt_client_manager_impl.h
@@ -52,6 +52,7 @@ void GetConnectedDevices(GetConnectDevicesCallback cb) override; void GetNumConnected(base::OnceCallback<void(size_t)> cb) const override; void NotifyConnect(const bluetooth_v2_shlib::Addr& addr) override; + void NotifyBonded(const bluetooth_v2_shlib::Addr& addr) override; scoped_refptr<base::SingleThreadTaskRunner> task_runner() override; // Add a Connect request to the queue. They can only be executed serially. @@ -70,6 +71,9 @@ void OnConnectChanged(const bluetooth_v2_shlib::Addr& addr, bool status, bool connected) override; + void OnBondChanged(const bluetooth_v2_shlib::Addr& addr, + bool status, + bool bonded) override; void OnNotification(const bluetooth_v2_shlib::Addr& addr, uint16_t handle, const std::vector<uint8_t>& value) override;
diff --git a/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc b/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc index 38facdd..18e0ac9 100644 --- a/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc +++ b/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc
@@ -203,6 +203,9 @@ EXPECT_CALL(cb_, Run(false)); device->Disconnect(cb_.Get()); + EXPECT_CALL(cb_, Run(false)); + device->CreateBond(cb_.Get()); + base::MockCallback<RemoteDevice::RssiCallback> rssi_cb; EXPECT_CALL(rssi_cb, Run(false, _)); device->ReadRemoteRssi(rssi_cb.Get()); @@ -245,6 +248,94 @@ fake_task_runner_->RunUntilIdle(); } +TEST_F(GattClientManagerTest, RemoteDeviceBond) { + bluetooth_v2_shlib::Gatt::Client::Delegate* delegate = + gatt_client_->delegate(); + + scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1); + Connect(kTestAddr1); + EXPECT_FALSE(device->IsBonded()); + + // CreateBond fails in the initial request. + EXPECT_CALL(*gatt_client_, CreateBond(kTestAddr1)).WillOnce(Return(false)); + EXPECT_CALL(cb_, Run(false)); + device->CreateBond(cb_.Get()); + EXPECT_FALSE(device->IsBonded()); + + // CreateBond fails in the callback. + EXPECT_CALL(*gatt_client_, CreateBond(kTestAddr1)).WillOnce(Return(true)); + EXPECT_CALL(cb_, Run(false)); + device->CreateBond(cb_.Get()); + delegate->OnBondChanged(kTestAddr1, false /* status */, false /* bonded */); + EXPECT_FALSE(device->IsBonded()); + + // CreateBond succeeds. + EXPECT_CALL(*gatt_client_, CreateBond(kTestAddr1)).WillOnce(Return(true)); + device->CreateBond(cb_.Get()); + EXPECT_CALL(cb_, Run(true)); + delegate->OnBondChanged(kTestAddr1, true /* status */, true /* bonded */); + EXPECT_TRUE(device->IsBonded()); + + // Bond with an already bonded device should fail. + EXPECT_CALL(cb_, Run(false)); + device->CreateBond(cb_.Get()); + EXPECT_TRUE(device->IsBonded()); + + // RemoveBond succeeds. + EXPECT_CALL(*gatt_client_, RemoveBond(kTestAddr1)).WillOnce(Return(true)); + device->RemoveBond(cb_.Get()); + EXPECT_CALL(cb_, Run(true)); + delegate->OnBondChanged(kTestAddr1, false /* status */, false /* bonded */); + EXPECT_FALSE(device->IsBonded()); + + // RemoveBond from an unbonded device succeeds. + EXPECT_CALL(*gatt_client_, RemoveBond(kTestAddr1)).WillOnce(Return(true)); + device->RemoveBond(cb_.Get()); + EXPECT_CALL(cb_, Run(true)); + delegate->OnBondChanged(kTestAddr1, false /* status */, false /* bonded */); + EXPECT_FALSE(device->IsBonded()); + + // CreateBond again succeeds. + EXPECT_CALL(*gatt_client_, CreateBond(kTestAddr1)).WillOnce(Return(true)); + device->CreateBond(cb_.Get()); + EXPECT_CALL(cb_, Run(true)); + delegate->OnBondChanged(kTestAddr1, true /* status */, true /* bonded */); + EXPECT_TRUE(device->IsBonded()); + + // Device should remain bonded when it disconnects. + EXPECT_CALL(*gatt_client_, Disconnect(kTestAddr1)).WillOnce(Return(true)); + device->Disconnect({}); + delegate->OnConnectChanged(kTestAddr1, true /* status */, + false /* connected */); + EXPECT_FALSE(device->IsConnected()); + EXPECT_TRUE(device->IsBonded()); + + // RemoveBond from a disconnected but bonded device. + EXPECT_CALL(*gatt_client_, RemoveBond(kTestAddr1)).WillOnce(Return(true)); + device->RemoveBond(cb_.Get()); + EXPECT_CALL(cb_, Run(true)); + delegate->OnBondChanged(kTestAddr1, false /* status */, false /* bonded */); + EXPECT_FALSE(device->IsBonded()); + + fake_task_runner_->RunUntilIdle(); +} + +TEST_F(GattClientManagerTest, RemoteDeviceBondedOnInitialization) { + // NotifyBonded at initialization. + gatt_client_manager_->NotifyBonded(kTestAddr1); + + // Device should have updated the bonding state. + scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1); + EXPECT_FALSE(device->IsConnected()); + EXPECT_TRUE(device->IsBonded()); + + Connect(kTestAddr1); + EXPECT_TRUE(device->IsConnected()); + EXPECT_TRUE(device->IsBonded()); + + fake_task_runner_->RunUntilIdle(); +} + TEST_F(GattClientManagerTest, RemoteDeviceConnectConcurrent) { bluetooth_v2_shlib::Gatt::Client::Delegate* delegate = gatt_client_->delegate();
diff --git a/chromecast/device/bluetooth/le/mock_gatt_client_manager.h b/chromecast/device/bluetooth/le/mock_gatt_client_manager.h index 67b568d..a67b769 100644 --- a/chromecast/device/bluetooth/le/mock_gatt_client_manager.h +++ b/chromecast/device/bluetooth/le/mock_gatt_client_manager.h
@@ -44,6 +44,7 @@ MOCK_CONST_METHOD1(GetNumConnected, void(base::OnceCallback<void(size_t)> cb)); MOCK_METHOD1(NotifyConnect, void(const bluetooth_v2_shlib::Addr& addr)); + MOCK_METHOD1(NotifyBonded, void(const bluetooth_v2_shlib::Addr& addr)); MOCK_METHOD0(task_runner, scoped_refptr<base::SingleThreadTaskRunner>()); base::ObserverList<Observer>::Unchecked observers_;
diff --git a/chromecast/device/bluetooth/le/mock_remote_device.h b/chromecast/device/bluetooth/le/mock_remote_device.h index f873f6e..ca11052 100644 --- a/chromecast/device/bluetooth/le/mock_remote_device.h +++ b/chromecast/device/bluetooth/le/mock_remote_device.h
@@ -30,6 +30,16 @@ MOCK_METHOD0(DisconnectSync, bool()); + MOCK_METHOD0(CreateBond, bool()); + void CreateBond(StatusCallback cb) override { + std::move(cb).Run(CreateBond()); + } + + MOCK_METHOD0(RemoveBond, bool()); + void RemoveBond(StatusCallback cb) override { + std::move(cb).Run(RemoveBond()); + } + MOCK_METHOD1(ReadRemoteRssi, void(RssiCallback cb)); MOCK_METHOD1(RequestMtu, bool(int mtu)); @@ -51,6 +61,8 @@ MOCK_METHOD0(IsConnected, bool()); + MOCK_METHOD0(IsBonded, bool()); + MOCK_METHOD0(GetMtu, int()); MOCK_METHOD0(GetServices, std::vector<scoped_refptr<RemoteService>>());
diff --git a/chromecast/device/bluetooth/le/remote_device.h b/chromecast/device/bluetooth/le/remote_device.h index 48be79a..7da66a4 100644 --- a/chromecast/device/bluetooth/le/remote_device.h +++ b/chromecast/device/bluetooth/le/remote_device.h
@@ -41,6 +41,14 @@ // TODO(bcf): Deprecated. Replace usage with async version. virtual bool DisconnectSync() = 0; + // Create bond to this device. Callback will return |true| if + // bonded successfully, otherwise false. Device must be connected. + virtual void CreateBond(StatusCallback cb) = 0; + + // Remove bond to this device. Callback will return |true| if + // bond is removed, otherwise false. + virtual void RemoveBond(StatusCallback cb) = 0; + // Read this device's RSSI. The result will be sent in |callback|. Only one // pending call is allowed at a time. using RssiCallback = base::OnceCallback<void(bool success, int rssi)>; @@ -61,6 +69,9 @@ // Returns true if this device is connected. virtual bool IsConnected() = 0; + // Returns true if this device is bonded. + virtual bool IsBonded() = 0; + // Returns the current MTU of the connection with this device. virtual int GetMtu() = 0;
diff --git a/chromecast/device/bluetooth/le/remote_device_impl.cc b/chromecast/device/bluetooth/le/remote_device_impl.cc index 1bb16c5e..1b1ab78 100644 --- a/chromecast/device/bluetooth/le/remote_device_impl.cc +++ b/chromecast/device/bluetooth/le/remote_device_impl.cc
@@ -130,6 +130,66 @@ return true; } +void RemoteDeviceImpl::CreateBond(StatusCallback cb) { + MAKE_SURE_IO_THREAD(CreateBond, BindToCurrentSequence(std::move(cb))); + LOG(INFO) << "CreateBond(" << util::AddrLastByteString(addr_) << ")"; + if (!gatt_client_manager_) { + LOG(ERROR) << __func__ << " failed: Destroyed"; + EXEC_CB_AND_RET(cb, false); + } + + if (!connected_) { + LOG(ERROR) << "Not connected"; + EXEC_CB_AND_RET(cb, false); + } + + if (create_bond_pending_ || remove_bond_pending_) { + // TODO(tiansong): b/120489954 Implement queuing and timeout logic. + LOG(ERROR) << __func__ << " failed: waiting for pending bond command"; + EXEC_CB_AND_RET(cb, false); + } + + if (bonded_) { + LOG(ERROR) << "Already bonded"; + EXEC_CB_AND_RET(cb, false); + } + + if (!gatt_client_manager_->gatt_client()->CreateBond(addr_)) { + LOG(ERROR) << __func__ << " failed"; + EXEC_CB_AND_RET(cb, false); + } + + create_bond_pending_ = true; + create_bond_cb_ = std::move(cb); +} + +void RemoteDeviceImpl::RemoveBond(StatusCallback cb) { + MAKE_SURE_IO_THREAD(RemoveBond, BindToCurrentSequence(std::move(cb))); + LOG(INFO) << "RemoveBond(" << util::AddrLastByteString(addr_) << ")"; + if (!gatt_client_manager_) { + LOG(ERROR) << __func__ << " failed: Destroyed"; + EXEC_CB_AND_RET(cb, false); + } + + if (create_bond_pending_ || remove_bond_pending_) { + // TODO(tiansong): b/120489954 Implement queuing and timeout logic. + LOG(ERROR) << __func__ << " failed: waiting for pending bond command"; + EXEC_CB_AND_RET(cb, false); + } + + if (!bonded_) { + LOG(WARNING) << "Not bonded"; + } + + if (!gatt_client_manager_->gatt_client()->RemoveBond(addr_)) { + LOG(ERROR) << __func__ << " failed"; + EXEC_CB_AND_RET(cb, false); + } + + remove_bond_pending_ = true; + remove_bond_cb_ = std::move(cb); +} + void RemoteDeviceImpl::ReadRemoteRssi(RssiCallback cb) { MAKE_SURE_IO_THREAD(ReadRemoteRssi, BindToCurrentSequence(std::move(cb))); if (!gatt_client_manager_) { @@ -186,6 +246,10 @@ return connected_; } +bool RemoteDeviceImpl::IsBonded() { + return bonded_; +} + int RemoteDeviceImpl::GetMtu() { return mtu_; } @@ -325,6 +389,26 @@ } } +void RemoteDeviceImpl::SetBonded(bool bonded) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + + bonded_ = bonded; + + if (create_bond_pending_) { + create_bond_pending_ = false; + if (create_bond_cb_) { + std::move(create_bond_cb_).Run(bonded); + } + } + + if (remove_bond_pending_) { + remove_bond_pending_ = false; + if (remove_bond_cb_) { + std::move(remove_bond_cb_).Run(!bonded); + } + } +} + void RemoteDeviceImpl::SetServicesDiscovered(bool discovered) { DCHECK(io_task_runner_->BelongsToCurrentThread()); services_discovered_ = discovered;
diff --git a/chromecast/device/bluetooth/le/remote_device_impl.h b/chromecast/device/bluetooth/le/remote_device_impl.h index 415fd179..1674dd5 100644 --- a/chromecast/device/bluetooth/le/remote_device_impl.h +++ b/chromecast/device/bluetooth/le/remote_device_impl.h
@@ -40,6 +40,8 @@ bool ConnectSync() override; void Disconnect(StatusCallback cb) override; bool DisconnectSync() override; + void CreateBond(StatusCallback cb) override; + void RemoveBond(StatusCallback cb) override; void ReadRemoteRssi(RssiCallback cb) override; void RequestMtu(int mtu, StatusCallback cb) override; void ConnectionParameterUpdate(int min_interval, @@ -48,6 +50,7 @@ int timeout, StatusCallback cb) override; bool IsConnected() override; + bool IsBonded() override; int GetMtu() override; void GetServices( base::OnceCallback<void(std::vector<scoped_refptr<RemoteService>>)> cb) @@ -88,6 +91,7 @@ // Friend methods for GattClientManagerImpl void SetConnected(bool connected); + void SetBonded(bool bonded); void SetServicesDiscovered(bool discovered); bool GetServicesDiscovered(); void SetMtu(int mtu); @@ -155,10 +159,17 @@ bool disconnect_pending_ = false; StatusCallback disconnect_cb_; + bool create_bond_pending_ = false; + StatusCallback create_bond_cb_; + + bool remove_bond_pending_ = false; + StatusCallback remove_bond_cb_; + bool rssi_pending_ = false; RssiCallback rssi_cb_; std::atomic<bool> connected_{false}; + std::atomic<bool> bonded_{false}; std::atomic<int> mtu_{kDefaultMtu}; std::map<bluetooth_v2_shlib::Uuid, scoped_refptr<RemoteService>> uuid_to_service_;
diff --git a/chromecast/device/bluetooth/shlib/gatt_client.h b/chromecast/device/bluetooth/shlib/gatt_client.h index c5c1ff0d..270a99e8 100644 --- a/chromecast/device/bluetooth/shlib/gatt_client.h +++ b/chromecast/device/bluetooth/shlib/gatt_client.h
@@ -19,6 +19,8 @@ virtual void SetDelegate(Gatt::Client::Delegate* delegate) = 0; virtual bool Connect(const Addr& addr) = 0; virtual bool Disconnect(const Addr& addr) = 0; + virtual bool CreateBond(const Addr& addr) = 0; + virtual bool RemoveBond(const Addr& addr) = 0; virtual bool ReadCharacteristic(const Addr& addr, const Gatt::Characteristic& characteristic, Gatt::Client::AuthReq auth_req) = 0;
diff --git a/chromecast/device/bluetooth/shlib/mock_gatt_client.h b/chromecast/device/bluetooth/shlib/mock_gatt_client.h index ebe9693..218c3df 100644 --- a/chromecast/device/bluetooth/shlib/mock_gatt_client.h +++ b/chromecast/device/bluetooth/shlib/mock_gatt_client.h
@@ -25,6 +25,8 @@ MOCK_METHOD0(Enable, bool()); MOCK_METHOD0(Disable, bool()); MOCK_METHOD1(Disconnect, bool(const Addr&)); + MOCK_METHOD1(CreateBond, bool(const Addr&)); + MOCK_METHOD1(RemoveBond, bool(const Addr&)); MOCK_METHOD3(ReadCharacteristic, bool(const Addr&, const Gatt::Characteristic&,
diff --git a/chromecast/public/bluetooth/gatt.h b/chromecast/public/bluetooth/gatt.h index 857b5ff..b4eb7c3 100644 --- a/chromecast/public/bluetooth/gatt.h +++ b/chromecast/public/bluetooth/gatt.h
@@ -131,6 +131,11 @@ bool status, bool connected) = 0; + // Called when the bonding state changes. + virtual void OnBondChanged(const Addr& addr, + bool status, + bool bonded) = 0; + // Called on a Characteristic value notification. virtual void OnNotification(const Addr& addr, uint16_t handle, @@ -194,6 +199,12 @@ // Remove connection to remote device |addr|. static bool Disconnect(const Addr& addr); + // Create bond to remote device |addr|. + static bool CreateBond(const Addr& addr); + + // Remove bond to remote device |addr|. + static bool RemoveBond(const Addr& addr); + // Read |characteristic| from remote device |addr|. If |auth_req| is // AUTH_REQ_INVALID, this function will automatically retry stronger // authentications on failure.
diff --git a/chromeos/components/proximity_auth/proximity_monitor_impl.cc b/chromeos/components/proximity_auth/proximity_monitor_impl.cc index fbaf346b8..11e1b44 100644 --- a/chromeos/components/proximity_auth/proximity_monitor_impl.cc +++ b/chromeos/components/proximity_auth/proximity_monitor_impl.cc
@@ -42,8 +42,8 @@ weak_ptr_factory_(this) { if (device::BluetoothAdapterFactory::IsBluetoothSupported()) { device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&ProximityMonitorImpl::OnAdapterInitialized, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce(&ProximityMonitorImpl::OnAdapterInitialized, + weak_ptr_factory_.GetWeakPtr())); } else { PA_LOG(ERROR) << "[Proximity] Proximity monitoring unavailable: " << "Bluetooth is unsupported on this platform.";
diff --git a/chromeos/components/proximity_auth/unlock_manager_impl.cc b/chromeos/components/proximity_auth/unlock_manager_impl.cc index 1892702..374f6e74 100644 --- a/chromeos/components/proximity_auth/unlock_manager_impl.cc +++ b/chromeos/components/proximity_auth/unlock_manager_impl.cc
@@ -126,8 +126,8 @@ if (device::BluetoothAdapterFactory::IsBluetoothSupported()) { device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&UnlockManagerImpl::OnBluetoothAdapterInitialized, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce(&UnlockManagerImpl::OnBluetoothAdapterInitialized, + weak_ptr_factory_.GetWeakPtr())); } }
diff --git a/chromeos/components/tether/tether_host_fetcher_impl.cc b/chromeos/components/tether/tether_host_fetcher_impl.cc index 85eabc8..b36061d 100644 --- a/chromeos/components/tether/tether_host_fetcher_impl.cc +++ b/chromeos/components/tether/tether_host_fetcher_impl.cc
@@ -110,72 +110,15 @@ TetherHostFetcherImpl::GenerateHostDeviceList() { multidevice::RemoteDeviceRefList host_list; - TetherHostSource tether_host_source = - IsInLegacyHostMode() ? TetherHostSource::DEVICE_SYNC_CLIENT - : TetherHostSource::MULTIDEVICE_SETUP_CLIENT; - - if (tether_host_source == TetherHostSource::MULTIDEVICE_SETUP_CLIENT) { - multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice - host_status_with_device = multidevice_setup_client_->GetHostStatus(); - if (host_status_with_device.first == - chromeos::multidevice_setup::mojom::HostStatus::kHostVerified) { - host_list.push_back(*host_status_with_device.second); - } - return host_list; + multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice + host_status_with_device = multidevice_setup_client_->GetHostStatus(); + if (host_status_with_device.first == + chromeos::multidevice_setup::mojom::HostStatus::kHostVerified) { + host_list.push_back(*host_status_with_device.second); } - - if (tether_host_source == TetherHostSource::DEVICE_SYNC_CLIENT) { - for (const multidevice::RemoteDeviceRef& remote_device_ref : - device_sync_client_->GetSyncedDevices()) { - multidevice::SoftwareFeatureState magic_tether_host_state = - remote_device_ref.GetSoftwareFeatureState( - chromeos::multidevice::SoftwareFeature::kInstantTetheringHost); - if (magic_tether_host_state == - multidevice::SoftwareFeatureState::kSupported || - magic_tether_host_state == - multidevice::SoftwareFeatureState::kEnabled) { - host_list.push_back(remote_device_ref); - } - } - return host_list; - } - - NOTREACHED(); return host_list; } -bool TetherHostFetcherImpl::IsInLegacyHostMode() { - if (!device_sync_client_->is_ready()) - return false; - - bool has_supported_tether_host = false; - for (const multidevice::RemoteDeviceRef& remote_device_ref : - device_sync_client_->GetSyncedDevices()) { - multidevice::SoftwareFeatureState better_together_host_state = - remote_device_ref.GetSoftwareFeatureState( - chromeos::multidevice::SoftwareFeature::kBetterTogetherHost); - // If there's any valid Better Together host, don't support legacy mode. - if (better_together_host_state == - multidevice::SoftwareFeatureState::kSupported || - better_together_host_state == - multidevice::SoftwareFeatureState::kEnabled) { - return false; - } - - multidevice::SoftwareFeatureState magic_tether_host_state = - remote_device_ref.GetSoftwareFeatureState( - chromeos::multidevice::SoftwareFeature::kInstantTetheringHost); - if (magic_tether_host_state == - multidevice::SoftwareFeatureState::kSupported || - magic_tether_host_state == - multidevice::SoftwareFeatureState::kEnabled) { - has_supported_tether_host = true; - } - } - - return has_supported_tether_host; -} - } // namespace tether } // namespace chromeos
diff --git a/chromeos/components/tether/tether_host_fetcher_impl.h b/chromeos/components/tether/tether_host_fetcher_impl.h index 904254fe..f35c80e 100644 --- a/chromeos/components/tether/tether_host_fetcher_impl.h +++ b/chromeos/components/tether/tether_host_fetcher_impl.h
@@ -77,23 +77,9 @@ multidevice_setup_client_); private: - enum class TetherHostSource { - UNKNOWN, - MULTIDEVICE_SETUP_CLIENT, - DEVICE_SYNC_CLIENT, - REMOTE_DEVICE_PROVIDER - }; - void CacheCurrentTetherHosts(); multidevice::RemoteDeviceRefList GenerateHostDeviceList(); - // This returns true if there is no BETTER_TOGETHER_HOST supported or enabled, - // but there *are* MAGIC_TETHER_HOSTs supported or enabled. This can only - // happen if the user's phone has not yet fully updated to the new multidevice - // world. - // TODO(crbug.com/894585): Remove this legacy special case after M71. - bool IsInLegacyHostMode(); - device_sync::DeviceSyncClient* device_sync_client_; chromeos::multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
diff --git a/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc b/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc index 3f24266..33cdc08b 100644 --- a/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc +++ b/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc
@@ -196,81 +196,6 @@ EXPECT_EQ(2u, test_observer_->num_updates()); } - void TestSingleTetherHost(bool use_legacy_mode = false) { - InitializeTest(); - if (use_legacy_mode) { - test_remote_device_list_[0].software_features - [chromeos::multidevice::SoftwareFeature::kBetterTogetherHost] = - multidevice::SoftwareFeatureState::kNotSupported; - test_remote_device_ref_list_ = - CreateTestRemoteDeviceRefList(test_remote_device_list_); - SetSyncedDevices(test_remote_device_list_); - NotifyNewDevicesSynced(); - } - - VerifySingleTetherHost(test_remote_device_ref_list_[0].GetDeviceId(), - test_remote_device_ref_list_[0]); - - // Now, set device 0 as the only device. It should still be returned when - // requested. - SetSyncedDevices( - multidevice::RemoteDeviceList{test_remote_device_list_[0]}); - NotifyNewDevicesSynced(); - VerifySingleTetherHost(test_remote_device_ref_list_[0].GetDeviceId(), - test_remote_device_ref_list_[0]); - - // Now, set another device as the only device, but remove its mobile data - // support. It should not be returned. - multidevice::RemoteDevice remote_device = multidevice::RemoteDevice(); - remote_device.software_features - [chromeos::multidevice::SoftwareFeature::kInstantTetheringHost] = - multidevice::SoftwareFeatureState::kNotSupported; - - SetSyncedDevices(multidevice::RemoteDeviceList{remote_device}); - NotifyNewDevicesSynced(); - VerifySingleTetherHost(test_remote_device_ref_list_[0].GetDeviceId(), - base::nullopt); - - // Update the list; now, there are no more devices. - SetSyncedDevices(multidevice::RemoteDeviceList()); - NotifyNewDevicesSynced(); - VerifySingleTetherHost(test_remote_device_ref_list_[0].GetDeviceId(), - base::nullopt); - } - - void TestFetchAllTetherHosts(bool use_legacy_mode = false) { - InitializeTest(); - - // Create a list of test devices, only some of which are valid tether hosts. - // Ensure that only that subset is fetched. - test_remote_device_list_[3].software_features - [chromeos::multidevice::SoftwareFeature::kInstantTetheringHost] = - multidevice::SoftwareFeatureState::kNotSupported; - test_remote_device_list_[4].software_features - [chromeos::multidevice::SoftwareFeature::kInstantTetheringHost] = - multidevice::SoftwareFeatureState::kNotSupported; - if (use_legacy_mode) { - test_remote_device_list_[0].software_features - [chromeos::multidevice::SoftwareFeature::kBetterTogetherHost] = - multidevice::SoftwareFeatureState::kNotSupported; - } - - SetSyncedDevices(test_remote_device_list_); - NotifyNewDevicesSynced(); - - multidevice::RemoteDeviceRefList expected_host_device_list; - if (!use_legacy_mode) { - expected_host_device_list = - CreateTestRemoteDeviceRefList({test_remote_device_list_[0]}); - } else { - expected_host_device_list = CreateTestRemoteDeviceRefList( - {test_remote_device_list_[0], test_remote_device_list_[1], - test_remote_device_list_[2]}); - } - - VerifyAllTetherHosts(expected_host_device_list); - } - multidevice::RemoteDeviceList test_remote_device_list_; multidevice::RemoteDeviceRefList test_remote_device_ref_list_; @@ -320,17 +245,58 @@ } TEST_F(TetherHostFetcherImplTest, TestFetchAllTetherHosts) { - TestFetchAllTetherHosts(); -} -TEST_F(TetherHostFetcherImplTest, TestFetchAllTetherHostsInLegacyMode) { - TestFetchAllTetherHosts(true /* use_legacy_mode */); + InitializeTest(); + + // Create a list of test devices, only some of which are valid tether hosts. + // Ensure that only that subset is fetched. + test_remote_device_list_[3].software_features + [chromeos::multidevice::SoftwareFeature::kInstantTetheringHost] = + multidevice::SoftwareFeatureState::kNotSupported; + test_remote_device_list_[4].software_features + [chromeos::multidevice::SoftwareFeature::kInstantTetheringHost] = + multidevice::SoftwareFeatureState::kNotSupported; + + SetSyncedDevices(test_remote_device_list_); + NotifyNewDevicesSynced(); + + multidevice::RemoteDeviceRefList expected_host_device_list; + + expected_host_device_list = + CreateTestRemoteDeviceRefList({test_remote_device_list_[0]}); + + VerifyAllTetherHosts(expected_host_device_list); } TEST_F(TetherHostFetcherImplTest, TestSingleTetherHost) { - TestSingleTetherHost(); -} -TEST_F(TetherHostFetcherImplTest, TestSingleTetherHostInLegacyMode) { - TestSingleTetherHost(true /* use_legacy_mode */); + InitializeTest(); + + VerifySingleTetherHost(test_remote_device_ref_list_[0].GetDeviceId(), + test_remote_device_ref_list_[0]); + + // Now, set device 0 as the only device. It should still be returned when + // requested. + SetSyncedDevices(multidevice::RemoteDeviceList{test_remote_device_list_[0]}); + NotifyNewDevicesSynced(); + VerifySingleTetherHost(test_remote_device_ref_list_[0].GetDeviceId(), + test_remote_device_ref_list_[0]); + + // Now, set another device as the only device, but remove its mobile data + // support. It should not be returned. + multidevice::RemoteDevice remote_device = multidevice::RemoteDevice(); + remote_device.software_features + [chromeos::multidevice::SoftwareFeature::kInstantTetheringHost] = + multidevice::SoftwareFeatureState::kNotSupported; + + SetSyncedDevices(multidevice::RemoteDeviceList{remote_device}); + NotifyNewDevicesSynced(); + VerifySingleTetherHost(test_remote_device_ref_list_[0].GetDeviceId(), + base::nullopt); + + // Update the list; now, there are no more devices. + SetSyncedDevices(multidevice::RemoteDeviceList()); + NotifyNewDevicesSynced(); + VerifySingleTetherHost(test_remote_device_ref_list_[0].GetDeviceId(), + base::nullopt); } TEST_F(TetherHostFetcherImplTest,
diff --git a/chromeos/geolocation/simple_geolocation_provider.cc b/chromeos/geolocation/simple_geolocation_provider.cc index 14fd85fb..63f6b01 100644 --- a/chromeos/geolocation/simple_geolocation_provider.cc +++ b/chromeos/geolocation/simple_geolocation_provider.cc
@@ -46,8 +46,11 @@ // Mostly necessary for testing and rare cases where NetworkHandler is not // initialized: in that case, calls to Get() will fail. if (send_wifi_access_points || send_cell_towers) { - NetworkHandler::Get()->geolocation_handler()->GetNetworkInformation( - wifi_vector.get(), cell_vector.get()); + GeolocationHandler* geolocation_handler = geolocation_handler_; + if (!geolocation_handler) + geolocation_handler = NetworkHandler::Get()->geolocation_handler(); + geolocation_handler->GetNetworkInformation(wifi_vector.get(), + cell_vector.get()); } if (!send_wifi_access_points || (wifi_vector->size() == 0))
diff --git a/chromeos/geolocation/simple_geolocation_provider.h b/chromeos/geolocation/simple_geolocation_provider.h index 76788f77..29a1267 100644 --- a/chromeos/geolocation/simple_geolocation_provider.h +++ b/chromeos/geolocation/simple_geolocation_provider.h
@@ -8,6 +8,7 @@ #include <memory> #include <vector> +#include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/threading/thread_checker.h" @@ -21,6 +22,7 @@ } namespace chromeos { +class GeolocationHandler; // This class implements Google Maps Geolocation API. // @@ -50,6 +52,8 @@ private: friend class TestGeolocationAPILoaderFactory; + FRIEND_TEST_ALL_PREFIXES(SimpleGeolocationWirelessTest, CellularExists); + FRIEND_TEST_ALL_PREFIXES(SimpleGeolocationWirelessTest, WiFiExists); // Geolocation response callback. Deletes request from requests_. void OnGeolocationResponse( @@ -59,6 +63,10 @@ bool server_error, const base::TimeDelta elapsed); + void set_geolocation_handler(GeolocationHandler* geolocation_handler) { + geolocation_handler_ = geolocation_handler; + } + scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_; // URL of the Google Maps Geolocation API. @@ -69,6 +77,8 @@ // destroy. std::vector<std::unique_ptr<SimpleGeolocationRequest>> requests_; + GeolocationHandler* geolocation_handler_ = nullptr; + // Creation and destruction should happen on the same thread. base::ThreadChecker thread_checker_;
diff --git a/chromeos/geolocation/simple_geolocation_unittest.cc b/chromeos/geolocation/simple_geolocation_unittest.cc index ab0fb4a..5230e8a6 100644 --- a/chromeos/geolocation/simple_geolocation_unittest.cc +++ b/chromeos/geolocation/simple_geolocation_unittest.cc
@@ -395,6 +395,7 @@ &url_factory), GURL(kTestGeolocationProviderUrl)); url_factory.SetSimpleGeolocationProvider(&provider); + provider.set_geolocation_handler(geolocation_handler_.get()); { GeolocationReceiver receiver; provider.RequestGeolocation(base::TimeDelta::FromSeconds(1), GetParam(), @@ -464,6 +465,7 @@ &url_factory), GURL(kTestGeolocationProviderUrl)); url_factory.SetSimpleGeolocationProvider(&provider); + provider.set_geolocation_handler(geolocation_handler_.get()); { GeolocationReceiver receiver; provider.RequestGeolocation(base::TimeDelta::FromSeconds(1), false,
diff --git a/chromeos/services/assistant/chromium_http_connection.cc b/chromeos/services/assistant/chromium_http_connection.cc index 3d49ce56..08aa20a 100644 --- a/chromeos/services/assistant/chromium_http_connection.cc +++ b/chromeos/services/assistant/chromium_http_connection.cc
@@ -110,11 +110,6 @@ DCHECK(delegate_); DCHECK(network_task_runner_); - // URLFetcher does not ignore client cert requests by default. - // We do not have such certificates. - // TODO(igorc): Talk to Cast platform to see if we can do better. - URLFetcher::SetIgnoreCertificateRequests(true); - // Add a reference, so |this| cannot go away until Close() is called. AddRef(); }
diff --git a/components/autofill/core/browser/credit_card_save_manager.cc b/components/autofill/core/browser/credit_card_save_manager.cc index e06cc153..ce273610 100644 --- a/components/autofill/core/browser/credit_card_save_manager.cc +++ b/components/autofill/core/browser/credit_card_save_manager.cc
@@ -96,10 +96,14 @@ local_card_save_candidate_ = card; show_save_prompt_ = base::nullopt; - // Query the Autofill LegacyStrikeDatabase on if we should pop up the + // Query the Autofill StrikeDatabase on if we should pop up the // offer-to-save prompt for this card. if (base::FeatureList::IsEnabled( - features::kAutofillSaveCreditCardUsesStrikeSystem)) { + features::kAutofillSaveCreditCardUsesStrikeSystemV2)) { + OnDidGetStrikesForLocalSave(GetCreditCardSaveStrikeDatabase()->GetStrikes( + base::UTF16ToUTF8(local_card_save_candidate_.LastFourDigits()))); + } else if (base::FeatureList::IsEnabled( + features::kAutofillSaveCreditCardUsesStrikeSystem)) { LegacyStrikeDatabase* strike_database = client_->GetLegacyStrikeDatabase(); strike_database->GetStrikes( strike_database->GetKeyForCreditCardSave( @@ -238,10 +242,14 @@ weak_ptr_factory_.GetWeakPtr()), payments::kUploadCardBillableServiceNumber, payments::PaymentsClient::UploadCardSource::UPSTREAM_CHECKOUT_FLOW); - // Query the Autofill LegacyStrikeDatabase on if we should pop up the + // Query the Autofill StrikeDatabase on if we should pop up the // offer-to-save prompt for this card. if (base::FeatureList::IsEnabled( - features::kAutofillSaveCreditCardUsesStrikeSystem)) { + features::kAutofillSaveCreditCardUsesStrikeSystemV2)) { + OnDidGetStrikesForUploadSave(GetCreditCardSaveStrikeDatabase()->GetStrikes( + base::UTF16ToUTF8(upload_request_.card.LastFourDigits()))); + } else if (base::FeatureList::IsEnabled( + features::kAutofillSaveCreditCardUsesStrikeSystem)) { LegacyStrikeDatabase* strike_database = client_->GetLegacyStrikeDatabase(); strike_database->GetStrikes( strike_database->GetKeyForCreditCardSave( @@ -307,7 +315,17 @@ personal_data_manager_->AddFullServerCreditCard(upload_request_.card); } if (base::FeatureList::IsEnabled( - features::kAutofillSaveCreditCardUsesStrikeSystem)) { + features::kAutofillSaveCreditCardUsesStrikeSystemV2)) { + // Log how many strikes the card had when it was saved. + LogStrikesPresentWhenCardSaved( + /*is_local=*/false, + GetCreditCardSaveStrikeDatabase()->GetStrikes( + base::UTF16ToUTF8(upload_request_.card.LastFourDigits()))); + // Clear all strikes for this card, in case it is later removed. + GetCreditCardSaveStrikeDatabase()->ClearStrikes( + base::UTF16ToUTF8(upload_request_.card.LastFourDigits())); + } else if (base::FeatureList::IsEnabled( + features::kAutofillSaveCreditCardUsesStrikeSystem)) { LegacyStrikeDatabase* strike_database = client_->GetLegacyStrikeDatabase(); // Log how many strikes the card had when it was saved. @@ -325,22 +343,40 @@ base::DoNothing()); } } else { - if (base::FeatureList::IsEnabled( - features::kAutofillSaveCreditCardUsesStrikeSystem) && - show_save_prompt_.value()) { - // If the upload failed and the bubble was actually shown (NOT just the - // icon), count that as a strike against offering upload in the future. - LegacyStrikeDatabase* strike_database = - client_->GetLegacyStrikeDatabase(); - strike_database->AddStrike( - strike_database->GetKeyForCreditCardSave( - base::UTF16ToUTF8(upload_request_.card.LastFourDigits())), - base::BindRepeating(&CreditCardSaveManager::OnStrikeChangeComplete, - weak_ptr_factory_.GetWeakPtr())); + if (show_save_prompt_.value()) { + if (base::FeatureList::IsEnabled( + features::kAutofillSaveCreditCardUsesStrikeSystemV2)) { + // If the upload failed and the bubble was actually shown (NOT just the + // icon), count that as a strike against offering upload in the future. + int nth_strike_added = GetCreditCardSaveStrikeDatabase()->AddStrike( + base::UTF16ToUTF8(upload_request_.card.LastFourDigits())); + // Notify the browsertests that a strike was added. + OnStrikeChangeComplete(nth_strike_added); + } else if (base::FeatureList::IsEnabled( + features::kAutofillSaveCreditCardUsesStrikeSystem)) { + // If the upload failed and the bubble was actually shown (NOT just the + // icon), count that as a strike against offering upload in the future. + LegacyStrikeDatabase* strike_database = + client_->GetLegacyStrikeDatabase(); + strike_database->AddStrike( + strike_database->GetKeyForCreditCardSave( + base::UTF16ToUTF8(upload_request_.card.LastFourDigits())), + base::BindRepeating(&CreditCardSaveManager::OnStrikeChangeComplete, + weak_ptr_factory_.GetWeakPtr())); + } } } } +CreditCardSaveStrikeDatabase* +CreditCardSaveManager::GetCreditCardSaveStrikeDatabase() { + if (strike_database_.get() == nullptr) { + strike_database_ = std::make_unique<CreditCardSaveStrikeDatabase>( + CreditCardSaveStrikeDatabase(client_->GetStrikeDatabase())); + } + return strike_database_.get(); +} + void CreditCardSaveManager::OnDidGetStrikesForLocalSave(const int num_strikes) { show_save_prompt_ = num_strikes < kMaxStrikesToPreventPoppingUpOfferToSavePrompt; @@ -492,9 +528,18 @@ if (local_card_save_candidate_.HasFirstAndLastName()) AutofillMetrics::LogSaveCardWithFirstAndLastNameComplete( /*is_local=*/true); - if (base::FeatureList::IsEnabled( - features::kAutofillSaveCreditCardUsesStrikeSystem)) { + features::kAutofillSaveCreditCardUsesStrikeSystemV2)) { + // Log how many strikes the card had when it was saved. + LogStrikesPresentWhenCardSaved( + /*is_local=*/true, + GetCreditCardSaveStrikeDatabase()->GetStrikes(base::UTF16ToUTF8( + local_card_save_candidate_.LastFourDigits()))); + // Clear all strikes for this card, in case it is later removed. + GetCreditCardSaveStrikeDatabase()->ClearStrikes( + base::UTF16ToUTF8(local_card_save_candidate_.LastFourDigits())); + } else if (base::FeatureList::IsEnabled( + features::kAutofillSaveCreditCardUsesStrikeSystem)) { LegacyStrikeDatabase* strike_database = client_->GetLegacyStrikeDatabase(); // Log how many strikes the card had when it was saved. @@ -861,18 +906,28 @@ void CreditCardSaveManager::OnUserDidIgnoreOrDeclineSave( const base::string16& card_last_four_digits) { - if (base::FeatureList::IsEnabled( - features::kAutofillSaveCreditCardUsesStrikeSystem) && - show_save_prompt_.value()) { - // If the user rejected or ignored save and the offer-to-save bubble or - // infobar was actually shown (NOT just the icon if on desktop), count - // that as a strike against offering upload in the future. - LegacyStrikeDatabase* strike_database = client_->GetLegacyStrikeDatabase(); - strike_database->AddStrike( - strike_database->GetKeyForCreditCardSave( - base::UTF16ToUTF8(card_last_four_digits)), - base::BindRepeating(&CreditCardSaveManager::OnStrikeChangeComplete, - weak_ptr_factory_.GetWeakPtr())); + if (show_save_prompt_.value()) { + if (base::FeatureList::IsEnabled( + features::kAutofillSaveCreditCardUsesStrikeSystemV2)) { + // If the user rejected or ignored save and the offer-to-save bubble or + // infobar was actually shown (NOT just the icon if on desktop), count + // that as a strike against offering upload in the future. + int nth_strike_added = GetCreditCardSaveStrikeDatabase()->AddStrike( + base::UTF16ToUTF8(card_last_four_digits)); + OnStrikeChangeComplete(nth_strike_added); + } else if (base::FeatureList::IsEnabled( + features::kAutofillSaveCreditCardUsesStrikeSystem)) { + // If the user rejected or ignored save and the offer-to-save bubble or + // infobar was actually shown (NOT just the icon if on desktop), count + // that as a strike against offering upload in the future. + LegacyStrikeDatabase* strike_database = + client_->GetLegacyStrikeDatabase(); + strike_database->AddStrike( + strike_database->GetKeyForCreditCardSave( + base::UTF16ToUTF8(card_last_four_digits)), + base::BindRepeating(&CreditCardSaveManager::OnStrikeChangeComplete, + weak_ptr_factory_.GetWeakPtr())); + } } }
diff --git a/components/autofill/core/browser/credit_card_save_manager.h b/components/autofill/core/browser/credit_card_save_manager.h index 6f02e1c9..e9ac73d2 100644 --- a/components/autofill/core/browser/credit_card_save_manager.h +++ b/components/autofill/core/browser/credit_card_save_manager.h
@@ -16,6 +16,7 @@ #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/credit_card.h" +#include "components/autofill/core/browser/credit_card_save_strike_database.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/personal_data_manager.h" @@ -123,8 +124,12 @@ private: friend class CreditCardSaveManagerTest; friend class CreditCardSaveManagerTestObserverBridge; + friend class TestCreditCardSaveManager; friend class SaveCardBubbleViewsBrowserTestBase; + // Returns the CreditCardSaveStrikeDatabase for |client_|. + CreditCardSaveStrikeDatabase* GetCreditCardSaveStrikeDatabase(); + // Sets |show_save_prompt| and moves forward with offering credit card local // save. void OnDidGetStrikesForLocalSave(const int num_strikes); @@ -303,6 +308,8 @@ // The returned legal message from a GetUploadDetails call to Google Payments. std::unique_ptr<base::DictionaryValue> legal_message_; + std::unique_ptr<CreditCardSaveStrikeDatabase> strike_database_; + // May be null. ObserverForTest* observer_for_testing_ = nullptr;
diff --git a/components/autofill/core/browser/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/credit_card_save_manager_unittest.cc index e0172ab..ba27deb 100644 --- a/components/autofill/core/browser/credit_card_save_manager_unittest.cc +++ b/components/autofill/core/browser/credit_card_save_manager_unittest.cc
@@ -35,6 +35,7 @@ #include "components/autofill/core/browser/test_autofill_driver.h" #include "components/autofill/core/browser/test_autofill_manager.h" #include "components/autofill/core/browser/test_credit_card_save_manager.h" +#include "components/autofill/core/browser/test_credit_card_save_strike_database.h" #include "components/autofill/core/browser/test_form_data_importer.h" #include "components/autofill/core/browser/test_legacy_strike_database.h" #include "components/autofill/core/browser/test_personal_data_manager.h" @@ -106,6 +107,10 @@ autofill_client_.SetPrefs(test::PrefServiceForTesting()); autofill_client_.set_test_legacy_strike_database( std::move(test_legacy_strike_database)); + std::unique_ptr<TestStrikeDatabase> test_strike_database = + std::make_unique<TestStrikeDatabase>(); + strike_database_ = test_strike_database.get(); + autofill_client_.set_test_strike_database(std::move(test_strike_database)); personal_data_.Init(/*profile_database=*/database_, /*account_database=*/nullptr, /*pref_service=*/autofill_client_.GetPrefs(), @@ -138,6 +143,7 @@ &personal_data_, "en-US"); autofill_client_.set_test_form_data_importer( std::unique_ptr<TestFormDataImporter>(test_form_data_importer)); + autofill_client_.GetStrikeDatabase(); autofill_manager_.reset(new TestAutofillManager( autofill_driver_.get(), &autofill_client_, &personal_data_, &autocomplete_history_manager_)); @@ -339,6 +345,8 @@ payments::TestPaymentsClient* payments_client_; // Ends up getting owned (and destroyed) by TestAutofillClient: TestLegacyStrikeDatabase* legacy_strike_database_; + // Ends up getting owned (and destroyed) by TestAutofillClient: + TestStrikeDatabase* strike_database_; private: int ToHistogramSample(AutofillMetrics::CardUploadDecisionMetric metric) { @@ -4265,10 +4273,10 @@ "Autofill.StrikeDatabase.StrikesPresentWhenServerCardSaved", 0); } -// Tests that a card with some strikes (but not max strikes) should still show -// the save bubble/infobar. +// Tests that a card with some strikes using LegacyStrikeDatabase (but not max +// strikes) should still show the save bubble/infobar. TEST_F(CreditCardSaveManagerTest, - LocallySaveCreditCard_NotEnoughStrikesStillShowsOfferToSave) { + LocallySaveCreditCard_NotEnoughLegacyStrikesStillShowsOfferToSave) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillSaveCreditCardUsesStrikeSystem); credit_card_save_manager_->SetCreditCardUploadEnabled(false); @@ -4306,10 +4314,10 @@ "Autofill.StrikeDatabase.CreditCardSaveNotOfferedDueToMaxStrikes", 0); } -// Tests that a card with some strikes (but not max strikes) should still show -// the save bubble/infobar. +// Tests that a card with some strikes using LegacyStrikeDatabase (but not max +// strikes) should still show the save bubble/infobar. TEST_F(CreditCardSaveManagerTest, - UploadCreditCard_NotEnoughStrikesStillShowsOfferToSave) { + UploadCreditCard_NotEnoughLegacyStrikesStillShowsOfferToSave) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillSaveCreditCardUsesStrikeSystem); @@ -4357,9 +4365,10 @@ } #if defined(OS_ANDROID) || defined(OS_IOS) -// Tests that a card with max strikes does not offer save on mobile at all. +// Tests that a card with max strikes using LegacyStrikeDatabase does not offer +// save on mobile at all. TEST_F(CreditCardSaveManagerTest, - LocallySaveCreditCard_MaxStrikesDisallowsSave) { + LocallySaveCreditCard_MaxLegacyStrikesDisallowsSave) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillSaveCreditCardUsesStrikeSystem); credit_card_save_manager_->SetCreditCardUploadEnabled(false); @@ -4395,8 +4404,10 @@ AutofillMetrics::SaveTypeMetric::LOCAL, 1); } -// Tests that a card with max strikes does not offer save on mobile at all. -TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MaxStrikesDisallowsSave) { +// Tests that a card with max strikes using LegacyStrikeDatabase does not offer +// save on mobile at all. +TEST_F(CreditCardSaveManagerTest, + UploadCreditCard_MaxLegacyStrikesDisallowsSave) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillSaveCreditCardUsesStrikeSystem); @@ -4448,10 +4459,11 @@ } #else // !defined(OS_ANDROID) && !defined(OS_IOS) -// Tests that a card with max strikes should still offer to save on Desktop via -// the omnibox icon, but that the offer-to-save bubble itself is not shown. +// Tests that a card with max strikes using LegacyStrikeDatabase should still +// offer to save on Desktop via the omnibox icon, but that the offer-to-save +// bubble itself is not shown. TEST_F(CreditCardSaveManagerTest, - LocallySaveCreditCard_MaxStrikesStillAllowsSave) { + LocallySaveCreditCard_MaxLegacyStrikesStillAllowsSave) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillSaveCreditCardUsesStrikeSystem); credit_card_save_manager_->SetCreditCardUploadEnabled(false); @@ -4490,9 +4502,11 @@ AutofillMetrics::SaveTypeMetric::LOCAL, 1); } -// Tests that a card with max strikes should still offer to save on Desktop via -// the omnibox icon, but that the offer-to-save bubble itself is not shown. -TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MaxStrikesStillAllowsSave) { +// Tests that a card with max strikes using LegacyStrikeDatabase should still +// offer to save on Desktop via the omnibox icon, but that the offer-to-save +// bubble itself is not shown. +TEST_F(CreditCardSaveManagerTest, + UploadCreditCard_MaxLegacyStrikesStillAllowsSave) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillSaveCreditCardUsesStrikeSystem); @@ -4541,8 +4555,10 @@ } #endif -// Tests that adding a card clears all strikes for that card. -TEST_F(CreditCardSaveManagerTest, LocallySaveCreditCard_ClearStrikesOnAdd) { +// Tests that adding a card clears all LegacyStrikeDatabase strikes for that +// card. +TEST_F(CreditCardSaveManagerTest, + LocallySaveCreditCard_ClearLegacyStrikesOnAdd) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillSaveCreditCardUsesStrikeSystem); credit_card_save_manager_->SetCreditCardUploadEnabled(false); @@ -4576,8 +4592,9 @@ legacy_strike_database_->GetKeyForCreditCardSave("1111"))); } -// Tests that adding a card clears all strikes for that card. -TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ClearStrikesOnAdd) { +// Tests that adding a card clears all LegacyStrikeDatabase strikes for that +// card. +TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ClearLegacyStrikesOnAdd) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillSaveCreditCardUsesStrikeSystem); @@ -4620,8 +4637,10 @@ legacy_strike_database_->GetKeyForCreditCardSave("1111"))); } -// Tests that adding a card clears all strikes for that card. -TEST_F(CreditCardSaveManagerTest, LocallySaveCreditCard_NumStrikesLoggedOnAdd) { +// Tests that adding a card clears all LegacyStrikeDatabase strikes for that +// card. +TEST_F(CreditCardSaveManagerTest, + LocallySaveCreditCard_NumLegacyStrikesLoggedOnAdd) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillSaveCreditCardUsesStrikeSystem); credit_card_save_manager_->SetCreditCardUploadEnabled(false); @@ -4658,8 +4677,10 @@ /*sample=*/2, /*count=*/1); } -// Tests that adding a card clears all strikes for that card. -TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NumStrikesLoggedOnAdd) { +// Tests that adding a card clears all LegacyStrikeDatabase strikes for that +// card. +TEST_F(CreditCardSaveManagerTest, + UploadCreditCard_NumLegacyStrikesLoggedOnAdd) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillSaveCreditCardUsesStrikeSystem); @@ -4705,6 +4726,503 @@ /*sample=*/2, /*count=*/1); } +// Tests that one LegacyStrikeDatabase strike is added when upload failed and +// bubble is shown. +TEST_F(CreditCardSaveManagerTest, + UploadCreditCard_NumLegacyStrikesLoggedOnUploadNotSuccess) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystem); + const char* const server_id = "InstrumentData:1234"; + payments_client_->SetServerIdForCardUpload(server_id); + EXPECT_EQ(0, legacy_strike_database_->GetStrikesForTesting( + legacy_strike_database_->GetKeyForCreditCardSave("1111"))); + + // If upload failed and the bubble was shown, strike count should increase + // by 1. + credit_card_save_manager_->set_show_save_prompt(true); + credit_card_save_manager_->set_upload_request_card_number( + ASCIIToUTF16("4111111111111111")); + credit_card_save_manager_->OnDidUploadCard(AutofillClient::TRY_AGAIN_FAILURE, + server_id); + EXPECT_EQ(1, legacy_strike_database_->GetStrikesForTesting( + legacy_strike_database_->GetKeyForCreditCardSave("1111"))); +} + +// Tests that a card with some strikes (but not max strikes) should still show +// the save bubble/infobar. +TEST_F(CreditCardSaveManagerTest, + LocallySaveCreditCard_NotEnoughStrikesStillShowsOfferToSave) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + credit_card_save_manager_->SetCreditCardUploadEnabled(false); + TestCreditCardSaveStrikeDatabase credit_card_save_strike_database = + TestCreditCardSaveStrikeDatabase(strike_database_); + + // Add a single strike for the card to be added. + credit_card_save_strike_database.AddStrike("1111"); + EXPECT_EQ(1, credit_card_save_strike_database.GetStrikes("1111")); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(1 /* num_fillable_forms_parsed */); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[4].value = ASCIIToUTF16("123"); + + base::HistogramTester histogram_tester; + + FormSubmitted(credit_card_form); + EXPECT_TRUE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Verify that the offer-to-save bubble was still shown because the card did + // not have too many strikes. + EXPECT_TRUE( + autofill_client_.get_offer_to_save_credit_card_bubble_was_shown()); + // Verify that no histogram entry was logged. + histogram_tester.ExpectTotalCount( + "Autofill.StrikeDatabase.CreditCardSaveNotOfferedDueToMaxStrikes", 0); +} + +// Tests that a card with some strikes (but not max strikes) should still show +// the save bubble/infobar. +TEST_F(CreditCardSaveManagerTest, + UploadCreditCard_NotEnoughStrikesStillShowsOfferToSave) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + TestCreditCardSaveStrikeDatabase credit_card_save_strike_database = + TestCreditCardSaveStrikeDatabase(strike_database_); + + // Add a single strike for the card to be added. + credit_card_save_strike_database.AddStrike("1111"); + EXPECT_EQ(1, credit_card_save_strike_database.GetStrikes("1111")); + + // Create, fill and submit an address form in order to establish a recent + // profile which can be selected for the upload request. + FormData address_form; + test::CreateTestAddressFormData(&address_form); + FormsSeen(std::vector<FormData>(1, address_form)); + ExpectUniqueFillableFormParsedUkm(); + + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + FormSubmitted(address_form); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[4].value = ASCIIToUTF16("123"); + + base::HistogramTester histogram_tester; + + FormSubmitted(credit_card_form); + EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Verify that the offer-to-save bubble was still shown because the card did + // not have too many strikes. + EXPECT_TRUE( + autofill_client_.get_offer_to_save_credit_card_bubble_was_shown()); + // Verify that no histogram entry was logged. + histogram_tester.ExpectTotalCount( + "Autofill.StrikeDatabase.CreditCardSaveNotOfferedDueToMaxStrikes", 0); +} + +#if defined(OS_ANDROID) || defined(OS_IOS) +// Tests that a card with max strikes does not offer save on mobile at all. +TEST_F(CreditCardSaveManagerTest, + LocallySaveCreditCard_MaxStrikesDisallowsSave) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + credit_card_save_manager_->SetCreditCardUploadEnabled(false); + TestCreditCardSaveStrikeDatabase credit_card_save_strike_database = + TestCreditCardSaveStrikeDatabase(strike_database_); + + // Max out strikes for the card to be added. + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + EXPECT_EQ(3, credit_card_save_strike_database.GetStrikes("1111")); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(1 /* num_fillable_forms_parsed */); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[4].value = ASCIIToUTF16("123"); + + base::HistogramTester histogram_tester; + + // No form of credit card save should be shown. + FormSubmitted(credit_card_form); + EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Verify that the correct histogram entry was logged. + histogram_tester.ExpectBucketCount( + "Autofill.StrikeDatabase.CreditCardSaveNotOfferedDueToMaxStrikes", + AutofillMetrics::SaveTypeMetric::LOCAL, 1); +} + +// Tests that a card with max strikes does not offer save on mobile at all. +TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MaxStrikesDisallowsSave) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + TestCreditCardSaveStrikeDatabase credit_card_save_strike_database = + TestCreditCardSaveStrikeDatabase(strike_database_); + + // Max out strikes for the card to be added. + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + EXPECT_EQ(3, credit_card_save_strike_database.GetStrikes("1111")); + + // Create, fill and submit an address form in order to establish a recent + // profile which can be selected for the upload request. + FormData address_form; + test::CreateTestAddressFormData(&address_form); + FormsSeen(std::vector<FormData>(1, address_form)); + ExpectUniqueFillableFormParsedUkm(); + + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + FormSubmitted(address_form); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[4].value = ASCIIToUTF16("123"); + + base::HistogramTester histogram_tester; + + // No form of credit card save should be shown. + FormSubmitted(credit_card_form); + EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Verify that the correct histogram entries were logged. + ExpectCardUploadDecision( + histogram_tester, + AutofillMetrics::UPLOAD_NOT_OFFERED_MAX_STRIKES_ON_MOBILE); + histogram_tester.ExpectBucketCount( + "Autofill.StrikeDatabase.CreditCardSaveNotOfferedDueToMaxStrikes", + AutofillMetrics::SaveTypeMetric::SERVER, 1); + // Verify that the correct UKM was logged. + ExpectCardUploadDecisionUkm( + AutofillMetrics::UPLOAD_NOT_OFFERED_MAX_STRIKES_ON_MOBILE); +} + +#else // !defined(OS_ANDROID) && !defined(OS_IOS) +// Tests that a card with max strikes should still offer to save on Desktop via +// the omnibox icon, but that the offer-to-save bubble itself is not shown. +TEST_F(CreditCardSaveManagerTest, + LocallySaveCreditCard_MaxStrikesStillAllowsSave) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + credit_card_save_manager_->SetCreditCardUploadEnabled(false); + TestCreditCardSaveStrikeDatabase credit_card_save_strike_database = + TestCreditCardSaveStrikeDatabase(strike_database_); + + // Max out strikes for the card to be added. + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + EXPECT_EQ(3, credit_card_save_strike_database.GetStrikes("1111")); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(1 /* num_fillable_forms_parsed */); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[4].value = ASCIIToUTF16("123"); + + base::HistogramTester histogram_tester; + + FormSubmitted(credit_card_form); + EXPECT_TRUE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Verify that the offer-to-save bubble was not shown because the card had too + // many strikes. + EXPECT_FALSE( + autofill_client_.get_offer_to_save_credit_card_bubble_was_shown()); + // Verify that the correct histogram entry was logged. + histogram_tester.ExpectBucketCount( + "Autofill.StrikeDatabase.CreditCardSaveNotOfferedDueToMaxStrikes", + AutofillMetrics::SaveTypeMetric::LOCAL, 1); +} + +// Tests that a card with max strikes should still offer to save on Desktop via +// the omnibox icon, but that the offer-to-save bubble itself is not shown. +TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MaxStrikesStillAllowsSave) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + TestCreditCardSaveStrikeDatabase credit_card_save_strike_database = + TestCreditCardSaveStrikeDatabase(strike_database_); + + // Max out strikes for the card to be added. + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + EXPECT_EQ(3, credit_card_save_strike_database.GetStrikes("1111")); + + // Create, fill and submit an address form in order to establish a recent + // profile which can be selected for the upload request. + FormData address_form; + test::CreateTestAddressFormData(&address_form); + FormsSeen(std::vector<FormData>(1, address_form)); + ExpectUniqueFillableFormParsedUkm(); + + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + FormSubmitted(address_form); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[4].value = ASCIIToUTF16("123"); + + base::HistogramTester histogram_tester; + + FormSubmitted(credit_card_form); + EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Verify that the offer-to-save bubble was not shown because the card had too + // many strikes. + EXPECT_FALSE( + autofill_client_.get_offer_to_save_credit_card_bubble_was_shown()); + // Verify that the correct histogram entry was logged. + histogram_tester.ExpectBucketCount( + "Autofill.StrikeDatabase.CreditCardSaveNotOfferedDueToMaxStrikes", + AutofillMetrics::SaveTypeMetric::SERVER, 1); +} +#endif + +// Tests that adding a card clears all strikes for that card. +TEST_F(CreditCardSaveManagerTest, LocallySaveCreditCard_ClearStrikesOnAdd) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + credit_card_save_manager_->SetCreditCardUploadEnabled(false); + TestCreditCardSaveStrikeDatabase credit_card_save_strike_database = + TestCreditCardSaveStrikeDatabase(strike_database_); + + // Add two strikes for the card to be added. + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + EXPECT_EQ(2, credit_card_save_strike_database.GetStrikes("1111")); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(1 /* num_fillable_forms_parsed */); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[4].value = ASCIIToUTF16("123"); + + FormSubmitted(credit_card_form); + EXPECT_TRUE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Verify that adding the card reset the strike count for that card. + EXPECT_EQ(0, credit_card_save_strike_database.GetStrikes("1111")); +} + +// Tests that adding a card clears all strikes for that card. +TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ClearStrikesOnAdd) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + TestCreditCardSaveStrikeDatabase credit_card_save_strike_database = + TestCreditCardSaveStrikeDatabase(strike_database_); + + // Add two strikes for the card to be added. + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + EXPECT_EQ(2, credit_card_save_strike_database.GetStrikes("1111")); + + // Create, fill and submit an address form in order to establish a recent + // profile which can be selected for the upload request. + FormData address_form; + test::CreateTestAddressFormData(&address_form); + FormsSeen(std::vector<FormData>(1, address_form)); + ExpectUniqueFillableFormParsedUkm(); + + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + FormSubmitted(address_form); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[4].value = ASCIIToUTF16("123"); + + FormSubmitted(credit_card_form); + EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Verify that adding the card reset the strike count for that card. + EXPECT_EQ(0, credit_card_save_strike_database.GetStrikes("1111")); +} + +// Tests that adding a card clears all strikes for that card. +TEST_F(CreditCardSaveManagerTest, LocallySaveCreditCard_NumStrikesLoggedOnAdd) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + credit_card_save_manager_->SetCreditCardUploadEnabled(false); + + TestCreditCardSaveStrikeDatabase credit_card_save_strike_database = + TestCreditCardSaveStrikeDatabase(strike_database_); + + // Add two strikes for the card to be added. + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + EXPECT_EQ(2, credit_card_save_strike_database.GetStrikes("1111")); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(1 /* num_fillable_forms_parsed */); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[4].value = ASCIIToUTF16("123"); + + base::HistogramTester histogram_tester; + + FormSubmitted(credit_card_form); + EXPECT_TRUE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Verify that adding the card logged the number of strikes it had previously. + histogram_tester.ExpectUniqueSample( + "Autofill.StrikeDatabase.StrikesPresentWhenLocalCardSaved", + /*sample=*/2, /*count=*/1); +} + +// Tests that adding a card clears all strikes for that card. +TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NumStrikesLoggedOnAdd) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + TestCreditCardSaveStrikeDatabase credit_card_save_strike_database = + TestCreditCardSaveStrikeDatabase(strike_database_); + + // Add two strikes for the card to be added. + credit_card_save_strike_database.AddStrike("1111"); + credit_card_save_strike_database.AddStrike("1111"); + EXPECT_EQ(2, credit_card_save_strike_database.GetStrikes("1111")); + + // Create, fill and submit an address form in order to establish a recent + // profile which can be selected for the upload request. + FormData address_form; + test::CreateTestAddressFormData(&address_form); + FormsSeen(std::vector<FormData>(1, address_form)); + ExpectUniqueFillableFormParsedUkm(); + + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + FormSubmitted(address_form); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[4].value = ASCIIToUTF16("123"); + + base::HistogramTester histogram_tester; + + FormSubmitted(credit_card_form); + EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Verify that adding the card logged the number of strikes it had previously. + histogram_tester.ExpectUniqueSample( + "Autofill.StrikeDatabase.StrikesPresentWhenServerCardSaved", + /*sample=*/2, /*count=*/1); +} + +// Tests that one strike is added when upload failed and +// bubble is shown. +TEST_F(CreditCardSaveManagerTest, + UploadCreditCard_NumStrikesLoggedOnUploadNotSuccess) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + const char* const server_id = "InstrumentData:1234"; + payments_client_->SetServerIdForCardUpload(server_id); + TestCreditCardSaveStrikeDatabase credit_card_save_strike_database = + TestCreditCardSaveStrikeDatabase(strike_database_); + EXPECT_EQ(0, credit_card_save_strike_database.GetStrikes("1111")); + + // If upload failed and the bubble was shown, strike count should increase + // by 1. + credit_card_save_manager_->set_show_save_prompt(true); + credit_card_save_manager_->set_upload_request_card_number( + ASCIIToUTF16("4111111111111111")); + credit_card_save_manager_->OnDidUploadCard(AutofillClient::TRY_AGAIN_FAILURE, + server_id); + EXPECT_EQ(1, credit_card_save_strike_database.GetStrikes("1111")); +} + // Make sure that the PersonalDataManager gets notified when the user accepts // an upload offer. TEST_F(CreditCardSaveManagerTest, OnUserDidAcceptUpload_NotifiesPDM) {
diff --git a/components/autofill/core/browser/credit_card_save_strike_database.h b/components/autofill/core/browser/credit_card_save_strike_database.h index 2ea8770..7ce7dbb9 100644 --- a/components/autofill/core/browser/credit_card_save_strike_database.h +++ b/components/autofill/core/browser/credit_card_save_strike_database.h
@@ -17,7 +17,7 @@ class CreditCardSaveStrikeDatabase : public StrikeDatabaseIntegratorBase { public: CreditCardSaveStrikeDatabase(StrikeDatabase* strike_database); - ~CreditCardSaveStrikeDatabase(); + ~CreditCardSaveStrikeDatabase() override; std::string GetProjectPrefix() override; int GetMaxStrikesLimit() override;
diff --git a/components/autofill/core/browser/strike_database_integrator_base.h b/components/autofill/core/browser/strike_database_integrator_base.h index e5ddf21..9a7d4261 100644 --- a/components/autofill/core/browser/strike_database_integrator_base.h +++ b/components/autofill/core/browser/strike_database_integrator_base.h
@@ -16,7 +16,7 @@ class StrikeDatabaseIntegratorBase { public: StrikeDatabaseIntegratorBase(StrikeDatabase* strike_database); - ~StrikeDatabaseIntegratorBase(); + virtual ~StrikeDatabaseIntegratorBase(); // Returns whether or not strike count for |id| has reached the strike limit // set by GetMaxStrikesLimit().
diff --git a/components/autofill/core/browser/test_credit_card_save_manager.cc b/components/autofill/core/browser/test_credit_card_save_manager.cc index ab8a640..21007fe 100644 --- a/components/autofill/core/browser/test_credit_card_save_manager.cc +++ b/components/autofill/core/browser/test_credit_card_save_manager.cc
@@ -33,6 +33,15 @@ return credit_card_was_uploaded_; } +void TestCreditCardSaveManager::set_show_save_prompt(bool show_save_prompt) { + show_save_prompt_ = show_save_prompt; +} + +void TestCreditCardSaveManager::set_upload_request_card_number( + const base::string16& credit_card_number) { + upload_request_.card.SetNumber(credit_card_number); +} + void TestCreditCardSaveManager::OnDidUploadCard( AutofillClient::PaymentsRpcResult result, const std::string& server_id) {
diff --git a/components/autofill/core/browser/test_credit_card_save_manager.h b/components/autofill/core/browser/test_credit_card_save_manager.h index cc157fc..c01c302 100644 --- a/components/autofill/core/browser/test_credit_card_save_manager.h +++ b/components/autofill/core/browser/test_credit_card_save_manager.h
@@ -34,6 +34,10 @@ // Returns whether OnDidUploadCard() was called. bool CreditCardWasUploaded(); + void set_show_save_prompt(bool show_save_prompt); + + void set_upload_request_card_number(const base::string16& credit_card_number); + private: void OnDidUploadCard(AutofillClient::PaymentsRpcResult result, const std::string& server_id) override; @@ -41,6 +45,12 @@ bool credit_card_upload_enabled_ = false; bool credit_card_was_uploaded_ = false; + FRIEND_TEST_ALL_PREFIXES( + CreditCardSaveManagerTest, + UploadCreditCard_NumLegacyStrikesLoggedOnUploadNotSuccess); + FRIEND_TEST_ALL_PREFIXES(CreditCardSaveManagerTest, + UploadCreditCard_NumStrikesLoggedOnUploadNotSuccess); + DISALLOW_COPY_AND_ASSIGN(TestCreditCardSaveManager); };
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc index 48039901..0ccb33d 100644 --- a/components/autofill/core/common/autofill_features.cc +++ b/components/autofill/core/common/autofill_features.cc
@@ -198,6 +198,12 @@ "AutofillSaveCreditCardUsesStrikeSystem", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls whether offering to save cards will consider data from the Autofill +// strike database (new version). +const base::Feature kAutofillSaveCreditCardUsesStrikeSystemV2{ + "AutofillSaveCreditCardUsesStrikeSystemV2", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls whether experiment ids should be sent through // Google Payments RPCs or not. const base::Feature kAutofillSendExperimentIdsInPaymentsRPCs{
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h index 23b8f7de..8cd368e 100644 --- a/components/autofill/core/common/autofill_features.h +++ b/components/autofill/core/common/autofill_features.h
@@ -58,6 +58,7 @@ extern const base::Feature kAutofillSaveCardImprovedUserConsent; extern const base::Feature kAutofillSaveCardSignInAfterLocalSave; extern const base::Feature kAutofillSaveCreditCardUsesStrikeSystem; +extern const base::Feature kAutofillSaveCreditCardUsesStrikeSystemV2; extern const base::Feature kAutofillSaveOnProbablySubmitted; extern const base::Feature kAutofillSendExperimentIdsInPaymentsRPCs; extern const base::Feature kAutofillSendOnlyCountryInGetUploadDetails;
diff --git a/components/autofill/ios/browser/BUILD.gn b/components/autofill/ios/browser/BUILD.gn index c17fc30..3a5e25be 100644 --- a/components/autofill/ios/browser/BUILD.gn +++ b/components/autofill/ios/browser/BUILD.gn
@@ -67,6 +67,7 @@ "//base", "//base/test:test_support", "//components/autofill/core/browser", + "//components/leveldb_proto:leveldb_proto", "//ios/web/public", ] }
diff --git a/components/cdm/browser/media_drm_storage_impl.cc b/components/cdm/browser/media_drm_storage_impl.cc index 6bbb5da..a88497a 100644 --- a/components/cdm/browser/media_drm_storage_impl.cc +++ b/components/cdm/browser/media_drm_storage_impl.cc
@@ -268,12 +268,13 @@ // information. Note that this clears any existing session information. base::Value* CreateOriginDictAndReturnSessionsDict( base::Value* storage_dict, - const std::string& origin, + const url::Origin& origin, const base::UnguessableToken& origin_id) { DCHECK(storage_dict); DCHECK(storage_dict->is_dict()); - return storage_dict->SetKey(origin, OriginData(origin_id).ToDictValue()) + return storage_dict + ->SetKey(origin.Serialize(), OriginData(origin_id).ToDictValue()) ->SetKey(kSessions, base::Value(base::Value::Type::DICTIONARY)); } @@ -404,6 +405,28 @@ return false; } +// Returns the origin ID for |origin|, if it exists. Will return an empty value +// if the origin ID can not be found in |storage_dict|. +base::UnguessableToken GetOriginIdForOrigin( + const base::DictionaryValue* storage_dict, + const url::Origin& origin) { + DCHECK(storage_dict); + + const base::Value* origin_dict = storage_dict->FindKeyOfType( + origin.Serialize(), base::Value::Type::DICTIONARY); + if (!origin_dict) + return base::UnguessableToken::Null(); + + std::unique_ptr<OriginData> origin_data = + OriginData::FromDictValue(*origin_dict); + if (!origin_data) + return base::UnguessableToken::Null(); + + // |origin_id| can be empty even if |origin_dict| exists. This can happen + // if |origin_dict| was created with an old version of Chrome. + return origin_data->origin_id(); +} + } // namespace // static @@ -500,9 +523,12 @@ MediaDrmStorageImpl::MediaDrmStorageImpl( content::RenderFrameHost* render_frame_host, PrefService* pref_service, + GetOriginIdCB get_origin_id_cb, media::mojom::MediaDrmStorageRequest request) : FrameServiceBase(render_frame_host, std::move(request)), - pref_service_(pref_service) { + pref_service_(pref_service), + get_origin_id_cb_(get_origin_id_cb), + weak_factory_(this) { DVLOG(1) << __func__ << ": origin = " << origin(); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(pref_service_); @@ -512,49 +538,65 @@ MediaDrmStorageImpl::~MediaDrmStorageImpl() { DVLOG(1) << __func__; DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (init_cb_) + std::move(init_cb_).Run(base::UnguessableToken::Null()); } void MediaDrmStorageImpl::Initialize(InitializeCallback callback) { DVLOG(1) << __func__ << ": origin = " << origin(); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(!init_cb_); if (IsInitialized()) { std::move(callback).Run(origin_id_); return; } + DCHECK(!origin_id_); + // Check if the preference has an existing origin ID. + const base::DictionaryValue* storage_dict = + pref_service_->GetDictionary(kMediaDrmStorage); + origin_id_ = GetOriginIdForOrigin(storage_dict, origin()); + if (origin_id_) { + std::move(callback).Run(origin_id_); + return; + } + + // No origin ID, so fetch one asynchronously. OnOriginIdObtained() is called + // to finish initialization. + init_cb_ = std::move(callback); + get_origin_id_cb_.Run(base::BindOnce(&MediaDrmStorageImpl::OnOriginIdObtained, + weak_factory_.GetWeakPtr())); +} + +void MediaDrmStorageImpl::OnOriginIdObtained( + const base::UnguessableToken& origin_id) { + // If multiple MediaDrmStorage instances from the same web origin call + // Initialize concurrently, then a previous instance may have successfully + // obtained an origin ID and stored it for this origin. So check again to see + // if an origin ID has been saved, and use it instead of |origin_id|. This + // does mean that |origin_id| will be lost. + // TODO(crbug.com/919228): Once pre-provisioned origins are used, |origin_id| + // needs to be unprovisioned rather than dropped. DictionaryPrefUpdate update(pref_service_, kMediaDrmStorage); - base::DictionaryValue* storage_dict = update.Get(); - DCHECK(storage_dict); - - const base::Value* origin_dict = storage_dict->FindKeyOfType( - origin().Serialize(), base::Value::Type::DICTIONARY); - - base::UnguessableToken origin_id; - if (origin_dict) { - std::unique_ptr<OriginData> origin_data = - OriginData::FromDictValue(*origin_dict); - if (origin_data) - origin_id = origin_data->origin_id(); + auto stored_origin_id = GetOriginIdForOrigin(update.Get(), origin()); + if (stored_origin_id) { + origin_id_ = stored_origin_id; + std::move(init_cb_).Run(origin_id_); + return; } - // |origin_id| can be empty even if |origin_dict| exists. This can happen if - // |origin_dict| was created with an old version of Chrome. When this happens, - // there's no origin ID. Generate a new one and store it in the storage. - if (origin_id.is_empty()) { - origin_id = base::UnguessableToken::Create(); - - // Persist the origin ID into storage now. If multiple MediaDrmStorage - // instances from the same web origin call Initialize concurrently, they can - // get the same origin ID. - CreateOriginDictAndReturnSessionsDict(storage_dict, origin().Serialize(), - origin_id); - } - + // As there is no current value, persist the origin ID into storage now. + // TODO(crbug.com/917527): When pre-provisioned origins are supported, there + // may not be any available, so an empty origin ID can be used temporarily. + // The empty origin ID should not be saved in the preference, but should be + // returned. Note that having |origin_id_| set is used to determine if this + // object is initialized or not, so temporarily using an empty origin ID will + // affect that, and that needs to be fixed too. + DCHECK(origin_id) << "Empty origin ID not handled."; origin_id_ = origin_id; - - DCHECK(origin_id_); - std::move(callback).Run(origin_id_); + CreateOriginDictAndReturnSessionsDict(update.Get(), origin(), origin_id_); + std::move(init_cb_).Run(origin_id_); } void MediaDrmStorageImpl::OnProvisioned(OnProvisionedCallback callback) { @@ -574,8 +616,7 @@ // Update origin dict once origin provisioning completes. There may be // orphaned session info from a previous provisioning. Clear them by // recreating the dicts. - CreateOriginDictAndReturnSessionsDict(storage_dict, origin().Serialize(), - origin_id_); + CreateOriginDictAndReturnSessionsDict(storage_dict, origin(), origin_id_); std::move(callback).Run(true); } @@ -605,8 +646,8 @@ // branch. Deleting the profile causes reprovisioning of the origin. if (!sessions_dict) { DVLOG(1) << __func__ << ": No entry for origin " << origin(); - sessions_dict = CreateOriginDictAndReturnSessionsDict( - storage_dict, origin().Serialize(), origin_id_); + sessions_dict = CreateOriginDictAndReturnSessionsDict(storage_dict, + origin(), origin_id_); DCHECK(sessions_dict); }
diff --git a/components/cdm/browser/media_drm_storage_impl.h b/components/cdm/browser/media_drm_storage_impl.h index 8d23f79..4ca4ae1 100644 --- a/components/cdm/browser/media_drm_storage_impl.h +++ b/components/cdm/browser/media_drm_storage_impl.h
@@ -9,6 +9,7 @@ #include <vector> #include "base/callback.h" +#include "base/memory/weak_ptr.h" #include "base/threading/thread_checker.h" #include "base/time/time.h" #include "base/unguessable_token.h" @@ -35,6 +36,9 @@ class MediaDrmStorageImpl final : public content::FrameServiceBase<media::mojom::MediaDrmStorage> { public: + using GetOriginIdCB = base::RepeatingCallback<void( + base::OnceCallback<void(const base::UnguessableToken&)>)>; + static void RegisterProfilePrefs(PrefRegistrySimple* registry); // Get a list of origins that have persistent storage on the device. @@ -61,6 +65,7 @@ MediaDrmStorageImpl(content::RenderFrameHost* render_frame_host, PrefService* pref_service, + GetOriginIdCB get_origin_id_cb, media::mojom::MediaDrmStorageRequest request); // media::mojom::MediaDrmStorage implementation. @@ -80,11 +85,23 @@ // |this| can only be destructed as a FrameServiceBase. ~MediaDrmStorageImpl() final; + // Called when |get_origin_id_cb_| asynchronously returns a origin ID as part + // of Initialize(); + void OnOriginIdObtained(const base::UnguessableToken& origin_id); + PrefService* const pref_service_ = nullptr; + GetOriginIdCB get_origin_id_cb_; // ID for the current origin. Per EME spec on individualization, // implementation should not expose application-specific information. base::UnguessableToken origin_id_; + + // As Initialize() may be asynchronous, save the InitializeCallback when + // necessary. + InitializeCallback init_cb_; + + // NOTE: Weak pointers must be invalidated before all other member variables. + base::WeakPtrFactory<MediaDrmStorageImpl> weak_factory_; }; } // namespace cdm
diff --git a/components/cdm/browser/media_drm_storage_impl_unittest.cc b/components/cdm/browser/media_drm_storage_impl_unittest.cc index 1a5b7e4..d89ce25 100644 --- a/components/cdm/browser/media_drm_storage_impl_unittest.cc +++ b/components/cdm/browser/media_drm_storage_impl_unittest.cc
@@ -8,6 +8,7 @@ #include <utility> #include "base/run_loop.h" +#include "base/threading/thread_task_runner_handle.h" #include "base/unguessable_token.h" #include "components/prefs/testing_pref_service.h" #include "content/public/test/navigation_simulator.h" @@ -34,6 +35,17 @@ *out_origin_id = origin_id; } +void CreateOriginId( + base::OnceCallback<void(const base::UnguessableToken&)> callback) { + std::move(callback).Run(base::UnguessableToken::Create()); +} + +void CreateOriginIdAsync( + base::OnceCallback<void(const base::UnguessableToken&)> callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&CreateOriginId, std::move(callback))); +} + } // namespace class MediaDrmStorageImplTest : public content::RenderViewHostTestHarness { @@ -60,7 +72,8 @@ using SessionData = media::MediaDrmStorage::SessionData; std::unique_ptr<media::MediaDrmStorage> CreateMediaDrmStorage( - content::RenderFrameHost* rfh) { + content::RenderFrameHost* rfh, + MediaDrmStorageImpl::GetOriginIdCB get_origin_id_cb) { media::mojom::MediaDrmStoragePtr media_drm_storage_ptr; auto request = mojo::MakeRequest(&media_drm_storage_ptr); @@ -68,7 +81,8 @@ std::move(media_drm_storage_ptr)); // The created object will be destroyed on connection error. - new MediaDrmStorageImpl(rfh, pref_service_.get(), std::move(request)); + new MediaDrmStorageImpl(rfh, pref_service_.get(), + std::move(get_origin_id_cb), std::move(request)); return std::move(media_drm_storage); } @@ -79,7 +93,8 @@ DCHECK(origin_id); std::unique_ptr<media::MediaDrmStorage> media_drm_storage = - CreateMediaDrmStorage(SimulateNavigation(origin)); + CreateMediaDrmStorage(SimulateNavigation(origin), + base::BindRepeating(&CreateOriginId)); media_drm_storage->Initialize( base::BindOnce(OnMediaDrmStorageInit, origin_id)); @@ -199,8 +214,27 @@ TEST_F(MediaDrmStorageImplTest, Initialize_Concurrent) { content::RenderFrameHost* rfh = SimulateNavigation(GURL(kTestOrigin2)); - std::unique_ptr<media::MediaDrmStorage> storage1 = CreateMediaDrmStorage(rfh); - std::unique_ptr<media::MediaDrmStorage> storage2 = CreateMediaDrmStorage(rfh); + std::unique_ptr<media::MediaDrmStorage> storage1 = + CreateMediaDrmStorage(rfh, base::BindRepeating(&CreateOriginId)); + std::unique_ptr<media::MediaDrmStorage> storage2 = + CreateMediaDrmStorage(rfh, base::BindRepeating(&CreateOriginId)); + + base::UnguessableToken origin_id_1; + storage1->Initialize(base::BindOnce(OnMediaDrmStorageInit, &origin_id_1)); + base::UnguessableToken origin_id_2; + storage2->Initialize(base::BindOnce(OnMediaDrmStorageInit, &origin_id_2)); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(origin_id_1, origin_id_2); +} + +TEST_F(MediaDrmStorageImplTest, Initialize_Concurrent_Async) { + content::RenderFrameHost* rfh = SimulateNavigation(GURL(kTestOrigin2)); + + std::unique_ptr<media::MediaDrmStorage> storage1 = + CreateMediaDrmStorage(rfh, base::BindRepeating(&CreateOriginIdAsync)); + std::unique_ptr<media::MediaDrmStorage> storage2 = + CreateMediaDrmStorage(rfh, base::BindRepeating(&CreateOriginIdAsync)); base::UnguessableToken origin_id_1; storage1->Initialize(base::BindOnce(OnMediaDrmStorageInit, &origin_id_1));
diff --git a/components/dialog_strings.grdp b/components/dialog_strings.grdp index adadaaa1..2dbbf71 100644 --- a/components/dialog_strings.grdp +++ b/components/dialog_strings.grdp
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <grit-part> <!-- HTTP POST Warning --> - <message name="IDS_HTTP_POST_WARNING_TITLE" desc="Title for dialog that warns users about a navigation that results in a repost"> + <message name="IDS_HTTP_POST_WARNING_TITLE" desc="Title for dialog that warns users about a navigation that results in a repost" formatter_data="android_java"> Confirm Form Resubmission </message> <message name="IDS_HTTP_POST_WARNING" desc="Re-navigation to page that leads to HTTP POST" formatter_data="android_java">
diff --git a/components/gwp_asan/client/guarded_page_allocator.cc b/components/gwp_asan/client/guarded_page_allocator.cc index 8d07b76..9841dcd 100644 --- a/components/gwp_asan/client/guarded_page_allocator.cc +++ b/components/gwp_asan/client/guarded_page_allocator.cc
@@ -35,7 +35,14 @@ state_.total_pages = total_pages; state_.page_size = base::GetPageSize(); - CHECK(MapPages()); + + void* region = MapRegion(); + if (!region) + PLOG(FATAL) << "Failed to reserve allocator region"; + + state_.pages_base_addr = reinterpret_cast<uintptr_t>(region); + state_.first_page_addr = state_.pages_base_addr + state_.page_size; + state_.pages_end_addr = state_.pages_base_addr + RegionSize(); { // Obtain this lock exclusively to satisfy the thread-safety annotations, @@ -52,7 +59,7 @@ GuardedPageAllocator::~GuardedPageAllocator() { if (state_.total_pages) { - UnmapPages(); + UnmapRegion(); DeallocateStackTraces(); } } @@ -121,6 +128,10 @@ return state_.data[slot].alloc_size; } +size_t GuardedPageAllocator::RegionSize() const { + return (2 * state_.total_pages + 1) * state_.page_size; +} + size_t GuardedPageAllocator::ReserveSlot() { base::AutoLock lock(lock_);
diff --git a/components/gwp_asan/client/guarded_page_allocator.h b/components/gwp_asan/client/guarded_page_allocator.h index da1a330..fa8d96c4 100644 --- a/components/gwp_asan/client/guarded_page_allocator.h +++ b/components/gwp_asan/client/guarded_page_allocator.h
@@ -26,6 +26,10 @@ // Default maximum alignment for all returned allocations. static constexpr size_t kGpaAllocAlignment = 16; + // Does not allocate any memory for the allocator, to finish initializing call + // Init(). + GuardedPageAllocator(); + // Configures this allocator to allocate up to max_alloced_pages pages at a // time from a pool of total_pages pages, where: // 1 <= max_alloced_pages <= total_pages <= kGpaMaxPages @@ -68,19 +72,15 @@ AllocatorState::kGpaMaxPages - 1, "SlotTy can hold all possible slot values"); - // Does not allocate any memory for the allocator, to finish initializing call - // Init(). - GuardedPageAllocator(); - // Unmaps memory allocated by this class, if Init was called. ~GuardedPageAllocator(); - // Maps pages into memory and sets pages_base_addr_, first_page_addr_, and - // pages_end_addr on success. Returns true on success, false on failure. - bool MapPages(); + // Allocates/deallocates the virtual memory used for allocations. + void* MapRegion(); + void UnmapRegion(); - // Unmaps pages. - void UnmapPages(); + // Returns the size of the virtual memory region used to store allocations. + size_t RegionSize() const; // Mark page read-write or inaccessible. void MarkPageReadWrite(void*);
diff --git a/components/gwp_asan/client/guarded_page_allocator_win.cc b/components/gwp_asan/client/guarded_page_allocator_win.cc index 5c5880a9..77ea7f57 100644 --- a/components/gwp_asan/client/guarded_page_allocator_win.cc +++ b/components/gwp_asan/client/guarded_page_allocator_win.cc
@@ -15,25 +15,12 @@ // TODO(vtsyrklevich): See if the platform-specific memory allocation and // protection routines can be broken out in base/ and merged with those used for // PartionAlloc/ProtectedMemory. -bool GuardedPageAllocator::MapPages() { - size_t len = (2 * state_.total_pages + 1) * state_.page_size; - void* base_ptr = VirtualAlloc(nullptr, len, MEM_RESERVE, PAGE_NOACCESS); - if (!base_ptr) { - DPLOG(ERROR) << "Failed to reserve guarded allocator region"; - return false; - } - - uintptr_t base_addr = reinterpret_cast<uintptr_t>(base_ptr); - - state_.pages_base_addr = base_addr; - state_.first_page_addr = state_.pages_base_addr + state_.page_size; - state_.pages_end_addr = state_.pages_base_addr + len; - - return true; +void* GuardedPageAllocator::MapRegion() { + return VirtualAlloc(nullptr, RegionSize(), MEM_RESERVE, PAGE_NOACCESS); } -void GuardedPageAllocator::UnmapPages() { - DCHECK(state_.pages_base_addr); +void GuardedPageAllocator::UnmapRegion() { + CHECK(state_.pages_base_addr); BOOL err = VirtualFree(reinterpret_cast<void*>(state_.pages_base_addr), 0, MEM_RELEASE); DCHECK(err);
diff --git a/components/gwp_asan/client/sampling_allocator_shims.cc b/components/gwp_asan/client/sampling_allocator_shims.cc index 564683d..be51e4d9 100644 --- a/components/gwp_asan/client/sampling_allocator_shims.cc +++ b/components/gwp_asan/client/sampling_allocator_shims.cc
@@ -79,15 +79,14 @@ // easy to do there is no reason to pay the extra cost. SamplingState sampling_state; -// Returns the global allocator singleton used by the shims. -GuardedPageAllocator& GetGpa() { - static base::NoDestructor<GuardedPageAllocator> gpa; - return *gpa; -} +// The global allocator singleton used by the shims. Implemented as a global +// pointer instead of a function-local static to avoid initialization checks +// for every access. +GuardedPageAllocator* gpa = nullptr; void* AllocFn(const AllocatorDispatch* self, size_t size, void* context) { if (UNLIKELY(sampling_state.Sample())) - if (void* allocation = GetGpa().Allocate(size)) + if (void* allocation = gpa->Allocate(size)) return allocation; return self->next->alloc_function(self->next, size, context); @@ -104,7 +103,7 @@ return nullptr; size_t total_size = checked_total.ValueOrDie(); - if (void* allocation = GetGpa().Allocate(total_size)) { + if (void* allocation = gpa->Allocate(total_size)) { memset(allocation, 0, total_size); return allocation; } @@ -119,7 +118,7 @@ size_t size, void* context) { if (UNLIKELY(sampling_state.Sample())) - if (void* allocation = GetGpa().Allocate(size, alignment)) + if (void* allocation = gpa->Allocate(size, alignment)) return allocation; return self->next->alloc_aligned_function(self->next, alignment, size, @@ -133,29 +132,28 @@ if (UNLIKELY(!address)) return AllocFn(self, size, context); - if (LIKELY(!GetGpa().PointerIsMine(address))) + if (LIKELY(!gpa->PointerIsMine(address))) return self->next->realloc_function(self->next, address, size, context); if (!size) { - GetGpa().Deallocate(address); + gpa->Deallocate(address); return nullptr; } - void* new_alloc = GetGpa().Allocate(size); + void* new_alloc = gpa->Allocate(size); if (!new_alloc) new_alloc = self->next->alloc_function(self->next, size, context); if (!new_alloc) return nullptr; - memcpy(new_alloc, address, - std::min(size, GetGpa().GetRequestedSize(address))); - GetGpa().Deallocate(address); + memcpy(new_alloc, address, std::min(size, gpa->GetRequestedSize(address))); + gpa->Deallocate(address); return new_alloc; } void FreeFn(const AllocatorDispatch* self, void* address, void* context) { - if (UNLIKELY(GetGpa().PointerIsMine(address))) - return GetGpa().Deallocate(address); + if (UNLIKELY(gpa->PointerIsMine(address))) + return gpa->Deallocate(address); self->next->free_function(self->next, address, context); } @@ -163,8 +161,8 @@ size_t GetSizeEstimateFn(const AllocatorDispatch* self, void* address, void* context) { - if (UNLIKELY(GetGpa().PointerIsMine(address))) - return GetGpa().GetRequestedSize(address); + if (UNLIKELY(gpa->PointerIsMine(address))) + return gpa->GetRequestedSize(address); return self->next->get_size_estimate_function(self->next, address, context); } @@ -198,11 +196,11 @@ void* address, size_t size, void* context) { - if (UNLIKELY(GetGpa().PointerIsMine(address))) { + if (UNLIKELY(gpa->PointerIsMine(address))) { // TODO(vtsyrklevich): Perform this check in GuardedPageAllocator and report // failed checks using the same pipeline. - CHECK_EQ(size, GetGpa().GetRequestedSize(address)); - GetGpa().Deallocate(address); + CHECK_EQ(size, gpa->GetRequestedSize(address)); + gpa->Deallocate(address); return; } @@ -214,7 +212,7 @@ size_t alignment, void* context) { if (UNLIKELY(sampling_state.Sample())) - if (void* allocation = GetGpa().Allocate(size, alignment)) + if (void* allocation = gpa->Allocate(size, alignment)) return allocation; return self->next->aligned_malloc_function(self->next, size, alignment, @@ -229,33 +227,32 @@ if (UNLIKELY(!address)) return AlignedMallocFn(self, size, alignment, context); - if (LIKELY(!GetGpa().PointerIsMine(address))) + if (LIKELY(!gpa->PointerIsMine(address))) return self->next->aligned_realloc_function(self->next, address, size, alignment, context); if (!size) { - GetGpa().Deallocate(address); + gpa->Deallocate(address); return nullptr; } - void* new_alloc = GetGpa().Allocate(size, alignment); + void* new_alloc = gpa->Allocate(size, alignment); if (!new_alloc) new_alloc = self->next->aligned_malloc_function(self->next, size, alignment, context); if (!new_alloc) return nullptr; - memcpy(new_alloc, address, - std::min(size, GetGpa().GetRequestedSize(address))); - GetGpa().Deallocate(address); + memcpy(new_alloc, address, std::min(size, gpa->GetRequestedSize(address))); + gpa->Deallocate(address); return new_alloc; } static void AlignedFreeFn(const AllocatorDispatch* self, void* address, void* context) { - if (UNLIKELY(GetGpa().PointerIsMine(address))) - return GetGpa().Deallocate(address); + if (UNLIKELY(gpa->PointerIsMine(address))) + return gpa->Deallocate(address); self->next->aligned_free_function(self->next, address, context); } @@ -280,20 +277,21 @@ // We expose the allocator singleton for unit tests. GWP_ASAN_EXPORT GuardedPageAllocator& GetGpaForTesting() { - return GetGpa(); + return *gpa; } void InstallAllocatorHooks(size_t max_allocated_pages, size_t total_pages, size_t sampling_frequency) { #if BUILDFLAG(USE_ALLOCATOR_SHIM) - GetGpa().Init(max_allocated_pages, total_pages); - RegisterAllocatorAddress(GetGpa().GetCrashKeyAddress()); + gpa = new GuardedPageAllocator(); + gpa->Init(max_allocated_pages, total_pages); + RegisterAllocatorAddress(gpa->GetCrashKeyAddress()); sampling_state.Init(sampling_frequency); base::allocator::InsertAllocatorDispatch(&g_allocator_dispatch); #else ignore_result(g_allocator_dispatch); - ignore_result(&GetGpa); + ignore_result(gpa); DLOG(WARNING) << "base::allocator shims are unavailable for GWP-ASan."; #endif // BUILDFLAG(USE_ALLOCATOR_SHIM) }
diff --git a/components/pairing/bluetooth_controller_pairing_controller.cc b/components/pairing/bluetooth_controller_pairing_controller.cc index 3553f82..df1bb69 100644 --- a/components/pairing/bluetooth_controller_pairing_controller.cc +++ b/components/pairing/bluetooth_controller_pairing_controller.cc
@@ -250,9 +250,8 @@ } device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothControllerPairingController::OnGetAdapter, - ptr_factory_.GetWeakPtr())); - + base::BindOnce(&BluetoothControllerPairingController::OnGetAdapter, + ptr_factory_.GetWeakPtr())); } ControllerPairingController::DeviceIdList
diff --git a/components/pairing/bluetooth_host_pairing_controller.cc b/components/pairing/bluetooth_host_pairing_controller.cc index 7818f05f..3e1121fd 100644 --- a/components/pairing/bluetooth_host_pairing_controller.cc +++ b/components/pairing/bluetooth_host_pairing_controller.cc
@@ -471,8 +471,8 @@ } device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothHostPairingController::OnGetAdapter, - ptr_factory_.GetWeakPtr())); + base::BindOnce(&BluetoothHostPairingController::OnGetAdapter, + ptr_factory_.GetWeakPtr())); } std::string BluetoothHostPairingController::GetDeviceName() {
diff --git a/components/services/heap_profiling/public/cpp/allocator_shim.cc b/components/services/heap_profiling/public/cpp/allocator_shim.cc index ef9d4c8..eb66489 100644 --- a/components/services/heap_profiling/public/cpp/allocator_shim.cc +++ b/components/services/heap_profiling/public/cpp/allocator_shim.cc
@@ -4,37 +4,25 @@ #include "components/services/heap_profiling/public/cpp/allocator_shim.h" -#include "base/allocator/allocator_shim.h" #include "base/allocator/buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc.h" #include "base/atomicops.h" #include "base/compiler_specific.h" #include "base/debug/debugging_buildflags.h" #include "base/debug/stack_trace.h" #include "base/lazy_instance.h" #include "base/no_destructor.h" -#include "base/numerics/safe_conversions.h" -#include "base/rand_util.h" #include "base/sampling_heap_profiler/poisson_allocation_sampler.h" #include "base/synchronization/lock.h" #include "base/threading/thread_id_name_manager.h" -#include "base/threading/thread_local.h" #include "base/threading/thread_local_storage.h" -#include "base/trace_event/heap_profiler_allocation_context_tracker.h" #include "base/trace_event/heap_profiler_event_filter.h" #include "base/trace_event/memory_dump_manager.h" #include "build/build_config.h" -#include "components/services/heap_profiling/public/cpp/stream.h" #if defined(OS_POSIX) -#include <limits.h> #include <pthread.h> #endif -#if defined(OS_WIN) -#include <windows.h> -#endif - #if defined(OS_LINUX) || defined(OS_ANDROID) #include <sys/prctl.h> #endif @@ -50,140 +38,19 @@ namespace heap_profiling { -namespace { - -// The base implementation of TLS will leak memory if accessed during late -// stages of thread destruction. We roll our own implementation of TLS to -// prevent reentrancy. Since this only requires storing a single bit of -// information, we don't need to deal with hooking thread destruction to free -// memory, and thus avoid leaks and other issues. -#if defined(OS_WIN) -using TLSKey = DWORD; -#else -using TLSKey = pthread_key_t; -#endif - -// Holds a key to a TLS value. The TLS value (0 or 1) indicates whether the -// allocator shim is already being used on the current thread. -TLSKey g_prevent_reentrancy_key = 0; - -void InitializeReentrancyKey() { -#if defined(OS_WIN) - g_prevent_reentrancy_key = TlsAlloc(); - DCHECK_NE(TLS_OUT_OF_INDEXES, g_prevent_reentrancy_key); -#else - // Returns |0| on success. - int result = pthread_key_create(&g_prevent_reentrancy_key, nullptr); - DCHECK(!result); -#endif -} - -bool CanEnterAllocatorShim() { -#if defined(OS_WIN) - return !TlsGetValue(g_prevent_reentrancy_key); -#else - return !pthread_getspecific(g_prevent_reentrancy_key); -#endif -} - -void SetEnteringAllocatorShim(bool entering) { - void* value = entering ? reinterpret_cast<void*>(1) : nullptr; -#if defined(OS_WIN) - BOOL ret = TlsSetValue(g_prevent_reentrancy_key, value); - DPCHECK(ret); -#else - int ret = pthread_setspecific(g_prevent_reentrancy_key, value); - DCHECK_EQ(ret, 0); -#endif -} - -} // namespace - -// A ScopedAllow{Free,Alloc} instance must be instantiated in the scope of all -// hooks. -// AllocatorShimLogAlloc/AllocatorShimLogFree must only be called if it -// evaluates to true. -// -// There are two reasons why logging may be disabled. -// 1) To prevent reentrancy from logging code. -// 2) During thread destruction, Chrome TLS has been destroyed and it can no -// longer be used to determine if reentrancy is occurring. Attempting to -// access Chrome TLS after it has been destroyed is disallowed. -// -// Failure to prevent reentrancy can cause non-deterministic deadlock. This -// happens if a thread has grabbed the SendBuffer lock, then performs a heap -// allocation/free, which in turn tries to grab the SendBuffer lock. -// -// On macOS, this guard is also used to prevent double-counting during sampling. -// The implementation of libmalloc will sometimes call malloc [from -// one zone to another] - without this guard, the allocation would get two -// chances of being sampled. -class ScopedAllowFree { - public: - ScopedAllowFree() : allowed_(LIKELY(CanEnterAllocatorShim())) { - if (allowed_) - SetEnteringAllocatorShim(true); - } - ~ScopedAllowFree() { - if (allowed_) - SetEnteringAllocatorShim(false); - } - explicit operator bool() const { return allowed_; } - - private: - const bool allowed_; -}; - // Allocation logging also requires use of base TLS, so we must also check that // that is available. This means that allocations that occur after base TLS has // been torn down will not be logged. +// TODO(alph): Get rid of the class. crbug.com/917476 class ScopedAllowAlloc { public: - ScopedAllowAlloc() - : allowed_(LIKELY(CanEnterAllocatorShim()) && !HasTLSBeenDestroyed()) { - if (allowed_) - SetEnteringAllocatorShim(true); - } - ~ScopedAllowAlloc() { - if (allowed_) - SetEnteringAllocatorShim(false); - } - explicit operator bool() const { return allowed_; } - static inline bool HasTLSBeenDestroyed() { return UNLIKELY(base::ThreadLocalStorage::HasBeenDestroyed()); } - - private: - const bool allowed_; -}; - -// Realloc triggers both a free and an alloc. -class ScopedAllowRealloc { - public: - ScopedAllowRealloc() - : allow_free_(LIKELY(CanEnterAllocatorShim())), - allow_alloc_(LIKELY(allow_free_ && - (!base::ThreadLocalStorage::HasBeenDestroyed()))) { - if (allow_free_) - SetEnteringAllocatorShim(true); - } - ~ScopedAllowRealloc() { - if (allow_free_) - SetEnteringAllocatorShim(false); - } - bool allow_free() { return allow_free_; } - bool allow_alloc() { return allow_alloc_; } - - private: - const bool allow_free_; - const bool allow_alloc_; }; namespace { -using base::allocator::AllocatorDispatch; - bool g_initialized_ = false; base::LazyInstance<base::Lock>::Leaky g_on_init_allocator_shim_lock_; base::LazyInstance<base::OnceClosure>::Leaky g_on_init_allocator_shim_callback_; @@ -195,13 +62,6 @@ // In NATIVE stack mode, whether to insert stack names into the backtraces. bool g_include_thread_names = false; -// Whether to sample allocations. -bool g_sample_allocations = false; - -// Sampling rate describes the probability of sampling small allocations. -// Probability = MIN((size of allocation) / g_sampling_rate, 1). -uint32_t g_sampling_rate = 0; - // Prime since this is used like a hash table. Numbers of this magnitude seemed // to provide sufficient parallelism to avoid lock overhead in ad-hoc testing. constexpr int kNumSendBuffers = 17; @@ -209,12 +69,6 @@ // If writing to the SenderPipe ever takes longer than 10s, just give up. constexpr int kTimeoutMs = 10000; -// Functions set by a callback if the GC heap exists in the current process. -// This function pointers can be used to hook or unhook the oilpan allocations. -// It will be null in the browser process. -SetGCAllocHookFunction g_hook_gc_alloc = nullptr; -SetGCFreeHookFunction g_hook_gc_free = nullptr; - // The allocator shim needs to retain some additional state for each thread. struct ShimState { // The pointer must be valid for the lifetime of the process. @@ -225,46 +79,18 @@ // thread-local unordered_set of every address that has been sent from the // thread in question. std::unordered_set<const void*> sent_strings; - - // When we are sampling, each allocation's size is subtracted from - // |interval_to_next_sample|. When |interval_to_next_sample| is 0 or lower, - // the allocation is sampled, and |interval_to_next_sample| is reset. - int32_t interval_to_next_sample = 0; }; -// This algorithm is copied from "v8/src/profiler/sampling-heap-profiler.cc". -// We sample with a Poisson process, with constant average sampling interval. -// This follows the exponential probability distribution with parameter -// λ = 1/rate where rate is the average number of bytes between samples. -// -// Let u be a uniformly distributed random number between 0 and 1, then -// next_sample = (- ln u) / λ -int32_t GetNextSampleInterval(uint32_t rate) { - double u = base::RandDouble(); // Random value in [0, 1) - double v = 1 - u; // Random value in (0, 1] - double next = (-std::log(v)) * rate; - int32_t next_int = static_cast<int32_t>(next); - if (next_int < 1) - return 1; - return next_int; -} - -// This function is added to the TLS slot to clean up the instance when the -// thread exits. -void DestructShimState(void* shim_state) { - delete static_cast<ShimState*>(shim_state); -} - // Technically, this code could be called after Thread destruction and we would // need to guard this with ThreadLocalStorage::HasBeenDestroyed(), but all calls // to this are guarded behind ScopedAllowAlloc, which already makes the check. base::ThreadLocalStorage::Slot& ShimStateTLS() { static base::NoDestructor<base::ThreadLocalStorage::Slot> shim_state_tls( - &DestructShimState); + [](void* shim_state) { delete static_cast<ShimState*>(shim_state); }); return *shim_state_tls; } -// We don't need to worry about re-entrancy because ScopedAllowAlloc. +// We don't need to worry about re-entrancy because PoissonAllocationSampler // already guards against that. ShimState* GetShimState() { ShimState* state = static_cast<ShimState*>(ShimStateTLS().Get()); @@ -354,10 +180,10 @@ SenderPipe::Result result = g_sender_pipe->Send(buffer_, used_, kTimeoutMs); used_ = 0; if (result == SenderPipe::Result::kError) { - StopAllocatorShimDangerous(); + FlushBuffersAndClosePipe(); } if (result == SenderPipe::Result::kTimeout) { - StopAllocatorShimDangerous(); + FlushBuffersAndClosePipe(); // TODO(erikchen): Emit a histogram. https://crbug.com/777546. } } @@ -414,231 +240,6 @@ send_buffers[bin_to_use].Send(data, size); } -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -void* HookAlloc(const AllocatorDispatch* self, size_t size, void* context) { - ScopedAllowAlloc allow_logging; - - const AllocatorDispatch* const next = self->next; - void* ptr = next->alloc_function(next, size, context); - - if (LIKELY(allow_logging)) { - AllocatorShimLogAlloc(AllocatorType::kMalloc, ptr, size, nullptr); - } - - return ptr; -} - -void* HookZeroInitAlloc(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context) { - ScopedAllowAlloc allow_logging; - - const AllocatorDispatch* const next = self->next; - void* ptr = next->alloc_zero_initialized_function(next, n, size, context); - - if (LIKELY(allow_logging)) { - AllocatorShimLogAlloc(AllocatorType::kMalloc, ptr, n * size, nullptr); - } - return ptr; -} - -void* HookAllocAligned(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - ScopedAllowAlloc allow_logging; - - const AllocatorDispatch* const next = self->next; - void* ptr = next->alloc_aligned_function(next, alignment, size, context); - - if (LIKELY(allow_logging)) { - AllocatorShimLogAlloc(AllocatorType::kMalloc, ptr, size, nullptr); - } - return ptr; -} - -void* HookRealloc(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - ScopedAllowRealloc allow_logging; - - const AllocatorDispatch* const next = self->next; - void* ptr = next->realloc_function(next, address, size, context); - - if (LIKELY(allow_logging.allow_free())) { - AllocatorShimLogFree(address); - - // realloc(size == 0) means free() - if (size > 0 && LIKELY(allow_logging.allow_alloc())) - AllocatorShimLogAlloc(AllocatorType::kMalloc, ptr, size, nullptr); - } - - return ptr; -} - -void HookFree(const AllocatorDispatch* self, void* address, void* context) { - ScopedAllowFree allow_logging; - - const AllocatorDispatch* const next = self->next; - next->free_function(next, address, context); - - if (LIKELY(allow_logging)) { - AllocatorShimLogFree(address); - } -} - -size_t HookGetSizeEstimate(const AllocatorDispatch* self, - void* address, - void* context) { - const AllocatorDispatch* const next = self->next; - return next->get_size_estimate_function(next, address, context); -} - -unsigned HookBatchMalloc(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context) { - ScopedAllowAlloc allow_logging; - - const AllocatorDispatch* const next = self->next; - unsigned count = - next->batch_malloc_function(next, size, results, num_requested, context); - - if (LIKELY(allow_logging)) { - for (unsigned i = 0; i < count; ++i) - AllocatorShimLogAlloc(AllocatorType::kMalloc, results[i], size, nullptr); - } - return count; -} - -void HookBatchFree(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - ScopedAllowFree allow_logging; - - const AllocatorDispatch* const next = self->next; - next->batch_free_function(next, to_be_freed, num_to_be_freed, context); - - if (LIKELY(allow_logging)) { - for (unsigned i = 0; i < num_to_be_freed; ++i) - AllocatorShimLogFree(to_be_freed[i]); - } -} - -void HookFreeDefiniteSize(const AllocatorDispatch* self, - void* ptr, - size_t size, - void* context) { - ScopedAllowFree allow_logging; - - const AllocatorDispatch* const next = self->next; - next->free_definite_size_function(next, ptr, size, context); - - if (LIKELY(allow_logging)) { - AllocatorShimLogFree(ptr); - } -} - -void* HookAlignedMalloc(const AllocatorDispatch* self, - size_t size, - size_t alignment, - void* context) { - ScopedAllowAlloc allow_logging; - - const AllocatorDispatch* const next = self->next; - void* ptr = next->aligned_malloc_function(next, size, alignment, context); - - if (LIKELY(allow_logging)) { - AllocatorShimLogAlloc(AllocatorType::kMalloc, ptr, size, nullptr); - } - - return ptr; -} - -void* HookAlignedRealloc(const AllocatorDispatch* self, - void* address, - size_t size, - size_t alignment, - void* context) { - ScopedAllowRealloc allow_logging; - - const AllocatorDispatch* const next = self->next; - void* ptr = - next->aligned_realloc_function(next, address, size, alignment, context); - - if (LIKELY(allow_logging.allow_free())) { - AllocatorShimLogFree(address); - - // _aligned_realloc(size == 0) means _aligned_free() - if (size > 0 && LIKELY(allow_logging.allow_alloc())) - AllocatorShimLogAlloc(AllocatorType::kMalloc, ptr, size, nullptr); - } - - return ptr; -} - -void HookAlignedFree(const AllocatorDispatch* self, - void* address, - void* context) { - ScopedAllowFree allow_logging; - - const AllocatorDispatch* const next = self->next; - next->aligned_free_function(next, address, context); - - if (LIKELY(allow_logging)) { - AllocatorShimLogFree(address); - } -} - -AllocatorDispatch g_hooks = { - &HookAlloc, // alloc_function - &HookZeroInitAlloc, // alloc_zero_initialized_function - &HookAllocAligned, // alloc_aligned_function - &HookRealloc, // realloc_function - &HookFree, // free_function - &HookGetSizeEstimate, // get_size_estimate_function - &HookBatchMalloc, // batch_malloc_function - &HookBatchFree, // batch_free_function - &HookFreeDefiniteSize, // free_definite_size_function - &HookAlignedMalloc, // aligned_malloc_function - &HookAlignedRealloc, // aligned_realloc_function - &HookAlignedFree, // aligned_free_function - nullptr, // next -}; -#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) - -void HookPartitionAlloc(void* address, size_t size, const char* type) { - ScopedAllowAlloc allow_logging; - if (LIKELY(allow_logging)) { - AllocatorShimLogAlloc(AllocatorType::kPartitionAlloc, address, size, type); - } -} - -void HookPartitionFree(void* address) { - ScopedAllowFree allow_logging; - if (LIKELY(allow_logging)) { - AllocatorShimLogFree(address); - } -} - -void HookGCAlloc(uint8_t* address, size_t size, const char* type) { - ScopedAllowAlloc allow_logging; - if (LIKELY(allow_logging)) { - AllocatorShimLogAlloc(AllocatorType::kOilpan, address, size, type); - } -} - -void HookGCFree(uint8_t* address) { - ScopedAllowFree allow_logging; - if (LIKELY(allow_logging)) { - AllocatorShimLogFree(address); - } -} - // Updates an existing in_memory buffer with frame data. If a frame contains a // pointer to a cstring rather than an instruction pointer, and the profiling // service has not yet been informed of that pointer -> cstring mapping, sends a @@ -741,7 +342,6 @@ void InitTLSSlot() { base::PoissonAllocationSampler::Init(); - InitializeReentrancyKey(); ignore_result(ShimStateTLS()); } @@ -772,9 +372,6 @@ // Must be done before hooking any functions that make stack traces. base::debug::EnableInProcessStackDumping(); - g_sample_allocations = params->sampling_rate > 1; - g_sampling_rate = params->sampling_rate; - if (params->stack_mode == mojom::StackMode::NATIVE_WITH_THREAD_NAMES) { g_include_thread_names = true; base::ThreadIdNameManager::GetInstance()->InstallSetNameCallback( @@ -801,35 +398,9 @@ g_sender_pipe = sender_pipe; } -void InitAllocatorShim() { -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - // Normal malloc allocator shim. - base::allocator::InsertAllocatorDispatch(&g_hooks); -#endif - - // PartitionAlloc allocator shim. - base::PartitionAllocHooks::SetAllocationHook(&HookPartitionAlloc); - base::PartitionAllocHooks::SetFreeHook(&HookPartitionFree); - - // GC (Oilpan) allocator shim. - if (g_hook_gc_alloc && g_hook_gc_free) { - g_hook_gc_alloc(&HookGCAlloc); - g_hook_gc_free(&HookGCFree); - } -} - -void StopAllocatorShimDangerous() { +void FlushBuffersAndClosePipe() { // This ShareBuffer array is leaked on purpose to avoid races on Stop. g_send_buffers.Write(nullptr); - - base::PartitionAllocHooks::SetAllocationHook(nullptr); - base::PartitionAllocHooks::SetFreeHook(nullptr); - - if (g_hook_gc_alloc && g_hook_gc_free) { - g_hook_gc_alloc(nullptr); - g_hook_gc_free(nullptr); - } - if (g_sender_pipe) g_sender_pipe->Close(); } @@ -857,7 +428,7 @@ // Skip 3 top frames related to the profiler itself, e.g.: // base::debug::StackTrace::StackTrace // heap_profiling::RecordAndSendAlloc - // heap_profiling::`anonymous namespace'::HookAlloc + // sampling_heap_profiler::PoissonAllocationSampler::DoRecordAlloc size_t skip_frames = 3; #if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \ defined(OFFICIAL_BUILD) @@ -895,47 +466,6 @@ } } -void AllocatorShimLogAlloc(AllocatorType type, - void* address, - size_t sz, - const char* context) { - if (!g_send_buffers.Read()) - return; - - // When sampling, we divide allocations into two buckets. For allocations - // larger than g_sampling_rate we just skip the sampling logic entirely, since - // we want to record them with probability 1. Allocations smaller than - // g_sampling_rate we use a poisson process to sample. That gives us a - // computationally cheap mechanism to sample allocations with probability P = - // (size) / g_sampling_rate. - if (g_sample_allocations && LIKELY(sz < g_sampling_rate)) { - ShimState* shim_state = GetShimState(); - - shim_state->interval_to_next_sample -= sz; - - // When |interval_to_next_sample| underflows, we record a sample. - if (LIKELY(shim_state->interval_to_next_sample > 0)) { - return; - } - - // Very occasionally, when sampling, we'll want to take more than 1 sample - // from the same object. Ideally, we'd have a "count" or "weight" associated - // with the allocation in question. Since the stream format does not - // support that, just use |sz| as a proxy. - int sz_multiplier = 0; - while (shim_state->interval_to_next_sample <= 0) { - shim_state->interval_to_next_sample += - GetNextSampleInterval(g_sampling_rate); - ++sz_multiplier; - } - - sz *= sz_multiplier; - } - - if (address) - RecordAndSendAlloc(type, address, sz, context); -} - void RecordAndSendAlloc(AllocatorType type, void* address, size_t sz, @@ -985,14 +515,6 @@ DoSend(address, message, message_end - message, send_buffers); } -// This function may be called post Chrome TLS destruction, so it must not use -// Chrome TLS. It currently uses 3 classes from Chrome: base::Lock, -// base::TimeTicks and base::ScopedPlatformFile, all of which are safe. -void AllocatorShimLogFree(void* address) { - if (address) - RecordAndSendFree(address); -} - void RecordAndSendFree(void* address) { SendBuffer* send_buffers = g_send_buffers.Read(); if (!send_buffers) @@ -1017,24 +539,11 @@ SenderPipe::Result result = g_sender_pipe->Send(&barrier, sizeof(barrier), kTimeoutMs); if (result != SenderPipe::Result::kSuccess) { - StopAllocatorShimDangerous(); + FlushBuffersAndClosePipe(); // TODO(erikchen): Emit a histogram. https://crbug.com/777546. } } -void SetGCHeapAllocationHookFunctions(SetGCAllocHookFunction hook_alloc, - SetGCFreeHookFunction hook_free) { - g_hook_gc_alloc = hook_alloc; - g_hook_gc_free = hook_free; - - if (g_sender_pipe) { - // If starting the pipe beat Blink initialization, hook the - // functions now. - g_hook_gc_alloc(&HookGCAlloc); - g_hook_gc_free(&HookGCFree); - } -} - bool SetOnInitAllocatorShimCallbackForTesting( base::OnceClosure callback, scoped_refptr<base::TaskRunner> task_runner) {
diff --git a/components/services/heap_profiling/public/cpp/allocator_shim.h b/components/services/heap_profiling/public/cpp/allocator_shim.h index c7582e4..870613a 100644 --- a/components/services/heap_profiling/public/cpp/allocator_shim.h +++ b/components/services/heap_profiling/public/cpp/allocator_shim.h
@@ -16,45 +16,13 @@ // TLS slot, which is the entity that's supposed to prevent re-entrancy. void InitTLSSlot(); -// Begin profiling all allocations in the process. -void InitAllocatorShim(); - -// Stop profiling allocations by dropping shim callbacks. There is no way to -// consistently, synchronously stop the allocator shim without negatively -// impacting fast-path performance. This method eventually "turns off" the -// allocator shim by turning future calls to AllocatorShimLogAlloc and -// AllocatorShimLogFree into no-ops, modulo caching [g_send_buffers is not -// volatile, intentionally]. This method is well-defined, but isn't guaranteed -// to stop all messages to sender_pipe, since another thread might already be in -// the process of forming a message. -void StopAllocatorShimDangerous(); - -// Logs an allocation. The context is a null-terminated string of -// allocator-specific context information. It can be null if there is no -// context. -void AllocatorShimLogAlloc(AllocatorType type, - void* address, - size_t sz, - const char* context); - -// Logs a free. This must not rely on the base implementation of TLS. -void AllocatorShimLogFree(void* address); +// This method closes sender pipe. +void FlushBuffersAndClosePipe(); // Ensures all send buffers are flushed. The given barrier ID is sent to the // logging process so it knows when this operation is complete. void AllocatorShimFlushPipe(uint32_t barrier_id); -// Sets the functions that can be called to hook GC heap allocations. These -// must be set externally since GC heap only exists in renderer processes. If -// set, these functions functions will be called to enable logging of the GC -// heap. -using SetGCAllocHookFunction = void (*)(void (*)(uint8_t*, - size_t, - const char*)); -using SetGCFreeHookFunction = void (*)(void (*)(uint8_t*)); -void SetGCHeapAllocationHookFunctions(SetGCAllocHookFunction hook_alloc, - SetGCFreeHookFunction hook_free); - // Initializes allocation recorder. void InitAllocationRecorder(SenderPipe* sender_pipe, mojom::ProfilingParamsPtr params);
diff --git a/components/services/heap_profiling/public/cpp/client.cc b/components/services/heap_profiling/public/cpp/client.cc index 699c5cb..51c9dda 100644 --- a/components/services/heap_profiling/public/cpp/client.cc +++ b/components/services/heap_profiling/public/cpp/client.cc
@@ -55,7 +55,8 @@ if (!started_profiling_) return; - StopAllocatorShimDangerous(); + sampling_profiler_->StopProfiling(); + FlushBuffersAndClosePipe(); base::trace_event::MallocDumpProvider::GetInstance()->EnableMetrics(); @@ -130,14 +131,7 @@ void Client::StartProfilingInternal(mojom::ProfilingParamsPtr params) { uint32_t sampling_rate = params->sampling_rate; InitAllocationRecorder(sender_pipe_.get(), std::move(params)); - bool sampling_v2_enabled = base::GetFieldTrialParamByFeatureAsBool( - kOOPHeapProfilingFeature, kOOPHeapProfilingFeatureSamplingV2, - /* default_value */ false); - if (sampling_v2_enabled) { - sampling_profiler_->StartProfiling(sampling_rate); - } else { - InitAllocatorShim(); - } + sampling_profiler_->StartProfiling(sampling_rate); AllocatorHooksHaveBeenInitialized(); }
diff --git a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc index c3e703a..42829fe6 100644 --- a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc +++ b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc
@@ -43,6 +43,10 @@ sampler->Start(); } +void SamplingProfilerWrapper::StopProfiling() { + base::PoissonAllocationSampler::Get()->Stop(); +} + void SamplingProfilerWrapper::SampleAdded( void* address, size_t size,
diff --git a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h index ac2246b..92626cf 100644 --- a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h +++ b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h
@@ -18,6 +18,7 @@ ~SamplingProfilerWrapper() override; void StartProfiling(size_t sampling_rate); + void StopProfiling(); private: // base::PoissonAllocationSampler::SamplesObserver
diff --git a/components/services/heap_profiling/public/cpp/settings.cc b/components/services/heap_profiling/public/cpp/settings.cc index 933146d..07d6d138 100644 --- a/components/services/heap_profiling/public/cpp/settings.cc +++ b/components/services/heap_profiling/public/cpp/settings.cc
@@ -17,7 +17,6 @@ const base::Feature kOOPHeapProfilingFeature{"OOPHeapProfiling", base::FEATURE_DISABLED_BY_DEFAULT}; const char kOOPHeapProfilingFeatureMode[] = "mode"; -const char kOOPHeapProfilingFeatureSamplingV2[] = "sampling-v2"; namespace {
diff --git a/components/services/heap_profiling/public/cpp/settings.h b/components/services/heap_profiling/public/cpp/settings.h index 45b70495..10ed1d0 100644 --- a/components/services/heap_profiling/public/cpp/settings.h +++ b/components/services/heap_profiling/public/cpp/settings.h
@@ -62,8 +62,6 @@ bool IsBackgroundHeapProfilingEnabled(); bool ShouldKeepSmallAllocations(); -extern const char kOOPHeapProfilingFeatureSamplingV2[]; - // Exposed for testing. extern const base::Feature kOOPHeapProfilingFeature; extern const char kOOPHeapProfilingFeatureMode[];
diff --git a/components/test/data/search_provider_logos/ddljson_desktop0.json b/components/test/data/search_provider_logos/ddljson_desktop0.json index a8dfed74..b2c9b99 100644 --- a/components/test/data/search_provider_logos/ddljson_desktop0.json +++ b/components/test/data/search_provider_logos/ddljson_desktop0.json
@@ -1,2 +1,2 @@ )]}' -{"ddljson":{"alt_text":"Mary G. Ross\u0027 110th birthday","data_uri":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAADICAMAAAApx+PaAAADAFBMVEVHcEwXKj8NIjodb0QiLjUhLzsbc0Q5dk8cNksVgkt8ob4fLDkbKjcZrFUsb0UVjU0aKTciMj4iLDQjKy4iKi0iKCsrRFUnPlcUn1CAi5aLlJ+GhoJpamhTdpQcIyMgJSccIyIgKiokKy8lLjIjKi0gJiglLTEmMDTM0dKmpZ8gaT81RlJ0foxedYm2vME8PzwuO0Lp6OJITEswODMjJis9UVolOTk1SmGXnaZ0dXEpOU9FYXtYZnclNC0dJSVGXF0KfkZFomg1kVp+fnlZXF1/oIeHq5BLZWEXYjhZpnPa3b8ZTTNZdGrMycPZ2dO4trBjj3Jwlnz37NHs58sTaz+hoJqcmpXl5N+ssbgpPFGry6TX1c3PzseWlI9DWHC/zLz59O9Og2FNa4jB1LC0xbPx7ujp5t5xsoOqvqz28ert6N/w6uLy7eQcJCNSdZePjomft6Pu6+STr5mUw5Zaf0FqiEUYHyBSfKkrO1HYfQATOCijnUz5hwAUGx03UW3wskX/fwCwewvXtlQ5cUL9/PiqplDxxF6DeCXtwFXkv1nrozUQFhnf3dbk4drDwbqwr6kuSD/ovlVZiGne2tPj39e7ubOtq6bjvFX6fgBFfVjU0cpIeUO0sqyqqKPp49vb18+/vbbeulT4myDIxb96j0jkw1/5kgugp7AvQlk+X4vBuWLmyWnAxcmEmE3rz3Ps1X77qDja3t+Umky2qVChfE6+nUdONB5qUzhgPCOITysKDxSNYT69d0g7JRXJrVDMhE4hFAsICQy9ajkCAQAmLCTArlLPslJ3RiecWDCrZjo+aVxEeGr61Kc1VkzTbzfKkmXhrX7Ymmm7h1/EqojitpT3vpDzy5/aonfttYDprXX73rTkoW7XklooN0vu4pKMqbjP5eu20d0gVlaTuMpig6B5mbChxtt7q9SCs92OwOpzostpl8BNlM1fi7EiQGY3eaeJuuU0VIEnSHUZNVpGZZo2TGlbgcBAhbstYZhFco4udY4oZHZQpOc7ZJBQo+hYnNZjU8jJAAABAHRSTlMAIAerf1z//xH/VU06////LW+TqsHV0bf//////9D27/v/6Pb/////////////////////////////9P///////////////////////////////////////+L////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+/v////////7///////////b///94/v//qtyDm/v0pQAAnmpJREFUeAHsxTESgCAMBMAL0RMUlf+/Vmt7BsbcNgv5JRExQzCSfEEwsnJLCEVyIVdIKE7S0YPsGVM6Cl8VEkc+C3nRIf2kz6P5eReSl2GAbJiUVWu1PYRQ12IDIQwLreniOvDInlzm//9gZV/GY3TDMljGAm9KCaHrUqnEomamVwirOK4Z3isfglzpA2ZjU4POlKsIE6GjkEwIyvFExR+5nsASKVTCUisHZ6FcykspkqnmvzRKpSsVJVK5Ui0Fc/rQHUgXY0cMgDh3Y/X7e/rmeO1KetSUv9AkmA5ppMFiITojDgErGAouwgaTc1GOscVDLF1Z0Oul2Gy+0MGjLccKEuuRQzpZIYtmRJUEnBkdJICEcoa5TH5sydgk0Jii1oLLyQR4WVeA8FzFTDmPniKv15vter3bNMcGb7/fH47HPd7+1PetWRtgBmp2T1vv36m1E8qctx6544QHMIMazpqRNmNFAvn5YlHi0qhCLwhcccK2RG/tm8CPiYqCwLKi7p+1ssCOHAaC6DIzox2721XhDNizzHT/G+2UFHWY82213Ga9LyAAgyNhAOiuiP0wGO/Ppaf5fZlqUdMhcN1B2/uUSlAyEgFBeAPXUYZMh4wkbSura+sbOR2NxxOQRL4kpkvrI1Dseju3fQbuUHDv5aPvvd/GgFyTul4uto+OI71rqoXGHHWSXnvj+hRpQtK1D8PTMC/5GWvLTabTOU29gFmsOsITxXJLa28mJkgzXZd2U1SYd5H53TRpV8o5xkBAuIe6w1h+cXdzer82S880COLQHWeH+/3Nu7VR6Qjv309R4ByQH16zsKffRi+Sai/WPdchnil6Pwyqivf2yrGkV11XOaqk3Gp03ebvm8gaP376/FHa2tHck3zYqAgXcSgYwdqZHs7XaVSV8mDWlkAzBpwRpMo+4Jh8md2/f/+FRvryteVlnC8kPToNsS8c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\u003d\u003d","doodle_type":"SIMPLE","fingerprint":"151314e7","gallery_url":"http://www.google.com/doodles/mary-g-ross-110th-birthday?hl\u003den","header_layout":"LEGACY","id":0,"intent":"BUILTIN","large_image":{"alternate_url":"https://lh3.googleusercontent.com/4WMMyZBIzhWnJi-lfknABhr9C0wdLV3LHhoOSxVJEX0ml6i8KxAfBs5lYvVgqeJ2QtDDm5ARqzOjXxOP0tRgHsAAl427PNU9rjcpshQ","height":200,"image_id":5629499534213120,"image_name":"Large","is_animated_gif":false,"slot":22,"url":"/logos/doodles/2018/mary-g-ross-110th-birthday-4781894964084736-l.png","width":500},"log_url":"/async/ddllog?async\u003ddoodle:0,slot:22,type:1,cta:0","search_url":"/search?q\u003dtest","share_text":"Mary G. Ross\u0027 110th birthday! #GoogleDoodle\nhttps://g.co/doodle/b8nb2p","short_link":"//g.co/doodle/b8nb2p","show_now_header_search_affordance":false,"show_now_header_share_button":true,"small_image":{"alternate_url":"https://lh3.googleusercontent.com/s5LYQ5koym8Ha2dWFmnFwa0Y8vuGdQtwWtmLQbuawBVc_bIOe1Xo6DBVmIxHPao0P4apoMKxZo4_2OEizgouSjOLlyN0Uf-AZ-Qdn73sOQ","height":44,"image_id":6192449487634432,"image_name":"Small","is_animated_gif":false,"slot":23,"url":"/logos/doodles/2018/mary-g-ross-110th-birthday-4781894964084736-s.png","width":120},"target_url":"/search?q\u003dtest\u0026source\u003ddoodle-ntp","time_to_live_ms":463124901000}} \ No newline at end of file +{"ddljson":{"alt_text":"Mary G. Ross\u0027 110th birthday","data_uri":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAADICAMAAAApx+PaAAADAFBMVEVHcEwXKj8NIjodb0QiLjUhLzsbc0Q5dk8cNksVgkt8ob4fLDkbKjcZrFUsb0UVjU0aKTciMj4iLDQjKy4iKi0iKCsrRFUnPlcUn1CAi5aLlJ+GhoJpamhTdpQcIyMgJSccIyIgKiokKy8lLjIjKi0gJiglLTEmMDTM0dKmpZ8gaT81RlJ0foxedYm2vME8PzwuO0Lp6OJITEswODMjJis9UVolOTk1SmGXnaZ0dXEpOU9FYXtYZnclNC0dJSVGXF0KfkZFomg1kVp+fnlZXF1/oIeHq5BLZWEXYjhZpnPa3b8ZTTNZdGrMycPZ2dO4trBjj3Jwlnz37NHs58sTaz+hoJqcmpXl5N+ssbgpPFGry6TX1c3PzseWlI9DWHC/zLz59O9Og2FNa4jB1LC0xbPx7ujp5t5xsoOqvqz28ert6N/w6uLy7eQcJCNSdZePjomft6Pu6+STr5mUw5Zaf0FqiEUYHyBSfKkrO1HYfQATOCijnUz5hwAUGx03UW3wskX/fwCwewvXtlQ5cUL9/PiqplDxxF6DeCXtwFXkv1nrozUQFhnf3dbk4drDwbqwr6kuSD/ovlVZiGne2tPj39e7ubOtq6bjvFX6fgBFfVjU0cpIeUO0sqyqqKPp49vb18+/vbbeulT4myDIxb96j0jkw1/5kgugp7AvQlk+X4vBuWLmyWnAxcmEmE3rz3Ps1X77qDja3t+Umky2qVChfE6+nUdONB5qUzhgPCOITysKDxSNYT69d0g7JRXJrVDMhE4hFAsICQy9ajkCAQAmLCTArlLPslJ3RiecWDCrZjo+aVxEeGr61Kc1VkzTbzfKkmXhrX7Ymmm7h1/EqojitpT3vpDzy5/aonfttYDprXX73rTkoW7XklooN0vu4pKMqbjP5eu20d0gVlaTuMpig6B5mbChxtt7q9SCs92OwOpzostpl8BNlM1fi7EiQGY3eaeJuuU0VIEnSHUZNVpGZZo2TGlbgcBAhbstYZhFco4udY4oZHZQpOc7ZJBQo+hYnNZjU8jJAAABAHRSTlMAIAerf1z//xH/VU06////LW+TqsHV0bf//////9D27/v/6Pb/////////////////////////////9P///////////////////////////////////////+L////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+/v////////7///////////b///94/v//qtyDm/v0pQAAnmpJREFUeAHsxTESgCAMBMAL0RMUlf+/Vmt7BsbcNgv5JRExQzCSfEEwsnJLCEVyIVdIKE7S0YPsGVM6Cl8VEkc+C3nRIf2kz6P5eReSl2GAbJiUVWu1PYRQ12IDIQwLreniOvDInlzm//9gZV/GY3TDMljGAm9KCaHrUqnEomamVwirOK4Z3isfglzpA2ZjU4POlKsIE6GjkEwIyvFExR+5nsASKVTCUisHZ6FcykspkqnmvzRKpSsVJVK5Ui0Fc/rQHUgXY0cMgDh3Y/X7e/rmeO1KetSUv9AkmA5ppMFiITojDgErGAouwgaTc1GOscVDLF1Z0Oul2Gy+0MGjLccKEuuRQzpZIYtmRJUEnBkdJICEcoa5TH5sydgk0Jii1oLLyQR4WVeA8FzFTDmPniKv15vter3bNMcGb7/fH47HPd7+1PetWRtgBmp2T1vv36m1E8qctx6544QHMIMazpqRNmNFAvn5YlHi0qhCLwhcccK2RG/tm8CPiYqCwLKi7p+1ssCOHAaC6DIzox2721XhDNizzHT/G+2UFHWY82213Ga9LyAAgyNhAOiuiP0wGO/Ppaf5fZlqUdMhcN1B2/uUSlAyEgFBeAPXUYZMh4wkbSura+sbOR2NxxOQRL4kpkvrI1Dseju3fQbuUHDv5aPvvd/GgFyTul4uto+OI71rqoXGHHWSXnvj+hRpQtK1D8PTMC/5GWvLTabTOU29gFmsOsITxXJLa28mJkgzXZd2U1SYd5H53TRpV8o5xkBAuIe6w1h+cXdzer82S880COLQHWeH+/3Nu7VR6Qjv309R4ByQH16zsKffRi+Sai/WPdchnil6Pwyqivf2yrGkV11XOaqk3Gp03ebvm8gaP376/FHa2tHck3zYqAgXcSgYwdqZHs7XaVSV8mDWlkAzBpwRpMo+4Jh8md2/f/+FRvryteVlnC8kPToNsS8c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\u003d\u003d","doodle_type":"SIMPLE","fingerprint":"151314e7","gallery_url":"http://www.google.com/doodles/mary-g-ross-110th-birthday?hl\u003den","header_layout":"LEGACY","id":0,"intent":"BUILTIN","large_image":{"alternate_url":"https://lh3.googleusercontent.com/4WMMyZBIzhWnJi-lfknABhr9C0wdLV3LHhoOSxVJEX0ml6i8KxAfBs5lYvVgqeJ2QtDDm5ARqzOjXxOP0tRgHsAAl427PNU9rjcpshQ","height":200,"image_id":5629499534213120,"image_name":"Large","is_animated_gif":false,"slot":22,"url":"/logos/doodles/2018/mary-g-ross-110th-birthday-4781894964084736-l.png","width":500},"log_url":"/async/ddllog?async\u003ddoodle:0,slot:22,type:1,cta:0","search_url":"/search?q\u003dtest\u0026ct\u003dmary-g-ross-110th-birthday-4781894964084736-l\u0026hl\u003den","share_button":{"background_color":"#ffcc66","icon_image":"iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAoklEQVR42mNgmJnGQAZ2AOKZQPwAimdCxRBqyDT0DBD/R8NnUAwnw+CZWAyF4ZmUGPwAj8EPBp3BzkB8mdSgwBfTMkC8EsmAW8RGHq6YPgu14DOUD6IrgNiF2OSGL6ZheDUQyxEdbEREyCcgdiU5kkmKaTIMxhcUlylxMa7Iu0VpGONKbqDYL6ckVRDCZKdjmua84VkI0bQ8plkNQrM6jygMAJlIiA8sL6ZgAAAAAElFTkSuQmCC","offset_x":287,"offset_y":149,"opacity":1},"share_text":"Mary G. Ross\u0027 110th birthday! #GoogleDoodle\nhttps://g.co/doodle/b8nb2p","short_link":"//g.co/doodle/b8nb2p","show_now_header_search_affordance":false,"show_now_header_share_button":true,"small_image":{"alternate_url":"https://lh3.googleusercontent.com/s5LYQ5koym8Ha2dWFmnFwa0Y8vuGdQtwWtmLQbuawBVc_bIOe1Xo6DBVmIxHPao0P4apoMKxZo4_2OEizgouSjOLlyN0Uf-AZ-Qdn73sOQ","height":44,"image_id":6192449487634432,"image_name":"Small","is_animated_gif":false,"slot":23,"url":"/logos/doodles/2018/mary-g-ross-110th-birthday-4781894964084736-s.png","width":120},"target_url":"/search?q\u003dtest\u0026source\u003ddoodle-ntp","time_to_live_ms":453109447000}}
diff --git a/components/test/data/search_provider_logos/ddljson_desktop0_fp.json b/components/test/data/search_provider_logos/ddljson_desktop0_fp.json index 811afa1..dd02e39 100644 --- a/components/test/data/search_provider_logos/ddljson_desktop0_fp.json +++ b/components/test/data/search_provider_logos/ddljson_desktop0_fp.json
@@ -1,2 +1,2 @@ )]}' -{"ddljson":{"alt_text":"Mary G. Ross\u0027 110th birthday","doodle_type":"SIMPLE","fingerprint":"151314e7","gallery_url":"http://www.google.com/doodles/mary-g-ross-110th-birthday?hl\u003den","header_layout":"LEGACY","id":0,"intent":"BUILTIN","large_image":{"alternate_url":"https://lh3.googleusercontent.com/4WMMyZBIzhWnJi-lfknABhr9C0wdLV3LHhoOSxVJEX0ml6i8KxAfBs5lYvVgqeJ2QtDDm5ARqzOjXxOP0tRgHsAAl427PNU9rjcpshQ","height":200,"image_id":5629499534213120,"image_name":"Large","is_animated_gif":false,"slot":22,"url":"/logos/doodles/2018/mary-g-ross-110th-birthday-4781894964084736-l.png","width":500},"log_url":"/async/ddllog?async\u003ddoodle:0,slot:22,type:1,cta:0","search_url":"/search?q\u003dtest","share_text":"Mary G. Ross\u0027 110th birthday! #GoogleDoodle\nhttps://g.co/doodle/b8nb2p","short_link":"//g.co/doodle/b8nb2p","show_now_header_search_affordance":false,"show_now_header_share_button":true,"small_image":{"alternate_url":"https://lh3.googleusercontent.com/s5LYQ5koym8Ha2dWFmnFwa0Y8vuGdQtwWtmLQbuawBVc_bIOe1Xo6DBVmIxHPao0P4apoMKxZo4_2OEizgouSjOLlyN0Uf-AZ-Qdn73sOQ","height":44,"image_id":6192449487634432,"image_name":"Small","is_animated_gif":false,"slot":23,"url":"/logos/doodles/2018/mary-g-ross-110th-birthday-4781894964084736-s.png","width":120},"target_url":"/search?q\u003dtest\u0026source\u003ddoodle-ntp","time_to_live_ms":463124901000}} \ No newline at end of file +{"ddljson":{"alt_text":"Mary G. Ross\u0027 110th birthday","doodle_type":"SIMPLE","fingerprint":"151314e7","gallery_url":"http://www.google.com/doodles/mary-g-ross-110th-birthday?hl\u003den","header_layout":"LEGACY","id":0,"intent":"BUILTIN","large_image":{"alternate_url":"https://lh3.googleusercontent.com/4WMMyZBIzhWnJi-lfknABhr9C0wdLV3LHhoOSxVJEX0ml6i8KxAfBs5lYvVgqeJ2QtDDm5ARqzOjXxOP0tRgHsAAl427PNU9rjcpshQ","height":200,"image_id":5629499534213120,"image_name":"Large","is_animated_gif":false,"slot":22,"url":"/logos/doodles/2018/mary-g-ross-110th-birthday-4781894964084736-l.png","width":500},"log_url":"/async/ddllog?async\u003ddoodle:0,slot:22,type:1,cta:0","search_url":"/search?q\u003dtest","share_button":{"background_color":"#ffcc66","icon_image":"iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAoklEQVR42mNgmJnGQAZ2AOKZQPwAimdCxRBqyDT0DBD/R8NnUAwnw+CZWAyF4ZmUGPwAj8EPBp3BzkB8mdSgwBfTMkC8EsmAW8RGHq6YPgu14DOUD6IrgNiF2OSGL6ZheDUQyxEdbEREyCcgdiU5kkmKaTIMxhcUlylxMa7Iu0VpGONKbqDYL6ckVRDCZKdjmua84VkI0bQ8plkNQrM6jygMAJlIiA8sL6ZgAAAAAElFTkSuQmCC","offset_x":287,"offset_y":149,"opacity":1},"share_text":"Mary G. Ross\u0027 110th birthday! #GoogleDoodle\nhttps://g.co/doodle/b8nb2p","short_link":"//g.co/doodle/b8nb2p","show_now_header_search_affordance":false,"show_now_header_share_button":true,"small_image":{"alternate_url":"https://lh3.googleusercontent.com/s5LYQ5koym8Ha2dWFmnFwa0Y8vuGdQtwWtmLQbuawBVc_bIOe1Xo6DBVmIxHPao0P4apoMKxZo4_2OEizgouSjOLlyN0Uf-AZ-Qdn73sOQ","height":44,"image_id":6192449487634432,"image_name":"Small","is_animated_gif":false,"slot":23,"url":"/logos/doodles/2018/mary-g-ross-110th-birthday-4781894964084736-s.png","width":120},"target_url":"/search?q\u003dtest\u0026source\u003ddoodle-ntp","time_to_live_ms":453109446000}} \ No newline at end of file
diff --git a/components/test/data/search_provider_logos/ddljson_desktop1.json b/components/test/data/search_provider_logos/ddljson_desktop1.json index a456c45..8fcd018 100644 --- a/components/test/data/search_provider_logos/ddljson_desktop1.json +++ b/components/test/data/search_provider_logos/ddljson_desktop1.json
@@ -1,2 +1,2 @@ )]}' -{"ddljson":{"alt_text":"Prayoon Yomyiam\u0027s 85th Birthday","cta_data_uri":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAbIAAADZCAMAAABy6UUDAAADAFBMVEVHcEyVmXmjmnp9jXpscYJpaYJhgH9Lln5UhHtCh3ZxdYKYm3mMm3uTmnqdm3l8mn2EknZscINpaYJsZoJViIFKmX9Mln9RmIFqn4J6pn9ZmH9bgYFraIJqa4Jfe4FjcIFSj4BPk39pa4NXeHxCh3ZYinVmbIJqjXZzj3Z8kXWDg3xpZ4Nydn6Ok3aflXWKinqXk3WkmXZwcIFpaoNIg3c+iHVNiXVhjHZec35QfnpmhHyrm3Z4d4BnZoJkd4Jufnt1gnx6iHpqbIJ/e3+Dmnxrcn5imH5ocYJrboNra4NjZYGIqn50mX23n3hsmH1wdoNuc4NuaYRtcYN+fot1cItybolvbYafo4Tv0sbXtq6KkJVxdYOMoIPuyLP/6dZ4co5pan5sbIVnanhgYG1YXWNPT1FKSkpISEhtYFiwlqHBuapWVlp7bmD/48VCREWShIajjKN8oolvkptxeYuSqY+7naiAqZOUvJ95gZWer6E6g3Kqya2Sn55mpZFKiX1goYxBjXu81LhuqJahwKZvdIeIs5t2rppYnIdflo5ZiYiqqo+xtZ1qh5E/lYVhiI2DeZc7oZJ1hp1yfpF8dJF/d5SstLd0jp6Lfp5olJV6hKKLtqyokK97jKSSgqWHfJuPgKHMqazlvqyYhqhwhpj/1rX/3b7/38P/2rr/4MZ+saPSs5j5zbD/4sn/7b3/5MyEiKqhhNerf/DFk/PftOFbUUrQsYWOdmVloMSxgf+2gv+ufv3yypf/2p7hv5W6hf//1pz805qylr2eiq//5dB7rc6CYcmYc9pqUbAmJmaje+eSeMS/ppQjJGI+NYCojoUrKWtUQ5h8nLv8zZT8xoz4v4jvrod4jaH4r4XTn4SEdK2/lH92b5bzrXjkrY7olGzacF3rpHSZtMGrgmv/tId3la/Yjnz+sIH/5LD/wHr/uXD/vm7/vHH9rmrrj1X1oGB6tNiNtMyAs9SBttdspchzqs1emr+/YHXPd3jGanfBZHZyjpx+h6f5wpHBZHZKSktdmr/AOsn2AAABAHRSTlMAZVx8jpGZr6a5if//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////iHw/gbCdEjKUIQAAZf5JREFUeAHs0FWCRSEMA9Bh0qSF/e/3uvvns4O7/L2hnzT57xKQYJzJO4KabNsdq841H0nuwVnkYIFgNHRHNqE5u3CiHJE7UnCj6+9KXioAumdN+jq4o+5+4Q1eUPiKSHbFI+EhI9F/csN4ZrxzMqImtDwXE8SeKP6vnwTC0ixcBHu5UoVENBBsqVvf/2X2XK64mKITuxOc4TfnDJf1cmQNQRQbIu4NScKDIN4J8i/KOVRNNxDNVrujm6T61NI6+FA3u7phE+VrqAr/N83Re31T17vq+ZvBcDRWJtPZnC5c1/X8IPTcIMTLiCo8luNhbA+T4XCY3D/cXx6frIYsbHWVpEvlSvTXdwLrDG2F4YZFiGcplBruI80uUkkXRRLVRMW9MVG+CUKI3XV04xw9A2+6RFHznt7LVeVWEBu9PCmUFoEru7NisZVlf5d9m0sjr4ww9JVsH85rWWdkANaQ0BvuYShJwkE4rOn5x/q81Fa71XTyc0OdEplm6o71XYc2/6eu4ujOGF3tSPXVeDhcqU+jh2J5nHkemG1QWhR4gTw7nn80TuwUyMBs9HxJhgySYTxMVuogSa+dKfLyfHcAMnQGZhU0vBLEUJQLi9bnC8jGHJnx8g0yolqmY+g1YobGAGKEiZobjPctZMs0iW1K6EJ2i/HyeJy7s6k729PvcrN56HlBgKL9DK/33yCrguksLEdREO7W4yqtbAXRbrd0rWoib3GVabpGvnaYa2wI0QsZA2uu69o/IkuTAXmdTiYv1He9wMfPYY7o3HMxdm+nFkiarAZJHDOZPY8uZNaPS5Q2O85VZHbnwJEhKqUhyleisH2lF8gMmyP77uyTvunUefXwRte6Pfa4IwQ6024SGyepslRodzIrovnTbCqFs8DzgOO7ZPhOyETGTowfRvQrMlGsmEnQGEZRhNJgjtX523XKep/bsMacXCDratzy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\u003d","cta_log_url":"/async/ddllog?async\u003ddoodle:69128162,slot:21,type:1,cta:1","doodle_type":"ANIMATED","fingerprint":"9eefd996","gallery_url":"http://www.google.com/doodles/prayoon-yomyiams-85th-birthday?hl\u003den","header_layout":"LEGACY","id":69128162,"iframe_height_px":217,"iframe_width_px":434,"intent":"BUILTIN","large_cta_image":{"height":217,"image_id":4905660105883648,"image_name":"StaticCTA","is_animated_gif":false,"slot":21,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904.2-scta.png","width":434},"large_image":{"alternate_url":"https://lh3.googleusercontent.com/RMr0-ruAIq-5Z5He0s8p8tmIsP_GVONwIDEEiOWtzOeFLQ9xsM9JluujPSmpokDR_bDlo-oNzuyMZQmjb3p_gJEijB3QJmaEte_XJDW0nA","height":215,"image_id":5750085036015616,"image_name":"LargeAnimatedWhite","is_animated_gif":true,"slot":22,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904-law.gif","width":430},"log_url":"/async/ddllog?async\u003ddoodle:69128162,slot:22,type:1,cta:0","search_url":"/search?q\u003dPrayoon+Yomyiam\u0026oi\u003dddle\u0026ct\u003dprayoon-yomyiams-85th-birthday-4927560591867904-law\u0026hl\u003den","share_text":"Prayoon Yomyiam\u0027s 85th Birthday #GoogleDoodle\nhttps://g.co/doodle/2645d6","short_link":"//g.co/doodle/2645d6","show_now_header_search_affordance":false,"show_now_header_share_button":true,"small_image":{"alternate_url":"https://lh3.googleusercontent.com/igEwLwnO_9Va2HJmM2Wy5RhsM2IhFIW4VFj7GCDvk2Yhs3qm-gEh4Bk9gBKaU46OQ7-_I2tufgYS_VbXqX37v_vgOY3MobKegc5Vt90","height":44,"image_id":6192449487634432,"image_name":"Small","is_animated_gif":false,"slot":23,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904.2-s.png","width":120},"target_url":"/search?q\u003dPrayoon+Yomyiam\u0026oi\u003dddle\u0026ct\u003dprayoon-yomyiams-85th-birthday-4927560591867904-law\u0026hl\u003den\u0026source\u003ddoodle-ntp","time_to_live_ms":463124901000}} \ No newline at end of file +{"ddljson":{"alt_text":"Prayoon Yomyiam\u0027s 85th Birthday","cta_data_uri":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAbIAAADZCAMAAABy6UUDAAADAFBMVEVHcEyVmXmjmnp9jXpscYJpaYJhgH9Lln5UhHtCh3ZxdYKYm3mMm3uTmnqdm3l8mn2EknZscINpaYJsZoJViIFKmX9Mln9RmIFqn4J6pn9ZmH9bgYFraIJqa4Jfe4FjcIFSj4BPk39pa4NXeHxCh3ZYinVmbIJqjXZzj3Z8kXWDg3xpZ4Nydn6Ok3aflXWKinqXk3WkmXZwcIFpaoNIg3c+iHVNiXVhjHZec35QfnpmhHyrm3Z4d4BnZoJkd4Jufnt1gnx6iHpqbIJ/e3+Dmnxrcn5imH5ocYJrboNra4NjZYGIqn50mX23n3hsmH1wdoNuc4NuaYRtcYN+fot1cItybolvbYafo4Tv0sbXtq6KkJVxdYOMoIPuyLP/6dZ4co5pan5sbIVnanhgYG1YXWNPT1FKSkpISEhtYFiwlqHBuapWVlp7bmD/48VCREWShIajjKN8oolvkptxeYuSqY+7naiAqZOUvJ95gZWer6E6g3Kqya2Sn55mpZFKiX1goYxBjXu81LhuqJahwKZvdIeIs5t2rppYnIdflo5ZiYiqqo+xtZ1qh5E/lYVhiI2DeZc7oZJ1hp1yfpF8dJF/d5SstLd0jp6Lfp5olJV6hKKLtqyokK97jKSSgqWHfJuPgKHMqazlvqyYhqhwhpj/1rX/3b7/38P/2rr/4MZ+saPSs5j5zbD/4sn/7b3/5MyEiKqhhNerf/DFk/PftOFbUUrQsYWOdmVloMSxgf+2gv+ufv3yypf/2p7hv5W6hf//1pz805qylr2eiq//5dB7rc6CYcmYc9pqUbAmJmaje+eSeMS/ppQjJGI+NYCojoUrKWtUQ5h8nLv8zZT8xoz4v4jvrod4jaH4r4XTn4SEdK2/lH92b5bzrXjkrY7olGzacF3rpHSZtMGrgmv/tId3la/Yjnz+sIH/5LD/wHr/uXD/vm7/vHH9rmrrj1X1oGB6tNiNtMyAs9SBttdspchzqs1emr+/YHXPd3jGanfBZHZyjpx+h6f5wpHBZHZKSktdmr/AOsn2AAABAHRSTlMAZVx8jpGZr6a5if//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////iHw/gbCdEjKUIQAAZf5JREFUeAHs0FWCRSEMA9Bh0qSF/e/3uvvns4O7/L2hnzT57xKQYJzJO4KabNsdq841H0nuwVnkYIFgNHRHNqE5u3CiHJE7UnCj6+9KXioAumdN+jq4o+5+4Q1eUPiKSHbFI+EhI9F/csN4ZrxzMqImtDwXE8SeKP6vnwTC0ixcBHu5UoVENBBsqVvf/2X2XK64mKITuxOc4TfnDJf1cmQNQRQbIu4NScKDIN4J8i/KOVRNNxDNVrujm6T61NI6+FA3u7phE+VrqAr/N83Re31T17vq+ZvBcDRWJtPZnC5c1/X8IPTcIMTLiCo8luNhbA+T4XCY3D/cXx6frIYsbHWVpEvlSvTXdwLrDG2F4YZFiGcplBruI80uUkkXRRLVRMW9MVG+CUKI3XV04xw9A2+6RFHznt7LVeVWEBu9PCmUFoEru7NisZVlf5d9m0sjr4ww9JVsH85rWWdkANaQ0BvuYShJwkE4rOn5x/q81Fa71XTyc0OdEplm6o71XYc2/6eu4ujOGF3tSPXVeDhcqU+jh2J5nHkemG1QWhR4gTw7nn80TuwUyMBs9HxJhgySYTxMVuogSa+dKfLyfHcAMnQGZhU0vBLEUJQLi9bnC8jGHJnx8g0yolqmY+g1YobGAGKEiZobjPctZMs0iW1K6EJ2i/HyeJy7s6k729PvcrN56HlBgKL9DK/33yCrguksLEdREO7W4yqtbAXRbrd0rWoib3GVabpGvnaYa2wI0QsZA2uu69o/IkuTAXmdTiYv1He9wMfPYY7o3HMxdm+nFkiarAZJHDOZPY8uZNaPS5Q2O85VZHbnwJEhKqUhyleisH2lF8gMmyP77uyTvunUefXwRte6Pfa4IwQ6024SGyepslRodzIrovnTbCqFs8DzgOO7ZPhOyETGTowfRvQrMlGsmEnQGEZRhNJgjtX523XKep/bsMacXCDratzy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\u003d","cta_log_url":"/async/ddllog?async\u003ddoodle:69128162,slot:21,type:1,cta:1","doodle_type":"ANIMATED","fingerprint":"9eefd996","gallery_url":"http://www.google.com/doodles/prayoon-yomyiams-85th-birthday?hl\u003den","header_layout":"LEGACY","id":69128162,"iframe_height_px":217,"iframe_width_px":434,"intent":"BUILTIN","large_cta_image":{"height":217,"image_id":4905660105883648,"image_name":"StaticCTA","is_animated_gif":false,"slot":21,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904.2-scta.png","width":434},"large_image":{"alternate_url":"https://lh3.googleusercontent.com/RMr0-ruAIq-5Z5He0s8p8tmIsP_GVONwIDEEiOWtzOeFLQ9xsM9JluujPSmpokDR_bDlo-oNzuyMZQmjb3p_gJEijB3QJmaEte_XJDW0nA","height":215,"image_id":5750085036015616,"image_name":"LargeAnimatedWhite","is_animated_gif":true,"slot":22,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904-law.gif","width":430},"log_url":"/async/ddllog?async\u003ddoodle:69128162,slot:22,type:1,cta:0","search_url":"/search?q\u003dPrayoon+Yomyiam\u0026oi\u003dddle\u0026ct\u003dprayoon-yomyiams-85th-birthday-4927560591867904-law\u0026hl\u003den","share_button":{"background_color":"#ffcccc","icon_image":"iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAoklEQVR42mNgYDjDQAZ2AOKZQPwAimdCxZDUkGcokHHmPxo+g2o46QbPxGIoDM+kxOAHeAx+MOgMdgbiy6QGBb6YlgHilUgG3CI28nDF9FmoBZ+hfBBdAcQuxCY3fDENw6uBWI74YCMcIZ+A2JX0SCYppkk3GF9QXKbExbgi7xalYYwruYFiv5ySVEEIk52OaZrzhmchRNPymGY1CM3qPKIwANWIJ6PVf7EqAAAAAElFTkSuQmCC","offset_x":396,"offset_y":12,"opacity":0.8},"share_text":"Prayoon Yomyiam\u0027s 85th Birthday #GoogleDoodle\nhttps://g.co/doodle/2645d6","short_link":"//g.co/doodle/2645d6","show_now_header_search_affordance":false,"show_now_header_share_button":true,"small_image":{"alternate_url":"https://lh3.googleusercontent.com/igEwLwnO_9Va2HJmM2Wy5RhsM2IhFIW4VFj7GCDvk2Yhs3qm-gEh4Bk9gBKaU46OQ7-_I2tufgYS_VbXqX37v_vgOY3MobKegc5Vt90","height":44,"image_id":6192449487634432,"image_name":"Small","is_animated_gif":false,"slot":23,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904.2-s.png","width":120},"target_url":"/search?q\u003dPrayoon+Yomyiam\u0026oi\u003dddle\u0026ct\u003dprayoon-yomyiams-85th-birthday-4927560591867904-law\u0026hl\u003den\u0026source\u003ddoodle-ntp","time_to_live_ms":453109439000}} \ No newline at end of file
diff --git a/components/test/data/search_provider_logos/ddljson_desktop1_fp.json b/components/test/data/search_provider_logos/ddljson_desktop1_fp.json index 5805e03..9cf0bb9 100644 --- a/components/test/data/search_provider_logos/ddljson_desktop1_fp.json +++ b/components/test/data/search_provider_logos/ddljson_desktop1_fp.json
@@ -1,2 +1,2 @@ )]}' -{"ddljson":{"alt_text":"Prayoon Yomyiam\u0027s 85th Birthday","cta_log_url":"/async/ddllog?async\u003ddoodle:69128162,slot:21,type:1,cta:1","doodle_type":"ANIMATED","fingerprint":"9eefd996","gallery_url":"http://www.google.com/doodles/prayoon-yomyiams-85th-birthday?hl\u003den","header_layout":"LEGACY","id":69128162,"iframe_height_px":217,"iframe_width_px":434,"intent":"BUILTIN","large_cta_image":{"height":217,"image_id":4905660105883648,"image_name":"StaticCTA","is_animated_gif":false,"slot":21,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904.2-scta.png","width":434},"large_image":{"alternate_url":"https://lh3.googleusercontent.com/RMr0-ruAIq-5Z5He0s8p8tmIsP_GVONwIDEEiOWtzOeFLQ9xsM9JluujPSmpokDR_bDlo-oNzuyMZQmjb3p_gJEijB3QJmaEte_XJDW0nA","height":215,"image_id":5750085036015616,"image_name":"LargeAnimatedWhite","is_animated_gif":true,"slot":22,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904-law.gif","width":430},"log_url":"/async/ddllog?async\u003ddoodle:69128162,slot:22,type:1,cta:0","search_url":"/search?q\u003dPrayoon+Yomyiam\u0026oi\u003dddle\u0026ct\u003dprayoon-yomyiams-85th-birthday-4927560591867904-law\u0026hl\u003den","share_text":"Prayoon Yomyiam\u0027s 85th Birthday #GoogleDoodle\nhttps://g.co/doodle/2645d6","short_link":"//g.co/doodle/2645d6","show_now_header_search_affordance":false,"show_now_header_share_button":true,"small_image":{"alternate_url":"https://lh3.googleusercontent.com/igEwLwnO_9Va2HJmM2Wy5RhsM2IhFIW4VFj7GCDvk2Yhs3qm-gEh4Bk9gBKaU46OQ7-_I2tufgYS_VbXqX37v_vgOY3MobKegc5Vt90","height":44,"image_id":6192449487634432,"image_name":"Small","is_animated_gif":false,"slot":23,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904.2-s.png","width":120},"target_url":"/search?q\u003dPrayoon+Yomyiam\u0026oi\u003dddle\u0026ct\u003dprayoon-yomyiams-85th-birthday-4927560591867904-law\u0026hl\u003den\u0026source\u003ddoodle-ntp","time_to_live_ms":463124901000}} \ No newline at end of file +{"ddljson":{"alt_text":"Prayoon Yomyiam\u0027s 85th Birthday","cta_log_url":"/async/ddllog?async\u003ddoodle:69128162,slot:21,type:1,cta:1","doodle_type":"ANIMATED","fingerprint":"9eefd996","gallery_url":"http://www.google.com/doodles/prayoon-yomyiams-85th-birthday?hl\u003den","header_layout":"LEGACY","id":69128162,"iframe_height_px":217,"iframe_width_px":434,"intent":"BUILTIN","large_cta_image":{"height":217,"image_id":4905660105883648,"image_name":"StaticCTA","is_animated_gif":false,"slot":21,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904.2-scta.png","width":434},"large_image":{"alternate_url":"https://lh3.googleusercontent.com/RMr0-ruAIq-5Z5He0s8p8tmIsP_GVONwIDEEiOWtzOeFLQ9xsM9JluujPSmpokDR_bDlo-oNzuyMZQmjb3p_gJEijB3QJmaEte_XJDW0nA","height":215,"image_id":5750085036015616,"image_name":"LargeAnimatedWhite","is_animated_gif":true,"slot":22,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904-law.gif","width":430},"log_url":"/async/ddllog?async\u003ddoodle:69128162,slot:22,type:1,cta:0","search_url":"/search?q\u003dPrayoon+Yomyiam\u0026oi\u003dddle\u0026ct\u003dprayoon-yomyiams-85th-birthday-4927560591867904-law\u0026hl\u003den","share_button":{"background_color":"#ffcccc","icon_image":"iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAoklEQVR42mNgYDjDQAZ2AOKZQPwAimdCxZDUkGcokHHmPxo+g2o46QbPxGIoDM+kxOAHeAx+MOgMdgbiy6QGBb6YlgHilUgG3CI28nDF9FmoBZ+hfBBdAcQuxCY3fDENw6uBWI74YCMcIZ+A2JX0SCYppkk3GF9QXKbExbgi7xalYYwruYFiv5ySVEEIk52OaZrzhmchRNPymGY1CM3qPKIwANWIJ6PVf7EqAAAAAElFTkSuQmCC","offset_x":396,"offset_y":12,"opacity":0.8},"share_text":"Prayoon Yomyiam\u0027s 85th Birthday #GoogleDoodle\nhttps://g.co/doodle/2645d6","short_link":"//g.co/doodle/2645d6","show_now_header_search_affordance":false,"show_now_header_share_button":true,"small_image":{"alternate_url":"https://lh3.googleusercontent.com/igEwLwnO_9Va2HJmM2Wy5RhsM2IhFIW4VFj7GCDvk2Yhs3qm-gEh4Bk9gBKaU46OQ7-_I2tufgYS_VbXqX37v_vgOY3MobKegc5Vt90","height":44,"image_id":6192449487634432,"image_name":"Small","is_animated_gif":false,"slot":23,"url":"/logos/doodles/2018/prayoon-yomyiams-85th-birthday-4927560591867904.2-s.png","width":120},"target_url":"/search?q\u003dPrayoon+Yomyiam\u0026oi\u003dddle\u0026ct\u003dprayoon-yomyiams-85th-birthday-4927560591867904-law\u0026hl\u003den\u0026source\u003ddoodle-ntp","time_to_live_ms":453109437000}} \ No newline at end of file
diff --git a/content/browser/blob_storage/blob_transport_host_unittest.cc b/content/browser/blob_storage/blob_transport_host_unittest.cc index 4649f1a..9dcaaf9 100644 --- a/content/browser/blob_storage/blob_transport_host_unittest.cc +++ b/content/browser/blob_storage/blob_transport_host_unittest.cc
@@ -483,7 +483,8 @@ PopulateBytes(response1.allocate_mutable_data(2), 2); std::vector<storage::BlobItemBytesResponse> responses = {response1}; host_.OnMemoryResponses(kBlob3, responses, &context_); - EXPECT_EQ(storage::BlobStatus::PENDING_INTERNALS, handle3->GetBlobStatus()); + EXPECT_EQ(storage::BlobStatus::PENDING_REFERENCED_BLOBS, + handle3->GetBlobStatus()); EXPECT_FALSE(request_called_); EXPECT_FALSE(host_.IsBeingBuilt(kBlob3)); EXPECT_TRUE(IsBeingBuiltInContext(kBlob3));
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc index d7afe5b..771083c3 100644 --- a/content/browser/bluetooth/web_bluetooth_service_impl.cc +++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -238,7 +238,7 @@ const base::Optional<std::string>& advertisement_name, base::Optional<int8_t> rssi, base::Optional<int8_t> tx_power, - uint16_t appearance, + base::Optional<uint16_t> appearance, const device::BluetoothDevice::UUIDList& advertised_uuids, const device::BluetoothDevice::ServiceDataMap& service_data_map, const device::BluetoothDevice::ManufacturerDataMap& manufacturer_data_map) { @@ -265,13 +265,20 @@ result->device = std::move(device); result->name = advertisement_name; - result->appearance = appearance; - // TODO(dougt) rssi and tx_power should be sent as optional. + // Note about the default value for these optional types. On the other side + // of this IPC, the receiver will be checking to see if |*_is_set| is true + // before using the value. Here we chose reasonable defaults in case the + // other side does something incorrect. We have to do this manual + // serialization because mojo does not support optional primitive types. + result->appearance_is_set = appearance.has_value(); + result->appearance = appearance.value_or(/*not present=*/0xffc0); - // 128 (0x80) means invalid value - result->rssi = rssi.value_or(128); - result->tx_power = tx_power.value_or(128); + result->rssi_is_set = rssi.has_value(); + result->rssi = rssi.value_or(/*invalid value=*/128); + + result->tx_power_is_set = tx_power.has_value(); + result->tx_power = tx_power.value_or(/*invalid value=*/128); std::vector<device::BluetoothUUID> uuids; for (auto& uuid : advertised_uuids) @@ -354,9 +361,9 @@ if (!GetAdapter()) { if (BluetoothAdapterFactoryWrapper::Get().IsLowEnergySupported()) { BluetoothAdapterFactoryWrapper::Get().AcquireAdapter( - this, base::Bind(&WebBluetoothServiceImpl::RequestDeviceImpl, - weak_ptr_factory_.GetWeakPtr(), - base::Passed(&options), base::Passed(&callback))); + this, base::BindOnce(&WebBluetoothServiceImpl::RequestDeviceImpl, + weak_ptr_factory_.GetWeakPtr(), + std::move(options), std::move(callback))); return; } RecordRequestDeviceOutcome(
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.h b/content/browser/bluetooth/web_bluetooth_service_impl.h index dbcdc84..3b88c4e 100644 --- a/content/browser/bluetooth/web_bluetooth_service_impl.h +++ b/content/browser/bluetooth/web_bluetooth_service_impl.h
@@ -91,7 +91,7 @@ const base::Optional<std::string>& advertisement_name, base::Optional<int8_t> rssi, base::Optional<int8_t> tx_power, - uint16_t appearance, + base::Optional<uint16_t> appearance, const device::BluetoothDevice::UUIDList& advertised_uuids, const device::BluetoothDevice::ServiceDataMap& service_data_map, const device::BluetoothDevice::ManufacturerDataMap& manufacturer_data_map)
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc index ce23646..007e3b7 100644 --- a/content/browser/browser_child_process_host_impl.cc +++ b/content/browser/browser_child_process_host_impl.cc
@@ -162,7 +162,7 @@ weak_factory_(this) { data_.id = ChildProcessHostImpl::GenerateChildProcessUniqueId(); - child_process_host_.reset(ChildProcessHost::Create(this)); + child_process_host_ = ChildProcessHost::Create(this); AddFilter(new TraceMessageFilter(data_.id)); g_child_process_list.Get().push_back(this);
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc index a7cb566..2571e620 100644 --- a/content/browser/frame_host/navigation_controller_impl.cc +++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -276,7 +276,7 @@ data.first_committed_url = replaced_entry.GetURL(); data.first_timestamp = replaced_entry.GetTimestamp(); data.first_transition_type = replaced_entry.GetTransitionType(); - output_entry->SetReplacedEntryData(data); + output_entry->set_replaced_entry_data(data); } FrameMsg_Navigate_Type::Value GetNavigationType(
diff --git a/content/browser/frame_host/navigation_entry_impl.h b/content/browser/frame_host/navigation_entry_impl.h index ada1a1c..c152c7c 100644 --- a/content/browser/frame_host/navigation_entry_impl.h +++ b/content/browser/frame_host/navigation_entry_impl.h
@@ -406,7 +406,7 @@ // Stores a record of the what was committed in this NavigationEntry's main // frame before it was replaced (e.g. by history.replaceState()). - void SetReplacedEntryData(const ReplacedNavigationEntryData& data) { + void set_replaced_entry_data(const ReplacedNavigationEntryData& data) { replaced_entry_data_ = data; }
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc index b6284c98..078c6b0 100644 --- a/content/browser/renderer_host/render_widget_host_input_event_router.cc +++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -500,8 +500,10 @@ if (use_viz_hit_test_) { viz::HitTestQuery* query = GetHitTestQuery(GetHostFrameSinkManager(), root_view->GetRootFrameSinkId()); - if (!query) - return {root_view, false, base::nullopt, false, false}; + if (!query) { + *transformed_point = point; + return {root_view, false, *transformed_point, false, false}; + } // |point_in_screen| is in the coordinate space of of the screen, but the // display HitTestQuery does a hit test in the coordinate space of the root // window. The following translation should account for that discrepancy.
diff --git a/content/browser/serial/serial_browsertest.cc b/content/browser/serial/serial_browsertest.cc index 4e4bfac..91b05d11 100644 --- a/content/browser/serial/serial_browsertest.cc +++ b/content/browser/serial/serial_browsertest.cc
@@ -3,16 +3,43 @@ // found in the LICENSE file. #include <string> +#include <utility> #include "base/command_line.h" +#include "base/unguessable_token.h" +#include "content/public/browser/web_contents_delegate.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" #include "content/shell/browser/shell.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using testing::ByMove; +using testing::Return; + namespace content { +namespace { + +class MockWebContentsDelegate : public WebContentsDelegate { + public: + MockWebContentsDelegate() {} + ~MockWebContentsDelegate() override {} + + std::unique_ptr<SerialChooser> RunSerialChooser( + content::RenderFrameHost* frame, + std::vector<blink::mojom::SerialPortFilterPtr> filters, + content::SerialChooser::Callback callback) override { + std::move(callback).Run(RunSerialChooserInternal()); + return nullptr; + } + + MOCK_METHOD0(RunSerialChooserInternal, blink::mojom::SerialPortInfoPtr()); +}; + +} // namespace + class SerialTest : public ContentBrowserTest { void SetUpCommandLine(base::CommandLine* command_line) override { ContentBrowserTest::SetUpCommandLine(command_line); @@ -37,6 +64,14 @@ IN_PROC_BROWSER_TEST_F(SerialTest, RequestPort) { NavigateToURL(shell(), GetTestUrl(nullptr, "simple_page.html")); + MockWebContentsDelegate delegate; + shell()->web_contents()->SetDelegate(&delegate); + + auto port = blink::mojom::SerialPortInfo::New(); + port->token = base::UnguessableToken::Create(); + EXPECT_CALL(delegate, RunSerialChooserInternal) + .WillOnce(Return(ByMove(std::move(port)))); + bool result; EXPECT_TRUE( ExecuteScriptAndExtractBool(shell(), @@ -47,7 +82,7 @@ " domAutomationController.send(false);" " });", &result)); - EXPECT_FALSE(result); + EXPECT_TRUE(result); } } // namespace content
diff --git a/content/browser/serial/serial_service.cc b/content/browser/serial/serial_service.cc index 514e0e1..0029965 100644 --- a/content/browser/serial/serial_service.cc +++ b/content/browser/serial/serial_service.cc
@@ -4,9 +4,14 @@ #include "content/browser/serial/serial_service.h" +#include "content/public/browser/serial_chooser.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_delegate.h" + namespace content { -SerialService::SerialService(RenderFrameHost* render_frame_host) {} +SerialService::SerialService(RenderFrameHost* render_frame_host) + : render_frame_host_(render_frame_host) {} SerialService::~SerialService() = default; @@ -21,7 +26,15 @@ void SerialService::RequestPort( std::vector<blink::mojom::SerialPortFilterPtr> filters, RequestPortCallback callback) { - std::move(callback).Run(nullptr); + WebContentsDelegate* delegate = + WebContents::FromRenderFrameHost(render_frame_host_)->GetDelegate(); + if (!delegate) { + std::move(callback).Run(nullptr); + return; + } + + chooser_ = delegate->RunSerialChooser(render_frame_host_, std::move(filters), + std::move(callback)); } } // namespace content
diff --git a/content/browser/serial/serial_service.h b/content/browser/serial/serial_service.h index 00d40b1..959df98 100644 --- a/content/browser/serial/serial_service.h +++ b/content/browser/serial/serial_service.h
@@ -14,6 +14,7 @@ namespace content { class RenderFrameHost; +class SerialChooser; class SerialService : public blink::mojom::SerialService { public: @@ -28,8 +29,12 @@ RequestPortCallback callback) override; private: + RenderFrameHost* const render_frame_host_; mojo::BindingSet<blink::mojom::SerialService> bindings_; + // The last shown serial port chooser UI. + std::unique_ptr<SerialChooser> chooser_; + DISALLOW_COPY_AND_ASSIGN(SerialService); };
diff --git a/content/browser/service_worker/service_worker_navigation_loader.cc b/content/browser/service_worker/service_worker_navigation_loader.cc index 63bf477..25c2b6d 100644 --- a/content/browser/service_worker/service_worker_navigation_loader.cc +++ b/content/browser/service_worker/service_worker_navigation_loader.cc
@@ -7,8 +7,10 @@ #include <sstream> #include <utility> +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/optional.h" +#include "base/strings/strcat.h" #include "base/trace_event/trace_event.h" #include "content/browser/service_worker/service_worker_provider_host.h" #include "content/browser/service_worker/service_worker_version.h" @@ -409,6 +411,7 @@ response_head_.load_timing.receive_headers_start = base::TimeTicks::Now(); response_head_.load_timing.receive_headers_end = response_head_.load_timing.receive_headers_start; + response_source_ = response->response_source; // Make the navigated page inherit the SSLInfo from its controller service // worker's script. This affects the HTTPS padlock, etc, shown by the @@ -599,6 +602,13 @@ "ServiceWorker.LoadTiming.MainFrame.MainResource." "ResponseReceivedToCompleted2", completion_time_ - response_head_.load_timing.receive_headers_end); + // Same as above, breakdown by response source. + base::UmaHistogramMediumTimes( + base::StrCat({"ServiceWorker.LoadTiming.MainFrame.MainResource." + "ResponseReceivedToCompleted2", + ServiceWorkerUtils::FetchResponseSourceToSuffix( + response_source_)}), + completion_time_ - response_head_.load_timing.receive_headers_end); } else { // Renderer -> Browser IPC delay (network fallback case). UMA_HISTOGRAM_TIMES(
diff --git a/content/browser/service_worker/service_worker_navigation_loader.h b/content/browser/service_worker/service_worker_navigation_loader.h index 9f8ad0b..88b1a1b 100644 --- a/content/browser/service_worker/service_worker_navigation_loader.h +++ b/content/browser/service_worker/service_worker_navigation_loader.h
@@ -200,6 +200,8 @@ bool devtools_attached_ = false; blink::mojom::ServiceWorkerFetchEventTimingPtr fetch_event_timing_; base::TimeTicks completion_time_; + network::mojom::FetchResponseSource response_source_ = + network::mojom::FetchResponseSource::kUnspecified; // Pointer to the URLLoaderClient (i.e. NavigationURLLoader). network::mojom::URLLoaderClientPtr url_loader_client_;
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc index 839da09..69aca76 100644 --- a/content/browser/site_per_process_hit_test_browsertest.cc +++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -16,10 +16,12 @@ #include "base/test/test_timeouts.h" #include "build/build_config.h" #include "components/viz/common/features.h" +#include "components/viz/test/host_frame_sink_manager_test_api.h" #include "content/browser/compositor/surface_utils.h" #include "content/browser/renderer_host/cursor_manager.h" #include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h" #include "content/browser/renderer_host/input/synthetic_tap_gesture.h" +#include "content/browser/renderer_host/input/synthetic_touchpad_pinch_gesture.h" #include "content/browser/renderer_host/input/touch_emulator.h" #include "content/browser/renderer_host/render_widget_host_input_event_router.h" #include "content/browser/renderer_host/render_widget_host_view_child_frame.h" @@ -4311,6 +4313,58 @@ #endif // defined(USE_AURA) +// Test that we can still perform a touchpad pinch gesture in the absence of viz +// hit test data without crashing. +IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, + TouchpadPinchWhenMissingHitTestDataDoesNotCrash) { + if (!features::IsVizHitTestingEnabled()) + return; + + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/frame_tree/page_with_positioned_frame.html")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + + WebContentsImpl* contents = web_contents(); + FrameTreeNode* root = contents->GetFrameTree()->root(); + ASSERT_EQ(1U, root->child_count()); + + // Even though we're sending the events to the root, we need an OOPIF so + // that hit testing doesn't short circuit. + EXPECT_EQ( + " Site A ------------ proxies for B\n" + " +--Site B ------- proxies for A\n" + "Where A = http://a.com/\n" + " B = http://baz.com/", + DepictFrameTree(root)); + + // Clobber the real hit test data once it comes in. + WaitForHitTestDataOrChildSurfaceReady(root->current_frame_host()); + ASSERT_TRUE(GetHostFrameSinkManager()); + viz::HostFrameSinkManager::DisplayHitTestQueryMap empty_hit_test_map; + viz::HostFrameSinkManagerTestApi(GetHostFrameSinkManager()) + .SetDisplayHitTestQuery(std::move(empty_hit_test_map)); + + const gfx::PointF point_in_root(1, 1); + SyntheticPinchGestureParams params; + params.gesture_source_type = SyntheticGestureParams::TOUCHPAD_INPUT; + params.scale_factor = 1.2f; + params.anchor = point_in_root; + + auto pinch_gesture = std::make_unique<SyntheticTouchpadPinchGesture>(params); + RenderWidgetHostImpl* render_widget_host = + root->current_frame_host()->GetRenderWidgetHost(); + + base::RunLoop run_loop; + render_widget_host->QueueSyntheticGesture( + std::move(pinch_gesture), + base::BindOnce( + [](base::OnceClosure quit_closure, SyntheticGesture::Result result) { + std::move(quit_closure).Run(); + }, + run_loop.QuitClosure())); + run_loop.Run(); +} + // Tests that performing a touchpad double-tap zoom over an OOPIF offers the // synthetic wheel event to the child. IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc index bc78aba..27b8c5a 100644 --- a/content/common/child_process_host_impl.cc +++ b/content/common/child_process_host_impl.cc
@@ -46,8 +46,9 @@ namespace content { // static -ChildProcessHost* ChildProcessHost::Create(ChildProcessHostDelegate* delegate) { - return new ChildProcessHostImpl(delegate); +std::unique_ptr<ChildProcessHost> ChildProcessHost::Create( + ChildProcessHostDelegate* delegate) { + return base::WrapUnique(new ChildProcessHostImpl(delegate)); } // static
diff --git a/content/common/service_worker/service_worker_utils.cc b/content/common/service_worker/service_worker_utils.cc index c3dcafaa8..4998ceb 100644 --- a/content/common/service_worker/service_worker_utils.cc +++ b/content/common/service_worker/service_worker_utils.cc
@@ -280,6 +280,24 @@ return request_ptr; } +// static +const char* ServiceWorkerUtils::FetchResponseSourceToSuffix( + network::mojom::FetchResponseSource source) { + // Don't change these returned strings. They are used for recording UMAs. + switch (source) { + case network::mojom::FetchResponseSource::kUnspecified: + return ".Unspecified"; + case network::mojom::FetchResponseSource::kNetwork: + return ".Network"; + case network::mojom::FetchResponseSource::kHttpCache: + return ".HttpCache"; + case network::mojom::FetchResponseSource::kCacheStorage: + return ".CacheStorage"; + } + NOTREACHED(); + return ".Unknown"; +} + bool LongestScopeMatcher::MatchLongest(const GURL& scope) { if (!ServiceWorkerUtils::ScopeMatches(scope, url_)) return false;
diff --git a/content/common/service_worker/service_worker_utils.h b/content/common/service_worker/service_worker_utils.h index a2c972f..97bf728 100644 --- a/content/common/service_worker/service_worker_utils.h +++ b/content/common/service_worker/service_worker_utils.h
@@ -88,6 +88,9 @@ CONTENT_EXPORT static blink::mojom::FetchAPIRequestPtr DeserializeFetchRequestFromString(const std::string& serialized); + CONTENT_EXPORT static const char* FetchResponseSourceToSuffix( + network::mojom::FetchResponseSource source); + private: static bool IsPathRestrictionSatisfiedInternal( const GURL& scope,
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json index 4a8b557..e95f05f4 100644 --- a/content/public/app/mojo/content_browser_manifest.json +++ b/content/public/app/mojo/content_browser_manifest.json
@@ -11,7 +11,6 @@ "provides": { // Interfaces needed by a generic client of content browser. "app": [ - "content.mojom.MemoryCoordinatorHandle", "discardable_memory.mojom.DiscardableSharedMemoryManager", "memory_instrumentation.mojom.Coordinator" ], @@ -69,7 +68,6 @@ "media.mojom.InterfaceFactory", "media.mojom.VideoCaptureHost", "media.mojom.VideoDecodePerfHistory", - "memory_coordinator.mojom.MemoryCoordinatorHandle", "metrics.mojom.SingleSampleMetricsProvider", "midi.mojom.MidiSessionProvider", "network.mojom.P2PSocketManager",
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn index 5485aa4c..1e52ddaf 100644 --- a/content/public/browser/BUILD.gn +++ b/content/public/browser/BUILD.gn
@@ -254,6 +254,8 @@ "security_style_explanation.h", "security_style_explanations.cc", "security_style_explanations.h", + "serial_chooser.cc", + "serial_chooser.h", "service_worker_context.h", "service_worker_context_observer.h", "session_storage_namespace.h",
diff --git a/content/public/browser/serial_chooser.cc b/content/public/browser/serial_chooser.cc new file mode 100644 index 0000000..e379b32 --- /dev/null +++ b/content/public/browser/serial_chooser.cc
@@ -0,0 +1,13 @@ +// Copyright 2019 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 "content/public/browser/serial_chooser.h" + +namespace content { + +SerialChooser::SerialChooser() = default; + +SerialChooser::~SerialChooser() = default; + +} // namespace content
diff --git a/content/public/browser/serial_chooser.h b/content/public/browser/serial_chooser.h new file mode 100644 index 0000000..4d4401d --- /dev/null +++ b/content/public/browser/serial_chooser.h
@@ -0,0 +1,32 @@ +// Copyright 2019 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 CONTENT_PUBLIC_BROWSER_SERIAL_CHOOSER_H_ +#define CONTENT_PUBLIC_BROWSER_SERIAL_CHOOSER_H_ + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "content/common/content_export.h" +#include "third_party/blink/public/mojom/serial/serial.mojom.h" + +namespace content { + +// Token representing an open serial port chooser prompt. Destroying this +// object should cancel the prompt. +class CONTENT_EXPORT SerialChooser { + public: + // Callback type used to report the user action. Passed |nullptr| if no port + // was selected. + using Callback = base::OnceCallback<void(blink::mojom::SerialPortInfoPtr)>; + + SerialChooser(); + virtual ~SerialChooser(); + + private: + DISALLOW_COPY_AND_ASSIGN(SerialChooser); +}; + +} // namespace content + +#endif // CONTENT_PUBLIC_BROWSER_SERIAL_CHOOSER_H_
diff --git a/content/public/browser/site_instance.h b/content/public/browser/site_instance.h index 8142b94..6a5a242 100644 --- a/content/public/browser/site_instance.h +++ b/content/public/browser/site_instance.h
@@ -100,14 +100,20 @@ // SiteInstances) belongs. virtual content::BrowserContext* GetBrowserContext() const = 0; - // Get the web site that this SiteInstance is rendering pages for. This + // Get the web site that this SiteInstance is rendering pages for. This // includes the scheme and registered domain, but not the port. // - // NOTE: In most cases, this value should not be considered authoritative - // because a SiteInstance can usually host pages from multiple sites. It is - // only an accurate representation of the pages within the SiteInstance in - // the "site per process" process model, or for sites that require process - // isolation (e.g., WebUI, extensions). + // NOTE: In most cases, code should be performing checks against the origin + // returned by |RenderFrameHost::GetLastCommittedOrigin()|. In contrast, the + // GURL returned by |GetSiteURL()| should not be considered authoritative + // because: + // - a SiteInstance can host pages from multiple sites if "site per process" + // is not enabled and the SiteInstance isn't hosting pages that require + // process isolation (e.g. WebUI or extensions) + // - even with site per process, the site URL is not an origin: while often + // derived from the origin, it only contains the scheme and the eTLD + 1, + // i.e. an origin with the host "deeply.nested.subdomain.example.com" + // corresponds to a site URL with the host "example.com". virtual const GURL& GetSiteURL() const = 0; // Gets a SiteInstance for the given URL that shares the current
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc index 384f9bd..8981e93d 100644 --- a/content/public/browser/web_contents_delegate.cc +++ b/content/public/browser/web_contents_delegate.cc
@@ -143,6 +143,14 @@ return nullptr; } +std::unique_ptr<SerialChooser> WebContentsDelegate::RunSerialChooser( + RenderFrameHost* frame, + std::vector<blink::mojom::SerialPortFilterPtr> filters, + SerialChooser::Callback callback) { + std::move(callback).Run(nullptr); + return nullptr; +} + bool WebContentsDelegate::EmbedsFullscreenWidget() const { return false; }
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h index 46a6c09f..2c162df 100644 --- a/content/public/browser/web_contents_delegate.h +++ b/content/public/browser/web_contents_delegate.h
@@ -18,6 +18,7 @@ #include "content/common/content_export.h" #include "content/public/browser/bluetooth_chooser.h" #include "content/public/browser/invalidate_type.h" +#include "content/public/browser/serial_chooser.h" #include "content/public/browser/web_contents.h" #include "content/public/common/media_stream_request.h" #include "content/public/common/previews_state.h" @@ -393,6 +394,14 @@ RenderFrameHost* frame, const BluetoothChooser::EventHandler& event_handler); + // Shows a chooser for the user to select a serial port. |callback| will be + // run when the prompt is closed. Deleting the returned object will cancel the + // prompt. + virtual std::unique_ptr<SerialChooser> RunSerialChooser( + RenderFrameHost* frame, + std::vector<blink::mojom::SerialPortFilterPtr> filters, + SerialChooser::Callback callback); + // Returns true if the delegate will embed a WebContents-owned fullscreen // render widget. In this case, the delegate may access the widget by calling // WebContents::GetFullscreenRenderWidgetHostView(). If false is returned,
diff --git a/content/public/common/child_process_host.h b/content/public/common/child_process_host.h index 926137f..52ecb0af 100644 --- a/content/public/common/child_process_host.h +++ b/content/public/common/child_process_host.h
@@ -6,6 +6,7 @@ #define CONTENT_PUBLIC_COMMON_CHILD_PROCESS_HOST_H_ #include <stdint.h> +#include <memory> #include "base/files/scoped_file.h" #include "build/build_config.h" @@ -37,7 +38,8 @@ enum : int { kInvalidUniqueID = -1 }; // Used to create a child process host. The delegate must outlive this object. - static ChildProcessHost* Create(ChildProcessHostDelegate* delegate); + static std::unique_ptr<ChildProcessHost> Create( + ChildProcessHostDelegate* delegate); // These flags may be passed to GetChildPath in order to alter its behavior, // causing it to return a child path more suited to a specific task.
diff --git a/content/public/test/url_loader_interceptor.cc b/content/public/test/url_loader_interceptor.cc index 12ee936..5e14fd3 100644 --- a/content/public/test/url_loader_interceptor.cc +++ b/content/public/test/url_loader_interceptor.cc
@@ -364,6 +364,9 @@ URLLoaderInterceptor::RequestParams& URLLoaderInterceptor::RequestParams:: operator=(RequestParams&& other) = default; +URLLoaderInterceptor::URLLoaderInterceptor(const InterceptCallback& callback) + : URLLoaderInterceptor(callback, {}) {} + URLLoaderInterceptor::URLLoaderInterceptor(const InterceptCallback& callback, base::OnceClosure ready_callback) : callback_(callback), io_thread_(base::MakeRefCounted<IOState>(this)) {
diff --git a/content/public/test/url_loader_interceptor.h b/content/public/test/url_loader_interceptor.h index 3fb90ca..9ffdec9 100644 --- a/content/public/test/url_loader_interceptor.h +++ b/content/public/test/url_loader_interceptor.h
@@ -82,8 +82,9 @@ // provided, a nested RunLoop is used to ensure the interceptor is ready // before returning. If |ready_callback| is provided, no RunLoop is called, // and instead |ready_callback| is called after the interceptor is installed. + explicit URLLoaderInterceptor(const InterceptCallback& callback); URLLoaderInterceptor(const InterceptCallback& callback, - base::OnceClosure ready_callback = {}); + base::OnceClosure ready_callback); ~URLLoaderInterceptor(); // Helper methods for use when intercepting.
diff --git a/content/renderer/child_frame_compositing_helper.cc b/content/renderer/child_frame_compositing_helper.cc index 0f44083..6bd5177 100644 --- a/content/renderer/child_frame_compositing_helper.cc +++ b/content/renderer/child_frame_compositing_helper.cc
@@ -7,8 +7,7 @@ #include <utility> #include "build/build_config.h" -#include "cc/layers/picture_image_layer.h" -#include "cc/layers/solid_color_layer.h" +#include "cc/layers/picture_layer.h" #include "cc/layers/surface_layer.h" #include "cc/paint/paint_image.h" #include "cc/paint/paint_image_builder.h" @@ -34,40 +33,18 @@ const gfx::Size& frame_size_in_dip, float device_scale_factor) { surface_id_ = viz::SurfaceId(); + crashed_ = true; + device_scale_factor_ = device_scale_factor; - scoped_refptr<cc::SolidColorLayer> crashed_layer = - cc::SolidColorLayer::Create(); - crashed_layer->SetMasksToBounds(true); - crashed_layer->SetBackgroundColor(SK_ColorGRAY); - - if (child_frame_compositor_->GetLayer()) { - SkBitmap* sad_bitmap = child_frame_compositor_->GetSadPageBitmap(); - if (sad_bitmap && frame_size_in_dip.width() > sad_bitmap->width() && - frame_size_in_dip.height() > sad_bitmap->height()) { - scoped_refptr<cc::PictureImageLayer> sad_layer = - cc::PictureImageLayer::Create(); - sad_layer->SetImage(cc::PaintImageBuilder::WithDefault() - .set_id(cc::PaintImage::GetNextId()) - .set_image(SkImage::MakeFromBitmap(*sad_bitmap), - cc::PaintImage::GetNextContentId()) - .TakePaintImage(), - SkMatrix::I(), false); - sad_layer->SetBounds( - gfx::Size(sad_bitmap->width() * device_scale_factor, - sad_bitmap->height() * device_scale_factor)); - sad_layer->SetPosition( - gfx::PointF((frame_size_in_dip.width() - sad_bitmap->width()) / 2, - (frame_size_in_dip.height() - sad_bitmap->height()) / 2)); - sad_layer->SetIsDrawable(true); - - crashed_layer->AddChild(sad_layer); - } - } + auto crash_ui_layer = cc::PictureLayer::Create(this); + crash_ui_layer->SetMasksToBounds(true); bool prevent_contents_opaque_changes = false; - child_frame_compositor_->SetLayer(std::move(crashed_layer), + bool is_surface_layer = false; + child_frame_compositor_->SetLayer(std::move(crash_ui_layer), prevent_contents_opaque_changes, - false /* is_surface_layer */); + is_surface_layer); + UpdateVisibility(true); } void ChildFrameCompositingHelper::SetSurfaceId( @@ -104,4 +81,60 @@ layer->SetIsDrawable(visible); } +gfx::Rect ChildFrameCompositingHelper::PaintableRegion() { + DCHECK(crashed_); + return gfx::Rect(child_frame_compositor_->GetLayer()->bounds()); +} + +scoped_refptr<cc::DisplayItemList> +ChildFrameCompositingHelper::PaintContentsToDisplayList( + PaintingControlSetting) { + DCHECK(crashed_); + auto layer_size = child_frame_compositor_->GetLayer()->bounds(); + auto display_list = base::MakeRefCounted<cc::DisplayItemList>(); + display_list->StartPaint(); + display_list->push<cc::DrawColorOp>(SK_ColorGRAY, SkBlendMode::kSrc); + + SkBitmap* sad_bitmap = child_frame_compositor_->GetSadPageBitmap(); + if (sad_bitmap) { + int paint_width = sad_bitmap->width() * device_scale_factor_; + int paint_height = sad_bitmap->height() * device_scale_factor_; + if (layer_size.width() >= paint_width && + layer_size.height() >= paint_height) { + int x = (layer_size.width() - paint_width) / 2; + int y = (layer_size.height() - paint_height) / 2; + if (device_scale_factor_ != 1.f) { + display_list->push<cc::SaveOp>(); + display_list->push<cc::TranslateOp>(x, y); + display_list->push<cc::ScaleOp>(device_scale_factor_, + device_scale_factor_); + x = 0; + y = 0; + } + + auto image = cc::PaintImageBuilder::WithDefault() + .set_id(cc::PaintImage::GetNextId()) + .set_image(SkImage::MakeFromBitmap(*sad_bitmap), + cc::PaintImage::GetNextContentId()) + .TakePaintImage(); + display_list->push<cc::DrawImageOp>(image, x, y, nullptr); + + if (device_scale_factor_ != 1.f) + display_list->push<cc::RestoreOp>(); + } + } + display_list->EndPaintOfUnpaired(gfx::Rect(layer_size)); + display_list->Finalize(); + return display_list; +} + +bool ChildFrameCompositingHelper::FillsBoundsCompletely() const { + // Because we paint a full opaque gray background. + return true; +} + +size_t ChildFrameCompositingHelper::GetApproximateUnsharedMemoryUsage() const { + return sizeof(*this); +} + } // namespace content
diff --git a/content/renderer/child_frame_compositing_helper.h b/content/renderer/child_frame_compositing_helper.h index 0560816..7d9ca17 100644 --- a/content/renderer/child_frame_compositing_helper.h +++ b/content/renderer/child_frame_compositing_helper.h
@@ -12,6 +12,7 @@ #include <vector> #include "base/macros.h" +#include "cc/layers/content_layer_client.h" #include "cc/layers/surface_layer.h" #include "components/viz/common/surfaces/surface_id.h" #include "content/common/content_export.h" @@ -28,12 +29,12 @@ class ChildFrameCompositor; -class CONTENT_EXPORT ChildFrameCompositingHelper { +class CONTENT_EXPORT ChildFrameCompositingHelper + : public cc::ContentLayerClient { public: explicit ChildFrameCompositingHelper( ChildFrameCompositor* child_frame_compositor); - - virtual ~ChildFrameCompositingHelper(); + ~ChildFrameCompositingHelper() override; void SetSurfaceId(const viz::SurfaceId& surface_id, const gfx::Size& frame_size_in_dip, @@ -45,9 +46,19 @@ const viz::SurfaceId& surface_id() const { return surface_id_; } private: + // cc::ContentLayerClient implementation. Called from the cc::PictureLayer + // created for the crashed child frame to display the sad image. + gfx::Rect PaintableRegion() override; + scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList( + PaintingControlSetting) override; + bool FillsBoundsCompletely() const override; + size_t GetApproximateUnsharedMemoryUsage() const override; + ChildFrameCompositor* const child_frame_compositor_; viz::SurfaceId surface_id_; scoped_refptr<cc::SurfaceLayer> surface_layer_; + bool crashed_ = false; + float device_scale_factor_ = 1.f; DISALLOW_COPY_AND_ASSIGN(ChildFrameCompositingHelper); };
diff --git a/content/renderer/internal_document_state_data.cc b/content/renderer/internal_document_state_data.cc index 401d9af3..c0324abc 100644 --- a/content/renderer/internal_document_state_data.cc +++ b/content/renderer/internal_document_state_data.cc
@@ -57,6 +57,7 @@ cache_policy_override_set_ = other->cache_policy_override_set_; cache_policy_override_ = other->cache_policy_override_; effective_connection_type_ = other->effective_connection_type_; + previews_state_ = other->previews_state_; } void InternalDocumentStateData::set_navigation_state(
diff --git a/content/renderer/internal_document_state_data.h b/content/renderer/internal_document_state_data.h index 78496b40..068a848 100644 --- a/content/renderer/internal_document_state_data.h +++ b/content/renderer/internal_document_state_data.h
@@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/supports_user_data.h" +#include "content/public/common/previews_state.h" #include "net/nqe/effective_connection_type.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h" #include "url/gurl.h" @@ -81,6 +82,11 @@ effective_connection_type_ = effective_connection_type; } + PreviewsState previews_state() const { return previews_state_; } + void set_previews_state(PreviewsState previews_state) { + previews_state_ = previews_state; + } + NavigationState* navigation_state() { return navigation_state_.get(); } void set_navigation_state(std::unique_ptr<NavigationState> navigation_state); @@ -92,6 +98,7 @@ blink::mojom::FetchCacheMode cache_policy_override_; net::EffectiveConnectionType effective_connection_type_ = net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN; + PreviewsState previews_state_ = PREVIEWS_UNSPECIFIED; std::unique_ptr<NavigationState> navigation_state_; DISALLOW_COPY_AND_ASSIGN(InternalDocumentStateData);
diff --git a/content/renderer/loader/child_url_loader_factory_bundle.cc b/content/renderer/loader/child_url_loader_factory_bundle.cc index 7ec6cc3..e1a07d2 100644 --- a/content/renderer/loader/child_url_loader_factory_bundle.cc +++ b/content/renderer/loader/child_url_loader_factory_bundle.cc
@@ -168,7 +168,7 @@ ChildURLLoaderFactoryBundle::ChildURLLoaderFactoryBundle( std::unique_ptr<ChildURLLoaderFactoryBundleInfo> info) { - Update(std::move(info), base::nullopt); + Update(std::move(info)); } ChildURLLoaderFactoryBundle::ChildURLLoaderFactoryBundle( @@ -245,9 +245,7 @@ } void ChildURLLoaderFactoryBundle::Update( - std::unique_ptr<ChildURLLoaderFactoryBundleInfo> info, - base::Optional<std::vector<mojom::TransferrableURLLoaderPtr>> - subresource_overrides) { + std::unique_ptr<ChildURLLoaderFactoryBundleInfo> info) { if (info->direct_network_factory_info()) { direct_network_factory_.Bind( std::move(info->direct_network_factory_info())); @@ -257,12 +255,12 @@ std::move(info->prefetch_loader_factory_info())); } URLLoaderFactoryBundle::Update(std::move(info)); +} - if (subresource_overrides) { - for (auto& element : *subresource_overrides) { - subresource_overrides_[element->url] = std::move(element); - } - } +void ChildURLLoaderFactoryBundle::UpdateSubresourceOverrides( + std::vector<mojom::TransferrableURLLoaderPtr>* subresource_overrides) { + for (auto& element : *subresource_overrides) + subresource_overrides_[element->url] = std::move(element); } void ChildURLLoaderFactoryBundle::SetPrefetchLoaderFactory(
diff --git a/content/renderer/loader/child_url_loader_factory_bundle.h b/content/renderer/loader/child_url_loader_factory_bundle.h index d0596a2..d7e191a 100644 --- a/content/renderer/loader/child_url_loader_factory_bundle.h +++ b/content/renderer/loader/child_url_loader_factory_bundle.h
@@ -106,9 +106,9 @@ std::unique_ptr<ChildURLLoaderFactoryBundleInfo> PassInterface(); - void Update(std::unique_ptr<ChildURLLoaderFactoryBundleInfo> info, - base::Optional<std::vector<mojom::TransferrableURLLoaderPtr>> - subresource_overrides); + void Update(std::unique_ptr<ChildURLLoaderFactoryBundleInfo> info); + void UpdateSubresourceOverrides( + std::vector<mojom::TransferrableURLLoaderPtr>* subresource_overrides); void SetPrefetchLoaderFactory( network::mojom::URLLoaderFactoryPtr prefetch_loader_factory);
diff --git a/content/renderer/loader/tracked_child_url_loader_factory_bundle.cc b/content/renderer/loader/tracked_child_url_loader_factory_bundle.cc index cc9e9792..f692057 100644 --- a/content/renderer/loader/tracked_child_url_loader_factory_bundle.cc +++ b/content/renderer/loader/tracked_child_url_loader_factory_bundle.cc
@@ -60,7 +60,7 @@ std::unique_ptr<TrackedChildURLLoaderFactoryBundleInfo> info) { DCHECK(info->main_thread_host_bundle()); main_thread_host_bundle_ = std::move(info->main_thread_host_bundle()); - Update(std::move(info), base::nullopt); + Update(std::move(info)); AddObserverOnMainThread(); } @@ -117,8 +117,7 @@ void TrackedChildURLLoaderFactoryBundle::OnUpdate( std::unique_ptr<network::SharedURLLoaderFactoryInfo> info) { Update(base::WrapUnique( - static_cast<ChildURLLoaderFactoryBundleInfo*>(info.release())), - base::nullopt); + static_cast<ChildURLLoaderFactoryBundleInfo*>(info.release()))); } // ----------------------------------------------------------------------------- @@ -187,7 +186,7 @@ partial_bundle->Clone()); } - Update(partial_bundle->PassInterface(), base::nullopt); + Update(partial_bundle->PassInterface()); } bool HostChildURLLoaderFactoryBundle::IsHostChildURLLoaderFactoryBundle()
diff --git a/content/renderer/media/android/media_player_renderer_client.cc b/content/renderer/media/android/media_player_renderer_client.cc index cf50216c7..f1bd671 100644 --- a/content/renderer/media/android/media_player_renderer_client.cc +++ b/content/renderer/media/android/media_player_renderer_client.cc
@@ -73,7 +73,11 @@ void MediaPlayerRendererClient::OnScopedSurfaceRequested( const base::UnguessableToken& request_token) { - DCHECK(request_token); + if (request_token == base::UnguessableToken::Null()) { + client_->OnError(media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + stream_texture_wrapper_->ForwardStreamTextureForSurfaceRequest(request_token); } @@ -82,11 +86,13 @@ DCHECK(media_task_runner_->BelongsToCurrentThread()); DCHECK(!init_cb_.is_null()); - // TODO(tguilbert): Measure and smooth out the initialization's ordering to - // have the lowest total initialization time. - mojo_renderer_->InitiateScopedSurfaceRequest( - base::Bind(&MediaPlayerRendererClient::OnScopedSurfaceRequested, - weak_factory_.GetWeakPtr())); + if (status == media::PIPELINE_OK) { + // TODO(tguilbert): Measure and smooth out the initialization's ordering to + // have the lowest total initialization time. + mojo_renderer_->InitiateScopedSurfaceRequest( + base::Bind(&MediaPlayerRendererClient::OnScopedSurfaceRequested, + weak_factory_.GetWeakPtr())); + } base::ResetAndReturn(&init_cb_).Run(status); }
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc index 8edd55e..80becdbe 100644 --- a/content/renderer/media/media_factory.cc +++ b/content/renderer/media/media_factory.cc
@@ -166,7 +166,20 @@ : render_frame_(render_frame), request_routing_token_cb_(std::move(request_routing_token_cb)) {} -MediaFactory::~MediaFactory() {} +MediaFactory::~MediaFactory() { + // Release the DecoderFactory to the media thread since it may still be in use + // there due to pending pipeline Stop() calls. Once each Stop() completes, no + // new tasks using the DecoderFactory will execute, so we don't need to worry + // about additional posted tasks from Stop(). + if (decoder_factory_) { + // DeleteSoon() shouldn't ever fail, we should always have a RenderThread at + // this time and subsequently a media thread. To fail, the media thread must + // be dead/dying (which only happens at ~RenderThreadImpl), in which case + // the process is about to die anyways. + RenderThreadImpl::current()->GetMediaThreadTaskRunner()->DeleteSoon( + FROM_HERE, std::move(decoder_factory_)); + } +} void MediaFactory::SetupMojo() { // Only do setup once.
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 39b2f6ee..f25ea89dd 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -862,6 +862,7 @@ internal_data->set_must_reset_scroll_and_scale_state( common_params.navigation_type == FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL); + internal_data->set_previews_state(common_params.previews_state); document_state->set_can_load_local_resources( commit_params.can_load_local_resources); @@ -4353,8 +4354,7 @@ // main frame documents. Subframes inherit from the main frame and should not // change at commit time. if (is_main_frame_) { - previews_state_ = - frame_->GetDocumentLoader()->GetRequest().GetPreviewsState(); + previews_state_ = internal_data->previews_state(); effective_connection_type_ = EffectiveConnectionTypeToWebEffectiveConnectionType( internal_data->effective_connection_type()); @@ -6624,14 +6624,15 @@ !frame_->GetDocumentLoader()); loader_factories_->Update(render_thread->blink_platform_impl() ->CreateDefaultURLLoaderFactoryBundle() - ->PassInterface(), - base::nullopt); + ->PassInterface()); } if (info) { loader_factories_->Update( - std::make_unique<ChildURLLoaderFactoryBundleInfo>(std::move(info)), - std::move(subresource_overrides)); + std::make_unique<ChildURLLoaderFactoryBundleInfo>(std::move(info))); + } + if (subresource_overrides) { + loader_factories_->UpdateSubresourceOverrides(&*subresource_overrides); } if (prefetch_loader_factory) { loader_factories_->SetPrefetchLoaderFactory(
diff --git a/content/renderer/renderer_webapplicationcachehost_impl.cc b/content/renderer/renderer_webapplicationcachehost_impl.cc index 48ef67bb..a48617e 100644 --- a/content/renderer/renderer_webapplicationcachehost_impl.cc +++ b/content/renderer/renderer_webapplicationcachehost_impl.cc
@@ -70,8 +70,7 @@ if (render_frame) { auto info = std::make_unique<ChildURLLoaderFactoryBundleInfo>(); info->appcache_factory_info() = url_loader_factory.PassInterface(); - render_frame->GetLoaderFactoryBundle()->Update(std::move(info), - base::nullopt); + render_frame->GetLoaderFactoryBundle()->Update(std::move(info)); } }
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc index 5c3724f..009ec4f 100644 --- a/content/renderer/service_worker/service_worker_context_client.cc +++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -547,8 +547,7 @@ loader_factories_ = base::MakeRefCounted<HostChildURLLoaderFactoryBundle>( main_thread_task_runner_); loader_factories_->Update(std::make_unique<ChildURLLoaderFactoryBundleInfo>( - std::move(subresource_loaders)), - base::nullopt /* subresource_overrides */); + std::move(subresource_loaders))); } // Create a content::ServiceWorkerNetworkProvider for this data source so
diff --git a/content/renderer/service_worker/service_worker_subresource_loader.cc b/content/renderer/service_worker/service_worker_subresource_loader.cc index 7718381..b55aad4c 100644 --- a/content/renderer/service_worker/service_worker_subresource_loader.cc +++ b/content/renderer/service_worker/service_worker_subresource_loader.cc
@@ -56,23 +56,6 @@ return new_head; } -const char* FetchResponseSourceToSuffix( - network::mojom::FetchResponseSource source) { - // Don't change these returned strings. They are used for recording UMAs. - switch (source) { - case network::mojom::FetchResponseSource::kUnspecified: - return ".Unspecified"; - case network::mojom::FetchResponseSource::kNetwork: - return ".Network"; - case network::mojom::FetchResponseSource::kHttpCache: - return ".HttpCache"; - case network::mojom::FetchResponseSource::kCacheStorage: - return ".CacheStorage"; - } - NOTREACHED(); - return ".Unknown"; -} - // A wrapper URLLoaderClient that invokes the given RewriteHeaderCallback // whenever a response or redirect is received. class HeaderRewritingURLLoaderClient : public network::mojom::URLLoaderClient { @@ -639,7 +622,8 @@ base::UmaHistogramMediumTimes( base::StrCat({"ServiceWorker.LoadTiming.Subresource." "ResponseReceivedToCompleted2", - FetchResponseSourceToSuffix(response_source_)}), + ServiceWorkerUtils::FetchResponseSourceToSuffix( + response_source_)}), completion_time - response_head_.load_timing.receive_headers_end); } else { // Mojo message delay (network fallback case). See above for the detail.
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.cc b/content/renderer/shared_worker/embedded_shared_worker_stub.cc index d6dfe0a..ca8328d 100644 --- a/content/renderer/shared_worker/embedded_shared_worker_stub.cc +++ b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
@@ -276,8 +276,7 @@ subresource_loader_factories_->Update( render_thread->blink_platform_impl() ->CreateDefaultURLLoaderFactoryBundle() - ->PassInterface(), - base::nullopt /* subresource_overrides */); + ->PassInterface()); } if (factory_bundle) { @@ -296,8 +295,7 @@ subresource_loader_factories_->Update( std::make_unique<ChildURLLoaderFactoryBundleInfo>( - std::move(factory_bundle)), - base::nullopt /* subresource_overrides */); + std::move(factory_bundle))); } impl_->StartWorkerContext(
diff --git a/content/shell/browser/shell_devtools_bindings.cc b/content/shell/browser/shell_devtools_bindings.cc index 1f32e9a..83ac337 100644 --- a/content/shell/browser/shell_devtools_bindings.cc +++ b/content/shell/browser/shell_devtools_bindings.cc
@@ -264,6 +264,9 @@ auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = gurl; + // TODO(caseq): this preserves behavior of URLFetcher-based implementation. + // We really need to pass proper first party origin from the front-end. + resource_request->site_for_cookies = gurl; resource_request->headers.AddHeadersFromString(headers); auto* partition = content::BrowserContext::GetStoragePartitionForSite(
diff --git a/device/bluetooth/bluetooth_adapter.h b/device/bluetooth/bluetooth_adapter.h index 07c9182..e4bb06fe0 100644 --- a/device/bluetooth/bluetooth_adapter.h +++ b/device/bluetooth/bluetooth_adapter.h
@@ -132,7 +132,7 @@ const base::Optional<std::string>& advertisement_name, base::Optional<int8_t> rssi, base::Optional<int8_t> tx_power, - uint16_t appearance, + base::Optional<uint16_t> appearance, const BluetoothDevice::UUIDList& advertised_uuids, const BluetoothDevice::ServiceDataMap& service_data_map, const BluetoothDevice::ManufacturerDataMap& manufacturer_data_map) {}
diff --git a/device/bluetooth/bluetooth_adapter_factory.cc b/device/bluetooth/bluetooth_adapter_factory.cc index ab82c1f..6413a16 100644 --- a/device/bluetooth/bluetooth_adapter_factory.cc +++ b/device/bluetooth/bluetooth_adapter_factory.cc
@@ -4,9 +4,11 @@ #include "device/bluetooth/bluetooth_adapter_factory.h" +#include <utility> #include <vector> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/lazy_instance.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" @@ -52,12 +54,9 @@ void RunAdapterCallbacks() { DCHECK(default_adapter.Get()); scoped_refptr<BluetoothAdapter> adapter(default_adapter.Get().get()); - for (std::vector<BluetoothAdapterFactory::AdapterCallback>::const_iterator - iter = adapter_callbacks.Get().begin(); - iter != adapter_callbacks.Get().end(); - ++iter) { - iter->Run(adapter); - } + for (auto& callback : adapter_callbacks.Get()) + std::move(callback).Run(adapter); + adapter_callbacks.Get().clear(); } #endif // defined(OS_WIN) || defined(OS_LINUX) @@ -76,7 +75,7 @@ DCHECK(classic_adapter.Get()); scoped_refptr<BluetoothAdapter> adapter(classic_adapter.Get().get()); for (auto& callback : classic_adapter_callbacks.Get()) - callback.Run(adapter); + std::move(callback).Run(adapter); classic_adapter_callbacks.Get().clear(); } @@ -128,53 +127,56 @@ } // static -void BluetoothAdapterFactory::GetAdapter(const AdapterCallback& callback) { +void BluetoothAdapterFactory::GetAdapter(AdapterCallback callback) { DCHECK(IsBluetoothSupported()); #if defined(OS_WIN) || defined(OS_LINUX) if (!default_adapter.Get()) { default_adapter.Get() = - BluetoothAdapter::CreateAdapter(base::Bind(&RunAdapterCallbacks)); + BluetoothAdapter::CreateAdapter(base::BindOnce(&RunAdapterCallbacks)); DCHECK(!default_adapter.Get()->IsInitialized()); } if (!default_adapter.Get()->IsInitialized()) - adapter_callbacks.Get().push_back(callback); + adapter_callbacks.Get().push_back(std::move(callback)); #else // !defined(OS_WIN) && !defined(OS_LINUX) if (!default_adapter.Get()) { default_adapter.Get() = - BluetoothAdapter::CreateAdapter(BluetoothAdapter::InitCallback()); + BluetoothAdapter::CreateAdapter(base::NullCallback()); } DCHECK(default_adapter.Get()->IsInitialized()); #endif // defined(OS_WIN) || defined(OS_LINUX) - if (default_adapter.Get()->IsInitialized()) - callback.Run(scoped_refptr<BluetoothAdapter>(default_adapter.Get().get())); + if (default_adapter.Get()->IsInitialized()) { + std::move(callback).Run( + scoped_refptr<BluetoothAdapter>(default_adapter.Get().get())); + } } // static -void BluetoothAdapterFactory::GetClassicAdapter( - const AdapterCallback& callback) { +void BluetoothAdapterFactory::GetClassicAdapter(AdapterCallback callback) { #if defined(OS_WIN) if (base::win::GetVersion() < base::win::VERSION_WIN10) { // Prior to Win10, the default adapter will support Bluetooth classic. - GetAdapter(callback); + GetAdapter(std::move(callback)); return; } if (!classic_adapter.Get()) { classic_adapter.Get() = BluetoothAdapterWin::CreateClassicAdapter( - base::Bind(&RunClassicAdapterCallbacks)); + base::BindOnce(&RunClassicAdapterCallbacks)); DCHECK(!classic_adapter.Get()->IsInitialized()); } - if (!classic_adapter.Get()->IsInitialized()) - classic_adapter_callbacks.Get().push_back(callback); - else - callback.Run(scoped_refptr<BluetoothAdapter>(classic_adapter.Get().get())); + if (!classic_adapter.Get()->IsInitialized()) { + classic_adapter_callbacks.Get().push_back(std::move(callback)); + } else { + std::move(callback).Run( + scoped_refptr<BluetoothAdapter>(classic_adapter.Get().get())); + } #else - GetAdapter(callback); + GetAdapter(std::move(callback)); #endif // defined(OS_WIN) }
diff --git a/device/bluetooth/bluetooth_adapter_factory.h b/device/bluetooth/bluetooth_adapter_factory.h index e82bad9..fc7e7da 100644 --- a/device/bluetooth/bluetooth_adapter_factory.h +++ b/device/bluetooth/bluetooth_adapter_factory.h
@@ -27,8 +27,8 @@ // TODO(crbug.com/569709): Use ValuesForTesting for all functions. class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterFactory { public: - typedef base::Callback<void(scoped_refptr<BluetoothAdapter> adapter)> - AdapterCallback; + using AdapterCallback = + base::OnceCallback<void(scoped_refptr<BluetoothAdapter> adapter)>; ~BluetoothAdapterFactory(); @@ -50,7 +50,7 @@ // initializing it if necessary. |callback| is called with the adapter // instance passed only once the adapter is fully initialized and ready to // use. - static void GetAdapter(const AdapterCallback& callback); + static void GetAdapter(AdapterCallback callback); // Returns the shared instance of the classic adapter, creating and // initializing it if necessary. |callback| is called with the adapter @@ -58,7 +58,7 @@ // use. // For all platforms except Windows this is equivalent to calling // GetAdapter(), as the default adapter already supports Bluetooth classic. - static void GetClassicAdapter(const AdapterCallback& callback); + static void GetClassicAdapter(AdapterCallback callback); #if defined(OS_LINUX) // Calls |BluetoothAdapter::Shutdown| on the adapter if
diff --git a/device/bluetooth/bluetooth_adapter_factory_wrapper.cc b/device/bluetooth/bluetooth_adapter_factory_wrapper.cc index c1756cb..ca14603 100644 --- a/device/bluetooth/bluetooth_adapter_factory_wrapper.cc +++ b/device/bluetooth/bluetooth_adapter_factory_wrapper.cc
@@ -45,21 +45,22 @@ void BluetoothAdapterFactoryWrapper::AcquireAdapter( BluetoothAdapter::Observer* observer, - const AcquireAdapterCallback& callback) { + AcquireAdapterCallback callback) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!GetAdapter(observer)); AddAdapterObserver(observer); if (adapter_.get()) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(callback, base::Unretained(adapter_.get()))); + FROM_HERE, + base::BindOnce(std::move(callback), base::Unretained(adapter_.get()))); return; } DCHECK(BluetoothAdapterFactory::Get().IsLowEnergySupported()); BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothAdapterFactoryWrapper::OnGetAdapter, - weak_ptr_factory_.GetWeakPtr(), callback)); + base::BindOnce(&BluetoothAdapterFactoryWrapper::OnGetAdapter, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } void BluetoothAdapterFactoryWrapper::ReleaseAdapter( @@ -94,12 +95,12 @@ } void BluetoothAdapterFactoryWrapper::OnGetAdapter( - const AcquireAdapterCallback& continuation, + AcquireAdapterCallback continuation, scoped_refptr<BluetoothAdapter> adapter) { DCHECK(thread_checker_.CalledOnValidThread()); set_adapter(adapter); - continuation.Run(adapter_.get()); + std::move(continuation).Run(adapter_.get()); } bool BluetoothAdapterFactoryWrapper::HasAdapter(
diff --git a/device/bluetooth/bluetooth_adapter_factory_wrapper.h b/device/bluetooth/bluetooth_adapter_factory_wrapper.h index 3731e3ca..e91b46af 100644 --- a/device/bluetooth/bluetooth_adapter_factory_wrapper.h +++ b/device/bluetooth/bluetooth_adapter_factory_wrapper.h
@@ -23,7 +23,7 @@ // http://crbug.com/603291 class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterFactoryWrapper { public: - typedef base::Callback<void(BluetoothAdapter*)> AcquireAdapterCallback; + using AcquireAdapterCallback = base::OnceCallback<void(BluetoothAdapter*)>; ~BluetoothAdapterFactoryWrapper(); @@ -38,7 +38,7 @@ // adapter, otherwise it gets a new adapter and adds |observer| to it. Runs // |callback| with the adapter |observer| has been added to. void AcquireAdapter(BluetoothAdapter::Observer* observer, - const AcquireAdapterCallback& callback); + AcquireAdapterCallback callback); // Removes |observer| from the list of adapter observers if |observer| // has acquired the adapter in the past. If there are no more observers // it deletes the reference to the adapter. @@ -60,7 +60,7 @@ BluetoothAdapterFactoryWrapper(); - void OnGetAdapter(const AcquireAdapterCallback& continuation, + void OnGetAdapter(AcquireAdapterCallback continuation, scoped_refptr<BluetoothAdapter> adapter); bool HasAdapter(BluetoothAdapter::Observer* observer);
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm index 7848d53b..b97439a4 100644 --- a/device/bluetooth/bluetooth_adapter_mac.mm +++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -693,8 +693,6 @@ // 0xXX: -127 to +127 dBm" // Core Specification Supplement (CSS) v7, Part 1.5 // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdatatxpowerlevelkey - // TODO(dougt): We shoud treat |tx_power| and |local_name| as optional as - // they do not have to be specified by the device. NSNumber* tx_power = [advertisement_data objectForKey:CBAdvertisementDataTxPowerLevelKey]; int8_t clamped_tx_power = BluetoothDevice::ClampPower([tx_power intValue]); @@ -709,10 +707,10 @@ base::SysNSStringToUTF8(local_name); observer.DeviceAdvertisementReceived( - device_mac->GetAddress(), device_name_opt, local_name_opt, rssi, - clamped_tx_power, - BluetoothDevice::kAppearanceNotPresent, /* TODO(crbug.com/588083) - Implement appearance */ + device_mac->GetAddress(), device_name_opt, + local_name == nil ? base::nullopt : local_name_opt, rssi, + tx_power == nil ? base::nullopt : base::make_optional(clamped_tx_power), + base::nullopt, /* TODO(crbug.com/588083) Implement appearance */ advertised_uuids, service_data_map, manufacturer_data_map); }
diff --git a/device/bluetooth/bluez/bluetooth_adapter_profile_bluez_unittest.cc b/device/bluetooth/bluez/bluetooth_adapter_profile_bluez_unittest.cc index 45e0af7..7c81a17 100644 --- a/device/bluetooth/bluez/bluetooth_adapter_profile_bluez_unittest.cc +++ b/device/bluetooth/bluez/bluetooth_adapter_profile_bluez_unittest.cc
@@ -58,8 +58,8 @@ // Grab a pointer to the adapter. device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothAdapterProfileBlueZTest::AdapterCallback, - base::Unretained(this))); + base::BindOnce(&BluetoothAdapterProfileBlueZTest::AdapterCallback, + base::Unretained(this))); base::RunLoop().Run(); ASSERT_TRUE(adapter_.get() != nullptr); ASSERT_TRUE(adapter_->IsInitialized());
diff --git a/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc b/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc index 630508e..292b9e2 100644 --- a/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc +++ b/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc
@@ -58,8 +58,8 @@ // Gets the existing Bluetooth adapter. void GetAdapter() { BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothAdvertisementBlueZTest::GetAdapterCallback, - base::Unretained(this))); + base::BindOnce(&BluetoothAdvertisementBlueZTest::GetAdapterCallback, + base::Unretained(this))); base::RunLoop().Run(); }
diff --git a/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc b/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc index ab60b46..4d69c1c 100644 --- a/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc +++ b/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
@@ -138,7 +138,7 @@ } void GetAdapter() { - device::BluetoothAdapterFactory::GetAdapter(base::Bind( + device::BluetoothAdapterFactory::GetAdapter(base::BindOnce( &BluetoothGattBlueZTest::AdapterCallback, base::Unretained(this))); base::RunLoop().Run(); ASSERT_TRUE(adapter_.get() != NULL);
diff --git a/device/bluetooth/bluez/bluetooth_socket_bluez_unittest.cc b/device/bluetooth/bluez/bluetooth_socket_bluez_unittest.cc index ae8c7388..eb5df55 100644 --- a/device/bluetooth/bluez/bluetooth_socket_bluez_unittest.cc +++ b/device/bluetooth/bluez/bluetooth_socket_bluez_unittest.cc
@@ -76,9 +76,9 @@ // Grab a pointer to the adapter. { base::RunLoop run_loop; - device::BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothSocketBlueZTest::AdapterCallback, - base::Unretained(this), run_loop.QuitWhenIdleClosure())); + device::BluetoothAdapterFactory::GetAdapter(base::BindOnce( + &BluetoothSocketBlueZTest::AdapterCallback, base::Unretained(this), + run_loop.QuitWhenIdleClosure())); run_loop.Run(); }
diff --git a/device/bluetooth/test/fake_central.cc b/device/bluetooth/test/fake_central.cc index c030e60..61e4490 100644 --- a/device/bluetooth/test/fake_central.cc +++ b/device/bluetooth/test/fake_central.cc
@@ -89,10 +89,7 @@ observer.DeviceAdvertisementReceived( scan_result_ptr->device_address, scan_record->name, scan_record->name, scan_result_ptr->rssi, scan_record->tx_power->value, - device::BluetoothDevice:: - kAppearanceNotPresent, /* TODO(crbug.com/588083) - Implement appearance - */ + base::nullopt, /* TODO(crbug.com/588083) Implement appearance */ uuids, service_data, manufacturer_data); }
diff --git a/device/bluetooth/test/test_bluetooth_adapter_observer.cc b/device/bluetooth/test/test_bluetooth_adapter_observer.cc index eccdf250..4162c5d 100644 --- a/device/bluetooth/test/test_bluetooth_adapter_observer.cc +++ b/device/bluetooth/test/test_bluetooth_adapter_observer.cc
@@ -151,7 +151,7 @@ const base::Optional<std::string>& advertisement_name, base::Optional<int8_t> rssi, base::Optional<int8_t> tx_power, - uint16_t appearance, + base::Optional<uint16_t> appearance, const device::BluetoothDevice::UUIDList& advertised_uuids, const device::BluetoothDevice::ServiceDataMap& service_data_map, const device::BluetoothDevice::ManufacturerDataMap& manufacturer_data_map) {
diff --git a/device/bluetooth/test/test_bluetooth_adapter_observer.h b/device/bluetooth/test/test_bluetooth_adapter_observer.h index afe04f54..b3eba2e1 100644 --- a/device/bluetooth/test/test_bluetooth_adapter_observer.h +++ b/device/bluetooth/test/test_bluetooth_adapter_observer.h
@@ -44,7 +44,7 @@ const base::Optional<std::string>& advertisement_name, base::Optional<int8_t> rssi, base::Optional<int8_t> tx_power, - uint16_t appearance, + base::Optional<uint16_t> appearance, const device::BluetoothDevice::UUIDList& advertised_uuids, const device::BluetoothDevice::ServiceDataMap& service_data_map, const device::BluetoothDevice::ManufacturerDataMap& manufacturer_data_map) @@ -126,7 +126,9 @@ } base::Optional<int8_t> device_last_rssi() const { return last_rssi_; } base::Optional<int8_t> device_last_tx_power() const { return last_tx_power_; } - uint16_t device_last_appearance() const { return last_appearance_; } + base::Optional<uint16_t> device_last_appearance() const { + return last_appearance_; + } #if defined(OS_CHROMEOS) || defined(OS_LINUX) int device_paired_changed_count() const { @@ -229,7 +231,7 @@ base::Optional<std::string> last_advertisement_name_; base::Optional<int8_t> last_rssi_; base::Optional<int8_t> last_tx_power_; - uint16_t last_appearance_; + base::Optional<uint16_t> last_appearance_; #if defined(OS_CHROMEOS) || defined(OS_LINUX) int device_paired_changed_count_;
diff --git a/device/fido/ble/fido_ble_discovery_base.cc b/device/fido/ble/fido_ble_discovery_base.cc index 19c24c1..ea15fc7 100644 --- a/device/fido/ble/fido_ble_discovery_base.cc +++ b/device/fido/ble/fido_ble_discovery_base.cc
@@ -7,7 +7,6 @@ #include <utility> #include "base/bind.h" -#include "base/callback_helpers.h" #include "base/location.h" #include "base/no_destructor.h" #include "base/threading/thread_task_runner_handle.h" @@ -83,9 +82,8 @@ } void FidoBleDiscoveryBase::StartInternal() { - BluetoothAdapterFactory::Get().GetAdapter( - base::AdaptCallbackForRepeating(base::BindOnce( - &FidoBleDiscoveryBase::OnGetAdapter, weak_factory_.GetWeakPtr()))); + BluetoothAdapterFactory::Get().GetAdapter(base::BindOnce( + &FidoBleDiscoveryBase::OnGetAdapter, weak_factory_.GetWeakPtr())); } } // namespace device
diff --git a/device/fido/ble_adapter_manager.cc b/device/fido/ble_adapter_manager.cc index 12f2aaf..13b95766 100644 --- a/device/fido/ble_adapter_manager.cc +++ b/device/fido/ble_adapter_manager.cc
@@ -15,8 +15,8 @@ BleAdapterManager::BleAdapterManager(FidoRequestHandlerBase* request_handler) : request_handler_(request_handler), weak_factory_(this) { - BluetoothAdapterFactory::Get().GetAdapter(base::BindRepeating( - &BleAdapterManager::Start, weak_factory_.GetWeakPtr())); + BluetoothAdapterFactory::Get().GetAdapter( + base::BindOnce(&BleAdapterManager::Start, weak_factory_.GetWeakPtr())); } BleAdapterManager::~BleAdapterManager() {
diff --git a/device/usb/public/mojom/BUILD.gn b/device/usb/public/mojom/BUILD.gn index 1d346fac..23a8a97 100644 --- a/device/usb/public/mojom/BUILD.gn +++ b/device/usb/public/mojom/BUILD.gn
@@ -7,7 +7,9 @@ mojom("mojom") { sources = [ "device.mojom", + "device_enumeration_options.mojom", "device_manager.mojom", + "device_manager_client.mojom", ] public_deps = [
diff --git a/device/usb/public/mojom/device_enumeration_options.mojom b/device/usb/public/mojom/device_enumeration_options.mojom new file mode 100644 index 0000000..304ac02df --- /dev/null +++ b/device/usb/public/mojom/device_enumeration_options.mojom
@@ -0,0 +1,30 @@ +// Copyright 2019 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 device.mojom; + +import "mojo/public/mojom/base/string16.mojom"; + +struct UsbDeviceFilter { + bool has_vendor_id; + uint16 vendor_id; + + bool has_product_id; + uint16 product_id; + + bool has_class_code; + uint8 class_code; + + bool has_subclass_code; + uint8 subclass_code; + + bool has_protocol_code; + uint8 protocol_code; + + mojo_base.mojom.String16? serial_number; +}; + +struct UsbEnumerationOptions { + array<UsbDeviceFilter> filters; +}; \ No newline at end of file
diff --git a/device/usb/public/mojom/device_manager.mojom b/device/usb/public/mojom/device_manager.mojom index 95ecff7..00b6590 100644 --- a/device/usb/public/mojom/device_manager.mojom +++ b/device/usb/public/mojom/device_manager.mojom
@@ -5,30 +5,8 @@ module device.mojom; import "device/usb/public/mojom/device.mojom"; -import "mojo/public/mojom/base/string16.mojom"; - -struct UsbDeviceFilter { - bool has_vendor_id; - uint16 vendor_id; - - bool has_product_id; - uint16 product_id; - - bool has_class_code; - uint8 class_code; - - bool has_subclass_code; - uint8 subclass_code; - - bool has_protocol_code; - uint8 protocol_code; - - mojo_base.mojom.String16? serial_number; -}; - -struct UsbEnumerationOptions { - array<UsbDeviceFilter> filters; -}; +import "device/usb/public/mojom/device_enumeration_options.mojom"; +import "device/usb/public/mojom/device_manager_client.mojom"; interface UsbDeviceManager { // Retrieves information about all devices available to the DeviceManager @@ -47,12 +25,4 @@ // Sets the client for this DeviceManager service. The service will notify its // client of device events such as connection and disconnection. SetClient(associated UsbDeviceManagerClient client); -}; - -interface UsbDeviceManagerClient { - // Called when a device is connected to the host. - OnDeviceAdded(UsbDeviceInfo device_info); - - // Called when a device is disconnected from the host. - OnDeviceRemoved(UsbDeviceInfo device_info); -}; +}; \ No newline at end of file
diff --git a/device/usb/public/mojom/device_manager_client.mojom b/device/usb/public/mojom/device_manager_client.mojom new file mode 100644 index 0000000..4f33422f --- /dev/null +++ b/device/usb/public/mojom/device_manager_client.mojom
@@ -0,0 +1,15 @@ +// Copyright 2019 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 device.mojom; + +import "device/usb/public/mojom/device.mojom"; + +interface UsbDeviceManagerClient { + // Called when a device is connected to the host. + OnDeviceAdded(UsbDeviceInfo device_info); + + // Called when a device is disconnected from the host. + OnDeviceRemoved(UsbDeviceInfo device_info); +};
diff --git a/extensions/browser/api/bluetooth/bluetooth_event_router.cc b/extensions/browser/api/bluetooth/bluetooth_event_router.cc index 50b35ab..7e4c115 100644 --- a/extensions/browser/api/bluetooth/bluetooth_event_router.cc +++ b/extensions/browser/api/bluetooth/bluetooth_event_router.cc
@@ -41,9 +41,9 @@ void IgnoreAdapterResult(scoped_refptr<device::BluetoothAdapter> adapter) {} void IgnoreAdapterResultAndThen( - const base::Closure& callback, + base::OnceClosure callback, scoped_refptr<device::BluetoothAdapter> adapter) { - callback.Run(); + std::move(callback).Run(); } std::string GetListenerId(const extensions::EventListenerInfo& details) { @@ -84,17 +84,17 @@ } void BluetoothEventRouter::GetAdapter( - const device::BluetoothAdapterFactory::AdapterCallback& callback) { + device::BluetoothAdapterFactory::AdapterCallback callback) { if (adapter_.get()) { - callback.Run(scoped_refptr<device::BluetoothAdapter>(adapter_)); + std::move(callback).Run(adapter_); return; } // Note: On ChromeOS this will return an adapter that also supports Bluetooth // Low Energy. device::BluetoothAdapterFactory::GetClassicAdapter( - base::Bind(&BluetoothEventRouter::OnAdapterInitialized, - weak_ptr_factory_.GetWeakPtr(), callback)); + base::BindOnce(&BluetoothEventRouter::OnAdapterInitialized, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } void BluetoothEventRouter::StartDiscoverySession( @@ -105,11 +105,12 @@ if (!adapter_.get() && IsBluetoothSupported()) { // If |adapter_| isn't set yet, call GetAdapter() which will synchronously // invoke the callback (StartDiscoverySessionImpl). - GetAdapter(base::Bind( + GetAdapter(base::BindOnce( &IgnoreAdapterResultAndThen, - base::Bind(&BluetoothEventRouter::StartDiscoverySessionImpl, - weak_ptr_factory_.GetWeakPtr(), base::RetainedRef(adapter), - extension_id, callback, error_callback))); + base::BindOnce(&BluetoothEventRouter::StartDiscoverySessionImpl, + weak_ptr_factory_.GetWeakPtr(), + base::RetainedRef(adapter), extension_id, callback, + error_callback))); return; } StartDiscoverySessionImpl(adapter, extension_id, callback, error_callback); @@ -213,14 +214,14 @@ } void BluetoothEventRouter::OnAdapterInitialized( - const device::BluetoothAdapterFactory::AdapterCallback& callback, + device::BluetoothAdapterFactory::AdapterCallback callback, scoped_refptr<device::BluetoothAdapter> adapter) { if (!adapter_.get()) { adapter_ = adapter; adapter_->AddObserver(this); } - callback.Run(adapter); + std::move(callback).Run(adapter); } void BluetoothEventRouter::MaybeReleaseAdapter() { @@ -234,10 +235,10 @@ void BluetoothEventRouter::AddPairingDelegate(const std::string& extension_id) { if (!adapter_.get() && IsBluetoothSupported()) { - GetAdapter( - base::Bind(&IgnoreAdapterResultAndThen, - base::Bind(&BluetoothEventRouter::AddPairingDelegateImpl, - weak_ptr_factory_.GetWeakPtr(), extension_id))); + GetAdapter(base::BindOnce( + &IgnoreAdapterResultAndThen, + base::BindOnce(&BluetoothEventRouter::AddPairingDelegateImpl, + weak_ptr_factory_.GetWeakPtr(), extension_id))); return; } AddPairingDelegateImpl(extension_id); @@ -380,7 +381,7 @@ int count = ++event_listener_count_[id]; BLUETOOTH_LOG(EVENT) << "Event Listener Added: " << id << " Count: " << count; if (!adapter_.get()) - GetAdapter(base::Bind(&IgnoreAdapterResult)); + GetAdapter(base::BindOnce(&IgnoreAdapterResult)); } void BluetoothEventRouter::OnListenerRemoved(const EventListenerInfo& details) {
diff --git a/extensions/browser/api/bluetooth/bluetooth_event_router.h b/extensions/browser/api/bluetooth/bluetooth_event_router.h index 1f0968b..de722ed 100644 --- a/extensions/browser/api/bluetooth/bluetooth_event_router.h +++ b/extensions/browser/api/bluetooth/bluetooth_event_router.h
@@ -48,8 +48,7 @@ // adapter is available for the current platform. bool IsBluetoothSupported() const; - void GetAdapter( - const device::BluetoothAdapterFactory::AdapterCallback& callback); + void GetAdapter(device::BluetoothAdapterFactory::AdapterCallback callback); // Requests that a new device discovery session be initiated for extension // with id |extension_id|. |callback| is called, if a session has been @@ -140,7 +139,7 @@ void AddPairingDelegateImpl(const std::string& extension_id); void OnAdapterInitialized( - const device::BluetoothAdapterFactory::AdapterCallback& callback, + device::BluetoothAdapterFactory::AdapterCallback callback, scoped_refptr<device::BluetoothAdapter> adapter); void MaybeReleaseAdapter(); void DispatchAdapterStateEvent();
diff --git a/extensions/browser/api/bluetooth/bluetooth_extension_function.cc b/extensions/browser/api/bluetooth/bluetooth_extension_function.cc index 3bc9d70..b459f312 100644 --- a/extensions/browser/api/bluetooth/bluetooth_extension_function.cc +++ b/extensions/browser/api/bluetooth/bluetooth_extension_function.cc
@@ -32,10 +32,10 @@ return GetEventRouter(context)->IsBluetoothSupported(); } -void GetAdapter(const device::BluetoothAdapterFactory::AdapterCallback callback, +void GetAdapter(device::BluetoothAdapterFactory::AdapterCallback callback, content::BrowserContext* context) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - GetEventRouter(context)->GetAdapter(callback); + GetEventRouter(context)->GetAdapter(std::move(callback)); } } // namespace @@ -57,8 +57,9 @@ if (!IsBluetoothSupported(browser_context())) return RespondNow(Error(kPlatformNotSupported)); - GetAdapter(base::Bind(&BluetoothExtensionFunction::RunOnAdapterReady, this), - browser_context()); + GetAdapter( + base::BindOnce(&BluetoothExtensionFunction::RunOnAdapterReady, this), + browser_context()); return did_respond() ? AlreadyResponded() : RespondLater(); }
diff --git a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc index bb988e19..adbe0ca 100644 --- a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc +++ b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc
@@ -141,9 +141,9 @@ } template <typename T> -void DoWorkCallback(const base::Callback<T()>& callback) { +void DoWorkCallback(base::OnceCallback<T()> callback) { DCHECK(!callback.is_null()); - callback.Run(); + std::move(callback).Run(); } std::unique_ptr<device::BluetoothAdvertisement::ManufacturerData> @@ -374,9 +374,10 @@ return RespondNow(Error(kErrorPlatformNotSupported)); // It is safe to pass |this| here as ExtensionFunction is refcounted. - if (!event_router_->InitializeAdapterAndInvokeCallback(base::Bind( + if (!event_router_->InitializeAdapterAndInvokeCallback(base::BindOnce( &DoWorkCallback<void>, - base::Bind(&BluetoothLowEnergyExtensionFunction::PreDoWork, this)))) { + base::BindOnce(&BluetoothLowEnergyExtensionFunction::PreDoWork, + this)))) { // DoWork will respond when the adapter gets initialized. return RespondNow(Error(kErrorAdapterNotInitialized)); }
diff --git a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc index 4c0005a..65e2600 100644 --- a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc +++ b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
@@ -282,18 +282,18 @@ } bool BluetoothLowEnergyEventRouter::InitializeAdapterAndInvokeCallback( - const base::Closure& callback) { + base::OnceClosure callback) { if (!IsBluetoothSupported()) return false; if (adapter_.get()) { - callback.Run(); + std::move(callback).Run(); return true; } BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothLowEnergyEventRouter::OnGetAdapter, - weak_ptr_factory_.GetWeakPtr(), callback)); + base::BindOnce(&BluetoothLowEnergyEventRouter::OnGetAdapter, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); return true; } @@ -1392,7 +1392,7 @@ } void BluetoothLowEnergyEventRouter::OnGetAdapter( - const base::Closure& callback, + base::OnceClosure callback, scoped_refptr<device::BluetoothAdapter> adapter) { adapter_ = adapter; @@ -1401,7 +1401,7 @@ InitializeIdentifierMappings(); adapter_->AddObserver(this); - callback.Run(); + std::move(callback).Run(); } void BluetoothLowEnergyEventRouter::InitializeIdentifierMappings() {
diff --git a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h index 1468bfe1..41bcaf6 100644 --- a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h +++ b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h
@@ -121,7 +121,7 @@ // it and invokes |callback|. Until the first successful call to this method, // none of the methods in this class will succeed and no device::Bluetooth* // API events will be observed. - bool InitializeAdapterAndInvokeCallback(const base::Closure& callback); + bool InitializeAdapterAndInvokeCallback(base::OnceClosure callback); // Returns true, if the BluetoothAdapter was initialized. bool HasAdapter() const; @@ -391,7 +391,7 @@ private: // Called by BluetoothAdapterFactory. - void OnGetAdapter(const base::Closure& callback, + void OnGetAdapter(base::OnceClosure callback, scoped_refptr<device::BluetoothAdapter> adapter); // Initializes the identifier for all existing GATT objects and devices.
diff --git a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc index fec95c8..443c052 100644 --- a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc +++ b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc
@@ -250,7 +250,7 @@ ExtensionFunction::ResponseAction BluetoothSocketListenFunction::Run() { DCHECK_CURRENTLY_ON(work_thread_id()); device::BluetoothAdapterFactory::GetClassicAdapter( - base::Bind(&BluetoothSocketListenFunction::OnGetAdapter, this)); + base::BindOnce(&BluetoothSocketListenFunction::OnGetAdapter, this)); return did_respond() ? AlreadyResponded() : RespondLater(); } @@ -425,8 +425,8 @@ ExtensionFunction::ResponseAction BluetoothSocketAbstractConnectFunction::Run() { DCHECK_CURRENTLY_ON(work_thread_id()); - device::BluetoothAdapterFactory::GetClassicAdapter( - base::Bind(&BluetoothSocketAbstractConnectFunction::OnGetAdapter, this)); + device::BluetoothAdapterFactory::GetClassicAdapter(base::BindOnce( + &BluetoothSocketAbstractConnectFunction::OnGetAdapter, this)); return did_respond() ? AlreadyResponded() : RespondLater(); }
diff --git a/gpu/command_buffer/build_cmd_buffer_lib.py b/gpu/command_buffer/build_cmd_buffer_lib.py index 43279b5b..9dfe7be4 100644 --- a/gpu/command_buffer/build_cmd_buffer_lib.py +++ b/gpu/command_buffer/build_cmd_buffer_lib.py
@@ -3378,7 +3378,7 @@ self.WriteClientGLCallLog(func, f) if self.__NeedsToCalcDataCount(func): - f.write(" size_t count = %sGLES2Util::Calc%sDataCount(%s);\n" % + f.write(" uint32_t count = %sGLES2Util::Calc%sDataCount(%s);\n" % (_Namespace(), func.name, func.GetOriginalArgs()[0].name)) f.write(" DCHECK_LE(count, %du);\n" % self.GetArrayCount(func)) f.write(" if (count == 0) {\n") @@ -3388,8 +3388,8 @@ f.write(" return;\n") f.write(" }\n") else: - f.write(" size_t count = %d;" % self.GetArrayCount(func)) - f.write(" for (size_t ii = 0; ii < count; ++ii)\n") + f.write(" uint32_t count = %d;" % self.GetArrayCount(func)) + f.write(" for (uint32_t ii = 0; ii < count; ++ii)\n") f.write(' GPU_CLIENT_LOG("value[" << ii << "]: " << %s[ii]);\n' % func.GetLastOriginalArg().name) for arg in func.GetOriginalArgs():
diff --git a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h index 1dc13693..b196b0a 100644 --- a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
@@ -241,13 +241,13 @@ << GLES2Util::GetStringBufferfv(buffer) << ", " << drawbuffers << ", " << static_cast<const void*>(value) << ")"); - size_t count = GLES2Util::CalcClearBufferfvDataCount(buffer); + uint32_t count = GLES2Util::CalcClearBufferfvDataCount(buffer); DCHECK_LE(count, 4u); if (count == 0) { SetGLErrorInvalidEnum("glClearBufferfv", buffer, "buffer"); return; } - for (size_t ii = 0; ii < count; ++ii) + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << value[ii]); helper_->ClearBufferfvImmediate(buffer, drawbuffers, value); CheckGLError(); @@ -261,13 +261,13 @@ << GLES2Util::GetStringBufferiv(buffer) << ", " << drawbuffers << ", " << static_cast<const void*>(value) << ")"); - size_t count = GLES2Util::CalcClearBufferivDataCount(buffer); + uint32_t count = GLES2Util::CalcClearBufferivDataCount(buffer); DCHECK_LE(count, 4u); if (count == 0) { SetGLErrorInvalidEnum("glClearBufferiv", buffer, "buffer"); return; } - for (size_t ii = 0; ii < count; ++ii) + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << value[ii]); helper_->ClearBufferivImmediate(buffer, drawbuffers, value); CheckGLError(); @@ -281,13 +281,13 @@ << GLES2Util::GetStringBufferuiv(buffer) << ", " << drawbuffers << ", " << static_cast<const void*>(value) << ")"); - size_t count = GLES2Util::CalcClearBufferuivDataCount(buffer); + uint32_t count = GLES2Util::CalcClearBufferuivDataCount(buffer); DCHECK_LE(count, 4u); if (count == 0) { SetGLErrorInvalidEnum("glClearBufferuiv", buffer, "buffer"); return; } - for (size_t ii = 0; ii < count; ++ii) + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << value[ii]); helper_->ClearBufferuivImmediate(buffer, drawbuffers, value); CheckGLError(); @@ -1761,8 +1761,8 @@ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glSamplerParameterfv(" << sampler << ", " << GLES2Util::GetStringSamplerParameter(pname) << ", " << static_cast<const void*>(params) << ")"); - size_t count = 1; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 1; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << params[ii]); helper_->SamplerParameterfvImmediate(sampler, pname, params); CheckGLError(); @@ -1786,8 +1786,8 @@ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glSamplerParameteriv(" << sampler << ", " << GLES2Util::GetStringSamplerParameter(pname) << ", " << static_cast<const void*>(params) << ")"); - size_t count = 1; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 1; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << params[ii]); helper_->SamplerParameterivImmediate(sampler, pname, params); CheckGLError(); @@ -1929,8 +1929,8 @@ << GLES2Util::GetStringTextureBindTarget(target) << ", " << GLES2Util::GetStringTextureParameter(pname) << ", " << static_cast<const void*>(params) << ")"); - size_t count = 1; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 1; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << params[ii]); helper_->TexParameterfvImmediate(target, pname, params); CheckGLError(); @@ -1956,8 +1956,8 @@ << GLES2Util::GetStringTextureBindTarget(target) << ", " << GLES2Util::GetStringTextureParameter(pname) << ", " << static_cast<const void*>(params) << ")"); - size_t count = 1; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 1; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << params[ii]); helper_->TexParameterivImmediate(target, pname, params); CheckGLError(); @@ -2647,8 +2647,8 @@ GPU_CLIENT_SINGLE_THREAD_CHECK(); GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttrib1fv(" << indx << ", " << static_cast<const void*>(values) << ")"); - size_t count = 1; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 1; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]); helper_->VertexAttrib1fvImmediate(indx, values); CheckGLError(); @@ -2666,8 +2666,8 @@ GPU_CLIENT_SINGLE_THREAD_CHECK(); GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttrib2fv(" << indx << ", " << static_cast<const void*>(values) << ")"); - size_t count = 2; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 2; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]); helper_->VertexAttrib2fvImmediate(indx, values); CheckGLError(); @@ -2688,8 +2688,8 @@ GPU_CLIENT_SINGLE_THREAD_CHECK(); GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttrib3fv(" << indx << ", " << static_cast<const void*>(values) << ")"); - size_t count = 3; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 3; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]); helper_->VertexAttrib3fvImmediate(indx, values); CheckGLError(); @@ -2711,8 +2711,8 @@ GPU_CLIENT_SINGLE_THREAD_CHECK(); GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttrib4fv(" << indx << ", " << static_cast<const void*>(values) << ")"); - size_t count = 4; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 4; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]); helper_->VertexAttrib4fvImmediate(indx, values); CheckGLError(); @@ -2734,8 +2734,8 @@ GPU_CLIENT_SINGLE_THREAD_CHECK(); GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttribI4iv(" << indx << ", " << static_cast<const void*>(values) << ")"); - size_t count = 4; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 4; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]); helper_->VertexAttribI4ivImmediate(indx, values); CheckGLError(); @@ -2758,8 +2758,8 @@ GPU_CLIENT_SINGLE_THREAD_CHECK(); GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttribI4uiv(" << indx << ", " << static_cast<const void*>(values) << ")"); - size_t count = 4; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 4; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]); helper_->VertexAttribI4uivImmediate(indx, values); CheckGLError(); @@ -3439,8 +3439,8 @@ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glMatrixLoadfCHROMIUM(" << GLES2Util::GetStringMatrixMode(matrixMode) << ", " << static_cast<const void*>(m) << ")"); - size_t count = 16; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 16; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << m[ii]); helper_->MatrixLoadfCHROMIUMImmediate(matrixMode, m); CheckGLError(); @@ -3607,8 +3607,8 @@ << "] glUniformMatrix4fvStreamTextureMatrixCHROMIUM(" << location << ", " << GLES2Util::GetStringBool(transpose) << ", " << static_cast<const void*>(transform) << ")"); - size_t count = 16; - for (size_t ii = 0; ii < count; ++ii) + uint32_t count = 16; + for (uint32_t ii = 0; ii < count; ++ii) GPU_CLIENT_LOG("value[" << ii << "]: " << transform[ii]); helper_->UniformMatrix4fvStreamTextureMatrixCHROMIUMImmediate( location, transpose, transform);
diff --git a/gpu/command_buffer/client/mapped_memory.cc b/gpu/command_buffer/client/mapped_memory.cc index ebb7b79..01dd619d 100644 --- a/gpu/command_buffer/client/mapped_memory.cc +++ b/gpu/command_buffer/client/mapped_memory.cc
@@ -37,9 +37,7 @@ CommandBufferHelper* helper) : shm_id_(shm_id), shm_(shm), - allocator_(base::checked_cast<unsigned int>(shm->size()), - helper, - shm->memory()) {} + allocator_(shm->size(), helper, shm->memory()) {} MemoryChunk::~MemoryChunk() = default;
diff --git a/gpu/command_buffer/client/mapped_memory.h b/gpu/command_buffer/client/mapped_memory.h index 6dca2bd..61d9c5fa 100644 --- a/gpu/command_buffer/client/mapped_memory.h +++ b/gpu/command_buffer/client/mapped_memory.h
@@ -31,25 +31,21 @@ ~MemoryChunk(); // Gets the size of the largest free block that is available without waiting. - unsigned int GetLargestFreeSizeWithoutWaiting() { + uint32_t GetLargestFreeSizeWithoutWaiting() { return allocator_.GetLargestFreeSize(); } // Gets the size of the largest free block that can be allocated if the // caller can wait. - unsigned int GetLargestFreeSizeWithWaiting() { + uint32_t GetLargestFreeSizeWithWaiting() { return allocator_.GetLargestFreeOrPendingSize(); } // Gets the size of the chunk. - unsigned int GetSize() const { - return static_cast<unsigned int>(shm_->size()); - } + uint32_t GetSize() const { return shm_->size(); } // The shared memory id for this chunk. - int32_t shm_id() const { - return shm_id_; - } + int32_t shm_id() const { return shm_id_; } gpu::Buffer* shared_memory() const { return shm_.get(); } @@ -63,15 +59,11 @@ // Returns: // the pointer to the allocated memory block, or nullptr if out of // memory. - void* Alloc(unsigned int size) { - return allocator_.Alloc(size); - } + void* Alloc(uint32_t size) { return allocator_.Alloc(size); } // Gets the offset to a memory block given the base memory and the address. // It translates nullptr to FencedAllocator::kInvalidOffset. - unsigned int GetOffset(void* pointer) { - return allocator_.GetOffset(pointer); - } + uint32_t GetOffset(void* pointer) { return allocator_.GetOffset(pointer); } // Frees a block of memory. // @@ -87,7 +79,7 @@ // Parameters: // pointer: the pointer to the memory block to free. // token: the token value to wait for before re-using the memory. - void FreePendingToken(void* pointer, unsigned int token) { + void FreePendingToken(void* pointer, uint32_t token) { allocator_.FreePendingToken(pointer, token); } @@ -97,7 +89,7 @@ } // Gets the free size of the chunk. - unsigned int GetFreeSize() { return allocator_.GetFreeSize(); } + uint32_t GetFreeSize() { return allocator_.GetFreeSize(); } // Returns true if pointer is in the range of this block. bool IsInChunk(void* pointer) const { @@ -139,11 +131,9 @@ ~MappedMemoryManager(); - unsigned int chunk_size_multiple() const { - return chunk_size_multiple_; - } + uint32_t chunk_size_multiple() const { return chunk_size_multiple_; } - void set_chunk_size_multiple(unsigned int multiple) { + void set_chunk_size_multiple(uint32_t multiple) { DCHECK(base::bits::IsPowerOfTwo(multiple)); DCHECK_GE(multiple, FencedAllocator::kAllocAlignment); chunk_size_multiple_ = multiple; @@ -164,8 +154,7 @@ // shm_offset: pointer to variable to receive the shared memory offset. // Returns: // pointer to allocated block of memory. nullptr if failure. - void* Alloc( - unsigned int size, int32_t* shm_id, unsigned int* shm_offset); + void* Alloc(uint32_t size, int32_t* shm_id, uint32_t* shm_offset); // Frees a block of memory. // @@ -215,7 +204,7 @@ typedef std::vector<std::unique_ptr<MemoryChunk>> MemoryChunkVector; // size a chunk is rounded up to. - unsigned int chunk_size_multiple_; + uint32_t chunk_size_multiple_; CommandBufferHelper* helper_; MemoryChunkVector chunks_; size_t allocated_memory_;
diff --git a/gpu/command_buffer/client/ring_buffer.cc b/gpu/command_buffer/client/ring_buffer.cc index 1b080ec5f..00670464 100644 --- a/gpu/command_buffer/client/ring_buffer.cc +++ b/gpu/command_buffer/client/ring_buffer.cc
@@ -16,14 +16,14 @@ namespace gpu { -RingBuffer::RingBuffer(unsigned int alignment, +RingBuffer::RingBuffer(uint32_t alignment, Offset base_offset, - size_t size, + uint32_t size, CommandBufferHelper* helper, void* base) : helper_(helper), base_offset_(base_offset), - size_(base::checked_cast<unsigned int>(size)), + size_(size), alignment_(alignment), base_(static_cast<int8_t*>(base) - base_offset) {} @@ -53,7 +53,7 @@ blocks_.pop_front(); } -void* RingBuffer::Alloc(unsigned int size) { +void* RingBuffer::Alloc(uint32_t size) { DCHECK_LE(size, size_) << "attempt to allocate more than maximum memory"; // Similarly to malloc, an allocation of 0 allocates at least 1 byte, to // return different pointers every time. @@ -87,8 +87,7 @@ return GetPointer(offset + base_offset_); } -void RingBuffer::FreePendingToken(void* pointer, - unsigned int token) { +void RingBuffer::FreePendingToken(void* pointer, uint32_t token) { Offset offset = GetOffset(pointer); offset -= base_offset_; DCHECK(!blocks_.empty()) << "no allocations to free"; @@ -150,13 +149,13 @@ NOTREACHED() << "attempt to discard non-existant block"; } -unsigned int RingBuffer::GetLargestFreeSizeNoWaiting() { - unsigned int size = GetLargestFreeSizeNoWaitingInternal(); +uint32_t RingBuffer::GetLargestFreeSizeNoWaiting() { + uint32_t size = GetLargestFreeSizeNoWaitingInternal(); DCHECK_EQ(size, RoundToAlignment(size)); return size; } -unsigned int RingBuffer::GetLargestFreeSizeNoWaitingInternal() { +uint32_t RingBuffer::GetLargestFreeSizeNoWaitingInternal() { while (!blocks_.empty()) { Block& block = blocks_.front(); if (!helper_->HasTokenPassed(block.token) || block.state == IN_USE) break; @@ -180,8 +179,8 @@ } } -unsigned int RingBuffer::GetTotalFreeSizeNoWaiting() { - unsigned int largest_free_size = GetLargestFreeSizeNoWaitingInternal(); +uint32_t RingBuffer::GetTotalFreeSizeNoWaiting() { + uint32_t largest_free_size = GetLargestFreeSizeNoWaitingInternal(); if (free_offset_ > in_use_offset_) { // It's free from free_offset_ to size_ and from 0 to in_use_offset_. return size_ - free_offset_ + in_use_offset_; @@ -190,7 +189,7 @@ } } -void RingBuffer::ShrinkLastBlock(unsigned int new_size) { +void RingBuffer::ShrinkLastBlock(uint32_t new_size) { if (blocks_.empty()) return; auto& block = blocks_.back();
diff --git a/gpu/command_buffer/client/ring_buffer.h b/gpu/command_buffer/client/ring_buffer.h index 3eac7f0..f026097 100644 --- a/gpu/command_buffer/client/ring_buffer.h +++ b/gpu/command_buffer/client/ring_buffer.h
@@ -22,7 +22,7 @@ // allocations must not be kept past new allocations. class GPU_EXPORT RingBuffer { public: - typedef unsigned int Offset; + typedef uint32_t Offset; // Creates a RingBuffer. // Parameters: @@ -31,9 +31,9 @@ // size: The size of the buffer in bytes. // helper: A CommandBufferHelper for dealing with tokens. // base: The physical address that corresponds to base_offset. - RingBuffer(unsigned int alignment, + RingBuffer(uint32_t alignment, Offset base_offset, - size_t size, + uint32_t size, CommandBufferHelper* helper, void* base); @@ -51,7 +51,7 @@ // // Returns: // the pointer to the allocated memory block. - void* Alloc(unsigned int size); + void* Alloc(uint32_t size); // Frees a block of memory, pending the passage of a token. That memory won't // be re-allocated until the token has passed through the command stream. @@ -61,7 +61,7 @@ // Parameters: // pointer: the pointer to the memory block to free. // token: the token value to wait for before re-using the memory. - void FreePendingToken(void* pointer, unsigned int token); + void FreePendingToken(void* pointer, uint32_t token); // Discards a block within the ring buffer. // @@ -70,15 +70,15 @@ void DiscardBlock(void* pointer); // Gets the size of the largest free block that is available without waiting. - unsigned int GetLargestFreeSizeNoWaiting(); + uint32_t GetLargestFreeSizeNoWaiting(); // Gets the total size of all free blocks that are available without waiting. - unsigned int GetTotalFreeSizeNoWaiting(); + uint32_t GetTotalFreeSizeNoWaiting(); // Gets the size of the largest free block that can be allocated if the // caller can wait. Allocating a block of this size will succeed, but may // block. - unsigned int GetLargestFreeOrPendingSize() { + uint32_t GetLargestFreeOrPendingSize() { // If size_ is not a multiple of alignment_, then trying to allocate it will // cause us to try to allocate more than we actually can due to rounding up. // So, round down here. @@ -86,9 +86,9 @@ } // Total size minus usable size. - unsigned int GetUsedSize() { return size_ - GetLargestFreeSizeNoWaiting(); } + uint32_t GetUsedSize() { return size_ - GetLargestFreeSizeNoWaiting(); } - unsigned int NumUsedBlocks() const { return num_used_blocks_; } + uint32_t NumUsedBlocks() const { return num_used_blocks_; } // Gets a pointer to a memory block given the base memory and the offset. void* GetPointer(RingBuffer::Offset offset) const { @@ -101,13 +101,13 @@ } // Rounds the given size to the alignment in use. - unsigned int RoundToAlignment(unsigned int size) { + uint32_t RoundToAlignment(uint32_t size) { return (size + alignment_ - 1) & ~(alignment_ - 1); } // Shrinks the last block. new_size must be smaller than the current size // and the block must still be in use in order to shrink. - void ShrinkLastBlock(unsigned int new_size); + void ShrinkLastBlock(uint32_t new_size); private: enum State { @@ -117,23 +117,19 @@ }; // Book-keeping sturcture that describes a block of memory. struct Block { - Block(Offset _offset, unsigned int _size, State _state) - : offset(_offset), - size(_size), - token(0), - state(_state) { - } + Block(Offset _offset, uint32_t _size, State _state) + : offset(_offset), size(_size), token(0), state(_state) {} Offset offset; - unsigned int size; - unsigned int token; // token to wait for. + uint32_t size; + uint32_t token; // token to wait for. State state; }; using Container = base::circular_deque<Block>; - using BlockIndex = unsigned int; + using BlockIndex = uint32_t; void FreeOldestBlock(); - unsigned int GetLargestFreeSizeNoWaitingInternal(); + uint32_t GetLargestFreeSizeNoWaitingInternal(); CommandBufferHelper* helper_; @@ -154,10 +150,10 @@ Offset in_use_offset_ = 0; // Alignment for allocations. - unsigned int alignment_; + uint32_t alignment_; // Number of blocks in |blocks_| that are in the IN_USE state. - unsigned int num_used_blocks_ = 0; + uint32_t num_used_blocks_ = 0; // The physical address that corresponds to base_offset. void* base_;
diff --git a/gpu/command_buffer/client/vertex_array_object_manager.cc b/gpu/command_buffer/client/vertex_array_object_manager.cc index b8049b57..f0d0ab8 100644 --- a/gpu/command_buffer/client/vertex_array_object_manager.cc +++ b/gpu/command_buffer/client/vertex_array_object_manager.cc
@@ -16,7 +16,8 @@ namespace gpu { namespace gles2 { -static GLsizei RoundUpToMultipleOf4(GLsizei size) { +template <typename T> +static T RoundUpToMultipleOf4(T size) { return (size + 3) & ~3; } @@ -505,20 +506,27 @@ return false; } *simulated = true; - GLsizei total_size = 0; + base::CheckedNumeric<GLsizei> checked_total_size = 0; // Compute the size of the buffer we need. const VertexArrayObject::VertexAttribs& vertex_attribs = bound_vertex_array_object_->vertex_attribs(); for (GLuint ii = 0; ii < vertex_attribs.size(); ++ii) { const VertexArrayObject::VertexAttrib& attrib = vertex_attribs[ii]; if (attrib.IsClientSide() && attrib.enabled()) { - size_t bytes_per_element = + uint32_t bytes_per_element = GLES2Util::GetGroupSizeForBufferType(attrib.size(), attrib.type()); GLsizei elements = (primcount && attrib.divisor() > 0) ? ((primcount - 1) / attrib.divisor() + 1) : num_elements; - total_size += RoundUpToMultipleOf4(bytes_per_element * elements); + checked_total_size += + RoundUpToMultipleOf4(base::CheckMul(bytes_per_element, elements)); } } + GLsizei total_size = 0; + if (!checked_total_size.AssignIfValid(&total_size)) { + gl->SetGLError(GL_INVALID_OPERATION, function_name, + "size overflow for client side arrays"); + return false; + } gl_helper->BindBuffer(GL_ARRAY_BUFFER, array_buffer_id_); array_buffer_offset_ = 0; if (total_size > array_buffer_size_) { @@ -528,7 +536,7 @@ for (GLuint ii = 0; ii < vertex_attribs.size(); ++ii) { const VertexArrayObject::VertexAttrib& attrib = vertex_attribs[ii]; if (attrib.IsClientSide() && attrib.enabled()) { - size_t bytes_per_element = + uint32_t bytes_per_element = GLES2Util::GetGroupSizeForBufferType(attrib.size(), attrib.type()); GLsizei real_stride = attrib.stride() ? attrib.stride() : static_cast<GLsizei>(bytes_per_element); @@ -612,8 +620,14 @@ break; } gl_helper->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_array_buffer_id_); - GLsizei bytes_per_element = GLES2Util::GetGLTypeSizeForBuffers(type); - GLsizei bytes_needed = bytes_per_element * count; + uint32_t bytes_per_element = GLES2Util::GetGLTypeSizeForBuffers(type); + GLsizei bytes_needed = 0; + if (!base::CheckMul(bytes_per_element, count) + .AssignIfValid(&bytes_needed)) { + gl->SetGLError(GL_INVALID_OPERATION, function_name, + "size overflow for client side index arrays"); + return false; + } if (bytes_needed > element_array_buffer_size_) { element_array_buffer_size_ = bytes_needed; gl->BufferDataHelper(GL_ELEMENT_ARRAY_BUFFER, bytes_needed, nullptr,
diff --git a/gpu/command_buffer/common/buffer.cc b/gpu/command_buffer/common/buffer.cc index 40e7498..f972822 100644 --- a/gpu/command_buffer/common/buffer.cc +++ b/gpu/command_buffer/common/buffer.cc
@@ -33,7 +33,7 @@ return base::UnguessableToken(); } -MemoryBufferBacking::MemoryBufferBacking(size_t size) +MemoryBufferBacking::MemoryBufferBacking(uint32_t size) : memory_(new char[size]), size_(size) {} MemoryBufferBacking::~MemoryBufferBacking() = default; @@ -42,7 +42,7 @@ return memory_.get(); } -size_t MemoryBufferBacking::GetSize() const { +uint32_t MemoryBufferBacking::GetSize() const { return size_; } @@ -52,6 +52,7 @@ : shared_memory_region_(std::move(shared_memory_region)), shared_memory_mapping_(std::move(shared_memory_mapping)) { DCHECK_EQ(shared_memory_region_.GetGUID(), shared_memory_mapping_.guid()); + DCHECK_LE(shared_memory_mapping_.size(), static_cast<size_t>(UINT32_MAX)); } SharedMemoryBufferBacking::~SharedMemoryBufferBacking() = default; @@ -69,8 +70,8 @@ return shared_memory_mapping_.memory(); } -size_t SharedMemoryBufferBacking::GetSize() const { - return shared_memory_mapping_.size(); +uint32_t SharedMemoryBufferBacking::GetSize() const { + return static_cast<uint32_t>(shared_memory_mapping_.size()); } Buffer::Buffer(std::unique_ptr<BufferBacking> backing)
diff --git a/gpu/command_buffer/common/buffer.h b/gpu/command_buffer/common/buffer.h index 095d826..21e4055 100644 --- a/gpu/command_buffer/common/buffer.h +++ b/gpu/command_buffer/common/buffer.h
@@ -24,19 +24,19 @@ virtual const base::UnsafeSharedMemoryRegion& shared_memory_region() const; virtual base::UnguessableToken GetGUID() const; virtual void* GetMemory() const = 0; - virtual size_t GetSize() const = 0; + virtual uint32_t GetSize() const = 0; }; class GPU_EXPORT MemoryBufferBacking : public BufferBacking { public: - explicit MemoryBufferBacking(size_t size); + explicit MemoryBufferBacking(uint32_t size); ~MemoryBufferBacking() override; void* GetMemory() const override; - size_t GetSize() const override; + uint32_t GetSize() const override; private: std::unique_ptr<char[]> memory_; - size_t size_; + uint32_t size_; DISALLOW_COPY_AND_ASSIGN(MemoryBufferBacking); }; @@ -50,7 +50,7 @@ const base::UnsafeSharedMemoryRegion& shared_memory_region() const override; base::UnguessableToken GetGUID() const override; void* GetMemory() const override; - size_t GetSize() const override; + uint32_t GetSize() const override; private: base::UnsafeSharedMemoryRegion shared_memory_region_; @@ -65,7 +65,7 @@ BufferBacking* backing() const { return backing_.get(); } void* memory() const { return memory_; } - size_t size() const { return size_; } + uint32_t size() const { return size_; } // Returns nullptr if the address overflows the memory. void* GetDataAddress(uint32_t data_offset, uint32_t data_size) const; @@ -82,7 +82,7 @@ std::unique_ptr<BufferBacking> backing_; void* memory_; - size_t size_; + uint32_t size_; DISALLOW_COPY_AND_ASSIGN(Buffer); }; @@ -100,7 +100,7 @@ std::move(shared_memory_region), std::move(shared_memory_mapping))); } -static inline scoped_refptr<Buffer> MakeMemoryBuffer(size_t size) { +static inline scoped_refptr<Buffer> MakeMemoryBuffer(uint32_t size) { return base::MakeRefCounted<Buffer>( std::make_unique<MemoryBufferBacking>(size)); }
diff --git a/gpu/command_buffer/common/discardable_handle_unittest.cc b/gpu/command_buffer/common/discardable_handle_unittest.cc index 3b5402b..7738027 100644 --- a/gpu/command_buffer/common/discardable_handle_unittest.cc +++ b/gpu/command_buffer/common/discardable_handle_unittest.cc
@@ -9,13 +9,9 @@ namespace gpu { namespace { -scoped_refptr<Buffer> MakeBufferForTesting(size_t num_handles) { - size_t size = sizeof(base::subtle::Atomic32) * num_handles; - base::UnsafeSharedMemoryRegion shmem_region = - base::UnsafeSharedMemoryRegion::Create(size); - base::WritableSharedMemoryMapping shmem_mapping = shmem_region.Map(); - return MakeBufferFromSharedMemory(std::move(shmem_region), - std::move(shmem_mapping)); +scoped_refptr<Buffer> MakeBufferForTesting(uint32_t num_handles) { + uint32_t size = sizeof(base::subtle::Atomic32) * num_handles; + return MakeMemoryBuffer(size); } } // namespace
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc index 7c7a455..8cc28ef 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils.cc +++ b/gpu/command_buffer/common/gles2_cmd_utils.cc
@@ -515,7 +515,7 @@ namespace { // Return the number of bytes per element, based on the element type. -int BytesPerElement(int type) { +uint32_t BytesPerElement(int type) { switch (type) { case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: return 8; @@ -546,7 +546,7 @@ } // anonymous namespace // Return the number of elements per group of a specified format. -int GLES2Util::ElementsPerGroup(int format, int type) { +uint32_t GLES2Util::ElementsPerGroup(int format, int type) { switch (type) { case GL_UNSIGNED_SHORT_5_6_5: case GL_UNSIGNED_SHORT_4_4_4_4: @@ -592,11 +592,11 @@ } uint32_t GLES2Util::ComputeImageGroupSize(int format, int type) { - int bytes_per_element = BytesPerElement(type); - DCHECK_GE(8, bytes_per_element); - int elements_per_group = ElementsPerGroup(format, type); - DCHECK_GE(4, elements_per_group); - return bytes_per_element * elements_per_group; + uint32_t bytes_per_element = BytesPerElement(type); + DCHECK_GE(8u, bytes_per_element); + uint32_t elements_per_group = ElementsPerGroup(format, type); + DCHECK_GE(4u, elements_per_group); + return bytes_per_element * elements_per_group; } bool GLES2Util::ComputeImageRowSizeHelper(int width, @@ -744,7 +744,7 @@ return true; } -size_t GLES2Util::RenderbufferBytesPerPixel(int format) { +uint32_t GLES2Util::RenderbufferBytesPerPixel(int format) { switch (format) { case GL_STENCIL_INDEX8: return 1; @@ -897,11 +897,11 @@ } } -size_t GLES2Util::GetGLTypeSizeForTextures(uint32_t type) { - return static_cast<size_t>(BytesPerElement(type)); +uint32_t GLES2Util::GetGLTypeSizeForTextures(uint32_t type) { + return BytesPerElement(type); } -size_t GLES2Util::GetGLTypeSizeForBuffers(uint32_t type) { +uint32_t GLES2Util::GetGLTypeSizeForBuffers(uint32_t type) { switch (type) { case GL_BYTE: return sizeof(GLbyte); // NOLINT @@ -930,8 +930,9 @@ } } -size_t GLES2Util::GetGroupSizeForBufferType(uint32_t count, uint32_t type) { - size_t type_size = GetGLTypeSizeForBuffers(type); +uint32_t GLES2Util::GetGroupSizeForBufferType(uint32_t count, uint32_t type) { + DCHECK_LE(count, 4u); + uint32_t type_size = GetGLTypeSizeForBuffers(type); // For packed types, group size equals to the type size. if (type == GL_INT_2_10_10_10_REV || type == GL_UNSIGNED_INT_2_10_10_10_REV) { DCHECK_EQ(4u, count); @@ -939,7 +940,8 @@ } return type_size * count; } -size_t GLES2Util::GetComponentCountForGLTransformType(uint32_t type) { + +uint32_t GLES2Util::GetComponentCountForGLTransformType(uint32_t type) { switch (type) { case GL_TRANSLATE_X_CHROMIUM: case GL_TRANSLATE_Y_CHROMIUM: @@ -958,7 +960,8 @@ return 0; } } -size_t GLES2Util::GetCoefficientCountForGLPathFragmentInputGenMode( + +uint32_t GLES2Util::GetCoefficientCountForGLPathFragmentInputGenMode( uint32_t gen_mode) { switch (gen_mode) { case GL_EYE_LINEAR_CHROMIUM: @@ -973,7 +976,7 @@ } } -size_t GLES2Util::GetGLTypeSizeForPathCoordType(uint32_t type) { +uint32_t GLES2Util::GetGLTypeSizeForPathCoordType(uint32_t type) { switch (type) { case GL_BYTE: return sizeof(GLbyte); // NOLINT @@ -990,7 +993,7 @@ } } -size_t GLES2Util::GetGLTypeSizeForGLPathNameType(uint32_t type) { +uint32_t GLES2Util::GetGLTypeSizeForGLPathNameType(uint32_t type) { switch (type) { case GL_BYTE: return sizeof(GLbyte); // NOLINT @@ -1627,7 +1630,7 @@ base_name_ = name.substr(0, open_pos); } -size_t GLES2Util::CalcClearBufferivDataCount(int buffer) { +uint32_t GLES2Util::CalcClearBufferivDataCount(int buffer) { switch (buffer) { case GL_COLOR: return 4; @@ -1638,7 +1641,7 @@ } } -size_t GLES2Util::CalcClearBufferfvDataCount(int buffer) { +uint32_t GLES2Util::CalcClearBufferfvDataCount(int buffer) { switch (buffer) { case GL_COLOR: return 4; @@ -1649,7 +1652,7 @@ } } -size_t GLES2Util::CalcClearBufferuivDataCount(int buffer) { +uint32_t GLES2Util::CalcClearBufferuivDataCount(int buffer) { switch (buffer) { case GL_COLOR: return 4;
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.h b/gpu/command_buffer/common/gles2_cmd_utils.h index d2b5c2ce..7346133 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils.h +++ b/gpu/command_buffer/common/gles2_cmd_utils.h
@@ -143,7 +143,7 @@ // function is called. If 0 is returned the id is invalid. int GLGetNumValuesReturned(int id) const; - static int ElementsPerGroup(int format, int type); + static uint32_t ElementsPerGroup(int format, int type); // Computes the size of a single group of elements from a format and type pair static uint32_t ComputeImageGroupSize(int format, int type); @@ -171,7 +171,7 @@ uint32_t* opt_padded_row_size, uint32_t* opt_skip_size, uint32_t* opt_padding); - static size_t RenderbufferBytesPerPixel(int format); + static uint32_t RenderbufferBytesPerPixel(int format); static uint8_t StencilBitsPerPixel(int format); @@ -182,22 +182,23 @@ // For example, GL_FLOAT_MAT3 returns 9. static uint32_t GetElementCountForUniformType(int type); - static size_t GetGLTypeSizeForTextures(uint32_t type); + static uint32_t GetGLTypeSizeForTextures(uint32_t type); - static size_t GetGLTypeSizeForBuffers(uint32_t type); + static uint32_t GetGLTypeSizeForBuffers(uint32_t type); - static size_t GetGroupSizeForBufferType(uint32_t count, uint32_t type); + static uint32_t GetGroupSizeForBufferType(uint32_t count, uint32_t type); - static size_t GetGLTypeSizeForPathCoordType(uint32_t type); + static uint32_t GetComponentCountForGLTransformType(uint32_t type); + + static uint32_t GetCoefficientCountForGLPathFragmentInputGenMode( + uint32_t gen_mode); + + static uint32_t GetGLTypeSizeForPathCoordType(uint32_t type); + + static uint32_t GetGLTypeSizeForGLPathNameType(uint32_t type); static uint32_t GLErrorToErrorBit(uint32_t gl_error); - static size_t GetComponentCountForGLTransformType(uint32_t type); - static size_t GetGLTypeSizeForGLPathNameType(uint32_t type); - - static size_t GetCoefficientCountForGLPathFragmentInputGenMode( - uint32_t gen_mode); - static uint32_t GLErrorBitToGLError(uint32_t error_bit); static uint32_t IndexToGLFaceTarget(int index); @@ -234,9 +235,9 @@ static std::string GetStringBool(uint32_t value); static std::string GetStringError(uint32_t value); - static size_t CalcClearBufferivDataCount(int buffer); - static size_t CalcClearBufferfvDataCount(int buffer); - static size_t CalcClearBufferuivDataCount(int buffer); + static uint32_t CalcClearBufferivDataCount(int buffer); + static uint32_t CalcClearBufferfvDataCount(int buffer); + static uint32_t CalcClearBufferuivDataCount(int buffer); static void MapUint64ToTwoUint32( uint64_t v64, uint32_t* v32_0, uint32_t* v32_1);
diff --git a/gpu/command_buffer/service/command_buffer_service.cc b/gpu/command_buffer/service/command_buffer_service.cc index a728519..89b0e1b 100644 --- a/gpu/command_buffer/service/command_buffer_service.cc +++ b/gpu/command_buffer/service/command_buffer_service.cc
@@ -106,7 +106,7 @@ // This means ring_buffer_ can be nullptr. ring_buffer_ = GetTransferBuffer(transfer_buffer_id); if (ring_buffer_) { - int32_t size = ring_buffer_->size(); + uint32_t size = ring_buffer_->size(); volatile void* memory = ring_buffer_->memory(); // check proper alignments. DCHECK_EQ(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index fb7891a0..164f321 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -12236,7 +12236,7 @@ GL_INVALID_VALUE, "glVertexAttribIPointer", "offset < 0"); return error::kNoError; } - GLsizei type_size = GLES2Util::GetGLTypeSizeForBuffers(type); + uint32_t type_size = GLES2Util::GetGLTypeSizeForBuffers(type); // type_size must be a power of two to use & as optimized modulo. DCHECK(GLES2Util::IsPOT(type_size)); if (offset & (type_size - 1)) { @@ -12256,7 +12256,8 @@ SHADER_VARIABLE_INT : SHADER_VARIABLE_UINT; state_.vertex_attrib_manager->UpdateAttribBaseTypeAndMask(indx, base_type); - GLsizei group_size = GLES2Util::GetGroupSizeForBufferType(size, type); + uint32_t group_size = GLES2Util::GetGroupSizeForBufferType(size, type); + DCHECK_LE(group_size, static_cast<uint32_t>(INT_MAX)); state_.vertex_attrib_manager ->SetAttribInfo(indx, state_.bound_array_buffer.get(), @@ -12327,7 +12328,7 @@ GL_INVALID_VALUE, "glVertexAttribPointer", "offset < 0"); return error::kNoError; } - GLsizei type_size = GLES2Util::GetGLTypeSizeForBuffers(type); + uint32_t type_size = GLES2Util::GetGLTypeSizeForBuffers(type); // type_size must be a power of two to use & as optimized modulo. DCHECK(GLES2Util::IsPOT(type_size)); if (offset & (type_size - 1)) { @@ -12346,7 +12347,8 @@ state_.vertex_attrib_manager->UpdateAttribBaseTypeAndMask( indx, SHADER_VARIABLE_FLOAT); - GLsizei group_size = GLES2Util::GetGroupSizeForBufferType(size, type); + uint32_t group_size = GLES2Util::GetGroupSizeForBufferType(size, type); + DCHECK_LE(group_size, static_cast<uint32_t>(INT_MAX)); state_.vertex_attrib_manager ->SetAttribInfo(indx, state_.bound_array_buffer.get(),
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc index 68b78db3..7d96522c6 100644 --- a/gpu/command_buffer/service/texture_manager.cc +++ b/gpu/command_buffer/service/texture_manager.cc
@@ -795,8 +795,7 @@ DCHECK(signature); DCHECK_GE(level, 0); size_t face_index = GLES2Util::GLTargetToFaceIndex(target); - DCHECK_LT(static_cast<size_t>(face_index), - face_infos_.size()); + DCHECK_LT(face_index, face_infos_.size()); DCHECK_LT(static_cast<size_t>(level), face_infos_[face_index].level_infos.size()); @@ -1018,8 +1017,7 @@ const gfx::Rect& cleared_rect) { DCHECK_GE(level, 0); size_t face_index = GLES2Util::GLTargetToFaceIndex(target); - DCHECK_LT(static_cast<size_t>(face_index), - face_infos_.size()); + DCHECK_LT(face_index, face_infos_.size()); DCHECK_LT(static_cast<size_t>(level), face_infos_[face_index].level_infos.size()); Texture::LevelInfo& info = @@ -1031,7 +1029,7 @@ void Texture::SetLevelCleared(GLenum target, GLint level, bool cleared) { DCHECK_GE(level, 0); size_t face_index = GLES2Util::GLTargetToFaceIndex(target); - DCHECK_LT(static_cast<size_t>(face_index), face_infos_.size()); + DCHECK_LT(face_index, face_infos_.size()); DCHECK_LT(static_cast<size_t>(level), face_infos_[face_index].level_infos.size()); Texture::LevelInfo& info = face_infos_[face_index].level_infos[level]; @@ -1199,8 +1197,7 @@ const gfx::Rect& cleared_rect) { DCHECK_GE(level, 0); size_t face_index = GLES2Util::GLTargetToFaceIndex(target); - DCHECK_LT(static_cast<size_t>(face_index), - face_infos_.size()); + DCHECK_LT(face_index, face_infos_.size()); DCHECK_LT(static_cast<size_t>(level), face_infos_[face_index].level_infos.size()); DCHECK_GE(width, 0); @@ -1304,8 +1301,7 @@ void Texture::MarkLevelAsInternalWorkaround(GLenum target, GLint level) { DCHECK_GE(level, 0); size_t face_index = GLES2Util::GLTargetToFaceIndex(target); - DCHECK_LT(static_cast<size_t>(face_index), - face_infos_.size()); + DCHECK_LT(face_index, face_infos_.size()); DCHECK_LT(static_cast<size_t>(level), face_infos_[face_index].level_infos.size()); Texture::LevelInfo& info = @@ -1786,7 +1782,7 @@ DCHECK(!stream_texture_image || stream_texture_image == image); DCHECK_GE(level, 0); size_t face_index = GLES2Util::GLTargetToFaceIndex(target); - DCHECK_LT(static_cast<size_t>(face_index), face_infos_.size()); + DCHECK_LT(face_index, face_infos_.size()); DCHECK_LT(static_cast<size_t>(level), face_infos_[face_index].level_infos.size()); Texture::LevelInfo& info = face_infos_[face_index].level_infos[level]; @@ -1821,7 +1817,7 @@ void Texture::SetLevelImageState(GLenum target, GLint level, ImageState state) { DCHECK_GE(level, 0); size_t face_index = GLES2Util::GLTargetToFaceIndex(target); - DCHECK_LT(static_cast<size_t>(face_index), face_infos_.size()); + DCHECK_LT(face_index, face_infos_.size()); DCHECK_LT(static_cast<size_t>(level), face_infos_[face_index].level_infos.size()); Texture::LevelInfo& info = face_infos_[face_index].level_infos[level]; @@ -2718,7 +2714,7 @@ "pixel unpack buffer is not large enough"); return false; } - size_t type_size = GLES2Util::GetGLTypeSizeForTextures(args.type); + uint32_t type_size = GLES2Util::GetGLTypeSizeForTextures(args.type); DCHECK_LT(0u, type_size); if (offset % type_size != 0) { ERRORSTATE_SET_GL_ERROR( @@ -3015,7 +3011,7 @@ "pixel unpack buffer is not large enough"); return false; } - size_t type_size = GLES2Util::GetGLTypeSizeForTextures(args.type); + uint32_t type_size = GLES2Util::GetGLTypeSizeForTextures(args.type); DCHECK_LT(0u, type_size); if (offset % type_size != 0) { ERRORSTATE_SET_GL_ERROR(
diff --git a/gpu/command_buffer/tests/fuzzer_main.cc b/gpu/command_buffer/tests/fuzzer_main.cc index 6b818b8..6e4e7f8 100644 --- a/gpu/command_buffer/tests/fuzzer_main.cc +++ b/gpu/command_buffer/tests/fuzzer_main.cc
@@ -441,7 +441,7 @@ if (!InitDecoder()) return; - size_t buffer_size = buffer_->size(); + uint32_t buffer_size = buffer_->size(); CHECK_LE(padded_size, buffer_size); command_buffer_->SetGetBuffer(buffer_id_); auto* memory = static_cast<char*>(buffer_->memory());
diff --git a/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc b/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc index 2288f1b..78255a9 100644 --- a/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc +++ b/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc
@@ -269,7 +269,7 @@ GLsizei height, uint8_t* expected_color, uint8_t* expected_mask) { - const int src_channel_count = gles2::GLES2Util::ElementsPerGroup( + const uint32_t src_channel_count = gles2::GLES2Util::ElementsPerGroup( src_format_type.format, src_format_type.type); uint8_t color[4] = {1u, 63u, 127u, 255u}; getExpectedColor(src_format_type.internal_format, @@ -278,9 +278,9 @@ if (src_format_type.type == GL_UNSIGNED_BYTE) { std::unique_ptr<uint8_t[]> pixels( new uint8_t[width * height * src_channel_count]); - for (int i = 0; i < width * height * src_channel_count; + for (uint32_t i = 0; i < width * height * src_channel_count; i += src_channel_count) { - for (int j = 0; j < src_channel_count; ++j) + for (uint32_t j = 0; j < src_channel_count; ++j) pixels[i + j] = color[j]; } return pixels; @@ -290,9 +290,9 @@ new uint8_t[width * height * src_channel_count * sizeof(uint16_t)]); uint16_t* pixels = reinterpret_cast<uint16_t*>(data.get()); int16_t flip_sign = -1; - for (int i = 0; i < width * height * src_channel_count; + for (uint32_t i = 0; i < width * height * src_channel_count; i += src_channel_count) { - for (int j = 0; j < src_channel_count; ++j) { + for (uint32_t j = 0; j < src_channel_count; ++j) { // Introduce an offset to the value to check. Expected value should be // the same as without the offset. flip_sign *= -1;
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.cc b/gpu/ipc/client/command_buffer_proxy_impl.cc index 4eb8b698..296d9255 100644 --- a/gpu/ipc/client/command_buffer_proxy_impl.cc +++ b/gpu/ipc/client/command_buffer_proxy_impl.cc
@@ -373,6 +373,7 @@ OnClientError(gpu::error::kOutOfBounds); return nullptr; } + DCHECK_LE(shared_memory_mapping.size(), static_cast<size_t>(UINT32_MAX)); if (last_state_.error == gpu::error::kNoError) { base::UnsafeSharedMemoryRegion region =
diff --git a/gpu/ipc/service/command_buffer_stub.cc b/gpu/ipc/service/command_buffer_stub.cc index d1bc3cf..6ff0dc2 100644 --- a/gpu/ipc/service/command_buffer_stub.cc +++ b/gpu/ipc/service/command_buffer_stub.cc
@@ -567,7 +567,7 @@ // Map the shared memory into this process. base::WritableSharedMemoryMapping mapping = transfer_buffer.Map(); - if (!mapping.IsValid()) { + if (!mapping.IsValid() || (mapping.size() > UINT32_MAX)) { DVLOG(0) << "Failed to map shared memory."; return; }
diff --git a/ios/build/bots/scripts/xcodebuild_runner.py b/ios/build/bots/scripts/xcodebuild_runner.py index 3e9ee6b..47848fb2 100644 --- a/ios/build/bots/scripts/xcodebuild_runner.py +++ b/ios/build/bots/scripts/xcodebuild_runner.py
@@ -104,7 +104,10 @@ for action in root['Actions']: action_result = action['ActionResult'] - if action_result['TestsCount'] == 0 and action_result['ErrorCount'] != 0: + if ((action_result['TestsCount'] == 0 and + action_result['TestsFailedCount'] == 0 and + action_result['ErrorCount'] != 0) + or 'TestSummaryPath' not in action_result): test_results['failed']['TESTS_DID_NOT_START'] = [] else: summary_plist = os.path.join(os.path.dirname(plist_path), @@ -186,8 +189,9 @@ Returns: A node with filled required fields about egtests. """ + module = self.module_name + '_module' xctestrun_data = { - self.module_name + '_module': { + module: { 'IsAppHostedTestBundle': True, 'TestBundlePath': '__TESTHOST__%s' % self._xctest_path(), 'TestHostPath': '%s' % self.egtests_path, @@ -203,10 +207,10 @@ } if self.filter: if self.invert: - xctestrun_data[self.module_name + '_module'].update( + xctestrun_data[module].update( {'SkipTestIdentifiers': self.filter}) else: - xctestrun_data[self.module_name + '_module'].update( + xctestrun_data[module].update( {'OnlyTestIdentifiers': self.filter}) return xctestrun_data @@ -383,6 +387,10 @@ cmd_list = self._make_cmd_list_for_failed_tests( self.test_results['attempts'][-1]['failed'], outdir_attempt) + else: + # If tests did not start, re-run the same command + # but with different output folder. + cmd_list = cmd_list[:-2] + ['-resultBundlePath', outdir_attempt] # TODO(crbug.com/914878): add heartbeat logging to xcodebuild_runner. print 'Start test attempt #%d for command [%s]' % ( attempt, ' '.join(cmd_list))
diff --git a/ios/build/bots/scripts/xcodebuild_runner_test.py b/ios/build/bots/scripts/xcodebuild_runner_test.py index e484f0a..614c2183 100644 --- a/ios/build/bots/scripts/xcodebuild_runner_test.py +++ b/ios/build/bots/scripts/xcodebuild_runner_test.py
@@ -116,12 +116,12 @@ destination=destination, shards=3)) - @mock.patch('tempfile.mkstemp', autospec=True) @mock.patch('plistlib.writePlist', autospec=True) @mock.patch('os.path.exists', autospec=True) - def testFill_xctest_run(self, mock_path_exists, _, mock_tmpfile): + @mock.patch('os.path.join', autospec=True) + def testFill_xctest_run(self, mock_path_join, mock_path_exists, _): + mock_path_join.return_value = _XTEST_RUN mock_path_exists.return_value = True - mock_tmpfile.return_value = (1, _XTEST_RUN) destination = 'platform=iOS Simulator,OS=12.0,name=iPhone X' mock_egtest = mock.MagicMock(spec=xcodebuild_runner.EgtestsApp) launch_command = xcodebuild_runner.LaunchCommand(
diff --git a/ios/chrome/app/application_delegate/mock_tab_opener.h b/ios/chrome/app/application_delegate/mock_tab_opener.h index c019b6a6..0d6c15bd 100644 --- a/ios/chrome/app/application_delegate/mock_tab_opener.h +++ b/ios/chrome/app/application_delegate/mock_tab_opener.h
@@ -15,8 +15,9 @@ // -dismissModalsAndOpenSelectedTabInMode:withURL:transition:completion:. @interface MockTabOpener : NSObject<TabOpening> // Arguments for -// -dismissModalsAndOpenSelectedTabInMode:withURL:transition:completion:. +// -dismissModalsAndOpenSelectedTabInMode:withURL:virtualURL:transition:completion:. @property(nonatomic, readonly) GURL url; +@property(nonatomic, readonly) GURL virtualURL; @property(nonatomic, readonly) ApplicationMode applicationMode; @property(nonatomic, strong, readonly) void (^completionBlock)(void);
diff --git a/ios/chrome/app/application_delegate/mock_tab_opener.mm b/ios/chrome/app/application_delegate/mock_tab_opener.mm index 02ba520..2745500 100644 --- a/ios/chrome/app/application_delegate/mock_tab_opener.mm +++ b/ios/chrome/app/application_delegate/mock_tab_opener.mm
@@ -17,15 +17,18 @@ @implementation MockTabOpener @synthesize url = _url; +@synthesize virtualURL = _virtualURL; @synthesize applicationMode = _applicationMode; @synthesize completionBlock = _completionBlock; - (void)dismissModalsAndOpenSelectedTabInMode:(ApplicationMode)targetMode withURL:(const GURL&)url + virtualURL:(const GURL&)virtualURL dismissOmnibox:(BOOL)dismissOmnibox transition:(ui::PageTransition)transition completion:(ProceduralBlock)completion { _url = url; + _virtualURL = virtualURL; _applicationMode = targetMode; _completionBlock = [completion copy]; }
diff --git a/ios/chrome/app/application_delegate/tab_opening.h b/ios/chrome/app/application_delegate/tab_opening.h index e8233d5..18c621b8 100644 --- a/ios/chrome/app/application_delegate/tab_opening.h +++ b/ios/chrome/app/application_delegate/tab_opening.h
@@ -20,9 +20,11 @@ // Dismisses any modal view, excluding the omnibox if |dismissOmnibox| is NO, // then opens either a normal or incognito tab with |url|. After opening |url|, -// run completion |handler| if it is not nil. +// run completion |handler| if it is not nil. After Tab is opened the virtual +// URL is set to the pending navigation item. - (void)dismissModalsAndOpenSelectedTabInMode:(ApplicationMode)targetMode withURL:(const GURL&)url + virtualURL:(const GURL&)virtualURL dismissOmnibox:(BOOL)dismissOmnibox transition:(ui::PageTransition)transition completion:(ProceduralBlock)completion;
diff --git a/ios/chrome/app/application_delegate/url_opener.mm b/ios/chrome/app/application_delegate/url_opener.mm index d018eff..8d1fd5c6 100644 --- a/ios/chrome/app/application_delegate/url_opener.mm +++ b/ios/chrome/app/application_delegate/url_opener.mm
@@ -12,6 +12,7 @@ #import "ios/chrome/app/application_delegate/tab_opening.h" #include "ios/chrome/app/startup/chrome_app_startup_parameters.h" #import "ios/chrome/browser/chrome_url_util.h" +#include "url/gurl.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -67,6 +68,7 @@ ? ApplicationMode::INCOGNITO : ApplicationMode::NORMAL withURL:[params externalURL] + virtualURL:GURL::EmptyGURL() dismissOmnibox:[params postOpeningAction] != FOCUS_OMNIBOX transition:ui::PAGE_TRANSITION_LINK
diff --git a/ios/chrome/app/application_delegate/user_activity_handler.mm b/ios/chrome/app/application_delegate/user_activity_handler.mm index 0982f35..e7978db 100644 --- a/ios/chrome/app/application_delegate/user_activity_handler.mm +++ b/ios/chrome/app/application_delegate/user_activity_handler.mm
@@ -21,6 +21,7 @@ #include "ios/chrome/app/startup/chrome_app_startup_parameters.h" #include "ios/chrome/browser/app_startup_parameters.h" #include "ios/chrome/browser/chrome_url_constants.h" +#include "ios/chrome/browser/experimental_flags.h" #include "ios/chrome/browser/metrics/first_user_action_recorder.h" #import "ios/chrome/browser/tabs/legacy_tab_helper.h" #import "ios/chrome/browser/tabs/tab.h" @@ -110,7 +111,8 @@ webpageURL = [NSURL URLWithString:base::SysUTF8ToNSString(kChromeUINewTabURL)]; AppStartupParameters* startupParams = [[AppStartupParameters alloc] - initWithExternalURL:GURL(kChromeUINewTabURL)]; + initWithExternalURL:GURL(kChromeUINewTabURL) + completeURL:GURL(kChromeUINewTabURL)]; BOOL startupParamsSet = spotlight::SetStartupParametersForSpotlightAction( itemID, startupParams); if (!startupParamsSet) { @@ -139,7 +141,8 @@ isEqualToString:@"SearchInChromeIntent"]) { base::RecordAction(UserMetricsAction("IOSLaunchedBySearchInChromeIntent")); AppStartupParameters* startupParams = [[AppStartupParameters alloc] - initWithExternalURL:GURL(kChromeUINewTabURL)]; + initWithExternalURL:GURL(kChromeUINewTabURL) + completeURL:GURL(kChromeUINewTabURL)]; [startupParams setPostOpeningAction:FOCUS_OMNIBOX]; [startupInformation setStartupParameters:startupParams]; return YES; @@ -174,6 +177,7 @@ : ApplicationMode::NORMAL; [tabOpener dismissModalsAndOpenSelectedTabInMode:targetMode withURL:webpageGURL + virtualURL:GURL::EmptyGURL() dismissOmnibox:YES transition:ui::PAGE_TRANSITION_LINK completion:^{ @@ -189,7 +193,8 @@ if (![startupInformation startupParameters]) { AppStartupParameters* startupParams = - [[AppStartupParameters alloc] initWithExternalURL:webpageGURL]; + [[AppStartupParameters alloc] initWithExternalURL:webpageGURL + completeURL:webpageGURL]; [startupInformation setStartupParameters:startupParams]; } return YES; @@ -258,10 +263,24 @@ [[startupInformation startupParameters] launchInIncognito] ? ApplicationMode::INCOGNITO : ApplicationMode::NORMAL; + GURL URL; + GURL virtualURL; + if (base::FeatureList::IsEnabled( + experimental_flags::kExternalFilesLoadedInWebState)) { + // External URL will be loaded by WebState, which expects |completeURL|. + // Omnibox however suppose to display |externalURL|, which is used as + // virtual URL. + URL = startupInformation.startupParameters.completeURL; + virtualURL = startupInformation.startupParameters.externalURL; + } else { + // |externalURL| is rewritten to chrome:// URL, which is expected by + // ExternalFileController. ExternalFileController will be used to load + // file:// URL. TODO(crbug.com/913602): Remove this code. + URL = startupInformation.startupParameters.externalURL; + } [tabOpener dismissModalsAndOpenSelectedTabInMode:targetMode - withURL:[[startupInformation - startupParameters] - externalURL] + withURL:URL + virtualURL:virtualURL dismissOmnibox:[[startupInformation startupParameters] postOpeningAction] != @@ -282,7 +301,8 @@ return NO; AppStartupParameters* startupParams = [[AppStartupParameters alloc] - initWithExternalURL:GURL(kChromeUINewTabURL)]; + initWithExternalURL:GURL(kChromeUINewTabURL) + completeURL:GURL(kChromeUINewTabURL)]; if ([shortcutItem.type isEqualToString:kShortcutNewTab]) { base::RecordAction(UserMetricsAction("ApplicationShortcut.NewTabPressed"));
diff --git a/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm b/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm index e5781ee..56d0a58 100644 --- a/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm +++ b/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
@@ -12,6 +12,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" #include "base/test/scoped_command_line.h" +#include "base/test/scoped_feature_list.h" #include "components/handoff/handoff_utility.h" #include "ios/chrome/app/application_delegate/fake_startup_information.h" #include "ios/chrome/app/application_delegate/mock_tab_opener.h" @@ -24,6 +25,7 @@ #include "ios/chrome/browser/app_startup_parameters.h" #include "ios/chrome/browser/chrome_switches.h" #include "ios/chrome/browser/chrome_url_constants.h" +#include "ios/chrome/browser/experimental_flags.h" #import "ios/chrome/browser/tabs/legacy_tab_helper.h" #import "ios/chrome/browser/tabs/tab.h" #import "ios/chrome/browser/tabs/tab_model.h" @@ -131,6 +133,13 @@ #pragma mark - Test class. +// UserActivityHandlerTest is parameterized on this enum to test with +// enabled and disabled kExternalFilesLoadedInWebState feature flag. +enum class ExternalFilesLoadedInWebStateFeature { + Disabled = 0, + Enabled, +}; + // A block that takes as arguments the caller and the arguments from // UserActivityHandler +handleStartupParameters and returns nothing. typedef void (^startupParameterBlock)(id, @@ -141,8 +150,20 @@ // A block that takes a BOOL argument and returns nothing. typedef void (^conditionBlock)(BOOL); -class UserActivityHandlerTest : public PlatformTest { +class UserActivityHandlerTest + : public PlatformTest, + public testing::WithParamInterface<ExternalFilesLoadedInWebStateFeature> { protected: + UserActivityHandlerTest() { + if (GetParam() == ExternalFilesLoadedInWebStateFeature::Enabled) { + scoped_feature_list_.InitAndEnableFeature( + experimental_flags::kExternalFilesLoadedInWebState); + } else { + scoped_feature_list_.InitAndDisableFeature( + experimental_flags::kExternalFilesLoadedInWebState); + } + } + void swizzleHandleStartupParameters() { handle_startup_parameters_has_been_called_ = NO; swizzle_block_ = [^(id self) { @@ -185,16 +206,14 @@ startupParameterBlock swizzle_block_; conditionBlock completion_block_; __block BOOL handle_startup_parameters_has_been_called_; + base::test::ScopedFeatureList scoped_feature_list_; }; #pragma mark - Tests. -using UserActivityHandlerNoFixtureTest = PlatformTest; - // Tests that Chrome notifies the user if we are passing a correct // userActivityType. -TEST_F(UserActivityHandlerNoFixtureTest, - willContinueUserActivityCorrectActivity) { +TEST_P(UserActivityHandlerTest, WillContinueUserActivityCorrectActivity) { EXPECT_TRUE([UserActivityHandler willContinueUserActivityWithType:handoff::kChromeHandoffActivityType]); @@ -206,8 +225,7 @@ // Tests that Chrome does not notifies the user if we are passing an incorrect // userActivityType. -TEST_F(UserActivityHandlerNoFixtureTest, - willContinueUserActivityIncorrectActivity) { +TEST_P(UserActivityHandlerTest, WillContinueUserActivityIncorrectActivity) { EXPECT_FALSE([UserActivityHandler willContinueUserActivityWithType:[handoff::kChromeHandoffActivityType stringByAppendingString:@"test"]]); @@ -220,11 +238,9 @@ EXPECT_FALSE([UserActivityHandler willContinueUserActivityWithType:nil]); } -using UserActivityHandlerNoFixtureTest = PlatformTest; - // Tests that Chrome does not continue the activity is the activity type is // random. -TEST_F(UserActivityHandlerNoFixtureTest, continueUserActivityFromGarbage) { +TEST_P(UserActivityHandlerTest, ContinueUserActivityFromGarbage) { // Setup. NSString* handoffWithSuffix = [handoff::kChromeHandoffActivityType stringByAppendingString:@"test"]; @@ -257,7 +273,7 @@ // Tests that Chrome does not continue the activity if the webpage url is not // set. -TEST_F(UserActivityHandlerNoFixtureTest, continueUserActivityNoWebpage) { +TEST_P(UserActivityHandlerTest, ContinueUserActivityNoWebpage) { // Setup. NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:handoff::kChromeHandoffActivityType]; @@ -280,8 +296,8 @@ // Tests that Chrome does not continue the activity if the activity is a // Spotlight action of an unknown type. -TEST_F(UserActivityHandlerNoFixtureTest, - continueUserActivitySpotlightActionFromGarbage) { +TEST_P(UserActivityHandlerTest, + ContinueUserActivitySpotlightActionFromGarbage) { // Only test Spotlight if it is enabled and available on the device. if (!spotlight::IsSpotlightAvailable()) { return; @@ -319,7 +335,7 @@ // Tests that Chrome continues the activity if the application is in background // by saving the url to startupParameters. -TEST_F(UserActivityHandlerNoFixtureTest, continueUserActivityBackground) { +TEST_P(UserActivityHandlerTest, ContinueUserActivityBackground) { // Setup. NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:handoff::kChromeHandoffActivityType]; @@ -354,12 +370,12 @@ // Tests that Chrome continues the activity if the application is in foreground // by opening a new tab. -TEST_F(UserActivityHandlerNoFixtureTest, continueUserActivityForeground) { +TEST_P(UserActivityHandlerTest, ContinueUserActivityForeground) { // Setup. NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:handoff::kChromeHandoffActivityType]; - NSURL* nsurl = [NSURL URLWithString:@"http://www.google.com"]; - [userActivity setWebpageURL:nsurl]; + GURL gurl("http://www.google.com"); + [userActivity setWebpageURL:net::NSURLWithGURL(gurl)]; MockTabOpener* tabOpener = [[MockTabOpener alloc] init]; @@ -367,8 +383,8 @@ [OCMockObject mockForProtocol:@protocol(StartupInformation)]; [[[startupInformationMock stub] andReturnValue:@NO] isPresentingFirstRunUI]; - AppStartupParameters* startupParams = [[AppStartupParameters alloc] - initWithExternalURL:(GURL("http://www.google.com"))]; + AppStartupParameters* startupParams = + [[AppStartupParameters alloc] initWithExternalURL:gurl completeURL:gurl]; [[[startupInformationMock stub] andReturn:startupParams] startupParameters]; // Action. @@ -379,13 +395,14 @@ startupInformation:startupInformationMock]; // Test. - EXPECT_EQ(net::GURLWithNSURL(nsurl), [tabOpener url]); + EXPECT_EQ(gurl, tabOpener.url); + EXPECT_TRUE(tabOpener.virtualURL.is_empty()); EXPECT_TRUE(result); } // Tests that a new tab is created when application is started via Universal // Link. -TEST_F(UserActivityHandlerTest, continueUserActivityBrowsingWeb) { +TEST_P(UserActivityHandlerTest, ContinueUserActivityBrowsingWeb) { NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb]; // This URL is passed to application by iOS but is not used in this part @@ -418,7 +435,7 @@ // Tests that continueUserActivity sets startupParameters accordingly to the // Spotlight action used. -TEST_F(UserActivityHandlerTest, continueUserActivityShortcutActions) { +TEST_P(UserActivityHandlerTest, ContinueUserActivityShortcutActions) { // Only test Spotlight if it is enabled and available on the device. if (!spotlight::IsSpotlightAvailable()) { return; @@ -479,13 +496,16 @@ } } -// Tests that handleStartupParameters with a non-U2F url opens a new tab. -TEST_F(UserActivityHandlerNoFixtureTest, handleStartupParamsNonU2F) { +// Tests that handleStartupParameters with a file url. "external URL" gets +// rewritten to chrome://URL, while "complete URL" remains full local file URL. +TEST_P(UserActivityHandlerTest, HandleStartupParamsWithExternalFile) { // Setup. - GURL gurl("http://www.google.com"); + GURL externalURL("chrome://test.pdf"); + GURL completeURL("file://test.pdf"); AppStartupParameters* startupParams = - [[AppStartupParameters alloc] initWithExternalURL:gurl]; + [[AppStartupParameters alloc] initWithExternalURL:externalURL + completeURL:completeURL]; [startupParams setLaunchInIncognito:YES]; id startupInformationMock = @@ -509,12 +529,62 @@ // Tests. EXPECT_OCMOCK_VERIFY(startupInformationMock); - EXPECT_EQ(gurl, [tabOpener url]); + if (GetParam() == ExternalFilesLoadedInWebStateFeature::Enabled) { + // External file:// URL will be loaded by WebState, which expects complete + // file:// URL. chrome:// URL is expected to be displayed in the omnibox, + // and omnibox shows virtual URL. + EXPECT_EQ(completeURL, tabOpener.url); + EXPECT_EQ(externalURL, tabOpener.virtualURL); + } else { + // External file:// URL will be loaded by ExternalFileController, which + // expects chrome:// URL. + EXPECT_EQ(externalURL, tabOpener.url); + EXPECT_TRUE(tabOpener.virtualURL.is_empty()); + } + EXPECT_EQ(ApplicationMode::INCOGNITO, [tabOpener applicationMode]); +} + +// Tests that handleStartupParameters with a non-U2F url opens a new tab. +TEST_P(UserActivityHandlerTest, HandleStartupParamsNonU2F) { + // Setup. + GURL gurl("http://www.google.com"); + + AppStartupParameters* startupParams = + [[AppStartupParameters alloc] initWithExternalURL:gurl completeURL:gurl]; + [startupParams setLaunchInIncognito:YES]; + + id startupInformationMock = + [OCMockObject mockForProtocol:@protocol(StartupInformation)]; + [[[startupInformationMock stub] andReturnValue:@NO] isPresentingFirstRunUI]; + [[[startupInformationMock stub] andReturn:startupParams] startupParameters]; + [[startupInformationMock expect] setStartupParameters:nil]; + + MockTabOpener* tabOpener = [[MockTabOpener alloc] init]; + + // The test will fail is a method of this object is called. + id interfaceProviderMock = + [OCMockObject mockForProtocol:@protocol(BrowserInterfaceProvider)]; + + // Action. + [UserActivityHandler + handleStartupParametersWithTabOpener:tabOpener + startupInformation:startupInformationMock + interfaceProvider:interfaceProviderMock]; + [tabOpener completionBlock](); + + // Tests. + EXPECT_OCMOCK_VERIFY(startupInformationMock); + EXPECT_EQ(gurl, tabOpener.url); + if (GetParam() == ExternalFilesLoadedInWebStateFeature::Enabled) { + EXPECT_EQ(gurl, tabOpener.virtualURL); + } else { + EXPECT_TRUE(tabOpener.virtualURL.is_empty()); + } EXPECT_EQ(ApplicationMode::INCOGNITO, [tabOpener applicationMode]); } // Tests that handleStartupParameters with a U2F url opens in the correct tab. -TEST_F(UserActivityHandlerNoFixtureTest, handleStartupParamsU2F) { +TEST_P(UserActivityHandlerTest, HandleStartupParamsU2F) { // Setup. UserActivityHandlerTabModelMock* mockTabModel = [[UserActivityHandlerTabModelMock alloc] init]; @@ -527,7 +597,7 @@ GURL gurl(urlRepresentation); AppStartupParameters* startupParams = - [[AppStartupParameters alloc] initWithExternalURL:gurl]; + [[AppStartupParameters alloc] initWithExternalURL:gurl completeURL:gurl]; [startupParams setLaunchInIncognito:YES]; id startupInformationMock = @@ -551,12 +621,14 @@ // Tests. EXPECT_OCMOCK_VERIFY(startupInformationMock); - EXPECT_EQ(gurl, [tabMock url]); + EXPECT_EQ(gurl, tabMock.url); + EXPECT_TRUE(tabOpener.url.is_empty()); + EXPECT_TRUE(tabOpener.virtualURL.is_empty()); } // Tests that performActionForShortcutItem set startupParameters accordingly to // the shortcut used -TEST_F(UserActivityHandlerTest, performActionForShortcutItemWithRealShortcut) { +TEST_P(UserActivityHandlerTest, PerformActionForShortcutItemWithRealShortcut) { // Setup. GURL gurlNewTab("chrome://newtab/"); @@ -607,7 +679,7 @@ // Tests that performActionForShortcutItem just executes the completionHandler // with NO if the firstRunUI is present. -TEST_F(UserActivityHandlerTest, performActionForShortcutItemWithFirstRunUI) { +TEST_P(UserActivityHandlerTest, PerformActionForShortcutItemWithFirstRunUI) { // Setup. id startupInformationMock = [OCMockObject mockForProtocol:@protocol(StartupInformation)]; @@ -636,3 +708,9 @@ EXPECT_FALSE(completionHandlerArgument()); EXPECT_FALSE(getHandleStartupParametersHasBeenCalled()); } + +INSTANTIATE_TEST_CASE_P( + ProgrammaticUserActivityHandlerTest, + UserActivityHandlerTest, + ::testing::Values(ExternalFilesLoadedInWebStateFeature::Enabled, + ExternalFilesLoadedInWebStateFeature::Disabled));
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm index 942e2908..160471f 100644 --- a/ios/chrome/app/main_controller.mm +++ b/ios/chrome/app/main_controller.mm
@@ -149,6 +149,7 @@ #include "ios/web/net/request_tracker_factory_impl.h" #include "ios/web/net/request_tracker_impl.h" #include "ios/web/net/web_http_protocol_handler_delegate.h" +#import "ios/web/public/navigation_item.h" #import "ios/web/public/navigation_manager.h" #import "ios/web/public/web_state/web_state.h" #import "ios/web/public/web_view_creation_util.h" @@ -444,9 +445,11 @@ // out if it is showing, the target BVC will become active, and the new tab will // be shown. // If the current tab in |targetMode| is a NTP, it can be reused to open URL. -// |completion| is executed after the tab is opened. +// |completion| is executed after the tab is opened. After Tab is open the +// virtual URL is set to the pending navigation item. - (Tab*)openSelectedTabInMode:(ApplicationMode)targetMode withURL:(const GURL&)url + virtualURL:(const GURL&)virtualURL transition:(ui::PageTransition)transition completion:(ProceduralBlock)completion; // Checks the target BVC's current tab's URL. If this URL is chrome://newtab, @@ -822,7 +825,8 @@ if (_startupParameters) { [self dismissModalsAndOpenSelectedTabInMode:ApplicationMode::NORMAL - withURL:[_startupParameters externalURL] + withURL:_startupParameters.externalURL + virtualURL:GURL::EmptyGURL() dismissOmnibox:YES transition:ui::PAGE_TRANSITION_LINK completion:^{ @@ -1496,6 +1500,7 @@ if ([command fromChrome]) { [self dismissModalsAndOpenSelectedTabInMode:ApplicationMode::NORMAL withURL:[command URL] + virtualURL:GURL::EmptyGURL() dismissOmnibox:YES transition:ui::PAGE_TRANSITION_TYPED completion:nil]; @@ -1736,6 +1741,7 @@ ProceduralBlock completion = ^{ [self dismissModalsAndOpenSelectedTabInMode:ApplicationMode::NORMAL withURL:[command URL] + virtualURL:GURL::EmptyGURL() dismissOmnibox:YES transition:ui::PAGE_TRANSITION_TYPED completion:nil]; @@ -2225,6 +2231,7 @@ - (Tab*)openSelectedTabInMode:(ApplicationMode)targetMode withURL:(const GURL&)url + virtualURL:(const GURL&)virtualURL transition:(ui::PageTransition)transition completion:(ProceduralBlock)completion { id<BrowserInterface> targetInterface = @@ -2302,6 +2309,10 @@ }); } + if (!virtualURL.is_empty()) { + tab.webState->GetNavigationManager()->GetPendingItem()->SetVirtualURL( + virtualURL); + } return tab; } @@ -2427,17 +2438,21 @@ - (void)dismissModalsAndOpenSelectedTabInMode:(ApplicationMode)targetMode withURL:(const GURL&)url + virtualURL:(const GURL&)virtualURL dismissOmnibox:(BOOL)dismissOmnibox transition:(ui::PageTransition)transition completion:(ProceduralBlock)completion { GURL copyOfURL = url; - [self dismissModalDialogsWithCompletion:^{ - [self openSelectedTabInMode:targetMode - withURL:copyOfURL - transition:transition - completion:completion]; - } - dismissOmnibox:dismissOmnibox]; + GURL copyOfVirtualURL = virtualURL; + [self + dismissModalDialogsWithCompletion:^{ + [self openSelectedTabInMode:targetMode + withURL:copyOfURL + virtualURL:copyOfVirtualURL + transition:transition + completion:completion]; + } + dismissOmnibox:dismissOmnibox]; } - (void)openTabFromLaunchOptions:(NSDictionary*)launchOptions
diff --git a/ios/chrome/app/startup/chrome_app_startup_parameters.h b/ios/chrome/app/startup/chrome_app_startup_parameters.h index 1111fd68..ac70aec 100644 --- a/ios/chrome/app/startup/chrome_app_startup_parameters.h +++ b/ios/chrome/app/startup/chrome_app_startup_parameters.h
@@ -32,7 +32,8 @@ @interface ChromeAppStartupParameters : AppStartupParameters -- (instancetype)initWithExternalURL:(const GURL&)externalURL NS_UNAVAILABLE; +- (instancetype)initWithExternalURL:(const GURL&)externalURL + completeURL:(const GURL&)completeURL NS_UNAVAILABLE; - (instancetype)initWithExternalURL:(const GURL&)externalURL declaredSourceApp:(NSString*)declaredSourceApp
diff --git a/ios/chrome/app/startup/chrome_app_startup_parameters.mm b/ios/chrome/app/startup/chrome_app_startup_parameters.mm index 0fde383..dfce669 100644 --- a/ios/chrome/app/startup/chrome_app_startup_parameters.mm +++ b/ios/chrome/app/startup/chrome_app_startup_parameters.mm
@@ -62,18 +62,17 @@ @implementation ChromeAppStartupParameters { NSString* _secureSourceApp; NSString* _declaredSourceApp; - NSURL* _completeURL; } - (instancetype)initWithExternalURL:(const GURL&)externalURL declaredSourceApp:(NSString*)declaredSourceApp secureSourceApp:(NSString*)secureSourceApp completeURL:(NSURL*)completeURL { - self = [super initWithExternalURL:externalURL]; + self = [super initWithExternalURL:externalURL + completeURL:net::GURLWithNSURL(completeURL)]; if (self) { _declaredSourceApp = [declaredSourceApp copy]; _secureSourceApp = [secureSourceApp copy]; - _completeURL = completeURL; } return self; } @@ -374,7 +373,7 @@ return first_run::LAUNCH_BY_OTHERS; } - NSString* query = [_completeURL query]; + NSString* query = base::SysUTF8ToNSString(self.completeURL.query()); // Takes care of degenerated case of no QUERY_STRING. if (![query length]) return first_run::LAUNCH_BY_MOBILESAFARI;
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/about_flags.mm index afabcaf..ae12d94f 100644 --- a/ios/chrome/browser/about_flags.mm +++ b/ios/chrome/browser/about_flags.mm
@@ -49,6 +49,7 @@ #include "ios/chrome/browser/crash_report/crash_report_flags.h" #include "ios/chrome/browser/download/features.h" #include "ios/chrome/browser/drag_and_drop/drag_and_drop_flag.h" +#include "ios/chrome/browser/experimental_flags.h" #include "ios/chrome/browser/find_in_page/features.h" #include "ios/chrome/browser/ios_chrome_flag_descriptions.h" #include "ios/chrome/browser/itunes_urls/itunes_urls_flag.h" @@ -244,6 +245,13 @@ flags_ui::kOsIos, FEATURE_VALUE_TYPE( autofill::features::kAutofillSaveCreditCardUsesStrikeSystem)}, + {"enable-autofill-save-credit-card-uses-strike-system-v2", + flag_descriptions::kEnableAutofillSaveCreditCardUsesStrikeSystemV2Name, + flag_descriptions:: + kEnableAutofillSaveCreditCardUsesStrikeSystemV2Description, + flags_ui::kOsIos, + FEATURE_VALUE_TYPE( + autofill::features::kAutofillSaveCreditCardUsesStrikeSystemV2)}, {"use-sync-sandbox", flag_descriptions::kSyncSandboxName, flag_descriptions::kSyncSandboxDescription, flags_ui::kOsIos, SINGLE_VALUE_TYPE_AND_VALUE( @@ -415,6 +423,11 @@ flag_descriptions::kBrowserContainerContainsNTPName, flag_descriptions::kBrowserContainerContainsNTPDescription, flags_ui::kOsIos, FEATURE_VALUE_TYPE(kBrowserContainerContainsNTP)}, + {"external-files-loaded-in-web-state", + flag_descriptions::kExternalFilesLoadedInWebStateName, + flag_descriptions::kExternalFilesLoadedInWebStateDescription, + flags_ui::kOsIos, + FEATURE_VALUE_TYPE(experimental_flags::kExternalFilesLoadedInWebState)}, {"search-icon-toggle", flag_descriptions::kSearchIconToggleName, flag_descriptions::kSearchIconToggleDescription, flags_ui::kOsIos, FEATURE_WITH_PARAMS_VALUE_TYPE(kIconForSearchButtonFeature,
diff --git a/ios/chrome/browser/app_startup_parameters.h b/ios/chrome/browser/app_startup_parameters.h index df3df4c..efd4a325 100644 --- a/ios/chrome/browser/app_startup_parameters.h +++ b/ios/chrome/browser/app_startup_parameters.h
@@ -25,11 +25,15 @@ @interface AppStartupParameters : NSObject // The URL that should be opened. This may not always be the same URL as the one -// that was receieved. The reason for this is in the case of Universal Link -// navigation where we may want to open up a fallback URL e.g., the New Tab -// Page instead of the actual universal link. +// that was received. The reason for this is in the case of Universal Link +// navigation where we may want to open up a fallback URL e.g., the New Tab Page +// instead of the actual universal link. @property(nonatomic, readonly, assign) const GURL& externalURL; +// Original URL that should be opened. May or may not be the same as +// |externalURL|. +@property(nonatomic, readonly, assign) const GURL& completeURL; + // The URL query string parameters in the case that the app was launched as a // result of Universal Link navigation. The map associates query string // parameters with their corresponding value. @@ -48,6 +52,7 @@ - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithExternalURL:(const GURL&)externalURL + completeURL:(const GURL&)completeURL NS_DESIGNATED_INITIALIZER; - (instancetype)initWithUniversalLink:(const GURL&)universalLink;
diff --git a/ios/chrome/browser/app_startup_parameters.mm b/ios/chrome/browser/app_startup_parameters.mm index 6294e60..55d34a8 100644 --- a/ios/chrome/browser/app_startup_parameters.mm +++ b/ios/chrome/browser/app_startup_parameters.mm
@@ -17,6 +17,7 @@ @implementation AppStartupParameters { GURL _externalURL; + GURL _completeURL; } @synthesize externalURLParams = _externalURLParams; @@ -28,10 +29,16 @@ return _externalURL; } -- (instancetype)initWithExternalURL:(const GURL&)externalURL { +- (const GURL&)completeURL { + return _completeURL; +} + +- (instancetype)initWithExternalURL:(const GURL&)externalURL + completeURL:(const GURL&)completeURL { self = [super init]; if (self) { _externalURL = externalURL; + _completeURL = completeURL; } return self; } @@ -40,7 +47,8 @@ // If a new tab with |_externalURL| needs to be opened after the App // was launched as the result of a Universal Link navigation, the only // supported possibility at this time is the New Tab Page. - self = [self initWithExternalURL:GURL(kChromeUINewTabURL)]; + self = [self initWithExternalURL:GURL(kChromeUINewTabURL) + completeURL:GURL(kChromeUINewTabURL)]; if (self) { std::map<std::string, std::string> parameters;
diff --git a/ios/chrome/browser/experimental_flags.h b/ios/chrome/browser/experimental_flags.h index dbe12be..681bf39 100644 --- a/ios/chrome/browser/experimental_flags.h +++ b/ios/chrome/browser/experimental_flags.h
@@ -7,11 +7,17 @@ #include <string> +#include "base/feature_list.h" + // This file can be empty. Its purpose is to contain the relatively short lived // declarations required for experimental flags. namespace experimental_flags { +// Feature to load external files with WebState instead of using +// ExternalFileController. +extern const base::Feature kExternalFilesLoadedInWebState; + enum GaiaEnvironment { GAIA_ENVIRONMENT_PROD, GAIA_ENVIRONMENT_STAGING,
diff --git a/ios/chrome/browser/experimental_flags.mm b/ios/chrome/browser/experimental_flags.mm index 880355d6..c6dd2eb 100644 --- a/ios/chrome/browser/experimental_flags.mm +++ b/ios/chrome/browser/experimental_flags.mm
@@ -47,6 +47,9 @@ namespace experimental_flags { +const base::Feature kExternalFilesLoadedInWebState{ + "ExternalFilesLoadedInWebState", base::FEATURE_DISABLED_BY_DEFAULT}; + bool AlwaysDisplayFirstRun() { return [[NSUserDefaults standardUserDefaults] boolForKey:kFirstRunForceEnabled];
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc index e664c25..cfc1c63 100644 --- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -59,6 +59,14 @@ "If enabled, prevents popping up the credit card offer-to-save prompt if " "it has repeatedly been ignored, declined, or failed."; +const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Name[] = + "Enable limit on offering to save the same credit card repeatedly using the" + "updated strike system implementation"; +const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Description[] = + "If enabled, uses the updated strike system implementation to prevent" + "popping up the credit card offer-to-save prompt if it has repeatedly been" + "ignored, declined, or failed."; + const char kEnableSyncUSSBookmarksName[] = "Enable USS for bookmarks sync"; const char kEnableSyncUSSBookmarksDescription[] = "Enables the new, experimental implementation of bookmark sync"; @@ -190,6 +198,12 @@ "When enabled, the BrowserContainer contains the NTP directly, rather than" "via native content."; +const char kExternalFilesLoadedInWebStateName[] = + "External files loaded in WebState"; +const char kExternalFilesLoadedInWebStateDescription[] = + "When enabled, external files are loaded in WebState instead of using " + "ExternalFileController."; + const char kBrowserTaskScheduler[] = "Task Scheduler"; const char kBrowserTaskSchedulerDescription[] = "Enables redirection of some task posting APIs to the task scheduler.";
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h index 46fb822..741afc6 100644 --- a/ios/chrome/browser/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -50,6 +50,12 @@ extern const char kEnableAutofillSaveCreditCardUsesStrikeSystemName[]; extern const char kEnableAutofillSaveCreditCardUsesStrikeSystemDescription[]; +// Title and description for the flag to control if credit card save should +// utilize the updated version of Autofill StrikeDatabase when determining +// whether save should be offered. +extern const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Name[]; +extern const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Description[]; + extern const char kEnableSyncUSSBookmarksName[]; extern const char kEnableSyncUSSBookmarksDescription[]; @@ -147,6 +153,11 @@ extern const char kBrowserContainerContainsNTPName[]; extern const char kBrowserContainerContainsNTPDescription[]; +// Title and description for the flag to load external files with WebState +// instead of using ExternalFileController. +extern const char kExternalFilesLoadedInWebStateName[]; +extern const char kExternalFilesLoadedInWebStateDescription[]; + // Title and description for the flag to control redirection to the task // scheduler. extern const char kBrowserTaskScheduler[];
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn index 052e7ab..99fe592 100644 --- a/ios/chrome/browser/ui/autofill/BUILD.gn +++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -138,6 +138,7 @@ "//components/autofill/core/browser", "//components/autofill/ios/browser:autofill_test_bundle_data", "//components/autofill/ios/browser:test_support", + "//components/leveldb_proto", "//components/strings:components_strings_grit", "//ios/chrome/browser/autofill", "//ios/chrome/test/app:test_support",
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm index 8cc3fc53..732881d 100644 --- a/ios/chrome/browser/ui/browser_view_controller.mm +++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -3594,13 +3594,16 @@ staticNativeController]; nativeController = staticNativeController; } else if (url_host == kChromeUIExternalFileHost) { - // Return an instance of the |ExternalFileController| only if the file is - // still in the sandbox. - NSString* filePath = [ExternalFileController pathForExternalFileURL:url]; - if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { - nativeController = - [[ExternalFileController alloc] initWithURL:url - browserState:_browserState]; + if (!base::FeatureList::IsEnabled( + experimental_flags::kExternalFilesLoadedInWebState)) { + // Return an instance of the |ExternalFileController| only if the file is + // still in the sandbox. + NSString* filePath = [ExternalFileController pathForExternalFileURL:url]; + if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { + nativeController = + [[ExternalFileController alloc] initWithURL:url + browserState:_browserState]; + } } } else if (url_host == kChromeUICrashHost) { // There is no native controller for kChromeUICrashHost, it is instead
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn index b5682e3..cb7e4a2 100644 --- a/ipc/BUILD.gn +++ b/ipc/BUILD.gn
@@ -103,6 +103,16 @@ "//base", ] + # TODO(https://crbug.com/916130): AFDO causes a substantial size increase in + # nacl_helper that originates from here. This is apparently due to some + # mixture of inlining, CFI, and (potentially) speculative devirtualization. + # Work around that by locally disabling AFDO. + # + # nacl_helper is only available on Linux. + if (is_linux) { + configs -= [ "//build/config/compiler:afdo" ] + } + if (enable_ipc_fuzzer) { public_configs = [ "//tools/ipc_fuzzer:ipc_fuzzer_config" ] }
diff --git a/media/blink/run_all_unittests.cc b/media/blink/run_all_unittests.cc index b2653b39..1e61f7d4c 100644 --- a/media/blink/run_all_unittests.cc +++ b/media/blink/run_all_unittests.cc
@@ -57,37 +57,53 @@ DISALLOW_COPY_AND_ASSIGN(BlinkPlatformWithTaskEnvironment); }; -static int RunTests(base::TestSuite* test_suite) { +class MediaBlinkTestSuite : public base::TestSuite { + public: + MediaBlinkTestSuite(int argc, char** argv) : base::TestSuite(argc, argv) {} + + private: + void Initialize() override { + base::TestSuite::Initialize(); + #if defined(OS_ANDROID) - if (media::MediaCodecUtil::IsMediaCodecAvailable()) - media::EnablePlatformDecoderSupport(); + if (media::MediaCodecUtil::IsMediaCodecAvailable()) + media::EnablePlatformDecoderSupport(); #endif - // Run this here instead of main() to ensure an AtExitManager is already - // present. - media::InitializeMediaLibrary(); + // Run this here instead of main() to ensure an AtExitManager is already + // present. + media::InitializeMediaLibrary(); #if defined(V8_USE_EXTERNAL_STARTUP_DATA) - gin::V8Initializer::LoadV8Snapshot(kSnapshotType); - gin::V8Initializer::LoadV8Natives(); + gin::V8Initializer::LoadV8Snapshot(kSnapshotType); + gin::V8Initializer::LoadV8Natives(); #endif #if !defined(OS_IOS) - // Initialize mojo firstly to enable Blink initialization to use it. - mojo::core::Init(); + // Initialize mojo firstly to enable Blink initialization to use it. + mojo::core::Init(); #endif - BlinkPlatformWithTaskEnvironment platform_; - service_manager::BinderRegistry empty_registry; - blink::Initialize(&platform_, &empty_registry, - platform_.GetMainThreadScheduler()); + platform_ = std::make_unique<BlinkPlatformWithTaskEnvironment>(); + blink::Initialize(platform_.get(), &empty_registry_, + platform_->GetMainThreadScheduler()); + } - return test_suite->Run(); -} + void Shutdown() override { + platform_.reset(); + base::TestSuite::Shutdown(); + } + + std::unique_ptr<BlinkPlatformWithTaskEnvironment> platform_; + service_manager::BinderRegistry empty_registry_; + + DISALLOW_COPY_AND_ASSIGN(MediaBlinkTestSuite); +}; int main(int argc, char** argv) { - base::TestSuite test_suite(argc, argv); + MediaBlinkTestSuite test_suite(argc, argv); return base::LaunchUnitTests( argc, argv, - base::BindRepeating(&RunTests, base::Unretained(&test_suite))); + base::BindRepeating(&MediaBlinkTestSuite::Run, + base::Unretained(&test_suite))); }
diff --git a/media/filters/decrypting_media_resource.cc b/media/filters/decrypting_media_resource.cc index 882f1be..8f783923 100644 --- a/media/filters/decrypting_media_resource.cc +++ b/media/filters/decrypting_media_resource.cc
@@ -53,7 +53,7 @@ return media_resource_->GetMediaUrlParams(); } -void DecryptingMediaResource::Initialize(InitCB init_cb) { +void DecryptingMediaResource::Initialize(InitCB init_cb, WaitingCB waiting_cb) { DCHECK(init_cb); auto streams = media_resource_->GetAllStreams(); @@ -64,11 +64,8 @@ num_dds_pending_init_ = streams.size(); for (auto* stream : streams) { - // TODO(chadduffin): Implement proper handling of the media::WaitingCB such - // that when the DecryptingDemuxerStream is waiting for a decryption key - // the firing of the callback will be bubbled up to the media pipeline. auto decrypting_demuxer_stream = std::make_unique<DecryptingDemuxerStream>( - task_runner_, media_log_, base::DoNothing()); + task_runner_, media_log_, waiting_cb); // DecryptingDemuxerStream always invokes the callback asynchronously so // that we have no reentrancy issues. "All public APIs and callbacks are
diff --git a/media/filters/decrypting_media_resource.h b/media/filters/decrypting_media_resource.h index 4a34bf0..05b0d6a3 100644 --- a/media/filters/decrypting_media_resource.h +++ b/media/filters/decrypting_media_resource.h
@@ -46,7 +46,7 @@ std::vector<DemuxerStream*> GetAllStreams() override; MediaUrlParams GetMediaUrlParams() const override; - void Initialize(InitCB init_cb); + void Initialize(InitCB init_cb, WaitingCB waiting_cb_); // Returns the number of DecryptingDemuxerStreams that were created. virtual int DecryptingDemuxerStreamCountForTesting() const;
diff --git a/media/filters/decrypting_media_resource_unittest.cc b/media/filters/decrypting_media_resource_unittest.cc index f40b150..31e5569 100644 --- a/media/filters/decrypting_media_resource_unittest.cc +++ b/media/filters/decrypting_media_resource_unittest.cc
@@ -3,13 +3,18 @@ // found in the LICENSE file. #include <memory> +#include <string> #include <vector> #include "base/bind.h" #include "base/bind_helpers.h" #include "base/test/mock_callback.h" #include "base/test/scoped_task_environment.h" +#include "media/base/decoder_buffer.h" +#include "media/base/decrypt_config.h" +#include "media/base/decryptor.h" #include "media/base/demuxer_stream.h" +#include "media/base/gmock_callback_support.h" #include "media/base/media_util.h" #include "media/base/mock_filters.h" #include "media/base/pipeline_status.h" @@ -26,9 +31,32 @@ namespace media { +static constexpr int kFakeBufferSize = 16; +static constexpr uint8_t kFakeKeyId[] = {0x4b, 0x65, 0x79, 0x20, 0x49, 0x44}; +static constexpr uint8_t kFakeIv[DecryptConfig::kDecryptionKeySize] = {0}; + +// Use anonymous namespace here to prevent the actions to be defined multiple +// times across multiple test files. Sadly we can't use static for them. +namespace { + +ACTION_P(ReturnBuffer, buffer) { + arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer); +} + +} // namespace + class DecryptingMediaResourceTest : public testing::Test { public: DecryptingMediaResourceTest() { + encrypted_buffer_ = + scoped_refptr<DecoderBuffer>(new DecoderBuffer(kFakeBufferSize)); + encrypted_buffer_->set_decrypt_config(DecryptConfig::CreateCencConfig( + std::string(reinterpret_cast<const char*>(kFakeKeyId), + base::size(kFakeKeyId)), + std::string(reinterpret_cast<const char*>(kFakeIv), + base::size(kFakeIv)), + {})); + EXPECT_CALL(cdm_context_, GetDecryptor()) .WillRepeatedly(Return(&decryptor_)); EXPECT_CALL(decryptor_, CanAlwaysDecrypt()).WillRepeatedly(Return(true)); @@ -75,16 +103,24 @@ return streams; } + MOCK_METHOD2(BufferReady, + void(DemuxerStream::Status, scoped_refptr<DecoderBuffer>)); + protected: base::test::ScopedTaskEnvironment scoped_task_environment_; base::MockCallback<DecryptingMediaResource::InitCB> decrypting_media_resource_init_cb_; + base::MockCallback<WaitingCB> waiting_cb_; NullMediaLog null_media_log_; StrictMock<MockDecryptor> decryptor_; StrictMock<MockDemuxer> demuxer_; StrictMock<MockCdmContext> cdm_context_; std::unique_ptr<DecryptingMediaResource> decrypting_media_resource_; std::vector<std::unique_ptr<StrictMock<MockDemuxerStream>>> streams_; + + // Constant buffer to be returned by the input demuxer streams and + // |decryptor_|. + scoped_refptr<DecoderBuffer> encrypted_buffer_; }; TEST_F(DecryptingMediaResourceTest, @@ -95,7 +131,7 @@ EXPECT_CALL(decrypting_media_resource_init_cb_, Run(true)); decrypting_media_resource_->Initialize( - decrypting_media_resource_init_cb_.Get()); + decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get()); scoped_task_environment_.RunUntilIdle(); EXPECT_EQ( @@ -111,7 +147,7 @@ EXPECT_CALL(decrypting_media_resource_init_cb_, Run(true)); decrypting_media_resource_->Initialize( - decrypting_media_resource_init_cb_.Get()); + decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get()); scoped_task_environment_.RunUntilIdle(); // When using an AesDecryptor we preemptively wrap our streams with a @@ -133,7 +169,7 @@ EXPECT_CALL(decrypting_media_resource_init_cb_, Run(true)); decrypting_media_resource_->Initialize( - decrypting_media_resource_init_cb_.Get()); + decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get()); scoped_task_environment_.RunUntilIdle(); EXPECT_EQ( @@ -155,7 +191,7 @@ EXPECT_CALL(decrypting_media_resource_init_cb_, Run(false)); decrypting_media_resource_->Initialize( - decrypting_media_resource_init_cb_.Get()); + decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get()); scoped_task_environment_.RunUntilIdle(); } @@ -170,7 +206,26 @@ EXPECT_CALL(decrypting_media_resource_init_cb_, Run(false)); decrypting_media_resource_->Initialize( - decrypting_media_resource_init_cb_.Get()); + decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get()); + scoped_task_environment_.RunUntilIdle(); +} + +TEST_F(DecryptingMediaResourceTest, + DecryptingDemuxerStreamInvokesWaitingCallback) { + AddStream(DemuxerStream::VIDEO, /* encrypted = */ true); + + EXPECT_CALL(*streams_.front(), Read(_)) + .WillRepeatedly(ReturnBuffer(encrypted_buffer_)); + EXPECT_CALL(decryptor_, Decrypt(_, encrypted_buffer_, _)) + .WillRepeatedly( + RunCallback<2>(Decryptor::kNoKey, scoped_refptr<DecoderBuffer>())); + EXPECT_CALL(decrypting_media_resource_init_cb_, Run(true)); + EXPECT_CALL(waiting_cb_, Run(WaitingReason::kNoDecryptionKey)); + + decrypting_media_resource_->Initialize( + decrypting_media_resource_init_cb_.Get(), waiting_cb_.Get()); + decrypting_media_resource_->GetAllStreams().front()->Read(base::BindRepeating( + &DecryptingMediaResourceTest::BufferReady, base::Unretained(this))); scoped_task_environment_.RunUntilIdle(); }
diff --git a/media/mojo/clients/mojo_renderer.cc b/media/mojo/clients/mojo_renderer.cc index cce7aa24..360a2e6 100644 --- a/media/mojo/clients/mojo_renderer.cc +++ b/media/mojo/clients/mojo_renderer.cc
@@ -244,9 +244,14 @@ void MojoRenderer::InitiateScopedSurfaceRequest( const ReceiveSurfaceRequestTokenCB& receive_request_token_cb) { + DCHECK(remote_renderer_.is_bound()); DVLOG(1) << __func__; - remote_renderer_->InitiateScopedSurfaceRequest(receive_request_token_cb); + if (encountered_error_) { + receive_request_token_cb.Run(base::UnguessableToken::Null()); + } else { + remote_renderer_->InitiateScopedSurfaceRequest(receive_request_token_cb); + } } void MojoRenderer::OnError() {
diff --git a/media/renderers/decrypting_renderer.cc b/media/renderers/decrypting_renderer.cc index a7712f98..3d26465d7 100644 --- a/media/renderers/decrypting_renderer.cc +++ b/media/renderers/decrypting_renderer.cc
@@ -143,8 +143,11 @@ decrypting_media_resource_ = std::make_unique<DecryptingMediaResource>( media_resource_, cdm_context_, media_log_, media_task_runner_); - decrypting_media_resource_->Initialize(base::BindOnce( - &DecryptingRenderer::InitializeRenderer, weak_factory_.GetWeakPtr())); + decrypting_media_resource_->Initialize( + base::BindOnce(&DecryptingRenderer::InitializeRenderer, + weak_factory_.GetWeakPtr()), + base::BindRepeating(&DecryptingRenderer::OnWaiting, + weak_factory_.GetWeakPtr())); } void DecryptingRenderer::InitializeRenderer(bool success) { @@ -181,4 +184,9 @@ return decrypting_media_resource_ != nullptr; } +void DecryptingRenderer::OnWaiting(WaitingReason reason) { + DCHECK(media_task_runner_->BelongsToCurrentThread()); + client_->OnWaiting(reason); +} + } // namespace media
diff --git a/media/renderers/decrypting_renderer.h b/media/renderers/decrypting_renderer.h index 4235ff3..6bd51fa2 100644 --- a/media/renderers/decrypting_renderer.h +++ b/media/renderers/decrypting_renderer.h
@@ -68,6 +68,7 @@ // initialized. void InitializeRenderer(bool success); bool HasEncryptedStream(); + void OnWaiting(WaitingReason reason); const std::unique_ptr<Renderer> renderer_; MediaLog* const media_log_;
diff --git a/net/BUILD.gn b/net/BUILD.gn index f1936dca..eebdea7 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -1331,6 +1331,10 @@ "third_party/quic/core/qpack/qpack_instruction_decoder.h", "third_party/quic/core/qpack/qpack_instruction_encoder.cc", "third_party/quic/core/qpack/qpack_instruction_encoder.h", + "third_party/quic/core/qpack/qpack_progressive_decoder.cc", + "third_party/quic/core/qpack/qpack_progressive_decoder.h", + "third_party/quic/core/qpack/qpack_progressive_encoder.cc", + "third_party/quic/core/qpack/qpack_progressive_encoder.h", "third_party/quic/core/qpack/qpack_static_table.cc", "third_party/quic/core/qpack/qpack_static_table.h", "third_party/quic/core/quic_ack_listener_interface.cc", @@ -1452,6 +1456,7 @@ "third_party/quic/platform/api/quic_export.h", "third_party/quic/platform/api/quic_exported_stats.h", "third_party/quic/platform/api/quic_fallthrough.h", + "third_party/quic/platform/api/quic_file_utils.cc", "third_party/quic/platform/api/quic_file_utils.h", "third_party/quic/platform/api/quic_flag_utils.h", "third_party/quic/platform/api/quic_flags.h", @@ -1676,6 +1681,7 @@ "third_party/spdy/core/zero_copy_output_buffer.h", "third_party/spdy/platform/api/spdy_arraysize.h", "third_party/spdy/platform/api/spdy_containers.h", + "third_party/spdy/platform/api/spdy_endianness_util.h", "third_party/spdy/platform/api/spdy_estimate_memory_usage.h", "third_party/spdy/platform/api/spdy_export.h", "third_party/spdy/platform/api/spdy_flags.h", @@ -1686,6 +1692,7 @@ "third_party/spdy/platform/api/spdy_string_utils.h", "third_party/spdy/platform/impl/spdy_arraysize_impl.h", "third_party/spdy/platform/impl/spdy_containers_impl.h", + "third_party/spdy/platform/impl/spdy_endianness_util_impl.h", "third_party/spdy/platform/impl/spdy_estimate_memory_usage_impl.h", "third_party/spdy/platform/impl/spdy_export_impl.h", "third_party/spdy/platform/impl/spdy_flags_impl.cc", @@ -3149,6 +3156,8 @@ "quic/mock_encrypter.h", "quic/test_task_runner.cc", "quic/test_task_runner.h", + "third_party/quic/core/qpack/offline/qpack_offline_decoder.cc", + "third_party/quic/core/qpack/offline/qpack_offline_decoder.h", "third_party/quic/core/qpack/qpack_decoder_test_utils.cc", "third_party/quic/core/qpack/qpack_decoder_test_utils.h", "third_party/quic/core/qpack/qpack_encoder_test_utils.cc", @@ -3432,6 +3441,21 @@ "//third_party/protobuf:protobuf_lite", ] } + + executable("qpack_offline_decoder") { + testonly = true + sources = [ + "third_party/quic/core/qpack/offline/qpack_offline_decoder_bin.cc", + ] + deps = [ + ":net", + ":quic_test_tools", + ":simple_quic_tools", + "//base", + "//testing/gmock", + ] + } + executable("crypto_message_printer") { sources = [ "tools/quic/crypto_message_printer_bin.cc", @@ -5104,6 +5128,7 @@ "third_party/quic/core/qpack/qpack_header_table_test.cc", "third_party/quic/core/qpack/qpack_instruction_decoder_test.cc", "third_party/quic/core/qpack/qpack_instruction_encoder_test.cc", + "third_party/quic/core/qpack/qpack_progressive_decoder_test.cc", "third_party/quic/core/qpack/qpack_round_trip_test.cc", "third_party/quic/core/qpack/qpack_static_table_test.cc", "third_party/quic/core/quic_alarm_test.cc", @@ -6338,6 +6363,7 @@ deps = [ ":net_fuzzer_test_support", ":quic_test_tools", + ":test_support", "//base", "//net", ] @@ -6376,6 +6402,7 @@ deps = [ ":net_fuzzer_test_support", ":quic_test_tools", + ":test_support", "//base", "//net", ]
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json index e9f1da9..8ce61dbd 100644 --- a/net/http/transport_security_state_static.json +++ b/net/http/transport_security_state_static.json
@@ -1704,7 +1704,6 @@ { "name": "slevomat.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "sour.is", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "spongepowered.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "staticanime.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "sunjaydhama.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "thusoy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tls.li", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -3278,7 +3277,6 @@ { "name": "gamenected.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gamenected.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "getsport.mobi", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "ghostblog.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gmdu.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "grafitec.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "greatfire.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -3755,7 +3753,6 @@ { "name": "gcs-ventures.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gerencianet.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gfournier.ca", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "gotech.com.eg", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gyboche.science", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "hackenturet.dk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "harmoney.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -5800,7 +5797,6 @@ { "name": "urphp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "v0tti.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "vagrantup.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "vaultproject.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "wegner.no", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "wilddog.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "wlzhiyin.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -9915,7 +9911,6 @@ { "name": "ksfh-mail.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kstan.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kucom.it", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "kuehnel.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kueulangtahunanak.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kulde.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kupelne-ptacek.sk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -10942,7 +10937,6 @@ { "name": "tsrstore.gq", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tubepro.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tunai.id", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "turbobit.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "turnik-67.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "turtle.ai", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "turtlementors.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -11468,7 +11462,6 @@ { "name": "doggieholic.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "dmz.ninja", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "dmfd.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "dfektlan.no", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "dne.lu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "drishti.guru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "drew.red", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -11593,7 +11586,6 @@ { "name": "gold24.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gratisonlinesex.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "grassenberg.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "graasp.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "groupebaillargeon.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "grokker.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gtchipsi.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -11631,12 +11623,10 @@ { "name": "holzheizer-forum.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "holzheizerforum.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "holzvergaser-forum.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "hotel-tongruben.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "hotelvictoriaoax-mailing.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "hostisan.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "hotelvillahermosa-mailing.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "horstmanshof.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "hypemgmt.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "hwag-pb.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "htmue.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "huang.nu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -11710,7 +11700,6 @@ { "name": "jschumacher.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kandalife.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kaliaa.fi", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "kabat-fans.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kajak.land", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "karmabaker.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "karatorian.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -11730,13 +11719,11 @@ { "name": "kinkenonline.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "knowledgesnap.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "klares-licht.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "khmb.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kjchernov.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kletterkater.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "koebbes.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "koniecfica.sk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "korsanparti.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "kontorhaus-schlachte.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kopular.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kolmann.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "korrelzout.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -11754,7 +11741,6 @@ { "name": "l4n-clan.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lak-berlin.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lagarderob.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "lamaland.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lancork.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "laozhu.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lars-ewald.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -13090,7 +13076,6 @@ { "name": "yarcom.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yinfor.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yoga-prive.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "yogeshbeniwal.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yooooex.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "youngandunited.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "youran.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -14502,7 +14487,6 @@ { "name": "sproutconnections.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "sptk.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "stalschermer.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "standoutbooks.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "starfm.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "starina.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "starkbim.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -15558,7 +15542,6 @@ { "name": "jayxon.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "jamesrussellward.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gulenet.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "janik.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "hispanic.dating", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "handenafvanhetmedischdossier.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "jackalworks.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -19273,7 +19256,6 @@ { "name": "luzfaltex.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lizhi.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "mastiffingles.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "lesquerda.cat", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "maskt.pw", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "ltecode.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lucysan.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -20096,7 +20078,6 @@ { "name": "twitter.ax", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "thejacksoninstitute.com.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "truetrophies.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "tomjonsson.se", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "simccorp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "togech.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "torproject.org.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -20221,7 +20202,6 @@ { "name": "villa-romantica-zillertal.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "voidserv.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "vinagro.sk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "vanderrijt.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "watersportmarkt.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "websecurity.is", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "webproject.rocks", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -20484,7 +20464,6 @@ { "name": "acgaudio.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "animorphsfanforum.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "4decor.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "ahlz.sk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "anantshri.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "anshumanbiswas.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "andrehansen.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -22776,7 +22755,6 @@ { "name": "top10mountainbikes.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "thedailyupvote.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tretail.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "totaku.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tlsbv.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "toyotamotala.se", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "treatprostatewithhifu.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -27824,7 +27802,6 @@ { "name": "upbeatrobot.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "uporoops.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "urbannewsservice.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "urbanwildlifealliance.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "urcentral.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "urist1011.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "url.fi", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -28283,7 +28260,6 @@ { "name": "algebraaec.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "abobuch.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "allsearch.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "alanhuang.name", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "aip-marine.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "akul.co.in", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "alexander-beck.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -30763,7 +30739,6 @@ { "name": "piraten-basel.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "playmaza.live", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "persoform.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "ownmay.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "pmbc.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "pirata.ga", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "phuong.faith", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -32790,7 +32765,6 @@ { "name": "gatewaybronco.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "followerrocket.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gemquery.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "gforce.ninja", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "fribourgviking.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "footballforum.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "fxislamic.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -42714,7 +42688,6 @@ { "name": "judge2020.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "judge2020.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "juliohernandezgt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "jungundwild-design.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "juridiqueo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "justiceo.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "justinrudio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -42736,7 +42709,6 @@ { "name": "kaotik4266.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kargl.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "keematdekho.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "kescher.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kfirba.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kidsareatrip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kin.life", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -47376,7 +47348,6 @@ { "name": "it-faul.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "italieflydrive.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "itforcc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "izhaojie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jackops.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jakubarbet.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "james-digital.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -48919,7 +48890,6 @@ { "name": "evemarketer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "evilmartians.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "exexcarriers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "expertohomestaging.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "f13cybertech.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fallenmystic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fastforwardsociety.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -55983,7 +55953,6 @@ { "name": "ricoydesign.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rimediogiusto.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rimorrecherche.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "rit.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ronzertnert.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rootonline.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rttvvip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -58483,7 +58452,6 @@ { "name": "tatuantes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "teamtravel.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "technologyhound.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "telefonsinyalguclendirici.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "terrorbilly.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "test-aankoop.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "test-achats.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -58836,7 +58804,6 @@ { "name": "ipid.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "iszy.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "itap.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "itsv.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "iwyc.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jack2celebrities.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jacksorrell.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -58885,7 +58852,6 @@ { "name": "livres-et-stickers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lizmooredestinationweddings.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "llemoz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "llnl.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "loanreadycredit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "locomocosec.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "loli.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -61052,7 +61018,6 @@ { "name": "carlocksmithkey.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "carshippingcarriers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "casaessencias.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "caseof.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cashfazz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "casirus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "catcoxx.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -63695,6 +63660,1497 @@ { "name": "zirrka.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "zjateaucafe.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "zubr.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "000books.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "003971.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "008207.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "008251.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "008253.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "008271.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "009p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "050869.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056657.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056675.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056679.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056687.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056690.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056697.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056867.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056869.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056875.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056879.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056950.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056976.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "056985.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "057587.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "057596.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "058509.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "058596.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "058679.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "059957.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "060757.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "060795.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "060796.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "060798.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "0607p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "060870.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "060875.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "065679.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "065706.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "065790.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "065970.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "065976.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "066570.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "066579.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "066590.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "066705.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "066709.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "066790.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "068697.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "068756.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "068957.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "069657.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "069676.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "0708p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "070968.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "070986.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "0720p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "0798rcw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "085806.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "085905.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "085950.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "086807.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "086907.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "087059.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "087065.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "087540.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "087569.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "087580.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "0vo.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "0xaf.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "110692.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "11221jz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "1126p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "112it.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "1130p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "120323.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "126772.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "127661.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "127662.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "127663.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "127665.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "12autoankauf-berlin.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "130212.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "131934.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "131954.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "133294.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "133492.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "136774.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "136814.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "137724.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "141145.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "1889p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2083236893.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2206p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "232192.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "249722.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24items.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2586p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "3351p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "336yh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "3880p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "3rsee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "3xbit.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "4111pk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "4138hd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "441jz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "442jz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "443jz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "46fa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "5002888.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "5007999.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "5287.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "532441.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "532445.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "545755.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "555wfcp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "58nav.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "5beanskit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "5stars.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "5yeb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "620881.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "6556hd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "6556pk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "6602p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "6603p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "69759.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "7080997.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "7198.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "7770b.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "77dostavkaroz.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "8080883.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "80883.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "80887.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "815jz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "816jz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "8211p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "8213p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "8214p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "8215p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "8216p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "848jz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "8802p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "885287.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "88851333.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "88851777.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "888666pj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "8yun.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "8yun.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "9090819.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "967606.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "9950p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "9box.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "9jatrust.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "a-care.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "abaev.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "abasalehngo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "abhibhat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "abitidasposa.roma.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "abogadosescobarysanchez.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aborla.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "acl.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "acorncredentialing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "adaptergonomics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "adcnvs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "adminless.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "adohanyzasjovoje.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "adomani-italia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "adtelligent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "advaithbot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "advenacs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aegis.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aenterprise.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aeonct.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aff.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aflam4you.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "african-bay.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "agenciamdg.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aimonline.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aipi.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "alcouponest.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "alex4386.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "alexandrefa.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "alexpnixon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "allamericanpaintingplus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "allram.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "allsun.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "almenrausch-pirkhof.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "alpencams.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "alpstarentaisetaxi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "alpstarentaisetaxi.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "alternativehosting.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "alternativehosting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aluminium-giesserei.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "alxu.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "amendine.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "americasdirector.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "amiciperlatesta.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "amielle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "amokinio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "amstelland.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "amzanalyzer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "anatoray.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ancel.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "andrewletson.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "angrido.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "anoboy.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "anodas.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "antonok.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "anythingautowebster.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aobeauty.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aod-tech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aori.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aostacarnavals.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "apiu.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "apocalypsemud.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aqua-bucht.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "archeologicatoscana.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "archit.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "archiweb.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "arrowit.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "arunjoshua.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aryabusines.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ashessin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "asirigbakaute.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "askeustache.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "asksatya.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "astroalloys.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aterlectric.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "athekiu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "atlascoffeeclub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "atmalta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aucarresainteloi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aulasvirtualesperu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "authenticationhub.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "autoreinigung-noack.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "autoskolaplzen.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "avestawebbtjanst.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "awplasticsurgery.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "awscloudrecipes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aying.love", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "b767.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "baitaplamvan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "baitcon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "balter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bananice.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bani99.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bankanswers.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bariumoxide.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "batkave.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "baumkuchen-aus-dresden.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "baza-gai.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bbsec.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "beavertales.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bee-social.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "belos.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "belyoung.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "benbalter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "benefitshub.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "benefitshub.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "berati.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "berg-freunde.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "berg-freunde.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bewegigsruum.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bfcgermania88.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bfob.gg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bgmn.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bibles.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bie08.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bie35.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bie79.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "billfazz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bitrefill.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bixbydevelopers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bizzdesign.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bkt.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "black1ce.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "blaindalefarms.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "blicy.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "blingwang.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bloogle.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bluepromocode.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "blueswandaily.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bookzaga.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "booplab.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "boothlabs.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bootsschule-weiss.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "boreo.si", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "boysontech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bps.vc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "breakwall.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "brightside.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "britanniacateringyeovil.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bryantzheng.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "btshe.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "builditfl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bukiskola.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bukivallalkozasok.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bukpcszerviz.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bulwarkcrypto.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bunny.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bunq.love", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "buscandolosmejores.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "busiteyiengelle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "butzies.ddnss.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "byjuschennai.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "bypetula.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "c376.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cafesdomundo.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "camshowdir.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "canberraoutletcentre.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "care-spot.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "care-spot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "care-spot.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "care-spot.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "care-spot.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "care-spot.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "care-spot.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespot.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespot.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespot.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespot.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespot.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespot.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespotexpress.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespotexpresshealthcare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespottravelmedicine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespottravelmedicine.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespoturgentcare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespoturgentcare.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespoturgentcare.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespoturgentcare.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "carespoturgentcare.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "casa-laguna.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "casadopulpo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "caseof.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cat93.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "catchkol.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "catram.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "caudo.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "caudohay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "celebphotos.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "centerperson.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "centos.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "centsi.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cgf-charcuterie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "chapstick.life", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "checkandreportlive.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "checkblau.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "checookies.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "chiangmaimontessori.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "chicurrichi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "christielepage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "chromeworld.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "chromopho.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "chtsi.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cihar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cinkciarz.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "citas-adultas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "claraism.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cldinc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cloudsecurityalliance-europe.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cloudsecurityalliance.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cloudsecurityalliance.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cloudsecuritycongress.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cloudsecuritycongress.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cloze.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "club-dieta.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cmov-plongeurs.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cockfile.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "coindesfilles.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "collab.ddnss.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "collaborativehealthpsychology.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "collegereligionandphilosophy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "comedyhuis.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "comercialdragon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "commercezen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "commissaris-vraagbaak.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "componentshop.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "comprarefiereygana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "computerwerk.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "concreterepairconcreteraising.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "congafasdesol.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "conotoxia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "consideryourways.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "constituenttracker.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "containerspace.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "corpoepele.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "corso-antincendio.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cosmetic-surgery-prices.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cote-chasse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cpe-registry.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cpe-registry.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cpe-registry.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cperegistry.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cperegistry.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cperegistry.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "crazybulksteroids.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "creaticworld.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "creativeangles.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "creatorswave.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cronenberg.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cryptomail.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "csa-library.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "csaapac.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "csaapac.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "csacongress.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "csacongress.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "csadc.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "csasummit.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "csasummit.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "csosa.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cube.la", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "curlify.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cvtemplatemaster.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cwc.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cyberbot.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "cyberdyne.llc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "d7211.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "d7215.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "d7216.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "daddybio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dafyddcrosby.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dai94.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dair.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "daitouryu-jujutsu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "damjanovic.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "danfromit.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "danfromit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dara-berlin.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "datingsite-vergelijken.website", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "daveops.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "davidandrewcoaching.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "davidkeane.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dazz.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dazzit.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dazzit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dazzit.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dazzit.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dazzit.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dbjl.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dbrand.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dd7211.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "deadbyhost.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dealspotr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dealszone.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "debatereport.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "decimatechnologies.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "decor-live.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "deep-labs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "deepinnov.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "degrasboom.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dein-trueffel.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dejting-sidor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "deleenheir.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "deli-tochigi.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "deltawolf.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "depedtambayan.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "desenfans.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "deskguide.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dev-greavesindia.pantheonsite.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "devils-point.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "diag.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dicionarios.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "die-bobbeloase.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "diendorfer.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "digital-sculpture.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "digitalblood.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "directoryhub.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "disciplesmakingdisciples.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "discus-communications.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dividendz.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "divisuite.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dleger.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dmk-realestate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dnalounge.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dnapizza.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "docassure.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "doctorxdentist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dogodki.today", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dominik-bergmann.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dongthucvat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "doxal.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dracoon.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dracoon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dracoon.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dracoon.team", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "drgn.li", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "drgrace.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "droneland.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dsble.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dsbutler.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "du-alex.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "duckcorp.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dustyro.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dustywilson.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dutchfoodie.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "dynocc.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "e-sushi.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "eac.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "easypayments.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "echarity.ae", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ecp.ae", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "eggqvq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "egicloud.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ehdud8451.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ekranos.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "eladlak-ingatlan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "eletrochape.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "elitel.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "elodrias.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "emilstahl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "emma.ly", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "encodecloud.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "enganches.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "engl-server.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "enotefile.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "envide.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "enviro-umweltservice.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "epinesdeparadis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "erlebnisarchaeologie-bayern.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ernsteisprung.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "estefan.dyndns.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "estonia.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "eternalflame.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ethanchin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "eurorecambios24.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "exadime.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "explorebigideas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "expmind.co.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "express1040.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "eyejobs.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "facai666.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "facai888.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "facarospauls.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fachmann-umzuege.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fakeduckpond.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "familiereimann.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fantraxhq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fastinviter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fbrief.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "federicoparty.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "feedermarket.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "feixiang.eu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "felix-hirner.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fidufinance.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "film-op-tv.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "financewithcromulent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "findelahistoria.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "findingtheuniverse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "finefriends.social", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "finefriendsapp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "finlandcook.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "finlandcook.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fiveyearsahead.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "flamingogroup.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "flibusta.appspot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fmstr.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "followmystaff.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fono.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "forbidden-mods.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ford.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ford.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ford.com.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ford.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fordsync.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fortestecnologia.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fosterpark.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fournarisopenday.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "frasch-umzuege.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "frau-pusteblu.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "frc.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "freeministryresources.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "freetaxusa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "friplay.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fruityten.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "fsgeek.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ftdev.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "furries-united.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "furry.bot", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "g3circuit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gabrielkoo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gadget-tips.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gadgetadvisor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gailbartist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gallmeyer-consulting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "galoserver.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gangnamavenue.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gangnamcool.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "garagedoorrepairingsanjose.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "garagelink.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gaw.sh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gd88.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gearbot.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "geekeffect.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gehrke.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "general-plast.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "geniofinanciero.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gesnex.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gfedating.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ghost-legion.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "givingtools.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "glass-mag.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "globalno.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gn00.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "go2archive.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gomel.city", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gomelphoto.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "good588.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "goru.travel", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gosnipe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gostargazing.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "goufaan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "graandco.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "grasboomamersfoort.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "grasboombinnendoor.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "grasboomclophaemer.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "grasboomderoos.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "grasboomleusden.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "grasboommax.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "grasboommeerbalans.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "grasboomveenendaal.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "grasboomvondellaan.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "grenlandkiropraktor.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "grupodatco.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gtn-pravda.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "guchengf.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gx3.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gyakori.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "gzriedstadt.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "haancommunity.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hackerone.blog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hackingondemand.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "haibara.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hajekj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hambassadors.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "handy-reparatur-berlin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "handynummer-info.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hansashop.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hatter.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "haustechnik-schulte-sanitaer-heizung-klima.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hd4138.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hd6556.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "heitepriem.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hellomookie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "helprocleaningservices.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "helptasker.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "helserbrothers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hendrickx.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "henrik-bondtofte.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "herbertjanvandinther.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "heribro.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "heroku.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hideo54.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hillcrestswimclub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "himalaya-cross.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "himalaya.video", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hitchpin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hkas.org.hk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hoathienthao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hoathienthao.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hoctap.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "holidayacademy.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "homelabquotes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "honoka-seitai.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "horizzon.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "horsegateway.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hostingalternative.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hotel1926.com.mt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hrafnkellbaldurs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hsuan.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "huangqifu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "hytale.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "idealcontabilidade.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ideatarmac.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "idyl.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "igdn.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "igrarium.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ijsclubdwarsgracht.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ikkakujuku.work", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ikmx.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "iliasdeli.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ima.re", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "imcsi.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "imperialinfosys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "imtikaib.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "inc.wf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "independenttravelcats.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infobalkans.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infosectekniques.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infraball.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infrabeta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infrabold.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infraboom.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infraclip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infracron.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infradart.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infradisk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infrafuse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infralira.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infraloon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "inframake.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "inframeet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "inframenu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infraname.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infranest.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infratask.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infratrip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "infravibe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "inoxandco.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "inoxdesign.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "inoxdesign.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "inspiratienodig.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "integrata.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "internetgardener.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "intropickup.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "investinturkey.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ipripojeni.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ipso.paris", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "irismq.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "irlfp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "isitef.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "itcs.services", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "itfly.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "its420somewhere.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "itseeze.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ivotemahdi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "iwascoding.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "iyn.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "iyoumu.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "izanah.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "j-robertson.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "j5lx.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "j5lx.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "j5lx.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jackwozny.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jackyliao.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jacobs-implantate.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jarrah-alsilawi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "javi.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jaybrokers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jcus.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jdm.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jemigjordy.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jennethaarfotografie.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jianwei.wang", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jime-hlavou.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "joesniderman.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "johngadenne.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jordiescudero.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jorsev.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "joshhoffer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "julm.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jusos-goettingen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "just-webdesign-berlin.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jwpoore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jwybk.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jwz.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "jzgj088.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kamen-master.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kayo.digital", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "keepitsecure24.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kennedyinsurancesolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kevindienst.blog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kiarayoga.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kieran.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kii91.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kinecle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kipsu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kirchhoff-getraenke.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kiropraktorvard.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kizomba.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kizzycode.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kjnotes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kk.in.th", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kl008888.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "klapib.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kleine-viecherei.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "knoji.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kobar.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "koeeusa.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kohparadise.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kojip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kokoushuvila.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "komodolabs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kritikawebu.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kroell.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ksopp.si", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kt-events.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kuanta.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kuechenserver.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kuechenserver.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kunaldesai.blog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kurszielnull.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kuunlamaailm.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kybqp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "kybqp.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lagsoftware.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lalunaonlinebr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lambangcapgiare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lannamontessori.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lannatefl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lansewu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "laserhealthsolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "layazc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "layordesign.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lbsistemas.com.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "legalforms.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "legionminecraft.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "legnami24.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lehrermarktplatz.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lequateur.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lesummeira.is", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "letson.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "level6.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "leventismotors.com.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lexic.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lgbtq.cool", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "liangyichen.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "liberationschool.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ligmadrive.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "likeometer.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "limx.win", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "linaklein.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "linan.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "linan.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "linan.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lincoln.com.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lincoln.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "linuxbg.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lipighor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lipighor.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "list-gymnasium.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "littlecrittersbrewery.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "littleduck.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "liu0hy.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lizhuogui.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lkellar.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lock23.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "locksmithcarrolltontx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "locksmithdrippingspringstx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "locksmithlakewaytx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "locksmithmesquitetexas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "locksmithsbuda.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lockwoodchristmastreefarm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "locus-dashboard.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "locusmap.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lonay.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lostsandal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lostsandal.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "louisapolicefoundation.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "louisapolicefoundation.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ls-modcompany.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "lundberghealthadvocates.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "m0v0.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "maduradas.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "maduradas.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "magicjudges.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mailjunky.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "makogaming.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "malvertise.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mamabepo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "manantialdevida1450.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "maniacoland.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "marietrap.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "maroismasso.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "martian.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "massageishealthy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "massagetherapyschoolsinformation.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "math-coaching.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "matocmedia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mauerwerk.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mcconciergerie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mcdsg.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-post.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-post.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-post.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-post.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-post.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-postclinic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-postdoctor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-postdoctors.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-postemergency.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-posthealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-postmedical.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-postphysicians.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "med-postwellness.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mediacloud.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpost.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpost.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpost.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpost.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpost.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpost.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpost.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpostcare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpostclinic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpostdoctor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpostdoctors.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpostemergency.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpostexpresscare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medposthealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medposthealthcare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpostimmediatecare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpostmedical.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpostphysicians.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medposturgentcare.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medposturgentcare.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medposturgentcare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medposturgentcare.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medposturgentcare.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medposturgentcare.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpostwalkincare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "medpostwellness.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mekongmontessori.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "memo2ch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "metrodetroitmommy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "meuble-house.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mexicodental.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mgiljum.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mi92.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mibh.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "micelius.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "michaell.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "michaell.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "michaelloveys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "micromookie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "micsell.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mihgroup.eu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mihgroup.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mijntelefoonboek.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mikusa.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "milkypond.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "minican.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "miraste.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mirazperu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mircarfinder.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mirete.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mnconsulting.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mnienamel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "modonor.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mods-community.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mods-pic.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "moepass.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "moleskinestudio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "monelephantapois.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "monplay.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "motor-forum.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "movfun.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mrmad.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mrstuudio.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mstdn.vodka", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mudit.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mueller-gaestehaus.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "muilties.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "muoivancauhoivisao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "musiikkiohjelmapalvelu.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "muy.ooo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mvwoensel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "my-co.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mycamshowhub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "myclgnotes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "myibidder.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "myloneworkers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mymonture.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "myopd.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "myphamaplus.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "myrepublic.net.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mysad.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "myservice.store", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mytime.gl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "mzlive.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "naarakah.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nabbar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "naganithin.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nagata.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "naijaxnet.com.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nais0ne.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nakayama.industries", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nakayamaresearch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nameproscdn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "namus.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "naseehah.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nasr.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nathan.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ncjrs.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nerdpol.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nereustech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nethack.ninja", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "netube.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "netzona.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "newhoperailroad.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nexter.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nguyencucthanh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nice.im", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nickmorris.name", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nicolaiteglskov.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nikpool.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ningbo.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nizhaoheng.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nob.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nojobook.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "noleggiolimousine.roma.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "noonan.family", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "northcoastlabs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nosuch.blog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nosuch.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nosuch.website", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "noteshare.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nourishandnestle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nowitzki.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "npbeta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nrsmart.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ntut.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nysis.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "nysis.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "o-s.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "octa.store", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "okqubit.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "okviz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "oldonyosafaris.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "olivier-rochet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "omegarazer.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "onehost.blue", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "online-biblio.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "onlinecasinoselite.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "opcionpublicitaria.pe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "open-ctp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "open-ctp.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "open-ctp.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "openbsd.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "opencaves.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "openctp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "openctp.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "openctp.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "openshippers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "openstandia.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "opportunityliu.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "optimaner.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ordoh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ore.cool", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "origin8delicafes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "osolutionscorp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ostachstore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "otisko.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ots.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "outfit-weimar.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "outincanberra.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "overlandireland.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ownagepranks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "paced.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "packs-de-mujeres.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pagamentosonline.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pagerduty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pahub.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "paigejulianne.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "palavalbasket.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "palermopride.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "panamatrippin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "paniodpolskiego.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "paragontasarim.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "parturi-manner.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "partyshop.ge", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "parys.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "patrykwegrzynek.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pause-canap.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pawspuppy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "paxchecker.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pbren.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pcs.org.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pearlsonly.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pearlsonly.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pearlsonly.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "peatsbeast.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "peckcloths.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "peepsfoundation.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pencil2d.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "penzionvzahrade.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pepfar.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "peppelmedi.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "performancegate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "perge.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "permaseal.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "peruvianphotography.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "peterboweycomputerservices.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "petervaldesii.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "petto.com.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "phonenumber-info.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pianos.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pickupenc.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pimusiccloud.hopto.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "piu.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pixshop.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pixulutinho.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pjp.com.mt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "plantron.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "plastic-id.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "plumbercincoranch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "plusminus30.si", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "plutonx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ponio.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ponxel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "porncompanions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "portesmagistral.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "premieravenue.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "princepessa.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "probano.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "productionscime.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "promobo.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "propertycrawl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "proservices.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "prove.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "proxirealtime.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "psauxit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pseric.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "psicometricas.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "pubkit.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "publi-all.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "punchlinetheatre.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "puntcunts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "puppo.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "purejewels.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "purityclothing.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "qaq.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "qq885.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "qrpatrol.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "quallo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "qunzi.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "qvq.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "qxzg.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "radiolla.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "radiopleer.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "radiumcode.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "raid-runners.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "raiffeisenzeitung.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "railduction.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rallypodium.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "randewoo.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "random.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "razrsec.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "reach-on.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "readabilitychecker.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "recebersms.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "recettecookeo.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "reddingsbrigadeveghel.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "regensburg-repariert.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "remembermidi.sytes.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rena.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "resdon.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "respons.je", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "respons.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "respons.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "respons.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "respons.ws", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "responscode.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "responscode.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "responscode.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "responscode.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "responsecode.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "responsecode.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "responsecode.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "reviderm-skinmedics-rheinbach.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "revuestarlight.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "riechsteiner.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rileyskains.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rincondenoticas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ristorantelittleitaly.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "riverbendessentialoil.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rle.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "robertkotlermd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "roboex.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rockerchyc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rolfsbuss.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "romtex.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rootetsy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rootkit.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rowancasting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rtsak.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rudel-wot.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ruequincampoix.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ruzzll.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rvc-france.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rvfit.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "rxguide.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ryuanerin.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "saga-umzuege.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sajtoskal.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "samdrewtakeson.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "samorazvitie.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sangyoui.health", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sanovnik.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sanych-msk.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "saorview.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sarahcheyette.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sarkoziadam.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "satplay.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "savbus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "savbus.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "schluesseldienst-berlin.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "schmatloch.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "schmidtlohwasser.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "schonstedt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "science.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "secondnature.bio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sehablazolano.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sek.ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sektor.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "selectionengine.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "selectionengine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "selectionengine.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "selectionengine.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "send4x.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "seomik.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "seotools.asia", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "seowebexpert.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "seriousaboutsecurity.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "servicerequesthub.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "servidoresadmin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "shahar.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sharefox.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sharefox.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "shopcord.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "shopperexpertss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "shotly.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "shrt.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sigma957.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sigmaweb.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "silicanetworks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "silvershadow.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "silvester-mitterschida.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "simonpayne.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sinsastudio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "site.pictures", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sklep-majster.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "skoilly.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "skyingo.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sluhockey.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sluo.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "slushpool.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "smartgridsecurity.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "smartgridsecurity.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "smartime.com.ar", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "smartminibushire.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "snh48live.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "soblaznenie.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "soblaznenie2.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "softcreatr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "software-search.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "solemare-hotel.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sotayhoctap.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sovereignpcs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "spaceunique.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sparumzuege.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "spewingmews.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "spiegel21.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sportmundschutz-info.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "spotfake.news", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "spotypal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "springtxcarpetcleaning.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sqlbi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sqprod.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ssab.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "st-tir-pln.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "stahlfeuer-ofenwerkstatt.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "star.garden", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "starease.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "starfriend.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "stevezheng.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "stevezheng.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "stickerparadise.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "stiebel.co.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "stiebel.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "stiebelmedia.co.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "stiebelmedia.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "stockportpyramid.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "stringbeanstudio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "subtitry.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sudanindependent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "summerbo.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sunbury.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sunhaoxiang.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sunplay.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "supplementswatch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sustc.ac.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sv-schody.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "sweepy.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "swifteh.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "swingerclub.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "swrpgitems.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "systemisbusy.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "szasz.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "szeptylasu.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tagnull.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tako-miyabi.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "talichi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tallship.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tamada.expert", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tamarimolhem.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tambayology.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tanchynski.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tangledmeditations.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "taowa.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tatler.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "taxhawk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "taxisantapolagranalacant.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tcl.sh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "teambim.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "techni-grav.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "techusers.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tecnosa.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "teektalk.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "test-greavesindia.pantheonsite.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "testingbot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tetragir.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "th-music-finder.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thcdev.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "the-jeuxflash.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "the-train.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "theagencywithoutaname.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "theasianshooter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "theasianshooters.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "theboats.agency", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "theboats.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "theboats.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "theboats.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "theboats.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "theboats.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "theboats.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thechavs.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thedailyprosper.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thedoctorsorders.pub", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "theghostlytavern.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thegioidulich.com.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thegreatcommissionpodcast.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thelounge.chat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thenetw.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thermo-recetas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thesage.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thesanta.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thetechbasket.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thurn.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "thymiaturtle.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tib1.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tieronegraphics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tigerscu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "timecd.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tinhchattrangda.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "titli.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tm-t.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tmd.cool", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "to-riktari.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tobiaalberti.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "todo-anime.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "top6casinos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "topwoodltd.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "toulineprestige.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tradingview.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "traducir.win", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "treehouse.pub", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "treestarmarketing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "trichdanhay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "trico-pigmentazione.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "triri.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "try2services.vc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ttfin.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ttlet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tuanhstore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tueplay.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "twatspot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "tzsec.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "uberpromocodes.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ubntleaks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "uddhabhaldar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ufo-blogger.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ultramookie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "umzuege-berlin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "umzug-berlin24.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "unik.bg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "united-german-commander.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "uoone.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "urbangymfirenze.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "urbexing.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "valuemywebsite.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "vasastansbygg.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "veilofsecurity.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "velocom.com.ar", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "verkkopalvelin.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "verschoren.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "verticesedge.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "vervewellness.co.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "viacation.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "vicugna.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "villaella.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "vindipoker.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "virtuebags.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "visionviral.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "vitamina.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "vitamina.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "vive.link", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "voolik.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "voxpopuli.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "vragenvanproust.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "vuatruyen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "vytea.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "waggs.link", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wammu.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wangwenbo.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "warmtepomp.express", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "waschmaschinen-dienst.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wasd.ms", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wasgehtheute.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wattmaedchen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wav-productions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wcosmeticsurgery.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "webhotelli.website", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "webionite.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "weblate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "weblate.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "webperformance.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "webplatform.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wedestock.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wedplay.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wegerecht.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wegrzynek.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "westcommunitycu.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wfcp1010.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wgtrm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "whocalledme.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wikilivres.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "winfographics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "withdewhua.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wmcns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wolfcrow.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "woshiluo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wpboot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wpcs.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wpherc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wug.fun", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "www-5287.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "wyomingexiles.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xb83studio.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xentho.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xf5888.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xiaohui.love", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xie38.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xie91.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xiwu.li", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xn--13-6kc0bufl.xn--p1ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xn--90aroj.xn--p1ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xn--gi8hwa.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xn--mgi-qla.life", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xn--registriertesexualstraftter-ykc.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xpressable.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xpresswifi.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xstreamable.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "xueanquan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yangjingwen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yangshangzhen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yayoba.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ycbmdevelopment.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ycbmstaging.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yes35.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yh64678.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yh66656.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yh66689.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yh88890.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yiffed.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yogaprague.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yooguo123.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yooomu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yosakoinight.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "youhs.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yourbodyknows.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yourbodyknows.is", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yourscotlandtour.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "youshouldbealiberal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yunloc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yxzero.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yycbike.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yyy116.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "yyy608.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zaffit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zalzalac.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zap-mag.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zeyi.fan", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zf1898.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zh-yds.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zhanghao.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zhiyuan.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zipfworks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zjc3.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zohair.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zooneshop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zwierslanguagetraining.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zxxcq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, // END OF 1-YEAR BULK HSTS ENTRIES // Only eTLD+1 domains can be submitted automatically to hstspreload.org,
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h index 2c502af..fa738d35 100644 --- a/net/quic/quic_flags_list.h +++ b/net/quic/quic_flags_list.h
@@ -335,3 +335,12 @@ QUIC_FLAG(bool, FLAGS_quic_restart_flag_quic_variable_length_connection_ids_server, false) +// If true, QuicPacketCreator::SetTransmissionType will set the transmission +// type of the next successfully added frame. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_set_transmission_type_for_next_frame, + false) +// If true, always send connection close/reset for IETF connections. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_always_reset_ietf_connections, + true)
diff --git a/net/third_party/quic/core/congestion_control/general_loss_algorithm.cc b/net/third_party/quic/core/congestion_control/general_loss_algorithm.cc index d8c86ea9..8420f3d9 100644 --- a/net/third_party/quic/core/congestion_control/general_loss_algorithm.cc +++ b/net/third_party/quic/core/congestion_control/general_loss_algorithm.cc
@@ -39,7 +39,7 @@ void GeneralLossAlgorithm::SetLossDetectionType(LossDetectionType loss_type) { loss_detection_timeout_ = QuicTime::Zero(); - largest_sent_on_spurious_retransmit_ = 0; + largest_sent_on_spurious_retransmit_ = kInvalidPacketNumber; loss_type_ = loss_type; reordering_shift_ = loss_type == kAdaptiveTime ? kDefaultAdaptiveLossDelayShift @@ -49,7 +49,7 @@ QUIC_RELOADABLE_FLAG_COUNT(quic_eighth_rtt_loss_detection); reordering_shift_ = 3; } - largest_previously_acked_ = 0; + largest_previously_acked_ = kInvalidPacketNumber; } LossDetectionType GeneralLossAlgorithm::GetLossDetectionType() const {
diff --git a/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes.cc b/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes.cc index fa39122a..d5279ba 100644 --- a/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes.cc +++ b/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes.cc
@@ -38,9 +38,9 @@ stats_(stats), reno_(reno), num_connections_(kDefaultNumConnections), - largest_sent_packet_number_(0), - largest_acked_packet_number_(0), - largest_sent_at_last_cutback_(0), + largest_sent_packet_number_(kInvalidPacketNumber), + largest_acked_packet_number_(kInvalidPacketNumber), + largest_sent_at_last_cutback_(kInvalidPacketNumber), min4_mode_(false), last_cutback_exited_slowstart_(false), slow_start_large_reduction_(false), @@ -241,7 +241,7 @@ bool TcpCubicSenderBytes::InRecovery() const { return largest_acked_packet_number_ <= largest_sent_at_last_cutback_ && - largest_acked_packet_number_ != 0; + largest_acked_packet_number_ != kInvalidPacketNumber; } bool TcpCubicSenderBytes::ShouldSendProbingPacket() const { @@ -249,7 +249,7 @@ } void TcpCubicSenderBytes::OnRetransmissionTimeout(bool packets_retransmitted) { - largest_sent_at_last_cutback_ = 0; + largest_sent_at_last_cutback_ = kInvalidPacketNumber; if (!packets_retransmitted) { return; } @@ -414,9 +414,9 @@ void TcpCubicSenderBytes::OnConnectionMigration() { hybrid_slow_start_.Restart(); prr_ = PrrSender(); - largest_sent_packet_number_ = 0; - largest_acked_packet_number_ = 0; - largest_sent_at_last_cutback_ = 0; + largest_sent_packet_number_ = kInvalidPacketNumber; + largest_acked_packet_number_ = kInvalidPacketNumber; + largest_sent_at_last_cutback_ = kInvalidPacketNumber; last_cutback_exited_slowstart_ = false; cubic_.ResetCubicState(); num_acked_packets_ = 0;
diff --git a/net/third_party/quic/core/frames/quic_ack_frame.cc b/net/third_party/quic/core/frames/quic_ack_frame.cc index f3d4775..993a1a7 100644 --- a/net/third_party/quic/core/frames/quic_ack_frame.cc +++ b/net/third_party/quic/core/frames/quic_ack_frame.cc
@@ -23,7 +23,7 @@ } QuicAckFrame::QuicAckFrame() - : largest_acked(0), + : largest_acked(kInvalidPacketNumber), ack_delay_time(QuicTime::Delta::Infinite()), ecn_counters_populated(false), ect_0_count(0), @@ -56,7 +56,7 @@ } void QuicAckFrame::Clear() { - largest_acked = 0; + largest_acked = kInvalidPacketNumber; ack_delay_time = QuicTime::Delta::Infinite(); received_packet_times.clear(); packets.Clear();
diff --git a/net/third_party/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc b/net/third_party/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc index 4ddea875..a9be4410 100644 --- a/net/third_party/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc +++ b/net/third_party/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc
@@ -14,18 +14,6 @@ namespace quic { namespace test { -namespace { - -class NoOpHeadersHandler : public QpackDecoder::HeadersHandlerInterface { - public: - ~NoOpHeadersHandler() override = default; - - void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override{}; - void OnDecodingCompleted() override{}; - void OnDecodingErrorDetected(QuicStringPiece error_message) override{}; -}; - -} // namespace // This fuzzer exercises QpackDecoder. It should be able to cover all possible // code paths. There is no point in encoding QpackDecoder's output to turn this @@ -42,7 +30,10 @@ std::bind(&QuicFuzzedDataProvider::ConsumeIntegralInRange<uint16_t>, &provider, 1, std::numeric_limits<uint16_t>::max()); - QpackDecode(&handler, fragment_size_generator, + NoopEncoderStreamErrorDelegate encoder_stream_error_delegate; + NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate; + QpackDecode(&encoder_stream_error_delegate, &decoder_stream_sender_delegate, + &handler, fragment_size_generator, provider.ConsumeRemainingBytesAsString()); return 0;
diff --git a/net/third_party/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc b/net/third_party/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc index f29d230..47d72ee 100644 --- a/net/third_party/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc +++ b/net/third_party/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
@@ -124,12 +124,18 @@ &provider, 1, std::numeric_limits<uint16_t>::max()); // Encode header list. - QuicString encoded_header_block = - QpackEncode(fragment_size_generator, &header_list); + NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; + NoopEncoderStreamSenderDelegate encoder_stream_sender_delegate; + QuicString encoded_header_block = QpackEncode( + &decoder_stream_error_delegate, &encoder_stream_sender_delegate, + fragment_size_generator, &header_list); // Decode header block. TestHeadersHandler handler; - QpackDecode(&handler, fragment_size_generator, encoded_header_block); + NoopEncoderStreamErrorDelegate encoder_stream_error_delegate; + NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate; + QpackDecode(&encoder_stream_error_delegate, &decoder_stream_sender_delegate, + &handler, fragment_size_generator, encoded_header_block); // Since header block has been produced by encoding a header list, it must be // valid.
diff --git a/net/third_party/quic/core/qpack/offline/BUILD b/net/third_party/quic/core/qpack/offline/BUILD new file mode 100644 index 0000000..1ae2bb1 --- /dev/null +++ b/net/third_party/quic/core/qpack/offline/BUILD
@@ -0,0 +1,39 @@ +licenses(["notice"]) # Chromium + +package(default_visibility = ["//third_party/quic:quic_users"]) + +load("//gfe/gfe2/tools:build_defs.bzl", "cc_test_library") + +cc_binary( + name = "qpack_offline_decoder", + testonly = 1, + srcs = ["qpack_offline_decoder_bin.cc"], + deps = [ + ":qpack_offline_decoder_lib", + "//base", + "//file/localfile", + "//third_party/quic/core/qpack:qpack_decoder_test_utils_lib", + "//third_party/quic/core/qpack:qpack_test_utils_lib", + "//third_party/quic/platform/api:quic_logging_lib", + "//third_party/quic/platform/api:quic_string_piece_lib", + ], +) + +cc_test_library( + name = "qpack_offline_decoder_lib", + srcs = ["qpack_offline_decoder.cc"], + hdrs = ["qpack_offline_decoder.h"], + deps = [ + "//third_party/quic/core:quic_types_lib", + "//third_party/quic/core/qpack:qpack_decoder_lib", + "//third_party/quic/core/qpack:qpack_decoder_test_utils_lib", + "//third_party/quic/core/qpack:qpack_test_utils_lib", + "//third_party/quic/platform/api:quic_endian_lib", + "//third_party/quic/platform/api:quic_file_utils_lib", + "//third_party/quic/platform/api:quic_logging_lib", + "//third_party/quic/platform/api:quic_string_lib", + "//third_party/quic/platform/api:quic_string_piece_lib", + "//third_party/quic/platform/api:quic_text_utils_lib", + "//third_party/spdy/core:spdy_header_block_lib", + ], +)
diff --git a/net/third_party/quic/core/qpack/offline/README.md b/net/third_party/quic/core/qpack/offline/README.md new file mode 100644 index 0000000..4f6697c --- /dev/null +++ b/net/third_party/quic/core/qpack/offline/README.md
@@ -0,0 +1,28 @@ +# QPACK Offline Interop Testing tools + +See +[QPACK Offline Interop](https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop) +for description of test data format. + +Example usage: + +```shell +$ # Download test data +$ cd $TEST_DATA +$ git clone https://github.com/qpackers/qifs.git +$ TEST_ENCODED_DATA=`pwd`/qifs/encoded/qpack-03 +$ TEST_QIF_DATA=`pwd`/qifs/qifs +$ +$ # Decode encoded test data in four files and verify that they match +$ # the original headers in corresponding files +$ $BIN/qpack_offline_decoder \ +> $TEST_ENCODED_DATA/f5/fb-req.qifencoded.4096.100.0 \ +> $TEST_QIF_DATA/fb-req.qif +> $TEST_ENCODED_DATA/h2o/fb-req-hq.out.512.0.1 \ +> $TEST_QIF_DATA/fb-req-hq.qif +> $TEST_ENCODED_DATA/ls-qpack/fb-resp-hq.out.0.0.0 \ +> $TEST_QIF_DATA/fb-resp-hq.qif +> $TEST_ENCODED_DATA/proxygen/netbsd.qif.proxygen.out.4096.0.0 \ +> $TEST_QIF_DATA/netbsd.qif +$ +```
diff --git a/net/third_party/quic/core/qpack/offline/qpack_offline_decoder.cc b/net/third_party/quic/core/qpack/offline/qpack_offline_decoder.cc new file mode 100644 index 0000000..03fa5cca --- /dev/null +++ b/net/third_party/quic/core/qpack/offline/qpack_offline_decoder.cc
@@ -0,0 +1,273 @@ +// Copyright (c) 2018 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/third_party/quic/core/qpack/offline/qpack_offline_decoder.h" + +#include <cstdint> +#include <utility> + +#include "net/third_party/quic/core/qpack/qpack_test_utils.h" +#include "net/third_party/quic/core/quic_types.h" +#include "net/third_party/quic/platform/api/quic_endian.h" +#include "net/third_party/quic/platform/api/quic_file_utils.h" +#include "net/third_party/quic/platform/api/quic_logging.h" +#include "net/third_party/quic/platform/api/quic_string.h" +#include "net/third_party/quic/platform/api/quic_text_utils.h" + +namespace quic { + +QpackOfflineDecoder::QpackOfflineDecoder() + : encoder_stream_error_detected_(false), + decoder_(this, &decoder_stream_sender_delegate_) {} + +bool QpackOfflineDecoder::DecodeAndVerifyOfflineData( + QuicStringPiece input_filename, + QuicStringPiece expected_headers_filename) { + if (!ParseInputFilename(input_filename)) { + QUIC_LOG(ERROR) << "Error parsing input filename " << input_filename; + return false; + } + + if (!DecodeHeaderBlocksFromFile(input_filename)) { + QUIC_LOG(ERROR) << "Error decoding header blocks in " << input_filename; + return false; + } + + if (!VerifyDecodedHeaderLists(expected_headers_filename)) { + QUIC_LOG(ERROR) << "Header lists decoded from " << input_filename + << " to not match expected headers parsed from " + << expected_headers_filename; + return false; + } + + return true; +} + +void QpackOfflineDecoder::OnError(QuicStringPiece error_message) { + QUIC_LOG(ERROR) << "Encoder stream error: " << error_message; + encoder_stream_error_detected_ = true; +} + +bool QpackOfflineDecoder::ParseInputFilename(QuicStringPiece input_filename) { + auto pieces = QuicTextUtils::Split(input_filename, '.'); + + if (pieces.size() < 3) { + QUIC_LOG(ERROR) << "Not enough fields in input filename " << input_filename; + return false; + } + + auto piece_it = pieces.rbegin(); + + // Acknowledgement mode: 1 for immediate, 0 for none. + bool immediate_acknowledgement = false; + if (*piece_it == "0") { + immediate_acknowledgement = false; + } else if (*piece_it == "1") { + immediate_acknowledgement = true; + } else { + QUIC_LOG(ERROR) + << "Header acknowledgement field must be 0 or 1 in input filename " + << input_filename; + return false; + } + + ++piece_it; + + // Maximum allowed number of blocked streams. + uint64_t max_blocked_streams = 0; + if (!QuicTextUtils::StringToUint64(*piece_it, &max_blocked_streams)) { + QUIC_LOG(ERROR) << "Error parsing part of input filename \"" << *piece_it + << "\" as an integer."; + return false; + } + + if (max_blocked_streams > 0) { + // TODO(bnc): Implement blocked streams. + QUIC_LOG(ERROR) << "Blocked streams not implemented."; + return false; + } + + ++piece_it; + + // Dynamic Table Size in bytes + uint64_t dynamic_table_size = 0; + if (!QuicTextUtils::StringToUint64(*piece_it, &dynamic_table_size)) { + QUIC_LOG(ERROR) << "Error parsing part of input filename \"" << *piece_it + << "\" as an integer."; + return false; + } + + decoder_.SetMaximumDynamicTableCapacity(dynamic_table_size); + + return true; +} + +bool QpackOfflineDecoder::DecodeHeaderBlocksFromFile( + QuicStringPiece input_filename) { + // Store data in |input_data_storage|; use a QuicStringPiece to efficiently + // keep track of remaining portion yet to be decoded. + QuicString input_data_storage; + ReadFileContents(input_filename, &input_data_storage); + QuicStringPiece input_data(input_data_storage); + + while (!input_data.empty()) { + if (input_data.size() < sizeof(uint64_t) + sizeof(uint32_t)) { + QUIC_LOG(ERROR) << "Unexpected end of input file."; + return false; + } + + uint64_t stream_id = QuicEndian::NetToHost64( + *reinterpret_cast<const uint64_t*>(input_data.data())); + input_data = input_data.substr(sizeof(uint64_t)); + + uint32_t length = QuicEndian::NetToHost32( + *reinterpret_cast<const uint32_t*>(input_data.data())); + input_data = input_data.substr(sizeof(uint32_t)); + + if (input_data.size() < length) { + QUIC_LOG(ERROR) << "Unexpected end of input file."; + return false; + } + + QuicStringPiece data = input_data.substr(0, length); + input_data = input_data.substr(length); + + if (stream_id == 0) { + decoder_.DecodeEncoderStreamData(data); + + if (encoder_stream_error_detected_) { + QUIC_LOG(ERROR) << "Error detected on encoder stream."; + return false; + } + + continue; + } + + test::TestHeadersHandler headers_handler; + + auto progressive_decoder = + decoder_.DecodeHeaderBlock(stream_id, &headers_handler); + progressive_decoder->Decode(data); + progressive_decoder->EndHeaderBlock(); + + if (headers_handler.decoding_error_detected()) { + QUIC_LOG(ERROR) << "Decoding error on stream " << stream_id; + return false; + } + + decoded_header_lists_.push_back(headers_handler.ReleaseHeaderList()); + } + + return true; +} + +bool QpackOfflineDecoder::VerifyDecodedHeaderLists( + QuicStringPiece expected_headers_filename) { + // Store data in |expected_headers_data_storage|; use a QuicStringPiece to + // efficiently keep track of remaining portion yet to be decoded. + QuicString expected_headers_data_storage; + ReadFileContents(expected_headers_filename, &expected_headers_data_storage); + QuicStringPiece expected_headers_data(expected_headers_data_storage); + + while (!decoded_header_lists_.empty()) { + spdy::SpdyHeaderBlock decoded_header_list = + std::move(decoded_header_lists_.front()); + decoded_header_lists_.pop_front(); + + spdy::SpdyHeaderBlock expected_header_list; + if (!ReadNextExpectedHeaderList(&expected_headers_data, + &expected_header_list)) { + QUIC_LOG(ERROR) + << "Error parsing expected header list to match next decoded " + "header list."; + return false; + } + + if (!CompareHeaderBlocks(std::move(decoded_header_list), + std::move(expected_header_list))) { + QUIC_LOG(ERROR) << "Decoded header does not match expected header."; + return false; + } + } + + if (!expected_headers_data.empty()) { + QUIC_LOG(ERROR) + << "Not enough encoded header lists to match expected ones."; + return false; + } + + return true; +} + +bool QpackOfflineDecoder::ReadNextExpectedHeaderList( + QuicStringPiece* expected_headers_data, + spdy::SpdyHeaderBlock* expected_header_list) { + while (true) { + QuicStringPiece::size_type endline = expected_headers_data->find('\n'); + + // Even last header list must be followed by an empty line. + if (endline == QuicStringPiece::npos) { + QUIC_LOG(ERROR) << "Unexpected end of expected header list file."; + return false; + } + + if (endline == 0) { + // Empty line indicates end of header list. + *expected_headers_data = expected_headers_data->substr(1); + return true; + } + + QuicStringPiece header_field = expected_headers_data->substr(0, endline); + auto pieces = QuicTextUtils::Split(header_field, '\t'); + + if (pieces.size() != 2) { + QUIC_LOG(ERROR) << "Header key and value must be separated by TAB."; + return false; + } + + expected_header_list->AppendValueOrAddHeader(pieces[0], pieces[1]); + + *expected_headers_data = expected_headers_data->substr(endline + 1); + } +} + +bool QpackOfflineDecoder::CompareHeaderBlocks( + spdy::SpdyHeaderBlock decoded_header_list, + spdy::SpdyHeaderBlock expected_header_list) { + if (decoded_header_list == expected_header_list) { + return true; + } + + // The h2o decoder reshuffles the "content-length" header and pseudo-headers, + // see + // https://github.com/qpackers/qifs/blob/master/encoded/qpack-03/h2o/README.md. + // Remove such headers one by one if they match. + const char* kContentLength = "content-length"; + const char* kPseudoHeaderPrefix = ":"; + for (spdy::SpdyHeaderBlock::iterator decoded_it = decoded_header_list.begin(); + decoded_it != decoded_header_list.end();) { + const QuicStringPiece key = decoded_it->first; + if (key != kContentLength && + !QuicTextUtils::StartsWith(key, kPseudoHeaderPrefix)) { + ++decoded_it; + continue; + } + spdy::SpdyHeaderBlock::iterator expected_it = + expected_header_list.find(key); + if (expected_it == expected_header_list.end() || + decoded_it->second != expected_it->second) { + ++decoded_it; + continue; + } + // SpdyHeaderBlock does not support erasing by iterator, only by key. + ++decoded_it; + expected_header_list.erase(key); + // This will invalidate |key|. + decoded_header_list.erase(key); + } + + return decoded_header_list == expected_header_list; +} + +} // namespace quic
diff --git a/net/third_party/quic/core/qpack/offline/qpack_offline_decoder.h b/net/third_party/quic/core/qpack/offline/qpack_offline_decoder.h new file mode 100644 index 0000000..8866549 --- /dev/null +++ b/net/third_party/quic/core/qpack/offline/qpack_offline_decoder.h
@@ -0,0 +1,69 @@ +// Copyright (c) 2018 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_THIRD_PARTY_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_ +#define NET_THIRD_PARTY_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_ + +#include "net/third_party/quic/core/qpack/qpack_decoder.h" +#include "net/third_party/quic/core/qpack/qpack_decoder_test_utils.h" +#include "net/third_party/quic/platform/api/quic_string_piece.h" +#include "net/third_party/spdy/core/spdy_header_block.h" + +namespace quic { + +// A decoder to read encoded data from a file, decode it, and compare to +// a list of expected header lists read from another file. File format is +// described at +// https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop. +class QpackOfflineDecoder : public QpackDecoder::EncoderStreamErrorDelegate { + public: + QpackOfflineDecoder(); + ~QpackOfflineDecoder() override = default; + + // Read encoded header blocks and encoder stream data from |input_filename| + // and decode them, read expected header lists from + // |expected_headers_filename|, and compare decoded header lists to expected + // ones. Returns true if there is an equal number of them and the + // corresponding ones match, false otherwise. + bool DecodeAndVerifyOfflineData(QuicStringPiece input_filename, + QuicStringPiece expected_headers_filename); + + // QpackDecoder::EncoderStreamErrorDelegate implementation: + void OnError(QuicStringPiece error_message) override; + + private: + // Parse decoder parameters from |input_filename| and set up |decoder_| + // accordingly. + bool ParseInputFilename(QuicStringPiece input_filename); + + // Read encoded header blocks and encoder stream data from |input_filename|, + // pass them to |decoder_| for decoding, and add decoded header lists to + // |decoded_header_lists_|. + bool DecodeHeaderBlocksFromFile(QuicStringPiece input_filename); + + // Read expected header lists from |expected_headers_filename| and verify + // decoded header lists in |decoded_header_lists_| against them. + bool VerifyDecodedHeaderLists(QuicStringPiece expected_headers_filename); + + // Parse next header list from |*expected_headers_data| into + // |*expected_header_list|, removing consumed data from the beginning of + // |*expected_headers_data|. Returns true on success, false if parsing fails. + bool ReadNextExpectedHeaderList(QuicStringPiece* expected_headers_data, + spdy::SpdyHeaderBlock* expected_header_list); + + // Compare two header lists. Allow for different orders of certain headers as + // described at + // https://github.com/qpackers/qifs/blob/master/encoded/qpack-03/h2o/README.md. + bool CompareHeaderBlocks(spdy::SpdyHeaderBlock decoded_header_list, + spdy::SpdyHeaderBlock expected_header_list); + + bool encoder_stream_error_detected_; + test::NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate_; + QpackDecoder decoder_; + std::list<spdy::SpdyHeaderBlock> decoded_header_lists_; +}; + +} // namespace quic + +#endif // NET_THIRD_PARTY_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_
diff --git a/net/third_party/quic/core/qpack/offline/qpack_offline_decoder_bin.cc b/net/third_party/quic/core/qpack/offline/qpack_offline_decoder_bin.cc new file mode 100644 index 0000000..edc6d7db --- /dev/null +++ b/net/third_party/quic/core/qpack/offline/qpack_offline_decoder_bin.cc
@@ -0,0 +1,41 @@ +// Copyright (c) 2018 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/third_party/quic/core/qpack/offline/qpack_offline_decoder.h" + +#include <cstddef> +#include <iostream> + +#include "base/command_line.h" +#include "net/third_party/quic/platform/api/quic_logging.h" +#include "net/third_party/quic/platform/api/quic_string_piece.h" + +int main(int argc, char* argv[]) { + base::CommandLine::Init(argc, argv); + + if (argc < 3 || argc % 2 != 1) { + QUIC_LOG(ERROR) << "Usage: " << argv[0] + << " input_filename expected_headers_filename ..."; + return 1; + } + + int i; + for (i = 0; 2 * i + 1 < argc; ++i) { + const quic::QuicStringPiece input_filename(argv[2 * i + 1]); + const quic::QuicStringPiece expected_headers_filename(argv[2 * i + 2]); + + // Every file represents a different connection, + // therefore every file needs a fresh decoding context. + quic::QpackOfflineDecoder decoder; + if (!decoder.DecodeAndVerifyOfflineData(input_filename, + expected_headers_filename)) { + return 1; + } + } + + std::cout << "Successfully verified " << i << " pairs of input files." + << std::endl; + + return 0; +}
diff --git a/net/third_party/quic/core/qpack/qpack_constants.cc b/net/third_party/quic/core/qpack/qpack_constants.cc index 579f49b6..2f97727 100644 --- a/net/third_party/quic/core/qpack/qpack_constants.cc +++ b/net/third_party/quic/core/qpack/qpack_constants.cc
@@ -4,8 +4,37 @@ #include "net/third_party/quic/core/qpack/qpack_constants.h" +#include <limits> + +#include "base/logging.h" + namespace quic { +namespace { + +// Validate that +// * in each instruction, the bits of |value| that are zero in |mask| are zero; +// * every byte matches exactly one opcode. +void ValidateLangague(const QpackLanguage* language) { +#ifndef NDEBUG + for (const auto* instruction : *language) { + DCHECK_EQ(0, instruction->opcode.value & ~instruction->opcode.mask); + } + + for (uint8_t byte = 0; byte < std::numeric_limits<uint8_t>::max(); ++byte) { + size_t match_count = 0; + for (const auto* instruction : *language) { + if ((byte & instruction->opcode.mask) == instruction->opcode.value) { + ++match_count; + } + } + DCHECK_EQ(1u, match_count) << static_cast<int>(byte); + } +#endif +} + +} // namespace + bool operator==(const QpackInstructionOpcode& a, const QpackInstructionOpcode& b) { return std::tie(a.value, a.mask) == std::tie(b.value, b.mask); @@ -53,6 +82,7 @@ InsertWithNameReferenceInstruction(), InsertWithoutNameReferenceInstruction(), DuplicateInstruction(), DynamicTableSizeUpdateInstruction()}; + ValidateLangague(language); return language; } @@ -84,6 +114,7 @@ static const QpackLanguage* const language = new QpackLanguage{ TableStateSynchronizeInstruction(), HeaderAcknowledgementInstruction(), StreamCancellationInstruction()}; + ValidateLangague(language); return language; } @@ -102,6 +133,7 @@ const QpackLanguage* QpackPrefixLanguage() { static const QpackLanguage* const language = new QpackLanguage{QpackPrefixInstruction()}; + ValidateLangague(language); return language; } @@ -161,6 +193,7 @@ QpackLiteralHeaderFieldNameReferenceInstruction(), QpackLiteralHeaderFieldPostBaseInstruction(), QpackLiteralHeaderFieldInstruction()}; + ValidateLangague(language); return language; }
diff --git a/net/third_party/quic/core/qpack/qpack_constants.h b/net/third_party/quic/core/qpack/qpack_constants.h index 163d376..6e7cf89 100644 --- a/net/third_party/quic/core/qpack/qpack_constants.h +++ b/net/third_party/quic/core/qpack/qpack_constants.h
@@ -74,7 +74,7 @@ }; // A language is a collection of instructions. The order does not matter. -// The set of instruction opcodes must cover all possible input. +// Every possible input must match exactly one instruction. using QpackLanguage = std::vector<const QpackInstruction*>; // TODO(bnc): Move this into HpackVarintEncoder.
diff --git a/net/third_party/quic/core/qpack/qpack_decoder.cc b/net/third_party/quic/core/qpack/qpack_decoder.cc index 8311acb..ae0ba5f 100644 --- a/net/third_party/quic/core/qpack/qpack_decoder.cc +++ b/net/third_party/quic/core/qpack/qpack_decoder.cc
@@ -12,149 +12,124 @@ namespace quic { -QpackDecoder::ProgressiveDecoder::ProgressiveDecoder( - QuicStreamId stream_id, - QpackHeaderTable* header_table, - QpackDecoder::HeadersHandlerInterface* handler) - : stream_id_(stream_id), - prefix_decoder_( - QuicMakeUnique<QpackInstructionDecoder>(QpackPrefixLanguage(), this)), - instruction_decoder_(QpackRequestStreamLanguage(), this), - header_table_(header_table), - handler_(handler), - largest_reference_(0), - base_index_(0), - prefix_decoded_(false), - decoding_(true), - error_detected_(false) {} +QpackDecoder::QpackDecoder( + EncoderStreamErrorDelegate* encoder_stream_error_delegate, + QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate) + : encoder_stream_error_delegate_(encoder_stream_error_delegate), + encoder_stream_receiver_(this), + decoder_stream_sender_(decoder_stream_sender_delegate) { + DCHECK(encoder_stream_error_delegate_); + DCHECK(decoder_stream_sender_delegate); +} -void QpackDecoder::ProgressiveDecoder::Decode(QuicStringPiece data) { - DCHECK(decoding_); +QpackDecoder::~QpackDecoder() {} - if (data.empty() || error_detected_) { - return; - } +void QpackDecoder::SetMaximumDynamicTableCapacity( + uint64_t maximum_dynamic_table_capacity) { + header_table_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity); +} - // Decode prefix byte by byte until the first (and only) instruction is - // decoded. - while (!prefix_decoded_) { - prefix_decoder_->Decode(data.substr(0, 1)); - data = data.substr(1, QuicStringPiece::npos); - if (data.empty()) { +void QpackDecoder::OnStreamReset(QuicStreamId stream_id) { + decoder_stream_sender_.SendStreamCancellation(stream_id); +} + +void QpackDecoder::DecodeEncoderStreamData(QuicStringPiece data) { + encoder_stream_receiver_.Decode(data); +} + +void QpackDecoder::OnInsertWithNameReference(bool is_static, + uint64_t name_index, + QuicStringPiece value) { + if (is_static) { + auto entry = header_table_.LookupEntry(/* is_static = */ true, name_index); + if (!entry) { + encoder_stream_error_delegate_->OnError("Invalid static table entry."); return; } - } - instruction_decoder_.Decode(data); -} - -void QpackDecoder::ProgressiveDecoder::EndHeaderBlock() { - DCHECK(decoding_); - decoding_ = false; - - if (error_detected_) { - return; - } - - if (!instruction_decoder_.AtInstructionBoundary()) { - OnError("Incomplete header block."); - return; - } - - if (!prefix_decoded_) { - OnError("Incomplete header data prefix."); - return; - } - - handler_->OnDecodingCompleted(); -} - -bool QpackDecoder::ProgressiveDecoder::OnInstructionDecoded( - const QpackInstruction* instruction) { - if (instruction == QpackPrefixInstruction()) { - DCHECK(!prefix_decoded_); - - largest_reference_ = instruction_decoder_.varint(); - base_index_ = instruction_decoder_.varint2(); - prefix_decoded_ = true; - - return true; - } - - if (instruction == QpackIndexedHeaderFieldInstruction()) { - if (!instruction_decoder_.s_bit()) { - // TODO(bnc): Implement. - OnError("Indexed Header Field with dynamic entry not implemented."); - return false; - } - - auto entry = header_table_->LookupEntry(/* is_static = */ true, - instruction_decoder_.varint()); + entry = header_table_.InsertEntry(entry->name(), value); if (!entry) { - OnError("Invalid static table index."); - return false; + encoder_stream_error_delegate_->OnError( + "Error inserting entry with name reference."); } - - handler_->OnHeaderDecoded(entry->name(), entry->value()); - return true; + return; } - if (instruction == QpackIndexedHeaderFieldPostBaseInstruction()) { - // TODO(bnc): Implement. - OnError("Indexed Header Field With Post-Base Index not implemented."); + uint64_t real_index; + if (!EncoderStreamRelativeIndexToRealIndex(name_index, &real_index)) { + encoder_stream_error_delegate_->OnError("Invalid relative index."); + return; + } + + const QpackEntry* entry = + header_table_.LookupEntry(/* is_static = */ false, real_index); + if (!entry) { + encoder_stream_error_delegate_->OnError("Dynamic table entry not found."); + return; + } + entry = header_table_.InsertEntry(entry->name(), value); + if (!entry) { + encoder_stream_error_delegate_->OnError( + "Error inserting entry with name reference."); + } +} + +void QpackDecoder::OnInsertWithoutNameReference(QuicStringPiece name, + QuicStringPiece value) { + const QpackEntry* entry = header_table_.InsertEntry(name, value); + if (!entry) { + encoder_stream_error_delegate_->OnError("Error inserting literal entry."); + } +} + +void QpackDecoder::OnDuplicate(uint64_t index) { + uint64_t real_index; + if (!EncoderStreamRelativeIndexToRealIndex(index, &real_index)) { + encoder_stream_error_delegate_->OnError("Invalid relative index."); + return; + } + + const QpackEntry* entry = + header_table_.LookupEntry(/* is_static = */ false, real_index); + if (!entry) { + encoder_stream_error_delegate_->OnError("Dynamic table entry not found."); + return; + } + entry = header_table_.InsertEntry(entry->name(), entry->value()); + if (!entry) { + encoder_stream_error_delegate_->OnError("Error inserting duplicate entry."); + } +} + +void QpackDecoder::OnDynamicTableSizeUpdate(uint64_t max_size) { + if (!header_table_.UpdateTableSize(max_size)) { + encoder_stream_error_delegate_->OnError( + "Error updating dynamic table size."); + } +} + +void QpackDecoder::OnErrorDetected(QuicStringPiece error_message) { + encoder_stream_error_delegate_->OnError(error_message); +} + +bool QpackDecoder::EncoderStreamRelativeIndexToRealIndex( + uint64_t relative_index, + uint64_t* real_index) const { + if (relative_index == std::numeric_limits<uint64_t>::max() || + relative_index + 1 > std::numeric_limits<uint64_t>::max() - + header_table_.inserted_entry_count()) { return false; } - if (instruction == QpackLiteralHeaderFieldNameReferenceInstruction()) { - if (!instruction_decoder_.s_bit()) { - // TODO(bnc): Implement. - OnError( - "Literal Header Field With Name Reference with dynamic entry not " - "implemented."); - return false; - } - - auto entry = header_table_->LookupEntry(/* is_static = */ true, - instruction_decoder_.varint()); - if (!entry) { - OnError( - "Invalid static table index in Literal Header Field With Name " - "Reference instruction."); - return false; - } - - handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); - return true; - } - - if (instruction == QpackLiteralHeaderFieldPostBaseInstruction()) { - // TODO(bnc): Implement. - OnError( - "Literal Header Field With Post-Base Name Reference not " - "implemented."); - return false; - } - - DCHECK_EQ(instruction, QpackLiteralHeaderFieldInstruction()); - - handler_->OnHeaderDecoded(instruction_decoder_.name(), - instruction_decoder_.value()); - + *real_index = header_table_.inserted_entry_count() - relative_index - 1; return true; } -void QpackDecoder::ProgressiveDecoder::OnError(QuicStringPiece error_message) { - DCHECK(!error_detected_); - - error_detected_ = true; - handler_->OnDecodingErrorDetected(error_message); -} - -std::unique_ptr<QpackDecoder::ProgressiveDecoder> -QpackDecoder::DecodeHeaderBlock( +std::unique_ptr<QpackProgressiveDecoder> QpackDecoder::DecodeHeaderBlock( QuicStreamId stream_id, - QpackDecoder::HeadersHandlerInterface* handler) { - return QuicMakeUnique<ProgressiveDecoder>(stream_id, &header_table_, handler); + QpackProgressiveDecoder::HeadersHandlerInterface* handler) { + return QuicMakeUnique<QpackProgressiveDecoder>( + stream_id, &header_table_, &decoder_stream_sender_, handler); } } // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_decoder.h b/net/third_party/quic/core/qpack/qpack_decoder.h index fb238ed3..38617582 100644 --- a/net/third_party/quic/core/qpack/qpack_decoder.h +++ b/net/third_party/quic/core/qpack/qpack_decoder.h
@@ -5,105 +5,96 @@ #ifndef NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_DECODER_H_ #define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_DECODER_H_ +#include <cstdint> #include <memory> +#include "net/third_party/quic/core/qpack/qpack_decoder_stream_sender.h" +#include "net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h" #include "net/third_party/quic/core/qpack/qpack_header_table.h" -#include "net/third_party/quic/core/qpack/qpack_instruction_decoder.h" +#include "net/third_party/quic/core/qpack/qpack_progressive_decoder.h" #include "net/third_party/quic/core/quic_types.h" #include "net/third_party/quic/platform/api/quic_export.h" -#include "net/third_party/quic/platform/api/quic_string.h" #include "net/third_party/quic/platform/api/quic_string_piece.h" namespace quic { // QPACK decoder class. Exactly one instance should exist per QUIC connection. -// This class vends a new ProgressiveDecoder instance for each new header list -// to be encoded. -// TODO(bnc): This class will manage the decoding context, send data on the -// decoder stream, and receive data on the encoder stream. -class QUIC_EXPORT_PRIVATE QpackDecoder { +// This class vends a new QpackProgressiveDecoder instance for each new header +// list to be encoded. +class QUIC_EXPORT_PRIVATE QpackDecoder + : public QpackEncoderStreamReceiver::Delegate { public: - // Interface for receiving decoded header block from the decoder. - class QUIC_EXPORT_PRIVATE HeadersHandlerInterface { + // Interface for receiving notification that an error has occurred on the + // encoder stream. This MUST be treated as a connection error of type + // HTTP_QPACK_ENCODER_STREAM_ERROR. + class QUIC_EXPORT_PRIVATE EncoderStreamErrorDelegate { public: - virtual ~HeadersHandlerInterface() {} + virtual ~EncoderStreamErrorDelegate() {} - // Called when a new header name-value pair is decoded. Multiple values for - // a given name will be emitted as multiple calls to OnHeader. - virtual void OnHeaderDecoded(QuicStringPiece name, - QuicStringPiece value) = 0; - - // Called when the header block is completely decoded. - // Indicates the total number of bytes in this block. - // The decoder will not access the handler after this call. - // Note that this method might not be called synchronously when the header - // block is received on the wire, in case decoding is blocked on receiving - // entries on the encoder stream. TODO(bnc): Implement blocked decoding. - virtual void OnDecodingCompleted() = 0; - - // Called when a decoding error has occurred. No other methods will be - // called afterwards. - virtual void OnDecodingErrorDetected(QuicStringPiece error_message) = 0; + virtual void OnError(QuicStringPiece error_message) = 0; }; - // Class to decode a single header block. - class QUIC_EXPORT_PRIVATE ProgressiveDecoder - : public QpackInstructionDecoder::Delegate { - public: - ProgressiveDecoder() = delete; - ProgressiveDecoder(QuicStreamId stream_id, - QpackHeaderTable* header_table, - HeadersHandlerInterface* handler); - ProgressiveDecoder(const ProgressiveDecoder&) = delete; - ProgressiveDecoder& operator=(const ProgressiveDecoder&) = delete; - ~ProgressiveDecoder() override = default; + QpackDecoder( + EncoderStreamErrorDelegate* encoder_stream_error_delegate, + QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate); + ~QpackDecoder() override; - // Provide a data fragment to decode. - void Decode(QuicStringPiece data); + // Set maximum capacity of dynamic table. + // This method must only be called at most once. + void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); - // Signal that the entire header block has been received and passed in - // through Decode(). No methods must be called afterwards. - void EndHeaderBlock(); + // Signal to the peer's encoder that a stream is reset. This lets the peer's + // encoder know that no more header blocks will be processed on this stream, + // therefore references to dynamic table entries shall not prevent their + // eviction. + // This method should be called regardless of whether a header block is being + // decoded on that stream, because a header block might be in flight from the + // peer. + // This method should be called every time a request or push stream is reset + // for any reason: for example, client cancels request, or a decoding error + // occurs and HeadersHandlerInterface::OnDecodingErrorDetected() is called. + // This method should also be called if the stream is reset by the peer, + // because the peer's encoder can only evict entries referenced by header + // blocks once it receives acknowledgement from this endpoint that the stream + // is reset. + // However, this method should not be called if the stream is closed normally + // using the FIN bit. + void OnStreamReset(QuicStreamId stream_id); - // QpackInstructionDecoder::Delegate implementation. - bool OnInstructionDecoded(const QpackInstruction* instruction) override; - void OnError(QuicStringPiece error_message) override; - - // TODO(zhongyi): remove this method once internal change lands: - QuicStreamId stream_id() const { return stream_id_; } - - private: - const QuicStreamId stream_id_; - - // |prefix_decoder_| only decodes a handful of bytes then it can be - // destroyed to conserve memory. |instruction_decoder_|, on the other hand, - // is used until the entire header block is decoded. - std::unique_ptr<QpackInstructionDecoder> prefix_decoder_; - QpackInstructionDecoder instruction_decoder_; - - const QpackHeaderTable* const header_table_; - HeadersHandlerInterface* handler_; - size_t largest_reference_; - size_t base_index_; - - // False until prefix is fully read and decoded. - bool prefix_decoded_; - - // True until EndHeaderBlock() is called. - bool decoding_; - - // True if a decoding error has been detected. - bool error_detected_; - }; - - // Factory method to create a ProgressiveDecoder for decoding a header block. - // |handler| must remain valid until the returned ProgressiveDecoder instance - // is destroyed or the decoder calls |handler->OnHeaderBlockEnd()|. - std::unique_ptr<ProgressiveDecoder> DecodeHeaderBlock( + // Factory method to create a QpackProgressiveDecoder for decoding a header + // block. |handler| must remain valid until the returned + // QpackProgressiveDecoder instance is destroyed or the decoder calls + // |handler->OnHeaderBlockEnd()|. + std::unique_ptr<QpackProgressiveDecoder> DecodeHeaderBlock( QuicStreamId stream_id, - HeadersHandlerInterface* handler); + QpackProgressiveDecoder::HeadersHandlerInterface* handler); + + // Decode data received on the encoder stream. + void DecodeEncoderStreamData(QuicStringPiece data); + + // QpackEncoderStreamReceiver::Delegate implementation + void OnInsertWithNameReference(bool is_static, + uint64_t name_index, + QuicStringPiece value) override; + void OnInsertWithoutNameReference(QuicStringPiece name, + QuicStringPiece value) override; + void OnDuplicate(uint64_t index) override; + void OnDynamicTableSizeUpdate(uint64_t max_size) override; + void OnErrorDetected(QuicStringPiece error_message) override; private: + // The encoder stream uses relative index (but different from the kind of + // relative index used on a request stream). + // The spec describes how to convert these into absolute index (one based). + // QpackHeaderTable uses real index (zero based, one less than the absolute + // index). This method converts relative index to real index. It returns + // true on success, or false if conversion fails due to overflow/underflow. + bool EncoderStreamRelativeIndexToRealIndex(uint64_t relative_index, + uint64_t* real_index) const; + + EncoderStreamErrorDelegate* const encoder_stream_error_delegate_; + QpackEncoderStreamReceiver encoder_stream_receiver_; + QpackDecoderStreamSender decoder_stream_sender_; QpackHeaderTable header_table_; };
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.h b/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.h index 54511083..b9fd17e 100644 --- a/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.h +++ b/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.h
@@ -8,12 +8,14 @@ #include <cstdint> #include "net/third_party/quic/core/qpack/qpack_instruction_decoder.h" +#include "net/third_party/quic/core/quic_types.h" #include "net/third_party/quic/platform/api/quic_export.h" #include "net/third_party/quic/platform/api/quic_string_piece.h" namespace quic { -// This class decodes data received on the decoder stream. +// This class decodes data received on the decoder stream, +// and passes it along to its Delegate. class QUIC_EXPORT_PRIVATE QpackDecoderStreamReceiver : public QpackInstructionDecoder::Delegate { public: @@ -26,9 +28,9 @@ // 5.3.1 Table State Synchronize virtual void OnTableStateSynchronize(uint64_t insert_count) = 0; // 5.3.2 Header Acknowledgement - virtual void OnHeaderAcknowledgement(uint64_t stream_id) = 0; + virtual void OnHeaderAcknowledgement(QuicStreamId stream_id) = 0; // 5.3.3 Stream Cancellation - virtual void OnStreamCancellation(uint64_t stream_id) = 0; + virtual void OnStreamCancellation(QuicStreamId stream_id) = 0; // Decoding error virtual void OnErrorDetected(QuicStringPiece error_message) = 0; };
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver_test.cc b/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver_test.cc index 9c166f4d..ae2afba 100644 --- a/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver_test.cc +++ b/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
@@ -21,8 +21,8 @@ ~MockDelegate() override = default; MOCK_METHOD1(OnTableStateSynchronize, void(uint64_t insert_count)); - MOCK_METHOD1(OnHeaderAcknowledgement, void(uint64_t stream_id)); - MOCK_METHOD1(OnStreamCancellation, void(uint64_t stream_id)); + MOCK_METHOD1(OnHeaderAcknowledgement, void(QuicStreamId stream_id)); + MOCK_METHOD1(OnStreamCancellation, void(QuicStreamId stream_id)); MOCK_METHOD1(OnErrorDetected, void(QuicStringPiece error_message)); };
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.cc b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.cc index 4c05fa56e2..9ff09d9 100644 --- a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.cc +++ b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.cc
@@ -4,6 +4,7 @@ #include "net/third_party/quic/core/qpack/qpack_decoder_stream_sender.h" +#include <cstddef> #include <limits> #include "net/third_party/quic/core/qpack/qpack_constants.h" @@ -31,7 +32,8 @@ delegate_->Write(output); } -void QpackDecoderStreamSender::SendHeaderAcknowledgement(uint64_t stream_id) { +void QpackDecoderStreamSender::SendHeaderAcknowledgement( + QuicStreamId stream_id) { instruction_encoder_.set_varint(stream_id); instruction_encoder_.Encode(HeaderAcknowledgementInstruction()); @@ -44,7 +46,7 @@ delegate_->Write(output); } -void QpackDecoderStreamSender::SendStreamCancellation(uint64_t stream_id) { +void QpackDecoderStreamSender::SendStreamCancellation(QuicStreamId stream_id) { instruction_encoder_.set_varint(stream_id); instruction_encoder_.Encode(StreamCancellationInstruction());
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.h b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.h index 283d656..bc47649 100644 --- a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.h +++ b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.h
@@ -8,6 +8,7 @@ #include <cstdint> #include "net/third_party/quic/core/qpack/qpack_instruction_encoder.h" +#include "net/third_party/quic/core/quic_types.h" #include "net/third_party/quic/platform/api/quic_export.h" #include "net/third_party/quic/platform/api/quic_string_piece.h" @@ -39,9 +40,9 @@ // 5.3.1 Table State Synchronize void SendTableStateSynchronize(uint64_t insert_count); // 5.3.2 Header Acknowledgement - void SendHeaderAcknowledgement(uint64_t stream_id); + void SendHeaderAcknowledgement(QuicStreamId stream_id); // 5.3.3 Stream Cancellation - void SendStreamCancellation(uint64_t stream_id); + void SendStreamCancellation(QuicStreamId stream_id); private: Delegate* const delegate_;
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender_test.cc b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender_test.cc index 35c9284c..1b1ed1d 100644 --- a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender_test.cc +++ b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender_test.cc
@@ -26,6 +26,7 @@ class QpackDecoderStreamSenderTest : public QuicTest { protected: QpackDecoderStreamSenderTest() : stream_(&delegate_) {} + ~QpackDecoderStreamSenderTest() override = default; StrictMock<MockSenderDelegate> delegate_; QpackDecoderStreamSender stream_;
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_test.cc b/net/third_party/quic/core/qpack/qpack_decoder_test.cc index 4710c3b..e6bb78e 100644 --- a/net/third_party/quic/core/qpack/qpack_decoder_test.cc +++ b/net/third_party/quic/core/qpack/qpack_decoder_test.cc
@@ -11,6 +11,8 @@ #include "net/third_party/quic/platform/api/quic_text_utils.h" #include "net/third_party/spdy/core/spdy_header_block.h" +using ::testing::Eq; +using ::testing::Sequence; using ::testing::StrictMock; using ::testing::Values; @@ -18,33 +20,43 @@ namespace test { namespace { -class MockHeadersHandler : public QpackDecoder::HeadersHandlerInterface { - public: - MockHeadersHandler() = default; - MockHeadersHandler(const MockHeadersHandler&) = delete; - MockHeadersHandler& operator=(const MockHeadersHandler&) = delete; - ~MockHeadersHandler() override = default; - - MOCK_METHOD2(OnHeaderDecoded, - void(QuicStringPiece name, QuicStringPiece value)); - MOCK_METHOD0(OnDecodingCompleted, void()); - MOCK_METHOD1(OnDecodingErrorDetected, void(QuicStringPiece error_message)); -}; +// Header Acknowledgement decoder stream instruction with stream_id = 1. +const char* const kHeaderAcknowledgement = "\x81"; class QpackDecoderTest : public QuicTestWithParam<FragmentMode> { - public: - QpackDecoderTest() : fragment_mode_(GetParam()) {} - ~QpackDecoderTest() override = default; - - void Decode(QuicStringPiece data) { - QpackDecode(&handler_, FragmentModeToFragmentSizeGenerator(fragment_mode_), - data); + protected: + QpackDecoderTest() + : qpack_decoder_(&encoder_stream_error_delegate_, + &decoder_stream_sender_delegate_), + fragment_mode_(GetParam()) { + qpack_decoder_.SetMaximumDynamicTableCapacity(1024); } - protected: + ~QpackDecoderTest() override = default; + + void DecodeEncoderStreamData(QuicStringPiece data) { + qpack_decoder_.DecodeEncoderStreamData(data); + } + + void DecodeHeaderBlock(QuicStringPiece data) { + auto fragment_size_generator = + FragmentModeToFragmentSizeGenerator(fragment_mode_); + auto progressive_decoder = + qpack_decoder_.DecodeHeaderBlock(/* stream_id = */ 1, &handler_); + while (!data.empty()) { + size_t fragment_size = std::min(fragment_size_generator(), data.size()); + progressive_decoder->Decode(data.substr(0, fragment_size)); + data = data.substr(fragment_size); + } + progressive_decoder->EndHeaderBlock(); + } + + StrictMock<MockEncoderStreamErrorDelegate> encoder_stream_error_delegate_; + StrictMock<MockDecoderStreamSenderDelegate> decoder_stream_sender_delegate_; StrictMock<MockHeadersHandler> handler_; private: + QpackDecoder qpack_decoder_; const FragmentMode fragment_mode_; }; @@ -57,61 +69,69 @@ EXPECT_CALL(handler_, OnDecodingErrorDetected( QuicStringPiece("Incomplete header data prefix."))); - QpackDecoder decoder; - auto progressive_decoder = - decoder.DecodeHeaderBlock(/* stream_id = */ 1, &handler_); // Header Data Prefix is at least two bytes long. - progressive_decoder->Decode(QuicTextUtils::HexDecode("00")); - progressive_decoder->EndHeaderBlock(); + DecodeHeaderBlock(QuicTextUtils::HexDecode("00")); } -TEST_P(QpackDecoderTest, Empty) { +TEST_P(QpackDecoderTest, EmptyHeaderBlock) { EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))); - Decode(QuicTextUtils::HexDecode("0000")); + DecodeHeaderBlock(QuicTextUtils::HexDecode("0000")); } -TEST_P(QpackDecoderTest, EmptyName) { +TEST_P(QpackDecoderTest, LiteralEntryEmptyName) { EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(""), QuicStringPiece("foo"))); EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))); - Decode(QuicTextUtils::HexDecode("00002003666f6f")); + DecodeHeaderBlock(QuicTextUtils::HexDecode("00002003666f6f")); } -TEST_P(QpackDecoderTest, EmptyValue) { +TEST_P(QpackDecoderTest, LiteralEntryEmptyValue) { EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece(""))); EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))); - Decode(QuicTextUtils::HexDecode("000023666f6f00")); + DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f00")); } -TEST_P(QpackDecoderTest, EmptyNameAndValue) { +TEST_P(QpackDecoderTest, LiteralEntryEmptyNameAndValue) { EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(""), QuicStringPiece(""))); EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))); - Decode(QuicTextUtils::HexDecode("00002000")); + DecodeHeaderBlock(QuicTextUtils::HexDecode("00002000")); } -TEST_P(QpackDecoderTest, Simple) { +TEST_P(QpackDecoderTest, SimpleLiteralEntry) { EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar"))); EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))); - Decode(QuicTextUtils::HexDecode("000023666f6f03626172")); + DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f03626172")); } -TEST_P(QpackDecoderTest, Multiple) { +TEST_P(QpackDecoderTest, MultipleLiteralEntries) { EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar"))); QuicString str(127, 'a'); EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("foobaar"), QuicStringPiece(str))); EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))); - Decode(QuicTextUtils::HexDecode( + DecodeHeaderBlock(QuicTextUtils::HexDecode( "0000" // prefix "23666f6f03626172" // foo: bar "2700666f6f62616172" // 7 octet long header name, the smallest number @@ -124,11 +144,12 @@ "616161616161")); } -TEST_P(QpackDecoderTest, NameLenTooLarge) { +// Name Length value is too large for varint decoder to decode. +TEST_P(QpackDecoderTest, NameLenTooLargeForVarintDecoder) { EXPECT_CALL(handler_, OnDecodingErrorDetected( QuicStringPiece("Encoded integer too large."))); - Decode(QuicTextUtils::HexDecode("000027ffffffffffffffffffff")); + DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffffffffffffffffffff")); } // Name Length value can be decoded by varint decoder but exceeds 1 MB limit. @@ -136,7 +157,7 @@ EXPECT_CALL(handler_, OnDecodingErrorDetected( QuicStringPiece("String literal too long."))); - Decode(QuicTextUtils::HexDecode("000027ffff7f")); + DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffff7f")); } // Value Length value is too large for varint decoder to decode. @@ -144,22 +165,33 @@ EXPECT_CALL(handler_, OnDecodingErrorDetected( QuicStringPiece("Encoded integer too large."))); - Decode(QuicTextUtils::HexDecode("000023666f6f7fffffffffffffffffffff")); + DecodeHeaderBlock( + QuicTextUtils::HexDecode("000023666f6f7fffffffffffffffffffff")); +} + +// Value Length value can be decoded by varint decoder but exceeds 1 MB limit. +TEST_P(QpackDecoderTest, ValueLenExceedsLimit) { + EXPECT_CALL(handler_, OnDecodingErrorDetected( + QuicStringPiece("String literal too long."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f7fffff7f")); } TEST_P(QpackDecoderTest, IncompleteHeaderBlock) { EXPECT_CALL(handler_, OnDecodingErrorDetected( QuicStringPiece("Incomplete header block."))); - Decode(QuicTextUtils::HexDecode("00002366")); + DecodeHeaderBlock(QuicTextUtils::HexDecode("00002366")); } TEST_P(QpackDecoderTest, HuffmanSimple) { EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("custom-key"), QuicStringPiece("custom-value"))); EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))); - Decode(QuicTextUtils::HexDecode( + DecodeHeaderBlock(QuicTextUtils::HexDecode( QuicStringPiece("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf"))); } @@ -168,8 +200,10 @@ QuicStringPiece("custom-value"))) .Times(4); EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))); - Decode(QuicTextUtils::HexDecode( + DecodeHeaderBlock(QuicTextUtils::HexDecode( "0000" // Prefix. "2f0125a849e95ba97d7f" // Huffman-encoded name. "8925a849e95bb8e8b4bf" // Huffman-encoded value. @@ -188,7 +222,7 @@ // 'y' ends in 0b0 on the most significant bit of the last byte. // The remaining 7 bits must be a prefix of EOS, which is all 1s. - Decode( + DecodeHeaderBlock( QuicTextUtils::HexDecode("00002f0125a849e95ba97d7e8925a849e95bb8e8b4bf")); } @@ -198,7 +232,7 @@ // 'e' ends in 0b101, taking up the 3 most significant bits of the last byte. // The remaining 5 bits must be a prefix of EOS, which is all 1s. - Decode( + DecodeHeaderBlock( QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4be")); } @@ -209,7 +243,7 @@ // The trailing EOS prefix must be at most 7 bits long. Appending one octet // with value 0xff is invalid, even though 0b111111111111111 (15 bits) is a // prefix of EOS. - Decode(QuicTextUtils::HexDecode( + DecodeHeaderBlock(QuicTextUtils::HexDecode( "00002f0225a849e95ba97d7fff8925a849e95bb8e8b4bf")); } @@ -220,7 +254,7 @@ // The trailing EOS prefix must be at most 7 bits long. Appending one octet // with value 0xff is invalid, even though 0b1111111111111 (13 bits) is a // prefix of EOS. - Decode(QuicTextUtils::HexDecode( + DecodeHeaderBlock(QuicTextUtils::HexDecode( "00002f0125a849e95ba97d7f8a25a849e95bb8e8b4bfff")); } @@ -248,8 +282,10 @@ QuicStringPiece("foo"))); EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))); - Decode(QuicTextUtils::HexDecode( + DecodeHeaderBlock(QuicTextUtils::HexDecode( "0000d1dfccd45f108621e9aec2a11f5c8294e75f000554524143455f1000")); } @@ -260,9 +296,418 @@ // Addressing entry 99 should trigger an error. EXPECT_CALL(handler_, OnDecodingErrorDetected( - QuicStringPiece("Invalid static table index."))); + QuicStringPiece("Static table entry not found."))); - Decode(QuicTextUtils::HexDecode("0000ff23ff24")); + DecodeHeaderBlock(QuicTextUtils::HexDecode("0000ff23ff24")); +} + +TEST_P(QpackDecoderTest, DynamicTable) { + DecodeEncoderStreamData(QuicTextUtils::HexDecode( + "6294e703626172" // Add literal entry with name "foo" and value "bar". + "80035a5a5a" // Add entry with name of dynamic table entry index 0 + // (relative index) and value "ZZZ". + "cf8294e7" // Add entry with name of static table entry index 15 + // and value "foo". + "01")); // Duplicate entry with relative index 1. + + // Now there are four entries in the dynamic table. + // Note that absolute indices start with 1. + // Entry 1: "foo", "bar" + // Entry 2: "foo", "ZZZ" + // Entry 3: ":method", "foo" + // Entry 4: "foo", "ZZZ" + + // Use a Sequence to test that mock methods are called in order. + Sequence s; + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo"))) + .InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))) + .InSequence(s); + EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0500" // Largest Reference 4 and Delta Base Index 0. + // Base Index is 4 + 0 = 4. + "83" // Dynamic table entry with relative index 3, absolute index 1. + "82" // Dynamic table entry with relative index 2, absolute index 2. + "81" // Dynamic table entry with relative index 1, absolute index 3. + "80" // Dynamic table entry with relative index 0, absolute index 4. + "41025a5a")); // Name of entry 1 (relative index) from dynamic table, + // with value "ZZ". + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo"))) + .InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))) + .InSequence(s); + EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0502" // Largest Reference 4 and Delta Base Index 2. + // Base Index is 4 + 2 = 6. + "85" // Dynamic table entry with relative index 5, absolute index 1. + "84" // Dynamic table entry with relative index 4, absolute index 2. + "83" // Dynamic table entry with relative index 3, absolute index 3. + "82" // Dynamic table entry with relative index 2, absolute index 4. + "43025a5a")); // Name of entry 3 (relative index) from dynamic table, + // with value "ZZ". + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo"))) + .InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))) + .InSequence(s); + EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0582" // Largest Reference 4 and Delta Base Index 2 with sign bit set. + // Base Index is 4 - 2 - 1 = 1. + "80" // Dynamic table entry with relative index 0, absolute index 1. + "10" // Dynamic table entry with post-base index 0, absolute index 2. + "11" // Dynamic table entry with post-base index 1, absolute index 3. + "12" // Dynamic table entry with post-base index 2, absolute index 4. + "01025a5a")); // Name of entry 1 (post-base index) from dynamic table, + // with value "ZZ". +} + +TEST_P(QpackDecoderTest, DecreasingDynamicTableCapacityEvictsEntries) { + // Add literal entry with name "foo" and value "bar". + DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0200" // Largest Reference 1 and Delta Base Index 0. + // Base Index is 1 + 0 = 1. + "80")); // Dynamic table entry with relative index 0, absolute index 1. + + // Change dynamic table capacity to 32 bytes, smaller than the entry. + // This must cause the entry to be evicted. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f01")); + + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0200" // Largest Reference 1 and Delta Base Index 0. + // Base Index is 1 + 0 = 1. + "80")); // Dynamic table entry with relative index 0, absolute index 1. +} + +TEST_P(QpackDecoderTest, EncoderStreamErrorEntryTooLarge) { + EXPECT_CALL(encoder_stream_error_delegate_, + OnError(Eq("Error inserting literal entry."))); + + // Set dynamic table capacity to 34. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f03")); + // Add literal entry with name "foo" and value "bar", size is 32 + 3 + 3 = 38. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); +} + +TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidStaticTableEntry) { + EXPECT_CALL(encoder_stream_error_delegate_, + OnError(Eq("Invalid static table entry."))); + + // Address invalid static table entry index 99. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("ff2400")); +} + +TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidDynamicTableEntry) { + EXPECT_CALL(encoder_stream_error_delegate_, + OnError(Eq("Dynamic table entry not found."))); + + DecodeEncoderStreamData(QuicTextUtils::HexDecode( + "6294e703626172" // Add literal entry with name "foo" and value "bar". + "8100")); // Address dynamic table entry with relative index 1. Such + // entry does not exist. The most recently added and only + // dynamic table entry has relative index 0. +} + +TEST_P(QpackDecoderTest, EncoderStreamErrorDuplicateInvalidEntry) { + EXPECT_CALL(encoder_stream_error_delegate_, + OnError(Eq("Dynamic table entry not found."))); + + DecodeEncoderStreamData(QuicTextUtils::HexDecode( + "6294e703626172" // Add literal entry with name "foo" and value "bar". + "01")); // Duplicate dynamic table entry with relative index 1. Such + // entry does not exist. The most recently added and only + // dynamic table entry has relative index 0. +} + +TEST_P(QpackDecoderTest, EncoderStreamErrorTooLargeInteger) { + EXPECT_CALL(encoder_stream_error_delegate_, + OnError(Eq("Encoded integer too large."))); + + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fffffffffffffffffffff")); +} + +TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIndexIsZero) { + EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0280" // Largest Reference is 1. Base Index 1 - 1 - 0 = 0 is explicitly + // permitted by the spec. + "80")); // However, addressing entry with relative index 0 would point to + // absolute index 0, which is invalid (absolute index is one + // based). +} + +TEST_P(QpackDecoderTest, InvalidNegativeBaseIndex) { + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Error calculating Base Index."))); + + // Largest reference 1, Delta Base Index 1 with sign bit set, Base Index would + // be 1 - 1 - 1 = -1, but it is not allowed to be negative. + DecodeHeaderBlock(QuicTextUtils::HexDecode("0281")); +} + +TEST_P(QpackDecoderTest, InvalidDynamicEntryByRelativeIndex) { + // Add literal entry with name "foo" and value "bar". + DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); + + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0500" // Largest Reference 4 and Delta Base Index 0. + // Base Index is 4 + 0 = 4. + "82")); // Indexed Header Field instruction addressing relative index 2. + // This is absolute index 2. Such entry does not exist. + + EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0500" // Largest Reference 4 and Delta Base Index 0. + // Base Index is 4 + 0 = 4. + "84")); // Indexed Header Field instruction addressing relative index 4. + // This is absolute index 0, which is invalid, because absolute + // indexing starts from 1. + + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0500" // Largest Reference 4 and Delta Base Index 0. + // Base Index is 4 + 0 = 4. + "4200")); // Literal Header Field with Name Reference instruction + // addressing relative index 2. This is absolute index 2. Such + // entry does not exist. + + EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0500" // Largest Reference 4 and Delta Base Index 0. + // Base Index is 4 + 0 = 4. + "4400")); // Literal Header Field with Name Reference instruction + // addressing relative index 4. This is absolute index 0, + // which is invalid, because absolute indexing starts from 1. +} + +TEST_P(QpackDecoderTest, InvalidDynamicEntryByPostBaseIndex) { + // Add literal entry with name "foo" and value "bar". + DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); + + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0380" // Largest Reference 2 and Delta Base Index 0 with sign bit set. + // Base Index is 2 - 0 - 1 = 1 + "10")); // Indexed Header Field instruction addressing dynamic table + // entry with post-base index 0, absolute index 2. Such entry + // does not exist. + + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0380" // Largest Reference 2 and Delta Base Index 0 with sign bit set. + // Base Index is 2 - 0 - 1 = 1 + "0000")); // Literal Header Field With Name Reference instruction + // addressing dynamic table entry with post-base index 0, + // absolute index 2. Such entry does not exist. +} + +TEST_P(QpackDecoderTest, TableCapacityMustNotExceedMaximum) { + EXPECT_CALL(encoder_stream_error_delegate_, + OnError(Eq("Error updating dynamic table size."))); + + // Try to update dynamic table capacity to 2048, which exceeds the maximum. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe10f")); +} + +TEST_P(QpackDecoderTest, SetMaximumDynamicTableCapacity) { + // Update dynamic table capacity to 128, which does not exceed the maximum. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f61")); +} + +TEST_P(QpackDecoderTest, LargestReferenceOutOfRange) { + // Maximum dynamic table capacity is 1024. + // MaxEntries is 1024 / 32 = 32. + // Largest Reference is decoded modulo 2 * MaxEntries, that is, modulo 64. + // A value of 1 cannot be encoded as 65 even though it has the same remainder. + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Error decoding Largest Reference."))); + DecodeHeaderBlock(QuicTextUtils::HexDecode("4100")); +} + +TEST_P(QpackDecoderTest, WrappedLargestReference) { + // Maximum dynamic table capacity is 1024. + // MaxEntries is 1024 / 32 = 32. + + // Add literal entry with name "foo" and a 600 byte long value. This will fit + // in the dynamic table once but not twice. + DecodeEncoderStreamData( + QuicTextUtils::HexDecode("6294e7" // Name "foo". + "7fd903")); // Value length 600. + QuicString header_value(600, 'Z'); + DecodeEncoderStreamData(header_value); + + // Duplicate most recent entry 200 times. + DecodeEncoderStreamData(QuicString(200, '\x00')); + + // Now there is only one entry in the dynamic table, with absolute index 201. + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(header_value))); + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + Write(Eq(kHeaderAcknowledgement))); + + // Send header block with Largest Reference = 201. + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0a00" // Wire Largest Reference 10, Largest Reference 201, + // Delta Base Index 0, Base Index 201. + "80")); // Emit dynamic table entry with relative index 0. +} + +TEST_P(QpackDecoderTest, NonZeroLargestReferenceButNoDynamicEntries) { + EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":method"), + QuicStringPiece("GET"))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Largest Reference too large."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0200" // Largest Reference is 1. + "d1")); // But the only instruction references the static table. +} + +TEST_P(QpackDecoderTest, AddressEntryBeyondLargestReference) { + EXPECT_CALL(handler_, OnDecodingErrorDetected( + Eq("Index larger than Largest Reference."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0201" // Largest Reference 1 and Delta Base Index 1. + // Base Index is 1 + 1 = 2. + "80")); // Indexed Header Field instruction addressing dynamic table + // entry with relative index 0, absolute index 2. This is beyond + // Largest Reference. + + EXPECT_CALL(handler_, OnDecodingErrorDetected( + Eq("Index larger than Largest Reference."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0201" // Largest Reference 1 and Delta Base Index 1. + // Base Index is 1 + 1 = 2. + "4000")); // Literal Header Field with Name Reference instruction + // addressing dynamic table entry with relative index 0, + // absolute index 2. This is beyond Largest Reference. + + EXPECT_CALL(handler_, OnDecodingErrorDetected( + Eq("Index larger than Largest Reference."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0200" // Largest Reference 1 and Delta Base Index 0. + // Base Index is 1 + 0 = 1. + "10")); // Indexed Header Field with Post-Base Index instruction + // addressing dynamic table entry with post-base index 0, + // absolute index 2. This is beyond Largest Reference. + + EXPECT_CALL(handler_, OnDecodingErrorDetected( + Eq("Index larger than Largest Reference."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0200" // Largest Reference 1 and Delta Base Index 0. + // Base Index is 1 + 0 = 1. + "0000")); // Literal Header Field with Post-Base Name Reference + // instruction addressing dynamic table entry with post-base + // index 0, absolute index 2. This is beyond Largest + // Reference. +} + +TEST_P(QpackDecoderTest, PromisedLargestReferenceLargerThanLargestActualIndex) { + // Add literal entry with name "foo" and value "bar". + DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); + // Duplicate entry. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("00")); + + EXPECT_CALL(handler_, + OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar"))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Largest Reference too large."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0300" // Largest Reference 2 and Delta Base Index 0. + // Base Index is 2 + 0 = 2. + "81")); // Indexed Header Field instruction addressing dynamic table + // entry with relative index 1, absolute index 1. This is the + // largest reference in this header block, even though Largest + // Reference is 2. + + EXPECT_CALL(handler_, + OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece(""))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Largest Reference too large."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0300" // Largest Reference 2 and Delta Base Index 0. + // Base Index is 2 + 0 = 2. + "4100")); // Literal Header Field with Name Reference instruction + // addressing dynamic table entry with relative index 1, + // absolute index 1. This is the largest reference in this + // header block, even though Largest Reference is 2. + + EXPECT_CALL(handler_, + OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar"))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Largest Reference too large."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0481" // Largest Reference 3 and Delta Base Index 1 with sign bit set. + // Base Index is 3 - 1 - 1 = 1. + "10")); // Indexed Header Field with Post-Base Index instruction + // addressing dynamic table entry with post-base index 0, + // absolute index 2. This is the largest reference in this + // header block, even though Largest Reference is 3. + + EXPECT_CALL(handler_, + OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece(""))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Largest Reference too large."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0481" // Largest Reference 3 and Delta Base Index 1 with sign bit set. + // Base Index is 3 - 1 - 1 = 1. + "0000")); // Literal Header Field with Post-Base Name Reference + // instruction addressing dynamic table entry with post-base + // index 0, absolute index 2. This is the largest reference in + // this header block, even though Largest Reference is 3. } } // namespace
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_test_utils.cc b/net/third_party/quic/core/qpack/qpack_decoder_test_utils.cc index e733eb0..2f916e9 100644 --- a/net/third_party/quic/core/qpack/qpack_decoder_test_utils.cc +++ b/net/third_party/quic/core/qpack/qpack_decoder_test_utils.cc
@@ -4,11 +4,17 @@ #include "net/third_party/quic/core/qpack/qpack_decoder_test_utils.h" +#include <cstddef> + #include "testing/gmock/include/gmock/gmock.h" namespace quic { namespace test { +void NoopEncoderStreamErrorDelegate::OnError(QuicStringPiece error_message) {} + +void NoopDecoderStreamSenderDelegate::Write(QuicStringPiece data) {} + TestHeadersHandler::TestHeadersHandler() : decoding_completed_(false), decoding_error_detected_(false) {} @@ -50,10 +56,14 @@ return decoding_error_detected_; } -void QpackDecode(QpackDecoder::HeadersHandlerInterface* handler, - const FragmentSizeGenerator& fragment_size_generator, - QuicStringPiece data) { - QpackDecoder decoder; +void QpackDecode( + QpackDecoder::EncoderStreamErrorDelegate* encoder_stream_error_delegate, + QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate, + QpackProgressiveDecoder::HeadersHandlerInterface* handler, + const FragmentSizeGenerator& fragment_size_generator, + QuicStringPiece data) { + QpackDecoder decoder(encoder_stream_error_delegate, + decoder_stream_sender_delegate); auto progressive_decoder = decoder.DecodeHeaderBlock(/* stream_id = */ 1, handler); while (!data.empty()) {
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_test_utils.h b/net/third_party/quic/core/qpack/qpack_decoder_test_utils.h index 98822fbc..6d2202bc 100644 --- a/net/third_party/quic/core/qpack/qpack_decoder_test_utils.h +++ b/net/third_party/quic/core/qpack/qpack_decoder_test_utils.h
@@ -6,16 +6,55 @@ #define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_DECODER_TEST_UTILS_H_ #include "net/third_party/quic/core/qpack/qpack_decoder.h" +#include "net/third_party/quic/core/qpack/qpack_progressive_decoder.h" #include "net/third_party/quic/core/qpack/qpack_test_utils.h" #include "net/third_party/quic/platform/api/quic_string_piece.h" #include "net/third_party/spdy/core/spdy_header_block.h" +#include "testing/gmock/include/gmock/gmock.h" namespace quic { namespace test { +// QpackDecoder::EncoderStreamErrorDelegate implementation that does nothing. +class NoopEncoderStreamErrorDelegate + : public QpackDecoder::EncoderStreamErrorDelegate { + public: + ~NoopEncoderStreamErrorDelegate() override = default; + + void OnError(QuicStringPiece error_message) override; +}; + +// Mock QpackDecoder::EncoderStreamErrorDelegate implementation. +class MockEncoderStreamErrorDelegate + : public QpackDecoder::EncoderStreamErrorDelegate { + public: + ~MockEncoderStreamErrorDelegate() override = default; + + MOCK_METHOD1(OnError, void(QuicStringPiece error_message)); +}; + +// QpackDecoderStreamSender::Delegate implementation that does nothing. +class NoopDecoderStreamSenderDelegate + : public QpackDecoderStreamSender::Delegate { + public: + ~NoopDecoderStreamSenderDelegate() override = default; + + void Write(QuicStringPiece data) override; +}; + +// Mock QpackDecoderStreamSender::Delegate implementation. +class MockDecoderStreamSenderDelegate + : public QpackDecoderStreamSender::Delegate { + public: + ~MockDecoderStreamSenderDelegate() override = default; + + MOCK_METHOD1(Write, void(QuicStringPiece data)); +}; + // HeadersHandlerInterface implementation that collects decoded headers // into a SpdyHeaderBlock. -class TestHeadersHandler : public QpackDecoder::HeadersHandlerInterface { +class TestHeadersHandler + : public QpackProgressiveDecoder::HeadersHandlerInterface { public: TestHeadersHandler(); ~TestHeadersHandler() override = default; @@ -38,9 +77,36 @@ bool decoding_error_detected_; }; -void QpackDecode(QpackDecoder::HeadersHandlerInterface* handler, - const FragmentSizeGenerator& fragment_size_generator, - QuicStringPiece data); +class MockHeadersHandler + : public QpackProgressiveDecoder::HeadersHandlerInterface { + public: + MockHeadersHandler() = default; + MockHeadersHandler(const MockHeadersHandler&) = delete; + MockHeadersHandler& operator=(const MockHeadersHandler&) = delete; + ~MockHeadersHandler() override = default; + + MOCK_METHOD2(OnHeaderDecoded, + void(QuicStringPiece name, QuicStringPiece value)); + MOCK_METHOD0(OnDecodingCompleted, void()); + MOCK_METHOD1(OnDecodingErrorDetected, void(QuicStringPiece error_message)); +}; + +class NoOpHeadersHandler + : public QpackProgressiveDecoder::HeadersHandlerInterface { + public: + ~NoOpHeadersHandler() override = default; + + void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override{}; + void OnDecodingCompleted() override{}; + void OnDecodingErrorDetected(QuicStringPiece error_message) override{}; +}; + +void QpackDecode( + QpackDecoder::EncoderStreamErrorDelegate* encoder_stream_error_delegate, + QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate, + QpackProgressiveDecoder::HeadersHandlerInterface* handler, + const FragmentSizeGenerator& fragment_size_generator, + QuicStringPiece data); } // namespace test } // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_encoder.cc b/net/third_party/quic/core/qpack/qpack_encoder.cc index 9941e21..490c4ebf 100644 --- a/net/third_party/quic/core/qpack/qpack_encoder.cc +++ b/net/third_party/quic/core/qpack/qpack_encoder.cc
@@ -5,171 +5,50 @@ #include "net/third_party/quic/core/qpack/qpack_encoder.h" #include "base/logging.h" -#include "net/third_party/quic/core/qpack/qpack_constants.h" -#include "net/third_party/quic/core/qpack/qpack_instruction_encoder.h" +#include "net/third_party/quic/core/qpack/qpack_progressive_encoder.h" #include "net/third_party/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quic/platform/api/quic_string.h" #include "net/third_party/quic/platform/api/quic_string_piece.h" -#include "net/third_party/quic/platform/api/quic_string_utils.h" namespace quic { -namespace { -// An implementation of ProgressiveEncoder interface that encodes a single -// header block. -class QpackProgressiveEncoder : public spdy::HpackEncoder::ProgressiveEncoder { - public: - QpackProgressiveEncoder() = delete; - QpackProgressiveEncoder(QuicStreamId stream_id, - QpackHeaderTable* header_table, - const spdy::SpdyHeaderBlock* header_list); - QpackProgressiveEncoder(const QpackProgressiveEncoder&) = delete; - QpackProgressiveEncoder& operator=(const QpackProgressiveEncoder&) = delete; - ~QpackProgressiveEncoder() override = default; - - // Returns true iff more remains to encode. - bool HasNext() const override; - - // Encodes up to |max_encoded_bytes| octets, appending to |output|. - void Next(size_t max_encoded_bytes, QuicString* output) override; - - private: - const QuicStreamId stream_id_; - QpackInstructionEncoder instruction_encoder_; - const QpackHeaderTable* const header_table_; - const spdy::SpdyHeaderBlock* const header_list_; - - // Header field currently being encoded. - spdy::SpdyHeaderBlock::const_iterator header_list_iterator_; - - // False until prefix is fully encoded. - bool prefix_encoded_; -}; - -QpackProgressiveEncoder::QpackProgressiveEncoder( - QuicStreamId stream_id, - QpackHeaderTable* header_table, - const spdy::SpdyHeaderBlock* header_list) - : stream_id_(stream_id), - header_table_(header_table), - header_list_(header_list), - header_list_iterator_(header_list_->begin()), - prefix_encoded_(false) { - // TODO(bnc): Use |stream_id_| for dynamic table entry management, and - // remove this dummy DCHECK. - DCHECK_LE(0u, stream_id_); +QpackEncoder::QpackEncoder( + DecoderStreamErrorDelegate* decoder_stream_error_delegate, + QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate) + : decoder_stream_error_delegate_(decoder_stream_error_delegate), + decoder_stream_receiver_(this), + encoder_stream_sender_(encoder_stream_sender_delegate) { + DCHECK(decoder_stream_error_delegate_); + DCHECK(encoder_stream_sender_delegate); } -bool QpackProgressiveEncoder::HasNext() const { - return header_list_iterator_ != header_list_->end() || !prefix_encoded_; -} - -void QpackProgressiveEncoder::Next(size_t max_encoded_bytes, - QuicString* output) { - DCHECK_NE(0u, max_encoded_bytes); - DCHECK(HasNext()); - - // Since QpackInstructionEncoder::Next() does not indicate the number of bytes - // written, save the maximum new size of |*output|. - const size_t max_length = output->size() + max_encoded_bytes; - - DCHECK_LT(output->size(), max_length); - - if (!prefix_encoded_ && !instruction_encoder_.HasNext()) { - // TODO(bnc): Implement dynamic entries and set Largest Reference and - // Delta Base Index accordingly. - instruction_encoder_.set_varint(0); - instruction_encoder_.set_varint2(0); - instruction_encoder_.set_s_bit(false); - - instruction_encoder_.Encode(QpackPrefixInstruction()); - - DCHECK(instruction_encoder_.HasNext()); - } - - do { - // Call QpackInstructionEncoder::Encode for |*header_list_iterator_| if it - // has not been called yet. - if (!instruction_encoder_.HasNext()) { - DCHECK(prefix_encoded_); - - // Even after |name| and |value| go out of scope, copies of these - // QuicStringPieces retained by QpackInstructionEncoder are still valid as - // long as |header_list_| is valid. - QuicStringPiece name = header_list_iterator_->first; - QuicStringPiece value = header_list_iterator_->second; - - // |is_static| and |index| are saved by QpackInstructionEncoder by value, - // there are no lifetime concerns. - bool is_static; - size_t index; - - auto match_type = - header_table_->FindHeaderField(name, value, &is_static, &index); - - switch (match_type) { - case QpackHeaderTable::MatchType::kNameAndValue: - DCHECK(is_static) << "Dynamic table entries not supported yet."; - - instruction_encoder_.set_s_bit(is_static); - instruction_encoder_.set_varint(index); - - instruction_encoder_.Encode(QpackIndexedHeaderFieldInstruction()); - - break; - case QpackHeaderTable::MatchType::kName: - DCHECK(is_static) << "Dynamic table entries not supported yet."; - - instruction_encoder_.set_s_bit(is_static); - instruction_encoder_.set_varint(index); - instruction_encoder_.set_value(value); - - instruction_encoder_.Encode( - QpackLiteralHeaderFieldNameReferenceInstruction()); - - break; - case QpackHeaderTable::MatchType::kNoMatch: - instruction_encoder_.set_name(name); - instruction_encoder_.set_value(value); - - instruction_encoder_.Encode(QpackLiteralHeaderFieldInstruction()); - - break; - } - } - - DCHECK(instruction_encoder_.HasNext()); - - instruction_encoder_.Next(max_length - output->size(), output); - - if (instruction_encoder_.HasNext()) { - // There was not enough room to completely encode current header field. - DCHECK_EQ(output->size(), max_length); - - return; - } - - // It is possible that the output buffer was just large enough for encoding - // the current header field, hence equality is allowed here. - DCHECK_LE(output->size(), max_length); - - if (prefix_encoded_) { - // Move on to the next header field. - ++header_list_iterator_; - } else { - // Mark prefix as encoded. - prefix_encoded_ = true; - } - } while (HasNext() && output->size() < max_length); -} - -} // namespace +QpackEncoder::~QpackEncoder() {} std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder> QpackEncoder::EncodeHeaderList(QuicStreamId stream_id, const spdy::SpdyHeaderBlock* header_list) { - return QuicMakeUnique<QpackProgressiveEncoder>(stream_id, &header_table_, - header_list); + return QuicMakeUnique<QpackProgressiveEncoder>( + stream_id, &header_table_, &encoder_stream_sender_, header_list); +} + +void QpackEncoder::DecodeDecoderStreamData(QuicStringPiece data) { + decoder_stream_receiver_.Decode(data); +} + +void QpackEncoder::OnTableStateSynchronize(uint64_t insert_count) { + // TODO(bnc): Implement dynamic table management for encoding. +} + +void QpackEncoder::OnHeaderAcknowledgement(QuicStreamId stream_id) { + // TODO(bnc): Implement dynamic table management for encoding. +} + +void QpackEncoder::OnStreamCancellation(QuicStreamId stream_id) { + // TODO(bnc): Implement dynamic table management for encoding. +} + +void QpackEncoder::OnErrorDetected(QuicStringPiece error_message) { + decoder_stream_error_delegate_->OnError(error_message); } } // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_encoder.h b/net/third_party/quic/core/qpack/qpack_encoder.h index 9a2df32..b50a90c 100644 --- a/net/third_party/quic/core/qpack/qpack_encoder.h +++ b/net/third_party/quic/core/qpack/qpack_encoder.h
@@ -8,29 +8,62 @@ #include <cstdint> #include <memory> +#include "net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.h" +#include "net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h" #include "net/third_party/quic/core/qpack/qpack_header_table.h" #include "net/third_party/quic/core/quic_types.h" #include "net/third_party/quic/platform/api/quic_export.h" #include "net/third_party/spdy/core/hpack/hpack_encoder.h" -#include "net/third_party/spdy/core/spdy_header_block.h" + +namespace spdy { + +class SpdyHeaderBlock; + +} namespace quic { // QPACK encoder class. Exactly one instance should exist per QUIC connection. -// This class vends a new ProgressiveEncoder instance for each new header list -// to be encoded. -// TODO(bnc): This class will manage the encoding context, send data on the -// encoder stream, and receive data on the decoder stream. -class QUIC_EXPORT_PRIVATE QpackEncoder { +// This class vends a new QpackProgressiveEncoder instance for each new header +// list to be encoded. +class QUIC_EXPORT_PRIVATE QpackEncoder + : public QpackDecoderStreamReceiver::Delegate { public: - // This factory method should be called to start encoding a header list. + // Interface for receiving notification that an error has occurred on the + // decoder stream. This MUST be treated as a connection error of type + // HTTP_QPACK_DECODER_STREAM_ERROR. + class QUIC_EXPORT_PRIVATE DecoderStreamErrorDelegate { + public: + virtual ~DecoderStreamErrorDelegate() {} + + virtual void OnError(QuicStringPiece error_message) = 0; + }; + + QpackEncoder( + DecoderStreamErrorDelegate* decoder_stream_error_delegate, + QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate); + ~QpackEncoder() override; + + // This factory method is called to start encoding a header list. // |*header_list| must remain valid and must not change // during the lifetime of the returned ProgressiveEncoder instance. std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder> EncodeHeaderList( QuicStreamId stream_id, const spdy::SpdyHeaderBlock* header_list); + // Decode data received on the decoder stream. + void DecodeDecoderStreamData(QuicStringPiece data); + + // QpackDecoderStreamReceiver::Delegate implementation + void OnTableStateSynchronize(uint64_t insert_count) override; + void OnHeaderAcknowledgement(QuicStreamId stream_id) override; + void OnStreamCancellation(QuicStreamId stream_id) override; + void OnErrorDetected(QuicStringPiece error_message) override; + private: + DecoderStreamErrorDelegate* const decoder_stream_error_delegate_; + QpackDecoderStreamReceiver decoder_stream_receiver_; + QpackEncoderStreamSender encoder_stream_sender_; QpackHeaderTable header_table_; };
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc index 5a4904ca..06ab3f7 100644 --- a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc +++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
@@ -103,7 +103,9 @@ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a")); } -TEST_F(QpackEncoderStreamReceiverTest, InsertWithoutNameReferenceNameTooLong) { +// Name Length value is too large for varint decoder to decode. +TEST_F(QpackEncoderStreamReceiverTest, + InsertWithoutNameReferenceNameTooLongForVarintDecoder) { EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large."))); Decode(QuicTextUtils::HexDecode("5fffffffffffffffffffff")); @@ -113,11 +115,27 @@ TEST_F(QpackEncoderStreamReceiverTest, InsertWithoutNameReferenceNameExceedsLimit) { EXPECT_CALL(*delegate(), - OnErrorDetected(QuicStringPiece("Encoded integer too large."))); + OnErrorDetected(QuicStringPiece("String literal too long."))); + + Decode(QuicTextUtils::HexDecode("5fffff7f")); +} + +// Value Length value is too large for varint decoder to decode. +TEST_F(QpackEncoderStreamReceiverTest, + InsertWithoutNameReferenceValueTooLongForVarintDecoder) { + EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large."))); Decode(QuicTextUtils::HexDecode("436261727fffffffffffffffffffff")); } +// Value Length value can be decoded by varint decoder but exceeds 1 MB limit. +TEST_F(QpackEncoderStreamReceiverTest, + InsertWithoutNameReferenceValueExceedsLimit) { + EXPECT_CALL(*delegate(), OnErrorDetected(Eq("String literal too long."))); + + Decode(QuicTextUtils::HexDecode("436261727fffff7f")); +} + TEST_F(QpackEncoderStreamReceiverTest, Duplicate) { // Small index fits in prefix. EXPECT_CALL(*delegate(), OnDuplicate(17));
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.cc index a719613..0243cc1 100644 --- a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.cc +++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.cc
@@ -4,6 +4,7 @@ #include "net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h" +#include <cstddef> #include <limits> #include "net/third_party/quic/core/qpack/qpack_constants.h"
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_test.cc index 92abd6c..25fb128 100644 --- a/net/third_party/quic/core/qpack/qpack_encoder_test.cc +++ b/net/third_party/quic/core/qpack/qpack_encoder_test.cc
@@ -11,6 +11,7 @@ #include "net/third_party/quic/platform/api/quic_text_utils.h" #include "testing/gtest/include/gtest/gtest.h" +using ::testing::StrictMock; using ::testing::Values; namespace quic { @@ -18,15 +19,19 @@ namespace { class QpackEncoderTest : public QuicTestWithParam<FragmentMode> { - public: + protected: QpackEncoderTest() : fragment_mode_(GetParam()) {} ~QpackEncoderTest() override = default; QuicString Encode(const spdy::SpdyHeaderBlock* header_list) { - return QpackEncode(FragmentModeToFragmentSizeGenerator(fragment_mode_), - header_list); + return QpackEncode( + &decoder_stream_error_delegate_, &encoder_stream_sender_delegate_, + FragmentModeToFragmentSizeGenerator(fragment_mode_), header_list); } + StrictMock<MockDecoderStreamErrorDelegate> decoder_stream_error_delegate_; + NoopEncoderStreamSenderDelegate encoder_stream_sender_delegate_; + private: const FragmentMode fragment_mode_; }; @@ -131,7 +136,8 @@ spdy::SpdyHeaderBlock header_list; header_list[":path"] = "/"; - QpackEncoder encoder; + QpackEncoder encoder(&decoder_stream_error_delegate_, + &encoder_stream_sender_delegate_); auto progressive_encoder = encoder.EncodeHeaderList(/* stream_id = */ 1, &header_list); EXPECT_TRUE(progressive_encoder->HasNext()); @@ -145,6 +151,16 @@ EXPECT_FALSE(progressive_encoder->HasNext()); } +TEST_P(QpackEncoderTest, DecoderStreamError) { + EXPECT_CALL(decoder_stream_error_delegate_, + OnError(QuicStringPiece("Encoded integer too large."))); + + QpackEncoder encoder(&decoder_stream_error_delegate_, + &encoder_stream_sender_delegate_); + encoder.DecodeDecoderStreamData( + QuicTextUtils::HexDecode("ffffffffffffffffffffff")); +} + } // namespace } // namespace test } // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_test_utils.cc b/net/third_party/quic/core/qpack/qpack_encoder_test_utils.cc index 7551ef0..9435070 100644 --- a/net/third_party/quic/core/qpack/qpack_encoder_test_utils.cc +++ b/net/third_party/quic/core/qpack/qpack_encoder_test_utils.cc
@@ -4,15 +4,22 @@ #include "net/third_party/quic/core/qpack/qpack_encoder_test_utils.h" -#include "net/third_party/quic/core/qpack/qpack_encoder.h" #include "net/third_party/spdy/core/hpack/hpack_encoder.h" namespace quic { namespace test { -QuicString QpackEncode(const FragmentSizeGenerator& fragment_size_generator, - const spdy::SpdyHeaderBlock* header_list) { - QpackEncoder encoder; +void NoopDecoderStreamErrorDelegate::OnError(QuicStringPiece error_message) {} + +void NoopEncoderStreamSenderDelegate::Write(QuicStringPiece data) {} + +QuicString QpackEncode( + QpackEncoder::DecoderStreamErrorDelegate* decoder_stream_error_delegate, + QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate, + const FragmentSizeGenerator& fragment_size_generator, + const spdy::SpdyHeaderBlock* header_list) { + QpackEncoder encoder(decoder_stream_error_delegate, + encoder_stream_sender_delegate); auto progressive_encoder = encoder.EncodeHeaderList(/* stream_id = */ 1, header_list);
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_test_utils.h b/net/third_party/quic/core/qpack/qpack_encoder_test_utils.h index 40df3c9..69e6b67 100644 --- a/net/third_party/quic/core/qpack/qpack_encoder_test_utils.h +++ b/net/third_party/quic/core/qpack/qpack_encoder_test_utils.h
@@ -5,15 +5,48 @@ #ifndef NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_ #define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_ +#include "net/third_party/quic/core/qpack/qpack_encoder.h" #include "net/third_party/quic/core/qpack/qpack_test_utils.h" #include "net/third_party/quic/platform/api/quic_string.h" +#include "net/third_party/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quic/platform/api/quic_test.h" #include "net/third_party/spdy/core/spdy_header_block.h" namespace quic { namespace test { -QuicString QpackEncode(const FragmentSizeGenerator& fragment_size_generator, - const spdy::SpdyHeaderBlock* header_list); +// QpackEncoder::DecoderStreamErrorDelegate implementation that does nothing. +class NoopDecoderStreamErrorDelegate + : public QpackEncoder::DecoderStreamErrorDelegate { + public: + ~NoopDecoderStreamErrorDelegate() override = default; + + void OnError(QuicStringPiece error_message) override; +}; + +// Mock QpackEncoder::DecoderStreamErrorDelegate implementation. +class MockDecoderStreamErrorDelegate + : public QpackEncoder::DecoderStreamErrorDelegate { + public: + ~MockDecoderStreamErrorDelegate() override = default; + + MOCK_METHOD1(OnError, void(QuicStringPiece error_message)); +}; + +// QpackEncoderStreamSender::Delegate implementation that does nothing. +class NoopEncoderStreamSenderDelegate + : public QpackEncoderStreamSender::Delegate { + public: + ~NoopEncoderStreamSenderDelegate() override = default; + + void Write(QuicStringPiece data) override; +}; + +QuicString QpackEncode( + QpackEncoder::DecoderStreamErrorDelegate* decoder_stream_error_delegate, + QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate, + const FragmentSizeGenerator& fragment_size_generator, + const spdy::SpdyHeaderBlock* header_list); } // namespace test } // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_header_table.cc b/net/third_party/quic/core/qpack/qpack_header_table.cc index d055fe46..db5308c 100644 --- a/net/third_party/quic/core/qpack/qpack_header_table.cc +++ b/net/third_party/quic/core/qpack/qpack_header_table.cc
@@ -11,9 +11,9 @@ namespace { -const size_t kEntrySizeOverhead = 32; +const uint64_t kEntrySizeOverhead = 32; -size_t EntrySize(QuicStringPiece name, QuicStringPiece value) { +uint64_t EntrySize(QuicStringPiece name, QuicStringPiece value) { return name.size() + value.size() + kEntrySizeOverhead; } @@ -32,7 +32,7 @@ QpackHeaderTable::~QpackHeaderTable() = default; const QpackEntry* QpackHeaderTable::LookupEntry(bool is_static, - size_t index) const { + uint64_t index) const { if (is_static) { if (index >= static_entries_.size()) { return nullptr; @@ -58,7 +58,7 @@ QuicStringPiece name, QuicStringPiece value, bool* is_static, - size_t* index) const { + uint64_t* index) const { QpackEntry query(name, value); // Look for exact match in static table. @@ -102,19 +102,20 @@ const QpackEntry* QpackHeaderTable::InsertEntry(QuicStringPiece name, QuicStringPiece value) { - const size_t entry_size = EntrySize(name, value); + const uint64_t entry_size = EntrySize(name, value); if (entry_size > dynamic_table_capacity_) { return nullptr; } - EvictDownTo(dynamic_table_capacity_ - entry_size); - dynamic_table_size_ += entry_size; - DCHECK_LE(dynamic_table_size_, dynamic_table_capacity_); - - const size_t index = dropped_entry_count_ + dynamic_entries_.size(); + const uint64_t index = dropped_entry_count_ + dynamic_entries_.size(); dynamic_entries_.push_back({name, value, /* is_static = */ false, index}); QpackEntry* const new_entry = &dynamic_entries_.back(); + // Evict entries after inserting the new entry instead of before + // in order to avoid invalidating |name| and |value|. + dynamic_table_size_ += entry_size; + EvictDownToCurrentCapacity(); + auto index_result = dynamic_index_.insert(new_entry); if (!index_result.second) { // An entry with the same name and value already exists. It needs to be @@ -142,13 +143,13 @@ return new_entry; } -bool QpackHeaderTable::UpdateTableSize(size_t max_size) { +bool QpackHeaderTable::UpdateTableSize(uint64_t max_size) { if (max_size > maximum_dynamic_table_capacity_) { return false; } dynamic_table_capacity_ = max_size; - EvictDownTo(dynamic_table_capacity_); + EvictDownToCurrentCapacity(); DCHECK_LE(dynamic_table_size_, dynamic_table_capacity_); @@ -156,7 +157,7 @@ } void QpackHeaderTable::SetMaximumDynamicTableCapacity( - size_t maximum_dynamic_table_capacity) { + uint64_t maximum_dynamic_table_capacity) { // This method can only be called once: in the decoding context, shortly after // construction; in the encoding context, upon receiving the SETTINGS frame. DCHECK_EQ(0u, dynamic_table_capacity_); @@ -168,12 +169,12 @@ max_entries_ = maximum_dynamic_table_capacity / 32; } -void QpackHeaderTable::EvictDownTo(size_t new_table_size) { - while (dynamic_table_size_ > new_table_size) { +void QpackHeaderTable::EvictDownToCurrentCapacity() { + while (dynamic_table_size_ > dynamic_table_capacity_) { DCHECK(!dynamic_entries_.empty()); QpackEntry* const entry = &dynamic_entries_.front(); - const size_t entry_size = EntrySize(entry->name(), entry->value()); + const uint64_t entry_size = EntrySize(entry->name(), entry->value()); DCHECK_GE(dynamic_table_size_, entry_size); dynamic_table_size_ -= entry_size;
diff --git a/net/third_party/quic/core/qpack/qpack_header_table.h b/net/third_party/quic/core/qpack/qpack_header_table.h index cecdff0..1d1f90c 100644 --- a/net/third_party/quic/core/qpack/qpack_header_table.h +++ b/net/third_party/quic/core/qpack/qpack_header_table.h
@@ -5,7 +5,7 @@ #ifndef NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_ #define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_ -#include <cstddef> +#include <cstdint> #include "net/third_party/quic/platform/api/quic_export.h" #include "net/third_party/quic/platform/api/quic_string_piece.h" @@ -41,7 +41,7 @@ // and the dynamic table. The returned pointer is valid until the entry is // evicted, even if other entries are inserted into the dynamic table. // Returns nullptr if entry does not exist. - const QpackEntry* LookupEntry(bool is_static, size_t index) const; + const QpackEntry* LookupEntry(bool is_static, uint64_t index) const; // Returns the absolute index of an entry with matching name and value if such // exists, otherwise one with matching name is such exists. |index| is zero @@ -49,7 +49,7 @@ MatchType FindHeaderField(QuicStringPiece name, QuicStringPiece value, bool* is_static, - size_t* index) const; + uint64_t* index) const; // Insert (name, value) into the dynamic table. May evict entries. Returns a // pointer to the inserted owned entry on success. Returns nullptr if entry @@ -58,7 +58,7 @@ // Change dynamic table capacity to |max_size|. Returns true on success. // Returns false is |max_size| exceeds maximum dynamic table capacity. - bool UpdateTableSize(size_t max_size); + bool UpdateTableSize(uint64_t max_size); // Set |maximum_dynamic_table_capacity_|. The initial value is zero. The // final value is determined by the decoder and is sent to the encoder as @@ -66,24 +66,24 @@ // value can be set upon connection establishment, whereas in the encoding // context it can be set when the SETTINGS frame is received. // This method must only be called at most once. - void SetMaximumDynamicTableCapacity(size_t maximum_dynamic_table_capacity); + void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); // Used by request streams to decode Largest Reference. - size_t max_entries() const { return max_entries_; } + uint64_t max_entries() const { return max_entries_; } // The number of entries inserted to the dynamic table (including ones that // were dropped since). Used for relative indexing on the encoder stream. - size_t inserted_entry_count() const { + uint64_t inserted_entry_count() const { return dynamic_entries_.size() + dropped_entry_count_; } // The number of entries dropped from the dynamic table. - size_t dropped_entry_count() const { return dropped_entry_count_; } + uint64_t dropped_entry_count() const { return dropped_entry_count_; } private: // Evict entries from the dynamic table until table size is less than or equal - // to |new_table_size|. - void EvictDownTo(size_t new_table_size); + // to current value of |dynamic_table_capacity_|. + void EvictDownToCurrentCapacity(); // Static Table @@ -117,7 +117,7 @@ NameToEntryMap dynamic_name_index_; // Size of the dynamic table. This is the sum of the size of its entries. - size_t dynamic_table_size_; + uint64_t dynamic_table_size_; // Dynamic Table Capacity is the maximum allowed value of // |dynamic_table_size_|. Entries are evicted if necessary before inserting a @@ -125,18 +125,18 @@ // Initial value is |maximum_dynamic_table_capacity_|. Capacity can be // changed by the encoder, as long as it does not exceed // |maximum_dynamic_table_capacity_|. - size_t dynamic_table_capacity_; + uint64_t dynamic_table_capacity_; // Maximum allowed value of |dynamic_table_capacity|. The initial value is // zero. Can be changed by SetMaximumDynamicTableCapacity(). - size_t maximum_dynamic_table_capacity_; + uint64_t maximum_dynamic_table_capacity_; // MaxEntries, see Section 3.2.2. Calculated based on // |maximum_dynamic_table_capacity_|. - size_t max_entries_; + uint64_t max_entries_; // The number of entries dropped from the dynamic table. - size_t dropped_entry_count_; + uint64_t dropped_entry_count_; }; } // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_header_table_test.cc b/net/third_party/quic/core/qpack/qpack_header_table_test.cc index e018ccee..9238478 100644 --- a/net/third_party/quic/core/qpack/qpack_header_table_test.cc +++ b/net/third_party/quic/core/qpack/qpack_header_table_test.cc
@@ -15,7 +15,7 @@ namespace test { namespace { -const size_t kMaximumDynamicTableCapacityForTesting = 1024 * 1024; +const uint64_t kMaximumDynamicTableCapacityForTesting = 1024 * 1024; class QpackHeaderTableTest : public QuicTest { protected: @@ -26,7 +26,7 @@ ~QpackHeaderTableTest() override = default; void ExpectEntryAtIndex(bool is_static, - size_t index, + uint64_t index, QuicStringPiece expected_name, QuicStringPiece expected_value) const { const auto* entry = table_.LookupEntry(is_static, index); @@ -35,7 +35,7 @@ EXPECT_EQ(expected_value, entry->value()); } - void ExpectNoEntryAtIndex(bool is_static, size_t index) const { + void ExpectNoEntryAtIndex(bool is_static, uint64_t index) const { EXPECT_FALSE(table_.LookupEntry(is_static, index)); } @@ -43,11 +43,11 @@ QuicStringPiece value, QpackHeaderTable::MatchType expected_match_type, bool expected_is_static, - size_t expected_index) const { + uint64_t expected_index) const { // Initialize outparams to a value different from the expected to ensure // that FindHeaderField() sets them. bool is_static = !expected_is_static; - size_t index = expected_index + 1; + uint64_t index = expected_index + 1; QpackHeaderTable::MatchType matchtype = table_.FindHeaderField(name, value, &is_static, &index); @@ -59,7 +59,7 @@ void ExpectNoMatch(QuicStringPiece name, QuicStringPiece value) const { bool is_static = false; - size_t index = 0; + uint64_t index = 0; QpackHeaderTable::MatchType matchtype = table_.FindHeaderField(name, value, &is_static, &index); @@ -76,13 +76,15 @@ EXPECT_FALSE(table_.InsertEntry(name, value)); } - bool UpdateTableSize(size_t max_size) { + bool UpdateTableSize(uint64_t max_size) { return table_.UpdateTableSize(max_size); } - size_t max_entries() const { return table_.max_entries(); } - size_t inserted_entry_count() const { return table_.inserted_entry_count(); } - size_t dropped_entry_count() const { return table_.dropped_entry_count(); } + uint64_t max_entries() const { return table_.max_entries(); } + uint64_t inserted_entry_count() const { + return table_.inserted_entry_count(); + } + uint64_t dropped_entry_count() const { return table_.dropped_entry_count(); } private: QpackHeaderTable table_; @@ -96,7 +98,8 @@ // 98 is the last entry. ExpectEntryAtIndex(/* is_static = */ true, 98, "x-frame-options", "sameorigin"); - assert(QUIC_ARRAYSIZE(kQpackStaticTable) == 99); + + ASSERT_EQ(99u, QpackStaticTableVector().size()); ExpectNoEntryAtIndex(/* is_static = */ true, 99); }
diff --git a/net/third_party/quic/core/qpack/qpack_instruction_decoder.h b/net/third_party/quic/core/qpack/qpack_instruction_decoder.h index bd8a615..5ae133f9 100644 --- a/net/third_party/quic/core/qpack/qpack_instruction_decoder.h +++ b/net/third_party/quic/core/qpack/qpack_instruction_decoder.h
@@ -6,6 +6,7 @@ #define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_ #include <cstddef> +#include <cstdint> #include "net/third_party/quic/core/qpack/qpack_constants.h" #include "net/third_party/quic/platform/api/quic_export.h"
diff --git a/net/third_party/quic/core/qpack/qpack_instruction_encoder.cc b/net/third_party/quic/core/qpack/qpack_instruction_encoder.cc index fbb9f16..68c3917 100644 --- a/net/third_party/quic/core/qpack/qpack_instruction_encoder.cc +++ b/net/third_party/quic/core/qpack/qpack_instruction_encoder.cc
@@ -72,7 +72,6 @@ void QpackInstructionEncoder::DoOpcode() { DCHECK_EQ(0u, byte_); - DCHECK_EQ(0, ~instruction_->opcode.mask & instruction_->opcode.value); byte_ = instruction_->opcode.value; @@ -116,7 +115,7 @@ field_->type == QpackInstructionFieldType::kValue); DCHECK(!varint_encoder_.IsEncodingInProgress()); - size_t integer_to_encode; + uint64_t integer_to_encode; switch (field_->type) { case QpackInstructionFieldType::kVarint: integer_to_encode = varint_;
diff --git a/net/third_party/quic/core/qpack/qpack_instruction_encoder.h b/net/third_party/quic/core/qpack/qpack_instruction_encoder.h index 02ab05e..0850667 100644 --- a/net/third_party/quic/core/qpack/qpack_instruction_encoder.h +++ b/net/third_party/quic/core/qpack/qpack_instruction_encoder.h
@@ -6,6 +6,7 @@ #define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_INSTRUCTION_ENCODER_H_ #include <cstddef> +#include <cstdint> #include "net/third_party/quic/core/qpack/qpack_constants.h" #include "net/third_party/quic/platform/api/quic_export.h" @@ -81,7 +82,7 @@ bool s_bit_; uint64_t varint_; uint64_t varint2_; - // The caller must keep the std::string that |name_| and |value_| point to + // The caller must keep the string that |name_| and |value_| point to // valid until they are encoded. QuicStringPiece name_; QuicStringPiece value_;
diff --git a/net/third_party/quic/core/qpack/qpack_progressive_decoder.cc b/net/third_party/quic/core/qpack/qpack_progressive_decoder.cc new file mode 100644 index 0000000..989166a --- /dev/null +++ b/net/third_party/quic/core/qpack/qpack_progressive_decoder.cc
@@ -0,0 +1,365 @@ +// Copyright (c) 2018 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/third_party/quic/core/qpack/qpack_progressive_decoder.h" + +#include "base/logging.h" +#include "net/third_party/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quic/core/qpack/qpack_header_table.h" +#include "net/third_party/quic/platform/api/quic_ptr_util.h" + +namespace quic { + +QpackProgressiveDecoder::QpackProgressiveDecoder( + QuicStreamId stream_id, + QpackHeaderTable* header_table, + QpackDecoderStreamSender* decoder_stream_sender, + HeadersHandlerInterface* handler) + : stream_id_(stream_id), + prefix_decoder_( + QuicMakeUnique<QpackInstructionDecoder>(QpackPrefixLanguage(), this)), + instruction_decoder_(QpackRequestStreamLanguage(), this), + header_table_(header_table), + decoder_stream_sender_(decoder_stream_sender), + handler_(handler), + largest_reference_(0), + base_index_(0), + largest_reference_seen_(0), + prefix_decoded_(false), + decoding_(true), + error_detected_(false) {} + +// static +bool QpackProgressiveDecoder::DecodeLargestReference( + uint64_t wire_largest_reference, + uint64_t max_entries, + uint64_t total_number_of_inserts, + uint64_t* largest_reference) { + if (wire_largest_reference == 0) { + *largest_reference = 0; + return true; + } + + // |max_entries| is calculated by dividing an unsigned 64-bit integer by 32, + // precluding all calculations in this method from overflowing. + DCHECK_LE(max_entries, std::numeric_limits<uint64_t>::max() / 32); + + if (wire_largest_reference > 2 * max_entries) { + return false; + } + + *largest_reference = wire_largest_reference - 1; + DCHECK_LT(*largest_reference, std::numeric_limits<uint64_t>::max() / 16); + + uint64_t current_wrapped = total_number_of_inserts % (2 * max_entries); + DCHECK_LT(current_wrapped, std::numeric_limits<uint64_t>::max() / 16); + + if (current_wrapped >= *largest_reference + max_entries) { + // Largest Reference wrapped around 1 extra time. + *largest_reference += 2 * max_entries; + } else if (current_wrapped + max_entries < *largest_reference) { + // Decoder wrapped around 1 extra time. + current_wrapped += 2 * max_entries; + } + + if (*largest_reference > + std::numeric_limits<uint64_t>::max() - total_number_of_inserts) { + return false; + } + + *largest_reference += total_number_of_inserts; + + // Prevent underflow, but also disallow invalid value 0 for Largest Reference. + if (current_wrapped >= *largest_reference) { + return false; + } + + *largest_reference -= current_wrapped; + + return true; +} + +void QpackProgressiveDecoder::Decode(QuicStringPiece data) { + DCHECK(decoding_); + + if (data.empty() || error_detected_) { + return; + } + + // Decode prefix byte by byte until the first (and only) instruction is + // decoded. + while (!prefix_decoded_) { + prefix_decoder_->Decode(data.substr(0, 1)); + data = data.substr(1); + if (data.empty()) { + return; + } + } + + instruction_decoder_.Decode(data); +} + +void QpackProgressiveDecoder::EndHeaderBlock() { + DCHECK(decoding_); + decoding_ = false; + + if (error_detected_) { + return; + } + + if (!instruction_decoder_.AtInstructionBoundary()) { + OnError("Incomplete header block."); + return; + } + + if (!prefix_decoded_) { + OnError("Incomplete header data prefix."); + return; + } + + if (largest_reference_ != largest_reference_seen_) { + OnError("Largest Reference too large."); + return; + } + + decoder_stream_sender_->SendHeaderAcknowledgement(stream_id_); + handler_->OnDecodingCompleted(); +} + +bool QpackProgressiveDecoder::OnInstructionDecoded( + const QpackInstruction* instruction) { + if (instruction == QpackIndexedHeaderFieldInstruction()) { + return DoIndexedHeaderFieldInstruction(); + } + if (instruction == QpackIndexedHeaderFieldPostBaseInstruction()) { + return DoIndexedHeaderFieldPostBaseInstruction(); + } + if (instruction == QpackLiteralHeaderFieldNameReferenceInstruction()) { + return DoLiteralHeaderFieldNameReferenceInstruction(); + } + if (instruction == QpackLiteralHeaderFieldPostBaseInstruction()) { + return DoLiteralHeaderFieldPostBaseInstruction(); + } + if (instruction == QpackLiteralHeaderFieldInstruction()) { + return DoLiteralHeaderFieldInstruction(); + } + DCHECK_EQ(instruction, QpackPrefixInstruction()); + return DoPrefixInstruction(); +} + +void QpackProgressiveDecoder::OnError(QuicStringPiece error_message) { + DCHECK(!error_detected_); + + error_detected_ = true; + handler_->OnDecodingErrorDetected(error_message); +} + +bool QpackProgressiveDecoder::DoIndexedHeaderFieldInstruction() { + if (!instruction_decoder_.s_bit()) { + uint64_t absolute_index; + if (!RequestStreamRelativeIndexToAbsoluteIndex( + instruction_decoder_.varint(), &absolute_index)) { + OnError("Invalid relative index."); + return false; + } + + if (absolute_index > largest_reference_) { + OnError("Index larger than Largest Reference."); + return false; + } + + largest_reference_seen_ = std::max(largest_reference_seen_, absolute_index); + + DCHECK_NE(0u, absolute_index); + const uint64_t real_index = absolute_index - 1; + auto entry = + header_table_->LookupEntry(/* is_static = */ false, real_index); + if (!entry) { + OnError("Dynamic table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), entry->value()); + return true; + } + + auto entry = header_table_->LookupEntry(/* is_static = */ true, + instruction_decoder_.varint()); + if (!entry) { + OnError("Static table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), entry->value()); + return true; +} + +bool QpackProgressiveDecoder::DoIndexedHeaderFieldPostBaseInstruction() { + uint64_t absolute_index; + if (!PostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), + &absolute_index)) { + OnError("Invalid post-base index."); + return false; + } + + if (absolute_index > largest_reference_) { + OnError("Index larger than Largest Reference."); + return false; + } + + largest_reference_seen_ = std::max(largest_reference_seen_, absolute_index); + + DCHECK_NE(0u, absolute_index); + const uint64_t real_index = absolute_index - 1; + auto entry = header_table_->LookupEntry(/* is_static = */ false, real_index); + if (!entry) { + OnError("Dynamic table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), entry->value()); + return true; +} + +bool QpackProgressiveDecoder::DoLiteralHeaderFieldNameReferenceInstruction() { + if (!instruction_decoder_.s_bit()) { + uint64_t absolute_index; + if (!RequestStreamRelativeIndexToAbsoluteIndex( + instruction_decoder_.varint(), &absolute_index)) { + OnError("Invalid relative index."); + return false; + } + + if (absolute_index > largest_reference_) { + OnError("Index larger than Largest Reference."); + return false; + } + + largest_reference_seen_ = std::max(largest_reference_seen_, absolute_index); + + DCHECK_NE(0u, absolute_index); + const uint64_t real_index = absolute_index - 1; + auto entry = + header_table_->LookupEntry(/* is_static = */ false, real_index); + if (!entry) { + OnError("Dynamic table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); + return true; + } + + auto entry = header_table_->LookupEntry(/* is_static = */ true, + instruction_decoder_.varint()); + if (!entry) { + OnError("Static table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); + return true; +} + +bool QpackProgressiveDecoder::DoLiteralHeaderFieldPostBaseInstruction() { + uint64_t absolute_index; + if (!PostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), + &absolute_index)) { + OnError("Invalid post-base index."); + return false; + } + + if (absolute_index > largest_reference_) { + OnError("Index larger than Largest Reference."); + return false; + } + + largest_reference_seen_ = std::max(largest_reference_seen_, absolute_index); + + DCHECK_NE(0u, absolute_index); + const uint64_t real_index = absolute_index - 1; + auto entry = header_table_->LookupEntry(/* is_static = */ false, real_index); + if (!entry) { + OnError("Dynamic table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); + return true; +} + +bool QpackProgressiveDecoder::DoLiteralHeaderFieldInstruction() { + handler_->OnHeaderDecoded(instruction_decoder_.name(), + instruction_decoder_.value()); + + return true; +} + +bool QpackProgressiveDecoder::DoPrefixInstruction() { + DCHECK(!prefix_decoded_); + + if (!DecodeLargestReference( + prefix_decoder_->varint(), header_table_->max_entries(), + header_table_->inserted_entry_count(), &largest_reference_)) { + OnError("Error decoding Largest Reference."); + return false; + } + + const bool sign = prefix_decoder_->s_bit(); + const uint64_t delta_base_index = prefix_decoder_->varint2(); + if (!DeltaBaseIndexToBaseIndex(sign, delta_base_index, &base_index_)) { + OnError("Error calculating Base Index."); + return false; + } + + prefix_decoded_ = true; + + return true; +} + +bool QpackProgressiveDecoder::DeltaBaseIndexToBaseIndex( + bool sign, + uint64_t delta_base_index, + uint64_t* base_index) { + if (sign) { + if (delta_base_index == std::numeric_limits<uint64_t>::max() || + largest_reference_ < delta_base_index + 1) { + return false; + } + *base_index = largest_reference_ - delta_base_index - 1; + return true; + } + + if (delta_base_index > + std::numeric_limits<uint64_t>::max() - largest_reference_) { + return false; + } + *base_index = largest_reference_ + delta_base_index; + return true; +} + +bool QpackProgressiveDecoder::RequestStreamRelativeIndexToAbsoluteIndex( + uint64_t relative_index, + uint64_t* absolute_index) const { + if (relative_index == std::numeric_limits<uint64_t>::max() || + relative_index + 1 > base_index_) { + return false; + } + + *absolute_index = base_index_ - relative_index; + return true; +} + +bool QpackProgressiveDecoder::PostBaseIndexToAbsoluteIndex( + uint64_t post_base_index, + uint64_t* absolute_index) const { + if (post_base_index >= std::numeric_limits<uint64_t>::max() - base_index_) { + return false; + } + + *absolute_index = base_index_ + post_base_index + 1; + return true; +} + +} // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_progressive_decoder.h b/net/third_party/quic/core/qpack/qpack_progressive_decoder.h new file mode 100644 index 0000000..5b9bc89 --- /dev/null +++ b/net/third_party/quic/core/qpack/qpack_progressive_decoder.h
@@ -0,0 +1,139 @@ +// Copyright (c) 2018 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_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_DECODER_H_ +#define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_DECODER_H_ + +#include <cstdint> +#include <memory> + +#include "net/third_party/quic/core/qpack/qpack_decoder_stream_sender.h" +#include "net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h" +#include "net/third_party/quic/core/qpack/qpack_instruction_decoder.h" +#include "net/third_party/quic/core/quic_types.h" +#include "net/third_party/quic/platform/api/quic_export.h" +#include "net/third_party/quic/platform/api/quic_string.h" +#include "net/third_party/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QpackHeaderTable; + +// Class to decode a single header block. +class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder + : public QpackInstructionDecoder::Delegate { + public: + // Interface for receiving decoded header block from the decoder. + class QUIC_EXPORT_PRIVATE HeadersHandlerInterface { + public: + virtual ~HeadersHandlerInterface() {} + + // Called when a new header name-value pair is decoded. Multiple values for + // a given name will be emitted as multiple calls to OnHeader. + virtual void OnHeaderDecoded(QuicStringPiece name, + QuicStringPiece value) = 0; + + // Called when the header block is completely decoded. + // Indicates the total number of bytes in this block. + // The decoder will not access the handler after this call. + // Note that this method might not be called synchronously when the header + // block is received on the wire, in case decoding is blocked on receiving + // entries on the encoder stream. TODO(bnc): Implement blocked decoding. + virtual void OnDecodingCompleted() = 0; + + // Called when a decoding error has occurred. No other methods will be + // called afterwards. + virtual void OnDecodingErrorDetected(QuicStringPiece error_message) = 0; + }; + + QpackProgressiveDecoder() = delete; + QpackProgressiveDecoder(QuicStreamId stream_id, + QpackHeaderTable* header_table, + QpackDecoderStreamSender* decoder_stream_sender, + HeadersHandlerInterface* handler); + QpackProgressiveDecoder(const QpackProgressiveDecoder&) = delete; + QpackProgressiveDecoder& operator=(const QpackProgressiveDecoder&) = delete; + ~QpackProgressiveDecoder() override = default; + + // Calculate actual Largest Reference from largest reference value sent on + // wire, MaxEntries, and total number of dynamic table insertions according to + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#largest-reference + // Returns true on success, false on invalid input or overflow/underflow. + static bool DecodeLargestReference(uint64_t wire_largest_reference, + uint64_t max_entries, + uint64_t total_number_of_inserts, + uint64_t* largest_reference); + + // Provide a data fragment to decode. + void Decode(QuicStringPiece data); + + // Signal that the entire header block has been received and passed in + // through Decode(). No methods must be called afterwards. + void EndHeaderBlock(); + + // QpackInstructionDecoder::Delegate implementation. + bool OnInstructionDecoded(const QpackInstruction* instruction) override; + void OnError(QuicStringPiece error_message) override; + + private: + bool DoIndexedHeaderFieldInstruction(); + bool DoIndexedHeaderFieldPostBaseInstruction(); + bool DoLiteralHeaderFieldNameReferenceInstruction(); + bool DoLiteralHeaderFieldPostBaseInstruction(); + bool DoLiteralHeaderFieldInstruction(); + bool DoPrefixInstruction(); + + // Calculates Base Index from |largest_reference_|, which must be set before + // calling this method, and sign bit and Delta Base Index in the Header Data + // Prefix, which are passed in as arguments. Returns true on success, false + // on failure due to overflow/underflow. + bool DeltaBaseIndexToBaseIndex(bool sign, + uint64_t delta_base_index, + uint64_t* base_index); + + // The request stream can use relative index (but different from the kind of + // relative index used on the encoder stream), and post-base index. + // These methods convert relative index and post-base index to absolute index + // (one based). They return true on success, or false if conversion fails due + // to overflow/underflow. + bool RequestStreamRelativeIndexToAbsoluteIndex( + uint64_t relative_index, + uint64_t* absolute_index) const; + bool PostBaseIndexToAbsoluteIndex(uint64_t post_base_index, + uint64_t* absolute_index) const; + + const QuicStreamId stream_id_; + + // |prefix_decoder_| only decodes a handful of bytes then it can be + // destroyed to conserve memory. |instruction_decoder_|, on the other hand, + // is used until the entire header block is decoded. + std::unique_ptr<QpackInstructionDecoder> prefix_decoder_; + QpackInstructionDecoder instruction_decoder_; + + const QpackHeaderTable* const header_table_; + QpackDecoderStreamSender* const decoder_stream_sender_; + HeadersHandlerInterface* const handler_; + + // Largest Reference and Base Index are parsed from the Header Data Prefix. + // They are both absolute indices, that is, one based. + uint64_t largest_reference_; + uint64_t base_index_; + + // Keep track of largest reference seen in this header block. + // After decoding is completed, this can be compared to |largest_reference_|. + uint64_t largest_reference_seen_; + + // False until prefix is fully read and decoded. + bool prefix_decoded_; + + // True until EndHeaderBlock() is called. + bool decoding_; + + // True if a decoding error has been detected. + bool error_detected_; +}; + +} // namespace quic + +#endif // NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_DECODER_H_
diff --git a/net/third_party/quic/core/qpack/qpack_progressive_decoder_test.cc b/net/third_party/quic/core/qpack/qpack_progressive_decoder_test.cc new file mode 100644 index 0000000..c88c50b4 --- /dev/null +++ b/net/third_party/quic/core/qpack/qpack_progressive_decoder_test.cc
@@ -0,0 +1,122 @@ +// Copyright (c) 2018 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/third_party/quic/core/qpack/qpack_progressive_decoder.h" + +#include "net/third_party/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +// For testing valid decodings, the encoded (wire) largest reference value is +// calculated for actual Largest Reference values, so that there is an expected +// value to comparte the decoded value against, and so that intricate +// inequalities can be documented. +struct { + uint64_t largest_reference; + uint64_t max_entries; + uint64_t total_number_of_inserts; +} kTestData[] = { + // Maximum dynamic table capacity is zero. + {0, 0, 0}, + // No dynamic entries in header. + {0, 100, 0}, + {0, 100, 500}, + // Largest Reference has not wrapped around yet, no entries evicted. + {15, 100, 25}, + {20, 100, 10}, + // Largest Reference has not wrapped around yet, some entries evicted. + {90, 100, 110}, + // Largest Reference has wrapped around. + {234, 100, 180}, + // Largest Reference has wrapped around many times. + {5678, 100, 5701}, + // Lowest and highest possible Largest Reference values + // for given MaxEntries and total number of insertions. + {401, 100, 500}, + {600, 100, 500}}; + +uint64_t EncodeLargestReference(uint64_t largest_reference, + uint64_t max_entries) { + if (largest_reference == 0) { + return 0; + } + + return largest_reference % (2 * max_entries) + 1; +} + +TEST(QpackProgressiveDecoderTest, DecodeLargestReference) { + for (size_t i = 0; i < QUIC_ARRAYSIZE(kTestData); ++i) { + const uint64_t largest_reference = kTestData[i].largest_reference; + const uint64_t max_entries = kTestData[i].max_entries; + const uint64_t total_number_of_inserts = + kTestData[i].total_number_of_inserts; + + if (largest_reference != 0) { + // Dynamic entries cannot be referenced if dynamic table capacity is zero. + ASSERT_LT(0u, max_entries) << i; + // Entry |total_number_of_inserts - max_entries| and earlier entries are + // evicted. Entry |largest_reference| is referenced. No evicted entry + // can be referenced. + ASSERT_LT(total_number_of_inserts, largest_reference + max_entries) << i; + // Entry |largest_reference - max_entries| and earlier entries are + // evicted, entry |total_number_of_inserts| is the last acknowledged + // entry. Every evicted entry must be acknowledged. + ASSERT_LE(largest_reference, total_number_of_inserts + max_entries) << i; + } + + uint64_t wire_largest_reference = + EncodeLargestReference(largest_reference, max_entries); + + // Initialize to a value different from the expected output to confirm that + // DecodeLargestReference() modifies the value of + // |decoded_largest_reference|. + uint64_t decoded_largest_reference = largest_reference + 1; + EXPECT_TRUE(QpackProgressiveDecoder::DecodeLargestReference( + wire_largest_reference, max_entries, total_number_of_inserts, + &decoded_largest_reference)) + << i; + + EXPECT_EQ(decoded_largest_reference, largest_reference) << i; + } +} + +// Failures are tested with hardcoded values for the on-the-wire largest +// reference field, to provide test coverage for values that would never be +// produced by a well behaved encoding function. +struct { + uint64_t wire_largest_reference; + uint64_t max_entries; + uint64_t total_number_of_inserts; +} kInvalidTestData[] = { + // Maximum dynamic table capacity is zero, yet header block + // claims to have a reference to a dynamic table entry. + {1, 0, 0}, + {9, 0, 0}, + // Examples from + // https://github.com/quicwg/base-drafts/issues/2112#issue-389626872. + {1, 10, 2}, + {18, 10, 2}, + // Largest Reference value too small or too large + // for given MaxEntries and total number of insertions. + {400, 100, 500}, + {601, 100, 500}}; + +TEST(QpackProgressiveDecoderTest, DecodeLargestReferenceError) { + for (size_t i = 0; i < QUIC_ARRAYSIZE(kInvalidTestData); ++i) { + uint64_t decoded_largest_reference = 0; + EXPECT_FALSE(QpackProgressiveDecoder::DecodeLargestReference( + kInvalidTestData[i].wire_largest_reference, + kInvalidTestData[i].max_entries, + kInvalidTestData[i].total_number_of_inserts, + &decoded_largest_reference)) + << i; + } +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_progressive_encoder.cc b/net/third_party/quic/core/qpack/qpack_progressive_encoder.cc new file mode 100644 index 0000000..ebfc038e --- /dev/null +++ b/net/third_party/quic/core/qpack/qpack_progressive_encoder.cc
@@ -0,0 +1,138 @@ +// Copyright (c) 2018 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/third_party/quic/core/qpack/qpack_progressive_encoder.h" + +#include "base/logging.h" +#include "net/third_party/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quic/core/qpack/qpack_header_table.h" +#include "net/third_party/quic/platform/api/quic_string.h" +#include "net/third_party/quic/platform/api/quic_string_piece.h" + +namespace quic { + +QpackProgressiveEncoder::QpackProgressiveEncoder( + QuicStreamId stream_id, + QpackHeaderTable* header_table, + QpackEncoderStreamSender* encoder_stream_sender, + const spdy::SpdyHeaderBlock* header_list) + : stream_id_(stream_id), + header_table_(header_table), + encoder_stream_sender_(encoder_stream_sender), + header_list_(header_list), + header_list_iterator_(header_list_->begin()), + prefix_encoded_(false) { + // TODO(bnc): Use |stream_id_| for dynamic table entry management, and + // remove this dummy DCHECK. + DCHECK_LE(0u, stream_id_); + + DCHECK(header_table_); + DCHECK(encoder_stream_sender_); + DCHECK(header_list_); +} + +bool QpackProgressiveEncoder::HasNext() const { + return header_list_iterator_ != header_list_->end() || !prefix_encoded_; +} + +void QpackProgressiveEncoder::Next(size_t max_encoded_bytes, + QuicString* output) { + DCHECK_NE(0u, max_encoded_bytes); + DCHECK(HasNext()); + + // Since QpackInstructionEncoder::Next() does not indicate the number of bytes + // written, save the maximum new size of |*output|. + const size_t max_length = output->size() + max_encoded_bytes; + + DCHECK_LT(output->size(), max_length); + + if (!prefix_encoded_ && !instruction_encoder_.HasNext()) { + // TODO(bnc): Implement dynamic entries and set Largest Reference and + // Delta Base Index accordingly. + instruction_encoder_.set_varint(0); + instruction_encoder_.set_varint2(0); + instruction_encoder_.set_s_bit(false); + + instruction_encoder_.Encode(QpackPrefixInstruction()); + + DCHECK(instruction_encoder_.HasNext()); + } + + do { + // Call QpackInstructionEncoder::Encode for |*header_list_iterator_| if it + // has not been called yet. + if (!instruction_encoder_.HasNext()) { + DCHECK(prefix_encoded_); + + // Even after |name| and |value| go out of scope, copies of these + // QuicStringPieces retained by QpackInstructionEncoder are still valid as + // long as |header_list_| is valid. + QuicStringPiece name = header_list_iterator_->first; + QuicStringPiece value = header_list_iterator_->second; + + // |is_static| and |index| are saved by QpackInstructionEncoder by value, + // there are no lifetime concerns. + bool is_static; + uint64_t index; + + auto match_type = + header_table_->FindHeaderField(name, value, &is_static, &index); + + switch (match_type) { + case QpackHeaderTable::MatchType::kNameAndValue: + DCHECK(is_static) << "Dynamic table entries not supported yet."; + + instruction_encoder_.set_s_bit(is_static); + instruction_encoder_.set_varint(index); + + instruction_encoder_.Encode(QpackIndexedHeaderFieldInstruction()); + + break; + case QpackHeaderTable::MatchType::kName: + DCHECK(is_static) << "Dynamic table entries not supported yet."; + + instruction_encoder_.set_s_bit(is_static); + instruction_encoder_.set_varint(index); + instruction_encoder_.set_value(value); + + instruction_encoder_.Encode( + QpackLiteralHeaderFieldNameReferenceInstruction()); + + break; + case QpackHeaderTable::MatchType::kNoMatch: + instruction_encoder_.set_name(name); + instruction_encoder_.set_value(value); + + instruction_encoder_.Encode(QpackLiteralHeaderFieldInstruction()); + + break; + } + } + + DCHECK(instruction_encoder_.HasNext()); + + instruction_encoder_.Next(max_length - output->size(), output); + + if (instruction_encoder_.HasNext()) { + // There was not enough room to completely encode current header field. + DCHECK_EQ(output->size(), max_length); + + return; + } + + // It is possible that the output buffer was just large enough for encoding + // the current header field, hence equality is allowed here. + DCHECK_LE(output->size(), max_length); + + if (prefix_encoded_) { + // Move on to the next header field. + ++header_list_iterator_; + } else { + // Mark prefix as encoded. + prefix_encoded_ = true; + } + } while (HasNext() && output->size() < max_length); +} + +} // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_progressive_encoder.h b/net/third_party/quic/core/qpack/qpack_progressive_encoder.h new file mode 100644 index 0000000..07442be --- /dev/null +++ b/net/third_party/quic/core/qpack/qpack_progressive_encoder.h
@@ -0,0 +1,57 @@ +// Copyright (c) 2018 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_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_ENCODER_H_ +#define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_ENCODER_H_ + +#include <cstddef> + +#include "net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h" +#include "net/third_party/quic/core/qpack/qpack_instruction_encoder.h" +#include "net/third_party/quic/core/quic_types.h" +#include "net/third_party/quic/platform/api/quic_export.h" +#include "net/third_party/spdy/core/hpack/hpack_encoder.h" +#include "net/third_party/spdy/core/spdy_header_block.h" + +namespace quic { + +class QpackHeaderTable; + +// An implementation of ProgressiveEncoder interface that encodes a single +// header block. +class QUIC_EXPORT_PRIVATE QpackProgressiveEncoder + : public spdy::HpackEncoder::ProgressiveEncoder { + public: + QpackProgressiveEncoder() = delete; + QpackProgressiveEncoder(QuicStreamId stream_id, + QpackHeaderTable* header_table, + QpackEncoderStreamSender* encoder_stream_sender, + const spdy::SpdyHeaderBlock* header_list); + QpackProgressiveEncoder(const QpackProgressiveEncoder&) = delete; + QpackProgressiveEncoder& operator=(const QpackProgressiveEncoder&) = delete; + ~QpackProgressiveEncoder() override = default; + + // Returns true iff more remains to encode. + bool HasNext() const override; + + // Encodes up to |max_encoded_bytes| octets, appending to |output|. + void Next(size_t max_encoded_bytes, QuicString* output) override; + + private: + const QuicStreamId stream_id_; + QpackInstructionEncoder instruction_encoder_; + const QpackHeaderTable* const header_table_; + QpackEncoderStreamSender* const encoder_stream_sender_; + const spdy::SpdyHeaderBlock* const header_list_; + + // Header field currently being encoded. + spdy::SpdyHeaderBlock::const_iterator header_list_iterator_; + + // False until prefix is fully encoded. + bool prefix_encoded_; +}; + +} // namespace quic + +#endif // NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_ENCODER_H_
diff --git a/net/third_party/quic/core/qpack/qpack_round_trip_test.cc b/net/third_party/quic/core/qpack/qpack_round_trip_test.cc index d22ba67..117b0b7 100644 --- a/net/third_party/quic/core/qpack/qpack_round_trip_test.cc +++ b/net/third_party/quic/core/qpack/qpack_round_trip_test.cc
@@ -30,12 +30,18 @@ spdy::SpdyHeaderBlock EncodeThenDecode( const spdy::SpdyHeaderBlock& header_list) { + NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; + NoopEncoderStreamSenderDelegate encoder_stream_sender_delegate; QuicString encoded_header_block = QpackEncode( + &decoder_stream_error_delegate, &encoder_stream_sender_delegate, FragmentModeToFragmentSizeGenerator(encoding_fragment_mode_), &header_list); TestHeadersHandler handler; - QpackDecode(&handler, + NoopEncoderStreamErrorDelegate encoder_stream_error_delegate; + NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate; + QpackDecode(&encoder_stream_error_delegate, &decoder_stream_sender_delegate, + &handler, FragmentModeToFragmentSizeGenerator(decoding_fragment_mode_), encoded_header_block);
diff --git a/net/third_party/quic/core/qpack/qpack_static_table.cc b/net/third_party/quic/core/qpack/qpack_static_table.cc index 87d3c231..9c232de8 100644 --- a/net/third_party/quic/core/qpack/qpack_static_table.cc +++ b/net/third_party/quic/core/qpack/qpack_static_table.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "net/third_party/quic/core/qpack/qpack_static_table.h" + #include "net/third_party/quic/platform/api/quic_arraysize.h" #include "net/third_party/quic/platform/api/quic_logging.h" #include "net/third_party/quic/platform/api/quic_ptr_util.h" @@ -14,128 +15,125 @@ #define STATIC_ENTRY(name, value) \ { name, QUIC_ARRAYSIZE(name) - 1, value, QUIC_ARRAYSIZE(value) - 1 } -const QpackStaticEntry kQpackStaticTable[] = { - STATIC_ENTRY(":authority", ""), // 0 - STATIC_ENTRY(":path", "/"), // 1 - STATIC_ENTRY("age", "0"), // 2 - STATIC_ENTRY("content-disposition", ""), // 3 - STATIC_ENTRY("content-length", "0"), // 4 - STATIC_ENTRY("cookie", ""), // 5 - STATIC_ENTRY("date", ""), // 6 - STATIC_ENTRY("etag", ""), // 7 - STATIC_ENTRY("if-modified-since", ""), // 8 - STATIC_ENTRY("if-none-match", ""), // 9 - STATIC_ENTRY("last-modified", ""), // 10 - STATIC_ENTRY("link", ""), // 11 - STATIC_ENTRY("location", ""), // 12 - STATIC_ENTRY("referer", ""), // 13 - STATIC_ENTRY("set-cookie", ""), // 14 - STATIC_ENTRY(":method", "CONNECT"), // 15 - STATIC_ENTRY(":method", "DELETE"), // 16 - STATIC_ENTRY(":method", "GET"), // 17 - STATIC_ENTRY(":method", "HEAD"), // 18 - STATIC_ENTRY(":method", "OPTIONS"), // 19 - STATIC_ENTRY(":method", "POST"), // 20 - STATIC_ENTRY(":method", "PUT"), // 21 - STATIC_ENTRY(":scheme", "http"), // 22 - STATIC_ENTRY(":scheme", "https"), // 23 - STATIC_ENTRY(":status", "103"), // 24 - STATIC_ENTRY(":status", "200"), // 25 - STATIC_ENTRY(":status", "304"), // 26 - STATIC_ENTRY(":status", "404"), // 27 - STATIC_ENTRY(":status", "503"), // 28 - STATIC_ENTRY("accept", "*/*"), // 29 - STATIC_ENTRY("accept", "application/dns-message"), // 30 - STATIC_ENTRY("accept-encoding", "gzip, deflate, br"), // 31 - STATIC_ENTRY("accept-ranges", "bytes"), // 32 - STATIC_ENTRY("access-control-allow-headers", "cache-control"), // 33 - STATIC_ENTRY("access-control-allow-headers", "content-type"), // 35 - STATIC_ENTRY("access-control-allow-origin", "*"), // 35 - STATIC_ENTRY("cache-control", "max-age=0"), // 36 - STATIC_ENTRY("cache-control", "max-age=2592000"), // 37 - STATIC_ENTRY("cache-control", "max-age=604800"), // 38 - STATIC_ENTRY("cache-control", "no-cache"), // 39 - STATIC_ENTRY("cache-control", "no-store"), // 40 - STATIC_ENTRY("cache-control", "public, max-age=31536000"), // 41 - STATIC_ENTRY("content-encoding", "br"), // 42 - STATIC_ENTRY("content-encoding", "gzip"), // 43 - STATIC_ENTRY("content-type", "application/dns-message"), // 44 - STATIC_ENTRY("content-type", "application/javascript"), // 45 - STATIC_ENTRY("content-type", "application/json"), // 46 - STATIC_ENTRY("content-type", "application/x-www-form-urlencoded"), // 47 - STATIC_ENTRY("content-type", "image/gif"), // 48 - STATIC_ENTRY("content-type", "image/jpeg"), // 49 - STATIC_ENTRY("content-type", "image/png"), // 50 - STATIC_ENTRY("content-type", "text/css"), // 51 - STATIC_ENTRY("content-type", "text/html; charset=utf-8"), // 52 - STATIC_ENTRY("content-type", "text/plain"), // 53 - STATIC_ENTRY("content-type", "text/plain;charset=utf-8"), // 54 - STATIC_ENTRY("range", "bytes=0-"), // 55 - STATIC_ENTRY("strict-transport-security", "max-age=31536000"), // 56 - STATIC_ENTRY("strict-transport-security", - "max-age=31536000; includesubdomains"), // 57 - STATIC_ENTRY("strict-transport-security", - "max-age=31536000; includesubdomains; preload"), // 58 - STATIC_ENTRY("vary", "accept-encoding"), // 59 - STATIC_ENTRY("vary", "origin"), // 60 - STATIC_ENTRY("x-content-type-options", "nosniff"), // 61 - STATIC_ENTRY("x-xss-protection", "1; mode=block"), // 62 - STATIC_ENTRY(":status", "100"), // 63 - STATIC_ENTRY(":status", "204"), // 64 - STATIC_ENTRY(":status", "206"), // 65 - STATIC_ENTRY(":status", "302"), // 66 - STATIC_ENTRY(":status", "400"), // 67 - STATIC_ENTRY(":status", "403"), // 68 - STATIC_ENTRY(":status", "421"), // 69 - STATIC_ENTRY(":status", "425"), // 70 - STATIC_ENTRY(":status", "500"), // 71 - STATIC_ENTRY("accept-language", ""), // 72 - STATIC_ENTRY("access-control-allow-credentials", "FALSE"), // 73 - STATIC_ENTRY("access-control-allow-credentials", "TRUE"), // 74 - STATIC_ENTRY("access-control-allow-headers", "*"), // 75 - STATIC_ENTRY("access-control-allow-methods", "get"), // 76 - STATIC_ENTRY("access-control-allow-methods", "get, post, options"), // 77 - STATIC_ENTRY("access-control-allow-methods", "options"), // 78 - STATIC_ENTRY("access-control-expose-headers", "content-length"), // 79 - STATIC_ENTRY("access-control-request-headers", "content-type"), // 80 - STATIC_ENTRY("access-control-request-method", "get"), // 81 - STATIC_ENTRY("access-control-request-method", "post"), // 82 - STATIC_ENTRY("alt-svc", "clear"), // 83 - STATIC_ENTRY("authorization", ""), // 84 - STATIC_ENTRY( - "content-security-policy", - "script-src 'none'; object-src 'none'; base-uri 'none'"), // 85 - STATIC_ENTRY("early-data", "1"), // 86 - STATIC_ENTRY("expect-ct", ""), // 87 - STATIC_ENTRY("forwarded", ""), // 88 - STATIC_ENTRY("if-range", ""), // 89 - STATIC_ENTRY("origin", ""), // 90 - STATIC_ENTRY("purpose", "prefetch"), // 91 - STATIC_ENTRY("server", ""), // 92 - STATIC_ENTRY("timing-allow-origin", "*"), // 93 - STATIC_ENTRY("upgrade-insecure-requests", "1"), // 94 - STATIC_ENTRY("user-agent", ""), // 95 - STATIC_ENTRY("x-forwarded-for", ""), // 96 - STATIC_ENTRY("x-frame-options", "deny"), // 97 - STATIC_ENTRY("x-frame-options", "sameorigin"), // 98 -}; +const std::vector<QpackStaticEntry>& QpackStaticTableVector() { + static const auto* kQpackStaticTable = new std::vector<QpackStaticEntry>{ + STATIC_ENTRY(":authority", ""), // 0 + STATIC_ENTRY(":path", "/"), // 1 + STATIC_ENTRY("age", "0"), // 2 + STATIC_ENTRY("content-disposition", ""), // 3 + STATIC_ENTRY("content-length", "0"), // 4 + STATIC_ENTRY("cookie", ""), // 5 + STATIC_ENTRY("date", ""), // 6 + STATIC_ENTRY("etag", ""), // 7 + STATIC_ENTRY("if-modified-since", ""), // 8 + STATIC_ENTRY("if-none-match", ""), // 9 + STATIC_ENTRY("last-modified", ""), // 10 + STATIC_ENTRY("link", ""), // 11 + STATIC_ENTRY("location", ""), // 12 + STATIC_ENTRY("referer", ""), // 13 + STATIC_ENTRY("set-cookie", ""), // 14 + STATIC_ENTRY(":method", "CONNECT"), // 15 + STATIC_ENTRY(":method", "DELETE"), // 16 + STATIC_ENTRY(":method", "GET"), // 17 + STATIC_ENTRY(":method", "HEAD"), // 18 + STATIC_ENTRY(":method", "OPTIONS"), // 19 + STATIC_ENTRY(":method", "POST"), // 20 + STATIC_ENTRY(":method", "PUT"), // 21 + STATIC_ENTRY(":scheme", "http"), // 22 + STATIC_ENTRY(":scheme", "https"), // 23 + STATIC_ENTRY(":status", "103"), // 24 + STATIC_ENTRY(":status", "200"), // 25 + STATIC_ENTRY(":status", "304"), // 26 + STATIC_ENTRY(":status", "404"), // 27 + STATIC_ENTRY(":status", "503"), // 28 + STATIC_ENTRY("accept", "*/*"), // 29 + STATIC_ENTRY("accept", "application/dns-message"), // 30 + STATIC_ENTRY("accept-encoding", "gzip, deflate, br"), // 31 + STATIC_ENTRY("accept-ranges", "bytes"), // 32 + STATIC_ENTRY("access-control-allow-headers", "cache-control"), // 33 + STATIC_ENTRY("access-control-allow-headers", "content-type"), // 35 + STATIC_ENTRY("access-control-allow-origin", "*"), // 35 + STATIC_ENTRY("cache-control", "max-age=0"), // 36 + STATIC_ENTRY("cache-control", "max-age=2592000"), // 37 + STATIC_ENTRY("cache-control", "max-age=604800"), // 38 + STATIC_ENTRY("cache-control", "no-cache"), // 39 + STATIC_ENTRY("cache-control", "no-store"), // 40 + STATIC_ENTRY("cache-control", "public, max-age=31536000"), // 41 + STATIC_ENTRY("content-encoding", "br"), // 42 + STATIC_ENTRY("content-encoding", "gzip"), // 43 + STATIC_ENTRY("content-type", "application/dns-message"), // 44 + STATIC_ENTRY("content-type", "application/javascript"), // 45 + STATIC_ENTRY("content-type", "application/json"), // 46 + STATIC_ENTRY("content-type", "application/x-www-form-urlencoded"), // 47 + STATIC_ENTRY("content-type", "image/gif"), // 48 + STATIC_ENTRY("content-type", "image/jpeg"), // 49 + STATIC_ENTRY("content-type", "image/png"), // 50 + STATIC_ENTRY("content-type", "text/css"), // 51 + STATIC_ENTRY("content-type", "text/html; charset=utf-8"), // 52 + STATIC_ENTRY("content-type", "text/plain"), // 53 + STATIC_ENTRY("content-type", "text/plain;charset=utf-8"), // 54 + STATIC_ENTRY("range", "bytes=0-"), // 55 + STATIC_ENTRY("strict-transport-security", "max-age=31536000"), // 56 + STATIC_ENTRY("strict-transport-security", + "max-age=31536000; includesubdomains"), // 57 + STATIC_ENTRY("strict-transport-security", + "max-age=31536000; includesubdomains; preload"), // 58 + STATIC_ENTRY("vary", "accept-encoding"), // 59 + STATIC_ENTRY("vary", "origin"), // 60 + STATIC_ENTRY("x-content-type-options", "nosniff"), // 61 + STATIC_ENTRY("x-xss-protection", "1; mode=block"), // 62 + STATIC_ENTRY(":status", "100"), // 63 + STATIC_ENTRY(":status", "204"), // 64 + STATIC_ENTRY(":status", "206"), // 65 + STATIC_ENTRY(":status", "302"), // 66 + STATIC_ENTRY(":status", "400"), // 67 + STATIC_ENTRY(":status", "403"), // 68 + STATIC_ENTRY(":status", "421"), // 69 + STATIC_ENTRY(":status", "425"), // 70 + STATIC_ENTRY(":status", "500"), // 71 + STATIC_ENTRY("accept-language", ""), // 72 + STATIC_ENTRY("access-control-allow-credentials", "FALSE"), // 73 + STATIC_ENTRY("access-control-allow-credentials", "TRUE"), // 74 + STATIC_ENTRY("access-control-allow-headers", "*"), // 75 + STATIC_ENTRY("access-control-allow-methods", "get"), // 76 + STATIC_ENTRY("access-control-allow-methods", "get, post, options"), // 77 + STATIC_ENTRY("access-control-allow-methods", "options"), // 78 + STATIC_ENTRY("access-control-expose-headers", "content-length"), // 79 + STATIC_ENTRY("access-control-request-headers", "content-type"), // 80 + STATIC_ENTRY("access-control-request-method", "get"), // 81 + STATIC_ENTRY("access-control-request-method", "post"), // 82 + STATIC_ENTRY("alt-svc", "clear"), // 83 + STATIC_ENTRY("authorization", ""), // 84 + STATIC_ENTRY( + "content-security-policy", + "script-src 'none'; object-src 'none'; base-uri 'none'"), // 85 + STATIC_ENTRY("early-data", "1"), // 86 + STATIC_ENTRY("expect-ct", ""), // 87 + STATIC_ENTRY("forwarded", ""), // 88 + STATIC_ENTRY("if-range", ""), // 89 + STATIC_ENTRY("origin", ""), // 90 + STATIC_ENTRY("purpose", "prefetch"), // 91 + STATIC_ENTRY("server", ""), // 92 + STATIC_ENTRY("timing-allow-origin", "*"), // 93 + STATIC_ENTRY("upgrade-insecure-requests", "1"), // 94 + STATIC_ENTRY("user-agent", ""), // 95 + STATIC_ENTRY("x-forwarded-for", ""), // 96 + STATIC_ENTRY("x-frame-options", "deny"), // 97 + STATIC_ENTRY("x-frame-options", "sameorigin"), // 98 + }; + return *kQpackStaticTable; +} #undef STATIC_ENTRY -namespace { - -QpackStaticTable* InitializeSharedStaticTable() { - auto shared_static_table = new QpackStaticTable(); - shared_static_table->Initialize(kQpackStaticTable, - QUIC_ARRAYSIZE(kQpackStaticTable)); - CHECK(shared_static_table->IsInitialized()); - return shared_static_table; -} - -} // namespace - const QpackStaticTable& ObtainQpackStaticTable() { - static QpackStaticTable* shared_static_table = InitializeSharedStaticTable(); + static const QpackStaticTable* const shared_static_table = []() { + auto* table = new QpackStaticTable(); + table->Initialize(QpackStaticTableVector().data(), + QpackStaticTableVector().size()); + CHECK(table->IsInitialized()); + return table; + }(); return *shared_static_table; }
diff --git a/net/third_party/quic/core/qpack/qpack_static_table.h b/net/third_party/quic/core/qpack/qpack_static_table.h index 2ce27635..e5367b9 100644 --- a/net/third_party/quic/core/qpack/qpack_static_table.h +++ b/net/third_party/quic/core/qpack/qpack_static_table.h
@@ -5,6 +5,8 @@ #ifndef NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_STATIC_TABLE_H_ #define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_STATIC_TABLE_H_ +#include <vector> + #include "net/third_party/quic/platform/api/quic_export.h" #include "net/third_party/spdy/core/hpack/hpack_constants.h" #include "net/third_party/spdy/core/hpack/hpack_static_table.h" @@ -16,7 +18,8 @@ // QPACK static table defined at // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#static-table. -QUIC_EXPORT_PRIVATE extern const QpackStaticEntry kQpackStaticTable[99]; +QUIC_EXPORT_PRIVATE const std::vector<QpackStaticEntry>& +QpackStaticTableVector(); // Returns a QpackStaticTable instance initialized with kQpackStaticTable. // The instance is read-only, has static lifetime, and is safe to share amoung
diff --git a/net/third_party/quic/core/qpack/qpack_static_table_test.cc b/net/third_party/quic/core/qpack/qpack_static_table_test.cc index 7bcecaed..f949f07f 100644 --- a/net/third_party/quic/core/qpack/qpack_static_table_test.cc +++ b/net/third_party/quic/core/qpack/qpack_static_table_test.cc
@@ -21,14 +21,15 @@ QpackStaticTable table; EXPECT_FALSE(table.IsInitialized()); - table.Initialize(kQpackStaticTable, QUIC_ARRAYSIZE(kQpackStaticTable)); + table.Initialize(QpackStaticTableVector().data(), + QpackStaticTableVector().size()); EXPECT_TRUE(table.IsInitialized()); auto static_entries = table.GetStaticEntries(); - EXPECT_EQ(QUIC_ARRAYSIZE(kQpackStaticTable), static_entries.size()); + EXPECT_EQ(QpackStaticTableVector().size(), static_entries.size()); auto static_index = table.GetStaticIndex(); - EXPECT_EQ(QUIC_ARRAYSIZE(kQpackStaticTable), static_index.size()); + EXPECT_EQ(QpackStaticTableVector().size(), static_index.size()); auto static_name_index = table.GetStaticNameIndex(); std::set<QuicStringPiece> names;
diff --git a/net/third_party/quic/core/quic_connection.cc b/net/third_party/quic/core/quic_connection.cc index 3c1251a..2bee1c1 100644 --- a/net/third_party/quic/core/quic_connection.cc +++ b/net/third_party/quic/core/quic_connection.cc
@@ -336,6 +336,9 @@ consecutive_num_packets_with_no_retransmittable_frames_(0), max_consecutive_num_packets_with_no_retransmittable_frames_( kMaxConsecutiveNonRetransmittablePackets), + min_received_before_ack_decimation_(kMinReceivedBeforeAckDecimation), + ack_frequency_before_ack_decimation_( + kDefaultRetransmittablePacketsBeforeAck), fill_up_link_during_probing_(false), probing_retransmission_pending_(false), stateless_reset_token_received_(false), @@ -1445,7 +1448,7 @@ if (should_last_packet_instigate_acks_ && !ack_queued_) { ++num_retransmittable_packets_received_since_last_ack_sent_; if (ack_mode_ != TCP_ACKING && - last_header_.packet_number > kMinReceivedBeforeAckDecimation) { + last_header_.packet_number > min_received_before_ack_decimation_) { // Ack up to 10 packets at once unless ack decimation is unlimited. if (!unlimited_ack_decimation_ && num_retransmittable_packets_received_since_last_ack_sent_ >= @@ -1472,7 +1475,7 @@ } else { // Ack with a timer or every 2 packets by default. if (num_retransmittable_packets_received_since_last_ack_sent_ >= - kDefaultRetransmittablePacketsBeforeAck) { + ack_frequency_before_ack_decimation_) { ack_queued_ = true; } else if (ShouldSetAckAlarm()) { const QuicTime approximate_now = clock_->ApproximateNow(); @@ -1880,7 +1883,7 @@ QUIC_RESTART_FLAG_COUNT_N(quic_enable_accept_random_ipn, 2, 2); // Configured to accept any packet number in range 1...0x7fffffff // as initial packet number. - if (last_header_.packet_number != 0) { + if (last_header_.packet_number != kInvalidPacketNumber) { // The last packet's number is not 0. Ensure that this packet // is reasonably close to where it should be. if (!Near(header.packet_number, last_header_.packet_number)) { @@ -1895,7 +1898,7 @@ // The "last packet's number" is 0, meaning that this packet is the first // one received. Ensure it is in range 1..kMaxRandomInitialPacketNumber, // inclusive. - if ((header.packet_number == 0) || + if ((header.packet_number == kInvalidPacketNumber) || (header.packet_number > kMaxRandomInitialPacketNumber)) { // packet number is bad. QUIC_DLOG(INFO) << ENDPOINT << "Initial packet " << header.packet_number @@ -2406,7 +2409,7 @@ if (transport_version() != QUIC_VERSION_35) { if (serialized_packet->retransmittable_frames.empty() && - serialized_packet->original_packet_number == 0) { + serialized_packet->original_packet_number == kInvalidPacketNumber) { // Increment consecutive_num_packets_with_no_retransmittable_frames_ if // this packet is a new transmission with no retransmittable frames. ++consecutive_num_packets_with_no_retransmittable_frames_;
diff --git a/net/third_party/quic/core/quic_connection.h b/net/third_party/quic/core/quic_connection.h index 81bca6bc..bb0b6b14 100644 --- a/net/third_party/quic/core/quic_connection.h +++ b/net/third_party/quic/core/quic_connection.h
@@ -792,6 +792,21 @@ fill_up_link_during_probing_ = new_value; } + size_t min_received_before_ack_decimation() const { + return min_received_before_ack_decimation_; + } + void set_min_received_before_ack_decimation(size_t new_value) { + min_received_before_ack_decimation_ = new_value; + } + + size_t ack_frequency_before_ack_decimation() const { + return ack_frequency_before_ack_decimation_; + } + void set_ack_frequency_before_ack_decimation(size_t new_value) { + DCHECK_GT(new_value, 0u); + ack_frequency_before_ack_decimation_ = new_value; + } + // If |defer| is true, configures the connection to defer sending packets in // response to an ACK to the SendAlarm. If |defer| is false, packets may be // sent immediately after receiving an ACK. @@ -1325,11 +1340,18 @@ // Consecutive number of sent packets which have no retransmittable frames. size_t consecutive_num_packets_with_no_retransmittable_frames_; + // After this many packets sent without retransmittable frames, an artificial // retransmittable frame(a WINDOW_UPDATE) will be created to solicit an ack // from the peer. Default to kMaxConsecutiveNonRetransmittablePackets. size_t max_consecutive_num_packets_with_no_retransmittable_frames_; + // Ack decimation will start happening after this many packets are received. + size_t min_received_before_ack_decimation_; + + // Before ack decimation starts (if enabled), we ack every n-th packet. + size_t ack_frequency_before_ack_decimation_; + // If true, the connection will fill up the pipe with extra data whenever the // congestion controller needs it in order to make a bandwidth estimate. This // is useful if the application pesistently underutilizes the link, but still
diff --git a/net/third_party/quic/core/quic_connection_test.cc b/net/third_party/quic/core/quic_connection_test.cc index dd5eb9c..b6143f30 100644 --- a/net/third_party/quic/core/quic_connection_test.cc +++ b/net/third_party/quic/core/quic_connection_test.cc
@@ -2251,6 +2251,52 @@ EXPECT_EQ(2u, writer_->packets_write_attempts()); } +TEST_P(QuicConnectionTest, AckSentEveryNthPacket) { + if (connection_.version().transport_version == QUIC_VERSION_35) { + return; + } + + connection_.set_ack_frequency_before_ack_decimation(3); + + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(39); + + // Expect 13 acks, every 3rd packet. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(13); + // Receives packets 1 - 39. + for (size_t i = 1; i <= 39; ++i) { + ProcessDataPacket(i); + } +} + +TEST_P(QuicConnectionTest, AckDecimationReducesAcks) { + if (GetQuicReloadableFlag(quic_enable_ack_decimation)) { + return; + } + + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); + + QuicConnectionPeer::SetAckMode( + &connection_, QuicConnection::ACK_DECIMATION_WITH_REORDERING); + + // Start ack decimation from 10th packet. + connection_.set_min_received_before_ack_decimation(10); + + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(30); + + // Expect 6 acks: 5 acks between packets 1-10, and ack at 20. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6); + // Receives packets 1 - 29. + for (size_t i = 1; i <= 29; ++i) { + ProcessDataPacket(i); + } + + // We now receive the 30th packet, and so we send an ack. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + ProcessDataPacket(30); +} + TEST_P(QuicConnectionTest, AckNeedsRetransmittableFrames) { if (connection_.version().transport_version == QUIC_VERSION_35) { return;
diff --git a/net/third_party/quic/core/quic_dispatcher.cc b/net/third_party/quic/core/quic_dispatcher.cc index 6177f88a..135aa80a 100644 --- a/net/third_party/quic/core/quic_dispatcher.cc +++ b/net/third_party/quic/core/quic_dispatcher.cc
@@ -137,7 +137,7 @@ // TODO(fayang): Use the right long header type for conneciton close sent by // dispatcher. creator_.SetLongHeaderType(RETRY); - if (!creator_.AddSavedFrame(QuicFrame(frame))) { + if (!creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION)) { QUIC_BUG << "Unable to add frame to an empty packet"; delete frame; return; @@ -163,7 +163,7 @@ QuicUtils::GetCryptoStreamId(framer_->transport_version()), reject.length(), offset, offset, /*fin=*/false, - /*needs_full_padding=*/true, &frame)) { + /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)) { QUIC_BUG << "Unable to consume data into an empty packet."; return; } @@ -548,9 +548,13 @@ !connection->termination_packets()->empty()) { action = QuicTimeWaitListManager::SEND_TERMINATION_PACKETS; } else if (connection->transport_version() > QUIC_VERSION_43) { - if (!GetQuicReloadableFlag( - quic_send_reset_for_post_handshake_connections_without_termination_packets) || // NOLINT - (source == ConnectionCloseSource::FROM_PEER)) { + // TODO(fayang): Always resetting IETF connections is a debugging + // expediency. Stop doing this when removing flag + // quic_always_reset_ietf_connections. + if (!GetQuicReloadableFlag(quic_always_reset_ietf_connections) && + (!GetQuicReloadableFlag( + quic_send_reset_for_post_handshake_connections_without_termination_packets) || // NOLINT + (source == ConnectionCloseSource::FROM_PEER))) { action = QuicTimeWaitListManager::DO_NOTHING; } else if (!connection->IsHandshakeConfirmed()) { QUIC_CODE_COUNT(quic_v44_add_to_time_wait_list_with_handshake_failed);
diff --git a/net/third_party/quic/core/quic_framer.cc b/net/third_party/quic/core/quic_framer.cc index 2d550a4..1bd81fa7 100644 --- a/net/third_party/quic/core/quic_framer.cc +++ b/net/third_party/quic/core/quic_framer.cc
@@ -1513,7 +1513,8 @@ set_detailed_error("Unable to read packet number."); return RaiseError(QUIC_INVALID_PACKET_HEADER); } - if (header->packet_number == 0u) { + + if (header->packet_number == kInvalidPacketNumber) { if (IsIetfStatelessResetPacket(*header)) { // This is a stateless reset packet. QuicIetfStatelessResetPacket packet( @@ -2080,7 +2081,7 @@ return RaiseError(QUIC_INVALID_PACKET_HEADER); } - if (header->packet_number == 0u) { + if (header->packet_number == kInvalidPacketNumber) { set_detailed_error("packet numbers cannot be 0."); return RaiseError(QUIC_INVALID_PACKET_HEADER); }
diff --git a/net/third_party/quic/core/quic_packet_creator.cc b/net/third_party/quic/core/quic_packet_creator.cc index e8c542a3..25ba1b2 100644 --- a/net/third_party/quic/core/quic_packet_creator.cc +++ b/net/third_party/quic/core/quic_packet_creator.cc
@@ -10,6 +10,7 @@ #include "base/macros.h" #include "net/third_party/quic/core/crypto/crypto_protocol.h" #include "net/third_party/quic/core/quic_data_writer.h" +#include "net/third_party/quic/core/quic_types.h" #include "net/third_party/quic/core/quic_utils.h" #include "net/third_party/quic/platform/api/quic_aligned.h" #include "net/third_party/quic/platform/api/quic_arraysize.h" @@ -55,7 +56,9 @@ long_header_type_(HANDSHAKE), pending_padding_bytes_(0), needs_full_padding_(false), - can_set_transmission_type_(false) { + can_set_transmission_type_(false), + set_transmission_type_for_next_frame_( + GetQuicReloadableFlag(quic_set_transmission_type_for_next_frame)) { SetMaxPacketLength(kDefaultMaxPacketSize); } @@ -133,6 +136,7 @@ QuicStreamOffset offset, bool fin, bool needs_full_padding, + TransmissionType transmission_type, QuicFrame* frame) { if (!HasRoomForStreamFrame(id, offset, write_length - iov_offset)) { return false; @@ -151,7 +155,8 @@ ConnectionCloseSource::FROM_SELF); return false; } - if (!AddFrame(*frame, /*save_retransmittable_frames=*/true)) { + if (!AddFrame(*frame, /*save_retransmittable_frames=*/true, + transmission_type)) { // Fails if we try to write unencrypted stream data. return false; } @@ -280,7 +285,7 @@ // Serialize the packet and restore packet number length state. for (const QuicFrame& frame : retransmission.retransmittable_frames) { - bool success = AddFrame(frame, false); + bool success = AddFrame(frame, false, retransmission.transmission_type); QUIC_BUG_IF(!success) << " Failed to add frame of type:" << frame.type << " num_frames:" << retransmission.retransmittable_frames.size() @@ -332,14 +337,14 @@ packet_.has_stop_waiting = false; packet_.has_crypto_handshake = NOT_HANDSHAKE; packet_.num_padding_bytes = 0; - packet_.original_packet_number = 0; - if (!can_set_transmission_type_) { + packet_.original_packet_number = kInvalidPacketNumber; + if (!can_set_transmission_type_ || ShouldSetTransmissionTypeForNextFrame()) { packet_.transmission_type = NOT_RETRANSMISSION; } packet_.encrypted_buffer = nullptr; packet_.encrypted_length = 0; DCHECK(packet_.retransmittable_frames.empty()); - packet_.largest_acked = 0; + packet_.largest_acked = kInvalidPacketNumber; needs_full_padding_ = false; } @@ -349,6 +354,7 @@ QuicStreamOffset iov_offset, QuicStreamOffset stream_offset, bool fin, + TransmissionType transmission_type, size_t* num_bytes_consumed) { DCHECK(queued_frames_.empty()); // Write out the packet header @@ -397,6 +403,12 @@ return; } + if (ShouldSetTransmissionTypeForNextFrame()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_set_transmission_type_for_next_frame, 1, + 2); + packet_.transmission_type = transmission_type; + } + size_t encrypted_length = framer_->EncryptInPlace( packet_.encryption_level, packet_.packet_number, GetStartOfEncryptedData(framer_->transport_version(), header), @@ -472,12 +484,17 @@ return packet_size_; } -bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame) { - return AddFrame(frame, /*save_retransmittable_frames=*/true); +bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame, + TransmissionType transmission_type) { + return AddFrame(frame, /*save_retransmittable_frames=*/true, + transmission_type); } -bool QuicPacketCreator::AddPaddedSavedFrame(const QuicFrame& frame) { - if (AddFrame(frame, /*save_retransmittable_frames=*/true)) { +bool QuicPacketCreator::AddPaddedSavedFrame( + const QuicFrame& frame, + TransmissionType transmission_type) { + if (AddFrame(frame, /*save_retransmittable_frames=*/true, + transmission_type)) { needs_full_padding_ = true; return true; } @@ -693,21 +710,11 @@ header->long_packet_type = long_header_type_; } -bool QuicPacketCreator::ShouldRetransmit(const QuicFrame& frame) { - switch (frame.type) { - case ACK_FRAME: - case PADDING_FRAME: - case STOP_WAITING_FRAME: - case MTU_DISCOVERY_FRAME: - return false; - default: - return true; - } -} - bool QuicPacketCreator::AddFrame(const QuicFrame& frame, - bool save_retransmittable_frames) { - QUIC_DVLOG(1) << ENDPOINT << "Adding frame: " << frame; + bool save_retransmittable_frames, + TransmissionType transmission_type) { + QUIC_DVLOG(1) << ENDPOINT << "Adding frame with transmission type " + << transmission_type << ": " << frame; if (frame.type == STREAM_FRAME && frame.stream_frame.stream_id != QuicUtils::GetCryptoStreamId(framer_->transport_version()) && @@ -732,7 +739,8 @@ packet_size_ += ExpansionOnNewFrame() + frame_len; - if (save_retransmittable_frames && ShouldRetransmit(frame)) { + if (save_retransmittable_frames && + QuicUtils::IsRetransmittableFrame(frame.type)) { if (packet_.retransmittable_frames.empty()) { packet_.retransmittable_frames.reserve(2); } @@ -758,6 +766,14 @@ debug_delegate_->OnFrameAddedToPacket(frame); } + // Packet transmission type is determined by the last added retransmittable + // frame. + if (ShouldSetTransmissionTypeForNextFrame() && + QuicUtils::IsRetransmittableFrame(frame.type)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_set_transmission_type_for_next_frame, 2, + 2); + packet_.transmission_type = transmission_type; + } return true; } @@ -789,7 +805,8 @@ } bool success = - AddFrame(QuicFrame(QuicPaddingFrame(packet_.num_padding_bytes)), false); + AddFrame(QuicFrame(QuicPaddingFrame(packet_.num_padding_bytes)), false, + packet_.transmission_type); DCHECK(success); } @@ -828,10 +845,14 @@ void QuicPacketCreator::SetTransmissionType(TransmissionType type) { DCHECK(can_set_transmission_type_); - QUIC_DVLOG_IF(1, type != packet_.transmission_type) - << ENDPOINT << "Setting Transmission type to " - << QuicUtils::TransmissionTypeToString(type); - packet_.transmission_type = type; + + if (!ShouldSetTransmissionTypeForNextFrame()) { + QUIC_DVLOG_IF(1, type != packet_.transmission_type) + << ENDPOINT << "Setting Transmission type to " + << QuicUtils::TransmissionTypeToString(type); + + packet_.transmission_type = type; + } } void QuicPacketCreator::SetLongHeaderType(QuicLongHeaderType type) {
diff --git a/net/third_party/quic/core/quic_packet_creator.h b/net/third_party/quic/core/quic_packet_creator.h index 7fa554c..beaff18 100644 --- a/net/third_party/quic/core/quic_packet_creator.h +++ b/net/third_party/quic/core/quic_packet_creator.h
@@ -18,6 +18,7 @@ #include "net/third_party/quic/core/quic_framer.h" #include "net/third_party/quic/core/quic_packets.h" #include "net/third_party/quic/core/quic_pending_retransmission.h" +#include "net/third_party/quic/core/quic_types.h" #include "net/third_party/quic/platform/api/quic_export.h" namespace quic { @@ -99,6 +100,7 @@ QuicStreamOffset offset, bool fin, bool needs_full_padding, + TransmissionType transmission_type, QuicFrame* frame); // Returns true if current open packet can accommodate more stream frames of @@ -130,6 +132,7 @@ QuicStreamOffset iov_offset, QuicStreamOffset stream_offset, bool fin, + TransmissionType transmission_type, size_t* num_bytes_consumed); // Returns true if there are frames pending to be serialized. @@ -162,10 +165,12 @@ // Tries to add |frame| to the packet creator's list of frames to be // serialized. If the frame does not fit into the current packet, flushes the // packet and returns false. - bool AddSavedFrame(const QuicFrame& frame); + bool AddSavedFrame(const QuicFrame& frame, + TransmissionType transmission_type); // Identical to AddSavedFrame, but allows the frame to be padded. - bool AddPaddedSavedFrame(const QuicFrame& frame); + bool AddPaddedSavedFrame(const QuicFrame& frame, + TransmissionType transmission_type); // Creates a version negotiation packet which supports |supported_versions|. std::unique_ptr<QuicEncryptedPacket> SerializeVersionNegotiationPacket( @@ -248,6 +253,10 @@ can_set_transmission_type_ = can_set_transmission_type; } + bool ShouldSetTransmissionTypeForNextFrame() const { + return can_set_transmission_type_ && set_transmission_type_for_next_frame_; + } + QuicByteCount pending_padding_bytes() const { return pending_padding_bytes_; } QuicTransportVersion transport_version() const { @@ -257,8 +266,6 @@ private: friend class test::QuicPacketCreatorPeer; - static bool ShouldRetransmit(const QuicFrame& frame); - // Creates a stream frame which fits into the current open packet. If // |write_length| is 0 and fin is true, the expected behavior is to consume // the fin but return 0. @@ -274,7 +281,9 @@ // Adds a |frame| if there is space and returns false and flushes all pending // frames if there isn't room. If |save_retransmittable_frames| is true, // saves the |frame| in the next SerializedPacket. - bool AddFrame(const QuicFrame& frame, bool save_retransmittable_frames); + bool AddFrame(const QuicFrame& frame, + bool save_retransmittable_frames, + TransmissionType transmission_type); // Adds a padding frame to the current packet (if there is space) when (1) // current packet needs full padding or (2) there are pending paddings. @@ -360,6 +369,10 @@ // If true, packet_'s transmission type is only set by // SetPacketTransmissionType and does not get cleared in ClearPacket. bool can_set_transmission_type_; + + // Latched value of --quic_set_transmission_type_for_next_frame. Don't use + // this variable directly, use ShouldSetTransmissionTypeForNextFrame instead. + bool set_transmission_type_for_next_frame_; }; } // namespace quic
diff --git a/net/third_party/quic/core/quic_packet_creator_test.cc b/net/third_party/quic/core/quic_packet_creator_test.cc index 41dbf3e..d977bc2c 100644 --- a/net/third_party/quic/core/quic_packet_creator_test.cc +++ b/net/third_party/quic/core/quic_packet_creator_test.cc
@@ -15,6 +15,7 @@ #include "net/third_party/quic/core/quic_data_writer.h" #include "net/third_party/quic/core/quic_pending_retransmission.h" #include "net/third_party/quic/core/quic_simple_buffer_allocator.h" +#include "net/third_party/quic/core/quic_types.h" #include "net/third_party/quic/core/quic_utils.h" #include "net/third_party/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quic/platform/api/quic_socket_address.h" @@ -85,6 +86,7 @@ QuicStreamOffset offset, bool fin, bool needs_full_padding, + TransmissionType transmission_type, QuicFrame* frame) { // Save data before data is consumed. QuicByteCount data_length = total_length - iov_offset; @@ -93,7 +95,8 @@ data_length); } return QuicPacketCreator::ConsumeData(id, data_length, iov_offset, offset, - fin, needs_full_padding, frame); + fin, needs_full_padding, + transmission_type, frame); } void StopSendingVersion() { @@ -540,7 +543,7 @@ MakeIOVector("test", &iov_); ASSERT_TRUE(creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, - 1u, iov_.iov_len, 0u, 0u, false, false, &frame)); + 1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame)); size_t consumed = frame.stream_frame.data_length; EXPECT_EQ(4u, consumed); CheckStreamFrame( @@ -554,7 +557,7 @@ MakeIOVector("test", &iov_); ASSERT_TRUE(creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, - 1u, iov_.iov_len, 0u, 0u, true, false, &frame)); + 1u, iov_.iov_len, 0u, 0u, true, false, NOT_RETRANSMISSION, &frame)); size_t consumed = frame.stream_frame.data_length; EXPECT_EQ(4u, consumed); CheckStreamFrame( @@ -567,7 +570,7 @@ QuicFrame frame; ASSERT_TRUE(creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), nullptr, - 0u, 0u, 0u, 0u, true, false, &frame)); + 0u, 0u, 0u, 0u, true, false, NOT_RETRANSMISSION, &frame)); size_t consumed = frame.stream_frame.data_length; EXPECT_EQ(0u, consumed); CheckStreamFrame( @@ -597,7 +600,7 @@ this, &QuicPacketCreatorTest::ClearSerializedPacketForTests)); ASSERT_TRUE(creator_.ConsumeData(GetNthClientInitiatedStreamId(1), &iov_, 1u, iov_.iov_len, 0u, kOffset, false, - false, &frame)); + false, NOT_RETRANSMISSION, &frame)); size_t bytes_consumed = frame.stream_frame.data_length; EXPECT_LT(0u, bytes_consumed); creator_.Flush(); @@ -621,7 +624,7 @@ MakeIOVector(data, &iov_); ASSERT_TRUE(creator_.ConsumeData(GetNthClientInitiatedStreamId(1), &iov_, 1u, iov_.iov_len, 0u, kOffset, false, - false, &frame)); + false, NOT_RETRANSMISSION, &frame)); // BytesFree() returns bytes available for the next frame, which will // be two bytes smaller since the stream frame would need to be grown. @@ -656,7 +659,8 @@ Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); ASSERT_TRUE(creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, - 1u, iov_.iov_len, 0u, kOffset, false, true, &frame)); + 1u, iov_.iov_len, 0u, kOffset, false, true, NOT_RETRANSMISSION, + &frame)); size_t bytes_consumed = frame.stream_frame.data_length; EXPECT_LT(0u, bytes_consumed); creator_.Flush(); @@ -694,7 +698,7 @@ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); ASSERT_TRUE(creator_.ConsumeData(GetNthClientInitiatedStreamId(1), &iov_, 1u, iov_.iov_len, 0u, kOffset, false, - false, &frame)); + false, NOT_RETRANSMISSION, &frame)); size_t bytes_consumed = frame.stream_frame.data_length; EXPECT_LT(0u, bytes_consumed); creator_.Flush(); @@ -1119,7 +1123,7 @@ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); ASSERT_TRUE(creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, - 1u, iov_.iov_len, 0u, 0u, true, false, &frame)); + 1u, iov_.iov_len, 0u, 0u, true, false, NOT_RETRANSMISSION, &frame)); size_t consumed = frame.stream_frame.data_length; EXPECT_EQ(payload_length, consumed); const QuicString payload(payload_length, 'a'); @@ -1151,7 +1155,8 @@ // Add a variety of frame types and then a padding frame. QuicAckFrame ack_frame(InitAckFrame(10u)); - EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame))); + EXPECT_TRUE( + creator_.AddSavedFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION)); EXPECT_TRUE(creator_.HasPendingFrames()); EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()))); @@ -1160,7 +1165,7 @@ MakeIOVector("test", &iov_); ASSERT_TRUE(creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, - 1u, iov_.iov_len, 0u, 0u, false, false, &frame)); + 1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame)); size_t consumed = frame.stream_frame.data_length; EXPECT_EQ(4u, consumed); EXPECT_TRUE(creator_.HasPendingFrames()); @@ -1168,14 +1173,16 @@ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()))); QuicPaddingFrame padding_frame; - EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(padding_frame))); + EXPECT_TRUE( + creator_.AddSavedFrame(QuicFrame(padding_frame), NOT_RETRANSMISSION)); EXPECT_TRUE(creator_.HasPendingFrames()); EXPECT_EQ(0u, creator_.BytesFree()); // Packet is full. Creator will flush. EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); - EXPECT_FALSE(creator_.AddSavedFrame(QuicFrame(&ack_frame))); + EXPECT_FALSE( + creator_.AddSavedFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION)); // Ensure the packet is successfully created. ASSERT_TRUE(serialized_packet_.encrypted_buffer); @@ -1216,7 +1223,7 @@ size_t num_bytes_consumed; creator_.CreateAndSerializeStreamFrame( QuicUtils::GetHeadersStreamId(client_framer_.transport_version()), - iov_.iov_len, 0, 0, true, &num_bytes_consumed); + iov_.iov_len, 0, 0, true, NOT_RETRANSMISSION, &num_bytes_consumed); EXPECT_EQ(4u, num_bytes_consumed); // Ensure the packet is successfully created. @@ -1241,8 +1248,9 @@ QuicStreamFrame stream_frame( QuicUtils::GetHeadersStreamId(client_framer_.transport_version()), /*fin=*/false, 0u, QuicStringPiece()); - EXPECT_QUIC_BUG(creator_.AddSavedFrame(QuicFrame(stream_frame)), - "Cannot send stream data without encryption."); + EXPECT_QUIC_BUG( + creator_.AddSavedFrame(QuicFrame(stream_frame), NOT_RETRANSMISSION), + "Cannot send stream data without encryption."); } TEST_P(QuicPacketCreatorTest, ChloTooLarge) { @@ -1264,11 +1272,11 @@ QuicFrame frame; EXPECT_CALL(delegate_, OnUnrecoverableError(QUIC_CRYPTO_CHLO_TOO_LARGE, _, _)); - EXPECT_QUIC_BUG( - creator_.ConsumeData( - QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), - &iov, 1u, iov.iov_len, 0u, 0u, false, false, &frame), - "Client hello won't fit in a single packet."); + EXPECT_QUIC_BUG(creator_.ConsumeData(QuicUtils::GetCryptoStreamId( + client_framer_.transport_version()), + &iov, 1u, iov.iov_len, 0u, 0u, false, + false, NOT_RETRANSMISSION, &frame), + "Client hello won't fit in a single packet."); } TEST_P(QuicPacketCreatorTest, PendingPadding) { @@ -1305,7 +1313,7 @@ ASSERT_TRUE(creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, 1u, iov_.iov_len, 0u, 0u, false, - /*needs_full_padding=*/true, &frame)); + /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)); EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); creator_.Flush(); @@ -1381,7 +1389,7 @@ creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, - 1u, iov_.iov_len, 0u, 0u, false, false, &frame); + 1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame); creator_.Flush(); { InSequence s; @@ -1422,7 +1430,7 @@ MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize), &iov_); creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, - 1u, iov_.iov_len, 0u, 0u, false, false, &frame); + 1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame); creator_.Flush(); // 1 byte padding is sent. EXPECT_EQ(pending_padding_bytes - 1, creator_.pending_padding_bytes()); @@ -1430,7 +1438,8 @@ MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize + 1), &iov_); creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, - 1u, iov_.iov_len, 0u, kStreamFramePayloadSize, false, false, &frame); + 1u, iov_.iov_len, 0u, kStreamFramePayloadSize, false, false, + NOT_RETRANSMISSION, &frame); // No padding is sent. creator_.Flush(); EXPECT_EQ(pending_padding_bytes - 1, creator_.pending_padding_bytes()); @@ -1451,7 +1460,7 @@ ASSERT_TRUE(creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, 1u, iov_.iov_len, 0u, 0u, false, - /*needs_full_padding=*/true, &frame)); + /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)); EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke([expected_buffer](SerializedPacket* serialized_packet) { @@ -1484,18 +1493,18 @@ EXPECT_TRUE( creator_.HasRoomForMessageFrame(creator_.GetLargestMessagePayload())); QuicString message(creator_.GetLargestMessagePayload(), 'a'); - EXPECT_TRUE( - creator_.AddSavedFrame(QuicFrame(new QuicMessageFrame(1, message)))); + EXPECT_TRUE(creator_.AddSavedFrame( + QuicFrame(new QuicMessageFrame(1, message)), NOT_RETRANSMISSION)); EXPECT_TRUE(creator_.HasPendingFrames()); creator_.Flush(); - EXPECT_TRUE( - creator_.AddSavedFrame(QuicFrame(new QuicMessageFrame(2, "message")))); + EXPECT_TRUE(creator_.AddSavedFrame( + QuicFrame(new QuicMessageFrame(2, "message")), NOT_RETRANSMISSION)); EXPECT_TRUE(creator_.HasPendingFrames()); // Verify if a new frame is added, 1 byte message length will be added. EXPECT_EQ(1u, creator_.ExpansionOnNewFrame()); - EXPECT_TRUE( - creator_.AddSavedFrame(QuicFrame(new QuicMessageFrame(3, "message2")))); + EXPECT_TRUE(creator_.AddSavedFrame( + QuicFrame(new QuicMessageFrame(3, "message2")), NOT_RETRANSMISSION)); EXPECT_EQ(1u, creator_.ExpansionOnNewFrame()); creator_.Flush(); @@ -1503,16 +1512,17 @@ MakeIOVector("test", &iov_); EXPECT_TRUE(creator_.ConsumeData( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, - 1u, iov_.iov_len, 0u, 0u, false, false, &frame)); - EXPECT_TRUE( - creator_.AddSavedFrame(QuicFrame(new QuicMessageFrame(1, "message")))); + 1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame)); + EXPECT_TRUE(creator_.AddSavedFrame( + QuicFrame(new QuicMessageFrame(1, "message")), NOT_RETRANSMISSION)); EXPECT_TRUE(creator_.HasPendingFrames()); // Verify there is not enough room for largest payload. EXPECT_FALSE( creator_.HasRoomForMessageFrame(creator_.GetLargestMessagePayload())); // Add largest message will causes the flush of the stream frame. QuicMessageFrame message_frame(2, message); - EXPECT_FALSE(creator_.AddSavedFrame(QuicFrame(&message_frame))); + EXPECT_FALSE( + creator_.AddSavedFrame(QuicFrame(&message_frame), NOT_RETRANSMISSION)); EXPECT_FALSE(creator_.HasPendingFrames()); } @@ -1525,8 +1535,10 @@ // Test all possible size of message frames. for (size_t message_size = 0; message_size <= creator_.GetLargestMessagePayload(); ++message_size) { - EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(new QuicMessageFrame( - 0, QuicStringPiece(message_buffer.data(), message_size))))); + EXPECT_TRUE(creator_.AddSavedFrame( + QuicFrame(new QuicMessageFrame( + 0, QuicStringPiece(message_buffer.data(), message_size))), + NOT_RETRANSMISSION)); EXPECT_TRUE(creator_.HasPendingFrames()); size_t expansion_bytes = message_size >= 64 ? 2 : 1; @@ -1547,6 +1559,45 @@ } } +TEST_P(QuicPacketCreatorTest, PacketTransmissionType) { + creator_.set_can_set_transmission_type(true); + creator_.SetTransmissionType(NOT_RETRANSMISSION); + + QuicAckFrame temp_ack_frame; + QuicFrame ack_frame(&temp_ack_frame); + ASSERT_FALSE(QuicUtils::IsRetransmittableFrame(ack_frame.type)); + + QuicFrame stream_frame(QuicStreamFrame( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + /*fin=*/false, 0u, QuicStringPiece())); + ASSERT_TRUE(QuicUtils::IsRetransmittableFrame(stream_frame.type)); + + QuicFrame padding_frame{QuicPaddingFrame()}; + ASSERT_FALSE(QuicUtils::IsRetransmittableFrame(padding_frame.type)); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + + EXPECT_TRUE(creator_.AddSavedFrame(ack_frame, LOSS_RETRANSMISSION)); + ASSERT_FALSE(serialized_packet_.encrypted_buffer); + + EXPECT_TRUE(creator_.AddSavedFrame(stream_frame, RTO_RETRANSMISSION)); + ASSERT_FALSE(serialized_packet_.encrypted_buffer); + + EXPECT_TRUE(creator_.AddSavedFrame(padding_frame, TLP_RETRANSMISSION)); + creator_.Flush(); + ASSERT_TRUE(serialized_packet_.encrypted_buffer); + + if (creator_.ShouldSetTransmissionTypeForNextFrame()) { + // The last retransmittable frame on packet is a stream frame, the packet's + // transmission type should be the same as the stream frame's. + EXPECT_EQ(serialized_packet_.transmission_type, RTO_RETRANSMISSION); + } else { + EXPECT_EQ(serialized_packet_.transmission_type, NOT_RETRANSMISSION); + } + DeleteSerializedPacket(); +} + } // namespace } // namespace test } // namespace quic
diff --git a/net/third_party/quic/core/quic_packet_generator.cc b/net/third_party/quic/core/quic_packet_generator.cc index 0cb0047..4e59b59 100644 --- a/net/third_party/quic/core/quic_packet_generator.cc +++ b/net/third_party/quic/core/quic_packet_generator.cc
@@ -7,6 +7,7 @@ #include <cstdint> #include "net/third_party/quic/core/crypto/quic_random.h" +#include "net/third_party/quic/core/quic_types.h" #include "net/third_party/quic/core/quic_utils.h" #include "net/third_party/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quic/platform/api/quic_flag_utils.h" @@ -21,6 +22,7 @@ DelegateInterface* delegate) : delegate_(delegate), packet_creator_(connection_id, framer, random_generator, delegate), + next_transmission_type_(NOT_RETRANSMISSION), flusher_attached_(false), should_send_ack_(false), should_send_stop_waiting_(false), @@ -93,7 +95,8 @@ QuicFrame frame; if (!packet_creator_.ConsumeData(id, write_length, total_bytes_consumed, offset + total_bytes_consumed, fin, - has_handshake, &frame)) { + has_handshake, next_transmission_type_, + &frame)) { // The creator is always flushed if there's not enough room for a new // stream frame before ConsumeData, so ConsumeData should always succeed. QUIC_BUG << "Failed to ConsumeData, stream:" << id; @@ -153,7 +156,7 @@ size_t bytes_consumed = 0; packet_creator_.CreateAndSerializeStreamFrame( id, write_length, total_bytes_consumed, offset + total_bytes_consumed, - fin, &bytes_consumed); + fin, next_transmission_type_, &bytes_consumed); total_bytes_consumed += bytes_consumed; } @@ -177,7 +180,8 @@ // Send the probe packet with the new length. SetMaxPacketLength(target_mtu); - const bool success = packet_creator_.AddPaddedSavedFrame(frame); + const bool success = + packet_creator_.AddPaddedSavedFrame(frame, next_transmission_type_); packet_creator_.Flush(); // The only reason AddFrame can fail is that the packet is too full to fit in // a ping. This is not possible for any sane MTU. @@ -262,16 +266,16 @@ QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " "generator tries to write control frames."; if (should_send_ack_) { - should_send_ack_ = - !packet_creator_.AddSavedFrame(delegate_->GetUpdatedAckFrame()); + should_send_ack_ = !packet_creator_.AddSavedFrame( + delegate_->GetUpdatedAckFrame(), next_transmission_type_); return !should_send_ack_; } if (should_send_stop_waiting_) { delegate_->PopulateStopWaitingFrame(&pending_stop_waiting_frame_); // If we can't this add the frame now, then we still need to do so later. - should_send_stop_waiting_ = - !packet_creator_.AddSavedFrame(QuicFrame(&pending_stop_waiting_frame_)); + should_send_stop_waiting_ = !packet_creator_.AddSavedFrame( + QuicFrame(&pending_stop_waiting_frame_), next_transmission_type_); // Return success if we have cleared out this flag (i.e., added the frame). // If we still need to send, then the frame is full, and we have failed. return !should_send_stop_waiting_; @@ -279,7 +283,9 @@ QUIC_BUG_IF(queued_control_frames_.empty()) << "AddNextPendingFrame called with no queued control frames."; - if (!packet_creator_.AddSavedFrame(queued_control_frames_.back())) { + + if (!packet_creator_.AddSavedFrame(queued_control_frames_.back(), + next_transmission_type_)) { // Packet was full. return false; } @@ -393,6 +399,9 @@ void QuicPacketGenerator::SetTransmissionType(TransmissionType type) { packet_creator_.SetTransmissionType(type); + if (packet_creator_.ShouldSetTransmissionTypeForNextFrame()) { + next_transmission_type_ = type; + } } void QuicPacketGenerator::SetLongHeaderType(QuicLongHeaderType type) { @@ -416,7 +425,8 @@ packet_creator_.Flush(); } QuicMessageFrame* frame = new QuicMessageFrame(message_id, message); - const bool success = packet_creator_.AddSavedFrame(QuicFrame(frame)); + const bool success = + packet_creator_.AddSavedFrame(QuicFrame(frame), next_transmission_type_); if (!success) { QUIC_BUG << "Failed to send message " << message_id; delete frame;
diff --git a/net/third_party/quic/core/quic_packet_generator.h b/net/third_party/quic/core/quic_packet_generator.h index a416f178..1ea66000 100644 --- a/net/third_party/quic/core/quic_packet_generator.h +++ b/net/third_party/quic/core/quic_packet_generator.h
@@ -249,6 +249,9 @@ QuicPacketCreator packet_creator_; QuicFrames queued_control_frames_; + // Transmission type of the next serialized packet. + TransmissionType next_transmission_type_; + // True if packet flusher is currently attached. bool flusher_attached_;
diff --git a/net/third_party/quic/core/quic_packet_generator_test.cc b/net/third_party/quic/core/quic_packet_generator_test.cc index 7dea9fc..19cbb229 100644 --- a/net/third_party/quic/core/quic_packet_generator_test.cc +++ b/net/third_party/quic/core/quic_packet_generator_test.cc
@@ -584,6 +584,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFastPath) { delegate_.SetCanWriteAnything(); + generator_.SetCanSetTransmissionType(true); + generator_.SetTransmissionType(LOSS_RETRANSMISSION); // Create a 10000 byte IOVector. CreateData(10000); @@ -603,6 +605,7 @@ EXPECT_FALSE(packets_.empty()); SerializedPacket packet = packets_.back(); EXPECT_TRUE(!packet.retransmittable_frames.empty()); + EXPECT_EQ(LOSS_RETRANSMISSION, packet.transmission_type); EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type); const QuicStreamFrame& stream_frame = packet.retransmittable_frames.front().stream_frame; @@ -811,6 +814,52 @@ CheckPacketContains(contents2, 1); } +// Regression test of b/120493795. +TEST_F(QuicPacketGeneratorTest, PacketTransmissionType) { + delegate_.SetCanWriteAnything(); + generator_.SetCanSetTransmissionType(true); + + // The first ConsumeData will fill the packet without flush. + generator_.SetTransmissionType(LOSS_RETRANSMISSION); + + size_t data_len = 1324; + CreateData(data_len); + QuicStreamId stream1_id = + QuicUtils::GetHeadersStreamId(framer_.transport_version()); + QuicConsumedData consumed = + generator_.ConsumeData(stream1_id, &iov_, 1u, iov_.iov_len, 0, NO_FIN); + EXPECT_EQ(data_len, consumed.bytes_consumed); + ASSERT_EQ(0u, creator_->BytesFree()) + << "Test setup failed: Please increase data_len to " + << data_len + creator_->BytesFree() << " bytes."; + + // The second ConsumeData can not be added to the packet and will flush. + generator_.SetTransmissionType(NOT_RETRANSMISSION); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + QuicStreamId stream2_id = stream1_id + 4; + + consumed = + generator_.ConsumeData(stream2_id, &iov_, 1u, iov_.iov_len, 0, NO_FIN); + EXPECT_EQ(data_len, consumed.bytes_consumed); + + // Ensure the packet is successfully created. + ASSERT_EQ(1u, packets_.size()); + ASSERT_TRUE(packets_[0].encrypted_buffer); + ASSERT_EQ(1u, packets_[0].retransmittable_frames.size()); + EXPECT_EQ(stream1_id, + packets_[0].retransmittable_frames[0].stream_frame.stream_id); + if (GetQuicReloadableFlag(quic_set_transmission_type_for_next_frame)) { + // Since the second frame was not added, the packet's transmission type + // should be the first frame's type. + EXPECT_EQ(packets_[0].transmission_type, LOSS_RETRANSMISSION); + } else { + EXPECT_EQ(packets_[0].transmission_type, NOT_RETRANSMISSION); + } +} + TEST_F(QuicPacketGeneratorTest, TestConnectionIdLength) { QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); generator_.SetConnectionIdLength(0);
diff --git a/net/third_party/quic/core/quic_packet_reader.cc b/net/third_party/quic/core/quic_packet_reader.cc index 39d74435..4bdfb076 100644 --- a/net/third_party/quic/core/quic_packet_reader.cc +++ b/net/third_party/quic/core/quic_packet_reader.cc
@@ -120,14 +120,13 @@ continue; } - QuicSocketAddress client_address = - QuicSocketAddress(packets_[i].raw_address); - QuicIpAddress server_ip; + QuicSocketAddress peer_address(packets_[i].raw_address); + QuicIpAddress self_ip; QuicWallTime packet_walltimestamp = QuicWallTime::Zero(); QuicSocketUtils::GetAddressAndTimestampFromMsghdr( - &mmsg_hdr_[i].msg_hdr, &server_ip, &packet_walltimestamp); - if (!server_ip.IsInitialized()) { - QUIC_BUG << "Unable to get server address."; + &mmsg_hdr_[i].msg_hdr, &self_ip, &packet_walltimestamp); + if (!self_ip.IsInitialized()) { + QUIC_BUG << "Unable to get self IP address."; continue; } @@ -167,8 +166,8 @@ QuicReceivedPacket packet(reinterpret_cast<char*>(packets_[i].iov.iov_base), mmsg_hdr_[i].msg_len, timestamp, false, ttl, has_ttl, headers, headers_length, false); - QuicSocketAddress server_address(server_ip, port); - processor->ProcessPacket(server_address, client_address, packet); + QuicSocketAddress self_address(self_ip, port); + processor->ProcessPacket(self_address, peer_address, packet); } if (packets_dropped != nullptr) { @@ -193,18 +192,18 @@ QuicPacketCount* packets_dropped) { char buf[kMaxV4PacketSize]; - QuicSocketAddress client_address; - QuicIpAddress server_ip; + QuicSocketAddress peer_address; + QuicIpAddress self_ip; QuicWallTime walltimestamp = QuicWallTime::Zero(); int bytes_read = QuicSocketUtils::ReadPacket(fd, buf, QUIC_ARRAYSIZE(buf), packets_dropped, - &server_ip, &walltimestamp, &client_address); + &self_ip, &walltimestamp, &peer_address); if (bytes_read < 0) { return false; // ReadPacket failed. } - if (!server_ip.IsInitialized()) { - QUIC_BUG << "Unable to get server address."; + if (!self_ip.IsInitialized()) { + QUIC_BUG << "Unable to get self IP address."; return false; } // This isn't particularly desirable, but not all platforms support socket @@ -215,8 +214,8 @@ QuicTime timestamp = clock.ConvertWallTimeToQuicTime(walltimestamp); QuicReceivedPacket packet(buf, bytes_read, timestamp, false); - QuicSocketAddress server_address(server_ip, port); - processor->ProcessPacket(server_address, client_address, packet); + QuicSocketAddress self_address(self_ip, port); + processor->ProcessPacket(self_address, peer_address, packet); // The socket read was successful, so return true even if packet dispatch // failed.
diff --git a/net/third_party/quic/core/quic_packets.cc b/net/third_party/quic/core/quic_packets.cc index 6dd0d43..0047fd7 100644 --- a/net/third_party/quic/core/quic_packets.cc +++ b/net/third_party/quic/core/quic_packets.cc
@@ -77,7 +77,7 @@ version( ParsedQuicVersion(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED)), nonce(nullptr), - packet_number(0), + packet_number(kInvalidPacketNumber), form(GOOGLE_QUIC_PACKET), long_packet_type(INITIAL), possible_stateless_reset_token(0) {} @@ -295,8 +295,8 @@ has_ack(has_ack), has_stop_waiting(has_stop_waiting), transmission_type(NOT_RETRANSMISSION), - original_packet_number(0), - largest_acked(0) {} + original_packet_number(kInvalidPacketNumber), + largest_acked(kInvalidPacketNumber) {} SerializedPacket::SerializedPacket(const SerializedPacket& other) = default; @@ -327,7 +327,7 @@ } serialized_packet->encrypted_buffer = nullptr; serialized_packet->encrypted_length = 0; - serialized_packet->largest_acked = 0; + serialized_packet->largest_acked = kInvalidPacketNumber; } char* CopyBuffer(const SerializedPacket& packet) {
diff --git a/net/third_party/quic/core/quic_sent_packet_manager.cc b/net/third_party/quic/core/quic_sent_packet_manager.cc index c997830..e3258685 100644 --- a/net/third_party/quic/core/quic_sent_packet_manager.cc +++ b/net/third_party/quic/core/quic_sent_packet_manager.cc
@@ -452,7 +452,7 @@ } else { // Clear the recorded first packet sent after loss when version or // encryption changes. - transmission_info->retransmission = 0; + transmission_info->retransmission = kInvalidPacketNumber; } } @@ -479,7 +479,7 @@ return; } QuicPacketNumber retransmission = info.retransmission; - while (retransmission != 0) { + while (retransmission != kInvalidPacketNumber) { const QuicTransmissionInfo& retransmit_info = unacked_packets_.GetTransmissionInfo(retransmission); retransmission = retransmit_info.retransmission; @@ -529,7 +529,7 @@ return packet_number; } QuicPacketNumber retransmission = transmission_info.retransmission; - while (retransmission != 0) { + while (retransmission != kInvalidPacketNumber) { packet_number = retransmission; retransmission = unacked_packets_.GetTransmissionInfo(retransmission).retransmission; @@ -612,7 +612,7 @@ QUIC_BUG_IF(serialized_packet->encrypted_length == 0) << "Cannot send empty packets."; - if (original_packet_number != 0) { + if (original_packet_number != kInvalidPacketNumber) { pending_retransmissions_.erase(original_packet_number); } @@ -758,7 +758,7 @@ } // Abandon non-retransmittable data that's in flight to ensure it doesn't // fill up the congestion window. - bool has_retransmissions = it->retransmission != 0; + bool has_retransmissions = it->retransmission != kInvalidPacketNumber; if (session_decides_what_to_write()) { has_retransmissions = it->state != OUTSTANDING; } @@ -1125,7 +1125,7 @@ QUIC_DVLOG(1) << ENDPOINT << "Got an ack for packet " << acked_packet.packet_number; last_ack_frame_.packets.Add(acked_packet.packet_number); - if (info->largest_acked > 0) { + if (info->largest_acked > kInvalidPacketNumber) { largest_packet_peer_knows_is_acked_ = std::max(largest_packet_peer_knows_is_acked_, info->largest_acked); }
diff --git a/net/third_party/quic/core/quic_transmission_info.cc b/net/third_party/quic/core/quic_transmission_info.cc index a4e3433..0ea2443 100644 --- a/net/third_party/quic/core/quic_transmission_info.cc +++ b/net/third_party/quic/core/quic_transmission_info.cc
@@ -16,8 +16,8 @@ state(OUTSTANDING), has_crypto_handshake(false), num_padding_bytes(0), - retransmission(0), - largest_acked(0) {} + retransmission(kInvalidPacketNumber), + largest_acked(kInvalidPacketNumber) {} QuicTransmissionInfo::QuicTransmissionInfo( EncryptionLevel level, @@ -36,8 +36,8 @@ state(OUTSTANDING), has_crypto_handshake(has_crypto_handshake), num_padding_bytes(num_padding_bytes), - retransmission(0), - largest_acked(0) {} + retransmission(kInvalidPacketNumber), + largest_acked(kInvalidPacketNumber) {} QuicTransmissionInfo::QuicTransmissionInfo(const QuicTransmissionInfo& other) = default;
diff --git a/net/third_party/quic/core/quic_unacked_packet_map.cc b/net/third_party/quic/core/quic_unacked_packet_map.cc index 92bb2a5..0224340 100644 --- a/net/third_party/quic/core/quic_unacked_packet_map.cc +++ b/net/third_party/quic/core/quic_unacked_packet_map.cc
@@ -78,7 +78,7 @@ unacked_packets_.push_back(info); // Swap the retransmittable frames to avoid allocations. // TODO(ianswett): Could use emplace_back when Chromium can. - if (old_packet_number == 0) { + if (old_packet_number == kInvalidPacketNumber) { if (has_crypto_handshake) { ++pending_crypto_packet_count_; last_crypto_packet_sent_time_ = sent_time; @@ -180,12 +180,12 @@ QuicTransmissionInfo* info) { if (session_decides_what_to_write_) { DeleteFrames(&info->retransmittable_frames); - info->retransmission = 0; + info->retransmission = kInvalidPacketNumber; return; } - while (info->retransmission != 0) { + while (info->retransmission != kInvalidPacketNumber) { const QuicPacketNumber retransmission = info->retransmission; - info->retransmission = 0; + info->retransmission = kInvalidPacketNumber; info = &unacked_packets_[retransmission - least_unacked_]; }
diff --git a/net/third_party/quic/core/quic_utils.cc b/net/third_party/quic/core/quic_utils.cc index ed89096..2418edf 100644 --- a/net/third_party/quic/core/quic_utils.cc +++ b/net/third_party/quic/core/quic_utils.cc
@@ -295,6 +295,19 @@ } // static +bool QuicUtils::IsRetransmittableFrame(QuicFrameType type) { + switch (type) { + case ACK_FRAME: + case PADDING_FRAME: + case STOP_WAITING_FRAME: + case MTU_DISCOVERY_FRAME: + return false; + default: + return true; + } +} + +// static SentPacketState QuicUtils::RetransmissionTypeToPacketState( TransmissionType retransmission_type) { switch (retransmission_type) {
diff --git a/net/third_party/quic/core/quic_utils.h b/net/third_party/quic/core/quic_utils.h index d09c91cc..e904dab 100644 --- a/net/third_party/quic/core/quic_utils.h +++ b/net/third_party/quic/core/quic_utils.h
@@ -84,6 +84,10 @@ // once, or if it's a crypto packet we never expect to receive an ack for. static bool IsAckable(SentPacketState state); + // Returns true if frame with |type| is retransmittable. A retransmittable + // frame should be retransmitted if it is detected as lost. + static bool IsRetransmittableFrame(QuicFrameType type); + // Returns packet state corresponding to |retransmission_type|. static SentPacketState RetransmissionTypeToPacketState( TransmissionType retransmission_type);
diff --git a/net/third_party/quic/platform/api/quic_file_utils.cc b/net/third_party/quic/platform/api/quic_file_utils.cc new file mode 100644 index 0000000..18158f0 --- /dev/null +++ b/net/third_party/quic/platform/api/quic_file_utils.cc
@@ -0,0 +1,22 @@ +// Copyright (c) 2019 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/third_party/quic/platform/api/quic_file_utils.h" + +#include "net/third_party/quic/platform/impl/quic_file_utils_impl.h" + +namespace quic { + +// Traverses the directory |dirname| and retuns all of the files +// it contains. +std::vector<QuicString> ReadFileContents(const QuicString& dirname) { + return ReadFileContentsImpl(dirname); +} + +// Reads the contents of |filename| as a string into |contents|. +void ReadFileContents(QuicStringPiece filename, QuicString* contents) { + ReadFileContentsImpl(filename, contents); +} + +} // namespace quic
diff --git a/net/third_party/quic/platform/api/quic_file_utils.h b/net/third_party/quic/platform/api/quic_file_utils.h index 053e41b..024c9958 100644 --- a/net/third_party/quic/platform/api/quic_file_utils.h +++ b/net/third_party/quic/platform/api/quic_file_utils.h
@@ -7,23 +7,20 @@ #include <vector> +#include "net/third_party/quic/platform/api/quic_export.h" #include "net/third_party/quic/platform/api/quic_string.h" #include "net/third_party/quic/platform/api/quic_string_piece.h" -#include "net/third_party/quic/platform/impl/quic_file_utils_impl.h" namespace quic { // Traverses the directory |dirname| and retuns all of the files // it contains. -std::vector<QuicString> ReadFileContents(const QuicString& dirname) { - return ReadFileContentsImpl(dirname); -} +QUIC_EXPORT_PRIVATE std::vector<QuicString> ReadFileContents( + const QuicString& dirname); // Reads the contents of |filename| as a string into |contents|. -void ReadFileContents(QuicStringPiece filename, QuicString* contents) { - ReadFileContentsImpl(filename, contents); -} - +QUIC_EXPORT_PRIVATE void ReadFileContents(QuicStringPiece filename, + QuicString* contents); } // namespace quic #endif // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_FILE_UTILS_H_
diff --git a/net/third_party/quic/platform/api/quic_goog_cc_sender.h b/net/third_party/quic/platform/api/quic_goog_cc_sender.h index 983efe6..e411a8b 100644 --- a/net/third_party/quic/platform/api/quic_goog_cc_sender.h +++ b/net/third_party/quic/platform/api/quic_goog_cc_sender.h
@@ -17,6 +17,7 @@ namespace quic { // Interface for creating a GoogCC SendAlgorithmInterface. +// TODO(b/122312335): Remove this file. SendAlgorithmInterface* CreateGoogCcSender( const QuicClock* clock, const RttStats* rtt_stats,
diff --git a/net/third_party/quic/platform/api/quic_ip_address.h b/net/third_party/quic/platform/api/quic_ip_address.h index eef6686..7229e80 100644 --- a/net/third_party/quic/platform/api/quic_ip_address.h +++ b/net/third_party/quic/platform/api/quic_ip_address.h
@@ -41,7 +41,7 @@ IpAddressFamily address_family() const; int AddressFamilyToInt() const; // Returns the address as a sequence of bytes in network-byte-order. IPv4 will - // be 6 bytes. IPv6 will be 18 bytes. + // be 4 bytes. IPv6 will be 16 bytes. QuicString ToPackedString() const; // Returns string representation of the address. QuicString ToString() const;
diff --git a/net/third_party/quic/platform/impl/quic_file_utils_impl.h b/net/third_party/quic/platform/impl/quic_file_utils_impl.h index 5145d2b..cf1d2f9a1 100644 --- a/net/third_party/quic/platform/impl/quic_file_utils_impl.h +++ b/net/third_party/quic/platform/impl/quic_file_utils_impl.h
@@ -11,7 +11,6 @@ #include "base/files/file_util.h" #include "net/third_party/quic/platform/api/quic_string.h" #include "net/third_party/quic/platform/api/quic_string_piece.h" -#include "net/third_party/quic/platform/impl/quic_file_utils_impl.h" using base::FilePath;
diff --git a/net/third_party/quic/quartc/quartc_factory.cc b/net/third_party/quic/quartc/quartc_factory.cc index 80e04f5b..d7f951e 100644 --- a/net/third_party/quic/quartc/quartc_factory.cc +++ b/net/third_party/quic/quartc/quartc_factory.cc
@@ -5,7 +5,6 @@ #include "net/third_party/quic/quartc/quartc_factory.h" #include "net/third_party/quic/core/crypto/quic_random.h" -#include "net/third_party/quic/platform/api/quic_goog_cc_sender.h" #include "net/third_party/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quic/platform/api/quic_socket_address.h" #include "net/third_party/quic/quartc/quartc_session.h" @@ -92,30 +91,6 @@ sent_packet_manager.set_delayed_ack_time( QuicTime::Delta::FromMilliseconds(100)); - switch (quartc_session_config.congestion_control_type) { - case kBBR: - copt.push_back(kTBBR); - break; - case kGoogCC: { - SendAlgorithmInterface* sender = CreateGoogCcSender( - clock_, sent_packet_manager.GetRttStats(), - &sent_packet_manager.unacked_packets(), GetRandomGenerator(), - /*stats=*/nullptr, sent_packet_manager.initial_congestion_window(), - kDefaultMaxCongestionWindowPackets); - sent_packet_manager.SetSendAlgorithm(sender); - break; - } - case kCubicBytes: - QUIC_LOG(FATAL) << "kCubicBytes is not supported"; - break; - case kRenoBytes: - QUIC_LOG(FATAL) << "kRenoBytes is not supported"; - break; - case kPCC: - QUIC_LOG(FATAL) << "kPCC is not supported"; - break; - } - // Note: flag settings have no effect for Exoblaze builds since // SetQuicReloadableFlag() gets stubbed out. SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true); // Enable BBR6,7,8. @@ -139,6 +114,13 @@ quic_connection->set_fill_up_link_during_probing(true); + // We start ack decimation after 15 packets. Typically, we would see + // 1-2 crypto handshake packets, one media packet, and 10 probing packets. + // We want to get acks for the probing packets as soon as possible, + // but we can start using ack decimation right after first probing completes. + // The default was to not start ack decimation for the first 100 packets. + quic_connection->set_min_received_before_ack_decimation(15); + // TODO(b/112192153): Test and possible enable slower startup when pipe // filling is ready to use. Slower startup is kBBRS.
diff --git a/net/third_party/quic/quartc/quartc_factory.h b/net/third_party/quic/quartc/quartc_factory.h index c5207f1..e15db88 100644 --- a/net/third_party/quic/quartc/quartc_factory.h +++ b/net/third_party/quic/quartc/quartc_factory.h
@@ -48,10 +48,6 @@ QuicTime::Delta max_time_before_crypto_handshake = QuicTime::Delta::Zero(); QuicTime::Delta idle_network_timeout = QuicTime::Delta::Zero(); - // Congestion control type used for this session. Only BBR and GoogCC are - // supported. - CongestionControlType congestion_control_type = kBBR; - // Tail loss probes (TLP) are enabled by default, but it may be useful to // disable them in tests. We can also consider disabling them in production // if we discover that tail loss probes add overhead in low bitrate audio.
diff --git a/net/third_party/quic/test_tools/quic_packet_creator_peer.cc b/net/third_party/quic/test_tools/quic_packet_creator_peer.cc index e1f4bde5..c6a98c9 100644 --- a/net/third_party/quic/test_tools/quic_packet_creator_peer.cc +++ b/net/third_party/quic/test_tools/quic_packet_creator_peer.cc
@@ -5,6 +5,7 @@ #include "net/third_party/quic/test_tools/quic_packet_creator_peer.h" #include "net/third_party/quic/core/quic_packet_creator.h" +#include "net/third_party/quic/core/quic_types.h" namespace quic { namespace test { @@ -73,7 +74,7 @@ DCHECK(creator->queued_frames_.empty()); DCHECK(!frames.empty()); for (const QuicFrame& frame : frames) { - bool success = creator->AddFrame(frame, false); + bool success = creator->AddFrame(frame, false, NOT_RETRANSMISSION); DCHECK(success); } creator->SerializePacket(buffer, buffer_len);
diff --git a/net/third_party/spdy/core/http2_frame_decoder_adapter.cc b/net/third_party/spdy/core/http2_frame_decoder_adapter.cc index e1faaf0..63e1d02 100644 --- a/net/third_party/spdy/core/http2_frame_decoder_adapter.cc +++ b/net/third_party/spdy/core/http2_frame_decoder_adapter.cc
@@ -16,7 +16,6 @@ #include <utility> #include "base/logging.h" -#include "base/sys_byteorder.h" #include "build/build_config.h" #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" #include "net/third_party/quiche/src/http2/decoder/decode_status.h" @@ -33,6 +32,7 @@ #include "net/third_party/spdy/core/spdy_header_block.h" #include "net/third_party/spdy/core/spdy_headers_handler_interface.h" #include "net/third_party/spdy/core/spdy_protocol.h" +#include "net/third_party/spdy/platform/api/spdy_endianness_util.h" #include "net/third_party/spdy/platform/api/spdy_estimate_memory_usage.h" #include "net/third_party/spdy/platform/api/spdy_flags.h" #include "net/third_party/spdy/platform/api/spdy_ptr_util.h"
diff --git a/net/third_party/spdy/core/spdy_frame_builder.h b/net/third_party/spdy/core/spdy_frame_builder.h index b634f07..63096aad 100644 --- a/net/third_party/spdy/core/spdy_frame_builder.h +++ b/net/third_party/spdy/core/spdy_frame_builder.h
@@ -11,10 +11,10 @@ #include <memory> #include "base/gtest_prod_util.h" -#include "base/sys_byteorder.h" #include "net/third_party/spdy/core/spdy_bug_tracker.h" #include "net/third_party/spdy/core/spdy_protocol.h" #include "net/third_party/spdy/core/zero_copy_output_buffer.h" +#include "net/third_party/spdy/platform/api/spdy_endianness_util.h" #include "net/third_party/spdy/platform/api/spdy_export.h" #include "net/third_party/spdy/platform/api/spdy_string_piece.h" @@ -81,20 +81,20 @@ // host to network form. bool WriteUInt8(uint8_t value) { return WriteBytes(&value, sizeof(value)); } bool WriteUInt16(uint16_t value) { - value = base::HostToNet16(value); + value = SpdyHostToNet16(value); return WriteBytes(&value, sizeof(value)); } bool WriteUInt24(uint32_t value) { - value = base::HostToNet32(value); + value = SpdyHostToNet32(value); return WriteBytes(reinterpret_cast<char*>(&value) + 1, sizeof(value) - 1); } bool WriteUInt32(uint32_t value) { - value = base::HostToNet32(value); + value = SpdyHostToNet32(value); return WriteBytes(&value, sizeof(value)); } bool WriteUInt64(uint64_t value) { - uint32_t upper = base::HostToNet32(static_cast<uint32_t>(value >> 32)); - uint32_t lower = base::HostToNet32(static_cast<uint32_t>(value)); + uint32_t upper = SpdyHostToNet32(static_cast<uint32_t>(value >> 32)); + uint32_t lower = SpdyHostToNet32(static_cast<uint32_t>(value)); return (WriteBytes(&upper, sizeof(upper)) && WriteBytes(&lower, sizeof(lower))); }
diff --git a/net/third_party/spdy/core/spdy_frame_reader.cc b/net/third_party/spdy/core/spdy_frame_reader.cc index e591183..c2b70f5 100644 --- a/net/third_party/spdy/core/spdy_frame_reader.cc +++ b/net/third_party/spdy/core/spdy_frame_reader.cc
@@ -4,9 +4,9 @@ #include <limits> -#include "base/sys_byteorder.h" #include "net/third_party/spdy/core/spdy_frame_reader.h" #include "net/third_party/spdy/core/spdy_protocol.h" +#include "net/third_party/spdy/platform/api/spdy_endianness_util.h" namespace spdy { @@ -37,8 +37,7 @@ } // Read into result. - *result = - base::NetToHost16(*(reinterpret_cast<const uint16_t*>(data_ + ofs_))); + *result = SpdyNetToHost16(*(reinterpret_cast<const uint16_t*>(data_ + ofs_))); // Iterate. ofs_ += 2; @@ -54,8 +53,7 @@ } // Read into result. - *result = - base::NetToHost32(*(reinterpret_cast<const uint32_t*>(data_ + ofs_))); + *result = SpdyNetToHost32(*(reinterpret_cast<const uint32_t*>(data_ + ofs_))); // Iterate. ofs_ += 4; @@ -72,9 +70,9 @@ // Read into result. Network byte order is big-endian. uint64_t upper = - base::NetToHost32(*(reinterpret_cast<const uint32_t*>(data_ + ofs_))); + SpdyNetToHost32(*(reinterpret_cast<const uint32_t*>(data_ + ofs_))); uint64_t lower = - base::NetToHost32(*(reinterpret_cast<const uint32_t*>(data_ + ofs_ + 4))); + SpdyNetToHost32(*(reinterpret_cast<const uint32_t*>(data_ + ofs_ + 4))); *result = (upper << 32) + lower; // Iterate. @@ -104,7 +102,7 @@ // Read into result. *result = 0; memcpy(reinterpret_cast<char*>(result) + 1, data_ + ofs_, 3); - *result = base::NetToHost32(*result); + *result = SpdyNetToHost32(*result); // Iterate. ofs_ += 3;
diff --git a/net/third_party/spdy/core/spdy_frame_reader_test.cc b/net/third_party/spdy/core/spdy_frame_reader_test.cc index fd88dc95..f9e0364c 100644 --- a/net/third_party/spdy/core/spdy_frame_reader_test.cc +++ b/net/third_party/spdy/core/spdy_frame_reader_test.cc
@@ -8,8 +8,8 @@ #include <iostream> #include <memory> -#include "base/sys_byteorder.h" #include "net/third_party/spdy/platform/api/spdy_arraysize.h" +#include "net/third_party/spdy/platform/api/spdy_endianness_util.h" #include "testing/platform_test.h" namespace spdy { @@ -17,7 +17,8 @@ TEST(SpdyFrameReaderTest, ReadUInt16) { // Frame data in network byte order. const uint16_t kFrameData[] = { - base::HostToNet16(1), base::HostToNet16(1 << 15), + SpdyHostToNet16(1), + SpdyHostToNet16(1 << 15), }; SpdyFrameReader frame_reader(reinterpret_cast<const char*>(kFrameData), @@ -37,7 +38,8 @@ TEST(SpdyFrameReaderTest, ReadUInt32) { // Frame data in network byte order. const uint32_t kFrameData[] = { - base::HostToNet32(1), base::HostToNet32(0x80000000), + SpdyHostToNet32(1), + SpdyHostToNet32(0x80000000), }; SpdyFrameReader frame_reader(reinterpret_cast<const char*>(kFrameData),
diff --git a/net/third_party/spdy/core/spdy_test_utils.cc b/net/third_party/spdy/core/spdy_test_utils.cc index 0ec70d5..602e664 100644 --- a/net/third_party/spdy/core/spdy_test_utils.cc +++ b/net/third_party/spdy/core/spdy_test_utils.cc
@@ -12,7 +12,7 @@ #include <vector> #include "base/logging.h" -#include "base/sys_byteorder.h" +#include "net/third_party/spdy/platform/api/spdy_endianness_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace spdy { @@ -95,7 +95,7 @@ void SetFrameLength(SpdySerializedFrame* frame, size_t length) { CHECK_GT(1u << 14, length); { - int32_t wire_length = base::HostToNet32(length); + int32_t wire_length = SpdyHostToNet32(length); memcpy(frame->data(), reinterpret_cast<char*>(&wire_length) + 1, 3); } }
diff --git a/net/third_party/spdy/platform/api/spdy_endianness_util.h b/net/third_party/spdy/platform/api/spdy_endianness_util.h new file mode 100644 index 0000000..d14ad8b --- /dev/null +++ b/net/third_party/spdy/platform/api/spdy_endianness_util.h
@@ -0,0 +1,44 @@ +// Copyright (c) 2018 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_THIRD_PARTY_SPDY_PLATFORM_API_SPDY_ENDIANNESS_UTIL_H_ +#define NET_THIRD_PARTY_SPDY_PLATFORM_API_SPDY_ENDIANNESS_UTIL_H_ + +#include <stdint.h> + +#include "net/third_party/spdy/platform/impl/spdy_endianness_util_impl.h" + +namespace spdy { + +// Converts the bytes in |x| from network to host order (endianness), and +// returns the result. +inline uint16_t SpdyNetToHost16(uint16_t x) { + return SpdyNetToHost16Impl(x); +} + +inline uint32_t SpdyNetToHost32(uint32_t x) { + return SpdyNetToHost32Impl(x); +} + +inline uint64_t SpdyNetToHost64(uint64_t x) { + return SpdyNetToHost64Impl(x); +} + +// Converts the bytes in |x| from host to network order (endianness), and +// returns the result. +inline uint16_t SpdyHostToNet16(uint16_t x) { + return SpdyHostToNet16Impl(x); +} + +inline uint32_t SpdyHostToNet32(uint32_t x) { + return SpdyHostToNet32Impl(x); +} + +inline uint64_t SpdyHostToNet64(uint64_t x) { + return SpdyHostToNet64Impl(x); +} + +} // namespace spdy + +#endif // NET_THIRD_PARTY_SPDY_PLATFORM_API_SPDY_ENDIANNESS_UTIL_H_
diff --git a/net/third_party/spdy/platform/impl/spdy_endianness_util_impl.h b/net/third_party/spdy/platform/impl/spdy_endianness_util_impl.h new file mode 100644 index 0000000..315bed3 --- /dev/null +++ b/net/third_party/spdy/platform/impl/spdy_endianness_util_impl.h
@@ -0,0 +1,34 @@ +// Copyright 2018 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_THIRD_PARTY_SPDY_PLATFORM_IMPL_SPDY_ENDIANNESS_UTIL_IMPL_H_ +#define NET_THIRD_PARTY_SPDY_PLATFORM_IMPL_SPDY_ENDIANNESS_UTIL_IMPL_H_ + +#include "base/sys_byteorder.h" + +inline uint16_t SpdyNetToHost16Impl(uint16_t x) { + return base::NetToHost16(x); +} + +inline uint32_t SpdyNetToHost32Impl(uint32_t x) { + return base::NetToHost32(x); +} + +inline uint64_t SpdyNetToHost64Impl(uint64_t x) { + return base::NetToHost64(x); +} + +inline uint16_t SpdyHostToNet16Impl(uint16_t x) { + return base::HostToNet16(x); +} + +inline uint32_t SpdyHostToNet32Impl(uint32_t x) { + return base::HostToNet32(x); +} + +inline uint64_t SpdyHostToNet64Impl(uint64_t x) { + return base::HostToNet64(x); +} + +#endif // NET_THIRD_PARTY_SPDY_PLATFORM_IMPL_SPDY_ENDIANNESS_UTIL_IMPL_H_
diff --git a/ppapi/proxy/ppapi_command_buffer_proxy.cc b/ppapi/proxy/ppapi_command_buffer_proxy.cc index e6160b6..426a496 100644 --- a/ppapi/proxy/ppapi_command_buffer_proxy.cc +++ b/ppapi/proxy/ppapi_command_buffer_proxy.cc
@@ -147,7 +147,8 @@ base::WritableSharedMemoryMapping shared_memory_mapping = shared_memory_region.Map(); - if (!shared_memory_mapping.IsValid()) { + if (!shared_memory_mapping.IsValid() || + (shared_memory_mapping.size() > UINT32_MAX)) { if (last_state_.error == gpu::error::kNoError) last_state_.error = gpu::error::kOutOfBounds; *id = -1;
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn index c394d43f..e263027 100644 --- a/services/network/public/mojom/BUILD.gn +++ b/services/network/public/mojom/BUILD.gn
@@ -48,22 +48,6 @@ ] } -# UDP socket interface relies on mojo.common.mojom.ReadOnlyBuffer, which is -# mapped to base::span<const uint8_t>. ReadOnlyBufffer doesn't yet work with -# lazy serialization, so this needs to be in a separate target that doesn't have -# support_lazy_serialization = true. -mojom("udp_socket_interface") { - sources = [ - "udp_socket.mojom", - ] - - public_deps = [ - ":mojom_ip_address", - ":mutable_network_traffic_annotation_interface", - "//mojo/public/mojom/base:read_only_buffer", - ] -} - # This target is split from "mojom" target as the lazy serialization may # cause problems. See https://crbug.com/822732. mojom("websocket_mojom") { @@ -85,11 +69,6 @@ } mojom("mojom") { - # URLLoader & URLLoaderFactory are used in-process in the browser when - # navigation uses URLLoader (NavigationMojoResponse) and in the renderer - # when Service Worker uses direct communication (S13nServiceWorker). - support_lazy_serialization = true - sources = [ "cookie_manager.mojom", "cors.mojom", @@ -118,6 +97,7 @@ "ssl_config.mojom", "tcp_socket.mojom", "tls_socket.mojom", + "udp_socket.mojom", "url_loader.mojom", "url_loader_factory.mojom", ] @@ -126,7 +106,6 @@ ":data_pipe_interfaces", ":mojom_ip_address", ":mutable_network_traffic_annotation_interface", - ":udp_socket_interface", ":websocket_mojom", "//components/content_settings/core/common:mojo_bindings", "//mojo/public/mojom/base",
diff --git a/skia/BUILD.gn b/skia/BUILD.gn index b2b0d5b..300036d 100644 --- a/skia/BUILD.gn +++ b/skia/BUILD.gn
@@ -243,6 +243,8 @@ ] } + defines = [ "SKCMS_LEGACY_TF_INVERT" ] + # LLVM automatically sets the equivalent of GCC's -mfp16-format=ieee on ARM # builds by default, while GCC itself does not. We need it to enable support # for half-precision floating point data types used by SKCMS on ARM.
diff --git a/skia/ext/benchmarking_canvas.cc b/skia/ext/benchmarking_canvas.cc index e6f0cc0e..6d514fbb 100644 --- a/skia/ext/benchmarking_canvas.cc +++ b/skia/ext/benchmarking_canvas.cc
@@ -248,11 +248,7 @@ std::unique_ptr<base::Value> SaveLayerFlagsAsValue( SkCanvas::SaveLayerFlags flags) { - FlagsBuilder builder('|'); - builder.addFlag(flags & SkCanvas::kPreserveLCDText_SaveLayerFlag, - "kPreserveLCDText"); - - std::unique_ptr<base::Value> val(new base::Value(builder.str())); + std::unique_ptr<base::Value> val(new base::Value(static_cast<int>(flags))); return val; }
diff --git a/storage/browser/blob/README.md b/storage/browser/blob/README.md index f74dd19..92b9a110 100644 --- a/storage/browser/blob/README.md +++ b/storage/browser/blob/README.md
@@ -322,7 +322,7 @@ The BlobStatus tracks the construction procedure (specifically the transport process), and the copy memory quota and dependent blob process is encompassed -in `PENDING_INTERNALS`. +in `PENDING_REFERENCED_BLOBS`. Once a blob is finished constructing, the status is set to `DONE` or any of the `ERR_*` values.
diff --git a/storage/browser/blob/blob_entry.h b/storage/browser/blob/blob_entry.h index e159e65..67068de 100644 --- a/storage/browser/blob/blob_entry.h +++ b/storage/browser/blob/blob_entry.h
@@ -52,7 +52,7 @@ // 1. Waiting for quota to be granted for transport data (PENDING_QUOTA) // 2. Waiting for user population of data after quota (PENDING_TRANSPORT) // 3. Waiting for blobs we reference to complete & quota granted for possible - // copies. (PENDING_INTERNALS) + // copies. (PENDING_REFERENCED_BLOBS) struct COMPONENT_EXPORT(STORAGE_BROWSER) BuildingState { // |transport_allowed_callback| is not null when data needs population. See // BlobStorageContext::BuildBlob for when the callback is called. @@ -110,8 +110,8 @@ // Returns if we're a pending blob that can finish building. bool CanFinishBuilding() const { - // PENDING_INTERNALS means transport is finished. - return status_ == BlobStatus::PENDING_INTERNALS && building_state_ && + // PENDING_REFERENCED_BLOBS means transport is finished. + return status_ == BlobStatus::PENDING_REFERENCED_BLOBS && building_state_ && !building_state_->copy_quota_request && building_state_->num_building_dependent_blobs == 0; }
diff --git a/storage/browser/blob/blob_reader.cc b/storage/browser/blob/blob_reader.cc index 88bf5381..ecc8f7a 100644 --- a/storage/browser/blob/blob_reader.cc +++ b/storage/browser/blob/blob_reader.cc
@@ -61,7 +61,7 @@ case BlobStatus::DONE: case BlobStatus::PENDING_QUOTA: case BlobStatus::PENDING_TRANSPORT: - case BlobStatus::PENDING_INTERNALS: + case BlobStatus::PENDING_REFERENCED_BLOBS: case BlobStatus::PENDING_CONSTRUCTION: NOTREACHED(); }
diff --git a/storage/browser/blob/blob_storage_context.cc b/storage/browser/blob/blob_storage_context.cc index c4febb8..b0b98b5 100644 --- a/storage/browser/blob/blob_storage_context.cc +++ b/storage/browser/blob/blob_storage_context.cc
@@ -227,7 +227,7 @@ } else if (content->transport_quota_needed()) { entry->set_status(BlobStatus::PENDING_QUOTA); } else { - entry->set_status(BlobStatus::PENDING_INTERNALS); + entry->set_status(BlobStatus::PENDING_REFERENCED_BLOBS); } std::unique_ptr<BlobDataHandle> handle = CreateHandle(content->uuid_, entry); @@ -459,7 +459,7 @@ DCHECK(shareable_item->state() == ShareableBlobDataItem::QUOTA_GRANTED); shareable_item->set_state(ShareableBlobDataItem::POPULATED_WITH_QUOTA); } - entry->set_status(BlobStatus::PENDING_INTERNALS); + entry->set_status(BlobStatus::PENDING_REFERENCED_BLOBS); if (entry->CanFinishBuilding()) FinishBuilding(entry); }
diff --git a/storage/browser/blob/view_blob_internals_job.cc b/storage/browser/blob/view_blob_internals_job.cc index 00e9e051..dd5c6715 100644 --- a/storage/browser/blob/view_blob_internals_job.cc +++ b/storage/browser/blob/view_blob_internals_job.cc
@@ -95,9 +95,9 @@ case BlobStatus::PENDING_TRANSPORT: return "BlobStatus::PENDING_TRANSPORT: Blob construction is pending on " "data transport from renderer."; - case BlobStatus::PENDING_INTERNALS: - return "BlobStatus::PENDING_INTERNALS: Blob construction is pending on " - "dependency blobs to finish construction."; + case BlobStatus::PENDING_REFERENCED_BLOBS: + return "BlobStatus::PENDING_REFERENCED_BLOBS: Blob construction is " + "pending on dependency blobs to finish construction."; case BlobStatus::PENDING_CONSTRUCTION: return "BlobStatus::PENDING_CONSTRUCTION: Blob construction is pending " "on resolving the UUIDs of refereneced blobs.";
diff --git a/storage/common/blob_storage/blob_storage_constants.h b/storage/common/blob_storage/blob_storage_constants.h index 077b5e4..48c405a 100644 --- a/storage/common/blob_storage/blob_storage_constants.h +++ b/storage/common/blob_storage/blob_storage_constants.h
@@ -140,8 +140,7 @@ PENDING_TRANSPORT = 202, // Waiting for any operations involving dependent blobs after transport data // has been populated. See BlobEntry::BuildingState for more info. - // TODO(dmurph): Change to PENDING_REFERENCED_BLOBS (crbug.com/670398). - PENDING_INTERNALS = 203, + PENDING_REFERENCED_BLOBS = 203, // Waiting for construction to begin. PENDING_CONSTRUCTION = 204, LAST_PENDING = PENDING_CONSTRUCTION,
diff --git a/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter b/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter index c987f6e..bca197b 100644 --- a/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter +++ b/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter
@@ -44,10 +44,6 @@ -TestAsNormalAndGuestUser/SpokenFeedbackTest.OverviewMode/0 -TestAsNormalAndGuestUser/SpokenFeedbackTest.OverviewMode/1 -# TabDragging: https://crbug.com/890071 --TabDragging/DetachToBrowserTabDragControllerTest.* --TabDragging/DetachToBrowserTabDragControllerTestTouch.* - # This test is flaky. https://crbug.com/897879 -ExtensionApiTest.DisplayModeWindowIsInFullscreen
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 6cb2cb26..237b810 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -2100,30 +2100,6 @@ ] } ], - "HTMLParsingYieldTime": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "ios", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled_50ms", - "params": { - "limit": "0.05" - }, - "enable_features": [ - "HTMLParsingYieldTime" - ] - } - ] - } - ], "HTTPReallyBadFinal": [ { "platforms": [
diff --git a/third_party/blink/public/mojom/usb/web_usb_service.mojom b/third_party/blink/public/mojom/usb/web_usb_service.mojom index b92b48a6..05c8e68 100644 --- a/third_party/blink/public/mojom/usb/web_usb_service.mojom +++ b/third_party/blink/public/mojom/usb/web_usb_service.mojom
@@ -5,7 +5,8 @@ module blink.mojom; import "device/usb/public/mojom/device.mojom"; -import "device/usb/public/mojom/device_manager.mojom"; +import "device/usb/public/mojom/device_enumeration_options.mojom"; +import "device/usb/public/mojom/device_manager_client.mojom"; // This is a parallel interface with UsbDeviceManager aimed to handle extra work // such as permission checking, chooser showing, etc. in browser.
diff --git a/third_party/blink/public/platform/modules/bluetooth/web_bluetooth.mojom b/third_party/blink/public/platform/modules/bluetooth/web_bluetooth.mojom index c27750a..ed26bcb 100644 --- a/third_party/blink/public/platform/modules/bluetooth/web_bluetooth.mojom +++ b/third_party/blink/public/platform/modules/bluetooth/web_bluetooth.mojom
@@ -144,10 +144,11 @@ WebBluetoothDevice device; string? name; array<bluetooth.mojom.UUID> uuids; + bool appearance_is_set; uint16 appearance; - - // TODO(dougt): Make tx_power and rssi optional. + bool tx_power_is_set; uint8 tx_power; + bool rssi_is_set; uint8 rssi; map<uint16, array<uint8>> manufacturer_data; map<string, array<uint8>> service_data;
diff --git a/third_party/blink/public/web/web_performance.h b/third_party/blink/public/web/web_performance.h index bb6a7379..3f64ff3 100644 --- a/third_party/blink/public/web/web_performance.h +++ b/third_party/blink/public/web/web_performance.h
@@ -92,9 +92,13 @@ BLINK_EXPORT double FirstMeaningfulPaint() const; BLINK_EXPORT double FirstMeaningfulPaintCandidate() const; BLINK_EXPORT double LargestImagePaint() const; + BLINK_EXPORT uint64_t LargestImagePaintSize() const; BLINK_EXPORT double LastImagePaint() const; + BLINK_EXPORT uint64_t LastImagePaintSize() const; BLINK_EXPORT double LargestTextPaint() const; + BLINK_EXPORT uint64_t LargestTextPaintSize() const; BLINK_EXPORT double LastTextPaint() const; + BLINK_EXPORT uint64_t LastTextPaintSize() const; BLINK_EXPORT double PageInteractive() const; BLINK_EXPORT double PageInteractiveDetection() const; BLINK_EXPORT double FirstInputInvalidatingInteractive() const;
diff --git a/third_party/blink/renderer/core/exported/web_performance.cc b/third_party/blink/renderer/core/exported/web_performance.cc index a5070b5..408008b 100644 --- a/third_party/blink/renderer/core/exported/web_performance.cc +++ b/third_party/blink/renderer/core/exported/web_performance.cc
@@ -175,18 +175,34 @@ return MillisecondsToSeconds(private_->timing()->LargestImagePaint()); } +uint64_t WebPerformance::LargestImagePaintSize() const { + return private_->timing()->LargestImagePaintSize(); +} + double WebPerformance::LastImagePaint() const { return MillisecondsToSeconds(private_->timing()->LastImagePaint()); } +uint64_t WebPerformance::LastImagePaintSize() const { + return private_->timing()->LastImagePaintSize(); +} + double WebPerformance::LargestTextPaint() const { return MillisecondsToSeconds(private_->timing()->LargestTextPaint()); } +uint64_t WebPerformance::LargestTextPaintSize() const { + return private_->timing()->LargestTextPaintSize(); +} + double WebPerformance::LastTextPaint() const { return MillisecondsToSeconds(private_->timing()->LastTextPaint()); } +uint64_t WebPerformance::LastTextPaintSize() const { + return private_->timing()->LastTextPaintSize(); +} + double WebPerformance::PageInteractive() const { return MillisecondsToSeconds(private_->timing()->PageInteractive()); }
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc index 20531ff..3122638 100644 --- a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc +++ b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
@@ -2482,8 +2482,7 @@ LogicalRightOffsetForLine(curr->LineTop(), indent_text); LayoutUnit block_left_edge = LogicalLeftOffsetForLine(curr->LineTop(), indent_text); - LayoutUnit line_box_edge = ltr ? curr->LogicalRightLayoutOverflow() - : curr->LogicalLeftLayoutOverflow(); + LayoutUnit line_box_edge = ltr ? curr->LogicalRight() : curr->LogicalLeft(); if ((ltr && line_box_edge > block_right_edge) || (!ltr && line_box_edge < block_left_edge)) { // This line spills out of our box in the appropriate direction. Now we
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h index eeadf12..472ee44 100644 --- a/third_party/blink/renderer/core/layout/layout_box.h +++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -495,14 +495,6 @@ LayoutSize MaxLayoutOverflow() const { return LayoutSize(LayoutOverflowRect().MaxX(), LayoutOverflowRect().MaxY()); } - LayoutUnit LogicalLeftLayoutOverflow() const { - return StyleRef().IsHorizontalWritingMode() ? LayoutOverflowRect().X() - : LayoutOverflowRect().Y(); - } - LayoutUnit LogicalRightLayoutOverflow() const { - return StyleRef().IsHorizontalWritingMode() ? LayoutOverflowRect().MaxX() - : LayoutOverflowRect().MaxY(); - } LayoutRect VisualOverflowRect() const override; LayoutRect PhysicalVisualOverflowRect() const {
diff --git a/third_party/blink/renderer/core/layout/line/inline_flow_box.cc b/third_party/blink/renderer/core/layout/line/inline_flow_box.cc index e875d4b..db63e72 100644 --- a/third_party/blink/renderer/core/layout/line/inline_flow_box.cc +++ b/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
@@ -1506,11 +1506,6 @@ // If our flow is ltr then iterate over the boxes from left to right, // otherwise iterate from right to left. Varying the order allows us to // correctly hide the boxes following the ellipsis. - LayoutUnit relative_offset = - BoxModelObject().IsInline() && BoxModelObject().IsRelPositioned() - ? BoxModelObject().RelativePositionLogicalOffset().Width() - : LayoutUnit(); - logical_left_offset += relative_offset; InlineBox* box = ltr ? FirstChild() : LastChild(); // NOTE: these will cross after foundBox = true. @@ -1540,7 +1535,7 @@ box = box->PrevOnLine(); } } - return result + relative_offset; + return result; } void InlineFlowBox::ClearTruncation() {
diff --git a/third_party/blink/renderer/core/layout/line/inline_flow_box.h b/third_party/blink/renderer/core/layout/line/inline_flow_box.h index db0237b..996be79d 100644 --- a/third_party/blink/renderer/core/layout/line/inline_flow_box.h +++ b/third_party/blink/renderer/core/layout/line/inline_flow_box.h
@@ -313,22 +313,6 @@ result = result.TransposedRect(); return result; } - LayoutUnit LogicalRightLayoutOverflow() const { - if (LayoutOverflowIsSet()) { - return IsHorizontal() - ? overflow_->layout_overflow->LayoutOverflowRect().MaxX() - : overflow_->layout_overflow->LayoutOverflowRect().MaxY(); - } - return LogicalRight(); - } - LayoutUnit LogicalLeftLayoutOverflow() const { - if (LayoutOverflowIsSet()) { - return IsHorizontal() - ? overflow_->layout_overflow->LayoutOverflowRect().X() - : overflow_->layout_overflow->LayoutOverflowRect().Y(); - } - return LogicalLeft(); - } LayoutRect VisualOverflowRect(LayoutUnit line_top, LayoutUnit line_bottom) const {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc index d11efb4..2be25c90 100644 --- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc +++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
@@ -439,42 +439,6 @@ local_to_border_box_transform_; } -LayoutRect LayoutSVGRoot::LocalVisualRectIgnoringVisibility() const { - // This is an open-coded aggregate of SVGLayoutSupport::localVisualRect - // and LayoutReplaced::localVisualRect. The reason for this is to optimize/ - // minimize the visual rect when the box is not "decorated" (does not have - // background/border/etc., see - // LayoutSVGRootTest.VisualRectMappingWithViewportClipWithoutBorder). - - // Return early for any cases where we don't actually paint. - if (!EnclosingLayer()->HasVisibleContent()) - return LayoutRect(); - - // Compute the visual rect of the content of the SVG in the border-box - // coordinate space. - FloatRect content_visual_rect = VisualRectInLocalSVGCoordinates(); - content_visual_rect = - local_to_border_box_transform_.MapRect(content_visual_rect); - - // Apply initial viewport clip, overflow:visible content is added to - // visualOverflow but the most common case is that overflow is hidden, so - // always intersect. - content_visual_rect.Intersect(PixelSnappedBorderBoxRect()); - - LayoutRect visual_rect = EnclosingLayoutRect(content_visual_rect); - // If the box is decorated or is overflowing, extend it to include the - // border-box and overflow. - if (has_box_decoration_background_ || HasOverflowModel()) { - // The selectionRect can project outside of the overflowRect, so take their - // union for paint invalidation to avoid selection painting glitches. - LayoutRect decorated_visual_rect = - UnionRect(LocalSelectionRect(), VisualOverflowRect()); - visual_rect.Unite(decorated_visual_rect); - } - - return LayoutRect(EnclosingIntRect(visual_rect)); -} - // This method expects local CSS box coordinates. // Callers with local SVG viewport coordinates should first apply the // localToBorderBoxTransform to convert from SVG viewport coordinates to local
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.h b/third_party/blink/renderer/core/layout/svg/layout_svg_root.h index 158d5d8..8c057a1 100644 --- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.h +++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.h
@@ -142,8 +142,6 @@ const LayoutPoint& accumulated_offset, HitTestAction) override; - LayoutRect LocalVisualRectIgnoringVisibility() const override; - void MapLocalToAncestor( const LayoutBoxModelObject* ancestor, TransformState&,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc index 1558bb1..0640e4aa 100644 --- a/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc +++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc
@@ -35,12 +35,12 @@ LayoutRect root_visual_rect = static_cast<const LayoutObject&>(root).LocalVisualRect(); - // SVG root's overflow includes overflow from descendants. - EXPECT_EQ(LayoutRect(0, 0, 220, 190), root_visual_rect); + // SVG root's local overflow does not include overflow from descendants. + EXPECT_EQ(LayoutRect(0, 0, 220, 120), root_visual_rect); rect = root_visual_rect; EXPECT_TRUE(root.MapToVisualRectInAncestorSpace(&root, rect)); - EXPECT_EQ(LayoutRect(0, 0, 220, 190), rect); + EXPECT_EQ(LayoutRect(0, 0, 220, 120), rect); } TEST_F(LayoutSVGRootTest, VisualOverflowExpandsLayer) { @@ -80,8 +80,6 @@ *ToLayoutSVGShape(GetLayoutObjectByElementId("rect")); LayoutRect rect = SVGLayoutSupport::VisualRectInAncestorSpace(svg_rect, root); - // (80, 80, 100, 100) added by root's content rect offset from border rect, - // clipped by (10, 10, 200, 100). EXPECT_EQ(LayoutRect(90, 90, 100, 20), rect); LayoutRect root_visual_rect = @@ -96,34 +94,6 @@ EXPECT_EQ(LayoutRect(0, 0, 220, 120), rect); } -TEST_F(LayoutSVGRootTest, VisualRectMappingWithViewportClipWithoutBorder) { - SetBodyInnerHTML(R"HTML( - <svg id='root' style='width: 200px; height: 100px; overflow: hidden' - viewBox='0 0 200 100'> - <rect id='rect' x='80' y='80' width='100' height='100'/> - </svg> - )HTML"); - - const LayoutSVGRoot& root = - *ToLayoutSVGRoot(GetLayoutObjectByElementId("root")); - const LayoutSVGShape& svg_rect = - *ToLayoutSVGShape(GetLayoutObjectByElementId("rect")); - - LayoutRect rect = SVGLayoutSupport::VisualRectInAncestorSpace(svg_rect, root); - // (80, 80, 100, 100) clipped by (0, 0, 200, 100). - EXPECT_EQ(LayoutRect(80, 80, 100, 20), rect); - - LayoutRect root_visual_rect = - static_cast<const LayoutObject&>(root).LocalVisualRect(); - // SVG root doesn't have box decoration background, so just use clipped - // overflow of children. - EXPECT_EQ(LayoutRect(80, 80, 100, 20), root_visual_rect); - - rect = root_visual_rect; - EXPECT_TRUE(root.MapToVisualRectInAncestorSpace(&root, rect)); - EXPECT_EQ(LayoutRect(80, 80, 100, 20), rect); -} - TEST_F(LayoutSVGRootTest, PaintedOutputOfObjectHasNoEffectRegardlessOfSizeEmpty) { SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc b/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc index a7a7d548..587f6655 100644 --- a/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc +++ b/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc
@@ -152,27 +152,25 @@ // reason of adding ScrollOrigin(). auto contents_paint_offset = snapped_paint_offset + ToLayoutBox(object).ScrollOrigin(); - auto SetContentsLayerState = [&fragment_data, &contents_paint_offset]( - GraphicsLayer* graphics_layer) { + auto SetScrollingContentsLayerState = [&fragment_data, + &contents_paint_offset]( + GraphicsLayer* graphics_layer) { if (graphics_layer) { graphics_layer->SetLayerState( fragment_data.ContentsProperties(), contents_paint_offset + graphics_layer->OffsetFromLayoutObject()); } }; - SetContentsLayerState(mapping->ScrollingContentsLayer()); - SetContentsLayerState(mapping->ForegroundLayer()); + SetScrollingContentsLayerState(mapping->ScrollingContentsLayer()); + SetScrollingContentsLayerState(mapping->ForegroundLayer()); } else { SetContainerLayerState(mapping->ForegroundLayer()); } auto* main_graphics_layer = mapping->MainGraphicsLayer(); - if (const auto* contents_layer = main_graphics_layer->ContentsLayer()) { - auto position = contents_layer->position(); - main_graphics_layer->SetContentsLayerState( - fragment_data.ContentsProperties(), - snapped_paint_offset + main_graphics_layer->OffsetFromLayoutObject() + - IntSize(position.x(), position.y())); + if (main_graphics_layer->ContentsLayer()) { + main_graphics_layer->SetContentsPropertyTreeState( + fragment_data.ContentsProperties()); } if (auto* squashing_layer = mapping->SquashingLayer()) {
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc index 46084492..0014ce6a 100644 --- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc +++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
@@ -131,27 +131,28 @@ } void ImagePaintTimingDetector::OnLargestImagePaintDetected( - const ImageRecord& largest_image_record) { - largest_image_paint_ = largest_image_record.first_paint_time_after_loaded; + ImageRecord* largest_image_record) { + DCHECK(largest_image_record); + largest_image_paint_ = largest_image_record; std::unique_ptr<TracedValue> value = TracedValue::Create(); - PopulateTraceValue(*value, largest_image_record, + PopulateTraceValue(*value, *largest_image_record, ++largest_image_candidate_index_max_); TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( "loading", "LargestImagePaint::Candidate", TRACE_EVENT_SCOPE_THREAD, - largest_image_record.first_paint_time_after_loaded, "data", + largest_image_record->first_paint_time_after_loaded, "data", std::move(value)); - frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming(); } void ImagePaintTimingDetector::OnLastImagePaintDetected( - const ImageRecord& last_image_record) { - last_image_paint_ = last_image_record.first_paint_time_after_loaded; + ImageRecord* last_image_record) { + DCHECK(last_image_record); + last_image_paint_ = last_image_record; std::unique_ptr<TracedValue> value = TracedValue::Create(); - PopulateTraceValue(*value, last_image_record, + PopulateTraceValue(*value, *last_image_record, ++last_image_candidate_index_max_); TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( "loading", "LastImagePaint::Candidate", TRACE_EVENT_SCOPE_THREAD, - last_image_record.first_paint_time_after_loaded, "data", + last_image_record->first_paint_time_after_loaded, "data", std::move(value)); frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming(); } @@ -165,19 +166,14 @@ // result unless it's a new candidate. ImageRecord* largest_image_record = FindLargestPaintCandidate(); bool new_candidate_detected = false; - if (largest_image_record && - !largest_image_record->first_paint_time_after_loaded.is_null() && - largest_image_record->first_paint_time_after_loaded != - largest_image_paint_) { + if (largest_image_record && largest_image_record != largest_image_paint_) { new_candidate_detected = true; - OnLargestImagePaintDetected(*largest_image_record); + OnLargestImagePaintDetected(largest_image_record); } ImageRecord* last_image_record = FindLastPaintCandidate(); - if (last_image_record && - !last_image_record->first_paint_time_after_loaded.is_null() && - last_image_record->first_paint_time_after_loaded != last_image_paint_) { + if (last_image_record && last_image_record != last_image_paint_) { new_candidate_detected = true; - OnLastImagePaintDetected(*last_image_record); + OnLastImagePaintDetected(last_image_record); } if (new_candidate_detected) { frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming(); @@ -214,17 +210,13 @@ detached_ids_.insert(node_id); if (id_record_map_.size() - detached_ids_.size() == 0) { - const bool largest_image_paint_invalidated = - largest_image_paint_ != base::TimeTicks(); - const bool last_image_paint_invalidated = - last_image_paint_ != base::TimeTicks(); - if (largest_image_paint_invalidated) - largest_image_paint_ = base::TimeTicks(); - if (last_image_paint_invalidated) - last_image_paint_ = base::TimeTicks(); - if (largest_image_paint_invalidated || last_image_paint_invalidated) { - frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming(); - } + // If either largest_image_paint_ or last_image_paint_ will change to + // nullptr, update performance timing. + if (!largest_image_paint_ && !last_image_paint_) + return; + largest_image_paint_ = nullptr; + last_image_paint_ = nullptr; + frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming(); } } }
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h index 7a952c7..ce36a84 100644 --- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h +++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
@@ -67,8 +67,22 @@ static bool HasContentfulBackgroundImage(const LayoutObject& object); void OnPrePaintFinished(); void NotifyNodeRemoved(DOMNodeId); - base::TimeTicks LargestImagePaint() const { return largest_image_paint_; } - base::TimeTicks LastImagePaint() const { return last_image_paint_; } + base::TimeTicks LargestImagePaint() const { + return !largest_image_paint_ + ? base::TimeTicks() + : largest_image_paint_->first_paint_time_after_loaded; + } + uint64_t LargestImagePaintSize() const { + return !largest_image_paint_ ? 0 : largest_image_paint_->first_size; + } + base::TimeTicks LastImagePaint() const { + return !last_image_paint_ + ? base::TimeTicks() + : last_image_paint_->first_paint_time_after_loaded; + } + uint64_t LastImagePaintSize() const { + return !last_image_paint_ ? 0 : last_image_paint_->first_size; + } // After the method being called, the detector stops to record new entries and // node removal. But it still observe the loading status. In other words, if // an image is recorded before stopping recording, and finish loading after @@ -93,14 +107,18 @@ WebLayerTreeView::SwapResult, base::TimeTicks); void RegisterNotifySwapTime(); - void OnLargestImagePaintDetected(const ImageRecord&); - void OnLastImagePaintDetected(const ImageRecord&); + void OnLargestImagePaintDetected(ImageRecord*); + void OnLastImagePaintDetected(ImageRecord*); + void Deactivate(); + void Analyze(); base::RepeatingCallback<void(WebLayerTreeView::ReportTimeCallback)> notify_swap_time_override_for_testing_; HashSet<DOMNodeId> size_zero_ids_; + // We will never destroy the pointers within |id_record_map_|. Once created + // they will exist for the whole life cycle of |id_record_map_|. HashMap<DOMNodeId, std::unique_ptr<ImageRecord>> id_record_map_; std::set<base::WeakPtr<ImageRecord>, bool (*)(const base::WeakPtr<ImageRecord>&, @@ -131,8 +149,8 @@ // no effect on recording the loading status. bool is_recording_ = true; - base::TimeTicks largest_image_paint_; - base::TimeTicks last_image_paint_; + ImageRecord* largest_image_paint_ = nullptr; + ImageRecord* last_image_paint_ = nullptr; Member<LocalFrameView> frame_view_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc index f392bb0..11a94af 100644 --- a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc +++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
@@ -71,15 +71,17 @@ } TimeTicks LargestPaintStoredResult() { - return GetPaintTimingDetector() - .GetImagePaintTimingDetector() - .largest_image_paint_; + ImageRecord* record = GetPaintTimingDetector() + .GetImagePaintTimingDetector() + .largest_image_paint_; + return !record ? base::TimeTicks() : record->first_paint_time_after_loaded; } TimeTicks LastPaintStoredResult() { - return GetPaintTimingDetector() - .GetImagePaintTimingDetector() - .last_image_paint_; + ImageRecord* record = GetPaintTimingDetector() + .GetImagePaintTimingDetector() + .last_image_paint_; + return !record ? base::TimeTicks() : record->first_paint_time_after_loaded; } void UpdateAllLifecyclePhasesAndInvokeCallbackIfAny() {
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc index cb56ce4..7d67986 100644 --- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc +++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
@@ -56,6 +56,7 @@ void TextPaintTimingDetector::OnLargestTextDetected( const TextRecord& largest_text_record) { largest_text_paint_ = largest_text_record.first_paint_time; + largest_text_paint_size_ = largest_text_record.first_size; std::unique_ptr<TracedValue> value = TracedValue::Create(); PopulateTraceValue(*value, largest_text_record, @@ -68,6 +69,7 @@ void TextPaintTimingDetector::OnLastTextDetected( const TextRecord& last_text_record) { last_text_paint_ = last_text_record.first_paint_time; + last_text_paint_size_ = last_text_record.first_size; std::unique_ptr<TracedValue> value = TracedValue::Create(); PopulateTraceValue(*value, last_text_record,
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h index 673ffc1d..72b527e 100644 --- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h +++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
@@ -62,7 +62,9 @@ void NotifyNodeRemoved(DOMNodeId); void Dispose() { timer_.Stop(); } base::TimeTicks LargestTextPaint() const { return largest_text_paint_; } + uint64_t LargestTextPaintSize() const { return largest_text_paint_size_; } base::TimeTicks LastTextPaint() const { return last_text_paint_; } + uint64_t LastTextPaintSize() const { return last_text_paint_size_; } void StopRecordEntries(); bool IsRecording() const { return is_recording_; } void Trace(blink::Visitor*); @@ -101,7 +103,9 @@ bool is_recording_ = true; base::TimeTicks largest_text_paint_; + uint64_t largest_text_paint_size_ = 0; base::TimeTicks last_text_paint_; + uint64_t last_text_paint_size_ = 0; TaskRunnerTimer<TextPaintTimingDetector> timer_; Member<LocalFrameView> frame_view_; };
diff --git a/third_party/blink/renderer/core/timing/performance_timing.cc b/third_party/blink/renderer/core/timing/performance_timing.cc index 6fc8c9a..8d6c90bc 100644 --- a/third_party/blink/renderer/core/timing/performance_timing.cc +++ b/third_party/blink/renderer/core/timing/performance_timing.cc
@@ -368,6 +368,15 @@ paint_timing_detector->GetImagePaintTimingDetector().LargestImagePaint()); } +uint64_t PerformanceTiming::LargestImagePaintSize() const { + PaintTimingDetector* paint_timing_detector = GetPaintTimingDetector(); + if (!paint_timing_detector) + return 0; + + return paint_timing_detector->GetImagePaintTimingDetector() + .LargestImagePaintSize(); +} + unsigned long long PerformanceTiming::LastImagePaint() const { PaintTimingDetector* paint_timing_detector = GetPaintTimingDetector(); if (!paint_timing_detector) @@ -377,6 +386,15 @@ paint_timing_detector->GetImagePaintTimingDetector().LastImagePaint()); } +uint64_t PerformanceTiming::LastImagePaintSize() const { + PaintTimingDetector* paint_timing_detector = GetPaintTimingDetector(); + if (!paint_timing_detector) + return 0; + + return paint_timing_detector->GetImagePaintTimingDetector() + .LastImagePaintSize(); +} + unsigned long long PerformanceTiming::LargestTextPaint() const { PaintTimingDetector* paint_timing_detector = GetPaintTimingDetector(); if (!paint_timing_detector) @@ -386,6 +404,15 @@ paint_timing_detector->GetTextPaintTimingDetector().LargestTextPaint()); } +uint64_t PerformanceTiming::LargestTextPaintSize() const { + PaintTimingDetector* paint_timing_detector = GetPaintTimingDetector(); + if (!paint_timing_detector) + return 0; + + return paint_timing_detector->GetTextPaintTimingDetector() + .LargestTextPaintSize(); +} + unsigned long long PerformanceTiming::LastTextPaint() const { PaintTimingDetector* paint_timing_detector = GetPaintTimingDetector(); if (!paint_timing_detector) @@ -395,6 +422,15 @@ paint_timing_detector->GetTextPaintTimingDetector().LastTextPaint()); } +uint64_t PerformanceTiming::LastTextPaintSize() const { + PaintTimingDetector* paint_timing_detector = GetPaintTimingDetector(); + if (!paint_timing_detector) + return 0; + + return paint_timing_detector->GetTextPaintTimingDetector() + .LastTextPaintSize(); +} + unsigned long long PerformanceTiming::PageInteractive() const { InteractiveDetector* interactive_detector = GetInteractiveDetector(); if (!interactive_detector)
diff --git a/third_party/blink/renderer/core/timing/performance_timing.h b/third_party/blink/renderer/core/timing/performance_timing.h index 24cb3bf..45d94048b 100644 --- a/third_party/blink/renderer/core/timing/performance_timing.h +++ b/third_party/blink/renderer/core/timing/performance_timing.h
@@ -109,16 +109,27 @@ // TODO(crbug.com/848639): This function is exposed as an experiment, and if // not useful, this function can be removed. unsigned long long FirstMeaningfulPaintCandidate() const; - // The time of the first paint after the largest image within viewport being - // fully loaded. + // Largest Image Paint is the first paint after the largest image within + // viewport being fully loaded. LargestImagePaint and LargestImagePaintSize + // are the time and size of it. unsigned long long LargestImagePaint() const; - // The time of the first paint after the last image within viewport being - // fully loaded. + uint64_t LargestImagePaintSize() const; + // Last Image Paint is the first paint after the last image within viewport + // being fully loaded. LastImagePaint and LastImagePaintSize are the time and + // size of it. unsigned long long LastImagePaint() const; + uint64_t LastImagePaintSize() const; // The time of the first paint of the largest text within viewport. + // Largest Text Paint is the first paint after the largest text within + // viewport being painted. LargestTextPaint and LargestTextPaintSize + // are the time and size of it. unsigned long long LargestTextPaint() const; - // The time of the first paint of the last text within viewport. + uint64_t LargestTextPaintSize() const; + // Last Text Paint is the first paint after the last text within viewport + // being painted. LastTextPaint and LastTextPaintSize are the time and + // size of it. unsigned long long LastTextPaint() const; + uint64_t LastTextPaintSize() const; // The first time the page is considered 'interactive'. This is determined // using heuristics based on main thread and network activity. unsigned long long PageInteractive() const;
diff --git a/third_party/blink/renderer/devtools/front_end/Tests.js b/third_party/blink/renderer/devtools/front_end/Tests.js index cb80f64a..6fa4ef30 100644 --- a/third_party/blink/renderer/devtools/front_end/Tests.js +++ b/third_party/blink/renderer/devtools/front_end/Tests.js
@@ -1287,6 +1287,14 @@ }); await testCase(baseURL + 'echoheader?Cookie', undefined, 200, ['cache-control'], 'devtools-test-cookie=Bar'); + await SDK.targetManager.mainTarget().runtimeAgent().invoke_evaluate({ + expression: `fetch("/set-cookie?devtools-test-cookie=same-site-cookie;SameSite=Lax", + {credentials: 'include'})`, + awaitPromise: true + }); + await testCase( + baseURL + 'echoheader?Cookie', undefined, 200, ['cache-control'], 'devtools-test-cookie=same-site-cookie'); + this.releaseControl(); };
diff --git a/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js b/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js index 49f82b9..39e191e 100644 --- a/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js +++ b/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js
@@ -89,6 +89,7 @@ this._showHTMLCommentsSetting = Common.moduleSetting('showHTMLComments'); this._showHTMLCommentsSetting.addChangeListener(this._onShowHTMLCommentsChange.bind(this)); + this.useLightSelectionColor(); } /**
diff --git a/third_party/blink/renderer/devtools/front_end/emulation/GeolocationsSettingsTab.js b/third_party/blink/renderer/devtools/front_end/emulation/GeolocationsSettingsTab.js index 4b114db..5cae4d2 100644 --- a/third_party/blink/renderer/devtools/front_end/emulation/GeolocationsSettingsTab.js +++ b/third_party/blink/renderer/devtools/front_end/emulation/GeolocationsSettingsTab.js
@@ -146,7 +146,6 @@ cell = fields.createChild('div', 'geolocations-list-text'); cell.appendChild(editor.createInput('long', 'text', '', longValidator)); - fields.createChild('div', 'geolocations-list-separator geolocations-list-separator-invisible'); return editor;
diff --git a/third_party/blink/renderer/devtools/front_end/sources/DebuggerPlugin.js b/third_party/blink/renderer/devtools/front_end/sources/DebuggerPlugin.js index d26e632..652c7da1 100644 --- a/third_party/blink/renderer/devtools/front_end/sources/DebuggerPlugin.js +++ b/third_party/blink/renderer/devtools/front_end/sources/DebuggerPlugin.js
@@ -151,21 +151,6 @@ return uiSourceCode.contentType().hasScripts(); } - /** - * @override - * @return {!Array<!UI.ToolbarItem>} - */ - rightToolbarItems() { - const originURL = Bindings.CompilerScriptMapping.uiSourceCodeOrigin(this._uiSourceCode); - if (originURL) { - const parsedURL = originURL.asParsedURL(); - if (parsedURL) - return [new UI.ToolbarText(Common.UIString('(source mapped from %s)', parsedURL.displayName))]; - } - - return []; - } - _showBlackboxInfobarIfNeeded() { const uiSourceCode = this._uiSourceCode; if (!uiSourceCode.contentType().hasScripts())
diff --git a/third_party/blink/renderer/devtools/front_end/sources/ScriptOriginPlugin.js b/third_party/blink/renderer/devtools/front_end/sources/ScriptOriginPlugin.js index 238f2483..706d4f2 100644 --- a/third_party/blink/renderer/devtools/front_end/sources/ScriptOriginPlugin.js +++ b/third_party/blink/renderer/devtools/front_end/sources/ScriptOriginPlugin.js
@@ -19,7 +19,7 @@ * @return {boolean} */ static accepts(uiSourceCode) { - return !!Sources.ScriptOriginPlugin._script(uiSourceCode); + return uiSourceCode.contentType().hasScripts() || !!Sources.ScriptOriginPlugin._script(uiSourceCode); } /** @@ -27,6 +27,13 @@ * @return {!Array<!UI.ToolbarItem>} */ rightToolbarItems() { + const originURL = Bindings.CompilerScriptMapping.uiSourceCodeOrigin(this._uiSourceCode); + if (originURL) { + const item = UI.formatLocalized('(source mapped from %s)', [Components.Linkifier.linkifyURL(originURL)]); + return [new UI.ToolbarItem(item)]; + } + + // Handle anonymous scripts with an originStackTrace. const script = Sources.ScriptOriginPlugin._script(this._uiSourceCode); if (!script || !script.originStackTrace) return [];
diff --git a/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css b/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css index dc4baa36..35ecfe80 100644 --- a/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css +++ b/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css
@@ -157,14 +157,6 @@ color: rgb(17, 85, 204); } -button, -input, -select { - font-family: inherit; - font-size: inherit; - color: inherit; -} - input { background-color: white; }
diff --git a/third_party/blink/renderer/devtools/front_end/ui/inspectorStyle.css b/third_party/blink/renderer/devtools/front_end/ui/inspectorStyle.css index 2f10487..c61b1b1 100644 --- a/third_party/blink/renderer/devtools/front_end/ui/inspectorStyle.css +++ b/third_party/blink/renderer/devtools/front_end/ui/inspectorStyle.css
@@ -33,7 +33,9 @@ } :root { - --accent-color: #42a5f5; + --accent-color: #1a73e8; + --accent-fg-color: #1a73e8; + --accent-color-hover: #3b86e8; --focus-bg-color: hsl(214, 40%, 92%); --toolbar-bg-color: #f3f3f3; --toolbar-hover-bg-color: #eaeaea; @@ -51,7 +53,9 @@ } .-theme-with-dark-background { - --accent-color: #2f84da; + --accent-color: #0e639c; + --accent-fg-color: #cccccc; + --accent-color-hover: rgb(17, 119, 187); --focus-bg-color: hsl(214, 19%, 27%); --toolbar-bg-color: #333333; --toolbar-hover-bg-color: #202020;
diff --git a/third_party/blink/renderer/devtools/front_end/ui/textButton.css b/third_party/blink/renderer/devtools/front_end/ui/textButton.css index 001189a1..1a1ff1b 100644 --- a/third_party/blink/renderer/devtools/front_end/ui/textButton.css +++ b/third_party/blink/renderer/devtools/front_end/ui/textButton.css
@@ -9,10 +9,10 @@ height: 24px; font-size: 12px; border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 2px; + border-radius: 4px; padding: 0px 12px; - font-weight: 500; - color: #333; + font-weight: 600; + color: var(--accent-fg-color); background-color: #fff; flex: none; white-space: nowrap; @@ -39,18 +39,15 @@ } :host(.primary-button), -theme-preserve { - background-color: #4285F4; + background-color: var(--accent-color); border: none; color: #fff; } :host(.primary-button:not(:disabled):focus), -:host(.primary-button:not(:disabled):hover), -theme-preserve { - background-color: #3B78E7; -} - +:host(.primary-button:not(:disabled):hover), :host(.primary-button:not(:disabled):active), -theme-preserve { - background-color: #3367D6; + background-color: var(--accent-color-hover); } :host-context(.-theme-with-dark-background):host(:not(.primary-button):not(:disabled):focus),
diff --git a/third_party/blink/renderer/devtools/front_end/ui/toolbar.css b/third_party/blink/renderer/devtools/front_end/ui/toolbar.css index 4a9b6e2..fb5e0db 100644 --- a/third_party/blink/renderer/devtools/front_end/ui/toolbar.css +++ b/third_party/blink/renderer/devtools/front_end/ui/toolbar.css
@@ -45,6 +45,11 @@ padding: 0; height: 26px; border: none; + white-space: pre; +} + +.toolbar-item, +.toolbar-item .devtools-link { color: #5a5a5a; }
diff --git a/third_party/blink/renderer/devtools/front_end/ui/treeoutline.js b/third_party/blink/renderer/devtools/front_end/ui/treeoutline.js index 4867ab16..2a722ffd 100644 --- a/third_party/blink/renderer/devtools/front_end/ui/treeoutline.js +++ b/third_party/blink/renderer/devtools/front_end/ui/treeoutline.js
@@ -177,6 +177,10 @@ this.contentElement.focus(); } + useLightSelectionColor() { + this._useLightSelectionColor = true; + } + /** * @param {!UI.TreeElement} element */ @@ -1092,11 +1096,15 @@ } _onFocus() { + if (this.treeOutline._useLightSelectionColor) + return; if (!this.treeOutline.contentElement.classList.contains('hide-selection-when-blurred')) this._listItemNode.classList.add('force-white-icons'); } _onBlur() { + if (this.treeOutline._useLightSelectionColor) + return; if (!this.treeOutline.contentElement.classList.contains('hide-selection-when-blurred')) this._listItemNode.classList.remove('force-white-icons'); }
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth.cc b/third_party/blink/renderer/modules/bluetooth/bluetooth.cc index 821d760..c4951da 100644 --- a/third_party/blink/renderer/modules/bluetooth/bluetooth.cc +++ b/third_party/blink/renderer/modules/bluetooth/bluetooth.cc
@@ -320,10 +320,21 @@ auto* service_data = MakeGarbageCollected<BluetoothServiceDataMap>(result->service_data); + base::Optional<int8_t> rssi; + if (result->rssi_is_set) + rssi = result->rssi; + + base::Optional<int8_t> tx_power; + if (result->tx_power_is_set) + tx_power = result->tx_power; + + base::Optional<int16_t> appearance; + if (result->appearance_is_set) + appearance = result->appearance; + auto* event = BluetoothAdvertisingEvent::Create( event_type_names::kAdvertisementreceived, bluetooth_device, result->name, - uuids, result->appearance, result->tx_power, result->rssi, - manufacturer_data, service_data); + uuids, appearance, tx_power, rssi, manufacturer_data, service_data); DispatchEvent(*event); }
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_advertising_event.cc b/third_party/blink/renderer/modules/bluetooth/bluetooth_advertising_event.cc index ec42a95..e9c75dc 100644 --- a/third_party/blink/renderer/modules/bluetooth/bluetooth_advertising_event.cc +++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_advertising_event.cc
@@ -31,9 +31,9 @@ BluetoothDevice* device, const String& name, const HeapVector<StringOrUnsignedLong>& uuids, - short appearance, - int8_t txPower, - int8_t rssi, + base::Optional<short> appearance, + base::Optional<int8_t> txPower, + base::Optional<int8_t> rssi, BluetoothManufacturerDataMap* manufacturerData, BluetoothServiceDataMap* serviceData) : Event(event_type, Bubbles::kYes, Cancelable::kYes),
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_advertising_event.h b/third_party/blink/renderer/modules/bluetooth/bluetooth_advertising_event.h index cc1e0cf..6b904c6 100644 --- a/third_party/blink/renderer/modules/bluetooth/bluetooth_advertising_event.h +++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_advertising_event.h
@@ -31,9 +31,9 @@ BluetoothDevice* device, const String& name, const HeapVector<StringOrUnsignedLong>& uuids, - short appearance, - int8_t txPower, - int8_t rssi, + base::Optional<short> appearance, + base::Optional<int8_t> txPower, + base::Optional<int8_t> rssi, BluetoothManufacturerDataMap* manufacturer_data_map, BluetoothServiceDataMap* service_data_map) { return MakeGarbageCollected<BluetoothAdvertisingEvent>( @@ -48,9 +48,9 @@ BluetoothDevice* device, const String& name, const HeapVector<StringOrUnsignedLong>& uuids, - short appearance, - int8_t txPower, - int8_t rssi, + base::Optional<short> appearance, + base::Optional<int8_t> txPower, + base::Optional<int8_t> rssi, BluetoothManufacturerDataMap* manufacturer_data_map, BluetoothServiceDataMap* service_data_map);
diff --git a/third_party/blink/renderer/modules/serial/serial.cc b/third_party/blink/renderer/modules/serial/serial.cc index 5c79c90..ff015fd 100644 --- a/third_party/blink/renderer/modules/serial/serial.cc +++ b/third_party/blink/renderer/modules/serial/serial.cc
@@ -11,6 +11,7 @@ #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" #include "third_party/blink/renderer/core/dom/dom_exception.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/modules/event_target_modules_names.h" #include "third_party/blink/renderer/modules/serial/serial_port.h" @@ -64,12 +65,21 @@ ScriptPromise Serial::requestPort(ScriptState* script_state, const SerialPortRequestOptions* options) { - if (!GetExecutionContext()) { + auto* frame = GetFrame(); + if (!frame) { return ScriptPromise::RejectWithDOMException( script_state, DOMException::Create(DOMExceptionCode::kNotSupportedError)); } + if (!LocalFrame::HasTransientUserActivation(frame)) { + return ScriptPromise::RejectWithDOMException( + script_state, + DOMException::Create( + DOMExceptionCode::kSecurityError, + "Must be handling a user gesture to show a permission request.")); + } + auto* resolver = ScriptPromiseResolver::Create(script_state); request_port_promises_.insert(resolver);
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc index 1e4fb53..c4f9f27 100644 --- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc +++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -433,8 +433,14 @@ if (!contents_layer) return; - contents_layer->SetPosition( - FloatPoint(contents_rect_.X(), contents_rect_.Y())); + if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) { + const auto& offset = GetContentsOffsetFromTransformNode(); + contents_layer->SetOffsetToTransformParent( + gfx::Vector2dF(offset.X(), offset.Y())); + } else { + contents_layer->SetPosition( + FloatPoint(contents_rect_.X(), contents_rect_.Y())); + } if (!image_layer_) { contents_layer->SetBounds(static_cast<gfx::Size>(contents_rect_.Size())); } else { @@ -1032,29 +1038,24 @@ CcLayer()->SetOffsetToTransformParent( gfx::Vector2dF(layer_offset.X(), layer_offset.Y())); - if (!contents_layer_state_ && ContentsLayer()) { + if (ContentsLayer()) { + const auto& offset = GetContentsOffsetFromTransformNode(); ContentsLayer()->SetOffsetToTransformParent( - gfx::Vector2dF(layer_offset.X(), layer_offset.Y())); + gfx::Vector2dF(offset.X(), offset.Y())); } } } -void GraphicsLayer::SetContentsLayerState(const PropertyTreeState& layer_state, - const IntPoint& layer_offset) { +void GraphicsLayer::SetContentsPropertyTreeState( + const PropertyTreeState& layer_state) { DCHECK(layer_state.Transform() && layer_state.Clip() && layer_state.Effect()); DCHECK(ContentsLayer()); - if (contents_layer_state_) { - contents_layer_state_->state = layer_state; - contents_layer_state_->offset = layer_offset; + if (contents_property_tree_state_) { + *contents_property_tree_state_ = layer_state; } else { - contents_layer_state_ = - std::make_unique<LayerState>(LayerState{layer_state, layer_offset}); - } - - if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) { - ContentsLayer()->SetOffsetToTransformParent( - gfx::Vector2dF(layer_offset.X(), layer_offset.Y())); + contents_property_tree_state_ = + std::make_unique<PropertyTreeState>(layer_state); } }
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.h b/third_party/blink/renderer/platform/graphics/graphics_layer.h index f158dc9ca..f0b10230 100644 --- a/third_party/blink/renderer/platform/graphics/graphics_layer.h +++ b/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -289,15 +289,16 @@ } IntPoint GetOffsetFromTransformNode() const { return layer_state_->offset; } - void SetContentsLayerState(const PropertyTreeState&, - const IntPoint& layer_offset); + void SetContentsPropertyTreeState(const PropertyTreeState&); const PropertyTreeState& GetContentsPropertyTreeState() const { - return contents_layer_state_ ? contents_layer_state_->state - : GetPropertyTreeState(); + return contents_property_tree_state_ ? *contents_property_tree_state_ + : GetPropertyTreeState(); } IntPoint GetContentsOffsetFromTransformNode() const { - return contents_layer_state_ ? contents_layer_state_->offset - : GetOffsetFromTransformNode(); + auto offset = contents_rect_.Location(); + if (layer_state_) + offset = offset + GetOffsetFromTransformNode(); + return offset; } // Capture the last painted result into a PaintRecord. This GraphicsLayer @@ -422,7 +423,7 @@ IntPoint offset; }; std::unique_ptr<LayerState> layer_state_; - std::unique_ptr<LayerState> contents_layer_state_; + std::unique_ptr<PropertyTreeState> contents_property_tree_state_; std::unique_ptr<RasterInvalidator> raster_invalidator_;
diff --git a/third_party/blink/renderer/platform/graphics/logging_canvas.cc b/third_party/blink/renderer/platform/graphics/logging_canvas.cc index ea989af..126779d5 100644 --- a/third_party/blink/renderer/platform/graphics/logging_canvas.cc +++ b/third_party/blink/renderer/platform/graphics/logging_canvas.cc
@@ -41,7 +41,6 @@ #include "third_party/blink/renderer/platform/image-encoders/image_encoder.h" #include "third_party/blink/renderer/platform/wtf/hex_number.h" #include "third_party/blink/renderer/platform/wtf/text/base64.h" -#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkPaint.h" @@ -416,22 +415,6 @@ }; } -String TextEncodingName(SkTextEncoding encoding) { - switch (encoding) { - case kUTF8_SkTextEncoding: - return "UTF-8"; - case kUTF16_SkTextEncoding: - return "UTF-16"; - case kUTF32_SkTextEncoding: - return "UTF-32"; - case kGlyphID_SkTextEncoding: - return "GlyphID"; - default: - NOTREACHED(); - return "?"; - }; -} - String HintingName(SkFontHinting hinting) { switch (hinting) { case SkFontHinting::kNone: @@ -464,8 +447,6 @@ paint_item->SetString("strokeCap", StrokeCapName(paint.getStrokeCap())); paint_item->SetString("strokeJoin", StrokeJoinName(paint.getStrokeJoin())); paint_item->SetString("styleName", StyleName(paint.getStyle())); - paint_item->SetString("textEncoding", - TextEncodingName(paint.getTextEncoding())); paint_item->SetString("hinting", HintingName(paint.getHinting())); if (paint.getBlendMode() != SkBlendMode::kSrcOver) paint_item->SetString("blendMode", SkBlendMode_Name(paint.getBlendMode())); @@ -485,13 +466,6 @@ }; } -String SaveLayerFlagsToString(SkCanvas::SaveLayerFlags flags) { - String flags_string = ""; - if (flags & SkCanvas::kPreserveLCDText_SaveLayerFlag) - flags_string.append("kPreserveLCDText_SaveLayerFlag "); - return flags_string; -} - } // namespace class AutoLogger @@ -777,7 +751,7 @@ params->SetObject("bounds", ObjectForSkRect(*rec.fBounds)); if (rec.fPaint) params->SetObject("paint", ObjectForSkPaint(*rec.fPaint)); - params->SetString("saveFlags", SaveLayerFlagsToString(rec.fSaveLayerFlags)); + params->SetInteger("saveFlags", static_cast<int>(rec.fSaveLayerFlags)); return this->SkCanvas::getSaveLayerStrategy(rec); }
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint index f98c60ca..49fd068 100644 --- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint +++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -200,7 +200,6 @@ Bug(none) inspector-protocol/layers/paint-profiler.js [ Failure ] Bug(none) inspector-protocol/layers/get-layers.js [ Timeout ] Bug(none) paint/pagination/pagination-change-clip-crash.html [ Failure ] -Bug(none) virtual/sampling-heap-profiler/inspector-protocol/memory/sampling-native-profile-blink-gc.js [ Failure ] # Less invalidations or different invalidations without pixel failures. # Some might be good. Some might be under-invalidations for which under-invalidation
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG index 215a925d..27baf0d 100644 --- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG +++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -257,8 +257,6 @@ crbug.com/591099 fast/replaced/replaced-breaking.html [ Failure Pass ] crbug.com/591099 fast/scroll-snap/snaps-after-keyboard-scrolling-rtl.html [ Failure ] crbug.com/591099 fast/text/descent-clip-in-scaled-page.html [ Failure ] -crbug.com/591099 fast/text/ellipsis-in-relative-inline-right.html [ Failure ] -crbug.com/591099 fast/text/ellipsis-in-relative-inline.html [ Failure ] crbug.com/899902 fast/text/ellipsis-with-self-painting-layer.html [ Pass ] crbug.com/796943 fast/text/international/shape-across-elements-simple.html [ Pass ] crbug.com/591099 fast/text/whitespace/018.html [ Failure ]
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService b/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService index 425ee60..bd14aa5 100644 --- a/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService +++ b/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService
@@ -1,8 +1,6 @@ # These tests currently fail when run with --enable-features=NetworkService # See https://crbug.com/729849 -Bug(none) http/tests/security/powerfulFeatureRestrictions/old-powerful-features-on-insecure-origin.html [ Pass Timeout ] - # Only passes with Network Servce enabled (unfortunately these expectations still # allow Failure or Timeout from the top-level expectations though). crbug.com/800898 external/wpt/FileAPI/url/url-in-tags-revoke.window.html [ Pass ] @@ -22,6 +20,7 @@ # Skip virtual/outofblink-cors when NetworkService is on, since it's only # intended to be run with NetworkService off. Bug(none) virtual/outofblink-cors [ Skip ] + # Skip virtual/outofblink-cors-ns on this bot, since when run normally it # already forces NetworkService on, so there is no need to run it again here. Bug(none) virtual/outofblink-cors-ns [ Skip ] @@ -29,3 +28,6 @@ # NetworkService needs S13nServiceWorker. Running these test with # NetworkService doesn't make sense. Bug(none) virtual/disabled-service-worker-servicification [ Skip ] + +# DO NOT ADD ANY ENTRIES! Network service is about to ship to stable. +# Please reach out to network-service-dev@ if you have any questions.
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 06f3f9c..9ef57d7 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -626,6 +626,10 @@ crbug.com/885175 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/align3/grid-abspos-staticpos-justify-self-vertWM-003.html [ Failure ] crbug.com/885175 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/align3/grid-abspos-staticpos-justify-self-vertWM-004.html [ Failure ] +# [css-ui] +# Layout team disagrees with the spec for this particular test. +crbug.com/591099 external/wpt/css/css-ui/text-overflow-015.html [ Failure ] + # ====== Layout team owned tests to here ====== # ====== LayoutNG-only failures from here ====== @@ -4808,8 +4812,6 @@ crbug.com/731018 [ Mac ] sensor/magnetometer.html [ Failure Pass Crash ] crbug.com/731018 [ Mac ] sensor/orientation-sensor.html [ Failure Pass Crash ] -crbug.com/746904 [ Win7 ] fast/text/ellipsis-in-relative-inline.html [ Failure Pass ] - # Tests failing when enabling new modern media controls crbug.com/831942 media/webkit-media-controls-webkit-appearance.html [ Failure Pass ] crbug.com/831957 compositing/video/video-controls-layer-creation-squashing.html [ Failure Pass ] @@ -5933,6 +5935,7 @@ # Sheriff 2018-11-29 crbug.com/910139 custom-elements/form-submission-file.html [ Crash Pass ] crbug.com/905772 [ Linux ] inspector-protocol/memory/sampling-native-profile-blink-gc.js [ Failure Pass ] +crbug.com/905772 [ Linux ] virtual/sampling-heap-profiler/inspector-protocol/memory/sampling-native-profile-blink-gc.js [ Failure Pass ] #Sheriff 2018-12-04 crbug.com/911515 [ Mac ] transforms/shadows.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/badging/idl.html b/third_party/blink/web_tests/badging/idl.html index 825f92f..ffd024f18 100644 --- a/third_party/blink/web_tests/badging/idl.html +++ b/third_party/blink/web_tests/badging/idl.html
@@ -7,7 +7,7 @@ <script type="text/plain" id="tested"> interface Badge { [CallWith=ScriptState, RaisesException] - static void set(optional long contents); + static void set(optional [EnforceRange] unsigned long long contents); [CallWith=ScriptState] static void clear(); }; </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/basic-scan.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/basic-scan.https.html index 31669be..3e43ae4 100644 --- a/third_party/blink/web_tests/bluetooth/requestLEScan/basic-scan.https.html +++ b/third_party/blink/web_tests/bluetooth/requestLEScan/basic-scan.https.html
@@ -14,7 +14,7 @@ await navigator.bluetooth.test.simulateCentral({state: 'powered-on'}); await callWithTrustedClick(async () => { - let scan = await navigator.bluetooth.requestLEScan(); + let scan = await navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true}); assert_equals(scan.constructor.name, 'BluetoothLEScan'); });
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/doesnt-consume-user-gesture.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/doesnt-consume-user-gesture.https.html index 803ddc3..c05d065 100644 --- a/third_party/blink/web_tests/bluetooth/requestLEScan/doesnt-consume-user-gesture.https.html +++ b/third_party/blink/web_tests/bluetooth/requestLEScan/doesnt-consume-user-gesture.https.html
@@ -10,8 +10,8 @@ bluetooth_test(() => navigator.bluetooth.test.simulateCentral({ state: 'powered-on' }) .then(() => callWithTrustedClick(() => { - let first = navigator.bluetooth.requestLEScan(); - let second = navigator.bluetooth.requestLEScan(); + let first = navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true}); + let second = navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true}); return Promise.all([ first.then(scan => assert_equals( scan.constructor.name, 'BluetoothLEScan')),
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/le-not-supported.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/le-not-supported.https.html index 8b8079a5..c523e58 100644 --- a/third_party/blink/web_tests/bluetooth/requestLEScan/le-not-supported.https.html +++ b/third_party/blink/web_tests/bluetooth/requestLEScan/le-not-supported.https.html
@@ -12,7 +12,7 @@ bluetooth_test(() => navigator.bluetooth.test.setLESupported(false) .then(() => assert_promise_rejects_with_message( - requestLEScanWithTrustedClick(), + requestLEScanWithTrustedClick({acceptAllAdvertisements: true}), expected, 'Bluetooth Low Energy is not supported.')), test_desc); </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/multiple-scan.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/multiple-scan.https.html index 8291428..73fa91f4 100644 --- a/third_party/blink/web_tests/bluetooth/requestLEScan/multiple-scan.https.html +++ b/third_party/blink/web_tests/bluetooth/requestLEScan/multiple-scan.https.html
@@ -17,8 +17,8 @@ await navigator.bluetooth.test.simulateCentral({state: 'powered-on'}); await callWithTrustedClick(async () => { - scan = await navigator.bluetooth.requestLEScan(); - scan2 = await navigator.bluetooth.requestLEScan(); + scan = await navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true}); + scan2 = await navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true}); }); scan2.stop()
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/radio-not-present.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/radio-not-present.https.html index d6ac9e3..a4c9b41 100644 --- a/third_party/blink/web_tests/bluetooth/requestLEScan/radio-not-present.https.html +++ b/third_party/blink/web_tests/bluetooth/requestLEScan/radio-not-present.https.html
@@ -12,7 +12,7 @@ bluetooth_test(() => navigator.bluetooth.test.simulateCentral({state: 'absent'}) .then(() => assert_promise_rejects_with_message( - requestLEScanWithTrustedClick(), + requestLEScanWithTrustedClick({acceptAllAdvertisements: true}), expected, 'Bluetooth adapter is not present.')), test_desc); </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/OWNERS b/third_party/blink/web_tests/external/wpt/bluetooth/OWNERS index f4b71db..82904a38 100644 --- a/third_party/blink/web_tests/external/wpt/bluetooth/OWNERS +++ b/third_party/blink/web_tests/external/wpt/bluetooth/OWNERS
@@ -1,3 +1,4 @@ +file://content/browser/bluetooth/OWNERS + # TEAM: web-bluetooth@chromium.org -# COMPONENT: Blink>Bluetooth -ortuno@chromium.org +# COMPONENT: Blink>Bluetooth \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-helpers.js b/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-helpers.js index c01fc9fc..8364323 100644 --- a/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-helpers.js +++ b/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-helpers.js
@@ -263,8 +263,9 @@ // Calls requestLEScan() in a context that's 'allowed to show a popup'. function requestLEScanWithTrustedClick() { + let args = arguments; return callWithTrustedClick( - () => navigator.bluetooth.requestLEScan.apply(navigator.bluetooth)); + () => navigator.bluetooth.requestLEScan.apply(navigator.bluetooth, args)); } // errorUUID(alias) returns a UUID with the top 32 bits of
diff --git a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-basics.https.html b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-basics.https.html index 8d7cf99e..11ed88c3 100644 --- a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-basics.https.html +++ b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-basics.https.html
@@ -13,57 +13,53 @@ /* clipboard.write() */ -promise_test(() => { +promise_test(async () => { const dt = new DataTransfer(); dt.items.add("Howdy", "text/plain"); - return navigator.clipboard.write(dt); + await navigator.clipboard.write(dt); }, "navigator.clipboard.write(DataTransfer) succeeds"); -promise_test(t => { - return promise_rejects(t, new TypeError(), +promise_test(async t => { + await promise_rejects(t, new TypeError(), navigator.clipboard.write()); }, "navigator.clipboard.write() fails (expect DataTransfer)"); -promise_test(t => { - return promise_rejects(t, new TypeError(), +promise_test(async t => { + await promise_rejects(t, new TypeError(), navigator.clipboard.write(null)); }, "navigator.clipboard.write(null) fails (expect DataTransfer)"); -promise_test(t => { - return promise_rejects(t, new TypeError(), +promise_test(async t => { + await promise_rejects(t, new TypeError(), navigator.clipboard.write("Bad string")); }, "navigator.clipboard.write(DOMString) fails (expect DataTransfer)"); /* clipboard.writeText() */ -promise_test(() => { - return navigator.clipboard.writeText("New clipboard text"); +promise_test(async () => { + await navigator.clipboard.writeText("New clipboard text"); }, "navigator.clipboard.writeText(DOMString) succeeds"); -promise_test(t => { - return promise_rejects(t, new TypeError(), +promise_test(async t => { + await promise_rejects(t, new TypeError(), navigator.clipboard.writeText()); }, "navigator.clipboard.writeText() fails (expect DOMString)"); /* clipboard.read() */ -promise_test(() => { - return navigator.clipboard.read() - .then(result => { - assert_true(result instanceof DataTransfer); - }); +promise_test(async () => { + const result = await navigator.clipboard.read(); + assert_true(result instanceof DataTransfer); }, "navigator.clipboard.read() succeeds"); /* clipboard.readText() */ -promise_test(() => { - return navigator.clipboard.readText() - .then(result => { - assert_equals(typeof result, "string"); - }); +promise_test(async () => { + const result = await navigator.clipboard.readText(); + assert_equals(typeof result, "string"); }, "navigator.clipboard.readText() succeeds"); </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/add-inline-child-after-marker-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-lists/add-inline-child-after-marker-001-ref.html new file mode 100644 index 0000000..0c1d2397 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/add-inline-child-after-marker-001-ref.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: Add inline child after marker</title> + +<p>The test passes if you see the list marker followed by the text "inline" and "axxx" in a line below.</p> + +<ul> + <li> + <span>inline</span> + <div style="overflow:hidden;"> + <span>a</span>xxx + </div> + </li> +</ul>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/add-inline-child-after-marker-001.html b/third_party/blink/web_tests/external/wpt/css/css-lists/add-inline-child-after-marker-001.html new file mode 100644 index 0000000..228604ee --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/add-inline-child-after-marker-001.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: Add inline child after marker</title> +<link rel=help href="https://www.w3.org/TR/CSS22/generate.html#lists"> +<link rel=match href="add-inline-child-after-marker-001-ref.html"> +<!-- https://bugs.chromium.org/p/chromium/issues/detail?id=344941 --> + +<p>The test passes if you see the list marker followed by the text "inline" and "axxx" in a line below.</p> + +<ul> + <li id="liTarget"> + <div id="divTarget" style="overflow:hidden;"> + <span>a</span>xxx + </div> + </li> +</ul> +<script> + document.body.offsetHeight; + var new_span=document.createElement("span"); + var text_node=document.createTextNode("inline"); + new_span.appendChild(text_node); + + var div_target=document.getElementById("divTarget"); + var li_target=document.getElementById("liTarget"); + li_target.insertBefore(new_span,div_target); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/li-with-overflow-hidden-change-list-style-position-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-lists/li-with-overflow-hidden-change-list-style-position-001-ref.html new file mode 100644 index 0000000..a3ea8b66 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/li-with-overflow-hidden-change-list-style-position-001-ref.html
@@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test inside and outside switch</title> + +<p>The test passes if the first li is inside and the second one is outside.</p> + +<ul> + <li style="list-style-position: inside;"> + <div style="overflow:hidden;"> + outside to inside + </div> + </li> +</ul> + +<ul> + <li style="list-style-position: outside;"> + <div style="overflow:hidden;"> + inside to outside + </div> + </li> +</ul>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/li-with-overflow-hidden-change-list-style-position-001.html b/third_party/blink/web_tests/external/wpt/css/css-lists/li-with-overflow-hidden-change-list-style-position-001.html new file mode 100644 index 0000000..992ed83 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/li-with-overflow-hidden-change-list-style-position-001.html
@@ -0,0 +1,36 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test inside and outside switch</title> +<link rel=help href="https://www.w3.org/TR/CSS22/generate.html#lists"> +<link rel=match href="li-with-overflow-hidden-change-list-style-position-001-ref.html"> +<!-- https://bugs.chromium.org/p/chromium/issues/detail?id=344941 --> + +<p>The test passes if the first li is inside and the second one is outside.</p> + +<ul> + <li id="outSide" style="list-style-position: outside;"> + <div style="overflow:hidden;"> + outside to inside + </div> + </li> +</ul> + +<ul> + <li id="inSide" style="list-style-position: inside;"> + <div style="overflow:hidden;"> + inside to outside + </div> + </li> +</ul> +<script> + document.body.offsetHeight; + + var outside_li=document.getElementById("outSide"); + outside_li.style.listStylePosition = "inside"; + document.body.offsetHeight; + + var inside_li=document.getElementById("inSide"); + inside_li.style.listStylePosition = "outside"; + document.body.offsetHeight; + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-block-textarea-001.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-block-textarea-001.html new file mode 100644 index 0000000..96af38a8 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-block-textarea-001.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test list with textarea as its first child</title> +<link rel=help href="https://www.w3.org/TR/CSS22/generate.html#lists"> +<!-- https://bugs.chromium.org/p/chromium/issues/detail?id=767408 --> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> +textarea { + border: 0px; + padding: 0px; +} +</style> + +<div id="log"></div> + +<ul> + <li id="target"> + <textarea rows="3" cols="20" style="display:block; height:45px"> + hello + </textarea> + </li> +</ul> + +<script> +test(function() { + var height = document.getElementById("target").offsetHeight; + assert_equals(height, 45, "the height of li should be 45px, and no extra line generated") +}, "list and block textarea"); + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-flex-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-flex-001-ref.html new file mode 100644 index 0000000..d7be687 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-flex-001-ref.html
@@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test list with flex as its first child</title> + +<p>There should be no extra line generated between the marker and the flex.</p> + +<ul> + <li> + <div style="border: 1px black solid;"> + <div style="display: inline-flex; align-items: flex-end; height: 200px;"> + <span style="line-height: 50px">text</span> + </div> + </div> + </li> +</ul>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-flex-001.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-flex-001.html new file mode 100644 index 0000000..98ab9c4 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-flex-001.html
@@ -0,0 +1,18 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test list with flex as its first child</title> +<link rel=help href="https://www.w3.org/TR/CSS22/generate.html#lists"> +<link rel=match href="list-and-flex-001-ref.html"> +<!-- https://bugs.chromium.org/p/chromium/issues/detail?id=767408 --> + +<p>There should be no extra line generated between the marker and the flex.</p> + +<ul> + <li> + <div style="border: 1px black solid;"> + <div style="display: flex; align-items: flex-end; height: 200px;"> + <span style="line-height: 50px">text</span> + </div> + </div> + </li> +</ul>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-grid-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-grid-001-ref.html new file mode 100644 index 0000000..b6e7774 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-grid-001-ref.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test list with grid as its first child</title> + +<p>There should be no extra line generated between the marker and the grid.</p> + +<ul> + <li> + <div style="display: inline-grid; grid-template-rows: 100px; align-items: center;"> + <div>grid</div> + </div> + </li> +</ul> +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-grid-001.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-grid-001.html new file mode 100644 index 0000000..562961d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-grid-001.html
@@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test list with grid as its first child</title> +<link rel=help href="https://www.w3.org/TR/CSS22/generate.html#lists"> +<link rel=match href="list-and-grid-001-ref.html"> +<!-- https://bugs.chromium.org/p/chromium/issues/detail?id=767408 --> + +<p>There should be no extra line generated between the marker and the grid.</p> + +<ul> + <li> + <div style="display: grid; grid-template-rows: 100px; align-items: center;"> + <div>grid</div> + </div> + </li> +</ul>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-001.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-001.html new file mode 100644 index 0000000..e267b83 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-001.html
@@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test the margin collapse of marker</title> +<link rel=help href="https://www.w3.org/TR/CSS22/generate.html#lists"> +<!-- https://bugs.chromium.org/p/chromium/issues/detail?id=767408 --> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<ul id="target" style="margin-top:100px;"> + <li> + <div style="overflow:hidden; margin-top:100px; height:25px;"><a href="#">xxx</a></div> + </li> +</ul> + +<script> +test(function() { + var height = document.getElementById("target").clientHeight; + assert_equals(height, 25, "the height of ul should be 25px") +}, "list and margin collapse"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-writing-mode-001.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-writing-mode-001.html new file mode 100644 index 0000000..df54e8fb --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-writing-mode-001.html
@@ -0,0 +1,24 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test list with root writing-mode as its first child</title> +<link rel=help href="https://www.w3.org/TR/CSS22/generate.html#lists"> +<!-- https://bugs.chromium.org/p/chromium/issues/detail?id=767408 --> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<ul> + <li id="target"> + <div style="writing-mode: vertical-lr; height: 45px;">a b c</div> + </li> +</ul> + +<script> +test(function() { + var height = document.getElementById("target").offsetHeight; + assert_equals(height, 45, "the height of li should be 45px, and no extra line generated") +}, "list and writing-mode"); +</script> +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-marker-with-lineheight-and-overflow-hidden-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-marker-with-lineheight-and-overflow-hidden-001-ref.html new file mode 100644 index 0000000..ae64861 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-marker-with-lineheight-and-overflow-hidden-001-ref.html
@@ -0,0 +1,19 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test list with overflow:hidden and line-height firstchild</title> + +<p>This test passes if there is a marker for each li and followed by "text" in the same line.</p> + +<ul> + <li> + <div style="line-height:100px;"> + <span>text</span> + </div> + </li> +</ul> +<ul> + <li style="list-style-image: url(resources/white.gif);"> + <div style="line-height:100px;">text</div> + </li> +</ul> +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-marker-with-lineheight-and-overflow-hidden-001.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-marker-with-lineheight-and-overflow-hidden-001.html new file mode 100644 index 0000000..d78743fa --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-marker-with-lineheight-and-overflow-hidden-001.html
@@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test list with overflow:hidden and line-height firstchild</title> +<link rel=help href="https://www.w3.org/TR/CSS22/generate.html#lists"> +<link rel=match href="list-marker-with-lineheight-and-overflow-hidden-001-ref.html"> +<!-- https://bugs.chromium.org/p/chromium/issues/detail?id=344941 --> + +<p>This test passes if there is a marker for each li and followed by "text" in the same line.</p> + +<ul> + <li> + <div style="overflow:hidden; line-height:100px;"> + <span>text</span> + </div> + </li> +</ul> + +<ul> + <li style="list-style-image: url(resources/white.gif);"> + <div style="overflow:hidden; line-height:100px;">text</div> + </li> +</ul>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-with-image-display-changed-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-with-image-display-changed-001-ref.html new file mode 100644 index 0000000..bfe0eb6 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-with-image-display-changed-001-ref.html
@@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test list with firstchild changing display</title> + +<style> +li { border: 3px solid black; margin: 3px; } +img { display: block; } +</style> + +<ul> + <li> + <a href="#"><img src="./resources/white.gif" width=32 height=32 /></a> + Some other text + </li> +</ul>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-with-image-display-changed-001.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-with-image-display-changed-001.html new file mode 100644 index 0000000..d4b3e30 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-with-image-display-changed-001.html
@@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Lists: test list with firstchild changing display</title> +<link rel=help href="https://github.com/w3c/csswg-drafts/issues/2787"> +<link rel=match href="list-with-image-display-changed-001-ref.html"> +<!-- https://bugs.chromium.org/p/chromium/issues/detail?id=715288 --> +<meta name="assert" content=" + After the display of img being changed from block to inline, then back to block, + the final position of marker should be the same as the beginning." /> + +<style> +li { border: 3px solid black; margin: 3px; } +img { display: block; } +</style> + +<ul> + <li> + <a href="#"><img src="./resources/white.gif" width=32 height=32 /></a> + Some other text + </li> +</ul> +<script> + document.body.offsetTop; + var img = document.querySelector('a img'); + img.style.display = 'inline'; + img.offsetWidth; + img.style.display = 'block'; +</script> +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/resources/white.gif b/third_party/blink/web_tests/external/wpt/css/css-lists/resources/white.gif new file mode 100644 index 0000000..3aa2098 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-lists/resources/white.gif Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/big_buffer.mojom.js b/third_party/blink/web_tests/external/wpt/resources/chromium/big_buffer.mojom.js new file mode 100644 index 0000000..f09706e --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/big_buffer.mojom.js
@@ -0,0 +1,263 @@ +// 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. + +'use strict'; + +(function() { + var mojomId = 'mojo/public/mojom/base/big_buffer.mojom'; + if (mojo.internal.isMojomLoaded(mojomId)) { + console.warn('The following mojom is loaded multiple times: ' + mojomId); + return; + } + mojo.internal.markMojomLoaded(mojomId); + var bindings = mojo; + var associatedBindings = mojo; + var codec = mojo.internal; + var validator = mojo.internal; + + var exports = mojo.internal.exposeNamespace('mojoBase.mojom'); + + + + function BigBufferSharedMemoryRegion(values) { + this.initDefaults_(); + this.initFields_(values); + } + + + BigBufferSharedMemoryRegion.prototype.initDefaults_ = function() { + this.bufferHandle = null; + this.size = 0; + }; + BigBufferSharedMemoryRegion.prototype.initFields_ = function(fields) { + for(var field in fields) { + if (this.hasOwnProperty(field)) + this[field] = fields[field]; + } + }; + + BigBufferSharedMemoryRegion.validate = function(messageValidator, offset) { + var err; + err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); + if (err !== validator.validationError.NONE) + return err; + + var kVersionSizes = [ + {version: 0, numBytes: 16} + ]; + err = messageValidator.validateStructVersion(offset, kVersionSizes); + if (err !== validator.validationError.NONE) + return err; + + + // validate BigBufferSharedMemoryRegion.bufferHandle + err = messageValidator.validateHandle(offset + codec.kStructHeaderSize + 0, false) + if (err !== validator.validationError.NONE) + return err; + + + return validator.validationError.NONE; + }; + + BigBufferSharedMemoryRegion.encodedSize = codec.kStructHeaderSize + 8; + + BigBufferSharedMemoryRegion.decode = function(decoder) { + var packed; + var val = new BigBufferSharedMemoryRegion(); + var numberOfBytes = decoder.readUint32(); + var version = decoder.readUint32(); + val.bufferHandle = decoder.decodeStruct(codec.Handle); + val.size = decoder.decodeStruct(codec.Uint32); + return val; + }; + + BigBufferSharedMemoryRegion.encode = function(encoder, val) { + var packed; + encoder.writeUint32(BigBufferSharedMemoryRegion.encodedSize); + encoder.writeUint32(0); + encoder.encodeStruct(codec.Handle, val.bufferHandle); + encoder.encodeStruct(codec.Uint32, val.size); + }; + + function BigBuffer(value) { + this.initDefault_(); + this.initValue_(value); + } + + + BigBuffer.Tags = { + bytes: 0, + sharedMemory: 1, + invalidBuffer: 2, + }; + + BigBuffer.prototype.initDefault_ = function() { + this.$data = null; + this.$tag = undefined; + } + + BigBuffer.prototype.initValue_ = function(value) { + if (value == undefined) { + return; + } + + var keys = Object.keys(value); + if (keys.length == 0) { + return; + } + + if (keys.length > 1) { + throw new TypeError("You may set only one member on a union."); + } + + var fields = [ + "bytes", + "sharedMemory", + "invalidBuffer", + ]; + + if (fields.indexOf(keys[0]) < 0) { + throw new ReferenceError(keys[0] + " is not a BigBuffer member."); + + } + + this[keys[0]] = value[keys[0]]; + } + Object.defineProperty(BigBuffer.prototype, "bytes", { + get: function() { + if (this.$tag != BigBuffer.Tags.bytes) { + throw new ReferenceError( + "BigBuffer.bytes is not currently set."); + } + return this.$data; + }, + + set: function(value) { + this.$tag = BigBuffer.Tags.bytes; + this.$data = value; + } + }); + Object.defineProperty(BigBuffer.prototype, "sharedMemory", { + get: function() { + if (this.$tag != BigBuffer.Tags.sharedMemory) { + throw new ReferenceError( + "BigBuffer.sharedMemory is not currently set."); + } + return this.$data; + }, + + set: function(value) { + this.$tag = BigBuffer.Tags.sharedMemory; + this.$data = value; + } + }); + Object.defineProperty(BigBuffer.prototype, "invalidBuffer", { + get: function() { + if (this.$tag != BigBuffer.Tags.invalidBuffer) { + throw new ReferenceError( + "BigBuffer.invalidBuffer is not currently set."); + } + return this.$data; + }, + + set: function(value) { + this.$tag = BigBuffer.Tags.invalidBuffer; + this.$data = value; + } + }); + + + BigBuffer.encode = function(encoder, val) { + if (val == null) { + encoder.writeUint64(0); + encoder.writeUint64(0); + return; + } + if (val.$tag == undefined) { + throw new TypeError("Cannot encode unions with an unknown member set."); + } + + encoder.writeUint32(16); + encoder.writeUint32(val.$tag); + switch (val.$tag) { + case BigBuffer.Tags.bytes: + encoder.encodeArrayPointer(codec.Uint8, val.bytes); + break; + case BigBuffer.Tags.sharedMemory: + encoder.encodeStructPointer(BigBufferSharedMemoryRegion, val.sharedMemory); + break; + case BigBuffer.Tags.invalidBuffer: + encoder.writeUint8(val.invalidBuffer ? 1 : 0); + break; + } + encoder.align(); + }; + + + BigBuffer.decode = function(decoder) { + var size = decoder.readUint32(); + if (size == 0) { + decoder.readUint32(); + decoder.readUint64(); + return null; + } + + var result = new BigBuffer(); + var tag = decoder.readUint32(); + switch (tag) { + case BigBuffer.Tags.bytes: + result.bytes = decoder.decodeArrayPointer(codec.Uint8); + break; + case BigBuffer.Tags.sharedMemory: + result.sharedMemory = decoder.decodeStructPointer(BigBufferSharedMemoryRegion); + break; + case BigBuffer.Tags.invalidBuffer: + result.invalidBuffer = decoder.readUint8() ? true : false; + break; + } + decoder.align(); + + return result; + }; + + + BigBuffer.validate = function(messageValidator, offset) { + var size = messageValidator.decodeUnionSize(offset); + if (size != 16) { + return validator.validationError.INVALID_UNION_SIZE; + } + + var tag = messageValidator.decodeUnionTag(offset); + var data_offset = offset + 8; + var err; + switch (tag) { + case BigBuffer.Tags.bytes: + + + // validate BigBuffer.bytes + err = messageValidator.validateArrayPointer(data_offset, 1, codec.Uint8, false, [0], 0); + if (err !== validator.validationError.NONE) + return err; + break; + case BigBuffer.Tags.sharedMemory: + + + // validate BigBuffer.sharedMemory + err = messageValidator.validateStructPointer(data_offset, BigBufferSharedMemoryRegion, false); + if (err !== validator.validationError.NONE) + return err; + break; + case BigBuffer.Tags.invalidBuffer: + + + break; + } + + return validator.validationError.NONE; + }; + + BigBuffer.encodedSize = 16; + exports.BigBufferSharedMemoryRegion = BigBufferSharedMemoryRegion; + exports.BigBuffer = BigBuffer; +})();
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js.headers b/third_party/blink/web_tests/external/wpt/resources/chromium/big_buffer.mojom.js.headers similarity index 97% rename from third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js.headers rename to third_party/blink/web_tests/external/wpt/resources/chromium/big_buffer.mojom.js.headers index 6805c323..6c61a34 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js.headers +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/big_buffer.mojom.js.headers
@@ -1 +1 @@ -Content-Type: text/javascript; charset=utf-8 +Content-Type: text/javascript; charset=utf-8 \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/device.mojom.js b/third_party/blink/web_tests/external/wpt/resources/chromium/device.mojom.js index 495971c..0172206e 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/device.mojom.js +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/device.mojom.js
@@ -18,7 +18,7 @@ var exports = mojo.internal.exposeNamespace('device.mojom'); var string16$ = - mojo.internal.exposeNamespace('mojo.common.mojom'); + mojo.internal.exposeNamespace('mojoBase.mojom'); if (mojo.config.autoLoadMojomDeps) { mojo.internal.loadMojomIfNecessary( 'mojo/public/mojom/base/string16.mojom', '../../../../mojo/public/mojom/base/string16.mojom.js'); @@ -35,6 +35,8 @@ UsbOpenDeviceError.OK = 0; UsbOpenDeviceError.ACCESS_DENIED = UsbOpenDeviceError.OK + 1; UsbOpenDeviceError.ALREADY_OPEN = UsbOpenDeviceError.ACCESS_DENIED + 1; + UsbOpenDeviceError.MIN_VALUE = 0, + UsbOpenDeviceError.MAX_VALUE = 2, UsbOpenDeviceError.isKnownEnumValue = function(value) { switch (value) { @@ -56,6 +58,8 @@ var UsbTransferDirection = {}; UsbTransferDirection.INBOUND = 0; UsbTransferDirection.OUTBOUND = UsbTransferDirection.INBOUND + 1; + UsbTransferDirection.MIN_VALUE = 0, + UsbTransferDirection.MAX_VALUE = 1, UsbTransferDirection.isKnownEnumValue = function(value) { switch (value) { @@ -78,6 +82,8 @@ UsbControlTransferType.CLASS = UsbControlTransferType.STANDARD + 1; UsbControlTransferType.VENDOR = UsbControlTransferType.CLASS + 1; UsbControlTransferType.RESERVED = UsbControlTransferType.VENDOR + 1; + UsbControlTransferType.MIN_VALUE = 0, + UsbControlTransferType.MAX_VALUE = 3, UsbControlTransferType.isKnownEnumValue = function(value) { switch (value) { @@ -102,6 +108,8 @@ UsbControlTransferRecipient.INTERFACE = UsbControlTransferRecipient.DEVICE + 1; UsbControlTransferRecipient.ENDPOINT = UsbControlTransferRecipient.INTERFACE + 1; UsbControlTransferRecipient.OTHER = UsbControlTransferRecipient.ENDPOINT + 1; + UsbControlTransferRecipient.MIN_VALUE = 0, + UsbControlTransferRecipient.MAX_VALUE = 3, UsbControlTransferRecipient.isKnownEnumValue = function(value) { switch (value) { @@ -126,6 +134,8 @@ UsbTransferType.ISOCHRONOUS = UsbTransferType.CONTROL + 1; UsbTransferType.BULK = UsbTransferType.ISOCHRONOUS + 1; UsbTransferType.INTERRUPT = UsbTransferType.BULK + 1; + UsbTransferType.MIN_VALUE = 0, + UsbTransferType.MAX_VALUE = 3, UsbTransferType.isKnownEnumValue = function(value) { switch (value) { @@ -155,6 +165,8 @@ UsbTransferStatus.BABBLE = UsbTransferStatus.DISCONNECT + 1; UsbTransferStatus.SHORT_PACKET = UsbTransferStatus.BABBLE + 1; UsbTransferStatus.PERMISSION_DENIED = UsbTransferStatus.SHORT_PACKET + 1; + UsbTransferStatus.MIN_VALUE = 0, + UsbTransferStatus.MAX_VALUE = 8, UsbTransferStatus.isKnownEnumValue = function(value) { switch (value) { @@ -3649,4 +3661,4 @@ exports.UsbDeviceClient = UsbDeviceClient; exports.UsbDeviceClientPtr = UsbDeviceClientPtr; exports.UsbDeviceClientAssociatedPtr = UsbDeviceClientAssociatedPtr; -})(); +})(); \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/device_enumeration_options.mojom.js b/third_party/blink/web_tests/external/wpt/resources/chromium/device_enumeration_options.mojom.js new file mode 100644 index 0000000..5a775ad3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/device_enumeration_options.mojom.js
@@ -0,0 +1,184 @@ +// 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. + +'use strict'; + +(function() { + var mojomId = 'device/usb/public/mojom/device_enumeration_options.mojom'; + if (mojo.internal.isMojomLoaded(mojomId)) { + console.warn('The following mojom is loaded multiple times: ' + mojomId); + return; + } + mojo.internal.markMojomLoaded(mojomId); + var bindings = mojo; + var associatedBindings = mojo; + var codec = mojo.internal; + var validator = mojo.internal; + + var exports = mojo.internal.exposeNamespace('device.mojom'); + var string16$ = + mojo.internal.exposeNamespace('mojoBase.mojom'); + if (mojo.config.autoLoadMojomDeps) { + mojo.internal.loadMojomIfNecessary( + 'mojo/public/mojom/base/string16.mojom', '../../../../mojo/public/mojom/base/string16.mojom.js'); + } + + + + function UsbDeviceFilter(values) { + this.initDefaults_(); + this.initFields_(values); + } + + + UsbDeviceFilter.prototype.initDefaults_ = function() { + this.hasVendorId = false; + this.hasProductId = false; + this.hasClassCode = false; + this.hasSubclassCode = false; + this.hasProtocolCode = false; + this.classCode = 0; + this.vendorId = 0; + this.productId = 0; + this.subclassCode = 0; + this.protocolCode = 0; + this.serialNumber = null; + }; + UsbDeviceFilter.prototype.initFields_ = function(fields) { + for(var field in fields) { + if (this.hasOwnProperty(field)) + this[field] = fields[field]; + } + }; + + UsbDeviceFilter.validate = function(messageValidator, offset) { + var err; + err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); + if (err !== validator.validationError.NONE) + return err; + + var kVersionSizes = [ + {version: 0, numBytes: 24} + ]; + err = messageValidator.validateStructVersion(offset, kVersionSizes); + if (err !== validator.validationError.NONE) + return err; + + + + + + + + + + + + + // validate UsbDeviceFilter.serialNumber + err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 8, string16$.String16, true); + if (err !== validator.validationError.NONE) + return err; + + return validator.validationError.NONE; + }; + + UsbDeviceFilter.encodedSize = codec.kStructHeaderSize + 16; + + UsbDeviceFilter.decode = function(decoder) { + var packed; + var val = new UsbDeviceFilter(); + var numberOfBytes = decoder.readUint32(); + var version = decoder.readUint32(); + packed = decoder.readUint8(); + val.hasVendorId = (packed >> 0) & 1 ? true : false; + val.hasProductId = (packed >> 1) & 1 ? true : false; + val.hasClassCode = (packed >> 2) & 1 ? true : false; + val.hasSubclassCode = (packed >> 3) & 1 ? true : false; + val.hasProtocolCode = (packed >> 4) & 1 ? true : false; + val.classCode = decoder.decodeStruct(codec.Uint8); + val.vendorId = decoder.decodeStruct(codec.Uint16); + val.productId = decoder.decodeStruct(codec.Uint16); + val.subclassCode = decoder.decodeStruct(codec.Uint8); + val.protocolCode = decoder.decodeStruct(codec.Uint8); + val.serialNumber = decoder.decodeStructPointer(string16$.String16); + return val; + }; + + UsbDeviceFilter.encode = function(encoder, val) { + var packed; + encoder.writeUint32(UsbDeviceFilter.encodedSize); + encoder.writeUint32(0); + packed = 0; + packed |= (val.hasVendorId & 1) << 0 + packed |= (val.hasProductId & 1) << 1 + packed |= (val.hasClassCode & 1) << 2 + packed |= (val.hasSubclassCode & 1) << 3 + packed |= (val.hasProtocolCode & 1) << 4 + encoder.writeUint8(packed); + encoder.encodeStruct(codec.Uint8, val.classCode); + encoder.encodeStruct(codec.Uint16, val.vendorId); + encoder.encodeStruct(codec.Uint16, val.productId); + encoder.encodeStruct(codec.Uint8, val.subclassCode); + encoder.encodeStruct(codec.Uint8, val.protocolCode); + encoder.encodeStructPointer(string16$.String16, val.serialNumber); + }; + function UsbEnumerationOptions(values) { + this.initDefaults_(); + this.initFields_(values); + } + + + UsbEnumerationOptions.prototype.initDefaults_ = function() { + this.filters = null; + }; + UsbEnumerationOptions.prototype.initFields_ = function(fields) { + for(var field in fields) { + if (this.hasOwnProperty(field)) + this[field] = fields[field]; + } + }; + + UsbEnumerationOptions.validate = function(messageValidator, offset) { + var err; + err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); + if (err !== validator.validationError.NONE) + return err; + + var kVersionSizes = [ + {version: 0, numBytes: 16} + ]; + err = messageValidator.validateStructVersion(offset, kVersionSizes); + if (err !== validator.validationError.NONE) + return err; + + + // validate UsbEnumerationOptions.filters + err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 8, new codec.PointerTo(UsbDeviceFilter), false, [0], 0); + if (err !== validator.validationError.NONE) + return err; + + return validator.validationError.NONE; + }; + + UsbEnumerationOptions.encodedSize = codec.kStructHeaderSize + 8; + + UsbEnumerationOptions.decode = function(decoder) { + var packed; + var val = new UsbEnumerationOptions(); + var numberOfBytes = decoder.readUint32(); + var version = decoder.readUint32(); + val.filters = decoder.decodeArrayPointer(new codec.PointerTo(UsbDeviceFilter)); + return val; + }; + + UsbEnumerationOptions.encode = function(encoder, val) { + var packed; + encoder.writeUint32(UsbEnumerationOptions.encodedSize); + encoder.writeUint32(0); + encoder.encodeArrayPointer(new codec.PointerTo(UsbDeviceFilter), val.filters); + }; + exports.UsbDeviceFilter = UsbDeviceFilter; + exports.UsbEnumerationOptions = UsbEnumerationOptions; +})(); \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js.headers b/third_party/blink/web_tests/external/wpt/resources/chromium/device_enumeration_options.mojom.js.headers similarity index 97% copy from third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js.headers copy to third_party/blink/web_tests/external/wpt/resources/chromium/device_enumeration_options.mojom.js.headers index 6805c323..6c61a34 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js.headers +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/device_enumeration_options.mojom.js.headers
@@ -1 +1 @@ -Content-Type: text/javascript; charset=utf-8 +Content-Type: text/javascript; charset=utf-8 \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js b/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js deleted file mode 100644 index 2d76263..0000000 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js +++ /dev/null
@@ -1,843 +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. - -'use strict'; - -(function() { - var mojomId = 'device/usb/public/mojom/device_manager.mojom'; - if (mojo.internal.isMojomLoaded(mojomId)) { - console.warn('The following mojom is loaded multiple times: ' + mojomId); - return; - } - mojo.internal.markMojomLoaded(mojomId); - var bindings = mojo; - var associatedBindings = mojo; - var codec = mojo.internal; - var validator = mojo.internal; - - var exports = mojo.internal.exposeNamespace('device.mojom'); - var device$ = - mojo.internal.exposeNamespace('device.mojom'); - if (mojo.config.autoLoadMojomDeps) { - mojo.internal.loadMojomIfNecessary( - 'device/usb/public/mojom/device.mojom', 'device.mojom.js'); - } - var string16$ = - mojo.internal.exposeNamespace('mojo.common.mojom'); - if (mojo.config.autoLoadMojomDeps) { - mojo.internal.loadMojomIfNecessary( - 'mojo/public/mojom/base/string16.mojom', '../../../../mojo/public/mojom/base/string16.mojom.js'); - } - - - - function UsbDeviceFilter(values) { - this.initDefaults_(); - this.initFields_(values); - } - - - UsbDeviceFilter.prototype.initDefaults_ = function() { - this.hasVendorId = false; - this.hasProductId = false; - this.hasClassCode = false; - this.hasSubclassCode = false; - this.hasProtocolCode = false; - this.classCode = 0; - this.vendorId = 0; - this.productId = 0; - this.subclassCode = 0; - this.protocolCode = 0; - this.serialNumber = null; - }; - UsbDeviceFilter.prototype.initFields_ = function(fields) { - for(var field in fields) { - if (this.hasOwnProperty(field)) - this[field] = fields[field]; - } - }; - - UsbDeviceFilter.validate = function(messageValidator, offset) { - var err; - err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); - if (err !== validator.validationError.NONE) - return err; - - var kVersionSizes = [ - {version: 0, numBytes: 24} - ]; - err = messageValidator.validateStructVersion(offset, kVersionSizes); - if (err !== validator.validationError.NONE) - return err; - - - - - - - - - - - - - // validate UsbDeviceFilter.serialNumber - err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 8, string16$.String16, true); - if (err !== validator.validationError.NONE) - return err; - - return validator.validationError.NONE; - }; - - UsbDeviceFilter.encodedSize = codec.kStructHeaderSize + 16; - - UsbDeviceFilter.decode = function(decoder) { - var packed; - var val = new UsbDeviceFilter(); - var numberOfBytes = decoder.readUint32(); - var version = decoder.readUint32(); - packed = decoder.readUint8(); - val.hasVendorId = (packed >> 0) & 1 ? true : false; - val.hasProductId = (packed >> 1) & 1 ? true : false; - val.hasClassCode = (packed >> 2) & 1 ? true : false; - val.hasSubclassCode = (packed >> 3) & 1 ? true : false; - val.hasProtocolCode = (packed >> 4) & 1 ? true : false; - val.classCode = decoder.decodeStruct(codec.Uint8); - val.vendorId = decoder.decodeStruct(codec.Uint16); - val.productId = decoder.decodeStruct(codec.Uint16); - val.subclassCode = decoder.decodeStruct(codec.Uint8); - val.protocolCode = decoder.decodeStruct(codec.Uint8); - val.serialNumber = decoder.decodeStructPointer(string16$.String16); - return val; - }; - - UsbDeviceFilter.encode = function(encoder, val) { - var packed; - encoder.writeUint32(UsbDeviceFilter.encodedSize); - encoder.writeUint32(0); - packed = 0; - packed |= (val.hasVendorId & 1) << 0 - packed |= (val.hasProductId & 1) << 1 - packed |= (val.hasClassCode & 1) << 2 - packed |= (val.hasSubclassCode & 1) << 3 - packed |= (val.hasProtocolCode & 1) << 4 - encoder.writeUint8(packed); - encoder.encodeStruct(codec.Uint8, val.classCode); - encoder.encodeStruct(codec.Uint16, val.vendorId); - encoder.encodeStruct(codec.Uint16, val.productId); - encoder.encodeStruct(codec.Uint8, val.subclassCode); - encoder.encodeStruct(codec.Uint8, val.protocolCode); - encoder.encodeStructPointer(string16$.String16, val.serialNumber); - }; - function UsbEnumerationOptions(values) { - this.initDefaults_(); - this.initFields_(values); - } - - - UsbEnumerationOptions.prototype.initDefaults_ = function() { - this.filters = null; - }; - UsbEnumerationOptions.prototype.initFields_ = function(fields) { - for(var field in fields) { - if (this.hasOwnProperty(field)) - this[field] = fields[field]; - } - }; - - UsbEnumerationOptions.validate = function(messageValidator, offset) { - var err; - err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); - if (err !== validator.validationError.NONE) - return err; - - var kVersionSizes = [ - {version: 0, numBytes: 16} - ]; - err = messageValidator.validateStructVersion(offset, kVersionSizes); - if (err !== validator.validationError.NONE) - return err; - - - // validate UsbEnumerationOptions.filters - err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 8, new codec.PointerTo(UsbDeviceFilter), false, [0], 0); - if (err !== validator.validationError.NONE) - return err; - - return validator.validationError.NONE; - }; - - UsbEnumerationOptions.encodedSize = codec.kStructHeaderSize + 8; - - UsbEnumerationOptions.decode = function(decoder) { - var packed; - var val = new UsbEnumerationOptions(); - var numberOfBytes = decoder.readUint32(); - var version = decoder.readUint32(); - val.filters = decoder.decodeArrayPointer(new codec.PointerTo(UsbDeviceFilter)); - return val; - }; - - UsbEnumerationOptions.encode = function(encoder, val) { - var packed; - encoder.writeUint32(UsbEnumerationOptions.encodedSize); - encoder.writeUint32(0); - encoder.encodeArrayPointer(new codec.PointerTo(UsbDeviceFilter), val.filters); - }; - function UsbDeviceManager_GetDevices_Params(values) { - this.initDefaults_(); - this.initFields_(values); - } - - - UsbDeviceManager_GetDevices_Params.prototype.initDefaults_ = function() { - this.options = null; - }; - UsbDeviceManager_GetDevices_Params.prototype.initFields_ = function(fields) { - for(var field in fields) { - if (this.hasOwnProperty(field)) - this[field] = fields[field]; - } - }; - - UsbDeviceManager_GetDevices_Params.validate = function(messageValidator, offset) { - var err; - err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); - if (err !== validator.validationError.NONE) - return err; - - var kVersionSizes = [ - {version: 0, numBytes: 16} - ]; - err = messageValidator.validateStructVersion(offset, kVersionSizes); - if (err !== validator.validationError.NONE) - return err; - - - // validate UsbDeviceManager_GetDevices_Params.options - err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, UsbEnumerationOptions, true); - if (err !== validator.validationError.NONE) - return err; - - return validator.validationError.NONE; - }; - - UsbDeviceManager_GetDevices_Params.encodedSize = codec.kStructHeaderSize + 8; - - UsbDeviceManager_GetDevices_Params.decode = function(decoder) { - var packed; - var val = new UsbDeviceManager_GetDevices_Params(); - var numberOfBytes = decoder.readUint32(); - var version = decoder.readUint32(); - val.options = decoder.decodeStructPointer(UsbEnumerationOptions); - return val; - }; - - UsbDeviceManager_GetDevices_Params.encode = function(encoder, val) { - var packed; - encoder.writeUint32(UsbDeviceManager_GetDevices_Params.encodedSize); - encoder.writeUint32(0); - encoder.encodeStructPointer(UsbEnumerationOptions, val.options); - }; - function UsbDeviceManager_GetDevices_ResponseParams(values) { - this.initDefaults_(); - this.initFields_(values); - } - - - UsbDeviceManager_GetDevices_ResponseParams.prototype.initDefaults_ = function() { - this.results = null; - }; - UsbDeviceManager_GetDevices_ResponseParams.prototype.initFields_ = function(fields) { - for(var field in fields) { - if (this.hasOwnProperty(field)) - this[field] = fields[field]; - } - }; - - UsbDeviceManager_GetDevices_ResponseParams.validate = function(messageValidator, offset) { - var err; - err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); - if (err !== validator.validationError.NONE) - return err; - - var kVersionSizes = [ - {version: 0, numBytes: 16} - ]; - err = messageValidator.validateStructVersion(offset, kVersionSizes); - if (err !== validator.validationError.NONE) - return err; - - - // validate UsbDeviceManager_GetDevices_ResponseParams.results - err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 8, new codec.PointerTo(device$.UsbDeviceInfo), false, [0], 0); - if (err !== validator.validationError.NONE) - return err; - - return validator.validationError.NONE; - }; - - UsbDeviceManager_GetDevices_ResponseParams.encodedSize = codec.kStructHeaderSize + 8; - - UsbDeviceManager_GetDevices_ResponseParams.decode = function(decoder) { - var packed; - var val = new UsbDeviceManager_GetDevices_ResponseParams(); - var numberOfBytes = decoder.readUint32(); - var version = decoder.readUint32(); - val.results = decoder.decodeArrayPointer(new codec.PointerTo(device$.UsbDeviceInfo)); - return val; - }; - - UsbDeviceManager_GetDevices_ResponseParams.encode = function(encoder, val) { - var packed; - encoder.writeUint32(UsbDeviceManager_GetDevices_ResponseParams.encodedSize); - encoder.writeUint32(0); - encoder.encodeArrayPointer(new codec.PointerTo(device$.UsbDeviceInfo), val.results); - }; - function UsbDeviceManager_GetDevice_Params(values) { - this.initDefaults_(); - this.initFields_(values); - } - - - UsbDeviceManager_GetDevice_Params.prototype.initDefaults_ = function() { - this.guid = null; - this.deviceRequest = new bindings.InterfaceRequest(); - }; - UsbDeviceManager_GetDevice_Params.prototype.initFields_ = function(fields) { - for(var field in fields) { - if (this.hasOwnProperty(field)) - this[field] = fields[field]; - } - }; - - UsbDeviceManager_GetDevice_Params.validate = function(messageValidator, offset) { - var err; - err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); - if (err !== validator.validationError.NONE) - return err; - - var kVersionSizes = [ - {version: 0, numBytes: 24} - ]; - err = messageValidator.validateStructVersion(offset, kVersionSizes); - if (err !== validator.validationError.NONE) - return err; - - - // validate UsbDeviceManager_GetDevice_Params.guid - err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 0, false) - if (err !== validator.validationError.NONE) - return err; - - - // validate UsbDeviceManager_GetDevice_Params.deviceRequest - err = messageValidator.validateInterfaceRequest(offset + codec.kStructHeaderSize + 8, false) - if (err !== validator.validationError.NONE) - return err; - - return validator.validationError.NONE; - }; - - UsbDeviceManager_GetDevice_Params.encodedSize = codec.kStructHeaderSize + 16; - - UsbDeviceManager_GetDevice_Params.decode = function(decoder) { - var packed; - var val = new UsbDeviceManager_GetDevice_Params(); - var numberOfBytes = decoder.readUint32(); - var version = decoder.readUint32(); - val.guid = decoder.decodeStruct(codec.String); - val.deviceRequest = decoder.decodeStruct(codec.InterfaceRequest); - decoder.skip(1); - decoder.skip(1); - decoder.skip(1); - decoder.skip(1); - return val; - }; - - UsbDeviceManager_GetDevice_Params.encode = function(encoder, val) { - var packed; - encoder.writeUint32(UsbDeviceManager_GetDevice_Params.encodedSize); - encoder.writeUint32(0); - encoder.encodeStruct(codec.String, val.guid); - encoder.encodeStruct(codec.InterfaceRequest, val.deviceRequest); - encoder.skip(1); - encoder.skip(1); - encoder.skip(1); - encoder.skip(1); - }; - function UsbDeviceManager_SetClient_Params(values) { - this.initDefaults_(); - this.initFields_(values); - } - - - UsbDeviceManager_SetClient_Params.prototype.initDefaults_ = function() { - this.client = new UsbDeviceManagerClientPtr(); - }; - UsbDeviceManager_SetClient_Params.prototype.initFields_ = function(fields) { - for(var field in fields) { - if (this.hasOwnProperty(field)) - this[field] = fields[field]; - } - }; - - UsbDeviceManager_SetClient_Params.validate = function(messageValidator, offset) { - var err; - err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); - if (err !== validator.validationError.NONE) - return err; - - var kVersionSizes = [ - {version: 0, numBytes: 16} - ]; - err = messageValidator.validateStructVersion(offset, kVersionSizes); - if (err !== validator.validationError.NONE) - return err; - - - // validate UsbDeviceManager_SetClient_Params.client - err = messageValidator.validateInterface(offset + codec.kStructHeaderSize + 0, false); - if (err !== validator.validationError.NONE) - return err; - - return validator.validationError.NONE; - }; - - UsbDeviceManager_SetClient_Params.encodedSize = codec.kStructHeaderSize + 8; - - UsbDeviceManager_SetClient_Params.decode = function(decoder) { - var packed; - var val = new UsbDeviceManager_SetClient_Params(); - var numberOfBytes = decoder.readUint32(); - var version = decoder.readUint32(); - val.client = decoder.decodeStruct(new codec.Interface(UsbDeviceManagerClientPtr)); - return val; - }; - - UsbDeviceManager_SetClient_Params.encode = function(encoder, val) { - var packed; - encoder.writeUint32(UsbDeviceManager_SetClient_Params.encodedSize); - encoder.writeUint32(0); - encoder.encodeStruct(new codec.Interface(UsbDeviceManagerClientPtr), val.client); - }; - function UsbDeviceManagerClient_OnDeviceAdded_Params(values) { - this.initDefaults_(); - this.initFields_(values); - } - - - UsbDeviceManagerClient_OnDeviceAdded_Params.prototype.initDefaults_ = function() { - this.deviceInfo = null; - }; - UsbDeviceManagerClient_OnDeviceAdded_Params.prototype.initFields_ = function(fields) { - for(var field in fields) { - if (this.hasOwnProperty(field)) - this[field] = fields[field]; - } - }; - - UsbDeviceManagerClient_OnDeviceAdded_Params.validate = function(messageValidator, offset) { - var err; - err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); - if (err !== validator.validationError.NONE) - return err; - - var kVersionSizes = [ - {version: 0, numBytes: 16} - ]; - err = messageValidator.validateStructVersion(offset, kVersionSizes); - if (err !== validator.validationError.NONE) - return err; - - - // validate UsbDeviceManagerClient_OnDeviceAdded_Params.deviceInfo - err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, device$.UsbDeviceInfo, false); - if (err !== validator.validationError.NONE) - return err; - - return validator.validationError.NONE; - }; - - UsbDeviceManagerClient_OnDeviceAdded_Params.encodedSize = codec.kStructHeaderSize + 8; - - UsbDeviceManagerClient_OnDeviceAdded_Params.decode = function(decoder) { - var packed; - var val = new UsbDeviceManagerClient_OnDeviceAdded_Params(); - var numberOfBytes = decoder.readUint32(); - var version = decoder.readUint32(); - val.deviceInfo = decoder.decodeStructPointer(device$.UsbDeviceInfo); - return val; - }; - - UsbDeviceManagerClient_OnDeviceAdded_Params.encode = function(encoder, val) { - var packed; - encoder.writeUint32(UsbDeviceManagerClient_OnDeviceAdded_Params.encodedSize); - encoder.writeUint32(0); - encoder.encodeStructPointer(device$.UsbDeviceInfo, val.deviceInfo); - }; - function UsbDeviceManagerClient_OnDeviceRemoved_Params(values) { - this.initDefaults_(); - this.initFields_(values); - } - - - UsbDeviceManagerClient_OnDeviceRemoved_Params.prototype.initDefaults_ = function() { - this.deviceInfo = null; - }; - UsbDeviceManagerClient_OnDeviceRemoved_Params.prototype.initFields_ = function(fields) { - for(var field in fields) { - if (this.hasOwnProperty(field)) - this[field] = fields[field]; - } - }; - - UsbDeviceManagerClient_OnDeviceRemoved_Params.validate = function(messageValidator, offset) { - var err; - err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); - if (err !== validator.validationError.NONE) - return err; - - var kVersionSizes = [ - {version: 0, numBytes: 16} - ]; - err = messageValidator.validateStructVersion(offset, kVersionSizes); - if (err !== validator.validationError.NONE) - return err; - - - // validate UsbDeviceManagerClient_OnDeviceRemoved_Params.deviceInfo - err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, device$.UsbDeviceInfo, false); - if (err !== validator.validationError.NONE) - return err; - - return validator.validationError.NONE; - }; - - UsbDeviceManagerClient_OnDeviceRemoved_Params.encodedSize = codec.kStructHeaderSize + 8; - - UsbDeviceManagerClient_OnDeviceRemoved_Params.decode = function(decoder) { - var packed; - var val = new UsbDeviceManagerClient_OnDeviceRemoved_Params(); - var numberOfBytes = decoder.readUint32(); - var version = decoder.readUint32(); - val.deviceInfo = decoder.decodeStructPointer(device$.UsbDeviceInfo); - return val; - }; - - UsbDeviceManagerClient_OnDeviceRemoved_Params.encode = function(encoder, val) { - var packed; - encoder.writeUint32(UsbDeviceManagerClient_OnDeviceRemoved_Params.encodedSize); - encoder.writeUint32(0); - encoder.encodeStructPointer(device$.UsbDeviceInfo, val.deviceInfo); - }; - var kUsbDeviceManager_GetDevices_Name = 0; - var kUsbDeviceManager_GetDevice_Name = 1; - var kUsbDeviceManager_SetClient_Name = 2; - - function UsbDeviceManagerPtr(handleOrPtrInfo) { - this.ptr = new bindings.InterfacePtrController(UsbDeviceManager, - handleOrPtrInfo); - } - - function UsbDeviceManagerAssociatedPtr(associatedInterfacePtrInfo) { - this.ptr = new associatedBindings.AssociatedInterfacePtrController( - UsbDeviceManager, associatedInterfacePtrInfo); - } - - UsbDeviceManagerAssociatedPtr.prototype = - Object.create(UsbDeviceManagerPtr.prototype); - UsbDeviceManagerAssociatedPtr.prototype.constructor = - UsbDeviceManagerAssociatedPtr; - - function UsbDeviceManagerProxy(receiver) { - this.receiver_ = receiver; - } - UsbDeviceManagerPtr.prototype.getDevices = function() { - return UsbDeviceManagerProxy.prototype.getDevices - .apply(this.ptr.getProxy(), arguments); - }; - - UsbDeviceManagerProxy.prototype.getDevices = function(options) { - var params = new UsbDeviceManager_GetDevices_Params(); - params.options = options; - return new Promise(function(resolve, reject) { - var builder = new codec.MessageV1Builder( - kUsbDeviceManager_GetDevices_Name, - codec.align(UsbDeviceManager_GetDevices_Params.encodedSize), - codec.kMessageExpectsResponse, 0); - builder.encodeStruct(UsbDeviceManager_GetDevices_Params, params); - var message = builder.finish(); - this.receiver_.acceptAndExpectResponse(message).then(function(message) { - var reader = new codec.MessageReader(message); - var responseParams = - reader.decodeStruct(UsbDeviceManager_GetDevices_ResponseParams); - resolve(responseParams); - }).catch(function(result) { - reject(Error("Connection error: " + result)); - }); - }.bind(this)); - }; - UsbDeviceManagerPtr.prototype.getDevice = function() { - return UsbDeviceManagerProxy.prototype.getDevice - .apply(this.ptr.getProxy(), arguments); - }; - - UsbDeviceManagerProxy.prototype.getDevice = function(guid, deviceRequest) { - var params = new UsbDeviceManager_GetDevice_Params(); - params.guid = guid; - params.deviceRequest = deviceRequest; - var builder = new codec.MessageV0Builder( - kUsbDeviceManager_GetDevice_Name, - codec.align(UsbDeviceManager_GetDevice_Params.encodedSize)); - builder.encodeStruct(UsbDeviceManager_GetDevice_Params, params); - var message = builder.finish(); - this.receiver_.accept(message); - }; - UsbDeviceManagerPtr.prototype.setClient = function() { - return UsbDeviceManagerProxy.prototype.setClient - .apply(this.ptr.getProxy(), arguments); - }; - - UsbDeviceManagerProxy.prototype.setClient = function(client) { - var params = new UsbDeviceManager_SetClient_Params(); - params.client = client; - var builder = new codec.MessageV0Builder( - kUsbDeviceManager_SetClient_Name, - codec.align(UsbDeviceManager_SetClient_Params.encodedSize)); - builder.encodeStruct(UsbDeviceManager_SetClient_Params, params); - var message = builder.finish(); - this.receiver_.accept(message); - }; - - function UsbDeviceManagerStub(delegate) { - this.delegate_ = delegate; - } - UsbDeviceManagerStub.prototype.getDevices = function(options) { - return this.delegate_ && this.delegate_.getDevices && this.delegate_.getDevices(options); - } - UsbDeviceManagerStub.prototype.getDevice = function(guid, deviceRequest) { - return this.delegate_ && this.delegate_.getDevice && this.delegate_.getDevice(guid, deviceRequest); - } - UsbDeviceManagerStub.prototype.setClient = function(client) { - return this.delegate_ && this.delegate_.setClient && this.delegate_.setClient(client); - } - - UsbDeviceManagerStub.prototype.accept = function(message) { - var reader = new codec.MessageReader(message); - switch (reader.messageName) { - case kUsbDeviceManager_GetDevice_Name: - var params = reader.decodeStruct(UsbDeviceManager_GetDevice_Params); - this.getDevice(params.guid, params.deviceRequest); - return true; - case kUsbDeviceManager_SetClient_Name: - var params = reader.decodeStruct(UsbDeviceManager_SetClient_Params); - this.setClient(params.client); - return true; - default: - return false; - } - }; - - UsbDeviceManagerStub.prototype.acceptWithResponder = - function(message, responder) { - var reader = new codec.MessageReader(message); - switch (reader.messageName) { - case kUsbDeviceManager_GetDevices_Name: - var params = reader.decodeStruct(UsbDeviceManager_GetDevices_Params); - this.getDevices(params.options).then(function(response) { - var responseParams = - new UsbDeviceManager_GetDevices_ResponseParams(); - responseParams.results = response.results; - var builder = new codec.MessageV1Builder( - kUsbDeviceManager_GetDevices_Name, - codec.align(UsbDeviceManager_GetDevices_ResponseParams.encodedSize), - codec.kMessageIsResponse, reader.requestID); - builder.encodeStruct(UsbDeviceManager_GetDevices_ResponseParams, - responseParams); - var message = builder.finish(); - responder.accept(message); - }); - return true; - default: - return false; - } - }; - - function validateUsbDeviceManagerRequest(messageValidator) { - var message = messageValidator.message; - var paramsClass = null; - switch (message.getName()) { - case kUsbDeviceManager_GetDevices_Name: - if (message.expectsResponse()) - paramsClass = UsbDeviceManager_GetDevices_Params; - break; - case kUsbDeviceManager_GetDevice_Name: - if (!message.expectsResponse() && !message.isResponse()) - paramsClass = UsbDeviceManager_GetDevice_Params; - break; - case kUsbDeviceManager_SetClient_Name: - if (!message.expectsResponse() && !message.isResponse()) - paramsClass = UsbDeviceManager_SetClient_Params; - break; - } - if (paramsClass === null) - return validator.validationError.NONE; - return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes()); - } - - function validateUsbDeviceManagerResponse(messageValidator) { - var message = messageValidator.message; - var paramsClass = null; - switch (message.getName()) { - case kUsbDeviceManager_GetDevices_Name: - if (message.isResponse()) - paramsClass = UsbDeviceManager_GetDevices_ResponseParams; - break; - } - if (paramsClass === null) - return validator.validationError.NONE; - return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes()); - } - - var UsbDeviceManager = { - name: 'device.mojom.UsbDeviceManager', - kVersion: 0, - ptrClass: UsbDeviceManagerPtr, - proxyClass: UsbDeviceManagerProxy, - stubClass: UsbDeviceManagerStub, - validateRequest: validateUsbDeviceManagerRequest, - validateResponse: validateUsbDeviceManagerResponse, - }; - UsbDeviceManagerStub.prototype.validator = validateUsbDeviceManagerRequest; - UsbDeviceManagerProxy.prototype.validator = validateUsbDeviceManagerResponse; - var kUsbDeviceManagerClient_OnDeviceAdded_Name = 0; - var kUsbDeviceManagerClient_OnDeviceRemoved_Name = 1; - - function UsbDeviceManagerClientPtr(handleOrPtrInfo) { - this.ptr = new bindings.InterfacePtrController(UsbDeviceManagerClient, - handleOrPtrInfo); - } - - function UsbDeviceManagerClientAssociatedPtr(associatedInterfacePtrInfo) { - this.ptr = new associatedBindings.AssociatedInterfacePtrController( - UsbDeviceManagerClient, associatedInterfacePtrInfo); - } - - UsbDeviceManagerClientAssociatedPtr.prototype = - Object.create(UsbDeviceManagerClientPtr.prototype); - UsbDeviceManagerClientAssociatedPtr.prototype.constructor = - UsbDeviceManagerClientAssociatedPtr; - - function UsbDeviceManagerClientProxy(receiver) { - this.receiver_ = receiver; - } - UsbDeviceManagerClientPtr.prototype.onDeviceAdded = function() { - return UsbDeviceManagerClientProxy.prototype.onDeviceAdded - .apply(this.ptr.getProxy(), arguments); - }; - - UsbDeviceManagerClientProxy.prototype.onDeviceAdded = function(deviceInfo) { - var params = new UsbDeviceManagerClient_OnDeviceAdded_Params(); - params.deviceInfo = deviceInfo; - var builder = new codec.MessageV0Builder( - kUsbDeviceManagerClient_OnDeviceAdded_Name, - codec.align(UsbDeviceManagerClient_OnDeviceAdded_Params.encodedSize)); - builder.encodeStruct(UsbDeviceManagerClient_OnDeviceAdded_Params, params); - var message = builder.finish(); - this.receiver_.accept(message); - }; - UsbDeviceManagerClientPtr.prototype.onDeviceRemoved = function() { - return UsbDeviceManagerClientProxy.prototype.onDeviceRemoved - .apply(this.ptr.getProxy(), arguments); - }; - - UsbDeviceManagerClientProxy.prototype.onDeviceRemoved = function(deviceInfo) { - var params = new UsbDeviceManagerClient_OnDeviceRemoved_Params(); - params.deviceInfo = deviceInfo; - var builder = new codec.MessageV0Builder( - kUsbDeviceManagerClient_OnDeviceRemoved_Name, - codec.align(UsbDeviceManagerClient_OnDeviceRemoved_Params.encodedSize)); - builder.encodeStruct(UsbDeviceManagerClient_OnDeviceRemoved_Params, params); - var message = builder.finish(); - this.receiver_.accept(message); - }; - - function UsbDeviceManagerClientStub(delegate) { - this.delegate_ = delegate; - } - UsbDeviceManagerClientStub.prototype.onDeviceAdded = function(deviceInfo) { - return this.delegate_ && this.delegate_.onDeviceAdded && this.delegate_.onDeviceAdded(deviceInfo); - } - UsbDeviceManagerClientStub.prototype.onDeviceRemoved = function(deviceInfo) { - return this.delegate_ && this.delegate_.onDeviceRemoved && this.delegate_.onDeviceRemoved(deviceInfo); - } - - UsbDeviceManagerClientStub.prototype.accept = function(message) { - var reader = new codec.MessageReader(message); - switch (reader.messageName) { - case kUsbDeviceManagerClient_OnDeviceAdded_Name: - var params = reader.decodeStruct(UsbDeviceManagerClient_OnDeviceAdded_Params); - this.onDeviceAdded(params.deviceInfo); - return true; - case kUsbDeviceManagerClient_OnDeviceRemoved_Name: - var params = reader.decodeStruct(UsbDeviceManagerClient_OnDeviceRemoved_Params); - this.onDeviceRemoved(params.deviceInfo); - return true; - default: - return false; - } - }; - - UsbDeviceManagerClientStub.prototype.acceptWithResponder = - function(message, responder) { - var reader = new codec.MessageReader(message); - switch (reader.messageName) { - default: - return false; - } - }; - - function validateUsbDeviceManagerClientRequest(messageValidator) { - var message = messageValidator.message; - var paramsClass = null; - switch (message.getName()) { - case kUsbDeviceManagerClient_OnDeviceAdded_Name: - if (!message.expectsResponse() && !message.isResponse()) - paramsClass = UsbDeviceManagerClient_OnDeviceAdded_Params; - break; - case kUsbDeviceManagerClient_OnDeviceRemoved_Name: - if (!message.expectsResponse() && !message.isResponse()) - paramsClass = UsbDeviceManagerClient_OnDeviceRemoved_Params; - break; - } - if (paramsClass === null) - return validator.validationError.NONE; - return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes()); - } - - function validateUsbDeviceManagerClientResponse(messageValidator) { - return validator.validationError.NONE; - } - - var UsbDeviceManagerClient = { - name: 'device.mojom.UsbDeviceManagerClient', - kVersion: 0, - ptrClass: UsbDeviceManagerClientPtr, - proxyClass: UsbDeviceManagerClientProxy, - stubClass: UsbDeviceManagerClientStub, - validateRequest: validateUsbDeviceManagerClientRequest, - validateResponse: null, - }; - UsbDeviceManagerClientStub.prototype.validator = validateUsbDeviceManagerClientRequest; - UsbDeviceManagerClientProxy.prototype.validator = null; - exports.UsbDeviceFilter = UsbDeviceFilter; - exports.UsbEnumerationOptions = UsbEnumerationOptions; - exports.UsbDeviceManager = UsbDeviceManager; - exports.UsbDeviceManagerPtr = UsbDeviceManagerPtr; - exports.UsbDeviceManagerAssociatedPtr = UsbDeviceManagerAssociatedPtr; - exports.UsbDeviceManagerClient = UsbDeviceManagerClient; - exports.UsbDeviceManagerClientPtr = UsbDeviceManagerClientPtr; - exports.UsbDeviceManagerClientAssociatedPtr = UsbDeviceManagerClientAssociatedPtr; -})();
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager_client.mojom.js b/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager_client.mojom.js new file mode 100644 index 0000000..439f646 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager_client.mojom.js
@@ -0,0 +1,262 @@ +// 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. + +'use strict'; + +(function() { + var mojomId = 'device/usb/public/mojom/device_manager_client.mojom'; + if (mojo.internal.isMojomLoaded(mojomId)) { + console.warn('The following mojom is loaded multiple times: ' + mojomId); + return; + } + mojo.internal.markMojomLoaded(mojomId); + var bindings = mojo; + var associatedBindings = mojo; + var codec = mojo.internal; + var validator = mojo.internal; + + var exports = mojo.internal.exposeNamespace('device.mojom'); + var device$ = + mojo.internal.exposeNamespace('device.mojom'); + if (mojo.config.autoLoadMojomDeps) { + mojo.internal.loadMojomIfNecessary( + 'device/usb/public/mojom/device.mojom', 'device.mojom.js'); + } + + + + function UsbDeviceManagerClient_OnDeviceAdded_Params(values) { + this.initDefaults_(); + this.initFields_(values); + } + + + UsbDeviceManagerClient_OnDeviceAdded_Params.prototype.initDefaults_ = function() { + this.deviceInfo = null; + }; + UsbDeviceManagerClient_OnDeviceAdded_Params.prototype.initFields_ = function(fields) { + for(var field in fields) { + if (this.hasOwnProperty(field)) + this[field] = fields[field]; + } + }; + + UsbDeviceManagerClient_OnDeviceAdded_Params.validate = function(messageValidator, offset) { + var err; + err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); + if (err !== validator.validationError.NONE) + return err; + + var kVersionSizes = [ + {version: 0, numBytes: 16} + ]; + err = messageValidator.validateStructVersion(offset, kVersionSizes); + if (err !== validator.validationError.NONE) + return err; + + + // validate UsbDeviceManagerClient_OnDeviceAdded_Params.deviceInfo + err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, device$.UsbDeviceInfo, false); + if (err !== validator.validationError.NONE) + return err; + + return validator.validationError.NONE; + }; + + UsbDeviceManagerClient_OnDeviceAdded_Params.encodedSize = codec.kStructHeaderSize + 8; + + UsbDeviceManagerClient_OnDeviceAdded_Params.decode = function(decoder) { + var packed; + var val = new UsbDeviceManagerClient_OnDeviceAdded_Params(); + var numberOfBytes = decoder.readUint32(); + var version = decoder.readUint32(); + val.deviceInfo = decoder.decodeStructPointer(device$.UsbDeviceInfo); + return val; + }; + + UsbDeviceManagerClient_OnDeviceAdded_Params.encode = function(encoder, val) { + var packed; + encoder.writeUint32(UsbDeviceManagerClient_OnDeviceAdded_Params.encodedSize); + encoder.writeUint32(0); + encoder.encodeStructPointer(device$.UsbDeviceInfo, val.deviceInfo); + }; + function UsbDeviceManagerClient_OnDeviceRemoved_Params(values) { + this.initDefaults_(); + this.initFields_(values); + } + + + UsbDeviceManagerClient_OnDeviceRemoved_Params.prototype.initDefaults_ = function() { + this.deviceInfo = null; + }; + UsbDeviceManagerClient_OnDeviceRemoved_Params.prototype.initFields_ = function(fields) { + for(var field in fields) { + if (this.hasOwnProperty(field)) + this[field] = fields[field]; + } + }; + + UsbDeviceManagerClient_OnDeviceRemoved_Params.validate = function(messageValidator, offset) { + var err; + err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); + if (err !== validator.validationError.NONE) + return err; + + var kVersionSizes = [ + {version: 0, numBytes: 16} + ]; + err = messageValidator.validateStructVersion(offset, kVersionSizes); + if (err !== validator.validationError.NONE) + return err; + + + // validate UsbDeviceManagerClient_OnDeviceRemoved_Params.deviceInfo + err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, device$.UsbDeviceInfo, false); + if (err !== validator.validationError.NONE) + return err; + + return validator.validationError.NONE; + }; + + UsbDeviceManagerClient_OnDeviceRemoved_Params.encodedSize = codec.kStructHeaderSize + 8; + + UsbDeviceManagerClient_OnDeviceRemoved_Params.decode = function(decoder) { + var packed; + var val = new UsbDeviceManagerClient_OnDeviceRemoved_Params(); + var numberOfBytes = decoder.readUint32(); + var version = decoder.readUint32(); + val.deviceInfo = decoder.decodeStructPointer(device$.UsbDeviceInfo); + return val; + }; + + UsbDeviceManagerClient_OnDeviceRemoved_Params.encode = function(encoder, val) { + var packed; + encoder.writeUint32(UsbDeviceManagerClient_OnDeviceRemoved_Params.encodedSize); + encoder.writeUint32(0); + encoder.encodeStructPointer(device$.UsbDeviceInfo, val.deviceInfo); + }; + var kUsbDeviceManagerClient_OnDeviceAdded_Name = 0; + var kUsbDeviceManagerClient_OnDeviceRemoved_Name = 1; + + function UsbDeviceManagerClientPtr(handleOrPtrInfo) { + this.ptr = new bindings.InterfacePtrController(UsbDeviceManagerClient, + handleOrPtrInfo); + } + + function UsbDeviceManagerClientAssociatedPtr(associatedInterfacePtrInfo) { + this.ptr = new associatedBindings.AssociatedInterfacePtrController( + UsbDeviceManagerClient, associatedInterfacePtrInfo); + } + + UsbDeviceManagerClientAssociatedPtr.prototype = + Object.create(UsbDeviceManagerClientPtr.prototype); + UsbDeviceManagerClientAssociatedPtr.prototype.constructor = + UsbDeviceManagerClientAssociatedPtr; + + function UsbDeviceManagerClientProxy(receiver) { + this.receiver_ = receiver; + } + UsbDeviceManagerClientPtr.prototype.onDeviceAdded = function() { + return UsbDeviceManagerClientProxy.prototype.onDeviceAdded + .apply(this.ptr.getProxy(), arguments); + }; + + UsbDeviceManagerClientProxy.prototype.onDeviceAdded = function(deviceInfo) { + var params_ = new UsbDeviceManagerClient_OnDeviceAdded_Params(); + params_.deviceInfo = deviceInfo; + var builder = new codec.MessageV0Builder( + kUsbDeviceManagerClient_OnDeviceAdded_Name, + codec.align(UsbDeviceManagerClient_OnDeviceAdded_Params.encodedSize)); + builder.encodeStruct(UsbDeviceManagerClient_OnDeviceAdded_Params, params_); + var message = builder.finish(); + this.receiver_.accept(message); + }; + UsbDeviceManagerClientPtr.prototype.onDeviceRemoved = function() { + return UsbDeviceManagerClientProxy.prototype.onDeviceRemoved + .apply(this.ptr.getProxy(), arguments); + }; + + UsbDeviceManagerClientProxy.prototype.onDeviceRemoved = function(deviceInfo) { + var params_ = new UsbDeviceManagerClient_OnDeviceRemoved_Params(); + params_.deviceInfo = deviceInfo; + var builder = new codec.MessageV0Builder( + kUsbDeviceManagerClient_OnDeviceRemoved_Name, + codec.align(UsbDeviceManagerClient_OnDeviceRemoved_Params.encodedSize)); + builder.encodeStruct(UsbDeviceManagerClient_OnDeviceRemoved_Params, params_); + var message = builder.finish(); + this.receiver_.accept(message); + }; + + function UsbDeviceManagerClientStub(delegate) { + this.delegate_ = delegate; + } + UsbDeviceManagerClientStub.prototype.onDeviceAdded = function(deviceInfo) { + return this.delegate_ && this.delegate_.onDeviceAdded && this.delegate_.onDeviceAdded(deviceInfo); + } + UsbDeviceManagerClientStub.prototype.onDeviceRemoved = function(deviceInfo) { + return this.delegate_ && this.delegate_.onDeviceRemoved && this.delegate_.onDeviceRemoved(deviceInfo); + } + + UsbDeviceManagerClientStub.prototype.accept = function(message) { + var reader = new codec.MessageReader(message); + switch (reader.messageName) { + case kUsbDeviceManagerClient_OnDeviceAdded_Name: + var params = reader.decodeStruct(UsbDeviceManagerClient_OnDeviceAdded_Params); + this.onDeviceAdded(params.deviceInfo); + return true; + case kUsbDeviceManagerClient_OnDeviceRemoved_Name: + var params = reader.decodeStruct(UsbDeviceManagerClient_OnDeviceRemoved_Params); + this.onDeviceRemoved(params.deviceInfo); + return true; + default: + return false; + } + }; + + UsbDeviceManagerClientStub.prototype.acceptWithResponder = + function(message, responder) { + var reader = new codec.MessageReader(message); + switch (reader.messageName) { + default: + return false; + } + }; + + function validateUsbDeviceManagerClientRequest(messageValidator) { + var message = messageValidator.message; + var paramsClass = null; + switch (message.getName()) { + case kUsbDeviceManagerClient_OnDeviceAdded_Name: + if (!message.expectsResponse() && !message.isResponse()) + paramsClass = UsbDeviceManagerClient_OnDeviceAdded_Params; + break; + case kUsbDeviceManagerClient_OnDeviceRemoved_Name: + if (!message.expectsResponse() && !message.isResponse()) + paramsClass = UsbDeviceManagerClient_OnDeviceRemoved_Params; + break; + } + if (paramsClass === null) + return validator.validationError.NONE; + return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes()); + } + + function validateUsbDeviceManagerClientResponse(messageValidator) { + return validator.validationError.NONE; + } + + var UsbDeviceManagerClient = { + name: 'device.mojom.UsbDeviceManagerClient', + kVersion: 0, + ptrClass: UsbDeviceManagerClientPtr, + proxyClass: UsbDeviceManagerClientProxy, + stubClass: UsbDeviceManagerClientStub, + validateRequest: validateUsbDeviceManagerClientRequest, + validateResponse: null, + }; + UsbDeviceManagerClientStub.prototype.validator = validateUsbDeviceManagerClientRequest; + UsbDeviceManagerClientProxy.prototype.validator = null; + exports.UsbDeviceManagerClient = UsbDeviceManagerClient; + exports.UsbDeviceManagerClientPtr = UsbDeviceManagerClientPtr; + exports.UsbDeviceManagerClientAssociatedPtr = UsbDeviceManagerClientAssociatedPtr; +})(); \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js.headers b/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager_client.mojom.js.headers similarity index 97% copy from third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js.headers copy to third_party/blink/web_tests/external/wpt/resources/chromium/device_manager_client.mojom.js.headers index 6805c323..6c61a34 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager.mojom.js.headers +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/device_manager_client.mojom.js.headers
@@ -1 +1 @@ -Content-Type: text/javascript; charset=utf-8 +Content-Type: text/javascript; charset=utf-8 \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/mojo_bindings.js b/third_party/blink/web_tests/external/wpt/resources/chromium/mojo_bindings.js index 67d6a88..b3e3020 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/mojo_bindings.js +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/mojo_bindings.js
@@ -4,14 +4,21 @@ 'use strict'; -if (mojo && mojo.internal) { +if ((typeof mojo !== 'undefined') && mojo.bindingsLibraryInitialized) { throw new Error('The Mojo bindings library has been initialized.'); } var mojo = mojo || {}; -mojo.internal = {}; -mojo.internal.global = this; -mojo.config = { +mojo.bindingsLibraryInitialized = true; + +mojo.internal = mojo.internal || {}; + +mojo.config = mojo.config || {}; +if (typeof mojo.config.global === 'undefined') { + mojo.config.global = this; +} + +if (typeof mojo.config.autoLoadMojomDeps === 'undefined') { // Whether to automatically load mojom dependencies. // For example, if foo.mojom imports bar.mojom, |autoLoadMojomDeps| set to // true means that loading foo.mojom.js will insert a <script> tag to load @@ -52,11 +59,12 @@ // <script src="http://example.org/scripts/b/d/bar.mojom.js"></script> // // --> - autoLoadMojomDeps: true -}; + mojo.config.autoLoadMojomDeps = true; +} (function() { var internal = mojo.internal; + var config = mojo.config; var LoadState = { PENDING_LOAD: 1, @@ -66,7 +74,7 @@ var mojomRegistry = new Map(); function exposeNamespace(namespace) { - var current = internal.global; + var current = config.global; var parts = namespace.split('.'); for (var part; parts.length && (part = parts.shift());) { @@ -104,7 +112,7 @@ return; } - if (internal.global.document === undefined) { + if (config.global.document === undefined) { throw new Error( 'Mojom dependency autoloading is not implemented in workers. ' + 'Please see config variable mojo.config.autoLoadMojomDeps for more ' + @@ -113,8 +121,8 @@ markMojomPendingLoad(id); var url = new URL(relativePath, document.currentScript.src).href; - internal.global.document.write('<script type="text/javascript" src="' + - url + '"><' + '/script>'); + config.global.document.write('<script type="text/javascript" src="' + + url + '"><' + '/script>'); } internal.exposeNamespace = exposeNamespace; @@ -672,6 +680,103 @@ mojo.BindingSet = BindingSet; mojo.InterfacePtrController = InterfacePtrController; })(); +// Copyright 2016 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. + +(function() { + var internal = mojo.internal; + + // Constants ---------------------------------------------------------------- + var kInterfaceIdNamespaceMask = 0x80000000; + var kMasterInterfaceId = 0x00000000; + var kInvalidInterfaceId = 0xFFFFFFFF; + + // --------------------------------------------------------------------------- + + function InterfacePtrInfo(handle, version) { + this.handle = handle; + this.version = version; + } + + InterfacePtrInfo.prototype.isValid = function() { + return this.handle instanceof MojoHandle; + }; + + InterfacePtrInfo.prototype.close = function() { + if (!this.isValid()) + return; + + this.handle.close(); + this.handle = null; + this.version = 0; + }; + + function AssociatedInterfacePtrInfo(interfaceEndpointHandle, version) { + this.interfaceEndpointHandle = interfaceEndpointHandle; + this.version = version; + } + + AssociatedInterfacePtrInfo.prototype.isValid = function() { + return this.interfaceEndpointHandle.isValid(); + }; + + // --------------------------------------------------------------------------- + + function InterfaceRequest(handle) { + this.handle = handle; + } + + InterfaceRequest.prototype.isValid = function() { + return this.handle instanceof MojoHandle; + }; + + InterfaceRequest.prototype.close = function() { + if (!this.isValid()) + return; + + this.handle.close(); + this.handle = null; + }; + + function AssociatedInterfaceRequest(interfaceEndpointHandle) { + this.interfaceEndpointHandle = interfaceEndpointHandle; + } + + AssociatedInterfaceRequest.prototype.isValid = function() { + return this.interfaceEndpointHandle.isValid(); + }; + + AssociatedInterfaceRequest.prototype.resetWithReason = function(reason) { + this.interfaceEndpointHandle.reset(reason); + }; + + function isMasterInterfaceId(interfaceId) { + return interfaceId === kMasterInterfaceId; + } + + function isValidInterfaceId(interfaceId) { + return interfaceId !== kInvalidInterfaceId; + } + + function hasInterfaceIdNamespaceBitSet(interfaceId) { + if (interfaceId >= 2 * kInterfaceIdNamespaceMask) { + throw new Error("Interface ID should be a 32-bit unsigned integer."); + } + return interfaceId >= kInterfaceIdNamespaceMask; + } + + mojo.InterfacePtrInfo = InterfacePtrInfo; + mojo.InterfaceRequest = InterfaceRequest; + mojo.AssociatedInterfacePtrInfo = AssociatedInterfacePtrInfo; + mojo.AssociatedInterfaceRequest = AssociatedInterfaceRequest; + internal.isMasterInterfaceId = isMasterInterfaceId; + internal.isValidInterfaceId = isValidInterfaceId; + internal.hasInterfaceIdNamespaceBitSet = hasInterfaceIdNamespaceBitSet; + internal.kInvalidInterfaceId = kInvalidInterfaceId; + internal.kMasterInterfaceId = kMasterInterfaceId; + internal.kInterfaceIdNamespaceMask = kInterfaceIdNamespaceMask; +})(); // 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. @@ -2071,6 +2176,10 @@ var receiverResult = this.incomingReceiver_ && this.incomingReceiver_.accept(message); + // Dispatching the message may have closed the connector. + if (this.handle_ == null) + return; + // Handle invalid incoming message. if (!internal.isTestingMode() && !receiverResult) { // TODO(yzshen): Consider notifying the embedder. @@ -2131,95 +2240,6 @@ internal.Connector = Connector; })(); -// Copyright 2016 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. - -(function() { - var internal = mojo.internal; - - // Constants ---------------------------------------------------------------- - var kInterfaceIdNamespaceMask = 0x80000000; - var kMasterInterfaceId = 0x00000000; - var kInvalidInterfaceId = 0xFFFFFFFF; - - // --------------------------------------------------------------------------- - - function InterfacePtrInfo(handle, version) { - this.handle = handle; - this.version = version; - } - - InterfacePtrInfo.prototype.isValid = function() { - return this.handle instanceof MojoHandle; - }; - - InterfacePtrInfo.prototype.close = function() { - if (!this.isValid()) - return; - - this.handle.close(); - this.handle = null; - this.version = 0; - }; - - function AssociatedInterfacePtrInfo(interfaceEndpointHandle, version) { - this.interfaceEndpointHandle = interfaceEndpointHandle; - this.version = version; - } - - AssociatedInterfacePtrInfo.prototype.isValid = function() { - return this.interfaceEndpointHandle.isValid(); - }; - - // --------------------------------------------------------------------------- - - function InterfaceRequest(handle) { - this.handle = handle; - } - - InterfaceRequest.prototype.isValid = function() { - return this.handle instanceof MojoHandle; - }; - - InterfaceRequest.prototype.close = function() { - if (!this.isValid()) - return; - - this.handle.close(); - this.handle = null; - }; - - function AssociatedInterfaceRequest(interfaceEndpointHandle) { - this.interfaceEndpointHandle = interfaceEndpointHandle; - } - - AssociatedInterfaceRequest.prototype.isValid = function() { - return this.interfaceEndpointHandle.isValid(); - }; - - AssociatedInterfaceRequest.prototype.resetWithReason = function(reason) { - this.interfaceEndpointHandle.reset(reason); - }; - - function isMasterInterfaceId(interfaceId) { - return interfaceId === kMasterInterfaceId; - } - - function isValidInterfaceId(interfaceId) { - return interfaceId !== kInvalidInterfaceId; - } - - mojo.InterfacePtrInfo = InterfacePtrInfo; - mojo.InterfaceRequest = InterfaceRequest; - mojo.AssociatedInterfacePtrInfo = AssociatedInterfacePtrInfo; - mojo.AssociatedInterfaceRequest = AssociatedInterfaceRequest; - internal.isMasterInterfaceId = isMasterInterfaceId; - internal.isValidInterfaceId = isValidInterfaceId; - internal.kInvalidInterfaceId = kInvalidInterfaceId; - internal.kMasterInterfaceId = kMasterInterfaceId; - internal.kInterfaceIdNamespaceMask = kInterfaceIdNamespaceMask; -})(); // 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. @@ -2234,12 +2254,12 @@ throw error; } - if (message.getName() != mojo.interfaceControl2.kRunMessageId) { + if (message.getName() != mojo.interfaceControl.kRunMessageId) { throw new Error("Control message name is not kRunMessageId"); } // Validate payload. - error = mojo.interfaceControl2.RunMessageParams.validate(messageValidator, + error = mojo.interfaceControl.RunMessageParams.validate(messageValidator, message.getHeaderNumBytes()); if (error != internal.validationError.NONE) { throw error; @@ -2253,12 +2273,12 @@ throw error; } - if (message.getName() != mojo.interfaceControl2.kRunOrClosePipeMessageId) { + if (message.getName() != mojo.interfaceControl.kRunOrClosePipeMessageId) { throw new Error("Control message name is not kRunOrClosePipeMessageId"); } // Validate payload. - error = mojo.interfaceControl2.RunOrClosePipeMessageParams.validate( + error = mojo.interfaceControl.RunOrClosePipeMessageParams.validate( messageValidator, message.getHeaderNumBytes()); if (error != internal.validationError.NONE) { throw error; @@ -2268,7 +2288,7 @@ function runOrClosePipe(message, interfaceVersion) { var reader = new internal.MessageReader(message); var runOrClosePipeMessageParams = reader.decodeStruct( - mojo.interfaceControl2.RunOrClosePipeMessageParams); + mojo.interfaceControl.RunOrClosePipeMessageParams); return interfaceVersion >= runOrClosePipeMessageParams.input.requireVersion.version; } @@ -2276,35 +2296,35 @@ function run(message, responder, interfaceVersion) { var reader = new internal.MessageReader(message); var runMessageParams = - reader.decodeStruct(mojo.interfaceControl2.RunMessageParams); + reader.decodeStruct(mojo.interfaceControl.RunMessageParams); var runOutput = null; if (runMessageParams.input.queryVersion) { - runOutput = new mojo.interfaceControl2.RunOutput(); + runOutput = new mojo.interfaceControl.RunOutput(); runOutput.queryVersionResult = new - mojo.interfaceControl2.QueryVersionResult( + mojo.interfaceControl.QueryVersionResult( {'version': interfaceVersion}); } var runResponseMessageParams = new - mojo.interfaceControl2.RunResponseMessageParams(); + mojo.interfaceControl.RunResponseMessageParams(); runResponseMessageParams.output = runOutput; - var messageName = mojo.interfaceControl2.kRunMessageId; + var messageName = mojo.interfaceControl.kRunMessageId; var payloadSize = - mojo.interfaceControl2.RunResponseMessageParams.encodedSize; + mojo.interfaceControl.RunResponseMessageParams.encodedSize; var requestID = reader.requestID; var builder = new internal.MessageV1Builder(messageName, payloadSize, internal.kMessageIsResponse, requestID); - builder.encodeStruct(mojo.interfaceControl2.RunResponseMessageParams, + builder.encodeStruct(mojo.interfaceControl.RunResponseMessageParams, runResponseMessageParams); responder.accept(builder.finish()); return true; } function isInterfaceControlMessage(message) { - return message.getName() == mojo.interfaceControl2.kRunMessageId || - message.getName() == mojo.interfaceControl2.kRunOrClosePipeMessageId; + return message.getName() == mojo.interfaceControl.kRunMessageId || + message.getName() == mojo.interfaceControl.kRunOrClosePipeMessageId; } function ControlMessageHandler(interfaceVersion) { @@ -2334,14 +2354,14 @@ function constructRunOrClosePipeMessage(runOrClosePipeInput) { var runOrClosePipeMessageParams = new - mojo.interfaceControl2.RunOrClosePipeMessageParams(); + mojo.interfaceControl.RunOrClosePipeMessageParams(); runOrClosePipeMessageParams.input = runOrClosePipeInput; - var messageName = mojo.interfaceControl2.kRunOrClosePipeMessageId; + var messageName = mojo.interfaceControl.kRunOrClosePipeMessageId; var payloadSize = - mojo.interfaceControl2.RunOrClosePipeMessageParams.encodedSize; + mojo.interfaceControl.RunOrClosePipeMessageParams.encodedSize; var builder = new internal.MessageV0Builder(messageName, payloadSize); - builder.encodeStruct(mojo.interfaceControl2.RunOrClosePipeMessageParams, + builder.encodeStruct(mojo.interfaceControl.RunOrClosePipeMessageParams, runOrClosePipeMessageParams); var message = builder.finish(); return message; @@ -2354,12 +2374,12 @@ throw error; } - if (message.getName() != mojo.interfaceControl2.kRunMessageId) { + if (message.getName() != mojo.interfaceControl.kRunMessageId) { throw new Error("Control message name is not kRunMessageId"); } // Validate payload. - error = mojo.interfaceControl2.RunResponseMessageParams.validate( + error = mojo.interfaceControl.RunResponseMessageParams.validate( messageValidator, message.getHeaderNumBytes()); if (error != internal.validationError.NONE) { throw error; @@ -2371,7 +2391,7 @@ var reader = new internal.MessageReader(message); var runResponseMessageParams = reader.decodeStruct( - mojo.interfaceControl2.RunResponseMessageParams); + mojo.interfaceControl.RunResponseMessageParams); return Promise.resolve(runResponseMessageParams); } @@ -2386,12 +2406,12 @@ * @return {Promise} that resolves to a RunResponseMessageParams. */ function sendRunMessage(receiver, runMessageParams) { - var messageName = mojo.interfaceControl2.kRunMessageId; - var payloadSize = mojo.interfaceControl2.RunMessageParams.encodedSize; + var messageName = mojo.interfaceControl.kRunMessageId; + var payloadSize = mojo.interfaceControl.RunMessageParams.encodedSize; // |requestID| is set to 0, but is later properly set by Router. var builder = new internal.MessageV1Builder(messageName, payloadSize, internal.kMessageExpectsResponse, 0); - builder.encodeStruct(mojo.interfaceControl2.RunMessageParams, + builder.encodeStruct(mojo.interfaceControl.RunMessageParams, runMessageParams); var message = builder.finish(); @@ -2403,10 +2423,10 @@ } ControlMessageProxy.prototype.queryVersion = function() { - var runMessageParams = new mojo.interfaceControl2.RunMessageParams(); - runMessageParams.input = new mojo.interfaceControl2.RunInput(); + var runMessageParams = new mojo.interfaceControl.RunMessageParams(); + runMessageParams.input = new mojo.interfaceControl.RunInput(); runMessageParams.input.queryVersion = - new mojo.interfaceControl2.QueryVersion(); + new mojo.interfaceControl.QueryVersion(); return sendRunMessage(this.receiver_, runMessageParams).then(function( runResponseMessageParams) { @@ -2415,9 +2435,9 @@ }; ControlMessageProxy.prototype.requireVersion = function(version) { - var runOrClosePipeInput = new mojo.interfaceControl2.RunOrClosePipeInput(); + var runOrClosePipeInput = new mojo.interfaceControl.RunOrClosePipeInput(); runOrClosePipeInput.requireVersion = - new mojo.interfaceControl2.RequireVersion({'version': version}); + new mojo.interfaceControl.RequireVersion({'version': version}); var message = constructRunOrClosePipeMessage(runOrClosePipeInput); this.receiver_.accept(message); }; @@ -2841,12 +2861,12 @@ throw error; } - if (message.getName() != mojo.pipeControl2.kRunOrClosePipeMessageId) { + if (message.getName() != mojo.pipeControl.kRunOrClosePipeMessageId) { throw new Error("Control message name is not kRunOrClosePipeMessageId"); } // Validate payload. - error = mojo.pipeControl2.RunOrClosePipeMessageParams.validate( + error = mojo.pipeControl.RunOrClosePipeMessageParams.validate( messageValidator, message.getHeaderNumBytes()); if (error != internal.validationError.NONE) { throw error; @@ -2856,7 +2876,7 @@ function runOrClosePipe(message, delegate) { var reader = new internal.MessageReader(message); var runOrClosePipeMessageParams = reader.decodeStruct( - mojo.pipeControl2.RunOrClosePipeMessageParams); + mojo.pipeControl.RunOrClosePipeMessageParams); var event = runOrClosePipeMessageParams.input .peerAssociatedEndpointClosedEvent; return delegate.onPeerAssociatedEndpointClosed(event.id, @@ -2888,15 +2908,15 @@ function constructRunOrClosePipeMessage(runOrClosePipeInput) { var runOrClosePipeMessageParams = new - mojo.pipeControl2.RunOrClosePipeMessageParams(); + mojo.pipeControl.RunOrClosePipeMessageParams(); runOrClosePipeMessageParams.input = runOrClosePipeInput; - var messageName = mojo.pipeControl2.kRunOrClosePipeMessageId; + var messageName = mojo.pipeControl.kRunOrClosePipeMessageId; var payloadSize = - mojo.pipeControl2.RunOrClosePipeMessageParams.encodedSize; + mojo.pipeControl.RunOrClosePipeMessageParams.encodedSize; var builder = new internal.MessageV0Builder(messageName, payloadSize); - builder.encodeStruct(mojo.pipeControl2.RunOrClosePipeMessageParams, + builder.encodeStruct(mojo.pipeControl.RunOrClosePipeMessageParams, runOrClosePipeMessageParams); var message = builder.finish(); message.setInterfaceId(internal.kInvalidInterfaceId); @@ -2915,14 +2935,14 @@ PipeControlMessageProxy.prototype.constructPeerEndpointClosedMessage = function(interfaceId, reason) { - var event = new mojo.pipeControl2.PeerAssociatedEndpointClosedEvent(); + var event = new mojo.pipeControl.PeerAssociatedEndpointClosedEvent(); event.id = interfaceId; if (reason) { - event.disconnectReason = new mojo.pipeControl2.DisconnectReason({ + event.disconnectReason = new mojo.pipeControl.DisconnectReason({ customReason: reason.customReason, description: reason.description}); } - var runOrClosePipeInput = new mojo.pipeControl2.RunOrClosePipeInput(); + var runOrClosePipeInput = new mojo.pipeControl.RunOrClosePipeInput(); runOrClosePipeInput.peerAssociatedEndpointClosedEvent = event; return constructRunOrClosePipeMessage(runOrClosePipeInput); }; @@ -3096,6 +3116,15 @@ return new internal.InterfaceEndpointHandle(); } + // Unless it is the master ID, |interfaceId| is from the remote side and + // therefore its namespace bit is supposed to be different than the value + // that this router would use. + if (!internal.isMasterInterfaceId(interfaceId) && + this.setInterfaceIdNamespaceBit_ === + internal.hasInterfaceIdNamespaceBitSet(interfaceId)) { + return new internal.InterfaceEndpointHandle(); + } + var endpoint = this.endpoints_.get(interfaceId); if (!endpoint) { @@ -3164,8 +3193,6 @@ Router.prototype.onPeerAssociatedEndpointClosed = function(interfaceId, reason) { - check(!internal.isMasterInterfaceId(interfaceId) || reason); - var endpoint = this.endpoints_.get(interfaceId); if (!endpoint) { endpoint = new InterfaceEndpoint(this, interfaceId); @@ -3250,6 +3277,8 @@ */ (function() { var internal = mojo.internal; + var textDecoder = new TextDecoder('utf-8'); + var textEncoder = new TextEncoder('utf-8'); /** * Decodes the UTF8 string from the given buffer. @@ -3257,7 +3286,7 @@ * @return {string} The corresponding JavaScript string. */ function decodeUtf8String(buffer) { - return decodeURIComponent(escape(String.fromCharCode.apply(null, buffer))); + return textDecoder.decode(buffer); } /** @@ -3269,12 +3298,11 @@ * @return {number} The number of bytes written to |outputBuffer|. */ function encodeUtf8String(str, outputBuffer) { - var utf8String = unescape(encodeURIComponent(str)); - if (outputBuffer.length < utf8String.length) + const utf8Buffer = textEncoder.encode(str); + if (outputBuffer.length < utf8Buffer.length) throw new Error("Buffer too small for encodeUtf8String"); - for (var i = 0; i < outputBuffer.length && i < utf8String.length; i++) - outputBuffer[i] = utf8String.charCodeAt(i); - return i; + outputBuffer.set(utf8Buffer); + return utf8Buffer.length; } /** @@ -3282,8 +3310,7 @@ * |str| would occupy. */ function utf8Length(str) { - var utf8String = unescape(encodeURIComponent(str)); - return utf8String.length; + return textEncoder.encode(str).length; } internal.decodeUtf8String = decodeUtf8String; @@ -3972,25 +3999,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - 'use strict'; (function() { - var mojomId = 'mojo/public/interfaces/bindings/new_bindings/interface_control_messages.mojom'; + var mojomId = 'mojo/public/interfaces/bindings/interface_control_messages.mojom'; if (mojo.internal.isMojomLoaded(mojomId)) { console.warn('The following mojom is loaded multiple times: ' + mojomId); return; } mojo.internal.markMojomLoaded(mojomId); - - // TODO(yzshen): Define these aliases to minimize the differences between the - // old/new modes. Remove them when the old mode goes away. var bindings = mojo; var associatedBindings = mojo; var codec = mojo.internal; var validator = mojo.internal; - var exports = mojo.internal.exposeNamespace('mojo.interfaceControl2'); + var exports = mojo.internal.exposeNamespace('mojo.interfaceControl'); var kRunMessageId = 0xFFFFFFFF; @@ -4374,42 +4397,42 @@ this.initDefault_(); this.initValue_(value); } - - + + RunInput.Tags = { queryVersion: 0, flushForTesting: 1, }; - + RunInput.prototype.initDefault_ = function() { this.$data = null; this.$tag = undefined; } - + RunInput.prototype.initValue_ = function(value) { if (value == undefined) { return; } - + var keys = Object.keys(value); if (keys.length == 0) { return; } - + if (keys.length > 1) { throw new TypeError("You may set only one member on a union."); } - + var fields = [ "queryVersion", "flushForTesting", ]; - + if (fields.indexOf(keys[0]) < 0) { throw new ReferenceError(keys[0] + " is not a RunInput member."); - + } - + this[keys[0]] = value[keys[0]]; } Object.defineProperty(RunInput.prototype, "queryVersion", { @@ -4420,7 +4443,7 @@ } return this.$data; }, - + set: function(value) { this.$tag = RunInput.Tags.queryVersion; this.$data = value; @@ -4434,14 +4457,14 @@ } return this.$data; }, - + set: function(value) { this.$tag = RunInput.Tags.flushForTesting; this.$data = value; } }); - - + + RunInput.encode = function(encoder, val) { if (val == null) { encoder.writeUint64(0); @@ -4451,7 +4474,7 @@ if (val.$tag == undefined) { throw new TypeError("Cannot encode unions with an unknown member set."); } - + encoder.writeUint32(16); encoder.writeUint32(val.$tag); switch (val.$tag) { @@ -4464,8 +4487,8 @@ } encoder.align(); }; - - + + RunInput.decode = function(decoder) { var size = decoder.readUint32(); if (size == 0) { @@ -4473,7 +4496,7 @@ decoder.readUint64(); return null; } - + var result = new RunInput(); var tag = decoder.readUint32(); switch (tag) { @@ -4485,24 +4508,24 @@ break; } decoder.align(); - + return result; }; - - + + RunInput.validate = function(messageValidator, offset) { var size = messageValidator.decodeUnionSize(offset); if (size != 16) { return validator.validationError.INVALID_UNION_SIZE; } - + var tag = messageValidator.decodeUnionTag(offset); var data_offset = offset + 8; var err; switch (tag) { case RunInput.Tags.queryVersion: - + // validate RunInput.queryVersion err = messageValidator.validateStructPointer(data_offset, QueryVersion, false); if (err !== validator.validationError.NONE) @@ -4510,57 +4533,57 @@ break; case RunInput.Tags.flushForTesting: - + // validate RunInput.flushForTesting err = messageValidator.validateStructPointer(data_offset, FlushForTesting, false); if (err !== validator.validationError.NONE) return err; break; } - + return validator.validationError.NONE; }; - + RunInput.encodedSize = 16; function RunOutput(value) { this.initDefault_(); this.initValue_(value); } - - + + RunOutput.Tags = { queryVersionResult: 0, }; - + RunOutput.prototype.initDefault_ = function() { this.$data = null; this.$tag = undefined; } - + RunOutput.prototype.initValue_ = function(value) { if (value == undefined) { return; } - + var keys = Object.keys(value); if (keys.length == 0) { return; } - + if (keys.length > 1) { throw new TypeError("You may set only one member on a union."); } - + var fields = [ "queryVersionResult", ]; - + if (fields.indexOf(keys[0]) < 0) { throw new ReferenceError(keys[0] + " is not a RunOutput member."); - + } - + this[keys[0]] = value[keys[0]]; } Object.defineProperty(RunOutput.prototype, "queryVersionResult", { @@ -4571,14 +4594,14 @@ } return this.$data; }, - + set: function(value) { this.$tag = RunOutput.Tags.queryVersionResult; this.$data = value; } }); - - + + RunOutput.encode = function(encoder, val) { if (val == null) { encoder.writeUint64(0); @@ -4588,7 +4611,7 @@ if (val.$tag == undefined) { throw new TypeError("Cannot encode unions with an unknown member set."); } - + encoder.writeUint32(16); encoder.writeUint32(val.$tag); switch (val.$tag) { @@ -4598,8 +4621,8 @@ } encoder.align(); }; - - + + RunOutput.decode = function(decoder) { var size = decoder.readUint32(); if (size == 0) { @@ -4607,7 +4630,7 @@ decoder.readUint64(); return null; } - + var result = new RunOutput(); var tag = decoder.readUint32(); switch (tag) { @@ -4616,74 +4639,74 @@ break; } decoder.align(); - + return result; }; - - + + RunOutput.validate = function(messageValidator, offset) { var size = messageValidator.decodeUnionSize(offset); if (size != 16) { return validator.validationError.INVALID_UNION_SIZE; } - + var tag = messageValidator.decodeUnionTag(offset); var data_offset = offset + 8; var err; switch (tag) { case RunOutput.Tags.queryVersionResult: - + // validate RunOutput.queryVersionResult err = messageValidator.validateStructPointer(data_offset, QueryVersionResult, false); if (err !== validator.validationError.NONE) return err; break; } - + return validator.validationError.NONE; }; - + RunOutput.encodedSize = 16; function RunOrClosePipeInput(value) { this.initDefault_(); this.initValue_(value); } - - + + RunOrClosePipeInput.Tags = { requireVersion: 0, }; - + RunOrClosePipeInput.prototype.initDefault_ = function() { this.$data = null; this.$tag = undefined; } - + RunOrClosePipeInput.prototype.initValue_ = function(value) { if (value == undefined) { return; } - + var keys = Object.keys(value); if (keys.length == 0) { return; } - + if (keys.length > 1) { throw new TypeError("You may set only one member on a union."); } - + var fields = [ "requireVersion", ]; - + if (fields.indexOf(keys[0]) < 0) { throw new ReferenceError(keys[0] + " is not a RunOrClosePipeInput member."); - + } - + this[keys[0]] = value[keys[0]]; } Object.defineProperty(RunOrClosePipeInput.prototype, "requireVersion", { @@ -4694,14 +4717,14 @@ } return this.$data; }, - + set: function(value) { this.$tag = RunOrClosePipeInput.Tags.requireVersion; this.$data = value; } }); - - + + RunOrClosePipeInput.encode = function(encoder, val) { if (val == null) { encoder.writeUint64(0); @@ -4711,7 +4734,7 @@ if (val.$tag == undefined) { throw new TypeError("Cannot encode unions with an unknown member set."); } - + encoder.writeUint32(16); encoder.writeUint32(val.$tag); switch (val.$tag) { @@ -4721,8 +4744,8 @@ } encoder.align(); }; - - + + RunOrClosePipeInput.decode = function(decoder) { var size = decoder.readUint32(); if (size == 0) { @@ -4730,7 +4753,7 @@ decoder.readUint64(); return null; } - + var result = new RunOrClosePipeInput(); var tag = decoder.readUint32(); switch (tag) { @@ -4739,34 +4762,34 @@ break; } decoder.align(); - + return result; }; - - + + RunOrClosePipeInput.validate = function(messageValidator, offset) { var size = messageValidator.decodeUnionSize(offset); if (size != 16) { return validator.validationError.INVALID_UNION_SIZE; } - + var tag = messageValidator.decodeUnionTag(offset); var data_offset = offset + 8; var err; switch (tag) { case RunOrClosePipeInput.Tags.requireVersion: - + // validate RunOrClosePipeInput.requireVersion err = messageValidator.validateStructPointer(data_offset, RequireVersion, false); if (err !== validator.validationError.NONE) return err; break; } - + return validator.validationError.NONE; }; - + RunOrClosePipeInput.encodedSize = 16; exports.kRunMessageId = kRunMessageId; exports.kRunOrClosePipeMessageId = kRunOrClosePipeMessageId; @@ -4784,25 +4807,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - 'use strict'; (function() { - var mojomId = 'mojo/public/interfaces/bindings/new_bindings/pipe_control_messages.mojom'; + var mojomId = 'mojo/public/interfaces/bindings/pipe_control_messages.mojom'; if (mojo.internal.isMojomLoaded(mojomId)) { console.warn('The following mojom is loaded multiple times: ' + mojomId); return; } mojo.internal.markMojomLoaded(mojomId); - - // TODO(yzshen): Define these aliases to minimize the differences between the - // old/new modes. Remove them when the old mode goes away. var bindings = mojo; var associatedBindings = mojo; var codec = mojo.internal; var validator = mojo.internal; - var exports = mojo.internal.exposeNamespace('mojo.pipeControl2'); + var exports = mojo.internal.exposeNamespace('mojo.pipeControl'); var kRunOrClosePipeMessageId = 0xFFFFFFFE; @@ -4894,7 +4913,6 @@ - // validate DisconnectReason.description err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 8, false) if (err !== validator.validationError.NONE) @@ -4962,7 +4980,6 @@ - // validate PeerAssociatedEndpointClosedEvent.disconnectReason err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 8, DisconnectReason, true); if (err !== validator.validationError.NONE) @@ -5003,40 +5020,40 @@ this.initDefault_(); this.initValue_(value); } - - + + RunOrClosePipeInput.Tags = { peerAssociatedEndpointClosedEvent: 0, }; - + RunOrClosePipeInput.prototype.initDefault_ = function() { this.$data = null; this.$tag = undefined; } - + RunOrClosePipeInput.prototype.initValue_ = function(value) { if (value == undefined) { return; } - + var keys = Object.keys(value); if (keys.length == 0) { return; } - + if (keys.length > 1) { throw new TypeError("You may set only one member on a union."); } - + var fields = [ "peerAssociatedEndpointClosedEvent", ]; - + if (fields.indexOf(keys[0]) < 0) { throw new ReferenceError(keys[0] + " is not a RunOrClosePipeInput member."); - + } - + this[keys[0]] = value[keys[0]]; } Object.defineProperty(RunOrClosePipeInput.prototype, "peerAssociatedEndpointClosedEvent", { @@ -5047,14 +5064,14 @@ } return this.$data; }, - + set: function(value) { this.$tag = RunOrClosePipeInput.Tags.peerAssociatedEndpointClosedEvent; this.$data = value; } }); - - + + RunOrClosePipeInput.encode = function(encoder, val) { if (val == null) { encoder.writeUint64(0); @@ -5064,7 +5081,7 @@ if (val.$tag == undefined) { throw new TypeError("Cannot encode unions with an unknown member set."); } - + encoder.writeUint32(16); encoder.writeUint32(val.$tag); switch (val.$tag) { @@ -5074,8 +5091,8 @@ } encoder.align(); }; - - + + RunOrClosePipeInput.decode = function(decoder) { var size = decoder.readUint32(); if (size == 0) { @@ -5083,7 +5100,7 @@ decoder.readUint64(); return null; } - + var result = new RunOrClosePipeInput(); var tag = decoder.readUint32(); switch (tag) { @@ -5092,34 +5109,34 @@ break; } decoder.align(); - + return result; }; - - + + RunOrClosePipeInput.validate = function(messageValidator, offset) { var size = messageValidator.decodeUnionSize(offset); if (size != 16) { return validator.validationError.INVALID_UNION_SIZE; } - + var tag = messageValidator.decodeUnionTag(offset); var data_offset = offset + 8; var err; switch (tag) { case RunOrClosePipeInput.Tags.peerAssociatedEndpointClosedEvent: - + // validate RunOrClosePipeInput.peerAssociatedEndpointClosedEvent err = messageValidator.validateStructPointer(data_offset, PeerAssociatedEndpointClosedEvent, false); if (err !== validator.validationError.NONE) return err; break; } - + return validator.validationError.NONE; }; - + RunOrClosePipeInput.encodedSize = 16; exports.kRunOrClosePipeMessageId = kRunOrClosePipeMessageId; exports.RunOrClosePipeMessageParams = RunOrClosePipeMessageParams;
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/string16.mojom.js b/third_party/blink/web_tests/external/wpt/resources/chromium/string16.mojom.js index 9dbb356..25377e6 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/string16.mojom.js +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/string16.mojom.js
@@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - 'use strict'; -if ((typeof mojo !== 'undefined') && mojo.internal && mojo.config) { (function() { var mojomId = 'mojo/public/mojom/base/string16.mojom'; @@ -13,15 +11,18 @@ return; } mojo.internal.markMojomLoaded(mojomId); - - // TODO(yzshen): Define these aliases to minimize the differences between the - // old/new modes. Remove them when the old mode goes away. var bindings = mojo; var associatedBindings = mojo; var codec = mojo.internal; var validator = mojo.internal; - var exports = mojo.internal.exposeNamespace('mojo.common.mojom'); + var exports = mojo.internal.exposeNamespace('mojoBase.mojom'); + var big_buffer$ = + mojo.internal.exposeNamespace('mojoBase.mojom'); + if (mojo.config.autoLoadMojomDeps) { + mojo.internal.loadMojomIfNecessary( + 'mojo/public/mojom/base/big_buffer.mojom', 'big_buffer.mojom.js'); + } @@ -55,7 +56,6 @@ return err; - // validate String16.data err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 2, codec.Uint16, false, [0], 0); if (err !== validator.validationError.NONE) @@ -81,79 +81,61 @@ encoder.writeUint32(0); encoder.encodeArrayPointer(codec.Uint16, val.data); }; - exports.String16 = String16; -})(); -} - -if ((typeof mojo === 'undefined') || !mojo.internal || !mojo.config) { - -define("mojo/public/mojom/base/string16.mojom", [ - "mojo/public/js/associated_bindings", - "mojo/public/js/bindings", - "mojo/public/js/codec", - "mojo/public/js/core", - "mojo/public/js/validator", -], function(associatedBindings, bindings, codec, core, validator) { - var exports = {}; - - function String16(values) { + function BigString16(values) { this.initDefaults_(); this.initFields_(values); } - String16.prototype.initDefaults_ = function() { + BigString16.prototype.initDefaults_ = function() { this.data = null; }; - String16.prototype.initFields_ = function(fields) { + BigString16.prototype.initFields_ = function(fields) { for(var field in fields) { if (this.hasOwnProperty(field)) this[field] = fields[field]; } }; - String16.validate = function(messageValidator, offset) { + BigString16.validate = function(messageValidator, offset) { var err; err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); if (err !== validator.validationError.NONE) return err; var kVersionSizes = [ - {version: 0, numBytes: 16} + {version: 0, numBytes: 24} ]; err = messageValidator.validateStructVersion(offset, kVersionSizes); if (err !== validator.validationError.NONE) return err; - - // validate String16.data - err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 2, codec.Uint16, false, [0], 0); + // validate BigString16.data + err = messageValidator.validateUnion(offset + codec.kStructHeaderSize + 0, big_buffer$.BigBuffer, false); if (err !== validator.validationError.NONE) return err; return validator.validationError.NONE; }; - String16.encodedSize = codec.kStructHeaderSize + 8; + BigString16.encodedSize = codec.kStructHeaderSize + 16; - String16.decode = function(decoder) { + BigString16.decode = function(decoder) { var packed; - var val = new String16(); + var val = new BigString16(); var numberOfBytes = decoder.readUint32(); var version = decoder.readUint32(); - val.data = decoder.decodeArrayPointer(codec.Uint16); + val.data = decoder.decodeStruct(big_buffer$.BigBuffer); return val; }; - String16.encode = function(encoder, val) { + BigString16.encode = function(encoder, val) { var packed; - encoder.writeUint32(String16.encodedSize); + encoder.writeUint32(BigString16.encodedSize); encoder.writeUint32(0); - encoder.encodeArrayPointer(codec.Uint16, val.data); + encoder.encodeStruct(big_buffer$.BigBuffer, val.data); }; exports.String16 = String16; - - return exports; -}); -} \ No newline at end of file + exports.BigString16 = BigString16; +})(); \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/url.mojom.js b/third_party/blink/web_tests/external/wpt/resources/chromium/url.mojom.js index abe7d00..c5eee1f 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/url.mojom.js +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/url.mojom.js
@@ -76,4 +76,4 @@ encoder.encodeStruct(codec.String, val.url); }; exports.Url = Url; -})(); +})(); \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/web_usb_service.mojom.js b/third_party/blink/web_tests/external/wpt/resources/chromium/web_usb_service.mojom.js index bacad8f..a4a017b1 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/web_usb_service.mojom.js +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/web_usb_service.mojom.js
@@ -23,11 +23,17 @@ mojo.internal.loadMojomIfNecessary( 'device/usb/public/mojom/device.mojom', '../../../../../device/usb/public/mojom/device.mojom.js'); } - var device_manager$ = + var device_enumeration_options$ = mojo.internal.exposeNamespace('device.mojom'); if (mojo.config.autoLoadMojomDeps) { mojo.internal.loadMojomIfNecessary( - 'device/usb/public/mojom/device_manager.mojom', '../../../../../device/usb/public/mojom/device_manager.mojom.js'); + 'device/usb/public/mojom/device_enumeration_options.mojom', '../../../../../device/usb/public/mojom/device_enumeration_options.mojom.js'); + } + var device_manager_client$ = + mojo.internal.exposeNamespace('device.mojom'); + if (mojo.config.autoLoadMojomDeps) { + mojo.internal.loadMojomIfNecessary( + 'device/usb/public/mojom/device_manager_client.mojom', '../../../../../device/usb/public/mojom/device_manager_client.mojom.js'); } @@ -236,7 +242,7 @@ // validate WebUsbService_GetPermission_Params.deviceFilters - err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 8, new codec.PointerTo(device_manager$.UsbDeviceFilter), false, [0], 0); + err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 8, new codec.PointerTo(device_enumeration_options$.UsbDeviceFilter), false, [0], 0); if (err !== validator.validationError.NONE) return err; @@ -250,7 +256,7 @@ var val = new WebUsbService_GetPermission_Params(); var numberOfBytes = decoder.readUint32(); var version = decoder.readUint32(); - val.deviceFilters = decoder.decodeArrayPointer(new codec.PointerTo(device_manager$.UsbDeviceFilter)); + val.deviceFilters = decoder.decodeArrayPointer(new codec.PointerTo(device_enumeration_options$.UsbDeviceFilter)); return val; }; @@ -258,7 +264,7 @@ var packed; encoder.writeUint32(WebUsbService_GetPermission_Params.encodedSize); encoder.writeUint32(0); - encoder.encodeArrayPointer(new codec.PointerTo(device_manager$.UsbDeviceFilter), val.deviceFilters); + encoder.encodeArrayPointer(new codec.PointerTo(device_enumeration_options$.UsbDeviceFilter), val.deviceFilters); }; function WebUsbService_GetPermission_ResponseParams(values) { this.initDefaults_(); @@ -605,4 +611,4 @@ exports.WebUsbService = WebUsbService; exports.WebUsbServicePtr = WebUsbServicePtr; exports.WebUsbServiceAssociatedPtr = WebUsbServiceAssociatedPtr; -})(); +})(); \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webusb/resources/usb-helpers.js b/third_party/blink/web_tests/external/wpt/webusb/resources/usb-helpers.js index 0abca16..67d907d 100644 --- a/third_party/blink/web_tests/external/wpt/webusb/resources/usb-helpers.js +++ b/third_party/blink/web_tests/external/wpt/webusb/resources/usb-helpers.js
@@ -42,10 +42,12 @@ let chain = Promise.resolve(); [ '/resources/chromium/mojo_bindings.js', + '/resources/chromium/big_buffer.mojom.js', '/resources/chromium/string16.mojom.js', '/resources/chromium/url.mojom.js', '/resources/chromium/device.mojom.js', - '/resources/chromium/device_manager.mojom.js', + '/resources/chromium/device_enumeration_options.mojom.js', + '/resources/chromium/device_manager_client.mojom.js', '/resources/chromium/web_usb_service.mojom.js', '/resources/chromium/webusb-test.js', ].forEach(path => {
diff --git a/third_party/blink/web_tests/fast/lists/add-inline-child-after-marker-expected.html b/third_party/blink/web_tests/fast/lists/add-inline-child-after-marker-expected.html deleted file mode 100644 index 20c879eb..0000000 --- a/third_party/blink/web_tests/fast/lists/add-inline-child-after-marker-expected.html +++ /dev/null
@@ -1,8 +0,0 @@ -<head></head><body><ul> - <li id="liTarget"> - <span>inline</span> - <div id="divTarget" style="overflow:hidden;"> - <span>a</span>xxx - </div> - </li> -</ul>
diff --git a/third_party/blink/web_tests/fast/lists/add-inline-child-after-marker.html b/third_party/blink/web_tests/fast/lists/add-inline-child-after-marker.html deleted file mode 100644 index c635d44..0000000 --- a/third_party/blink/web_tests/fast/lists/add-inline-child-after-marker.html +++ /dev/null
@@ -1,17 +0,0 @@ -<ul> - <li id="liTarget"> - <div id="divTarget" style="overflow:hidden;"> - <span>a</span>xxx - </div> - </li> -</ul> -<script> - var new_span=document.createElement("span"); - var text_node=document.createTextNode("inline"); - new_span.appendChild(text_node); - - var div_target=document.getElementById("divTarget"); - var li_target=document.getElementById("liTarget"); - li_target.insertBefore(new_span,div_target); - document.body.offsetHeight; -</script>
diff --git a/third_party/blink/web_tests/fast/lists/li-with-overflow-hidden-change-list-style-position-expected.html b/third_party/blink/web_tests/fast/lists/li-with-overflow-hidden-change-list-style-position-expected.html deleted file mode 100644 index aacff1b..0000000 --- a/third_party/blink/web_tests/fast/lists/li-with-overflow-hidden-change-list-style-position-expected.html +++ /dev/null
@@ -1,15 +0,0 @@ -<ul> - <li style="list-style-position: inside;"> - <div style="overflow:hidden;"> - outside to inside - </div> - </li> -</ul> - -<ul> - <li style="list-style-position: outside;"> - <div style="overflow:hidden;"> - inside to outside - </div> - </li> -</ul>
diff --git a/third_party/blink/web_tests/fast/lists/li-with-overflow-hidden-change-list-style-position.html b/third_party/blink/web_tests/fast/lists/li-with-overflow-hidden-change-list-style-position.html deleted file mode 100644 index 524d6db2..0000000 --- a/third_party/blink/web_tests/fast/lists/li-with-overflow-hidden-change-list-style-position.html +++ /dev/null
@@ -1,27 +0,0 @@ -<ul> - <li id="outSide" style="list-style-position: outside;"> - <div style="overflow:hidden;"> - outside to inside - </div> - </li> -</ul> - -<ul> - <li id="inSide" style="list-style-position: inside;"> - <div style="overflow:hidden;"> - inside to outside - </div> - </li> -</ul> -<script> - document.body.offsetHeight; - - var outside_li=document.getElementById("outSide"); - outside_li.style.listStylePosition = "inside"; - document.body.offsetHeight; - - var inside_li=document.getElementById("inSide"); - inside_li.style.listStylePosition = "outside"; - document.body.offsetHeight; - -</script>
diff --git a/third_party/blink/web_tests/fast/lists/list-and-block-textarea-expected.html b/third_party/blink/web_tests/fast/lists/list-and-block-textarea-expected.html deleted file mode 100644 index 2ae2839..0000000 --- a/third_party/blink/web_tests/fast/lists/list-and-block-textarea-expected.html +++ /dev/null
@@ -1 +0,0 @@ -pass
diff --git a/third_party/blink/web_tests/fast/lists/list-and-block-textarea.html b/third_party/blink/web_tests/fast/lists/list-and-block-textarea.html deleted file mode 100644 index b37979c..0000000 --- a/third_party/blink/web_tests/fast/lists/list-and-block-textarea.html +++ /dev/null
@@ -1,15 +0,0 @@ -<ul> - <li id="target"> - <textarea rows="3" cols="20" style="display:block; height:45px"> - hello - </textarea> - </li> -</ul> - -<script> - document.body.offsetHeight; - if (document.getElementById("target").offsetHeight == 45) - document.body.innerHTML="pass"; - else - document.body.innerHTML="fail"; -</script>
diff --git a/third_party/blink/web_tests/fast/lists/list-and-flex-expected.html b/third_party/blink/web_tests/fast/lists/list-and-flex-expected.html deleted file mode 100644 index 07ad4cc..0000000 --- a/third_party/blink/web_tests/fast/lists/list-and-flex-expected.html +++ /dev/null
@@ -1,9 +0,0 @@ -<ul> - <li> - <div style="border: 1px black solid;"> - <div style="display: inline-flex; align-items: flex-end; height: 200px;"> - <span style="line-height: 50px">text</span> - </div> - </div> - </li> -</ul>
diff --git a/third_party/blink/web_tests/fast/lists/list-and-flex.html b/third_party/blink/web_tests/fast/lists/list-and-flex.html deleted file mode 100644 index 1917138..0000000 --- a/third_party/blink/web_tests/fast/lists/list-and-flex.html +++ /dev/null
@@ -1,9 +0,0 @@ -<ul> - <li> - <div style="border: 1px black solid;"> - <div style="display: flex; align-items: flex-end; height: 200px;"> - <span style="line-height: 50px">text</span> - </div> - </div> - </li> -</ul>
diff --git a/third_party/blink/web_tests/fast/lists/list-and-grid-expected.html b/third_party/blink/web_tests/fast/lists/list-and-grid-expected.html deleted file mode 100644 index 0a5686fc..0000000 --- a/third_party/blink/web_tests/fast/lists/list-and-grid-expected.html +++ /dev/null
@@ -1,8 +0,0 @@ -<ul> - <li> - <div style="display: inline-grid; grid-template-rows: 100px; align-items: center;"> - <div>grid</div> - </div> - </li> -</ul> -
diff --git a/third_party/blink/web_tests/fast/lists/list-and-grid.html b/third_party/blink/web_tests/fast/lists/list-and-grid.html deleted file mode 100644 index 68f727fe..0000000 --- a/third_party/blink/web_tests/fast/lists/list-and-grid.html +++ /dev/null
@@ -1,7 +0,0 @@ -<ul> - <li> - <div style="display: grid; grid-template-rows: 100px; align-items: center;"> - <div>grid</div> - </div> - </li> -</ul>
diff --git a/third_party/blink/web_tests/fast/lists/list-and-margin-collapse.html b/third_party/blink/web_tests/fast/lists/list-and-margin-collapse.html deleted file mode 100644 index 2905721..0000000 --- a/third_party/blink/web_tests/fast/lists/list-and-margin-collapse.html +++ /dev/null
@@ -1,14 +0,0 @@ -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<ul id="target" style="margin-top:100px;"> - <li> - <div style="overflow:hidden; margin-top:100px; height:25px;"><a href="#">xxx</a></div> - </li> -</ul> -<div id="log"></div> -<script> -test(function() { - var height = document.getElementById("target").clientHeight; - assert_equals(height, 25, "the height of ul should be 25px") -}, "list and margin collapse"); -</script>
diff --git a/third_party/blink/web_tests/fast/lists/list-and-writing-mode-expected.html b/third_party/blink/web_tests/fast/lists/list-and-writing-mode-expected.html deleted file mode 100644 index 2ae2839..0000000 --- a/third_party/blink/web_tests/fast/lists/list-and-writing-mode-expected.html +++ /dev/null
@@ -1 +0,0 @@ -pass
diff --git a/third_party/blink/web_tests/fast/lists/list-and-writing-mode.html b/third_party/blink/web_tests/fast/lists/list-and-writing-mode.html deleted file mode 100644 index 0c45987a..0000000 --- a/third_party/blink/web_tests/fast/lists/list-and-writing-mode.html +++ /dev/null
@@ -1,14 +0,0 @@ -<ul> - <li id="target"> - <div style="writing-mode: vertical-lr; height: 45px;">a b c</div> - </li> -</ul> - -<script> - document.body.offsetHeight; - if (document.getElementById("target").offsetHeight == 45) - document.body.innerHTML="pass"; - else - document.body.innerHTML="fail"; -</script> -
diff --git a/third_party/blink/web_tests/fast/lists/list-marker-with-lineheight-and-overflow-hidden-expected.html b/third_party/blink/web_tests/fast/lists/list-marker-with-lineheight-and-overflow-hidden-expected.html deleted file mode 100644 index 049af61b..0000000 --- a/third_party/blink/web_tests/fast/lists/list-marker-with-lineheight-and-overflow-hidden-expected.html +++ /dev/null
@@ -1,13 +0,0 @@ -<ul> - <li> - <div style="line-height:100px;"> - <span>aaa</span> - </div> - </li> -</ul> -<ul> - <li style="list-style-image: url(resources/white.gif);"> - <div style="line-height:100px;">One</div> - </li> -</ul> -
diff --git a/third_party/blink/web_tests/fast/lists/list-marker-with-lineheight-and-overflow-hidden.html b/third_party/blink/web_tests/fast/lists/list-marker-with-lineheight-and-overflow-hidden.html deleted file mode 100644 index 73d3237e..0000000 --- a/third_party/blink/web_tests/fast/lists/list-marker-with-lineheight-and-overflow-hidden.html +++ /dev/null
@@ -1,13 +0,0 @@ -<ul> - <li> - <div style="overflow:hidden; line-height:100px;"> - <span>aaa</span> - </div> - </li> -</ul> - -<ul> - <li style="list-style-image: url(resources/white.gif);"> - <div style="overflow:hidden; line-height:100px;">One</div> - </li> -</ul>
diff --git a/third_party/blink/web_tests/fast/lists/list-with-image-display-changed-expected.html b/third_party/blink/web_tests/fast/lists/list-with-image-display-changed-expected.html deleted file mode 100644 index 31c2a79d..0000000 --- a/third_party/blink/web_tests/fast/lists/list-with-image-display-changed-expected.html +++ /dev/null
@@ -1,11 +0,0 @@ -<!DOCTYPE html> -<style> -li { border: 3px solid red; margin: 3px; } -img { display: block; } -</style> -<ul> - <li> - <a href="#"><img src="./resources/white.gif" width=32 height=32 /></a> - Some other text - </li> -</ul>
diff --git a/third_party/blink/web_tests/fast/lists/list-with-image-display-changed.html b/third_party/blink/web_tests/fast/lists/list-with-image-display-changed.html deleted file mode 100644 index 99218067..0000000 --- a/third_party/blink/web_tests/fast/lists/list-with-image-display-changed.html +++ /dev/null
@@ -1,19 +0,0 @@ -<!DOCTYPE html> -<style> -li { border: 3px solid red; margin: 3px; } -img { display: block; } -</style> -<ul> - <li> - <a href="#"><img src="./resources/white.gif" width=32 height=32 /></a> - Some other text - </li> -</ul> -<script> - document.body.offsetTop; - var img = document.querySelector('a img'); - img.style.display = 'inline'; - img.offsetWidth; - img.style.display = 'block'; -</script> -
diff --git a/third_party/blink/web_tests/fast/text/ellipsis-in-relative-inline-right.html b/third_party/blink/web_tests/fast/text/ellipsis-in-relative-inline-right.html deleted file mode 100644 index 8d9ecf6..0000000 --- a/third_party/blink/web_tests/fast/text/ellipsis-in-relative-inline-right.html +++ /dev/null
@@ -1,31 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<style> -div { - overflow: hidden; - text-overflow: ellipsis; - width: 100px; - white-space: pre; - font-size: 20px; - border: 1px solid black; - font-family: arial; -} -.rtl { - direction: rtl; -} -span { - position: relative; - right: 2em; -} -</style> -<p>crbug.com/737837: Place ellipsis correctly when inline has relative position.</p> -<p>These should have an ellipsis.</p> -<div class="rtl"><span>abcdefghijklmnop</span></div> -<div class="rtl"><span>abcdefghijklm</span></div> -<div class="rtl"><span>אבגדהוזחטיכךללכךלכךל</span></div> -<div class="rtl"><span>אבגדהוזחטיכךל</span></div> -<p>These should have no ellipsis.</p> -<div><span>abcdefghij</span></div> -<div><span>abcde</span></div> -<div><span>אבגדהוזחטיכךל</span></div> -<div><span>אבגדהו</span></div>
diff --git a/third_party/blink/web_tests/fast/text/ellipsis-in-relative-inline.html b/third_party/blink/web_tests/fast/text/ellipsis-in-relative-inline.html deleted file mode 100644 index 0bb6afce..0000000 --- a/third_party/blink/web_tests/fast/text/ellipsis-in-relative-inline.html +++ /dev/null
@@ -1,30 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<style> -div { - overflow: hidden; - text-overflow: ellipsis; - width: 100px; - white-space: pre; - font-size: 20px; - border: 1px solid black; - font-family: arial; -} -.rtl { - direction: rtl; -} -span { - position: relative; - left: 2em; -} -</style> -<p>crbug.com/737837: Place ellipsis correctly when inline has relative position.</p> -<div><span>abcdefghij</span></div> -<div class="rtl"><span>abcdefghijklmnop</span></div> -<div><span>אבגדהוזחטיכךל</span></div> -<div class="rtl"><span>אבגדהוזחטיכךללכךלכךל</span></div> -<p>These should have no ellipsis.</p> -<div><span>abcde</span></div> -<div class="rtl"><span>abcdefghijklm</span></div> -<div><span>אבגדהו</span></div> -<div class="rtl"><span>אבגדהוזחטיכךל</span></div>
diff --git a/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt b/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt index c20361c..0083cbeb 100644 --- a/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt +++ b/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt
@@ -16,7 +16,6 @@ strokeMiter : 4 strokeWidth : 0 styleName : "Fill" - textEncoding : "UTF-8" textScaleX : 1 textSize : 12 textSkewX : 0 @@ -43,7 +42,6 @@ strokeMiter : 4 strokeWidth : 0 styleName : "Fill" - textEncoding : "UTF-8" textScaleX : 1 textSize : 12 textSkewX : 0 @@ -81,7 +79,6 @@ strokeMiter : 4 strokeWidth : 0 styleName : "Fill" - textEncoding : "UTF-8" textScaleX : 1 textSize : 12 textSkewX : 0 @@ -135,7 +132,6 @@ strokeMiter : 4 strokeWidth : 0 styleName : "Fill" - textEncoding : "UTF-8" textScaleX : 1 textSize : 12 textSkewX : 0
diff --git a/third_party/blink/web_tests/platform/linux/fast/text/ellipsis-in-relative-inline-expected.png b/third_party/blink/web_tests/platform/linux/fast/text/ellipsis-in-relative-inline-expected.png deleted file mode 100644 index af04ab8b..0000000 --- a/third_party/blink/web_tests/platform/linux/fast/text/ellipsis-in-relative-inline-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/text/ellipsis-in-relative-inline-right-expected.png b/third_party/blink/web_tests/platform/linux/fast/text/ellipsis-in-relative-inline-right-expected.png deleted file mode 100644 index f20a43a..0000000 --- a/third_party/blink/web_tests/platform/linux/fast/text/ellipsis-in-relative-inline-right-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/ellipsis-in-relative-inline-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/ellipsis-in-relative-inline-expected.png deleted file mode 100644 index 8b4c811d90..0000000 --- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/ellipsis-in-relative-inline-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/ellipsis-in-relative-inline-right-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/ellipsis-in-relative-inline-right-expected.png deleted file mode 100644 index 7e35f98..0000000 --- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/ellipsis-in-relative-inline-right-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/ellipsis-in-relative-inline-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/ellipsis-in-relative-inline-expected.png deleted file mode 100644 index 30aa4d37..0000000 --- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/ellipsis-in-relative-inline-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/ellipsis-in-relative-inline-right-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/ellipsis-in-relative-inline-right-expected.png deleted file mode 100644 index 284a0c0b..0000000 --- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/ellipsis-in-relative-inline-right-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/ellipsis-in-relative-inline-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/ellipsis-in-relative-inline-expected.png deleted file mode 100644 index f7feeda..0000000 --- a/third_party/blink/web_tests/platform/mac/fast/text/ellipsis-in-relative-inline-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/ellipsis-in-relative-inline-right-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/ellipsis-in-relative-inline-right-expected.png deleted file mode 100644 index 4f68419..0000000 --- a/third_party/blink/web_tests/platform/mac/fast/text/ellipsis-in-relative-inline-right-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/text/ellipsis-in-relative-inline-expected.png b/third_party/blink/web_tests/platform/win/fast/text/ellipsis-in-relative-inline-expected.png deleted file mode 100644 index 57827c1..0000000 --- a/third_party/blink/web_tests/platform/win/fast/text/ellipsis-in-relative-inline-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/text/ellipsis-in-relative-inline-right-expected.png b/third_party/blink/web_tests/platform/win/fast/text/ellipsis-in-relative-inline-right-expected.png deleted file mode 100644 index 9a460f06..0000000 --- a/third_party/blink/web_tests/platform/win/fast/text/ellipsis-in-relative-inline-right-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/text/ellipsis-in-relative-inline-expected.png b/third_party/blink/web_tests/platform/win7/fast/text/ellipsis-in-relative-inline-expected.png deleted file mode 100644 index 0f65a79a..0000000 --- a/third_party/blink/web_tests/platform/win7/fast/text/ellipsis-in-relative-inline-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/text/ellipsis-in-relative-inline-right-expected.png b/third_party/blink/web_tests/platform/win7/fast/text/ellipsis-in-relative-inline-right-expected.png deleted file mode 100644 index 4219709b..0000000 --- a/third_party/blink/web_tests/platform/win7/fast/text/ellipsis-in-relative-inline-right-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/serial/resources/serial-test-utils.js b/third_party/blink/web_tests/serial/resources/serial-test-utils.js index f5f97b72..b0670bf 100644 --- a/third_party/blink/web_tests/serial/resources/serial-test-utils.js +++ b/third_party/blink/web_tests/serial/resources/serial-test-utils.js
@@ -73,3 +73,19 @@ } }, name, properties); } + +function trustedClick() { + return new Promise(resolve => { + let button = document.createElement('button'); + button.textContent = 'click to continue test'; + button.style.display = 'block'; + button.style.fontSize = '20px'; + button.style.padding = '10px'; + button.onclick = () => { + document.body.removeChild(button); + resolve(); + }; + document.body.appendChild(button); + test_driver.click(button); + }); +}
diff --git a/third_party/blink/web_tests/serial/serial_requestPort.html b/third_party/blink/web_tests/serial/serial_requestPort.html index dd2f7d83..5abed21 100644 --- a/third_party/blink/web_tests/serial/serial_requestPort.html +++ b/third_party/blink/web_tests/serial/serial_requestPort.html
@@ -1,6 +1,9 @@ <!DOCTYPE html> +<body> <script src="../resources/testharness.js"></script> <script src="../resources/testharnessreport.js"></script> +<script src="../resources/testdriver.js"></script> +<script src="../resources/testdriver-vendor.js"></script> <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script> <script src="file:///gen/mojo/public/mojom/base/unguessable_token.mojom.js"></script> <script src="file:///gen/third_party/blink/public/mojom/serial/serial.mojom.js"></script> @@ -8,23 +11,34 @@ <script> promise_test(async () => { + try { + let port = await navigator.serial.requestPort({}); + assert_unreached(); + } catch (e) { + assert_equals(e.code, DOMException.SECURITY_ERR); + } +}, 'requestPort() rejects without a user gesture'); + +promise_test(async () => { let interceptor = new MojoInterfaceInterceptor(blink.mojom.SerialService.name); interceptor.oninterfacerequest = e => e.handle.close(); interceptor.start(); try { + await trustedClick(); let port = await navigator.serial.requestPort({}); assert_unreached(); } catch (e) { assert_equals(e.code, DOMException.NOT_FOUND_ERR); + } finally { + interceptor.stop(); } - - interceptor.stop(); }, 'requestPort() rejects if Mojo service connection fails'); serial_test(async (fake) => { try { + await trustedClick(); let port = await navigator.serial.requestPort({}); assert_unreached(); } catch (e) { @@ -36,6 +50,7 @@ let guid = fake.addPort(); fake.setSelectedPort(guid); + await trustedClick(); let port = await navigator.serial.requestPort({}); assert_true(port instanceof SerialPort); // TODO: Assert that product IDs (if provided) are passed through. @@ -45,6 +60,7 @@ let guid = fake.addPort(); fake.setSelectedPort(guid); + await trustedClick(); let firstPort = await navigator.serial.requestPort({}); assert_true(firstPort instanceof SerialPort); let secondPort = await navigator.serial.requestPort({}); @@ -53,3 +69,4 @@ }, 'requestPort() returns the same port object every time'); </script> +</body>
diff --git a/third_party/blink/web_tests/usb/protected-interface-classes.html b/third_party/blink/web_tests/usb/protected-interface-classes.html index 022c2b9..df9a9d4 100644 --- a/third_party/blink/web_tests/usb/protected-interface-classes.html +++ b/third_party/blink/web_tests/usb/protected-interface-classes.html
@@ -4,7 +4,8 @@ <script src="../external/wpt/resources/chromium/mojo_bindings.js"></script> <script src="../external/wpt/resources/chromium/string16.mojom.js"></script> <script src="../external/wpt/resources/chromium/device.mojom.js"></script> -<script src="../external/wpt/resources/chromium/device_manager.mojom.js"></script> +<script src="../external/wpt/resources/chromium/device_enumeration_options.mojom.js"></script> +<script src="../external/wpt/resources/chromium/device_manager_client.mojom.js"></script> <script src="../external/wpt/resources/chromium/web_usb_service.mojom.js"></script> <script src="../external/wpt/resources/chromium/webusb-test.js"></script> <body>
diff --git a/third_party/blink/web_tests/usb/usbDevice-iframe.html b/third_party/blink/web_tests/usb/usbDevice-iframe.html index 1b1c7f3..286cccc 100644 --- a/third_party/blink/web_tests/usb/usbDevice-iframe.html +++ b/third_party/blink/web_tests/usb/usbDevice-iframe.html
@@ -4,7 +4,8 @@ <script src="../external/wpt/webusb/resources/fake-devices.js"></script> <script src="../external/wpt/resources/chromium/mojo_bindings.js"></script> <script src="../external/wpt/resources/chromium/device.mojom.js"></script> -<script src="../external/wpt/resources/chromium/device_manager.mojom.js"></script> +<script src="../external/wpt/resources/chromium/device_enumeration_options.mojom.js"></script> +<script src="../external/wpt/resources/chromium/device_manager_client.mojom.js"></script> <script src="../external/wpt/resources/chromium/web_usb_service.mojom.js"></script> <script src="../external/wpt/resources/chromium/webusb-test.js"></script> <body>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 99810ff..44a3eaa6f 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -1518,6 +1518,11 @@ <int value="3" label="Camera"/> </enum> +<enum name="AppListOmniboxResult"> + <int value="0" label="QUERY_SUGGESTION"/> + <int value="1" label="ZERO_STATE_SUGEESTION"/> +</enum> + <enum name="AppListPage"> <int value="0" label="APPS"/> <int value="1" label="SEARCH_RESULTS"/> @@ -1611,6 +1616,16 @@ <int value="10" label="Peeking To Half"/> </enum> +<enum name="AppListZeroStateResultRemovalConfirmation"> + <int value="0" label="Removal Confirmed"/> + <int value="1" label="Removal Canceled"/> +</enum> + +<enum name="AppListZeroStateSearchResultUserActionType"> + <int value="0" label="Remove Result"/> + <int value="1" label="Append Result"/> +</enum> + <enum name="AppLoadedInTabSource"> <int value="0" label="SOURCE_APP"/> <int value="1" label="SOURCE_BACKGROUND_PAGE"/> @@ -30181,6 +30196,8 @@ <int value="-2063014275" label="enable-web-bluetooth"/> <int value="-2062872298" label="market-url-for-testing"/> <int value="-2062373123" label="WebPaymentsModifiers:enabled"/> + <int value="-2061363134" + label="AutofillSaveCreditCardUsesStrikeSystemV2:disabled"/> <int value="-2059771509" label="NTPTilesLowerResolutionFavicons:disabled"/> <int value="-2058656447" label="ContextualSearchUrlActions:enabled"/> <int value="-2053860791" label="XGEOVisibleNetworks:enabled"/> @@ -31227,6 +31244,8 @@ <int value="-275164173" label="QuickUnlockPinSignin:enabled"/> <int value="-271790049" label="ArcUsbHost:enabled"/> <int value="-270626757" label="log-net-log"/> + <int value="-268549184" + label="AutofillSaveCreditCardUsesStrikeSystemV2:enabled"/> <int value="-268357961" label="enable-feature-policy"/> <int value="-263150202" label="BundledConnectionHelp:disabled"/> <int value="-260355779" label="UnfilteredBluetoothDevices:enabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 9d5cafd2..1c091a03 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -3278,6 +3278,28 @@ <summary>Interactions with the App Launcher promo dialog.</summary> </histogram> +<histogram name="Apps.AppList.ZeroStateSearchResultUserActionType" + enum="AppListZeroStateSearchResultUserActionType" + expires_after="2019-12-31"> + <owner>jennyz@chromium.org</owner> + <owner>newcomer@chromium.org</owner> + <summary> + The type of the action user performed on zero state search result. This is + gathered per click of a remove or append button of a search result. + </summary> +</histogram> + +<histogram name="Apps.AppList.ZeroStateSearchResutRemovalDecision" + enum="AppListZeroStateResultRemovalConfirmation" expires_after="2019-12-31"> + <owner>jennyz@chromium.org</owner> + <owner>newcomer@chromium.org</owner> + <summary> + The decision of the user whether to remove a zero state search result. This + is gathered per click of a remove or cancel button of a search result + removal confirmation dialog. + </summary> +</histogram> + <histogram name="Apps.AppListAppLaunched" enum="SuggestedAppListAppLaunched"> <owner>newcomer@chromium.org</owner> <summary> @@ -3449,6 +3471,17 @@ </summary> </histogram> +<histogram name="Apps.AppListSearchOmniboxResultOpenType" + enum="AppListOmniboxResult" expires_after="2019-12-31"> + <owner>jennyz@chromium.org</owner> + <owner>newcomer@chromium.org</owner> + <summary> + The type of app list search omnibox result that was opened by the user. This + is gathered per OmniboxResult opened in the app list's launcher suggestion + window. + </summary> +</histogram> + <histogram name="Apps.AppListSearchQueryLength" units="characters"> <owner>calamity@chromium.org</owner> <summary> @@ -93616,8 +93649,8 @@ </histogram> <histogram name="SafeBrowsing.ContentsSize.Height" units="DIPs" - expires_after="M73"> - <owner>jialiul@chromium.org</owner> + expires_after="M77"> + <owner>drubery@chromium.org</owner> <owner>nparker@chromium.org</owner> <summary> Records the height of content area when the user opens a new browser window @@ -93626,8 +93659,8 @@ </histogram> <histogram name="SafeBrowsing.ContentsSize.Width" units="DIPs" - expires_after="M73"> - <owner>jialiul@chromium.org</owner> + expires_after="M77"> + <owner>drubery@chromium.org</owner> <owner>nparker@chromium.org</owner> <summary> Records the width of content area when the user opens a new browser window @@ -122785,8 +122818,22 @@ </summary> </histogram> +<histogram base="true" name="WebApp.Engagement" + enum="SiteEngagementServiceEngagementType" expires_after="2021-01-01"> + <owner>calamity@chromium.org</owner> + <owner>mgiuca@chromium.org</owner> + <owner>loyso@chromium.org</owner> + <summary> + The type of engagement (navigation, user input, etc.) which led to an + accumulation in site engagement within a web app window or tab. Should be + collected for a subset of SiteEngagementService.EngagementType, which is + triggered for all browsing contexts. Recorded at the time of engagement + accumulation (e.g., when mouse is clicked). + </summary> +</histogram> + <histogram name="Webapp.Engagement.EngagementType" - enum="SiteEngagementServiceEngagementType"> + enum="SiteEngagementServiceEngagementType" expires_after="2019-03-07"> <owner>calamity@chromium.org</owner> <owner>mgiuca@chromium.org</owner> <summary> @@ -122794,7 +122841,9 @@ accumulation in site engagement within a PWA app window. Should be collected for a subset of SiteEngagementService.EngagementType, which is triggered for all browsing contexts. Recorded at the time of engagement accumulation - (e.g., when mouse is clicked). + (e.g., when mouse is clicked). Deprecated because only counts PWAs. + Superseded by the WebApp.Engagement histogram with suffixes. + http://crbug.com/918089. </summary> </histogram> @@ -140079,6 +140128,8 @@ <suffix name="Network" label="The response directly came from network."/> <suffix name="Unspecified" label="The source of a response was unspecified."/> <affected-histogram + name="ServiceWorker.LoadTiming.MainFrame.MainResource.ResponseReceivedToCompleted2"/> + <affected-histogram name="ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted2"/> </histogram_suffixes> @@ -141837,6 +141888,36 @@ <affected-histogram name="WebApk.ShellApkVersion"/> </histogram_suffixes> +<histogram_suffixes name="WebAppContainerEngagementType" separator="."> + <suffix name="InTab" label="Happened in app running as a tab"/> + <suffix name="InWindow" label="Happened in app running as a window"/> + <affected-histogram name="WebApp.Engagement"/> + <affected-histogram name="WebApp.Engagement.DefaultInstalled"/> + <affected-histogram name="WebApp.Engagement.UserInstalled"/> + <affected-histogram + name="WebApp.Engagement.UserInstalled.FromCreateShortcutButton"/> + <affected-histogram name="WebApp.Engagement.UserInstalled.FromInstallButton"/> +</histogram_suffixes> + +<histogram_suffixes name="WebAppEngagementType" separator="."> + <suffix base="true" name="DefaultInstalled" label="Happened in default app"/> + <suffix name="MoreThanThreeUserInstalledApps" + label="Happenned in web site for user with 4 or more user-installed + apps"/> + <suffix name="NoUserInstalledApps" + label="Happenned in web site for user without user-installed apps"/> + <suffix name="UpToThreeUserInstalledApps" + label="Happenned in web site for user with [1-3] user-installed apps"/> + <suffix base="true" name="UserInstalled" + label="Happened in user-installed app"/> + <suffix base="true" name="UserInstalled.FromCreateShortcutButton" + label="Happened in user-installed app created via the Create Shortcut + button"/> + <suffix base="true" name="UserInstalled.FromInstallButton" + label="Happened in user-installed app created via the Install button"/> + <affected-histogram name="WebApp.Engagement"/> +</histogram_suffixes> + <histogram_suffixes name="WebBluetoothMacOSAPIs" separator="."> <suffix name="DidDisconnectPeripheral" label="Disconnected from peripheral"/> <suffix name="DidDiscoverCharacteristics" label="Discovered characteristics"/>
diff --git a/ui/aura/test/mus/window_tree_client_test_api.h b/ui/aura/test/mus/window_tree_client_test_api.h index fd50f70..329a6d38 100644 --- a/ui/aura/test/mus/window_tree_client_test_api.h +++ b/ui/aura/test/mus/window_tree_client_test_api.h
@@ -14,6 +14,7 @@ #include "ui/aura/mus/mus_types.h" #include "ui/aura/mus/window_tree_client.h" #include "ui/aura/test/mus/change_completion_waiter.h" +#include "ui/aura/test/ui_controls_factory_aura.h" namespace ws { namespace mojom { @@ -76,6 +77,10 @@ bool HasChangeInFlightOfType(ChangeType type); private: +#if defined(USE_OZONE) + friend void test::OnWindowServiceProcessedEvent(base::OnceClosure closure, + bool result); +#endif friend void test::WaitForAllChangesToComplete(WindowTreeClient* client); ws::mojom::WindowDataPtr CreateWindowDataForEmbed();
diff --git a/ui/aura/test/ui_controls_factory_aura.h b/ui/aura/test/ui_controls_factory_aura.h index b675222..d94b92e 100644 --- a/ui/aura/test/ui_controls_factory_aura.h +++ b/ui/aura/test/ui_controls_factory_aura.h
@@ -14,6 +14,13 @@ ui_controls::UIControlsAura* CreateUIControlsAura(WindowTreeHost* host); +#if defined(USE_OZONE) +// Callback from Window Service with the result of posting an event. |result| +// is true if event successfully processed and |closure| is an optional closure +// to run when done (used in client code to wait for ack). +void OnWindowServiceProcessedEvent(base::OnceClosure closure, bool result); +#endif + } // namespace test } // namespace aura
diff --git a/ui/aura/test/ui_controls_factory_ozone.cc b/ui/aura/test/ui_controls_factory_ozone.cc index a551bc5..f2bd4f9 100644 --- a/ui/aura/test/ui_controls_factory_ozone.cc +++ b/ui/aura/test/ui_controls_factory_ozone.cc
@@ -16,6 +16,7 @@ #include "ui/aura/mus/window_tree_client.h" #include "ui/aura/test/aura_test_utils.h" #include "ui/aura/test/env_test_helper.h" +#include "ui/aura/test/mus/window_tree_client_test_api.h" #include "ui/aura/test/ui_controls_factory_aura.h" #include "ui/aura/window_tree_host.h" #include "ui/base/test/ui_controls_aura.h" @@ -29,15 +30,6 @@ namespace test { namespace { -// Callback from Window Service with the result of posting an event. |result| -// is true if event successfully processed and |closure| is an optional closure -// to run when done (used in client code to wait for ack). -void OnWindowServiceProcessedEvent(base::OnceClosure closure, bool result) { - DCHECK(result); - if (closure) - std::move(closure).Run(); -} - class UIControlsOzone : public ui_controls::UIControlsAura, display::DisplayObserver { public: @@ -242,25 +234,18 @@ return false; bool has_move = action & ui_controls::MOVE; bool has_release = action & ui_controls::RELEASE; - ui::PointerDetails details(ui::EventPointerType::POINTER_TYPE_TOUCH, id, - 1.0f, 1.0f, 0.0f); if (action & ui_controls::PRESS) { - ui::TouchEvent event(ui::ET_TOUCH_PRESSED, host_location, - ui::EventTimeForNow(), details); - SendEventToSink( - &event, display_id, + PostTouchEvent( + ui::ET_TOUCH_PRESSED, host_location, id, display_id, (has_move || has_release) ? base::OnceClosure() : std::move(task)); } if (has_move) { - ui::TouchEvent event(ui::ET_TOUCH_MOVED, host_location, - ui::EventTimeForNow(), details); - SendEventToSink(&event, display_id, - has_release ? base::OnceClosure() : std::move(task)); + PostTouchEvent(ui::ET_TOUCH_MOVED, host_location, id, display_id, + has_release ? base::OnceClosure() : std::move(task)); } if (has_release) { - ui::TouchEvent event(ui::ET_TOUCH_RELEASED, host_location, - ui::EventTimeForNow(), details); - SendEventToSink(&event, display_id, std::move(task)); + PostTouchEvent(ui::ET_TOUCH_RELEASED, host_location, id, display_id, + std::move(task)); } return true; } @@ -347,6 +332,29 @@ SendEventToSink(&mouse_event2, display_id, std::move(closure)); } + void PostTouchEvent(ui::EventType type, + const gfx::Point& host_location, + int id, + int64_t display_id, + base::OnceClosure closure) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&UIControlsOzone::PostTouchEventTask, + base::Unretained(this), type, host_location, + id, display_id, std::move(closure))); + } + + void PostTouchEventTask(ui::EventType type, + const gfx::Point& host_location, + int id, + int64_t display_id, + base::OnceClosure closure) { + ui::PointerDetails details(ui::EventPointerType::POINTER_TYPE_TOUCH, id, + 1.0f, 1.0f, 0.0f); + ui::TouchEvent touch_event(type, host_location, ui::EventTimeForNow(), + details); + SendEventToSink(&touch_event, display_id, std::move(closure)); + } + // Initializes EventInjector when Mus. Otherwise do nothing. void MaybeInitializeEventInjector() { if (host_->window()->env()->mode() != Env::Mode::MUS) @@ -405,5 +413,20 @@ return new UIControlsOzone(host); } +void OnWindowServiceProcessedEvent(base::OnceClosure closure, bool result) { + DCHECK(result); + if (closure) { + // There can be several mojo calls are queued in the window tree client, + // which may change the order of the operations unexpectedly. Do not call + // WaitForAllChangesToComplete() here, since some in-flight changes might + // not be resolved by just waiting (like window-dragging will not finish + // until it's cancelled or the mouse or touch is released). + // See also: https://crbug.com/916177 + WindowTreeClientTestApi(EnvTestHelper().GetWindowTreeClient()) + .FlushForTesting(); + std::move(closure).Run(); + } +} + } // namespace test } // namespace aura
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc index be2f26d..974d806 100644 --- a/ui/gfx/render_text.cc +++ b/ui/gfx/render_text.cc
@@ -191,7 +191,6 @@ SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas) : canvas_(canvas), canvas_skia_(canvas->sk_canvas()) { DCHECK(canvas_skia_); - flags_.setTextEncoding(cc::PaintFlags::kGlyphID_TextEncoding); flags_.setStyle(cc::PaintFlags::kFill_Style); flags_.setAntiAlias(true); flags_.setSubpixelText(true);
diff --git a/ui/message_center/vector_icons/notification_snooze_button.icon b/ui/message_center/vector_icons/notification_snooze_button.icon index a62c3b42..da0b7511 100644 --- a/ui/message_center/vector_icons/notification_snooze_button.icon +++ b/ui/message_center/vector_icons/notification_snooze_button.icon
@@ -5,9 +5,9 @@ // Converted from the svg files attached in http://crbug.com/840497. CANVAS_DIMENSIONS, 24, -MOVE_TO, 11.99f, 2, -ARC_TO, 10, 10, 0, 1, 0, 12, 21.99f, -ARC_TO, 10, 10, 0, 0, 0, 11.99f, 2, +MOVE_TO, 12, 2, +ARC_TO, 10, 10, 0, 1, 0, 12, 22, +ARC_TO, 10, 10, 0, 0, 0, 12, 2, CLOSE, MOVE_TO, 12, 20, R_ARC_TO, 8, 8, 0, 1, 1, 0, -16,
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd index 4210d8a..5fcb4f6 100644 --- a/ui/strings/ui_strings.grd +++ b/ui/strings/ui_strings.grd
@@ -803,6 +803,15 @@ <message name="IDS_APP_LIST_CLEAR_SEARCHBOX" desc="Tooltip for the button that clears all text from the search box in the app list."> Clear searchbox text </message> + <message name="IDS_REMOVE_ZERO_STATE_SUGGESTION_TITLE" desc="Titlebar of removing zero state suggestion confirmation dialog"> + Remove this suggestion? + </message> + <message name="IDS_REMOVE_ZERO_STATE_SUGGESTION_DETAILS" desc="Detailed explanation message for removing zero state suggestion"> + Hiding this suggestion will not show it again from your account across all your devices. + </message> + <message name="IDS_REMOVE_SUGGESTION_BUTTON_LABEL" desc="Remove suggestion button label"> + Remove + </message> <message name="IDS_APP_LIST_START_ASSISTANT" desc="Tooltip for the button that starts Google Assistant from the search box in the app list."> Start Google Assistant </message>
diff --git a/ui/views/layout/OWNERS b/ui/views/layout/OWNERS new file mode 100644 index 0000000..b4ee07df --- /dev/null +++ b/ui/views/layout/OWNERS
@@ -0,0 +1 @@ +per-file flex_layout*=dfried@chromium.org \ No newline at end of file
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc index a4db5ebc..afd5488c 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -1007,10 +1007,6 @@ desktop_window_tree_host_->SizeConstraintsChanged(); } -void DesktopNativeWidgetAura::RepostNativeEvent(gfx::NativeEvent native_event) { - OnEvent(native_event); -} - std::string DesktopNativeWidgetAura::GetName() const { return name_; }
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h index 91a48265f..8e85a951 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -188,7 +188,6 @@ bool IsTranslucentWindowOpacitySupported() const override; ui::GestureRecognizer* GetGestureRecognizer() override; void OnSizeConstraintsChanged() override; - void RepostNativeEvent(gfx::NativeEvent native_event) override; std::string GetName() const override; // Overridden from aura::WindowDelegate:
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc index 16d366f0..9559f5a3 100644 --- a/ui/views/widget/native_widget_aura.cc +++ b/ui/views/widget/native_widget_aura.cc
@@ -814,10 +814,6 @@ window_->SetProperty(aura::client::kResizeBehaviorKey, behavior); } -void NativeWidgetAura::RepostNativeEvent(gfx::NativeEvent native_event) { - OnEvent(native_event); -} - std::string NativeWidgetAura::GetName() const { return window_ ? window_->GetName() : std::string(); }
diff --git a/ui/views/widget/native_widget_aura.h b/ui/views/widget/native_widget_aura.h index 2d87a99a..c708ebc7 100644 --- a/ui/views/widget/native_widget_aura.h +++ b/ui/views/widget/native_widget_aura.h
@@ -149,7 +149,6 @@ bool IsTranslucentWindowOpacitySupported() const override; ui::GestureRecognizer* GetGestureRecognizer() override; void OnSizeConstraintsChanged() override; - void RepostNativeEvent(gfx::NativeEvent native_event) override; std::string GetName() const override; // Overridden from aura::WindowDelegate:
diff --git a/ui/views/widget/native_widget_mac.h b/ui/views/widget/native_widget_mac.h index cf1d7436..64eba8978 100644 --- a/ui/views/widget/native_widget_mac.h +++ b/ui/views/widget/native_widget_mac.h
@@ -166,7 +166,6 @@ bool IsTranslucentWindowOpacitySupported() const override; ui::GestureRecognizer* GetGestureRecognizer() override; void OnSizeConstraintsChanged() override; - void RepostNativeEvent(gfx::NativeEvent native_event) override; std::string GetName() const override; protected:
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm index d93c719..eec61ad 100644 --- a/ui/views/widget/native_widget_mac.mm +++ b/ui/views/widget/native_widget_mac.mm
@@ -634,10 +634,6 @@ widget->widget_delegate()->CanMaximize()); } -void NativeWidgetMac::RepostNativeEvent(gfx::NativeEvent native_event) { - NOTIMPLEMENTED(); -} - std::string NativeWidgetMac::GetName() const { return name_; }
diff --git a/ui/views/widget/native_widget_private.h b/ui/views/widget/native_widget_private.h index 6aedb90..1867d1906 100644 --- a/ui/views/widget/native_widget_private.h +++ b/ui/views/widget/native_widget_private.h
@@ -231,9 +231,6 @@ virtual ui::GestureRecognizer* GetGestureRecognizer() = 0; virtual void OnSizeConstraintsChanged() = 0; - // Repost an unhandled event to the native widget for default OS processing. - virtual void RepostNativeEvent(gfx::NativeEvent native_event) = 0; - // Returns an internal name that matches the name of the associated Widget. virtual std::string GetName() const = 0;
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn b/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn index a83ed92..838b871 100644 --- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn +++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn
@@ -14,7 +14,6 @@ ":multidevice_setup", ":multidevice_setup_browser_proxy", ":multidevice_setup_delegate", - ":setup_failed_page", ":setup_succeeded_page", ":start_setup_page", ":ui_page_container_behavior", @@ -59,7 +58,6 @@ ":mojo_api", ":multidevice_setup_delegate", ":password_page", - ":setup_failed_page", ":setup_succeeded_page", ":start_setup_page", "//ui/webui/resources/js:cr", @@ -105,12 +103,6 @@ extra_sources = [ "$interfaces_path/quick_unlock_private_interface.js" ] } -js_library("setup_failed_page") { - deps = [ - ":ui_page_container_behavior", - ] -} - js_library("setup_succeeded_page") { deps = [ ":multidevice_setup_browser_proxy",
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html index 660f1ac..8cce94f 100644 --- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html +++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
@@ -6,7 +6,6 @@ <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.html"> <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html"> <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/password_page.html"> -<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/setup_failed_page.html"> <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html"> <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/start_setup_page.html"> <link rel="import" href="chrome://resources/html/cr.html"> @@ -43,7 +42,6 @@ on-user-submitted-password="onUserSubmittedPassword_"> </password-page> </template> - <setup-failed-page></setup-failed-page> <template is="dom-if" if="[[shouldSetupSucceededPageBeIncluded_(delegate)]]" restamp> <setup-succeeded-page></setup-succeeded-page>
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js index 784c201..b49b3e7d 100644 --- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js +++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
@@ -6,7 +6,6 @@ /** @enum {string} */ multidevice_setup.PageName = { - FAILURE: 'setup-failed-page', PASSWORD: 'password-page', SUCCESS: 'setup-succeeded-page', START: 'start-setup-page', @@ -80,7 +79,7 @@ * DOM Element corresponding to the visible page. * * @private {!PasswordPageElement|!StartSetupPageElement| - * !SetupSucceededPageElement|!SetupFailedPageElement} + * !SetupSucceededPageElement} */ visiblePage_: Object, @@ -190,9 +189,6 @@ /** @private */ navigateForward_: function() { switch (this.visiblePageName) { - case PageName.FAILURE: - this.visiblePageName = PageName.START; - return; case PageName.PASSWORD: this.$$('password-page').clearPasswordTextInput(); this.setHostDevice_();
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.html deleted file mode 100644 index 181687d..0000000 --- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.html +++ /dev/null
@@ -1,18 +0,0 @@ -<link rel="import" href="chrome://resources/html/polymer.html"> - -<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page.html"> -<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.html"> -<link rel="import" href="chrome://resources/html/cr.html"> - -<dom-module id="setup-failed-page"> - <template> - <ui-page header-text="[[headerText]]" icon-name="error-icon"> - <span slot="message" inner-h-t-m-l="[[messageHtml]]"></span> - <div slot="additional-content"> - This is empty... (PlAcEhOlDeR tExT!!) - </div> - </ui-page> - </template> - <script src="chrome://resources/cr_components/chromeos/multidevice_setup/setup_failed_page.js"> - </script> -</dom-module>
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.js deleted file mode 100644 index 36f2d4a..0000000 --- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.js +++ /dev/null
@@ -1,43 +0,0 @@ -// Copyright 2018 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. - -Polymer({ - is: 'setup-failed-page', - - properties: { - /** Overridden from UiPageContainerBehavior. */ - forwardButtonTextId: { - type: String, - value: 'tryAgain', - }, - - /** Overridden from UiPageContainerBehavior. */ - cancelButtonTextId: { - type: String, - value: 'cancel', - }, - - /** Overridden from UiPageContainerBehavior. */ - backwardButtonTextId: { - type: String, - value: 'back', - }, - - /** Overridden from UiPageContainerBehavior. */ - headerId: { - type: String, - value: 'setupFailedPageHeader', - }, - - /** Overridden from UiPageContainerBehavior. */ - messageId: { - type: String, - value: 'setupFailedPageMessage', - }, - }, - - behaviors: [ - UiPageContainerBehavior, - ], -});
diff --git a/ui/webui/resources/cr_components/cr_components_resources.grdp b/ui/webui/resources/cr_components/cr_components_resources.grdp index 5723b56..8c5d906e 100644 --- a/ui/webui/resources/cr_components/cr_components_resources.grdp +++ b/ui/webui/resources/cr_components/cr_components_resources.grdp
@@ -324,14 +324,6 @@ file="cr_components/chromeos/multidevice_setup/password_page.js" type="chrome_html" compress="gzip" /> - <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_HTML" - file="cr_components/chromeos/multidevice_setup/setup_failed_page.html" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_JS" - file="cr_components/chromeos/multidevice_setup/setup_failed_page.js" - type="chrome_html" - compress="gzip" /> <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_PAGE_HTML" file="cr_components/chromeos/multidevice_setup/setup_succeeded_page.html" type="chrome_html"