diff --git a/DEPS b/DEPS index c9845b4..c90f66a 100644 --- a/DEPS +++ b/DEPS
@@ -129,11 +129,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': 'e178c0586cd6daadb7671d6fd53642c25b464d2a', + 'skia_revision': 'bc94e79eb7b6c0f370101012199c4753a336cc18', # 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': 'c3ba2e82ba6ffaded66173ff8babb6f500c90746', + 'v8_revision': '0a278d064c61f6f482db698cd2c8096850991247', # 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. @@ -145,11 +145,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': 'a32d6303a613c170ea5795669a2161e70e1d7678', + 'swiftshader_revision': '28f142f1b0a2ec18304ec2f11518f72747ac6d11', # 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': '8bceb48c222230c21aada35281b68746d9fffc46', + 'pdfium_revision': 'ae6ebf06c7e85453b91ccd489a6715eb5f013e1b', # 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. @@ -196,7 +196,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': 'ffa3433a8c084d5cab2a04a603ec5d396292886c', + 'catapult_revision': 'd235eb23657332468da6ceeb78789a4bd693fda6', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -1395,7 +1395,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c6130f7f5dc8d6b98dcd1bb6841e60f3de0dec92', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c0a0741eb59828931b200197ef026715d30d7957', 'condition': 'checkout_src_internal', },
diff --git a/ash/public/cpp/shelf_item_delegate.cc b/ash/public/cpp/shelf_item_delegate.cc index 8eaeca1..1bd8e9ad 100644 --- a/ash/public/cpp/shelf_item_delegate.cc +++ b/ash/public/cpp/shelf_item_delegate.cc
@@ -19,6 +19,9 @@ ShelfItemDelegate::~ShelfItemDelegate() = default; mojom::ShelfItemDelegatePtr ShelfItemDelegate::CreateInterfacePtrAndBind() { + if (binding_.is_bound()) + binding_.Close(); + mojom::ShelfItemDelegatePtr ptr; binding_.Bind(mojo::MakeRequest(&ptr)); return ptr;
diff --git a/ash/public/cpp/shelf_model.cc b/ash/public/cpp/shelf_model.cc index 9fee37d..b740d05 100644 --- a/ash/public/cpp/shelf_model.cc +++ b/ash/public/cpp/shelf_model.cc
@@ -5,6 +5,7 @@ #include "ash/public/cpp/shelf_model.h" #include <algorithm> +#include <utility> #include "ash/public/cpp/shelf_item_delegate.h" #include "ash/public/cpp/shelf_model_observer.h" @@ -144,6 +145,18 @@ observer.ShelfItemRemoved(index, old_item); } +std::unique_ptr<ShelfItemDelegate> +ShelfModel::RemoveItemAndTakeShelfItemDelegate(const ShelfID& shelf_id) { + const int index = ItemIndexByID(shelf_id); + if (index < 0) + return nullptr; + + auto it = id_to_item_delegate_map_.find(shelf_id); + std::unique_ptr<ShelfItemDelegate> item = std::move(it->second); + RemoveItemAt(index); + return item; +} + void ShelfModel::Move(int index, int target_index) { if (index == target_index) return;
diff --git a/ash/public/cpp/shelf_model.h b/ash/public/cpp/shelf_model.h index fd438acfc..d2ec5d2 100644 --- a/ash/public/cpp/shelf_model.h +++ b/ash/public/cpp/shelf_model.h
@@ -58,6 +58,12 @@ // Removes the item at |index|. void RemoveItemAt(int index); + // Removes the item with id |shelf_id| and passes ownership of its + // ShelfItemDelegate to the caller. This is useful if you want to remove an + // item from the shelf temporarily and be able to restore its behavior later. + std::unique_ptr<ShelfItemDelegate> RemoveItemAndTakeShelfItemDelegate( + const ShelfID& shelf_id); + // Moves the item at |index| to |target_index|. |target_index| is in terms // of the model *after* the item at |index| is removed. void Move(int index, int target_index);
diff --git a/ash/public/cpp/shelf_model_unittest.cc b/ash/public/cpp/shelf_model_unittest.cc index 29fe0fd..0fb8616 100644 --- a/ash/public/cpp/shelf_model_unittest.cc +++ b/ash/public/cpp/shelf_model_unittest.cc
@@ -7,6 +7,7 @@ #include <set> #include <string> +#include "ash/public/cpp/shelf_item_delegate.h" #include "ash/public/cpp/shelf_model_observer.h" #include "base/strings/stringprintf.h" #include "testing/gtest/include/gtest/gtest.h" @@ -28,7 +29,9 @@ AddToResult("removed=%d", removed_count_, &result); AddToResult("changed=%d", changed_count_, &result); AddToResult("moved=%d", moved_count_, &result); - added_count_ = removed_count_ = changed_count_ = moved_count_ = 0; + AddToResult("delegate_changed=%d", delegate_changed_count_, &result); + added_count_ = removed_count_ = changed_count_ = moved_count_ = + delegate_changed_count_ = 0; return result; } @@ -37,6 +40,11 @@ void ShelfItemRemoved(int, const ShelfItem&) override { removed_count_++; } void ShelfItemChanged(int, const ShelfItem&) override { changed_count_++; } void ShelfItemMoved(int, int) override { moved_count_++; } + void ShelfItemDelegateChanged(const ShelfID&, + ShelfItemDelegate*, + ShelfItemDelegate*) override { + delegate_changed_count_++; + } private: void AddToResult(const std::string& format, int count, std::string* result) { @@ -51,10 +59,27 @@ int removed_count_ = 0; int changed_count_ = 0; int moved_count_ = 0; + int delegate_changed_count_ = 0; DISALLOW_COPY_AND_ASSIGN(TestShelfModelObserver); }; +class TestShelfItemDelegate : public ShelfItemDelegate { + public: + TestShelfItemDelegate(const ShelfID& shelf_id) + : ShelfItemDelegate(shelf_id) {} + + void ItemSelected(std::unique_ptr<ui::Event> event, + int64_t display_id, + ash::ShelfLaunchSource source, + ItemSelectedCallback callback) override {} + void ExecuteCommand(bool from_context_menu, + int64_t command_id, + int32_t event_flags, + int64_t display_id) override {} + void Close() override {} +}; + } // namespace class ShelfModelTest : public testing::Test { @@ -509,4 +534,32 @@ EXPECT_FALSE(model_->items()[index].has_notification); } +// Test that RemoveItemAndTakeShelfItemDelegate has the same effect as +// RemoveItemAt and returns the correct delegate. +TEST_F(ShelfModelTest, RemoveItemAndTakeShelfItemDelegate) { + // Add an item. + ShelfItem item1; + item1.id = ShelfID("item1"); + item1.type = TYPE_PINNED_APP; + model_->Add(item1); + EXPECT_EQ(3, model_->item_count()); + EXPECT_LE(0, model_->ItemIndexByID(item1.id)); + EXPECT_NE(model_->items().end(), model_->ItemByID(item1.id)); + EXPECT_EQ("added=1", observer_->StateStringAndClear()); + + // Set item delegate. + auto* delegate = new TestShelfItemDelegate(item1.id); + model_->SetShelfItemDelegate(item1.id, + std::unique_ptr<ShelfItemDelegate>(delegate)); + EXPECT_EQ("delegate_changed=1", observer_->StateStringAndClear()); + + // Remove the item. + auto taken_delegate = model_->RemoveItemAndTakeShelfItemDelegate(item1.id); + EXPECT_EQ(2, model_->item_count()); + EXPECT_EQ(-1, model_->ItemIndexByID(item1.id)); + EXPECT_EQ(model_->items().end(), model_->ItemByID(item1.id)); + EXPECT_EQ("removed=1", observer_->StateStringAndClear()); + EXPECT_EQ(delegate, taken_delegate.get()); +} + } // namespace ash
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index 6a739f6..fab6fe1ab 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -8915089885321306400 \ No newline at end of file +8914872956664439568 \ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index 280a06d..ee8554c 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -8915114915290958432 \ No newline at end of file +8914885528414328080 \ No newline at end of file
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py index 0ce237fe13..15e385e6 100755 --- a/build/vs_toolchain.py +++ b/build/vs_toolchain.py
@@ -254,7 +254,7 @@ sdk_redist_root_version = os.path.join(sdk_redist_root, directory) if not os.path.isdir(sdk_redist_root_version): continue - if re.match('10\.\d+\.\d+\.\d+', directory): + if re.match(r'10\.\d+\.\d+\.\d+', directory): source_dir = os.path.join(sdk_redist_root_version, target_cpu, 'ucrt') break _CopyRuntimeImpl(os.path.join(target_dir, 'ucrtbase' + suffix), @@ -278,7 +278,7 @@ for directory in vc_component_msvc_contents: if not os.path.isdir(os.path.join(vc_component_msvc_root, directory)): continue - if re.match('14\.\d+\.\d+', directory): + if re.match(r'14\.\d+\.\d+', directory): return os.path.join(vc_component_msvc_root, directory) raise Exception('Unable to find the VC %s directory.' % component)
diff --git a/chrome/VERSION b/chrome/VERSION index 08f391c..5fdb5876 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=76 MINOR=0 -BUILD=3778 +BUILD=3781 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java index 1ca1ada..a2224bb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -15,6 +15,7 @@ import org.chromium.base.Log; import org.chromium.base.ObserverList; import org.chromium.base.SysUtils; +import org.chromium.base.TimeUtils; import org.chromium.base.VisibleForTesting; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.metrics.RecordUserAction; @@ -116,8 +117,6 @@ // an existing tap-selection. private static final int TAP_ON_TAP_SELECTION_DELAY_MS = 100; - private static final int NANOSECONDS_IN_A_MILLISECOND = 1000000; - private final ObserverList<ContextualSearchObserver> mObservers = new ObserverList<ContextualSearchObserver>(); @@ -1419,7 +1418,8 @@ && tapTimeNanoseconds > 0) { delayBeforeFinishingWorkMs = ContextualSearchFieldTrial.getValue( ContextualSearchSetting.WAIT_AFTER_TAP_DELAY_MS) - - (System.nanoTime() - tapTimeNanoseconds) / NANOSECONDS_IN_A_MILLISECOND; + - (System.nanoTime() - tapTimeNanoseconds) + / TimeUtils.NANOSECONDS_PER_MILLISECOND; } // Finish work on the current state, either immediately or with a delay.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java index d7fabdd..83a8f9a5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java
@@ -372,23 +372,23 @@ switch (DownloadFilter.fromMimeType(downloadInfo.getMimeType())) { case DownloadFilter.Type.PAGE: - offlineItem.filter = OfflineItemFilter.FILTER_PAGE; + offlineItem.filter = OfflineItemFilter.PAGE; break; case DownloadFilter.Type.VIDEO: - offlineItem.filter = OfflineItemFilter.FILTER_VIDEO; + offlineItem.filter = OfflineItemFilter.VIDEO; break; case DownloadFilter.Type.AUDIO: - offlineItem.filter = OfflineItemFilter.FILTER_AUDIO; + offlineItem.filter = OfflineItemFilter.AUDIO; break; case DownloadFilter.Type.IMAGE: - offlineItem.filter = OfflineItemFilter.FILTER_IMAGE; + offlineItem.filter = OfflineItemFilter.IMAGE; break; case DownloadFilter.Type.DOCUMENT: - offlineItem.filter = OfflineItemFilter.FILTER_DOCUMENT; + offlineItem.filter = OfflineItemFilter.DOCUMENT; break; case DownloadFilter.Type.OTHER: default: - offlineItem.filter = OfflineItemFilter.FILTER_OTHER; + offlineItem.filter = OfflineItemFilter.OTHER; break; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java index 6bc1d95..e97db96 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java
@@ -63,16 +63,16 @@ */ public static @FilterType Integer fromOfflineItem(@OfflineItemFilter int filter) { switch (filter) { - case OfflineItemFilter.FILTER_PAGE: + case OfflineItemFilter.PAGE: return FilterType.SITES; - case OfflineItemFilter.FILTER_VIDEO: + case OfflineItemFilter.VIDEO: return FilterType.VIDEOS; - case OfflineItemFilter.FILTER_AUDIO: + case OfflineItemFilter.AUDIO: return FilterType.MUSIC; - case OfflineItemFilter.FILTER_IMAGE: + case OfflineItemFilter.IMAGE: return FilterType.IMAGES; - // case OfflineItemFilter.FILTER_OTHER - // case OfflineItemFilter.FILTER_DOCUMENT + // case OfflineItemFilter.OTHER + // case OfflineItemFilter.DOCUMENT default: return FilterType.OTHER; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutator.java index 70a9c91c..0550da7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutator.java
@@ -185,7 +185,7 @@ sectionIndex == 0 && dateIndex > 0 /* showDivider */); if (!mHideSectionHeaders) { sectionHeaderItem.showTitle = !mHideTitleFromSectionHeaders; - sectionHeaderItem.showMenu = filter == OfflineItemFilter.FILTER_IMAGE; + sectionHeaderItem.showMenu = filter == OfflineItemFilter.IMAGE; sectionHeaderItem.items = new ArrayList<>(section.items.values()); } listItems.add(sectionHeaderItem); @@ -195,7 +195,7 @@ for (OfflineItem offlineItem : section.items.values()) { OfflineItemListItem item = new OfflineItemListItem(offlineItem); if (mConfig.supportFullWidthImages && section.items.size() == 1 - && offlineItem.filter == OfflineItemFilter.FILTER_IMAGE) { + && offlineItem.filter == OfflineItemFilter.IMAGE) { item.spanFullWidth = true; } listItems.add(item);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java index 4062b694..0b511fc 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java
@@ -93,16 +93,16 @@ if (offlineItem.item.isSuggested) return ViewType.PREFETCH; switch (offlineItem.item.filter) { - case OfflineItemFilter.FILTER_VIDEO: + case OfflineItemFilter.VIDEO: return inProgress ? ViewType.IN_PROGRESS_VIDEO : ViewType.VIDEO; - case OfflineItemFilter.FILTER_IMAGE: + case OfflineItemFilter.IMAGE: return inProgress ? ViewType.IN_PROGRESS_IMAGE : (offlineItem.spanFullWidth ? ViewType.IMAGE_FULL_WIDTH : ViewType.IMAGE); - // case OfflineItemFilter.FILTER_PAGE: - // case OfflineItemFilter.FILTER_AUDIO: - // case OfflineItemFilter.FILTER_OTHER: - // case OfflineItemFilter.FILTER_DOCUMENT: + // case OfflineItemFilter.PAGE: + // case OfflineItemFilter.AUDIO: + // case OfflineItemFilter.OTHER: + // case OfflineItemFilter.DOCUMENT: default: return inProgress ? ViewType.IN_PROGRESS : ViewType.GENERIC; } @@ -117,17 +117,17 @@ */ public static @StringRes int getTextForSection(int filter) { switch (filter) { - case OfflineItemFilter.FILTER_PAGE: + case OfflineItemFilter.PAGE: return R.string.download_manager_ui_pages; - case OfflineItemFilter.FILTER_IMAGE: + case OfflineItemFilter.IMAGE: return R.string.download_manager_ui_images; - case OfflineItemFilter.FILTER_VIDEO: + case OfflineItemFilter.VIDEO: return R.string.download_manager_ui_video; - case OfflineItemFilter.FILTER_AUDIO: + case OfflineItemFilter.AUDIO: return R.string.download_manager_ui_audio; - case OfflineItemFilter.FILTER_OTHER: + case OfflineItemFilter.OTHER: return R.string.download_manager_ui_other; - case OfflineItemFilter.FILTER_DOCUMENT: + case OfflineItemFilter.DOCUMENT: return R.string.download_manager_ui_documents; default: return R.string.download_manager_ui_all_downloads;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java index 4666b99..48296908 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java
@@ -168,9 +168,9 @@ /** @return Whether or not {@code item} can show a thumbnail in the UI. */ public static boolean canHaveThumbnails(OfflineItem item) { switch (item.filter) { - case OfflineItemFilter.FILTER_PAGE: - case OfflineItemFilter.FILTER_VIDEO: - case OfflineItemFilter.FILTER_IMAGE: + case OfflineItemFilter.PAGE: + case OfflineItemFilter.VIDEO: + case OfflineItemFilter.IMAGE: return true; default: return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java index 40b3d45..8205c03 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java
@@ -212,7 +212,7 @@ */ public static void recordItemsShared(Collection<OfflineItem> items) { for (OfflineItem item : items) { - if (item.filter == OfflineItemFilter.FILTER_PAGE) { + if (item.filter == OfflineItemFilter.PAGE) { RecordUserAction.record("OfflinePages.Sharing.SharePageFromDownloadHome"); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java index e6f620f..12bffbe2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
@@ -540,7 +540,7 @@ @Override public boolean isOfflinePage() { - return mItem.filter == OfflineItemFilter.FILTER_PAGE; + return mItem.filter == OfflineItemFilter.PAGE; } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java index 99d896d..5029bc55 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
@@ -8,9 +8,13 @@ import android.support.annotation.IntDef; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; +import android.view.animation.AnimationSet; import android.view.animation.DecelerateInterpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.ScaleAnimation; import android.view.animation.Transformation; import org.chromium.base.metrics.RecordHistogram; @@ -68,13 +72,14 @@ private static final float DECELERATE_INTERPOLATION_FACTOR = 2f; - private static final int SCALE_DOWN_DURATION_MS = 500; + private static final int SCALE_DOWN_DURATION_MS = 400; private static final int ANIMATE_TO_START_DURATION_MS = 500; // Minimum number of pull updates necessary to trigger a side nav. private static final int MIN_PULLS_TO_ACTIVATE = 3; private final DecelerateInterpolator mDecelerateInterpolator; + private final LinearInterpolator mLinearInterpolator; private final float mTotalDragDistance; private final int mMediumAnimationDuration; private final int mCircleWidth; @@ -101,7 +106,8 @@ private int mFrom; private int mOriginalOffset; - private Animation mScaleDownAnimation; + private AnimationSet mHidingAnimation; + private int mAnimationViewWidth; private AnimationListener mCancelAnimationListener; private boolean mIsForward; @@ -116,8 +122,8 @@ @Override public void onAnimationEnd(Animation animation) { + mArrowView.setVisibility(View.INVISIBLE); if (mNavigating) { - // Make sure the arrow widget is fully visible if (mListener != null) mListener.onNavigate(mIsForward); recordHistogram("Overscroll.Navigated3", mIsForward); } else { @@ -147,6 +153,7 @@ setWillNotDraw(false); mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR); + mLinearInterpolator = new LinearInterpolator(); final float density = getResources().getDisplayMetrics().density; mCircleWidth = (int) (CIRCLE_DIAMETER_DP * density); @@ -183,7 +190,7 @@ private void setNavigating(boolean navigating) { if (mNavigating != navigating) { mNavigating = navigating; - if (mNavigating) startScaleDownAnimation(mNavigateListener); + if (mNavigating) startHidingAnimation(mNavigateListener); } } @@ -191,20 +198,25 @@ return mIsForward ? -Math.min(0, mTotalMotion) : Math.max(0, mTotalMotion); } - private void startScaleDownAnimation(AnimationListener listener) { - if (mScaleDownAnimation == null) { - mScaleDownAnimation = new Animation() { - @Override - public void applyTransformation(float interpolatedTime, Transformation t) { - float progress = 1 - interpolatedTime; // [0..1] - mArrowView.setScaleY(progress); - } - }; - mScaleDownAnimation.setDuration(SCALE_DOWN_DURATION_MS); + private void startHidingAnimation(AnimationListener listener) { + // ScaleAnimation needs to be created again if the arrow widget width changes over time + // (due to turning on/off close indicator) to set the right x pivot point. + if (mHidingAnimation == null || mAnimationViewWidth != mArrowViewWidth) { + mAnimationViewWidth = mArrowViewWidth; + ScaleAnimation scalingDown = + new ScaleAnimation(1, 0, 1, 0, mArrowViewWidth / 2, mArrowView.getHeight() / 2); + scalingDown.setInterpolator(mLinearInterpolator); + scalingDown.setDuration(SCALE_DOWN_DURATION_MS); + Animation fadingOut = new AlphaAnimation(1, 0); + fadingOut.setInterpolator(mDecelerateInterpolator); + fadingOut.setDuration(SCALE_DOWN_DURATION_MS); + mHidingAnimation = new AnimationSet(false); + mHidingAnimation.addAnimation(fadingOut); + mHidingAnimation.addAnimation(scalingDown); } mArrowView.setAnimationListener(listener); mArrowView.clearAnimation(); - mArrowView.startAnimation(mScaleDownAnimation); + mArrowView.startAnimation(mHidingAnimation); } /** @@ -284,7 +296,6 @@ * 2f; if (mArrowView.getVisibility() != View.VISIBLE) mArrowView.setVisibility(View.VISIBLE); - mArrowView.setScaleY(1f); float originalDragPercent = overscroll / mTotalDragDistance; float dragPercent = Math.min(1f, Math.abs(originalDragPercent)); @@ -340,7 +351,7 @@ @Override public void onAnimationEnd(Animation animation) { - startScaleDownAnimation(mNavigateListener); + startHidingAnimation(mNavigateListener); } @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java index 508571f..8e7634c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java
@@ -407,7 +407,7 @@ offlineItem.filePath = targetPath; offlineItem.creationTimeMs = startTime; offlineItem.totalSizeBytes = totalSize; - offlineItem.filter = OfflineItemFilter.FILTER_PAGE; + offlineItem.filter = OfflineItemFilter.PAGE; return offlineItem; }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java index 31dd6156..ecb20fd 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java
@@ -19,16 +19,16 @@ @Test public void testFilterConversions() { Assert.assertEquals(Integer.valueOf(Filters.FilterType.SITES), - Filters.fromOfflineItem(OfflineItemFilter.FILTER_PAGE)); + Filters.fromOfflineItem(OfflineItemFilter.PAGE)); Assert.assertEquals(Integer.valueOf(Filters.FilterType.VIDEOS), - Filters.fromOfflineItem(OfflineItemFilter.FILTER_VIDEO)); + Filters.fromOfflineItem(OfflineItemFilter.VIDEO)); Assert.assertEquals(Integer.valueOf(Filters.FilterType.MUSIC), - Filters.fromOfflineItem(OfflineItemFilter.FILTER_AUDIO)); + Filters.fromOfflineItem(OfflineItemFilter.AUDIO)); Assert.assertEquals(Integer.valueOf(Filters.FilterType.IMAGES), - Filters.fromOfflineItem(OfflineItemFilter.FILTER_IMAGE)); + Filters.fromOfflineItem(OfflineItemFilter.IMAGE)); Assert.assertEquals(Integer.valueOf(Filters.FilterType.OTHER), - Filters.fromOfflineItem(OfflineItemFilter.FILTER_OTHER)); + Filters.fromOfflineItem(OfflineItemFilter.OTHER)); Assert.assertEquals(Integer.valueOf(Filters.FilterType.OTHER), - Filters.fromOfflineItem(OfflineItemFilter.FILTER_DOCUMENT)); + Filters.fromOfflineItem(OfflineItemFilter.DOCUMENT)); } } \ No newline at end of file
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java index 63e36bc..6b52b88 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java
@@ -39,14 +39,14 @@ @Test public void testTypeFiltering() { - OfflineItem item1 = buildItem(OfflineItemFilter.FILTER_PAGE, false); - OfflineItem item2 = buildItem(OfflineItemFilter.FILTER_VIDEO, false); - OfflineItem item3 = buildItem(OfflineItemFilter.FILTER_AUDIO, false); - OfflineItem item4 = buildItem(OfflineItemFilter.FILTER_IMAGE, false); - OfflineItem item5 = buildItem(OfflineItemFilter.FILTER_OTHER, false); - OfflineItem item6 = buildItem(OfflineItemFilter.FILTER_DOCUMENT, false); - OfflineItem item7 = buildItem(OfflineItemFilter.FILTER_PAGE, true); - OfflineItem item8 = buildItem(OfflineItemFilter.FILTER_VIDEO, true); + OfflineItem item1 = buildItem(OfflineItemFilter.PAGE, false); + OfflineItem item2 = buildItem(OfflineItemFilter.VIDEO, false); + OfflineItem item3 = buildItem(OfflineItemFilter.AUDIO, false); + OfflineItem item4 = buildItem(OfflineItemFilter.IMAGE, false); + OfflineItem item5 = buildItem(OfflineItemFilter.OTHER, false); + OfflineItem item6 = buildItem(OfflineItemFilter.DOCUMENT, false); + OfflineItem item7 = buildItem(OfflineItemFilter.PAGE, true); + OfflineItem item8 = buildItem(OfflineItemFilter.VIDEO, true); Collection<OfflineItem> sourceItems = CollectionUtil.newHashSet(item1, item2, item3, item4, item5, item6, item7, item8); when(mSource.getItems()).thenReturn(sourceItems);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java index 090c827..986193f2 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java
@@ -89,14 +89,13 @@ */ @Test public void testSingleItem() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); Assert.assertEquals(2, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1); } @@ -109,16 +108,14 @@ */ @Test public void testTwoItemsSameDay() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); Assert.assertEquals(3, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 2), item1); assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 1), item2); } @@ -133,19 +130,17 @@ */ @Test public void testTwoItemsSameDayDifferentSection() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_AUDIO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.AUDIO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); Assert.assertEquals(4, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 2), item1); - assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_AUDIO, false, false); + assertSectionHeader( + mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.AUDIO, false, false); assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 1), item2); } @@ -159,21 +154,19 @@ */ @Test public void testShowMenuButtonForImageSectionWithoutDate() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_IMAGE); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.IMAGE); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); Assert.assertEquals(4, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false); Assert.assertFalse(((SectionHeaderListItem) mModel.get(0)).showMenu); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 2), item1); - assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_IMAGE, false, false); + assertSectionHeader( + mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.IMAGE, false, false); Assert.assertTrue(((SectionHeaderListItem) mModel.get(2)).showMenu); assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 1), item2); } @@ -188,21 +181,19 @@ */ @Test public void testShowMenuButtonForImageSectionWithDate() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.FILTER_IMAGE); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_PAGE); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.IMAGE); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.PAGE); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); Assert.assertEquals(4, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_IMAGE, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.IMAGE, true, false); Assert.assertTrue(((SectionHeaderListItem) mModel.get(0)).showMenu); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 2), item1); - assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_PAGE, false, false); + assertSectionHeader( + mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.PAGE, false, false); Assert.assertFalse(((SectionHeaderListItem) mModel.get(2)).showMenu); assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 1), item2); } @@ -215,14 +206,13 @@ */ @Test public void testSingleItemInJustNowSection() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); item1.state = OfflineItemState.IN_PROGRESS; when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1)); DateOrderedListMutator list = createMutatorWithJustNowProvider(); Assert.assertEquals(2, mModel.size()); - assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false); + assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1); } @@ -236,10 +226,8 @@ */ @Test public void testMultipleSectionsInJustNowSection() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_AUDIO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.AUDIO); item1.state = OfflineItemState.IN_PROGRESS; item2.state = OfflineItemState.IN_PROGRESS; item2.completionTimeMs = item2.creationTimeMs; @@ -247,9 +235,9 @@ DateOrderedListMutator list = createMutatorWithJustNowProvider(); Assert.assertEquals(4, mModel.size()); - assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false); + assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1); - assertJustNowSection(mModel.get(2), OfflineItemFilter.FILTER_AUDIO, false, false); + assertJustNowSection(mModel.get(2), OfflineItemFilter.AUDIO, false, false); assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 1), item2); } @@ -270,50 +258,46 @@ */ @Test public void testItemDoesNotMoveOutOfJustNowSection() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); item1.state = OfflineItemState.PAUSED; when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1)); DateOrderedListMutator list = createMutatorWithJustNowProvider(); mModel.addObserver(mObserver); Assert.assertEquals(2, mModel.size()); - assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false); + assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1); // Resume the download. - OfflineItem update1 = - buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO); + OfflineItem update1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); update1.state = OfflineItemState.IN_PROGRESS; when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(update1)); list.onItemUpdated(item1, update1); Assert.assertEquals(2, mModel.size()); - assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false); + assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), update1); // Complete the download. - OfflineItem update2 = - buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO); + OfflineItem update2 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); update2.state = OfflineItemState.COMPLETE; update2.completionTimeMs = update2.creationTimeMs; when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(update2)); list.onItemUpdated(update1, update2); Assert.assertEquals(2, mModel.size()); - assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false); + assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), update2); // Too much time has passed since completion of the download. - OfflineItem update3 = - buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO); + OfflineItem update3 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); update3.state = OfflineItemState.COMPLETE; update3.completionTimeMs = buildCalendar(2017, 1, 1, 1).getTimeInMillis(); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(update3)); list.onItemUpdated(update2, update3); Assert.assertEquals(2, mModel.size()); - assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false); + assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), update3); } @@ -328,19 +312,17 @@ */ @Test public void testJustNowSectionWithOtherDates() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 2, 1, 1), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_AUDIO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 2, 1, 1), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.AUDIO); item1.state = OfflineItemState.IN_PROGRESS; when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithJustNowProvider(); Assert.assertEquals(4, mModel.size()); - assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false); + assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 2, 1, 1), item1); - assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_AUDIO, true, true); + assertSectionHeader( + mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.AUDIO, true, true); assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 1), item2); } @@ -355,19 +337,17 @@ */ @Test public void testTwoItemsDifferentDayMatchHeader() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 2, 0), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 0), OfflineItemFilter.FILTER_AUDIO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 0), OfflineItemFilter.AUDIO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); Assert.assertEquals(4, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 0), item1); - assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_AUDIO, true, true); + assertSectionHeader( + mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.AUDIO, true, true); assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 0), item2); } @@ -380,16 +360,14 @@ */ @Test public void testTwoItemsSameDayOutOfOrder() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); Assert.assertEquals(3, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 5), item2); assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 4), item1); } @@ -405,19 +383,17 @@ */ @Test public void testTwoItemsDifferentDaySameSection() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); Assert.assertEquals(4, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 4), item1); - assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, true); + assertSectionHeader( + mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, true); assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 5), item2); } @@ -432,19 +408,17 @@ */ @Test public void testTwoItemsDifferentDayDifferentSection() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.FILTER_PAGE); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.PAGE); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); Assert.assertEquals(4, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 4), item1); - assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_PAGE, true, true); + assertSectionHeader( + mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.PAGE, true, true); assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 5), item2); } @@ -459,19 +433,17 @@ */ @Test public void testTwoItemsDifferentDayOutOfOrder() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); Assert.assertEquals(4, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 3), item2); - assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, true); + assertSectionHeader( + mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, true); assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 4), item1); } @@ -489,8 +461,7 @@ DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); mModel.addObserver(mObserver); - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1)); list.onItemsAdded(CollectionUtil.newArrayList(item1)); @@ -517,15 +488,13 @@ */ @Test public void testAddFirstItemToList() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 2, 1), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 1), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); mModel.addObserver(mObserver); // Add an item on the same day that will be placed first. - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); list.onItemsAdded(CollectionUtil.newArrayList(item2)); @@ -534,8 +503,7 @@ assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 2, 1), item1); // Add an item on an earlier day that will be placed first. - OfflineItem item3 = - buildItem("3", buildCalendar(2018, 1, 3, 2), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 3, 2), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2, item3)); list.onItemsAdded(CollectionUtil.newArrayList(item3)); @@ -566,15 +534,13 @@ */ @Test public void testAddLastItemToList() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); mModel.addObserver(mObserver); // Add an item on the same day that will be placed last. - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); list.onItemsAdded(CollectionUtil.newArrayList(item2)); @@ -583,8 +549,7 @@ assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 2, 3), item2); // Add an item on a later day that will be placed last. - OfflineItem item3 = - buildItem("3", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2, item3)); list.onItemsAdded(CollectionUtil.newArrayList(item3)); @@ -604,8 +569,7 @@ */ @Test public void testRemoveOnlyItemInList() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); mModel.addObserver(mObserver); @@ -629,10 +593,8 @@ */ @Test public void testRemoveFirstItemInListSameDay() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); mModel.addObserver(mObserver); @@ -641,8 +603,8 @@ list.onItemsRemoved(CollectionUtil.newArrayList(item1)); Assert.assertEquals(2, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 2), item2); } @@ -659,10 +621,8 @@ */ @Test public void testRemoveLastItemInListSameDay() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); mModel.addObserver(mObserver); @@ -671,8 +631,8 @@ list.onItemsRemoved(CollectionUtil.newArrayList(item2)); Assert.assertEquals(2, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 3), item1); } @@ -690,10 +650,8 @@ */ @Test public void testRemoveOnlyItemInSection() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_IMAGE); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.IMAGE); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); mModel.addObserver(mObserver); @@ -703,8 +661,8 @@ list.onItemsRemoved(CollectionUtil.newArrayList(item1)); Assert.assertEquals(2, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), - OfflineItemFilter.FILTER_IMAGE, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.IMAGE, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 2), item2); } @@ -723,10 +681,8 @@ */ @Test public void testRemoveLastItemInListWithMultipleDays() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 3, 3), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 3, 3), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); mModel.addObserver(mObserver); @@ -735,8 +691,8 @@ list.onItemsRemoved(CollectionUtil.newArrayList(item2)); Assert.assertEquals(2, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 3, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 3, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 3, 3), item1); } @@ -759,26 +715,22 @@ DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); mModel.addObserver(mObserver); - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item3 = - buildItem("3", buildCalendar(2018, 1, 2, 10), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item4 = - buildItem("4", buildCalendar(2018, 1, 2, 12), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); + OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 2, 10), OfflineItemFilter.VIDEO); + OfflineItem item4 = buildItem("4", buildCalendar(2018, 1, 2, 12), OfflineItemFilter.VIDEO); when(mSource.getItems()) .thenReturn(CollectionUtil.newArrayList(item1, item2, item3, item4)); list.onItemsAdded(CollectionUtil.newArrayList(item1, item2, item3, item4)); Assert.assertEquals(6, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 12), item4); assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 2, 10), item3); - assertSectionHeader(mModel.get(3), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, true); + assertSectionHeader( + mModel.get(3), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, true); assertOfflineItem(mModel.get(4), buildCalendar(2018, 1, 1, 6), item1); assertOfflineItem(mModel.get(5), buildCalendar(2018, 1, 1, 4), item2); } @@ -802,32 +754,29 @@ DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); mModel.addObserver(mObserver); - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1)); list.onItemsAdded(CollectionUtil.newArrayList(item1)); Assert.assertEquals(2, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 6), item1); // Complete the download. - OfflineItem update1 = - buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.FILTER_VIDEO); + OfflineItem update1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO); update1.state = OfflineItemState.COMPLETE; when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(update1)); list.onItemUpdated(item1, update1); // Add a new download. - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); list.onItemsAdded(CollectionUtil.newArrayList(item2)); Assert.assertEquals(3, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 6), update1); assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 4), item2); } @@ -849,14 +798,10 @@ */ @Test public void testRemoveMultipleItems() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item3 = - buildItem("3", buildCalendar(2018, 1, 2, 10), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item4 = - buildItem("4", buildCalendar(2018, 1, 2, 12), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); + OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 2, 10), OfflineItemFilter.VIDEO); + OfflineItem item4 = buildItem("4", buildCalendar(2018, 1, 2, 12), OfflineItemFilter.VIDEO); when(mSource.getItems()) .thenReturn(CollectionUtil.newArrayList(item1, item2, item3, item4)); @@ -867,8 +812,8 @@ list.onItemsRemoved(CollectionUtil.newArrayList(item2, item3, item4)); Assert.assertEquals(2, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 6), item1); } @@ -885,8 +830,7 @@ */ @Test public void testItemUpdatedSameTimestamp() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); @@ -894,13 +838,13 @@ // Update an item with the same timestamp. OfflineItem newItem1 = - buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); + buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(newItem1)); list.onItemUpdated(item1, newItem1); Assert.assertEquals(2, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), newItem1); } @@ -918,10 +862,8 @@ */ @Test public void testItemUpdatedSameDay() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); @@ -929,13 +871,13 @@ // Update an item with the same timestamp. OfflineItem newItem1 = - buildItem("1", buildCalendar(2018, 1, 1, 3), OfflineItemFilter.FILTER_VIDEO); + buildItem("1", buildCalendar(2018, 1, 1, 3), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(newItem1, item2)); list.onItemUpdated(item1, newItem1); Assert.assertEquals(3, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), item2); assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 3), newItem1); } @@ -955,10 +897,8 @@ */ @Test public void testItemUpdatedSameDayDifferentSection() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.FILTER_VIDEO); - OfflineItem item2 = - buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO); + OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); @@ -966,16 +906,16 @@ // Update an item with the same timestamp. OfflineItem newItem1 = - buildItem("1", buildCalendar(2018, 1, 1, 3), OfflineItemFilter.FILTER_IMAGE); + buildItem("1", buildCalendar(2018, 1, 1, 3), OfflineItemFilter.IMAGE); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(newItem1, item2)); list.onItemUpdated(item1, newItem1); Assert.assertEquals(4, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), item2); - assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), - OfflineItemFilter.FILTER_IMAGE, false, false); + assertSectionHeader( + mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.IMAGE, false, false); assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 3), newItem1); } @@ -992,8 +932,7 @@ */ @Test public void testItemUpdatedDifferentDay() { - OfflineItem item1 = - buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO); + OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1)); DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); @@ -1001,13 +940,13 @@ // Update an item with the same timestamp. OfflineItem newItem1 = - buildItem("1", buildCalendar(2018, 1, 2, 6), OfflineItemFilter.FILTER_VIDEO); + buildItem("1", buildCalendar(2018, 1, 2, 6), OfflineItemFilter.VIDEO); when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(newItem1)); list.onItemUpdated(item1, newItem1); Assert.assertEquals(2, mModel.size()); - assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), - OfflineItemFilter.FILTER_VIDEO, true, false); + assertSectionHeader( + mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false); assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 6), newItem1); }
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt index e1cac02..6ed6bad0 100644 --- a/chrome/android/profiles/newest.txt +++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-76.0.3777.0_rc-r1-merged.afdo.bz2 \ No newline at end of file +chromeos-chrome-amd64-76.0.3779.0_rc-r1-merged.afdo.bz2 \ No newline at end of file
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index 932ce8ee..0787eb7 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -3460,7 +3460,7 @@ Your app will open when the upgrade is finished. Upgrades can take a few minutes. </message> <message name="IDS_CROSTINI_SHUT_DOWN_LINUX_MENU_ITEM" desc="Text shown in the context menu for the Linux terminal app, allowing users to shut down the Linux virtual machine."> - Shut Down Linux (Beta) + Shut down Linux (Beta) </message> <message name="IDS_CROSTINI_TERMINA_UPDATE_REQUIRED" desc="Text shown in the Crostini update dialog when the VM component needs updating."> Linux (Beta) update required
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index c4623747..5c550a91 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -3946,6 +3946,14 @@ FEATURE_VALUE_TYPE(features::kEnterpriseReportingInBrowser)}, #endif // !defined(OS_ANDROID) + {"enable-autofill-do-not-migrate-unsupported-local-cards", + flag_descriptions::kEnableAutofillDoNotMigrateUnsupportedLocalCardsName, + flag_descriptions:: + kEnableAutofillDoNotMigrateUnsupportedLocalCardsDescription, + kOsDesktop, + FEATURE_VALUE_TYPE( + autofill::features::kAutofillDoNotMigrateUnsupportedLocalCards)}, + // NOTE: Adding a new flag requires adding a corresponding entry to enum // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc index 9f1ccf92..3324eb0 100644 --- a/chrome/browser/apps/app_service/arc_apps.cc +++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -110,24 +110,26 @@ // request to ARC++ container to extract the real data. This logic is // isolated inside ArcAppListPrefs and I don't think that anybody else should // call mojom RequestAppIcon". - if (icon_resource_id.empty()) { - auto* app_instance = - ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder, RequestAppIcon); - if (app_instance) { - app_instance->RequestAppIcon( - package_name, activity, size_hint_in_px, - base::BindOnce(&LoadIcon1, icon_compression, std::move(callback))); - return; - } + if (app_connection_holder) { + if (icon_resource_id.empty()) { + auto* app_instance = + ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder, RequestAppIcon); + if (app_instance) { + app_instance->RequestAppIcon( + package_name, activity, size_hint_in_px, + base::BindOnce(&LoadIcon1, icon_compression, std::move(callback))); + return; + } - } else { - auto* app_instance = - ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder, RequestShortcutIcon); - if (app_instance) { - app_instance->RequestShortcutIcon( - icon_resource_id, size_hint_in_px, - base::BindOnce(&LoadIcon1, icon_compression, std::move(callback))); - return; + } else { + auto* app_instance = ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder, + RequestShortcutIcon); + if (app_instance) { + app_instance->RequestShortcutIcon( + icon_resource_id, size_hint_in_px, + base::BindOnce(&LoadIcon1, icon_compression, std::move(callback))); + return; + } } } @@ -179,6 +181,13 @@ } ArcApps::~ArcApps() { + // Clear out any pending icon calls to avoid a CHECK for Mojo callbacks that + // have not been run at App Service destruction. + for (auto& pending : pending_load_icon_calls_) { + std::move(pending).Run(nullptr); + } + pending_load_icon_calls_.clear(); + if (prefs_) { prefs_->app_connection_holder()->RemoveObserver(this); prefs_->RemoveObserver(this);
diff --git a/chrome/browser/banners/app_banner_manager_desktop.cc b/chrome/browser/banners/app_banner_manager_desktop.cc index 4f8680c6..18b5611e 100644 --- a/chrome/browser/banners/app_banner_manager_desktop.cc +++ b/chrome/browser/banners/app_banner_manager_desktop.cc
@@ -13,8 +13,10 @@ #include "chrome/browser/banners/app_banner_settings_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/web_applications/web_app_dialog_utils.h" +#include "chrome/browser/web_applications/components/app_registrar.h" #include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/browser/web_applications/extensions/bookmark_app_util.h" +#include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/common/chrome_features.h" #include "extensions/common/constants.h" @@ -93,8 +95,10 @@ const GURL& validated_url, const GURL& start_url, const GURL& manifest_url) { - return extensions::BookmarkOrHostedAppInstalled( - web_contents->GetBrowserContext(), start_url); + return web_app::WebAppProvider::Get( + Profile::FromBrowserContext(web_contents->GetBrowserContext())) + ->registrar() + .IsInstalled(start_url); } void AppBannerManagerDesktop::ShowBannerUi(WebappInstallSource install_source) {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index eb7f566..c671528 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -578,6 +578,8 @@ "arc/policy/arc_policy_util.h", "arc/print/arc_print_service.cc", "arc/print/arc_print_service.h", + "arc/print_spooler/arc_print_spooler_bridge.cc", + "arc/print_spooler/arc_print_spooler_bridge.h", "arc/process/arc_process.cc", "arc/process/arc_process.h", "arc/process/arc_process_service.cc",
diff --git a/chrome/browser/chromeos/arc/arc_service_launcher.cc b/chrome/browser/chromeos/arc/arc_service_launcher.cc index 6255b645..75a76d6 100644 --- a/chrome/browser/chromeos/arc/arc_service_launcher.cc +++ b/chrome/browser/chromeos/arc/arc_service_launcher.cc
@@ -35,6 +35,7 @@ #include "chrome/browser/chromeos/arc/pip/arc_pip_bridge.h" #include "chrome/browser/chromeos/arc/policy/arc_policy_bridge.h" #include "chrome/browser/chromeos/arc/print/arc_print_service.h" +#include "chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h" #include "chrome/browser/chromeos/arc/process/arc_process_service.h" #include "chrome/browser/chromeos/arc/screen_capture/arc_screen_capture_bridge.h" #include "chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h" @@ -181,6 +182,7 @@ ArcPolicyBridge::GetForBrowserContext(profile); ArcPowerBridge::GetForBrowserContext(profile); ArcPrintService::GetForBrowserContext(profile); + ArcPrintSpoolerBridge::GetForBrowserContext(profile); ArcProcessService::GetForBrowserContext(profile); ArcPropertyBridge::GetForBrowserContext(profile); ArcProvisionNotificationService::GetForBrowserContext(profile);
diff --git a/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc b/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc index 2f2efe93..ed37626 100644 --- a/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc +++ b/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc
@@ -14,6 +14,8 @@ #include "base/threading/thread_restrictions.h" #include "chrome/browser/chromeos/arc/arc_service_launcher.h" #include "chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge.h" +#include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h" +#include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h" #include "chrome/browser/chromeos/platform_keys/key_permissions.h" #include "chrome/browser/chromeos/platform_keys/platform_keys.h" #include "chrome/browser/chromeos/policy/user_policy_test_helper.h" @@ -24,7 +26,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/pref_names.h" -#include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/constants/chromeos_switches.h" #include "components/arc/arc_prefs.h" @@ -93,22 +94,21 @@ bool is_on_certs_changed_called_ = false; }; -class ArcCertStoreBridgeTest : public InProcessBrowserTest { +class ArcCertStoreBridgeTest : public chromeos::MixinBasedInProcessBrowserTest { protected: ArcCertStoreBridgeTest() = default; - // InProcessBrowserTest: + // chromeos::MixinBasedInProcessBrowserTest: void SetUpCommandLine(base::CommandLine* command_line) override { - InProcessBrowserTest::SetUpCommandLine(command_line); + chromeos::MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line); arc::SetArcAvailableCommandLineForTesting(command_line); - policy_helper_ = - std::make_unique<policy::UserPolicyTestHelper>(kFakeUserName); - policy_helper_->Init( + policy_helper_ = std::make_unique<policy::UserPolicyTestHelper>( + kFakeUserName, &local_policy_server_); + policy_helper_->SetPolicy( base::DictionaryValue() /* empty mandatory policy */, base::DictionaryValue() /* empty recommended policy */); - policy_helper_->UpdateCommandLine(command_line); command_line->AppendSwitchASCII(chromeos::switches::kLoginUser, kFakeUserName); @@ -129,7 +129,7 @@ } void SetUpOnMainThread() override { - InProcessBrowserTest::SetUpOnMainThread(); + chromeos::MixinBasedInProcessBrowserTest::SetUpOnMainThread(); policy_helper_->WaitForInitialPolicy(browser()->profile()); @@ -163,6 +163,7 @@ // instance in fixture, once), but it should be no op. ArcServiceLauncher::Get()->Shutdown(); chromeos::ProfileHelper::SetAlwaysReturnPrimaryUserForTesting(false); + chromeos::MixinBasedInProcessBrowserTest::TearDownOnMainThread(); } ArcBridgeService* arc_bridge() { @@ -192,7 +193,7 @@ user_policy.SetKey(policy::key::kKeyPermissions, base::Value(key_permissions_policy_str)); - policy_helper_->UpdatePolicy( + policy_helper_->SetPolicyAndWait( user_policy, base::DictionaryValue() /* empty recommended policy */, browser()->profile()); } @@ -299,6 +300,7 @@ std::unique_ptr<policy::UserPolicyTestHelper> policy_helper_; std::unique_ptr<FakeArcCertStoreInstance> instance_; + chromeos::LocalPolicyTestServerMixin local_policy_server_{&mixin_host_}; DISALLOW_COPY_AND_ASSIGN(ArcCertStoreBridgeTest); };
diff --git a/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.cc b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.cc new file mode 100644 index 0000000..2060e2bd --- /dev/null +++ b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.cc
@@ -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. + +#include "chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h" + +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "chrome/browser/ui/browser.h" +#include "components/arc/arc_browser_context_keyed_service_factory_base.h" +#include "components/arc/session/arc_bridge_service.h" +#include "content/public/browser/browser_thread.h" + +namespace arc { +namespace { + +// Singleton factory for ArcPrintSpoolerBridge. +class ArcPrintSpoolerBridgeFactory + : public internal::ArcBrowserContextKeyedServiceFactoryBase< + ArcPrintSpoolerBridge, ArcPrintSpoolerBridgeFactory> { + public: + // Factory name used by ArcBrowserContextKeyedServiceFactoryBase. + static constexpr const char* kName = "ArcPrintSpoolerBridgeFactory"; + + static ArcPrintSpoolerBridgeFactory* GetInstance() { + return base::Singleton<ArcPrintSpoolerBridgeFactory>::get(); + } + + private: + friend base::DefaultSingletonTraits<ArcPrintSpoolerBridgeFactory>; + ArcPrintSpoolerBridgeFactory() = default; + ~ArcPrintSpoolerBridgeFactory() override = default; +}; + +} // namespace + +// static +ArcPrintSpoolerBridge* ArcPrintSpoolerBridge::GetForBrowserContext( + content::BrowserContext* context) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + return ArcPrintSpoolerBridgeFactory::GetForBrowserContext(context); +} + +ArcPrintSpoolerBridge::ArcPrintSpoolerBridge(content::BrowserContext* context, + ArcBridgeService* bridge_service) + : profile_(Profile::FromBrowserContext(context)), + arc_bridge_service_(bridge_service), + weak_ptr_factory_(this) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + arc_bridge_service_->print_spooler()->SetHost(this); +} + +ArcPrintSpoolerBridge::~ArcPrintSpoolerBridge() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + arc_bridge_service_->print_spooler()->SetHost(nullptr); +} + +} // namespace arc
diff --git a/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h new file mode 100644 index 0000000..74a2cced --- /dev/null +++ b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h
@@ -0,0 +1,53 @@ +// 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_CHROMEOS_ARC_PRINT_SPOOLER_ARC_PRINT_SPOOLER_BRIDGE_H_ +#define CHROME_BROWSER_CHROMEOS_ARC_PRINT_SPOOLER_ARC_PRINT_SPOOLER_BRIDGE_H_ + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/profiles/profile.h" +#include "components/arc/common/print_spooler.mojom.h" +#include "components/keyed_service/core/keyed_service.h" + +namespace content { +class BrowserContext; +} // namespace content + +namespace arc { + +class ArcBridgeService; + +// This class handles print related IPC from the ARC container and allows print +// jobs to be displayed and managed in Chrome print preview instead of the +// Android print UI. +class ArcPrintSpoolerBridge + : public KeyedService, + public mojom::PrintSpoolerHost { + public: + // Returns singleton instance for the given BrowserContext, + // or nullptr if the browser |context| is not allowed to use ARC. + static ArcPrintSpoolerBridge* GetForBrowserContext( + content::BrowserContext* context); + + ArcPrintSpoolerBridge(content::BrowserContext* context, + ArcBridgeService* bridge_service); + ~ArcPrintSpoolerBridge() override; + + // mojom::PrintSpoolerHost overrides: + // TODO(jschettler): Add overrides. + + private: + Profile* const profile_; + + ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager. + + base::WeakPtrFactory<ArcPrintSpoolerBridge> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ArcPrintSpoolerBridge); +}; + +} // namespace arc + +#endif // CHROME_BROWSER_CHROMEOS_ARC_PRINT_SPOOLER_ARC_PRINT_SPOOLER_BRIDGE_H_
diff --git a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc index 33ca6437..b9a341c 100644 --- a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc +++ b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
@@ -176,8 +176,8 @@ policy.SetKey(policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(), - child_profile_); + user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(), + child_profile_); EXPECT_FALSE(IsAuthEnabled()); } @@ -204,8 +204,8 @@ bedtime_policy.SetKey( policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(bedtime_policy, base::DictionaryValue(), - child_profile_); + user_policy_helper()->SetPolicyAndWait( + bedtime_policy, base::DictionaryValue(), child_profile_); // Check that auth is disabled, since the bedtime has already started. EXPECT_FALSE(IsAuthEnabled()); @@ -218,8 +218,8 @@ unlock_override_policy.SetKey( policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(unlock_override_policy, - base::DictionaryValue(), child_profile_); + user_policy_helper()->SetPolicyAndWait( + unlock_override_policy, base::DictionaryValue(), child_profile_); // Check that the unlock worked and auth is enabled. EXPECT_TRUE(IsAuthEnabled()); @@ -255,8 +255,8 @@ bedtime_policy.SetKey( policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(bedtime_policy, base::DictionaryValue(), - child_profile_); + user_policy_helper()->SetPolicyAndWait( + bedtime_policy, base::DictionaryValue(), child_profile_); // Check that auth is enable, since the bedtime hasn't started. EXPECT_TRUE(IsAuthEnabled()); @@ -270,8 +270,8 @@ unlock_override_policy.SetKey( policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(unlock_override_policy, - base::DictionaryValue(), child_profile_); + user_policy_helper()->SetPolicyAndWait( + unlock_override_policy, base::DictionaryValue(), child_profile_); // Check that the unlock worked and auth is enabled. EXPECT_TRUE(IsAuthEnabled()); @@ -321,8 +321,8 @@ daily_limit_policy.SetKey( policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(daily_limit_policy, - base::DictionaryValue(), child_profile_); + user_policy_helper()->SetPolicyAndWait( + daily_limit_policy, base::DictionaryValue(), child_profile_); // Check that auth is enabled at 10 AM with 0 usage time. EXPECT_TRUE(IsAuthEnabled()); @@ -341,8 +341,8 @@ unlock_override_policy.SetKey( policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(unlock_override_policy, - base::DictionaryValue(), child_profile_); + user_policy_helper()->SetPolicyAndWait( + unlock_override_policy, base::DictionaryValue(), child_profile_); // Check that the unlock worked and auth is enabled. EXPECT_TRUE(IsAuthEnabled()); @@ -389,8 +389,8 @@ bedtime_policy.SetKey( policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(bedtime_policy, base::DictionaryValue(), - child_profile_); + user_policy_helper()->SetPolicyAndWait( + bedtime_policy, base::DictionaryValue(), child_profile_); // Check that auth is disabled, since the bedtime has already started. EXPECT_FALSE(IsAuthEnabled()); @@ -404,8 +404,8 @@ unlock_override_policy.SetKey( policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(unlock_override_policy, - base::DictionaryValue(), child_profile_); + user_policy_helper()->SetPolicyAndWait( + unlock_override_policy, base::DictionaryValue(), child_profile_); // Check that the unlock worked and auth is enabled. EXPECT_TRUE(IsAuthEnabled()); @@ -451,8 +451,8 @@ daily_limit_policy.SetKey( policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(daily_limit_policy, - base::DictionaryValue(), child_profile_); + user_policy_helper()->SetPolicyAndWait( + daily_limit_policy, base::DictionaryValue(), child_profile_); // Check that auth is enabled at 10 AM with 0 usage time. EXPECT_TRUE(IsAuthEnabled()); @@ -471,8 +471,8 @@ unlock_override_policy.SetKey( policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(unlock_override_policy, - base::DictionaryValue(), child_profile_); + user_policy_helper()->SetPolicyAndWait( + unlock_override_policy, base::DictionaryValue(), child_profile_); // Check that the unlock worked and auth is enabled. EXPECT_TRUE(IsAuthEnabled()); @@ -535,8 +535,8 @@ policy.SetKey(policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(), - child_profile_); + user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(), + child_profile_); // Iterate over a week checking that the device is locked properly everyday. for (int i = 0; i < 7; i++) { @@ -591,8 +591,8 @@ policy.SetKey(policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(), - child_profile_); + user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(), + child_profile_); // Iterate over a week checking that the device is locked properly // every day. @@ -645,8 +645,8 @@ policy.SetKey(policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(), - child_profile_); + user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(), + child_profile_); // Verify that device is unlocked at 10 AM. EXPECT_FALSE(IsLocked()); @@ -682,8 +682,8 @@ policy.SetKey(policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(), - child_profile_); + user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(), + child_profile_); // Verify that device is unlocked at 10 AM. EXPECT_FALSE(IsLocked()); @@ -720,8 +720,8 @@ policy.SetKey(policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(), - child_profile_); + user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(), + child_profile_); // Verify that auth is enabled at 10 AM. EXPECT_TRUE(IsAuthEnabled()); @@ -772,8 +772,8 @@ policy.SetKey(policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(), - child_profile_); + user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(), + child_profile_); // Verify that auth is disabled at 8 AM. EXPECT_TRUE(IsAuthEnabled()); @@ -816,8 +816,8 @@ policy.SetKey(policy::key::kUsageTimeLimit, base::Value(utils::PolicyToString(policy_content.get()))); - user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(), - child_profile_); + user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(), + child_profile_); TestScreenTimeControllerObserver observer; ScreenTimeControllerFactory::GetForBrowserContext(child_profile_)
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc index e2ac697..b332d3c 100644 --- a/chrome/browser/chromeos/crostini/crostini_util.cc +++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -72,7 +72,7 @@ ChromeLauncherController* chrome_controller = ChromeLauncherController::instance(); DCHECK(chrome_controller); - chrome_controller->GetShelfSpinnerController()->Close(app_id); + chrome_controller->GetShelfSpinnerController()->CloseSpinner(app_id); } void OnCrostiniRestarted(Profile* profile,
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc index 85eae95..65205896 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -13,6 +13,7 @@ #include "chromeos/constants/chromeos_switches.h" #include "components/session_manager/core/session_manager.h" #include "components/user_manager/user_manager.h" +#include "components/user_manager/user_manager_base.h" #include "services/identity/public/cpp/identity_manager.h" #include "services/identity/public/cpp/identity_test_utils.h" #include "ui/keyboard/public/keyboard_switches.h" @@ -907,7 +908,9 @@ TestCase("requestMountSourceFile"), TestCase("requestMountSourceFile").DisableNativeSmb(), TestCase("providerEject"), - TestCase("providerEject").DisableNativeSmb())); + TestCase("providerEject").DisableNativeSmb(), + TestCase("installNewServiceOnline"), + TestCase("installNewServiceOffline").Offline())); WRAPPED_INSTANTIATE_TEST_SUITE_P( GearMenu, /* gear_menu.js */ @@ -1085,10 +1088,17 @@ base::ScopedAllowBlockingForTesting allow_blocking; const AccountId account_id( AccountId::FromUserEmailGaiaId(info.email, info.gaia_id)); + user_manager::User* user = + user_manager::User::CreateRegularUserForTesting(account_id); + static_cast<user_manager::UserManagerBase*>( + user_manager::UserManager::Get()) + ->AddUserRecordForTesting(user); if (log_in) { session_manager::SessionManager::Get()->CreateSession(account_id, info.hash, false); } + chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting( + user, profile()); user_manager::UserManager::Get()->SaveUserDisplayName( account_id, base::UTF8ToUTF16(info.display_name)); Profile* profile =
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc index 0e501d03..afa8569 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -539,24 +539,6 @@ DISALLOW_COPY_AND_ASSIGN(TestVolume); }; -class OfflineGetDriveConnectionState : public UIThreadExtensionFunction { - public: - OfflineGetDriveConnectionState() = default; - - ResponseAction Run() override { - extensions::api::file_manager_private::DriveConnectionState result; - result.type = "offline"; - return RespondNow( - ArgumentList(extensions::api::file_manager_private:: - GetDriveConnectionState::Results::Create(result))); - } - - private: - ~OfflineGetDriveConnectionState() override = default; - - DISALLOW_COPY_AND_ASSIGN(OfflineGetDriveConnectionState); -}; - base::Lock& GetLockForBlockingDefaultFileTaskRunner() { static base::NoDestructor<base::Lock> lock; return *lock; @@ -1508,6 +1490,10 @@ command_line->AppendSwitch(switches::kIncognito); } + if (IsOfflineTest()) { + command_line->AppendSwitchASCII(chromeos::switches::kShillStub, "clear=1"); + } + std::vector<base::Feature> enabled_features; std::vector<base::Feature> disabled_features; @@ -1666,12 +1652,6 @@ display_service_ = std::make_unique<NotificationDisplayServiceTester>(profile()); - if (IsOfflineTest()) { - ExtensionFunctionRegistry::GetInstance().OverrideFunctionForTesting( - "fileManagerPrivate.getDriveConnectionState", - &NewExtensionFunction<OfflineGetDriveConnectionState>); - } - content::NetworkConnectionChangeSimulator network_change_simulator; network_change_simulator.SetConnectionType( IsOfflineTest() ? network::mojom::ConnectionType::CONNECTION_NONE
diff --git a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc index 16db619e..87fb383 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
@@ -683,6 +683,8 @@ SET_STRING("PASTE_BUTTON_LABEL", IDS_FILE_BROWSER_PASTE_BUTTON_LABEL); SET_STRING("PASTE_INTO_FOLDER_BUTTON_LABEL", IDS_FILE_BROWSER_PASTE_INTO_FOLDER_BUTTON_LABEL); + SET_STRING("PLUGIN_VM_DIRECTORY_LABEL", + IDS_FILE_BROWSER_PLUGIN_VM_DIRECTORY_LABEL); SET_STRING("PREPARING_LABEL", IDS_FILE_BROWSER_PREPARING_LABEL); SET_STRING("QUICK_VIEW_CLOSE_BUTTON_LABEL", IDS_FILE_BROWSER_QUICK_VIEW_CLOSE_BUTTON_LABEL);
diff --git a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc index 9476a97..75b68240 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
@@ -87,6 +87,10 @@ RunTest("menu"); } +IN_PROC_BROWSER_TEST_F(FileManagerUITest, PluginVm) { + RunTest("pluginVm"); +} + IN_PROC_BROWSER_TEST_F(FileManagerUITest, PluginVmShare) { RunTest("pluginVmShare"); }
diff --git a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc index 2d3513d..7d604b2 100644 --- a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc +++ b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc
@@ -128,6 +128,26 @@ std::string() /* entity_id */, policy.SerializeAsString()); } +bool LocalPolicyTestServerMixin::UpdateUserPolicy( + const base::Value& mandatory_policy, + const base::Value& recommended_policy, + const std::string& policy_user) { + DCHECK(policy_test_server_); + base::Value policy_type_dict(base::Value::Type::DICTIONARY); + policy_type_dict.SetKey("mandatory", mandatory_policy.Clone()); + policy_type_dict.SetKey("recommended", recommended_policy.Clone()); + + base::Value managed_users_list(base::Value::Type::LIST); + managed_users_list.GetList().emplace_back("*"); + + server_config_.SetKey(policy::dm_protocol::kChromeUserPolicyType, + std::move(policy_type_dict)); + server_config_.SetKey("managed_users", std::move(managed_users_list)); + server_config_.SetKey("policy_user", base::Value(policy_user)); + server_config_.SetKey("current_key_index", base::Value(0)); + return policy_test_server_->SetConfig(server_config_); +} + void LocalPolicyTestServerMixin::SetFakeAttestationFlow() { g_browser_process->platform_part() ->browser_policy_connector_chromeos()
diff --git a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h index f21ca4a..d9b3f5c 100644 --- a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h +++ b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_CHROMEOS_LOGIN_TEST_LOCAL_POLICY_TEST_SERVER_MIXIN_H_ #include <memory> +#include <string> #include "base/macros.h" #include "base/values.h" @@ -34,6 +35,10 @@ bool UpdateDevicePolicy( const enterprise_management::ChromeDeviceSettingsProto& policy); + bool UpdateUserPolicy(const base::Value& mandatory_policy, + const base::Value& recommended_policy, + const std::string& policy_user); + // Configures and sets expectations for enrollment flow with license // selection. Non-negative values indicate number of available licenses. // There should be at least one license type.
diff --git a/chrome/browser/chromeos/policy/login_policy_test_base.cc b/chrome/browser/chromeos/policy/login_policy_test_base.cc index afdb76f..cdf624e 100644 --- a/chrome/browser/chromeos/policy/login_policy_test_base.cc +++ b/chrome/browser/chromeos/policy/login_policy_test_base.cc
@@ -43,19 +43,15 @@ LoginPolicyTestBase::~LoginPolicyTestBase() = default; -void LoginPolicyTestBase::SetUp() { +void LoginPolicyTestBase::SetUpInProcessBrowserTestFixture() { + OobeBaseTest::SetUpInProcessBrowserTestFixture(); base::DictionaryValue mandatory; GetMandatoryPoliciesValue(&mandatory); base::DictionaryValue recommended; GetRecommendedPoliciesValue(&recommended); - user_policy_helper_.reset(new UserPolicyTestHelper(GetAccount())); - user_policy_helper_->Init(mandatory, recommended); - OobeBaseTest::SetUp(); -} - -void LoginPolicyTestBase::SetUpCommandLine(base::CommandLine* command_line) { - user_policy_helper_->UpdateCommandLine(command_line); - OobeBaseTest::SetUpCommandLine(command_line); + user_policy_helper_.reset( + new UserPolicyTestHelper(GetAccount(), &local_policy_server_)); + user_policy_helper_->SetPolicy(mandatory, recommended); } void LoginPolicyTestBase::SetUpOnMainThread() {
diff --git a/chrome/browser/chromeos/policy/login_policy_test_base.h b/chrome/browser/chromeos/policy/login_policy_test_base.h index a6ac354..10316b6 100644 --- a/chrome/browser/chromeos/policy/login_policy_test_base.h +++ b/chrome/browser/chromeos/policy/login_policy_test_base.h
@@ -10,6 +10,7 @@ #include "base/macros.h" #include "chrome/browser/chromeos/login/test/fake_gaia_mixin.h" +#include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h" #include "chrome/browser/chromeos/login/test/oobe_base_test.h" namespace base { @@ -28,8 +29,7 @@ ~LoginPolicyTestBase() override; // chromeos::OobeBaseTest:: - void SetUp() override; - void SetUpCommandLine(base::CommandLine* command_line) override; + void SetUpInProcessBrowserTestFixture() override; void SetUpOnMainThread() override; virtual void GetMandatoryPoliciesValue(base::DictionaryValue* policy) const; @@ -52,6 +52,7 @@ static const char kEmptyServices[]; chromeos::FakeGaiaMixin fake_gaia_{&mixin_host_, embedded_test_server()}; + chromeos::LocalPolicyTestServerMixin local_policy_server_{&mixin_host_}; private: void SetUpGaiaServerWithAccessTokens();
diff --git a/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc b/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc index bd1e69ff..f9f7c1b3 100644 --- a/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc +++ b/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
@@ -86,7 +86,8 @@ std::unique_ptr<base::DictionaryValue> policy = std::make_unique<base::DictionaryValue>(); policy->SetKey(key::kWallpaperImage, base::Value(value)); - user_policy_helper()->UpdatePolicy(*policy, base::DictionaryValue(), profile); + user_policy_helper()->SetPolicyAndWait(*policy, base::DictionaryValue(), + profile); UserCloudPolicyManagerChromeOS* policy_manager = UserPolicyManagerFactoryChromeOS::GetCloudPolicyManagerForProfile(
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc index e837e9ad..0fafca4 100644 --- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc +++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc
@@ -154,8 +154,8 @@ } IN_PROC_BROWSER_TEST_P(UserCloudPolicyManagerTest, ErrorLoadingPolicy) { - // Delete the policy file - this will cause a 500 error on policy requests. - user_policy_helper()->DeletePolicyFile(); + local_policy_server_.SetExpectedPolicyFetchError(500); + SkipToLoginScreen(); CountNotificationObserver observer( chrome::NOTIFICATION_SESSION_STARTED, @@ -185,8 +185,7 @@ account_id, user_manager::known_user::ProfileRequiresPolicy::kNoPolicyRequired); - // Delete the policy file - this will cause a 500 error on policy requests. - user_policy_helper()->DeletePolicyFile(); + local_policy_server_.SetExpectedPolicyFetchError(500); SkipToLoginScreen(); LogIn(kAccountId, kAccountPassword, kEmptyServices);
diff --git a/chrome/browser/chromeos/policy/user_policy_test_helper.cc b/chrome/browser/chromeos/policy/user_policy_test_helper.cc index a6e5e3d..82b9c666 100644 --- a/chrome/browser/chromeos/policy/user_policy_test_helper.cc +++ b/chrome/browser/chromeos/policy/user_policy_test_helper.cc
@@ -7,18 +7,15 @@ #include <utility> #include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/json/json_writer.h" #include "base/run_loop.h" #include "base/values.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h" #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h" #include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h" #include "chrome/browser/policy/chrome_browser_policy_connector.h" #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/policy/profile_policy_connector_factory.h" -#include "chrome/browser/policy/test/local_policy_test_server.h" #include "chrome/browser/profiles/profile.h" #include "components/policy/core/browser/browser_policy_connector.h" #include "components/policy/core/common/cloud/cloud_policy_client.h" @@ -32,58 +29,18 @@ namespace policy { -namespace { - -std::string BuildPolicy(const base::DictionaryValue& mandatory, - const base::DictionaryValue& recommended, - const std::string& policyType, - const std::string& account_id) { - std::unique_ptr<base::DictionaryValue> policy_type_dict( - new base::DictionaryValue); - policy_type_dict->SetWithoutPathExpansion("mandatory", - mandatory.CreateDeepCopy()); - policy_type_dict->SetWithoutPathExpansion("recommended", - recommended.CreateDeepCopy()); - - std::unique_ptr<base::ListValue> managed_users_list(new base::ListValue); - managed_users_list->AppendString("*"); - - base::DictionaryValue root_dict; - root_dict.SetWithoutPathExpansion(policyType, std::move(policy_type_dict)); - root_dict.SetWithoutPathExpansion("managed_users", - std::move(managed_users_list)); - root_dict.SetKey("policy_user", base::Value(account_id)); - root_dict.SetKey("current_key_index", base::Value(0)); - - std::string json_policy; - base::JSONWriter::WriteWithOptions( - root_dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_policy); - return json_policy; -} - -} // namespace - -UserPolicyTestHelper::UserPolicyTestHelper(const std::string& account_id) - : account_id_(account_id) { -} +UserPolicyTestHelper::UserPolicyTestHelper( + const std::string& account_id, + chromeos::LocalPolicyTestServerMixin* local_policy_server) + : account_id_(account_id), local_policy_server_(local_policy_server) {} UserPolicyTestHelper::~UserPolicyTestHelper() { } -void UserPolicyTestHelper::Init( - const base::DictionaryValue& mandatory_policy, - const base::DictionaryValue& recommended_policy) { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - WritePolicyFile(mandatory_policy, recommended_policy); - - test_server_.reset(new LocalPolicyTestServer(PolicyFilePath())); - ASSERT_TRUE(test_server_->Start()); -} - -void UserPolicyTestHelper::UpdateCommandLine( - base::CommandLine* command_line) const { - command_line->AppendSwitchASCII(policy::switches::kDeviceManagementUrl, - test_server_->GetServiceURL().spec()); +void UserPolicyTestHelper::SetPolicy(const base::DictionaryValue& mandatory, + const base::DictionaryValue& recommended) { + ASSERT_TRUE(local_policy_server_->UpdateUserPolicy(mandatory, recommended, + account_id_)); } void UserPolicyTestHelper::WaitForInitialPolicy(Profile* profile) { @@ -119,11 +76,11 @@ run_loop.Run(); } -void UserPolicyTestHelper::UpdatePolicy( +void UserPolicyTestHelper::SetPolicyAndWait( const base::DictionaryValue& mandatory_policy, const base::DictionaryValue& recommended_policy, Profile* profile) { - WritePolicyFile(mandatory_policy, recommended_policy); + SetPolicy(mandatory_policy, recommended_policy); policy::ProfilePolicyConnector* const profile_connector = policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile); @@ -135,25 +92,4 @@ run_loop.Run(); } -void UserPolicyTestHelper::DeletePolicyFile() { - base::ScopedAllowBlockingForTesting allow_io; - base::DeleteFile(PolicyFilePath(), false); -} - -void UserPolicyTestHelper::WritePolicyFile( - const base::DictionaryValue& mandatory, - const base::DictionaryValue& recommended) { - const std::string policy = BuildPolicy( - mandatory, recommended, dm_protocol::kChromeUserPolicyType, account_id_); - - base::ScopedAllowBlockingForTesting allow_io; - const int bytes_written = - base::WriteFile(PolicyFilePath(), policy.data(), policy.size()); - ASSERT_EQ(static_cast<int>(policy.size()), bytes_written); -} - -base::FilePath UserPolicyTestHelper::PolicyFilePath() const { - return temp_dir_.GetPath().AppendASCII("policy.json"); -} - } // namespace policy
diff --git a/chrome/browser/chromeos/policy/user_policy_test_helper.h b/chrome/browser/chromeos/policy/user_policy_test_helper.h index 5026456c..c3304895 100644 --- a/chrome/browser/chromeos/policy/user_policy_test_helper.h +++ b/chrome/browser/chromeos/policy/user_policy_test_helper.h
@@ -14,29 +14,26 @@ class Profile; namespace base { -class CommandLine; -class FilePath; class DictionaryValue; } -namespace policy { +namespace chromeos { +class LocalPolicyTestServerMixin; +} -class LocalPolicyTestServer; +namespace policy { // This class can be used to apply a user policy to the profile in a // BrowserTest. class UserPolicyTestHelper { public: - explicit UserPolicyTestHelper(const std::string& account_id); + UserPolicyTestHelper( + const std::string& account_id, + chromeos::LocalPolicyTestServerMixin* local_policy_server); virtual ~UserPolicyTestHelper(); - // Must be called after construction to start the policy test server. - void Init(const base::DictionaryValue& mandatory_policy, - const base::DictionaryValue& recommended_policy); - - // Must be used during BrowserTestBase::SetUpCommandLine to direct Chrome to - // the policy test server. - void UpdateCommandLine(base::CommandLine* command_line) const; + void SetPolicy(const base::DictionaryValue& mandatory, + const base::DictionaryValue& recommended); // Can be optionally used to wait for the initial policy to be applied to the // profile. Alternatively, a login can be simulated, which makes it @@ -45,20 +42,13 @@ // Update the policy test server with the given policy. Then refresh and wait // for the new policy being applied to |profile|. - void UpdatePolicy(const base::DictionaryValue& mandatory_policy, - const base::DictionaryValue& recommended_policy, - Profile* profile); - - void DeletePolicyFile(); + void SetPolicyAndWait(const base::DictionaryValue& mandatory_policy, + const base::DictionaryValue& recommended_policy, + Profile* profile); private: - void WritePolicyFile(const base::DictionaryValue& mandatory, - const base::DictionaryValue& recommended); - base::FilePath PolicyFilePath() const; - const std::string account_id_; - base::ScopedTempDir temp_dir_; - std::unique_ptr<LocalPolicyTestServer> test_server_; + chromeos::LocalPolicyTestServerMixin* local_policy_server_; DISALLOW_COPY_AND_ASSIGN(UserPolicyTestHelper); };
diff --git a/chrome/browser/download/offline_item_model.cc b/chrome/browser/download/offline_item_model.cc index a7387fed..ac22f7e2 100644 --- a/chrome/browser/download/offline_item_model.cc +++ b/chrome/browser/download/offline_item_model.cc
@@ -144,7 +144,7 @@ return download::DownloadItem::COMPLETE; case OfflineItemState::CANCELLED: return download::DownloadItem::CANCELLED; - case OfflineItemState::MAX_DOWNLOAD_STATE: + case OfflineItemState::NUM_ENTRIES: NOTREACHED(); return download::DownloadItem::CANCELLED; } @@ -181,7 +181,7 @@ FALLTHROUGH; case OfflineItemState::CANCELLED: return true; - case OfflineItemState::MAX_DOWNLOAD_STATE: + case OfflineItemState::NUM_ENTRIES: NOTREACHED(); } return false;
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc index 574534c1..c00c75c3 100644 --- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc +++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
@@ -29,11 +29,11 @@ #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" #include "chrome/browser/ui/web_applications/web_app_dialog_utils.h" #include "chrome/browser/ui/webui/extensions/extension_icon_source.h" +#include "chrome/browser/web_applications/components/app_registrar.h" #include "chrome/browser/web_applications/components/install_manager.h" #include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/browser/web_applications/components/web_app_provider_base.h" #include "chrome/browser/web_applications/components/web_app_utils.h" -#include "chrome/browser/web_applications/extensions/bookmark_app_util.h" #include "chrome/common/extensions/extension_metrics.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "chrome/common/web_application_info.h" @@ -368,7 +368,10 @@ bool ChromeManagementAPIDelegate::IsWebAppInstalled( content::BrowserContext* context, const GURL& web_app_url) const { - return extensions::BookmarkOrHostedAppInstalled(context, web_app_url); + auto* provider = web_app::WebAppProviderBase::GetProviderBase( + Profile::FromBrowserContext(context)); + DCHECK(provider); + return provider->registrar().IsInstalled(web_app_url); } bool ChromeManagementAPIDelegate::CanContextInstallWebApps(
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc index 1616369c7..c4e8062 100644 --- a/chrome/browser/extensions/api/management/management_apitest.cc +++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -16,7 +16,8 @@ #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/web_applications/extensions/bookmark_app_util.h" +#include "chrome/browser/web_applications/components/app_registrar.h" +#include "chrome/browser/web_applications/components/web_app_provider_base.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_constants.h" #include "extensions/browser/api/management/management_api.h" @@ -258,12 +259,13 @@ chrome::SetAutoAcceptPWAInstallConfirmationForTesting(true); const GURL good_web_app_url = https_test_server_.GetURL(kGoodWebAppURL); - EXPECT_FALSE(extensions::BookmarkOrHostedAppInstalled(browser()->profile(), - good_web_app_url)); + + auto* provider = + web_app::WebAppProviderBase::GetProviderBase(browser()->profile()); + EXPECT_FALSE(provider->registrar().IsInstalled(good_web_app_url)); RunTest(kGoodWebAppURL, kBackground, true /* from_webstore */); - EXPECT_TRUE(extensions::BookmarkOrHostedAppInstalled(browser()->profile(), - good_web_app_url)); + EXPECT_TRUE(provider->registrar().IsInstalled(good_web_app_url)); chrome::SetAutoAcceptPWAInstallConfirmationForTesting(false); }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index acac44fc..044a42c 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -600,8 +600,8 @@ }, { "name": "discover-app", - // "owners": [ "your-team" ], - "expiry_milestone": 76 + "owners": [ "alemate" ], + "expiry_milestone": 80 }, { "name": "document-passive-event-listeners", @@ -792,6 +792,11 @@ "expiry_milestone": 76 }, { + "name": "enable-autofill-do-not-migrate-unsupported-local-cards", + "owners": [ "sujiezhu@google.com", "jsaul@google.com" ], + "expiry_milestone": 80 + }, + { "name": "enable-autofill-do-not-upload-save-unsupported-cards", "owners": [ "annelim@google.com", "jsaul@google.com" ], "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 52029d1..11ddf2f 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -430,6 +430,12 @@ "offering card upload to Google Payments, the offer-to-save dialog " "displays an expiration date selector."; +const char kEnableAutofillDoNotMigrateUnsupportedLocalCardsName[] = + "Prevents local card migration on local cards from unsupported networks"; +const char kEnableAutofillDoNotMigrateUnsupportedLocalCardsDescription[] = + "If enabled, local cards from unsupported networks will not be offered " + "local card migration."; + const char kEnableAutofillDoNotUploadSaveUnsupportedCardsName[] = "Prevents upload save on cards from unsupported networks"; const char kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 4903a2d7..0e13d9b 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -285,6 +285,9 @@ extern const char kEnableAutofillCreditCardUploadEditableExpirationDateDescription[]; +extern const char kEnableAutofillDoNotMigrateUnsupportedLocalCardsName[]; +extern const char kEnableAutofillDoNotMigrateUnsupportedLocalCardsDescription[]; + extern const char kEnableAutofillDoNotUploadSaveUnsupportedCardsName[]; extern const char kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription[];
diff --git a/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc b/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc index 1d8a518a..f1ff805f8 100644 --- a/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc +++ b/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc
@@ -200,11 +200,6 @@ void SetUpCommandLine(base::CommandLine* cl) override { ChromeMimeHandlerViewTestBase::SetUpCommandLine(cl); is_cross_process_mode_ = GetParam(); - // TODO(ekaramad): All these tests started timing out on ChromeOS (https:// - // crbug.com/949565). -#if defined(OS_CHROMEOS) - is_cross_process_mode_ = false; -#endif if (is_cross_process_mode_) { scoped_feature_list_.InitAndEnableFeature( features::kMimeHandlerViewInCrossProcessFrame); @@ -560,6 +555,12 @@ IN_PROC_BROWSER_TEST_P(ChromeMimeHandlerViewCrossProcessTest, UMA_SameOriginResource) { +#if defined(OS_CHROMEOS) + // TODO(ekaramad): This test started timing out on ChromeOS (https:// + // crbug.com/949565). + if (GetParam()) + return; +#endif auto url = embedded_test_server()->GetURL("a.com", "/testPostMessageUMA.csv"); auto page_url = embedded_test_server()->GetURL( "a.com", @@ -584,6 +585,12 @@ IN_PROC_BROWSER_TEST_P(ChromeMimeHandlerViewCrossProcessTest, UMA_CrossOriginResource) { +#if defined(OS_CHROMEOS) + // TODO(ekaramad): This test started timing out on ChromeOS (https:// + // crbug.com/949565). + if (GetParam()) + return; +#endif auto url = embedded_test_server()->GetURL("b.com", "/testPostMessageUMA.csv"); auto page_url = embedded_test_server()->GetURL( "a.com",
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc index 432e9cf4..60c5f63 100644 --- a/chrome/browser/policy/policy_browsertest.cc +++ b/chrome/browser/policy/policy_browsertest.cc
@@ -1279,7 +1279,8 @@ base::ListValue allowed_languages; allowed_languages.AppendString("fr"); policy->SetKey(key::kAllowedLanguages, std::move(allowed_languages)); - user_policy_helper()->UpdatePolicy(*policy, base::DictionaryValue(), profile); + user_policy_helper()->SetPolicyAndWait(*policy, base::DictionaryValue(), + profile); } IN_PROC_BROWSER_TEST_F(LoginPolicyTestBase, AllowedLanguages) { @@ -1351,7 +1352,8 @@ allowed_input_methods.AppendString("xkb:de::ger"); allowed_input_methods.AppendString("invalid_value_will_be_ignored"); policy->SetKey(key::kAllowedInputMethods, std::move(allowed_input_methods)); - user_policy_helper()->UpdatePolicy(*policy, base::DictionaryValue(), profile); + user_policy_helper()->SetPolicyAndWait(*policy, base::DictionaryValue(), + profile); // Only "xkb:fr::fra", "xkb:de::ger" should be allowed, current input method // should be "xkb:fr::fra", enabling "xkb:us::eng" should not be possible, @@ -1369,8 +1371,8 @@ invalid_input_methods.AppendString("invalid_value_will_be_ignored"); policy_invalid->SetKey(key::kAllowedInputMethods, std::move(invalid_input_methods)); - user_policy_helper()->UpdatePolicy(*policy_invalid, base::DictionaryValue(), - profile); + user_policy_helper()->SetPolicyAndWait(*policy_invalid, + base::DictionaryValue(), profile); // No restrictions and current input method should still be "xkb:fr::fra". EXPECT_EQ(0U, ime_state->GetAllowedInputMethods().size()); @@ -1379,8 +1381,8 @@ EXPECT_TRUE(ime_state->EnableInputMethod(input_methods[2])); // Allow all input methods again. - user_policy_helper()->UpdatePolicy(base::DictionaryValue(), - base::DictionaryValue(), profile); + user_policy_helper()->SetPolicyAndWait(base::DictionaryValue(), + base::DictionaryValue(), profile); // No restrictions and current input method should still be "xkb:fr::fra". EXPECT_EQ(0U, ime_state->GetAllowedInputMethods().size());
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc b/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc index 1453aa6..fb0b66c 100644 --- a/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc +++ b/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc
@@ -7,16 +7,17 @@ #include <set> #include "build/build_config.h" +#include "chrome/browser/apps/app_service/app_service_proxy_factory.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/app_list/app_list_syncable_service.h" +#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/prefs/pref_service.h" #include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system_provider.h" #include "extensions/browser/extensions_browser_client.h" -#include "chrome/browser/chromeos/profiles/profile_helper.h" -#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h" namespace app_list { @@ -65,6 +66,7 @@ dependent_factories.insert( extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); dependent_factories.insert(ArcAppListPrefsFactory::GetInstance()); + dependent_factories.insert(apps::AppServiceProxyFactory::GetInstance()); for (FactorySet::iterator it = dependent_factories.begin(); it != dependent_factories.end(); ++it) {
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc index aa2040c..9d152886 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -565,8 +565,7 @@ return ash::SHELF_ACTION_WINDOW_ACTIVATED; } -void ChromeLauncherController::ActiveUserChanged( - const std::string& user_email) { +void ChromeLauncherController::ActiveUserChanged(const AccountId& account_id) { // Store the order of running applications for the user which gets inactive. RememberUnpinnedRunningApplicationOrder(); // Coming here the default profile is already switched. All profile specific @@ -576,17 +575,21 @@ // set it as active. AttachProfile(ProfileManager::GetActiveUserProfile()); // Update the V1 applications. - browser_status_monitor_->ActiveUserChanged(user_email); + browser_status_monitor_->ActiveUserChanged(account_id.GetUserEmail()); + // Save/restore spinners belonging to the old/new user. Must be called before + // notifying the AppWindowControllers, as some of them assume spinners owned + // by the new user have already been added to the shelf. + shelf_spinner_controller_->ActiveUserChanged(account_id); // Switch the running applications to the new user. for (auto& controller : app_window_controllers_) - controller->ActiveUserChanged(user_email); + controller->ActiveUserChanged(account_id.GetUserEmail()); // Update the user specific shell properties from the new user profile. // Shelf preferences are loaded in ChromeLauncherController::AttachProfile. UpdateAppLaunchersFromSync(); // Restore the order of running, but unpinned applications for the activated // user. - RestoreUnpinnedRunningApplicationOrder(user_email); + RestoreUnpinnedRunningApplicationOrder(account_id.GetUserEmail()); } void ChromeLauncherController::AdditionalUserAddedToSession(Profile* profile) {
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h index fe579d24f..f47f8f81 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -23,6 +23,7 @@ #include "chrome/browser/ui/ash/launcher/discover_window_observer.h" #include "chrome/browser/ui/ash/launcher/launcher_app_updater.h" #include "chrome/browser/ui/ash/launcher/settings_window_observer.h" +#include "components/account_id/account_id.h" #include "components/prefs/pref_change_registrar.h" #include "components/sync_preferences/pref_service_syncable_observer.h" #include "mojo/public/cpp/bindings/associated_binding.h" @@ -169,7 +170,7 @@ bool allow_minimize); // Called when the active user has changed. - void ActiveUserChanged(const std::string& user_email); + void ActiveUserChanged(const AccountId& account_id); // Called when a user got added to the session. void AdditionalUserAddedToSession(Profile* profile);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc index beef424..873e3cdb 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -63,6 +63,7 @@ #include "chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.h" #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h" #include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h" +#include "chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h" #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_client.h" #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.h" @@ -659,7 +660,10 @@ } // Create and initialize the controller, owned by the test shell delegate. - void InitLauncherController() { CreateLauncherController()->Init(); } + void InitLauncherController() { + CreateLauncherController()->Init(); + FlushBindings(); + } // Create and initialize the controller; create a tab and show the browser. void InitLauncherControllerWithBrowser() { @@ -1308,6 +1312,9 @@ // Call FlushBindings() to ensure ash has completed processing of the // switch. FlushBindings(); + + // TODO(crbug.com/956841) This should be redundant with the FlushBindings + // call, but removing it breaks some tests. launcher_controller_->browser_status_monitor_for_test()->ActiveUserChanged( account_id.GetUserEmail()); @@ -3608,6 +3615,108 @@ } } +// Checks that spinners are hidden and restored on profile switching +TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest, + SpinnersUpdateOnUserSwitch) { + InitLauncherController(); + + const AccountId account_id( + multi_user_util::GetAccountIdFromProfile(profile())); + const std::string user2 = "user2"; + const TestingProfile* profile2 = CreateMultiUserProfile(user2); + const AccountId account_id2( + multi_user_util::GetAccountIdFromProfile(profile2)); + + const std::string app_id = extension1_->id(); + extension_service_->AddExtension(extension1_.get()); + + EXPECT_EQ(3, model_->item_count()); + EXPECT_FALSE( + launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); + + // Add a spinner to the shelf + launcher_controller_->GetShelfSpinnerController()->AddSpinnerToShelf( + app_id, std::make_unique<ShelfSpinnerItemController>(app_id)); + EXPECT_EQ(4, model_->item_count()); + EXPECT_TRUE( + launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); + + // Switch to a new profile + SwitchActiveUser(account_id2); + EXPECT_EQ(3, model_->item_count()); + EXPECT_FALSE( + launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); + + // Switch back + SwitchActiveUser(account_id); + EXPECT_EQ(4, model_->item_count()); + EXPECT_TRUE( + launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); + + // Close the spinner + launcher_controller_->GetShelfSpinnerController()->CloseSpinner(app_id); + EXPECT_EQ(3, model_->item_count()); + EXPECT_FALSE( + launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); +} + +// Checks that pinned spinners are hidden and restored on profile switching +// but are not removed when the spinner closes. +TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest, + PinnedSpinnersUpdateOnUserSwitch) { + InitLauncherController(); + + const AccountId account_id( + multi_user_util::GetAccountIdFromProfile(profile())); + const std::string user2 = "user2"; + const TestingProfile* profile2 = CreateMultiUserProfile(user2); + const AccountId account_id2( + multi_user_util::GetAccountIdFromProfile(profile2)); + + const std::string app_id = extension1_->id(); + extension_service_->AddExtension(extension1_.get()); + + EXPECT_EQ(3, model_->item_count()); + EXPECT_FALSE( + launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); + + // Pin an app to the shelf + launcher_controller_->PinAppWithID(app_id); + EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id)); + EXPECT_EQ(4, model_->item_count()); + EXPECT_FALSE( + launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); + + // Activate the spinner + launcher_controller_->GetShelfSpinnerController()->AddSpinnerToShelf( + app_id, std::make_unique<ShelfSpinnerItemController>(app_id)); + EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id)); + EXPECT_EQ(4, model_->item_count()); + EXPECT_TRUE( + launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); + + // Switch to a new profile + SwitchActiveUser(account_id2); + EXPECT_FALSE(launcher_controller_->IsAppPinned(app_id)); + EXPECT_EQ(3, model_->item_count()); + EXPECT_FALSE( + launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); + + // Switch back + SwitchActiveUser(account_id); + EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id)); + EXPECT_EQ(4, model_->item_count()); + EXPECT_TRUE( + launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); + + // Close the spinner + launcher_controller_->GetShelfSpinnerController()->CloseSpinner(app_id); + EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id)); + EXPECT_EQ(4, model_->item_count()); + EXPECT_FALSE( + launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); +} + // Checks that the generated menu list properly activates items. TEST_F(ChromeLauncherControllerTest, V1AppMenuExecution) { InitLauncherControllerWithBrowser();
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc index 6fc603e1..a9b74e3 100644 --- a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc +++ b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc
@@ -17,6 +17,7 @@ #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h" #include "chrome/browser/chromeos/crostini/crostini_util.h" #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/ash/launcher/app_window_base.h" #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h" @@ -205,9 +206,16 @@ return; } + // Currently Crostini can only be used from the primary profile. In the + // future, this may be replaced by some way of matching the container that + // runs this app with the user that owns it. + const AccountId& primary_account_id = + user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId(); + crostini::CrostiniRegistryService* registry_service = crostini::CrostiniRegistryServiceFactory::GetForProfile( - owner()->profile()); + chromeos::ProfileHelper::Get()->GetProfileByAccountId( + primary_account_id)); const std::string& shelf_app_id = registry_service->GetCrostiniShelfAppId( exo::GetShellApplicationId(window), exo::GetShellStartupId(window)); // Windows without an application id set will get filtered out here. @@ -233,8 +241,7 @@ // Prevent Crostini window from showing up after user switch. MultiUserWindowManagerClient::GetInstance()->SetWindowOwner( - window, - user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId()); + window, primary_account_id); RegisterAppWindow(window, shelf_app_id);
diff --git a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.cc b/chrome/browser/ui/ash/launcher/shelf_spinner_controller.cc index defee205..5aaf0a7 100644 --- a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.cc +++ b/chrome/browser/ui/ash/launcher/shelf_spinner_controller.cc
@@ -4,15 +4,17 @@ #include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h" -#include <memory> +#include <vector> #include "ash/public/cpp/shelf_model.h" #include "base/bind.h" #include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/chromeos/crostini/crostini_registry_service.h" #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" #include "chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h" +#include "components/user_manager/user_manager.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/canvas_image_source.h" #include "ui/gfx/image/image_skia_operations.h" @@ -61,6 +63,14 @@ ShelfSpinnerController::ShelfSpinnerController(ChromeLauncherController* owner) : owner_(owner), weak_ptr_factory_(this) { owner->shelf_model()->AddObserver(this); + if (user_manager::UserManager::IsInitialized()) { + if (auto* active_user = user_manager::UserManager::Get()->GetActiveUser()) + current_account_id_ = active_user->GetAccountId(); + else + LOG(ERROR) << "Failed to get active user, UserManager returned null"; + } else { + LOG(ERROR) << "Failed to get active user, UserManager is not initialized"; + } } ShelfSpinnerController::~ShelfSpinnerController() { @@ -83,31 +93,61 @@ image->size()); } -void ShelfSpinnerController::Close(const std::string& app_id) { - // Code below may invalidate passed |app_id|. Use local variable for safety. - const std::string safe_app_id(app_id); - - AppControllerMap::const_iterator it = app_controller_map_.find(safe_app_id); - if (it == app_controller_map_.end()) +void ShelfSpinnerController::HideSpinner(const std::string& app_id) { + if (!RemoveSpinnerFromControllerMap(app_id)) return; - const ash::ShelfID shelf_id(safe_app_id); + const ash::ShelfID shelf_id(app_id); + + // If the app whose spinner is being hidden is pinned, we don't want to un-pin + // it when we remove it from the shelf, so disable pin syncing while we update + // things. + auto pin_disabler = owner_->GetScopedPinSyncDisabler(); + // The static_cast here is safe, because if the delegate were not a + // ShelfSpinnerItemController then ShelfItemDelegateChanged would have been + // called and we would not have reached this place. + auto delegate = + owner_->shelf_model()->RemoveItemAndTakeShelfItemDelegate(shelf_id); + std::unique_ptr<ShelfSpinnerItemController> cast_delegate( + static_cast<ShelfSpinnerItemController*>(delegate.release())); + + hidden_app_controller_map_.emplace( + current_account_id_, std::make_pair(app_id, std::move(cast_delegate))); +} + +void ShelfSpinnerController::CloseSpinner(const std::string& app_id) { + if (!RemoveSpinnerFromControllerMap(app_id)) + return; + + owner_->CloseLauncherItem(ash::ShelfID(app_id)); + UpdateShelfItemIcon(app_id); +} + +bool ShelfSpinnerController::RemoveSpinnerFromControllerMap( + const std::string& app_id) { + AppControllerMap::const_iterator it = app_controller_map_.find(app_id); + if (it == app_controller_map_.end()) + return false; + + const ash::ShelfID shelf_id(app_id); DCHECK_EQ(it->second, owner_->shelf_model()->GetShelfItemDelegate(shelf_id)); app_controller_map_.erase(it); - owner_->CloseLauncherItem(shelf_id); - UpdateShelfItemIcon(safe_app_id); + + return true; } void ShelfSpinnerController::CloseCrostiniSpinners() { std::vector<std::string> app_ids_to_close; crostini::CrostiniRegistryService* registry_service = - crostini::CrostiniRegistryServiceFactory::GetForProfile(OwnerProfile()); + crostini::CrostiniRegistryServiceFactory::GetForProfile( + chromeos::ProfileHelper::Get()->GetProfileByAccountId( + current_account_id_)); for (const auto& app_id_controller_pair : app_controller_map_) { if (registry_service->IsCrostiniShelfAppId(app_id_controller_pair.first)) app_ids_to_close.push_back(app_id_controller_pair.first); } for (const auto& app_id : app_ids_to_close) - Close(app_id); + CloseSpinner(app_id); } bool ShelfSpinnerController::HasApp(const std::string& app_id) const { @@ -132,12 +172,43 @@ ash::ShelfItemDelegate* old_delegate, ash::ShelfItemDelegate* delegate) { auto it = app_controller_map_.find(id.app_id); - if (it == app_controller_map_.end()) + if (it != app_controller_map_.end()) { + app_controller_map_.erase(it); + UpdateShelfItemIcon(id.app_id); + } +} + +void ShelfSpinnerController::ActiveUserChanged(const AccountId& account_id) { + if (account_id == current_account_id_) { + LOG(WARNING) << "Tried switching to currently active user"; return; - DCHECK_EQ(it->second, old_delegate); - app_controller_map_.erase(it); - // Update the icon one more time so it doesn't remain stuck with a spinner. - UpdateShelfItemIcon(id.app_id); + } + + std::vector<std::string> to_hide; + std::vector< + std::pair<std::string, std::unique_ptr<ShelfSpinnerItemController>>> + to_show; + + for (const auto& app_id : app_controller_map_) + to_hide.push_back(app_id.first); + for (auto it = hidden_app_controller_map_.lower_bound(account_id); + it != hidden_app_controller_map_.upper_bound(account_id); it++) { + to_show.push_back(std::move(it->second)); + } + + hidden_app_controller_map_.erase( + hidden_app_controller_map_.lower_bound(account_id), + hidden_app_controller_map_.upper_bound(account_id)); + + for (const auto& app_id : to_hide) + HideSpinner(app_id); + + for (auto& app_id_delegate_pair : to_show) { + AddSpinnerToShelf(app_id_delegate_pair.first, + std::move(app_id_delegate_pair.second)); + } + + current_account_id_ = account_id; } void ShelfSpinnerController::UpdateShelfItemIcon(const std::string& app_id) {
diff --git a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.h b/chrome/browser/ui/ash/launcher/shelf_spinner_controller.h index bea34f2..c3fa453 100644 --- a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.h +++ b/chrome/browser/ui/ash/launcher/shelf_spinner_controller.h
@@ -8,13 +8,17 @@ #include <stdint.h> #include <map> +#include <memory> #include <string> +#include <utility> #include "ash/public/cpp/shelf_model_observer.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h" +#include "components/account_id/account_id.h" +class ShelfItemDelegate; class ShelfSpinnerItemController; class ChromeLauncherController; class Profile; @@ -44,9 +48,10 @@ void MaybeApplySpinningEffect(const std::string& app_id, gfx::ImageSkia* image); - // Closes Shelf item if it has ShelfSpinnerItemController controller - // and removes entry from the list of tracking items. - void Close(const std::string& app_id); + // Finishes spinning on an icon. If an icon is pinned it will be kept on the + // shelf as a shortcut, otherwise it will be removed without storing the + // delegate. + void CloseSpinner(const std::string& app_id); // Closes all Crostini spinner shelf items. // This should be avoided when possible. @@ -54,25 +59,48 @@ Profile* OwnerProfile(); + // Hide all the spinners associated with the old user, and restore to the + // shelf any spinners associated with the new active user. Called by + // ChromeLauncherController when the active user is changed. + void ActiveUserChanged(const AccountId& account_id); + // ash::ShelfModelObserver: void ShelfItemDelegateChanged(const ash::ShelfID& id, ash::ShelfItemDelegate* old_delegate, ash::ShelfItemDelegate* delegate) override; private: - // Defines mapping of a shelf app id to a corresponded controller. Shelf app + // Defines mapping of a shelf app id to a corresponding controller. Shelf app // id is optional mapping (for example, Play Store to ARC Host Support). using AppControllerMap = std::map<std::string, ShelfSpinnerItemController*>; + // Defines a mapping from account id to (app id, ShelfSpinnerItemController) + // for spinners that are not currently on the shelf. Taking ownership of these + // delegates allows us to reuse them if we need to add the spinner back on to + // the shelf. + using HiddenAppControllerMap = std::multimap< + AccountId, + std::pair<std::string, std::unique_ptr<ShelfSpinnerItemController>>>; void UpdateApps(); void UpdateShelfItemIcon(const std::string& app_id); void RegisterNextUpdate(); + // Removes the spinner with id |app_id| from |app_controller_map_| and returns + // true if it was present, false otherwise. + bool RemoveSpinnerFromControllerMap(const std::string& app_id); + + // Removes a spinner from the shelf and stores the delegate for later + // restoration. Used when the user switches from one profile to another. + void HideSpinner(const std::string& app_id); // Unowned pointers. ChromeLauncherController* owner_; + AccountId current_account_id_; + AppControllerMap app_controller_map_; + HiddenAppControllerMap hidden_app_controller_map_; + // Always keep this the last member of this class. base::WeakPtrFactory<ShelfSpinnerController> weak_ptr_factory_;
diff --git a/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.cc b/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.cc index ea6bb9e3..d044702 100644 --- a/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.cc +++ b/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.cc
@@ -21,7 +21,7 @@ void ShelfSpinnerItemController::SetHost( const base::WeakPtr<ShelfSpinnerController>& host) { - DCHECK(!host_); + DCHECK(!host_ || host_.get() == host.get()); host_ = host; } @@ -55,6 +55,10 @@ } void ShelfSpinnerItemController::Close() { - if (host_) - host_->Close(app_id()); + if (host_) { + // CloseSpinner can result in |app_id| being deleted, so make a copy of it + // first. + const std::string safe_app_id = app_id(); + host_->CloseSpinner(safe_app_id); + } }
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_util.cc b/chrome/browser/ui/ash/multi_user/multi_user_util.cc index 6787c13..7b439b4 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_util.cc +++ b/chrome/browser/ui/ash/multi_user/multi_user_util.cc
@@ -12,7 +12,7 @@ namespace multi_user_util { -AccountId GetAccountIdFromProfile(Profile* profile) { +AccountId GetAccountIdFromProfile(const Profile* profile) { // This will guarantee an nonempty AccountId be returned if a valid profile is // provided. const user_manager::User* user =
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_util.h b/chrome/browser/ui/ash/multi_user/multi_user_util.h index 476e68f..26b5a93 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_util.h +++ b/chrome/browser/ui/ash/multi_user/multi_user_util.h
@@ -17,7 +17,7 @@ namespace multi_user_util { // Get the user id from a given profile. -AccountId GetAccountIdFromProfile(Profile* profile); +AccountId GetAccountIdFromProfile(const Profile* profile); // Get the user id from an email address. AccountId GetAccountIdFromEmail(const std::string& email);
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc index cf57767c..f9d4e6d 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc +++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc
@@ -428,10 +428,8 @@ ChromeLauncherController* chrome_launcher_controller = ChromeLauncherController::instance(); // Some unit tests have no ChromeLauncherController. - if (chrome_launcher_controller) { - chrome_launcher_controller->ActiveUserChanged( - current_account_id_.GetUserEmail()); - } + if (chrome_launcher_controller) + chrome_launcher_controller->ActiveUserChanged(current_account_id_); } void MultiUserWindowManagerClientImpl::OnDidSwitchActiveAccount() {
diff --git a/chrome/browser/ui/autofill/autofill_popup_layout_model.cc b/chrome/browser/ui/autofill/autofill_popup_layout_model.cc index b52eef1..f5c9f57 100644 --- a/chrome/browser/ui/autofill/autofill_popup_layout_model.cc +++ b/chrome/browser/ui/autofill/autofill_popup_layout_model.cc
@@ -71,16 +71,17 @@ {autofill::kMirCard, IDR_AUTOFILL_CC_MIR, IDS_AUTOFILL_CC_MIR}, {autofill::kUnionPay, IDR_AUTOFILL_CC_UNIONPAY, IDS_AUTOFILL_CC_UNION_PAY}, {autofill::kVisaCard, IDR_AUTOFILL_CC_VISA, IDS_AUTOFILL_CC_VISA}, -#if defined(GOOGLE_CHROME_BUILD) - {"googlePay", IDR_ANDROID_AUTOFILL_GOOGLE_PAY, kResourceNotFoundId}, -#endif // GOOGLE_CHROME_BUILD #if defined(OS_ANDROID) {"httpWarning", IDR_AUTOFILL_HTTP_WARNING, kResourceNotFoundId}, {"httpsInvalid", IDR_AUTOFILL_HTTPS_INVALID_WARNING, kResourceNotFoundId}, {"scanCreditCardIcon", IDR_AUTOFILL_CC_SCAN_NEW, kResourceNotFoundId}, {"settings", IDR_AUTOFILL_SETTINGS, kResourceNotFoundId}, {"create", IDR_AUTOFILL_CREATE, kResourceNotFoundId}, +#if defined(GOOGLE_CHROME_BUILD) + {"googlePay", IDR_ANDROID_AUTOFILL_GOOGLE_PAY, kResourceNotFoundId}, +#endif // GOOGLE_CHROME_BUILD #elif defined(GOOGLE_CHROME_BUILD) + {"googlePay", IDR_AUTOFILL_GOOGLE_PAY, kResourceNotFoundId}, {"googlePayDark", IDR_AUTOFILL_GOOGLE_PAY_DARK, kResourceNotFoundId}, #endif // GOOGLE_CHROME_BUILD };
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc index 2a06a54..f4c8d5a 100644 --- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc +++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -795,11 +795,9 @@ // content. RenderFrameHost* subframe = content::ChildFrameAt(tab->GetMainFrame(), 0); EXPECT_EQ(app_url, subframe->GetLastCommittedURL()); - std::string result; - EXPECT_TRUE(ExecuteScriptAndExtractString( - subframe, "window.domAutomationController.send(document.body.innerText);", - &result)); - EXPECT_EQ("This page has no title.", result); + EXPECT_EQ( + "This page has no title.", + EvalJs(subframe, "document.body.innerText.trim();").ExtractString()); } // Check that no assertions are hit when showing a permission request bubble.
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.cc b/chrome/browser/ui/views/page_action/pwa_install_view.cc index 6bce56ce..5cf5609 100644 --- a/chrome/browser/ui/views/page_action/pwa_install_view.cc +++ b/chrome/browser/ui/views/page_action/pwa_install_view.cc
@@ -30,20 +30,17 @@ if (!web_contents) return false; - banners::AppBannerManager* manager = - banners::AppBannerManager::FromWebContents(web_contents); + auto* manager = banners::AppBannerManager::FromWebContents(web_contents); // May not be present e.g. in incognito mode. if (!manager) return false; - bool is_installable = manager->IsProbablyInstallable(); - bool is_installed = - web_app::WebAppTabHelperBase::FromWebContents(web_contents) - ->HasAssociatedApp(); - bool show_install_button = is_installable && !is_installed; - // TODO(crbug.com/907351): When installability is unknown and we're still in - // the scope of a previously-determined installable site, display it as still - // being installable. + bool is_probably_installable = manager->IsProbablyInstallable(); + auto* tab_helper = + web_app::WebAppTabHelperBase::FromWebContents(web_contents); + bool is_installed = tab_helper && tab_helper->HasAssociatedApp(); + + bool show_install_button = is_probably_installable && !is_installed; if (show_install_button && manager->MaybeConsumeInstallAnimation()) AnimateIn(base::nullopt);
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc index 5c953070..bc89f9d 100644 --- a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc +++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
@@ -10,13 +10,13 @@ #include "base/bind.h" #include "base/callback.h" #include "base/scoped_observer.h" -#include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/extensions/bookmark_app_helper.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/installable/installable_metrics.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/web_applications/components/install_finalizer.h" #include "chrome/browser/web_applications/components/install_manager_observer.h" #include "chrome/browser/web_applications/components/install_options.h" #include "chrome/browser/web_applications/components/web_app_constants.h" @@ -26,7 +26,6 @@ #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "extensions/browser/extension_system.h" -#include "extensions/common/extension.h" namespace extensions { @@ -252,8 +251,10 @@ } // namespace -BookmarkAppInstallManager::BookmarkAppInstallManager(Profile* profile) - : InstallManager(profile) { +BookmarkAppInstallManager::BookmarkAppInstallManager( + Profile* profile, + web_app::InstallFinalizer* finalizer) + : InstallManager(profile), finalizer_(finalizer) { bookmark_app_helper_factory_ = base::BindRepeating( [](Profile* profile, const WebApplicationInfo& web_app_info, content::WebContents* web_contents, @@ -380,23 +381,14 @@ ExtensionSystem::Get(profile())->extension_service(); DCHECK(extension_service); - const Extension* extension = extension_service->GetInstalledExtension(app_id); - - // Return if there are no bookmark app details that need updating. - const std::string extension_sync_data_name = - base::UTF16ToUTF8(web_application_info->title); - const std::string bookmark_app_description = - base::UTF16ToUTF8(web_application_info->description); - if (extension && - extension->non_localized_name() == extension_sync_data_name && - extension->description() == bookmark_app_description) { + if (finalizer_->CanSkipAppUpdateForSync(app_id, *web_application_info)) return; - } #if defined(OS_CHROMEOS) - const bool is_locally_installed = true; + bool is_locally_installed = true; #else - const bool is_locally_installed = extension != nullptr; + bool is_locally_installed = + extension_service->GetInstalledExtension(app_id) != nullptr; #endif CreateOrUpdateBookmarkApp(extension_service, web_application_info.get(),
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h index 27ca81f..77e1336 100644 --- a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h +++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
@@ -20,6 +20,7 @@ } namespace web_app { +class InstallFinalizer; class WebAppDataRetriever; } @@ -31,7 +32,8 @@ // crbug.com/915043. class BookmarkAppInstallManager final : public web_app::InstallManager { public: - explicit BookmarkAppInstallManager(Profile* profile); + BookmarkAppInstallManager(Profile* profile, + web_app::InstallFinalizer* finalizer); ~BookmarkAppInstallManager() override; // InstallManager: @@ -83,6 +85,7 @@ private: BookmarkAppHelperFactory bookmark_app_helper_factory_; DataRetrieverFactory data_retriever_factory_; + web_app::InstallFinalizer* finalizer_; DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallManager); };
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc index 4c3ace7..b7ce597 100644 --- a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc +++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc
@@ -19,6 +19,7 @@ #include "chrome/browser/web_applications/components/install_options.h" #include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/browser/web_applications/test/test_data_retriever.h" +#include "chrome/browser/web_applications/test/test_install_finalizer.h" #include "chrome/common/web_application_info.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/testing_profile.h" @@ -33,7 +34,9 @@ void SetUp() override { ChromeRenderViewHostTestHarness::SetUp(); - install_manager_ = std::make_unique<BookmarkAppInstallManager>(profile()); + install_finalizer_ = std::make_unique<web_app::TestInstallFinalizer>(); + install_manager_ = std::make_unique<BookmarkAppInstallManager>( + profile(), install_finalizer_.get()); extensions::TestExtensionSystem* test_extension_system = static_cast<extensions::TestExtensionSystem*>( @@ -69,6 +72,7 @@ } protected: + std::unique_ptr<web_app::TestInstallFinalizer> install_finalizer_; std::unique_ptr<BookmarkAppInstallManager> install_manager_; };
diff --git a/chrome/browser/web_applications/components/app_registrar.h b/chrome/browser/web_applications/components/app_registrar.h index 21a3c27f..4ae6254a 100644 --- a/chrome/browser/web_applications/components/app_registrar.h +++ b/chrome/browser/web_applications/components/app_registrar.h
@@ -8,6 +8,8 @@ #include "base/callback_forward.h" #include "chrome/browser/web_applications/components/web_app_helpers.h" +class GURL; + namespace web_app { class AppRegistrar { @@ -16,6 +18,12 @@ virtual void Init(base::OnceClosure callback) = 0; + // Returns true if the app with the specified |start_url| is currently fully + // locally installed. The provided |start_url| must exactly match the launch + // URL for the app; this method does not consult the app scope or match URLs + // that fall within the scope. + virtual bool IsInstalled(const GURL& start_url) const = 0; + // Returns true if the app with |app_id| is currently installed. virtual bool IsInstalled(const AppId& app_id) const = 0;
diff --git a/chrome/browser/web_applications/components/install_finalizer.h b/chrome/browser/web_applications/components/install_finalizer.h index 56a8465..a099ebd1 100644 --- a/chrome/browser/web_applications/components/install_finalizer.h +++ b/chrome/browser/web_applications/components/install_finalizer.h
@@ -60,6 +60,10 @@ virtual bool CanRevealAppShim() const = 0; virtual void RevealAppShim(const AppId& app_id) = 0; + virtual bool CanSkipAppUpdateForSync( + const AppId& app_id, + const WebApplicationInfo& web_app_info) const = 0; + virtual ~InstallFinalizer() = default; };
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc index 123be10..f9c7c79 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc +++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
@@ -9,8 +9,10 @@ #include "base/bind.h" #include "base/optional.h" +#include "base/strings/utf_string_conversions.h" #include "chrome/browser/extensions/bookmark_app_extension_util.h" #include "chrome/browser/extensions/crx_installer.h" +#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/launch_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/web_applications/components/web_app_constants.h" @@ -19,6 +21,7 @@ #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "chrome/common/web_application_info.h" #include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_system.h" #include "extensions/browser/install/crx_install_error.h" #include "extensions/common/extension.h" #include "extensions/common/extension_id.h" @@ -165,6 +168,31 @@ BookmarkAppRevealAppShim(profile_, app); } +bool BookmarkAppInstallFinalizer::CanSkipAppUpdateForSync( + const web_app::AppId& app_id, + const WebApplicationInfo& web_app_info) const { + ExtensionService* extension_service = + ExtensionSystem::Get(profile_)->extension_service(); + DCHECK(extension_service); + + const Extension* extension = extension_service->GetInstalledExtension(app_id); + if (!extension) + return false; + + // We can skip if there are no bookmark app details that need updating. + // TODO(loyso): We need to check more data fields. crbug.com/949427. + const std::string extension_sync_data_name = + base::UTF16ToUTF8(web_app_info.title); + const std::string bookmark_app_description = + base::UTF16ToUTF8(web_app_info.description); + if (extension->non_localized_name() == extension_sync_data_name && + extension->description() == bookmark_app_description) { + return true; + } + + return false; +} + void BookmarkAppInstallFinalizer::SetCrxInstallerFactoryForTesting( CrxInstallerFactory crx_installer_factory) { crx_installer_factory_ = crx_installer_factory;
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h index 40a934ff..42685c5 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h +++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
@@ -41,6 +41,9 @@ content::WebContents* web_contents) override; bool CanRevealAppShim() const override; void RevealAppShim(const web_app::AppId& app_id) override; + bool CanSkipAppUpdateForSync( + const web_app::AppId& app_id, + const WebApplicationInfo& web_app_info) const override; using CrxInstallerFactory = base::RepeatingCallback<scoped_refptr<CrxInstaller>(Profile*)>;
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc index af3c03d..e2f64ef8 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc +++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
@@ -297,4 +297,41 @@ run_loop.Run(); } +TEST_F(BookmarkAppInstallFinalizerTest, CanSkipAppUpdateForSync) { + BookmarkAppInstallFinalizer installer(profile()); + + auto info = std::make_unique<WebApplicationInfo>(); + info->app_url = GURL(kWebAppUrl); + info->title = base::ASCIIToUTF16("Title1"); + info->description = base::ASCIIToUTF16("Description1"); + + const web_app::AppId app_id = web_app::GenerateAppIdFromURL(info->app_url); + + EXPECT_FALSE(installer.CanSkipAppUpdateForSync(app_id, *info)); + + base::RunLoop run_loop; + web_app::InstallFinalizer::FinalizeOptions options; + + installer.FinalizeInstall( + *info, options, + base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id, + web_app::InstallResultCode code) { + EXPECT_EQ(web_app::InstallResultCode::kSuccess, code); + EXPECT_EQ(app_id, installed_app_id); + run_loop.Quit(); + })); + run_loop.Run(); + + EXPECT_TRUE(installer.CanSkipAppUpdateForSync(app_id, *info)); + + WebApplicationInfo info_with_diff_title = *info; + info_with_diff_title.title = base::ASCIIToUTF16("Title2"); + EXPECT_FALSE(installer.CanSkipAppUpdateForSync(app_id, info_with_diff_title)); + + WebApplicationInfo info_with_diff_description = *info; + info_with_diff_description.title = base::ASCIIToUTF16("Description2"); + EXPECT_FALSE( + installer.CanSkipAppUpdateForSync(app_id, info_with_diff_description)); +} + } // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc index 33e73ce..90f2588 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc +++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
@@ -9,10 +9,14 @@ #include "base/one_shot_event.h" #include "chrome/browser/extensions/convert_web_app.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/web_applications/extensions/bookmark_app_util.h" #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h" +#include "chrome/common/extensions/manifest_handlers/app_launch_info.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 "url/gurl.h" namespace extensions { @@ -25,6 +29,27 @@ ExtensionSystem::Get(profile_)->ready().Post(FROM_HERE, std::move(callback)); } +bool BookmarkAppRegistrar::IsInstalled(const GURL& start_url) const { + ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); + const ExtensionSet& extensions = registry->enabled_extensions(); + + // Iterate through the extensions and extract the LaunchWebUrl (bookmark apps) + // or check the web extent (hosted apps). + for (const scoped_refptr<const Extension>& extension : extensions) { + if (!extension->is_hosted_app()) + continue; + + if (!BookmarkAppIsLocallyInstalled(profile_, extension.get())) + continue; + + if (extension->web_extent().MatchesURL(start_url) || + AppLaunchInfo::GetLaunchWebURL(extension.get()) == start_url) { + return true; + } + } + return false; +} + bool BookmarkAppRegistrar::IsInstalled(const web_app::AppId& app_id) const { return GetExtension(app_id) != nullptr; }
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h index f31fe1d..5ab43a4 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h +++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
@@ -21,6 +21,7 @@ // AppRegistrar void Init(base::OnceClosure callback) override; + bool IsInstalled(const GURL& start_url) const override; bool IsInstalled(const web_app::AppId& app_id) const override; bool WasExternalAppUninstalledByUser( const web_app::AppId& app_id) const override;
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_util.cc b/chrome/browser/web_applications/extensions/bookmark_app_util.cc index ebf56ac..c5985bd 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_util.cc +++ b/chrome/browser/web_applications/extensions/bookmark_app_util.cc
@@ -55,28 +55,6 @@ return true; } -bool BookmarkOrHostedAppInstalled(content::BrowserContext* browser_context, - const GURL& url) { - ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context); - const ExtensionSet& extensions = registry->enabled_extensions(); - - // Iterate through the extensions and extract the LaunchWebUrl (bookmark apps) - // or check the web extent (hosted apps). - for (const scoped_refptr<const Extension>& extension : extensions) { - if (!extension->is_hosted_app()) - continue; - - if (!BookmarkAppIsLocallyInstalled(browser_context, extension.get())) - continue; - - if (extension->web_extent().MatchesURL(url) || - AppLaunchInfo::GetLaunchWebURL(extension.get()) == url) { - return true; - } - } - return false; -} - bool IsInNavigationScopeForLaunchUrl(const GURL& launch_url, const GURL& url) { // Drop any "suffix" components after the path (Resolve "."): const GURL nav_scope = launch_url.GetWithoutFilename();
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_util.h b/chrome/browser/web_applications/extensions/bookmark_app_util.h index 6d539db..fb475aea 100644 --- a/chrome/browser/web_applications/extensions/bookmark_app_util.h +++ b/chrome/browser/web_applications/extensions/bookmark_app_util.h
@@ -34,11 +34,6 @@ bool BookmarkAppIsLocallyInstalled(const ExtensionPrefs* prefs, const Extension* extension); -// Returns true if a bookmark or hosted app from a given URL is already -// installed and enabled. -bool BookmarkOrHostedAppInstalled(content::BrowserContext* browser_context, - const GURL& url); - // Generates a scope based on |launch_url| and checks if the |url| falls under // it. https://www.w3.org/TR/appmanifest/#navigation-scope bool IsInNavigationScopeForLaunchUrl(const GURL& launch_url, const GURL& url);
diff --git a/chrome/browser/web_applications/test/test_app_registrar.cc b/chrome/browser/web_applications/test/test_app_registrar.cc index bae4cb2b..6e31395 100644 --- a/chrome/browser/web_applications/test/test_app_registrar.cc +++ b/chrome/browser/web_applications/test/test_app_registrar.cc
@@ -30,6 +30,11 @@ void TestAppRegistrar::Init(base::OnceClosure callback) {} +bool TestAppRegistrar::IsInstalled(const GURL& start_url) const { + NOTIMPLEMENTED(); + return false; +} + bool TestAppRegistrar::IsInstalled(const AppId& app_id) const { return base::ContainsKey(installed_apps_, app_id); }
diff --git a/chrome/browser/web_applications/test/test_app_registrar.h b/chrome/browser/web_applications/test/test_app_registrar.h index 14bb9bc..0cf1aa3 100644 --- a/chrome/browser/web_applications/test/test_app_registrar.h +++ b/chrome/browser/web_applications/test/test_app_registrar.h
@@ -28,6 +28,7 @@ // AppRegistrar void Init(base::OnceClosure callback) override; + bool IsInstalled(const GURL& start_url) const override; bool IsInstalled(const AppId& app_id) const override; bool WasExternalAppUninstalledByUser(const AppId& app_id) const override; bool HasScopeUrl(const AppId& app_id) const override;
diff --git a/chrome/browser/web_applications/test/test_install_finalizer.cc b/chrome/browser/web_applications/test/test_install_finalizer.cc index 7f5b5e72..8ac6e3e 100644 --- a/chrome/browser/web_applications/test/test_install_finalizer.cc +++ b/chrome/browser/web_applications/test/test_install_finalizer.cc
@@ -82,6 +82,12 @@ ++num_reveal_appshim_calls_; } +bool TestInstallFinalizer::CanSkipAppUpdateForSync( + const AppId& app_id, + const WebApplicationInfo& web_app_info) const { + return false; +} + void TestInstallFinalizer::SetNextFinalizeInstallResult( const AppId& app_id, InstallResultCode code) {
diff --git a/chrome/browser/web_applications/test/test_install_finalizer.h b/chrome/browser/web_applications/test/test_install_finalizer.h index f073d7e6..7aff46b 100644 --- a/chrome/browser/web_applications/test/test_install_finalizer.h +++ b/chrome/browser/web_applications/test/test_install_finalizer.h
@@ -36,6 +36,9 @@ content::WebContents* web_contents) override; bool CanRevealAppShim() const override; void RevealAppShim(const AppId& app_id) override; + bool CanSkipAppUpdateForSync( + const AppId& app_id, + const WebApplicationInfo& web_app_info) const override; void SetNextFinalizeInstallResult(const AppId& app_id, InstallResultCode code);
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc index e4c735c..91da2b0 100644 --- a/chrome/browser/web_applications/web_app_install_finalizer.cc +++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -149,4 +149,11 @@ NOTIMPLEMENTED(); } +bool WebAppInstallFinalizer::CanSkipAppUpdateForSync( + const AppId& app_id, + const WebApplicationInfo& web_app_info) const { + NOTIMPLEMENTED(); + return true; +} + } // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h index b9bd908..e1a8bb4 100644 --- a/chrome/browser/web_applications/web_app_install_finalizer.h +++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -41,6 +41,9 @@ content::WebContents* web_contents) override; bool CanRevealAppShim() const override; void RevealAppShim(const AppId& app_id) override; + bool CanSkipAppUpdateForSync( + const AppId& app_id, + const WebApplicationInfo& web_app_info) const override; private: void OnDataWritten(InstallFinalizedCallback callback,
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc index fd2e267..edec6f4 100644 --- a/chrome/browser/web_applications/web_app_provider.cc +++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -130,8 +130,8 @@ install_manager_ = std::make_unique<WebAppInstallManager>( profile, install_finalizer_.get()); } else { - install_manager_ = - std::make_unique<extensions::BookmarkAppInstallManager>(profile); + install_manager_ = std::make_unique<extensions::BookmarkAppInstallManager>( + profile, install_finalizer_.get()); } pending_app_manager_ =
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc index 649f1324..01843030 100644 --- a/chrome/browser/web_applications/web_app_registrar.cc +++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -74,6 +74,11 @@ std::move(callback).Run(); } +bool WebAppRegistrar::IsInstalled(const GURL& start_url) const { + NOTIMPLEMENTED(); + return false; +} + bool WebAppRegistrar::IsInstalled(const AppId& app_id) const { return GetAppById(app_id) != nullptr; }
diff --git a/chrome/browser/web_applications/web_app_registrar.h b/chrome/browser/web_applications/web_app_registrar.h index b14c87ac..cb7def4 100644 --- a/chrome/browser/web_applications/web_app_registrar.h +++ b/chrome/browser/web_applications/web_app_registrar.h
@@ -37,6 +37,7 @@ // AppRegistrar void Init(base::OnceClosure callback) override; + bool IsInstalled(const GURL& start_url) const override; bool IsInstalled(const AppId& app_id) const override; bool WasExternalAppUninstalledByUser(const AppId& app_id) const override; bool HasScopeUrl(const AppId& app_id) const override;
diff --git a/chrome/services/app_service/app_service_impl.h b/chrome/services/app_service/app_service_impl.h index 61688131..27287ee2 100644 --- a/chrome/services/app_service/app_service_impl.h +++ b/chrome/services/app_service/app_service_impl.h
@@ -54,12 +54,15 @@ private: void OnPublisherDisconnected(apps::mojom::AppType app_type); - mojo::BindingSet<apps::mojom::AppService> bindings_; // publishers_ is a std::map, not a mojo::InterfacePtrSet, since we want to // be able to find *the* publisher for a given apps::mojom::AppType. std::map<apps::mojom::AppType, apps::mojom::PublisherPtr> publishers_; mojo::InterfacePtrSet<apps::mojom::Subscriber> subscribers_; + // Must come after the publisher and subscriber maps to ensure it is + // destroyed first, closing the connection to avoid dangling callbacks. + mojo::BindingSet<apps::mojom::AppService> bindings_; + DISALLOW_COPY_AND_ASSIGN(AppServiceImpl); };
diff --git a/chrome/test/data/webui/app_management/app_management_browsertest.js b/chrome/test/data/webui/app_management/app_management_browsertest.js index df89e3cd..1b4db21a 100644 --- a/chrome/test/data/webui/app_management/app_management_browsertest.js +++ b/chrome/test/data/webui/app_management/app_management_browsertest.js
@@ -5,10 +5,7 @@ /** * @fileoverview Test suite for the App Management page. */ -const ROOT_PATH = '../../../../../'; - -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); GEN('#include "chrome/common/chrome_features.h"'); function AppManagementBrowserTest() {} @@ -18,11 +15,12 @@ browsePreload: 'chrome://app-management', - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, '../test_store.js', 'test_util.js', 'test_store.js', - ]), + ], featureList: ['features::kAppManagement', ''],
diff --git a/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js b/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js index acd78dd..ca000ed 100644 --- a/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js +++ b/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js
@@ -5,10 +5,7 @@ /** * @fileoverview Test suite for the bookmarks page. */ -const ROOT_PATH = '../../../../../'; - -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); GEN('#include "chrome/browser/prefs/incognito_mode_prefs.h"'); GEN('#include "chrome/browser/ui/webui/bookmarks/bookmarks_browsertest.h"'); @@ -21,13 +18,14 @@ typedefCppFixture: 'BookmarksBrowserTest', - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, '../test_store.js', 'test_command_manager.js', 'test_store.js', 'test_timer_proxy.js', 'test_util.js', - ]), + ], /** override */ runAccessibilityChecks: true, @@ -54,7 +52,7 @@ extraLibraries: BookmarksBrowserTest.prototype.extraLibraries.concat([ 'app_test.js', - ROOT_PATH + 'ui/webui/resources/js/util.js', + '//ui/webui/resources/js/util.js', ]), };
diff --git a/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js b/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js index ab723c0..c5acc58 100644 --- a/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js +++ b/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js
@@ -7,10 +7,7 @@ * Should be used for tests which care about focus. */ -const ROOT_PATH = '../../../../../'; - -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']); function BookmarksFocusTest() {} @@ -19,14 +16,15 @@ browsePreload: 'chrome://bookmarks', - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ - ROOT_PATH + 'ui/webui/resources/js/util.js', + extraLibraries: [ + ...PolymerInteractiveUITest.prototype.extraLibraries, + '//ui/webui/resources/js/util.js', '../settings/test_util.js', '../test_store.js', 'test_command_manager.js', 'test_store.js', 'test_util.js', - ]), + ], }; // Web UI interactive tests are flaky on Win10, see https://crbug.com/711256
diff --git a/chrome/test/data/webui/cr_components/cr_components_browsertest.js b/chrome/test/data/webui/cr_components/cr_components_browsertest.js index 97ac28c..5cd0bfa 100644 --- a/chrome/test/data/webui/cr_components/cr_components_browsertest.js +++ b/chrome/test/data/webui/cr_components/cr_components_browsertest.js
@@ -4,12 +4,8 @@ /** @fileoverview Tests for shared Polymer components. */ -/** @const {string} Path to source root. */ -var ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); /** * Test fixture for shared Polymer components. @@ -22,9 +18,6 @@ __proto__: PolymerTest.prototype, /** @override */ - extraLibraries: PolymerTest.getLibraries(ROOT_PATH), - - /** @override */ get browsePreload() { throw 'subclasses should override to load a WebUI page that includes it.'; }, @@ -79,8 +72,8 @@ /** @override */ extraLibraries: CrComponentsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/assert.js', - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/assert.js', + '//ui/webui/resources/js/promise_resolver.js', '../fake_chrome_event.js', '../chromeos/networking_private_constants.js', '../chromeos/fake_networking_private.js',
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js index a5b2076..0ee9fb8 100644 --- a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js +++ b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
@@ -4,12 +4,8 @@ /** @fileoverview Tests for shared Polymer elements. */ -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); /** * Test fixture for shared Polymer elements. @@ -22,9 +18,10 @@ __proto__: PolymerTest.prototype, /** @override */ - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ - ROOT_PATH + 'ui/webui/resources/js/assert.js', - ]), + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, + '//ui/webui/resources/js/assert.js', + ], /** @override */ get browsePreload() { @@ -396,7 +393,7 @@ /** @override */ extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'chrome/test/data/webui/mock_timer.js', + '//chrome/test/data/webui/mock_timer.js', 'cr_toast_test.js', ]), };
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js b/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js index 7945b083..7e87149 100644 --- a/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js +++ b/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js
@@ -4,19 +4,13 @@ /** @fileoverview Tests for shared Polymer elements which rely on focus. */ -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']); function CrElementsFocusTest() {} CrElementsFocusTest.prototype = { __proto__: PolymerInteractiveUITest.prototype, - - extraLibraries: PolymerTest.getLibraries(ROOT_PATH), }; function CrElementsActionMenuTest() {} @@ -202,7 +196,7 @@ /** @override */ extraLibraries: CrElementsFocusTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/util.js', + '//ui/webui/resources/js/util.js', '../settings/test_util.js', 'cr_expand_button_focus_tests.js', ]),
diff --git a/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js b/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js index 3e76d98e..1c2e9c3 100644 --- a/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js +++ b/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js
@@ -2,12 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']); /** * Test fixture for FocusRowBehavior. @@ -23,11 +19,12 @@ browsePreload: 'chrome://resources/html/cr/ui/focus_row_behavior.html', /** @override */ - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ - ROOT_PATH + 'ui/webui/resources/js/util.js', + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, + '//ui/webui/resources/js/util.js', 'cr_focus_row_behavior_test.js', 'settings/test_util.js', - ]), + ], /** @override */ setUp: function() {
diff --git a/chrome/test/data/webui/downloads/downloads_browsertest.js b/chrome/test/data/webui/downloads/downloads_browsertest.js index 672014b..ceb5557 100644 --- a/chrome/test/data/webui/downloads/downloads_browsertest.js +++ b/chrome/test/data/webui/downloads/downloads_browsertest.js
@@ -4,12 +4,8 @@ /** @fileoverview Tests for the Material Design downloads page. */ -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); /** * @constructor @@ -27,9 +23,6 @@ }, /** @override */ - extraLibraries: PolymerTest.getLibraries(ROOT_PATH), - - /** @override */ runAccessibilityChecks: true, }; @@ -47,7 +40,7 @@ /** @override */ extraLibraries: DownloadsTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/cr.js', + '//ui/webui/resources/js/cr.js', '../test_browser_proxy.js', 'test_support.js', 'item_tests.js', @@ -72,8 +65,8 @@ /** @override */ extraLibraries: DownloadsTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/cr.js', - ROOT_PATH + 'chrome/browser/resources/downloads/constants.js', + '//ui/webui/resources/js/cr.js', + '//chrome/browser/resources/downloads/constants.js', '../test_browser_proxy.js', 'test_support.js', 'manager_tests.js',
diff --git a/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js b/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js index a7db9e4..c8bcf0b 100644 --- a/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js +++ b/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js
@@ -2,13 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/** @const {string} Path to root from chrome/test/data/webui/extensions/a11y. */ -const ROOT_PATH = '../../../../../../'; - // Polymer BrowserTest fixture and aXe-core accessibility audit. GEN_INCLUDE([ - ROOT_PATH + 'chrome/test/data/webui/a11y/accessibility_test.js', - ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js', + '//chrome/test/data/webui/a11y/accessibility_test.js', + '//chrome/test/data/webui/polymer_browser_test_base.js', ]); GEN('#include "chrome/browser/ui/webui/extensions/' + 'extension_settings_browsertest.h"'); @@ -24,11 +21,6 @@ return 'chrome://extensions/'; } - // Include files that define the mocha tests. - get extraLibraries() { - return PolymerTest.getLibraries(ROOT_PATH); - } - // Default accessibility audit options. Specify in test definition to use. static get axeOptions() { return {
diff --git a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js index 7448723..deb23ef 100644 --- a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js +++ b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
@@ -4,12 +4,8 @@ /** @fileoverview Runs the Polymer Settings tests. */ -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); GEN('#include "chrome/browser/ui/webui/extensions/' + 'extension_settings_browsertest.h"'); @@ -25,8 +21,9 @@ /** @override */ get extraLibraries() { - return PolymerTest.getLibraries(ROOT_PATH).concat([ - ROOT_PATH + 'ui/webui/resources/js/assert.js', + return [ + ...super.extraLibraries, + '//ui/webui/resources/js/assert.js', 'test_util.js', '../mock_controller.js', '../../../../../ui/webui/resources/js/promise_resolver.js', @@ -35,7 +32,7 @@ '../settings/test_util.js', '../test_browser_proxy.js', 'test_service.js', - ]); + ]; } /** @override */
diff --git a/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js b/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js index 967b27e..b8daf7e 100644 --- a/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js +++ b/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js
@@ -4,12 +4,8 @@ /** @fileoverview Runs the Polymer Extensions interactive UI tests. */ -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']); GEN('#include "chrome/browser/ui/webui/extensions/' + 'extension_settings_browsertest.h"'); @@ -26,9 +22,10 @@ /** @override */ get extraLibraries() { - return PolymerTest.getLibraries(ROOT_PATH).concat([ + return [ + ...super.extraLibraries, '../settings/test_util.js', - ]); + ]; } };
diff --git a/chrome/test/data/webui/find_shortcut_behavior_browsertest.js b/chrome/test/data/webui/find_shortcut_behavior_browsertest.js index 0bb67f2..24575db 100644 --- a/chrome/test/data/webui/find_shortcut_behavior_browsertest.js +++ b/chrome/test/data/webui/find_shortcut_behavior_browsertest.js
@@ -2,12 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); /** * Test fixture for FindShortcutBehavior. @@ -28,8 +24,8 @@ /** @override */ extraLibraries: [ - ...PolymerTest.getLibraries(ROOT_PATH), - ROOT_PATH + 'ui/webui/resources/js/util.js', + ...PolymerTest.prototype.extraLibraries, + '//ui/webui/resources/js/util.js', 'settings/test_util.js', 'settings/test_util.js', 'find_shortcut_behavior_test.js',
diff --git a/chrome/test/data/webui/history/history_browsertest.js b/chrome/test/data/webui/history/history_browsertest.js index 50a50312b..7b378ef 100644 --- a/chrome/test/data/webui/history/history_browsertest.js +++ b/chrome/test/data/webui/history/history_browsertest.js
@@ -6,10 +6,7 @@ * @fileoverview Test suite for the Material Design history page. */ -const ROOT_PATH = '../../../../../'; - -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); GEN('#include "base/command_line.h"'); GEN('#include "chrome/test/data/webui/history_ui_browsertest.h"'); @@ -23,9 +20,10 @@ /** @override */ runAccessibilityChecks: false, - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, 'test_util.js', - ]), + ], /** @override */ setUp: function() {
diff --git a/chrome/test/data/webui/history/history_focus_test.js b/chrome/test/data/webui/history/history_focus_test.js index 0a23f83..2e7dc2d 100644 --- a/chrome/test/data/webui/history/history_focus_test.js +++ b/chrome/test/data/webui/history/history_focus_test.js
@@ -7,10 +7,7 @@ * Should be used for tests which care about focus. */ -const ROOT_PATH = '../../../../../'; - -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']); function HistoryFocusTest() {} @@ -19,12 +16,13 @@ browsePreload: 'chrome://history', - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + extraLibraries: [ + ...PolymerInteractiveUITest.prototype.extraLibraries, 'test_util.js', - ]), + ], setUp: function() { - PolymerTest.prototype.setUp.call(this); + PolymerInteractiveUITest.prototype.setUp.call(this); suiteSetup(function() { // Wait for the top-level app element to be upgraded.
diff --git a/chrome/test/data/webui/management/a11y/management_a11y_test.js b/chrome/test/data/webui/management/a11y/management_a11y_test.js index 6b8865f1..75842b1 100644 --- a/chrome/test/data/webui/management/a11y/management_a11y_test.js +++ b/chrome/test/data/webui/management/a11y/management_a11y_test.js
@@ -2,13 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/** @const {string} Path to root from chrome/test/data/webui/management/a11y. */ -const ROOT_PATH = '../../../../../../'; - // Polymer BrowserTest fixture and aXe-core accessibility audit. GEN_INCLUDE([ - ROOT_PATH + 'chrome/test/data/webui/a11y/accessibility_test.js', - ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js', + '//chrome/test/data/webui/a11y/accessibility_test.js', + '//chrome/test/data/webui/polymer_browser_test_base.js', ]); GEN('#include "chrome/browser/ui/webui/management_a11y_browsertest.h"'); @@ -24,11 +21,6 @@ return 'chrome://management/'; } - // Include files that define the mocha tests. - get extraLibraries() { - return PolymerTest.getLibraries(ROOT_PATH); - } - // Default accessibility audit options. Specify in test definition to use. static get axeOptions() { return {
diff --git a/chrome/test/data/webui/media_router/media_router_elements_browsertest.js b/chrome/test/data/webui/media_router/media_router_elements_browsertest.js index 9cfb6a5..d8b9ed2 100644 --- a/chrome/test/data/webui/media_router/media_router_elements_browsertest.js +++ b/chrome/test/data/webui/media_router/media_router_elements_browsertest.js
@@ -4,12 +4,8 @@ /** @fileoverview Runs the Media Router Polymer elements tests. */ -/** @const {string} Path to source root. */ -var ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); /** * Test fixture for Media Router Polymer elements. @@ -32,7 +28,8 @@ // List tests for individual elements. The media-router-container tests are // split between several files and use common functionality from // media_router_container_test_base.js. - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, 'issue_banner_tests.js', 'media_router_container_cast_mode_list_tests.js', 'media_router_container_filter_tests.js', @@ -45,7 +42,7 @@ 'media_router_search_highlighter_tests.js', 'route_controls_tests.js', 'route_details_tests.js', - ]), + ], /** * Mocks the browser API methods to make them fire events instead.
diff --git a/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js b/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js index 87e14d0..3eb4217 100644 --- a/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js +++ b/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js
@@ -4,12 +4,8 @@ /** @fileoverview Tests for MultiDevice unified setup WebUI. Chrome OS only. */ -/** @const {string} Path to source root. */ -var ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); /** * Test fixture for MultiDeviceSetup elements. @@ -23,7 +19,8 @@ browsePreload: 'chrome://multidevice-setup/', - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, '../test_browser_proxy.js', '../fake_chrome_event.js', // Necessary for fake_quick_unlock_private.js '../settings/fake_quick_unlock_private.js', @@ -31,7 +28,7 @@ 'integration_test.js', 'setup_succeeded_page_test.js', 'start_setup_page_test.js', - ]), + ], }; TEST_F('MultiDeviceSetupBrowserTest', 'Integration', function() {
diff --git a/chrome/test/data/webui/polymer_browser_test_base.js b/chrome/test/data/webui/polymer_browser_test_base.js index 7a00117..60b03af5 100644 --- a/chrome/test/data/webui/polymer_browser_test_base.js +++ b/chrome/test/data/webui/polymer_browser_test_base.js
@@ -175,15 +175,6 @@ } }; -/** - * Just an alias for PolymerTest.prototype.extraLibraries. - * TODO(olsen): Remove this function since it is no longer needed - since paths - * no longer need to be re-written in terms of the ROOT_PATH. - */ -PolymerTest.getLibraries = function() { - return PolymerTest.prototype.extraLibraries; -}; - /* * Waits for queued up tasks to finish before proceeding. Inspired by: * https://github.com/Polymer/web-component-tester/blob/master/browser/environment/helpers.js#L97
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js index aca6d41..3ab107c6 100644 --- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js +++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -4,10 +4,7 @@ /** @fileoverview Runs the Print Preview tests for the new UI. */ -const ROOT_PATH = '../../../../../'; - -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); const NewPrintPreviewTest = class extends PolymerTest { /** @override */ @@ -17,9 +14,10 @@ /** @override */ get extraLibraries() { - return PolymerTest.getLibraries(ROOT_PATH).concat([ - ROOT_PATH + 'ui/webui/resources/js/assert.js', - ]); + return [ + ...super.extraLibraries, + '//ui/webui/resources/js/assert.js', + ]; } // The name of the mocha suite. Should be overridden by subclasses. @@ -538,7 +536,7 @@ /** @override */ get extraLibraries() { return super.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/cr/event_target.js', + '//ui/webui/resources/js/cr/event_target.js', '../settings/test_util.js', '../test_browser_proxy.js', 'cloud_print_interface_stub.js', @@ -682,8 +680,8 @@ /** @override */ get extraLibraries() { return super.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js', - ROOT_PATH + 'ui/webui/resources/js/cr/event_target.js', + '//ui/webui/resources/js/web_ui_listener_behavior.js', + '//ui/webui/resources/js/cr/event_target.js', '../settings/test_util.js', '../test_browser_proxy.js', 'cloud_print_interface_stub.js', @@ -849,7 +847,7 @@ /** @override */ get extraLibraries() { return super.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js', + '//ui/webui/resources/js/web_ui_listener_behavior.js', '../settings/test_util.js', '../test_browser_proxy.js', 'native_layer_stub.js', @@ -1230,7 +1228,7 @@ /** @override */ get extraLibraries() { return super.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js', + '//ui/webui/resources/js/web_ui_listener_behavior.js', '../test_browser_proxy.js', '../settings/test_util.js', 'cloud_print_interface_stub.js',
diff --git a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js index f5d18636..73df732c 100644 --- a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js +++ b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
@@ -4,12 +4,8 @@ /** @fileoverview Runs the Polymer Print Preview interactive UI tests. */ -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']); const PrintPreviewInteractiveUITest = class extends PolymerInteractiveUITest { /** @override */ @@ -19,9 +15,10 @@ /** @override */ get extraLibraries() { - return PolymerTest.getLibraries(ROOT_PATH).concat([ - ROOT_PATH + 'ui/webui/resources/js/assert.js', - ]); + return [ + ...super.extraLibraries, + '//ui/webui/resources/js/assert.js', + ]; } // The name of the mocha suite. Should be overridden by subclasses. @@ -45,7 +42,7 @@ /** @override */ get extraLibraries() { return super.extraLibraries.concat([ - ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js', + '//chrome/test/data/webui/settings/test_util.js', 'print_header_interactive_test.js', ]); } @@ -79,7 +76,7 @@ /** @override */ get extraLibraries() { return super.extraLibraries.concat([ - ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js', + '//chrome/test/data/webui/settings/test_util.js', 'button_strip_interactive_test.js', ]); } @@ -113,8 +110,8 @@ /** @override */ get extraLibraries() { return super.extraLibraries.concat([ - ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js', - ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js', + '//chrome/test/data/webui/settings/test_util.js', + '//ui/webui/resources/js/web_ui_listener_behavior.js', '../test_browser_proxy.js', 'native_layer_stub.js', 'print_preview_test_utils.js', @@ -256,7 +253,7 @@ /** @override */ get extraLibraries() { return super.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/util.js', + '//ui/webui/resources/js/util.js', '../settings/test_util.js', 'print_preview_test_utils.js', 'scaling_settings_interactive_test.js',
diff --git a/chrome/test/data/webui/resources/webui_resources_browsertest.js b/chrome/test/data/webui/resources/webui_resources_browsertest.js index 63516986..a45306f8 100644 --- a/chrome/test/data/webui/resources/webui_resources_browsertest.js +++ b/chrome/test/data/webui/resources/webui_resources_browsertest.js
@@ -4,12 +4,8 @@ /** @fileoverview Runs the WebUI resources tests. */ -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); /** * Test fixture for Polymer Settings elements. @@ -27,9 +23,6 @@ }, /** @override */ - extraLibraries: PolymerTest.getLibraries(ROOT_PATH), - - /** @override */ setUp: function() { PolymerTest.prototype.setUp.call(this); },
diff --git a/chrome/test/data/webui/set_time_dialog_browsertest.js b/chrome/test/data/webui/set_time_dialog_browsertest.js index 6b57724b..a18e570a 100644 --- a/chrome/test/data/webui/set_time_dialog_browsertest.js +++ b/chrome/test/data/webui/set_time_dialog_browsertest.js
@@ -2,11 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../'; - -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); /** * SetTimeDialogBrowserTest tests the "Set Time" web UI dialog. @@ -20,9 +16,10 @@ browsePreload: 'chrome://set-time/', - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ - ROOT_PATH + 'chrome/test/data/webui/test_browser_proxy.js', - ]), + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, + '//chrome/test/data/webui/test_browser_proxy.js', + ], }; TEST_F('SetTimeDialogBrowserTest', 'All', function() {
diff --git a/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js b/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js index da11a7ab..7d52614 100644 --- a/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js +++ b/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js
@@ -4,13 +4,10 @@ /** @fileoverview Runs the Polymer Accessibility Settings tests. */ -/** @const {string} Path to root from chrome/test/data/webui/settings/a11y. */ -const ROOT_PATH = '../../../../../../'; - // Polymer BrowserTest fixture and aXe-core accessibility audit. GEN_INCLUDE([ - ROOT_PATH + 'chrome/test/data/webui/a11y/accessibility_test.js', - ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js', + '//chrome/test/data/webui/a11y/accessibility_test.js', + '//chrome/test/data/webui/polymer_browser_test_base.js', ]); /** @@ -58,9 +55,10 @@ browsePreload: 'chrome://settings/', // Include files that define the mocha tests. - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, '../ensure_lazy_loaded.js', - ]), + ], // TODO(hcarmona): Remove once ADT is not longer in the testing infrastructure runAccessibilityChecks: false,
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js index 086ed3c..8e7bd966 100644 --- a/chrome/test/data/webui/settings/cr_settings_browsertest.js +++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -4,12 +4,8 @@ /** @fileoverview Runs the Polymer Settings tests. */ -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); GEN('#if defined(OS_CHROMEOS)'); GEN('#include "ash/public/cpp/ash_features.h"'); @@ -35,9 +31,10 @@ }, /** @override */ - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, 'ensure_lazy_loaded.js', - ]), + ], /** @override */ setUp: function() { @@ -767,7 +764,7 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/promise_resolver.js', '../test_browser_proxy.js', 'appearance_page_test.js', ]), @@ -791,7 +788,7 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/promise_resolver.js', '../test_browser_proxy.js', 'appearance_fonts_page_test.js', ]), @@ -867,7 +864,7 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/promise_resolver.js', '../test_browser_proxy.js', 'downloads_page_test.js', ]), @@ -1033,7 +1030,7 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/promise_resolver.js', 'test_util.js', '../test_browser_proxy.js', 'test_privacy_page_browser_proxy.js', @@ -1067,7 +1064,7 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/promise_resolver.js', 'test_util.js', '../test_browser_proxy.js', 'test_privacy_page_browser_proxy.js', @@ -1505,7 +1502,7 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/assert.js', + '//ui/webui/resources/js/assert.js', '../fake_chrome_event.js', 'fake_settings_private.js', 'fake_system_display.js', @@ -1552,7 +1549,7 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/assert.js', + '//ui/webui/resources/js/assert.js', '../fake_chrome_event.js', 'fake_bluetooth.js', 'fake_bluetooth_private.js', @@ -1579,8 +1576,8 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', - ROOT_PATH + 'ui/webui/resources/js/assert.js', + '//ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/assert.js', '../fake_chrome_event.js', '../chromeos/fake_networking_private.js', '../chromeos/cr_onc_strings.js', @@ -1607,9 +1604,9 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', - ROOT_PATH + 'ui/webui/resources/js/assert.js', - ROOT_PATH + 'ui/webui/resources/js/util.js', + '//ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/assert.js', + '//ui/webui/resources/js/util.js', '../fake_chrome_event.js', '../chromeos/fake_networking_private.js', '../chromeos/cr_onc_strings.js', @@ -1761,7 +1758,7 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/promise_resolver.js', '../fake_chrome_event.js', 'test_util.js', '../test_browser_proxy.js', @@ -2055,7 +2052,7 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/assert.js', + '//ui/webui/resources/js/assert.js', 'test_util.js', '../test_browser_proxy.js', 'cups_printer_page_tests.js', @@ -2266,7 +2263,7 @@ featureList: ['features::kCrostini', ''], extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/promise_resolver.js', '../test_browser_proxy.js', 'test_crostini_browser_proxy.js', 'crostini_page_test.js', @@ -2291,7 +2288,7 @@ browsePreload: 'chrome://settings/plugin_vm_page/plugin_vm_page.html', extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/promise_resolver.js', '../test_browser_proxy.js', 'plugin_vm_page_test.js', ]), @@ -2315,7 +2312,7 @@ browsePreload: 'chrome://settings/android_apps_page/android_apps_page.html', extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/promise_resolver.js', '../test_browser_proxy.js', 'test_android_apps_browser_proxy.js', 'android_apps_page_test.js', @@ -2369,7 +2366,7 @@ /** @override */ extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ - ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + '//ui/webui/resources/js/promise_resolver.js', '../test_browser_proxy.js', 'google_assistant_page_test.js', ]),
diff --git a/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js b/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js index a297ff7b..36a5fde7 100644 --- a/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js +++ b/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js
@@ -4,12 +4,8 @@ /** @fileoverview Runs the Polymer Settings interactive UI tests. */ -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']); /** * Test fixture for interactive Polymer Settings elements. @@ -27,9 +23,6 @@ }, /** @override */ - extraLibraries: PolymerTest.getLibraries(ROOT_PATH), - - /** @override */ setUp: function() { PolymerTest.prototype.setUp.call(this); // We aren't loading the main document.
diff --git a/chrome/test/data/webui/settings/help_page_browsertest.js b/chrome/test/data/webui/settings/help_page_browsertest.js index 7e09fd55..09ff3cb 100644 --- a/chrome/test/data/webui/settings/help_page_browsertest.js +++ b/chrome/test/data/webui/settings/help_page_browsertest.js
@@ -19,9 +19,6 @@ browsePreload: 'chrome://help/', /** @override */ - extraLibraries: PolymerTest.getLibraries(ROOT_PATH), - - /** @override */ setUp: function() { // Intentionally bypassing SettingsPageBrowserTest#setUp. PolymerTest.prototype.setUp.call(this);
diff --git a/chrome/test/data/webui/settings/settings_page_browsertest.js b/chrome/test/data/webui/settings/settings_page_browsertest.js index ccc8616..f7c1323b 100644 --- a/chrome/test/data/webui/settings/settings_page_browsertest.js +++ b/chrome/test/data/webui/settings/settings_page_browsertest.js
@@ -4,13 +4,8 @@ /** @fileoverview Prototype for Settings page tests. */ -/** @const {string} Path to root from chrome/test/data/webui/settings/. */ -// eslint-disable-next-line no-var -var ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); /** * @constructor @@ -25,10 +20,11 @@ browsePreload: 'chrome://settings/', /** @override */ - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, '../fake_chrome_event.js', 'fake_settings_private.js', - ]), + ], /** @type {?SettingsBasicPageElement} */ basicPage: null,
diff --git a/chrome/test/data/webui/signin/signin_browsertest.js b/chrome/test/data/webui/signin/signin_browsertest.js index b5e367a..ec4c5e9 100644 --- a/chrome/test/data/webui/signin/signin_browsertest.js +++ b/chrome/test/data/webui/signin/signin_browsertest.js
@@ -4,12 +4,8 @@ /** @fileoverview Runs the Sign-in web UI tests. */ -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); GEN('#include "base/command_line.h"'); GEN('#include "chrome/test/data/webui/signin_browsertest.h"'); @@ -36,13 +32,14 @@ /** @override */ get extraLibraries() { - return PolymerTest.getLibraries(ROOT_PATH).concat([ - ROOT_PATH + 'chrome/test/data/webui/test_browser_proxy.js', - ROOT_PATH + 'chrome/browser/resources/signin/dice_sync_confirmation/' + + return [ + ...super.extraLibraries, + '//chrome/test/data/webui/test_browser_proxy.js', + '//chrome/browser/resources/signin/dice_sync_confirmation/' + 'sync_confirmation_browser_proxy.js', 'test_sync_confirmation_browser_proxy.js', 'sync_confirmation_test.js', - ]); + ]; } };
diff --git a/chrome/test/data/webui/user_manager/user_manager_browsertest.js b/chrome/test/data/webui/user_manager/user_manager_browsertest.js index 4e92b851..f8f22c4b 100644 --- a/chrome/test/data/webui/user_manager/user_manager_browsertest.js +++ b/chrome/test/data/webui/user_manager/user_manager_browsertest.js
@@ -4,12 +4,8 @@ /** @fileoverview Tests for the Material Design user manager page. */ -/** @const {string} Path to root from chrome/test/data/webui/user_manager/ */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); GEN('#include "chrome/common/chrome_features.h"'); /** @@ -25,13 +21,14 @@ browsePreload: 'chrome://md-user-manager/', /** @override */ - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + extraLibraries: [ + ...PolymerTest.prototype.extraLibraries, '../test_browser_proxy.js', 'control_bar_tests.js', 'create_profile_tests.js', 'test_profile_browser_proxy.js', 'user_manager_pages_tests.js', - ]), + ], }; GEN('#if defined(OS_WIN)');
diff --git a/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js b/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js index 48095d3..3ae01aba 100644 --- a/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js +++ b/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
@@ -4,12 +4,8 @@ /** @fileoverview Runs the Polymer welcome tests on onboarding-welcome UI. */ -/** @const {string} Path to source root. */ -const ROOT_PATH = '../../../../../'; - // Polymer BrowserTest fixture. -GEN_INCLUDE( - [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']); +GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']); GEN('#include "chrome/browser/ui/webui/welcome/nux_helper.h"'); /** @@ -22,9 +18,10 @@ } get extraLibraries() { - return PolymerTest.getLibraries(ROOT_PATH).concat([ + return [ + ...super.extraLibraries, '../test_browser_proxy.js', - ]); + ]; } /** @override */
diff --git a/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc b/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc index 74e2c10..e1b8a9e 100644 --- a/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc +++ b/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc
@@ -81,7 +81,8 @@ public: AudioDeviceOwnerTest() : task_env_(base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT, - base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED) {} + base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode:: + QUEUED) {} ~AudioDeviceOwnerTest() override { task_env_.RunUntilIdle(); }
diff --git a/components/arc/common/BUILD.gn b/components/arc/common/BUILD.gn index 79ed570..f722f4c 100644 --- a/components/arc/common/BUILD.gn +++ b/components/arc/common/BUILD.gn
@@ -45,6 +45,7 @@ "policy.mojom", "power.mojom", "print.mojom", + "print_spooler.mojom", "process.mojom", "property.mojom", "rotation_lock.mojom",
diff --git a/components/arc/common/arc_bridge.mojom b/components/arc/common/arc_bridge.mojom index 64b73c9d..7a2121f 100644 --- a/components/arc/common/arc_bridge.mojom +++ b/components/arc/common/arc_bridge.mojom
@@ -36,6 +36,7 @@ import "components/arc/common/policy.mojom"; import "components/arc/common/power.mojom"; import "components/arc/common/print.mojom"; +import "components/arc/common/print_spooler.mojom"; import "components/arc/common/process.mojom"; import "components/arc/common/property.mojom"; import "components/arc/common/rotation_lock.mojom"; @@ -52,9 +53,9 @@ import "components/arc/common/wake_lock.mojom"; import "components/arc/common/wallpaper.mojom"; -// Next MinVersion: 45 +// Next MinVersion: 46 // Deprecated method IDs: 101, 105 -// Next method ID: 150 +// Next method ID: 151 interface ArcBridgeHost { // Keep the entries alphabetical. In order to do so without breaking // compatibility with the ARC instance, explicitly assign each interface a @@ -165,6 +166,10 @@ // Notifies Chrome that the PrintInstance interface is ready. [MinVersion=16] OnPrintInstanceReady@121(PrintInstance instance_ptr); + // Notifies Chrome that the PrintSpoolerInstance interface is ready. + [MinVersion=45] OnPrintSpoolerInstanceReady@150( + PrintSpoolerInstance instance_ptr); + // Notifies Chrome that the ProcessInstance interface is ready. OnProcessInstanceReady@104(ProcessInstance instance_ptr);
diff --git a/components/arc/common/print_spooler.mojom b/components/arc/common/print_spooler.mojom new file mode 100644 index 0000000..81c415b --- /dev/null +++ b/components/arc/common/print_spooler.mojom
@@ -0,0 +1,18 @@ +// 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. +// +// Next MinVersion: 1 + +module arc.mojom; + +// Next method ID: 0 +interface PrintSpoolerHost { + // TODO(jschettler): Add methods to open and close print preview +}; + +// Next method ID: 1 +interface PrintSpoolerInstance { + // Establishes full-duplex communication with the host. + [MinVersion=0] Init@0(PrintSpoolerHost host_ptr) => (); +}; \ No newline at end of file
diff --git a/components/arc/session/arc_bridge_host_impl.cc b/components/arc/session/arc_bridge_host_impl.cc index f90cacd..16bdd5f 100644 --- a/components/arc/session/arc_bridge_host_impl.cc +++ b/components/arc/session/arc_bridge_host_impl.cc
@@ -42,6 +42,7 @@ #include "components/arc/common/policy.mojom.h" #include "components/arc/common/power.mojom.h" #include "components/arc/common/print.mojom.h" +#include "components/arc/common/print_spooler.mojom.h" #include "components/arc/common/process.mojom.h" #include "components/arc/common/property.mojom.h" #include "components/arc/common/rotation_lock.mojom.h" @@ -256,6 +257,12 @@ OnInstanceReady(arc_bridge_service_->print(), std::move(print_ptr)); } +void ArcBridgeHostImpl::OnPrintSpoolerInstanceReady( + mojom::PrintSpoolerInstancePtr print_spooler_ptr) { + OnInstanceReady(arc_bridge_service_->print_spooler(), + std::move(print_spooler_ptr)); +} + void ArcBridgeHostImpl::OnProcessInstanceReady( mojom::ProcessInstancePtr process_ptr) { OnInstanceReady(arc_bridge_service_->process(), std::move(process_ptr));
diff --git a/components/arc/session/arc_bridge_host_impl.h b/components/arc/session/arc_bridge_host_impl.h index 317afea..7f1d3fa 100644 --- a/components/arc/session/arc_bridge_host_impl.h +++ b/components/arc/session/arc_bridge_host_impl.h
@@ -87,6 +87,8 @@ void OnPolicyInstanceReady(mojom::PolicyInstancePtr policy_ptr) override; void OnPowerInstanceReady(mojom::PowerInstancePtr power_ptr) override; void OnPrintInstanceReady(mojom::PrintInstancePtr print_ptr) override; + void OnPrintSpoolerInstanceReady( + mojom::PrintSpoolerInstancePtr print_spooler_ptr) override; void OnProcessInstanceReady(mojom::ProcessInstancePtr process_ptr) override; void OnPropertyInstanceReady( mojom::PropertyInstancePtr property_ptr) override;
diff --git a/components/arc/session/arc_bridge_service.cc b/components/arc/session/arc_bridge_service.cc index 1a4ecc7f..cc3791bb 100644 --- a/components/arc/session/arc_bridge_service.cc +++ b/components/arc/session/arc_bridge_service.cc
@@ -37,6 +37,7 @@ #include "components/arc/common/policy.mojom.h" #include "components/arc/common/power.mojom.h" #include "components/arc/common/print.mojom.h" +#include "components/arc/common/print_spooler.mojom.h" #include "components/arc/common/process.mojom.h" #include "components/arc/common/property.mojom.h" #include "components/arc/common/rotation_lock.mojom.h"
diff --git a/components/arc/session/arc_bridge_service.h b/components/arc/session/arc_bridge_service.h index 5f590ab3..31f2e311 100644 --- a/components/arc/session/arc_bridge_service.h +++ b/components/arc/session/arc_bridge_service.h
@@ -71,6 +71,8 @@ class PowerInstance; class PrintHost; class PrintInstance; +class PrintSpoolerHost; +class PrintSpoolerInstance; class ProcessInstance; class PropertyInstance; class RotationLockInstance; @@ -204,6 +206,10 @@ ConnectionHolder<mojom::PrintInstance, mojom::PrintHost>* print() { return &print_; } + ConnectionHolder<mojom::PrintSpoolerInstance, mojom::PrintSpoolerHost>* + print_spooler() { + return &print_spooler_; + } ConnectionHolder<mojom::ProcessInstance>* process() { return &process_; } ConnectionHolder<mojom::PropertyInstance>* property() { return &property_; } ConnectionHolder<mojom::RotationLockInstance>* rotation_lock() { @@ -282,6 +288,8 @@ ConnectionHolder<mojom::PolicyInstance, mojom::PolicyHost> policy_; ConnectionHolder<mojom::PowerInstance, mojom::PowerHost> power_; ConnectionHolder<mojom::PrintInstance, mojom::PrintHost> print_; + ConnectionHolder<mojom::PrintSpoolerInstance, mojom::PrintSpoolerHost> + print_spooler_; ConnectionHolder<mojom::ProcessInstance> process_; ConnectionHolder<mojom::PropertyInstance> property_; ConnectionHolder<mojom::RotationLockInstance> rotation_lock_;
diff --git a/components/arc/test/fake_arc_bridge_host.cc b/components/arc/test/fake_arc_bridge_host.cc index 5b52f52..4f4247b5 100644 --- a/components/arc/test/fake_arc_bridge_host.cc +++ b/components/arc/test/fake_arc_bridge_host.cc
@@ -37,6 +37,7 @@ #include "components/arc/common/policy.mojom.h" #include "components/arc/common/power.mojom.h" #include "components/arc/common/print.mojom.h" +#include "components/arc/common/print_spooler.mojom.h" #include "components/arc/common/process.mojom.h" #include "components/arc/common/property.mojom.h" #include "components/arc/common/rotation_lock.mojom.h" @@ -150,6 +151,9 @@ void FakeArcBridgeHost::OnPrintInstanceReady( mojom::PrintInstancePtr print_ptr) {} +void FakeArcBridgeHost::OnPrintSpoolerInstanceReady( + mojom::PrintSpoolerInstancePtr print_spooler_ptr) {} + void FakeArcBridgeHost::OnProcessInstanceReady( mojom::ProcessInstancePtr process_ptr) {}
diff --git a/components/arc/test/fake_arc_bridge_host.h b/components/arc/test/fake_arc_bridge_host.h index b704a5b..114e8a2 100644 --- a/components/arc/test/fake_arc_bridge_host.h +++ b/components/arc/test/fake_arc_bridge_host.h
@@ -67,6 +67,8 @@ void OnPolicyInstanceReady(mojom::PolicyInstancePtr policy_ptr) override; void OnPowerInstanceReady(mojom::PowerInstancePtr power_ptr) override; void OnPrintInstanceReady(mojom::PrintInstancePtr print_ptr) override; + void OnPrintSpoolerInstanceReady( + mojom::PrintSpoolerInstancePtr print_spooler_ptr) override; void OnProcessInstanceReady(mojom::ProcessInstancePtr process_ptr) override; void OnPropertyInstanceReady( mojom::PropertyInstancePtr property_ptr) override;
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc index 4439416..3dc8cf20 100644 --- a/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -2249,6 +2249,57 @@ "text", response_data.fields[3]); } +// Test that if a company is of a format of a birthyear and the relevant feature +// is enabled, we would not fill it. +TEST_F(AutofillManagerTest, FillAddressForm_CompanyBirthyear) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillRejectCompanyBirthyear); + + // Set up our form data. + FormData address_form; + address_form.name = ASCIIToUTF16("MyForm"); + address_form.url = GURL("https://myform.com/form.html"); + address_form.action = GURL("https://myform.com/submit.html"); + + FormFieldData field; + test::CreateTestFormField("First name", "firstname", "", "text", &field); + address_form.fields.push_back(field); + test::CreateTestFormField("Middle name", "middle", "", "text", &field); + address_form.fields.push_back(field); + test::CreateTestFormField("Last name", "lastname", "", "text", &field); + address_form.fields.push_back(field); + test::CreateTestFormField("Company", "company", "", "text", &field); + address_form.fields.push_back(field); + + std::vector<FormData> address_forms(1, address_form); + FormsSeen(address_forms); + + AutofillProfile profile; + const char guid[] = "00000000-0000-0000-0000-000000000123"; + test::SetProfileInfo(&profile, "Elvis", "Aaron", "Presley", + "theking@gmail.com", "1987", "3734 Elvis Presley Blvd.", + "Apt. 10", "Memphis", "Tennessee", "38116", "US", + "12345678901"); + profile.set_guid(guid); + personal_data_.AddProfile(profile); + + int response_page_id = 0; + FormData response_data; + FillAutofillFormDataAndSaveResults( + kDefaultPageID, address_form, *address_form.fields.begin(), + MakeFrontendID(std::string(), guid), &response_page_id, &response_data); + + // All the fields should be filled except the company. + ExpectFilledField("First name", "firstname", "Elvis", "text", + response_data.fields[0]); + ExpectFilledField("Middle name", "middle", "Aaron", "text", + response_data.fields[1]); + ExpectFilledField("Last name", "lastname", "Presley", "text", + response_data.fields[2]); + ExpectFilledField("Company", "company", "", "text", response_data.fields[3]); +} + // Test that a field with a value equal to it's placeholder attribute is filled. TEST_F(AutofillManagerTest, FillAddressForm_PlaceholderEqualsValue) { FormData address_form;
diff --git a/components/autofill/core/browser/autofill_profile.cc b/components/autofill/core/browser/autofill_profile.cc index 97c62222..504acbfa 100644 --- a/components/autofill/core/browser/autofill_profile.cc +++ b/components/autofill/core/browser/autofill_profile.cc
@@ -228,6 +228,7 @@ AutofillProfile::AutofillProfile(const std::string& guid, const std::string& origin) : AutofillDataModel(guid, origin), + company_(this), phone_number_(this), record_type_(LOCAL_PROFILE), has_converted_(false), @@ -235,6 +236,7 @@ AutofillProfile::AutofillProfile(RecordType type, const std::string& server_id) : AutofillDataModel(base::GenerateGUID(), std::string()), + company_(this), phone_number_(this), server_id_(server_id), record_type_(type), @@ -245,6 +247,7 @@ AutofillProfile::AutofillProfile() : AutofillDataModel(base::GenerateGUID(), std::string()), + company_(this), phone_number_(this), record_type_(LOCAL_PROFILE), has_converted_(false), @@ -252,6 +255,7 @@ AutofillProfile::AutofillProfile(const AutofillProfile& profile) : AutofillDataModel(std::string(), std::string()), + company_(this), phone_number_(this), weak_ptr_factory_(this) { operator=(profile); @@ -277,6 +281,7 @@ name_ = profile.name_; email_ = profile.email_; company_ = profile.company_; + company_.set_profile(this); phone_number_ = profile.phone_number_; phone_number_.set_profile(this); @@ -451,10 +456,12 @@ } bool AutofillProfile::EqualsForUpdatePurposes( - const AutofillProfile& profile) const { - return use_count() == profile.use_count() && - UseDateEqualsInSeconds(&profile) && - language_code() == profile.language_code() && Compare(profile) == 0; + const AutofillProfile& new_profile) const { + return use_count() == new_profile.use_count() && + (origin() == new_profile.origin() || !new_profile.IsVerified()) && + UseDateEqualsInSeconds(&new_profile) && + language_code() == new_profile.language_code() && + Compare(new_profile) == 0; } bool AutofillProfile::EqualsForClientValidationPurpose( @@ -559,7 +566,7 @@ NameInfo name; EmailInfo email; - CompanyInfo company; + CompanyInfo company(this); PhoneNumber phone_number(this); Address address;
diff --git a/components/autofill/core/browser/autofill_profile.h b/components/autofill/core/browser/autofill_profile.h index 3f45fbb..4fec90b 100644 --- a/components/autofill/core/browser/autofill_profile.h +++ b/components/autofill/core/browser/autofill_profile.h
@@ -104,7 +104,10 @@ // differences in usage stats. bool EqualsForSyncPurposes(const AutofillProfile& profile) const; - bool EqualsForUpdatePurposes(const AutofillProfile& profile) const; + // Returns true if |new_profile| and this are considered equal for updating + // purposes, meaning that if equal we do not need to update this profile to + // the |new_profile|. + bool EqualsForUpdatePurposes(const AutofillProfile& new_profile) const; // Compares the values of kSupportedTypesByClientForValidation fields. bool EqualsForClientValidationPurpose(const AutofillProfile& profile) const;
diff --git a/components/autofill/core/browser/autofill_profile_comparator_unittest.cc b/components/autofill/core/browser/autofill_profile_comparator_unittest.cc index 36da4fdf..0fac040 100644 --- a/components/autofill/core/browser/autofill_profile_comparator_unittest.cc +++ b/components/autofill/core/browser/autofill_profile_comparator_unittest.cc
@@ -6,11 +6,13 @@ #include "base/guid.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/contact_info.h" #include "components/autofill/core/browser/country_names.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/common/autofill_features.h" #include "testing/gtest/include/gtest/gtest.h" // Field Type Constants @@ -758,9 +760,16 @@ } TEST_F(AutofillProfileComparatorTest, MergeCompanyNames) { + base::test::ScopedFeatureList scoped_features; + scoped_features.InitWithFeatures( + /*enabled_features=*/{autofill::features:: + kAutofillRejectCompanyBirthyear}, + /*disabled_features=*/{}); + static const char kCompanyA[] = "Some Company"; static const char kCompanyB[] = "SÔMÈ ÇÖMPÁÑÝ"; static const char kCompanyC[] = "SÔMÈ ÇÖMPÁÑÝ A.G."; + static const char kCompanyD[] = "1987"; CompanyInfo company_a; company_a.SetRawInfo(COMPANY_NAME, UTF8ToUTF16(kCompanyA)); @@ -781,15 +790,31 @@ AutofillProfile profile_c = CreateProfileWithCompanyName(kCompanyC); profile_c.set_use_date(profile_a.use_date() - base::TimeDelta::FromDays(1)); + // Company Name D is in the format of a birthyear, invalid and non-verified. + CompanyInfo company_d; + company_d.SetRawInfo(COMPANY_NAME, UTF8ToUTF16(kCompanyD)); + AutofillProfile profile_d = CreateProfileWithCompanyName(kCompanyD); + profile_a.set_use_date(base::Time::Now()); + MergeCompanyNamesAndExpect(profile_a, profile_a, company_a); MergeCompanyNamesAndExpect(profile_a, profile_b, company_b); MergeCompanyNamesAndExpect(profile_a, profile_c, company_c); + MergeCompanyNamesAndExpect(profile_a, profile_d, company_a); + MergeCompanyNamesAndExpect(profile_b, profile_a, company_b); MergeCompanyNamesAndExpect(profile_b, profile_b, company_b); MergeCompanyNamesAndExpect(profile_b, profile_c, company_c); + MergeCompanyNamesAndExpect(profile_b, profile_d, company_b); + MergeCompanyNamesAndExpect(profile_c, profile_a, company_c); MergeCompanyNamesAndExpect(profile_c, profile_b, company_c); MergeCompanyNamesAndExpect(profile_c, profile_c, company_c); + MergeCompanyNamesAndExpect(profile_c, profile_d, company_c); + + MergeCompanyNamesAndExpect(profile_d, profile_a, company_a); + MergeCompanyNamesAndExpect(profile_d, profile_b, company_b); + MergeCompanyNamesAndExpect(profile_d, profile_c, company_c); + MergeCompanyNamesAndExpect(profile_d, profile_d, company_d); } TEST_F(AutofillProfileComparatorTest, MergePhoneNumbers_NA) {
diff --git a/components/autofill/core/browser/contact_info.cc b/components/autofill/core/browser/contact_info.cc index c2d5104..3f8dc70 100644 --- a/components/autofill/core/browser/contact_info.cc +++ b/components/autofill/core/browser/contact_info.cc
@@ -13,9 +13,11 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_data_util.h" +#include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_l10n_util.h" +#include "components/autofill/core/common/autofill_regexes.h" namespace autofill { @@ -218,6 +220,8 @@ CompanyInfo::CompanyInfo() {} +CompanyInfo::CompanyInfo(const AutofillProfile* profile) : profile_(profile) {} + CompanyInfo::CompanyInfo(const CompanyInfo& info) { *this = info; } @@ -228,12 +232,13 @@ if (this == &info) return *this; - company_name_ = info.company_name_; + company_name_ = info.GetRawInfo(COMPANY_NAME); return *this; } bool CompanyInfo::operator==(const CompanyInfo& other) const { - return this == &other || company_name_ == other.company_name_; + return this == &other || + GetRawInfo(COMPANY_NAME) == other.GetRawInfo(COMPANY_NAME); } void CompanyInfo::GetSupportedTypes(ServerFieldTypeSet* supported_types) const { @@ -241,7 +246,7 @@ } base::string16 CompanyInfo::GetRawInfo(ServerFieldType type) const { - return company_name_; + return IsValidOrVerified(company_name_) ? company_name_ : base::string16(); } void CompanyInfo::SetRawInfo(ServerFieldType type, @@ -250,4 +255,16 @@ company_name_ = value; } +bool CompanyInfo::IsValidOrVerified(const base::string16& value) const { + if (!base::FeatureList::IsEnabled( + autofill::features::kAutofillRejectCompanyBirthyear)) + return true; + + if (profile_ && profile_->IsVerified()) + return true; + + // Companies that are of the format of a four digit birth year are not valid. + return !MatchesPattern(value, base::UTF8ToUTF16("^(19|20)\\d{2}$")); +} + } // namespace autofill
diff --git a/components/autofill/core/browser/contact_info.h b/components/autofill/core/browser/contact_info.h index 3c5e451a..3bb79c4 100644 --- a/components/autofill/core/browser/contact_info.h +++ b/components/autofill/core/browser/contact_info.h
@@ -13,6 +13,8 @@ namespace autofill { +class AutofillProfile; + // A form group that stores name information. class NameInfo : public FormGroup { public: @@ -91,6 +93,7 @@ public: CompanyInfo(); CompanyInfo(const CompanyInfo& info); + explicit CompanyInfo(const AutofillProfile* profile); ~CompanyInfo() override; CompanyInfo& operator=(const CompanyInfo& info); @@ -100,12 +103,15 @@ // FormGroup: base::string16 GetRawInfo(ServerFieldType type) const override; void SetRawInfo(ServerFieldType type, const base::string16& value) override; + void set_profile(const AutofillProfile* profile) { profile_ = profile; } private: // FormGroup: void GetSupportedTypes(ServerFieldTypeSet* supported_types) const override; + bool IsValidOrVerified(const base::string16& value) const; base::string16 company_name_; + const AutofillProfile* profile_ = nullptr; }; } // namespace autofill
diff --git a/components/autofill/core/browser/contact_info_unittest.cc b/components/autofill/core/browser/contact_info_unittest.cc index 3f2502fa..cac7c92 100644 --- a/components/autofill/core/browser/contact_info_unittest.cc +++ b/components/autofill/core/browser/contact_info_unittest.cc
@@ -11,8 +11,11 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" +#include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/common/autofill_features.h" #include "testing/gtest/include/gtest/gtest.h" using base::ASCIIToUTF16; @@ -397,4 +400,96 @@ NamePartsAreEmptyTestCase{"", "Mitchell", "", "", false}, NamePartsAreEmptyTestCase{"", "", "Morrison", "", false})); +TEST(CompanyTest, CompanyNameYear) { + base::test::ScopedFeatureList scoped_features; + scoped_features.InitWithFeatures( + /*enabled_features=*/{features::kAutofillRejectCompanyBirthyear}, + /*disabled_features=*/{}); + + AutofillProfile profile; + CompanyInfo company(&profile); + ASSERT_FALSE(profile.IsVerified()); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Google")); + EXPECT_EQ(UTF8ToUTF16("Google"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987")); + EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("It was 1987.")); + EXPECT_EQ(UTF8ToUTF16("It was 1987."), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987 was the year.")); + EXPECT_EQ(UTF8ToUTF16("1987 was the year."), + company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Yes, 1987 was the year.")); + EXPECT_EQ(UTF8ToUTF16("Yes, 1987 was the year."), + company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2019")); + EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1818")); + EXPECT_EQ(UTF8ToUTF16("1818"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2345")); + EXPECT_EQ(UTF8ToUTF16("2345"), company.GetRawInfo(COMPANY_NAME)); + + profile.set_origin("Not empty"); + ASSERT_TRUE(profile.IsVerified()); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Google")); + EXPECT_EQ(UTF8ToUTF16("Google"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987")); + EXPECT_EQ(UTF8ToUTF16("1987"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2019")); + EXPECT_EQ(UTF8ToUTF16("2019"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1818")); + EXPECT_EQ(UTF8ToUTF16("1818"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2345")); + EXPECT_EQ(UTF8ToUTF16("2345"), company.GetRawInfo(COMPANY_NAME)); +} + +TEST(CompanyTest, CompanyNameYearCopy) { + base::test::ScopedFeatureList scoped_features; + scoped_features.InitWithFeatures( + /*enabled_features=*/{features::kAutofillRejectCompanyBirthyear}, + /*disabled_features=*/{}); + + AutofillProfile profile; + ASSERT_FALSE(profile.IsVerified()); + + CompanyInfo company_google(&profile); + CompanyInfo company_year(&profile); + + company_google.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Google")); + company_year.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987")); + + company_google = company_year; + EXPECT_EQ(UTF8ToUTF16(""), company_google.GetRawInfo(COMPANY_NAME)); +} + +TEST(CompanyTest, CompanyNameYearIsEqual) { + base::test::ScopedFeatureList scoped_features; + scoped_features.InitWithFeatures( + /*enabled_features=*/{features::kAutofillRejectCompanyBirthyear}, + /*disabled_features=*/{}); + + AutofillProfile profile; + ASSERT_FALSE(profile.IsVerified()); + + CompanyInfo company_old(&profile); + CompanyInfo company_young(&profile); + + company_old.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2019")); + company_young.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987")); + + EXPECT_EQ(company_old, company_young); +} + } // namespace autofill
diff --git a/components/autofill/core/browser/payments/local_card_migration_manager.cc b/components/autofill/core/browser/payments/local_card_migration_manager.cc index a920209..9103db1 100644 --- a/components/autofill/core/browser/payments/local_card_migration_manager.cc +++ b/components/autofill/core/browser/payments/local_card_migration_manager.cc
@@ -209,6 +209,7 @@ migration_request_.context_token = context_token; legal_message_ = base::DictionaryValue::From(std::move(legal_message)); migration_request_.risk_data.clear(); + supported_card_bin_ranges_ = supported_card_bin_ranges; // If we successfully received the legal docs, trigger the offer-to-migrate // dialog. If triggered from settings page, we pop-up the main prompt // directly. If not, we pop up the intermediate bubble. @@ -219,6 +220,12 @@ // Pops up a larger, modal dialog showing the local cards to be uploaded. ShowMainMigrationDialog(); } else { + // Filter the migratable credit cards with |supported_card_bin_ranges_|. + FilterOutUnsupportedLocalCards(); + // Abandon the migration if no supported card left. + // TODO(crbug.com/954367): Log a metric here. + if (migratable_credit_cards_.empty()) + return; client_->ShowLocalCardMigrationDialog(base::BindOnce( &LocalCardMigrationManager::OnUserAcceptedIntermediateMigrationDialog, weak_ptr_factory_.GetWeakPtr())); @@ -383,6 +390,28 @@ migratable_credit_cards_.push_back(MigratableCreditCard(*credit_card)); } } + + // Filter out Unsupported local cards when |supported_card_bin_ranges_| is not + // empty. + FilterOutUnsupportedLocalCards(); +} + +void LocalCardMigrationManager::FilterOutUnsupportedLocalCards() { + if (base::FeatureList::IsEnabled( + features::kAutofillDoNotMigrateUnsupportedLocalCards) && + !supported_card_bin_ranges_.empty()) { + // Update the |migratable_credit_cards_| with the + // |supported_card_bin_ranges|. This will remove any card from + // |migratable_credit_cards_| of which the card number is not in + // |supported_card_bin_ranges|. + auto card_is_unsupported = + [& supported_card_bin_ranges = + supported_card_bin_ranges_](MigratableCreditCard& card) { + return !payments::IsCreditCardSupported(card.credit_card(), + supported_card_bin_ranges); + }; + base::EraseIf(migratable_credit_cards_, card_is_unsupported); + } } } // namespace autofill
diff --git a/components/autofill/core/browser/payments/local_card_migration_manager.h b/components/autofill/core/browser/payments/local_card_migration_manager.h index 9c12b4e..1126f97b 100644 --- a/components/autofill/core/browser/payments/local_card_migration_manager.h +++ b/components/autofill/core/browser/payments/local_card_migration_manager.h
@@ -156,6 +156,11 @@ private: friend class LocalCardMigrationBrowserTest; FRIEND_TEST_ALL_PREFIXES(LocalCardMigrationManagerTest, + MigrateCreditCard_MigrateWhenHasSupportedLocalCard); + FRIEND_TEST_ALL_PREFIXES( + LocalCardMigrationManagerTest, + MigrateCreditCard_MigrationAbortWhenNoSupportedCards); + FRIEND_TEST_ALL_PREFIXES(LocalCardMigrationManagerTest, MigrateCreditCard_MigrationPermanentFailure); FRIEND_TEST_ALL_PREFIXES(LocalCardMigrationManagerTest, MigrateCreditCard_MigrationTemporaryFailure); @@ -167,6 +172,10 @@ // Returns the LocalCardMigrationStrikeDatabase for |client_|. LocalCardMigrationStrikeDatabase* GetLocalCardMigrationStrikeDatabase(); + // Filter the |migratable_credit_cards_| with |supported_card_bin_ranges_| and + // keep supported local cards in |migratable_credit_cards_|. + void FilterOutUnsupportedLocalCards(); + // Pops up a larger, modal dialog showing the local cards to be uploaded. void ShowMainMigrationDialog(); @@ -215,6 +224,10 @@ std::unique_ptr<LocalCardMigrationStrikeDatabase> local_card_migration_strike_database_; + // List of BIN prefix ranges which are supoorted, with the first and second + // number in the pair being the start and end of the range. + std::vector<std::pair<int, int>> supported_card_bin_ranges_; + base::WeakPtrFactory<LocalCardMigrationManager> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(LocalCardMigrationManager);
diff --git a/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc b/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc index bd35d564..5ded259 100644 --- a/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc +++ b/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc
@@ -1183,4 +1183,126 @@ 1); } +// Use one unsupported local card with more unsupported local cards available +// but DoNotMigrateUnsupportedLocalCards experiment flag is off, will trigger +// migration. +TEST_F(LocalCardMigrationManagerTest, + MigrateCreditCard_MigrateUnsupportedWhenExpOff) { + scoped_feature_list_.InitAndDisableFeature( + features::kAutofillDoNotMigrateUnsupportedLocalCards); + + // Set the billing_customer_number to designate existence of a Payments + // account. + personal_data_.SetPaymentsCustomerData( + std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456")); + + // Add a local credit card whose |TypeAndLastFourDigits| matches what we will + // enter below. + AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11", + test::NextYear().c_str(), "1", "guid1"); + // Add another local credit card. + AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11", + test::NextYear().c_str(), "1", "guid2"); + + // Set up our credit card form data. + FormData credit_card_form; + test::CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // Set up the supported card bin ranges so that there are no supported cards. + std::vector<std::pair<int, int>> supported_card_bin_ranges{ + std::make_pair(34, 34), std::make_pair(300, 305)}; + payments_client_->SetSupportedBINRanges(supported_card_bin_ranges); + + // Edit the data, and submit. + EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + test::NextYear().c_str(), "123"); + FormSubmitted(credit_card_form); + EXPECT_TRUE(local_card_migration_manager_->IntermediatePromptWasShown()); +} + +// Use one unsupported local card with more unsupported local cards will not +// trigger migration. +TEST_F(LocalCardMigrationManagerTest, + MigrateCreditCard_MigrationAbortWhenNoSupportedCards) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillDoNotMigrateUnsupportedLocalCards); + + // Set the billing_customer_number to designate existence of a Payments + // account. + personal_data_.SetPaymentsCustomerData( + std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456")); + + // Add a local credit card whose |TypeAndLastFourDigits| matches what we will + // enter below. + AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11", + test::NextYear().c_str(), "1", "guid1"); + // Add another local credit card. + AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11", + test::NextYear().c_str(), "1", "guid2"); + + // Set up our credit card form data. + FormData credit_card_form; + test::CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // Set up the supported card bin ranges so that there are no supported cards. + std::vector<std::pair<int, int>> supported_card_bin_ranges{ + std::make_pair(34, 34), std::make_pair(300, 305)}; + payments_client_->SetSupportedBINRanges(supported_card_bin_ranges); + + // Edit the data, and submit. + EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + test::NextYear().c_str(), "123"); + FormSubmitted(credit_card_form); + EXPECT_TRUE(local_card_migration_manager_->migratable_credit_cards_.empty()); + EXPECT_FALSE(local_card_migration_manager_->IntermediatePromptWasShown()); +} + +// Use one supported local card with more unsupported local cards available +// will trigger migration with the only supported local card. +TEST_F(LocalCardMigrationManagerTest, + MigrateCreditCard_MigrateWhenHasSupportedLocalCard) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillDoNotMigrateUnsupportedLocalCards); + + // Set the billing_customer_number to designate existence of a Payments + // account. + personal_data_.SetPaymentsCustomerData( + std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456")); + + // Add a local credit card whose |TypeAndLastFourDigits| matches what we will + // enter below. + AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11", + test::NextYear().c_str(), "1", "guid1"); + // Add another local credit card. + AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11", + test::NextYear().c_str(), "1", "guid2"); + + // Set up the supported card bin ranges so that there is only one supported + // card. + std::vector<std::pair<int, int>> supported_card_bin_ranges{ + std::make_pair(411, 412), std::make_pair(300, 305)}; + payments_client_->SetSupportedBINRanges(supported_card_bin_ranges); + + // Set up our credit card form data. + FormData credit_card_form; + test::CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // Edit the data, and submit. + EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + test::NextYear().c_str(), "123"); + FormSubmitted(credit_card_form); + + EXPECT_EQ(static_cast<int>( + local_card_migration_manager_->migratable_credit_cards_.size()), + 1); + EXPECT_EQ(local_card_migration_manager_->migratable_credit_cards_[0] + .credit_card() + .number(), + base::ASCIIToUTF16("4111111111111111")); + EXPECT_TRUE(local_card_migration_manager_->IntermediatePromptWasShown()); +} + } // namespace autofill
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc index 2266125..c2f1f6f 100644 --- a/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -1226,26 +1226,25 @@ // Try to update with just the origin changed. AutofillProfile original_profile(profile); + ASSERT_FALSE(original_profile.IsVerified()); CreditCard original_credit_card(credit_card); profile.set_origin(kSettingsOrigin); credit_card.set_origin(kSettingsOrigin); EXPECT_TRUE(profile.IsVerified()); EXPECT_TRUE(credit_card.IsVerified()); - UpdateProfileOnPersonalDataManager(profile); personal_data_->UpdateCreditCard(credit_card); - // Note: No refresh, as no update is expected. - + // Credit Card origin should not be overwritten. const std::vector<AutofillProfile*>& profiles2 = personal_data_->GetProfiles(); const std::vector<CreditCard*>& cards2 = personal_data_->GetCreditCards(); ASSERT_EQ(1U, profiles2.size()); ASSERT_EQ(1U, cards2.size()); - EXPECT_NE(profile.origin(), profiles2[0]->origin()); + EXPECT_EQ(profile.origin(), profiles2[0]->origin()); EXPECT_NE(credit_card.origin(), cards2[0]->origin()); - EXPECT_EQ(original_profile.origin(), profiles2[0]->origin()); + EXPECT_NE(original_profile.origin(), profiles2[0]->origin()); EXPECT_EQ(original_credit_card.origin(), cards2[0]->origin()); // Try to update with data changed as well. @@ -1268,6 +1267,39 @@ EXPECT_EQ(credit_card.origin(), cards3[0]->origin()); } +// Test that updating a verified profile with another profile whose only +// difference is the origin, would not change the old profile, and thus it would +// remain verified. +TEST_F(PersonalDataManagerTest, UpdateVerifiedProfilesOrigin) { + // Start with verified data. + AutofillProfile profile(base::GenerateGUID(), kSettingsOrigin); + test::SetProfileInfo(&profile, "Marion", "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", + "Hollywood", "CA", "91601", "US", "12345678910"); + ASSERT_TRUE(profile.IsVerified()); + AddProfileToPersonalDataManager(profile); + + const std::vector<AutofillProfile*>& profiles1 = + personal_data_->GetProfiles(); + ASSERT_EQ(1U, profiles1.size()); + EXPECT_EQ(0, profile.Compare(*profiles1[0])); + + // Try to update with just the origin changed to a non-setting origin. + AutofillProfile new_profile(profile); + new_profile.set_origin(""); + ASSERT_FALSE(new_profile.IsVerified()); + + UpdateProfileOnPersonalDataManager(profile); + + // Verified profile origin should not be overwritten. + const std::vector<AutofillProfile*>& profiles2 = + personal_data_->GetProfiles(); + ASSERT_EQ(1U, profiles2.size()); + EXPECT_EQ(profile.origin(), profiles2[0]->origin()); + EXPECT_NE(new_profile.origin(), profiles2[0]->origin()); + EXPECT_TRUE(profiles2[0]->IsVerified()); +} + // Makes sure that full cards are re-masked when full PAN storage is off. TEST_F(PersonalDataManagerTest, RefuseToStoreFullCard) { // On Linux this should be disabled automatically. Elsewhere, only if the
diff --git a/components/autofill/core/browser/phone_number.cc b/components/autofill/core/browser/phone_number.cc index f60a58b..7498365f 100644 --- a/components/autofill/core/browser/phone_number.cc +++ b/components/autofill/core/browser/phone_number.cc
@@ -31,7 +31,7 @@ } // namespace -PhoneNumber::PhoneNumber(AutofillProfile* profile) : profile_(profile) {} +PhoneNumber::PhoneNumber(const AutofillProfile* profile) : profile_(profile) {} PhoneNumber::PhoneNumber(const PhoneNumber& number) : profile_(nullptr) { *this = number;
diff --git a/components/autofill/core/browser/phone_number.h b/components/autofill/core/browser/phone_number.h index 4773e8c..ab350f3 100644 --- a/components/autofill/core/browser/phone_number.h +++ b/components/autofill/core/browser/phone_number.h
@@ -21,7 +21,7 @@ // A form group that stores phone number information. class PhoneNumber : public FormGroup { public: - explicit PhoneNumber(AutofillProfile* profile); + explicit PhoneNumber(const AutofillProfile* profile); PhoneNumber(const PhoneNumber& number); ~PhoneNumber() override; @@ -29,7 +29,7 @@ bool operator==(const PhoneNumber& other) const; bool operator!=(const PhoneNumber& other) const { return !operator==(other); } - void set_profile(AutofillProfile* profile) { profile_ = profile; } + void set_profile(const AutofillProfile* profile) { profile_ = profile; } // FormGroup implementation: void GetMatchingTypes(const base::string16& text,
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc index cde9e8db..9c8f937 100644 --- a/components/autofill/core/common/autofill_payments_features.cc +++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -28,6 +28,10 @@ const base::Feature kAutofillCreditCardAuthentication{ "AutofillCreditCardAuthentication", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kAutofillDoNotMigrateUnsupportedLocalCards{ + "AutofillDoNotMigrateUnsupportedLocalCards", + base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kAutofillDoNotUploadSaveUnsupportedCards{ "AutofillDoNotUploadSaveUnsupportedCards", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/autofill/core/common/autofill_payments_features.h b/components/autofill/core/common/autofill_payments_features.h index 3180375..df49dccc 100644 --- a/components/autofill/core/common/autofill_payments_features.h +++ b/components/autofill/core/common/autofill_payments_features.h
@@ -21,6 +21,7 @@ // All features in alphabetical order. extern const base::Feature kAutofillCreditCardAblationExperiment; extern const base::Feature kAutofillCreditCardAuthentication; +extern const base::Feature kAutofillDoNotMigrateUnsupportedLocalCards; extern const base::Feature kAutofillDoNotUploadSaveUnsupportedCards; extern const base::Feature kAutofillDownstreamUseGooglePayBrandingOniOS; extern const base::Feature kAutofillEnableLocalCardMigrationForNonSyncUser;
diff --git a/components/chromeos_camera/common/jpeg_encode_accelerator.mojom b/components/chromeos_camera/common/jpeg_encode_accelerator.mojom index 074118c3..6d6b6d7 100644 --- a/components/chromeos_camera/common/jpeg_encode_accelerator.mojom +++ b/components/chromeos_camera/common/jpeg_encode_accelerator.mojom
@@ -17,6 +17,13 @@ PLATFORM_FAILURE, }; +struct DmaBufPlane { + handle fd_handle; + int32 stride; + uint32 offset; + uint32 size; +}; + // GPU process interface exposed to the browser for encoding JPEG images. interface JpegEncodeAccelerator { // Initializes the JPEG encoder. Should be called once per encoder @@ -24,6 +31,8 @@ // initialization is successful. Initialize() => (bool success); + // TODO(wtlee): To be deprecated. (crbug.com/944705) + // // Encodes the given buffer that contains one I420 image. // |input_fd| and |output_fd| are file descriptors of shared memory. // The image is encoded from memory of |input_fd| @@ -43,4 +52,30 @@ handle exif_fd, uint32 exif_buffer_size, handle output_fd, uint32 output_buffer_size) => (int32 buffer_id, uint32 encoded_buffer_size, EncodeStatus status); + + // Encodes the given DMA-buf. The buffer id of output is |buffer_id|. The + // size of input image defined in |coded_size_width| and |coded_size_height| + // and the format |input_format| represents by its fourcc value. The plane + // information of input DMA-buf and output DMA-buf is stored in |input_planes| + // and |output_planes| respectively. Although the actual amount of buffers + // could be equal to or less than the number of planes, the amount of plane + // information |input_planes| and |output_planes| should be same as the number + // of planes. |exif_handle| is the shared memory buffer, with + // |exif_buffer_size| as size, containing Exif data which will be put onto + // APP1 segment in the output JPEG image. When the task ends, it returns + // |status| as the status code and |encoded_buffer_size| is the actual size of + // the encoded JPEG. + // + // TODO(crbug.com/957469): Consider passing more unique identifier rather than + // |buffer_id| for JpegEocdeAccelerator + EncodeWithDmaBuf( + int32 buffer_id, + uint32 input_format, + array<DmaBufPlane> input_planes, + array<DmaBufPlane> output_planes, + handle exif_handle, + uint32 exif_buffer_size, + int32 coded_size_width, + int32 coded_size_height) + => (uint32 encoded_buffer_size, EncodeStatus status); };
diff --git a/components/gwp_asan/client/BUILD.gn b/components/gwp_asan/client/BUILD.gn index 1726f2d..0c5c102 100644 --- a/components/gwp_asan/client/BUILD.gn +++ b/components/gwp_asan/client/BUILD.gn
@@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/allocator.gni") + component("client") { output_name = "gwp_asan_client" sources = [ @@ -13,14 +15,19 @@ "guarded_page_allocator_win.cc", "gwp_asan.cc", "gwp_asan.h", - "sampling_allocator_shims.cc", - "sampling_allocator_shims.h", ] if (is_posix) { sources += [ "guarded_page_allocator_posix.cc" ] } + if (use_allocator_shim) { + sources += [ + "sampling_allocator_shims.cc", + "sampling_allocator_shims.h", + ] + } + defines = [ "GWP_ASAN_IMPLEMENTATION" ] deps = [ @@ -35,8 +42,12 @@ testonly = true sources = [ "guarded_page_allocator_unittest.cc", - "sampling_allocator_shims_unittest.cc", ] + + if (use_allocator_shim) { + sources += [ "sampling_allocator_shims_unittest.cc" ] + } + deps = [ ":client", "//base/allocator:buildflags",
diff --git a/components/gwp_asan/client/gwp_asan.cc b/components/gwp_asan/client/gwp_asan.cc index 69084aa..dc04d68 100644 --- a/components/gwp_asan/client/gwp_asan.cc +++ b/components/gwp_asan/client/gwp_asan.cc
@@ -8,6 +8,7 @@ #include <cmath> #include <limits> +#include "base/allocator/buildflags.h" #include "base/debug/crash_logging.h" #include "base/feature_list.h" #include "base/logging.h" @@ -18,13 +19,17 @@ #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "components/gwp_asan/client/guarded_page_allocator.h" + +#if BUILDFLAG(USE_ALLOCATOR_SHIM) #include "components/gwp_asan/client/sampling_allocator_shims.h" +#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) namespace gwp_asan { namespace internal { namespace { +#if BUILDFLAG(USE_ALLOCATOR_SHIM) constexpr int kDefaultMaxAllocations = 35; constexpr int kDefaultMaxMetadata = 150; @@ -176,14 +181,19 @@ alloc_sampling_freq); return true; } +#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) } // namespace } // namespace internal void EnableForMalloc(bool is_canary_dev, bool is_browser_process) { +#if BUILDFLAG(USE_ALLOCATOR_SHIM) static bool init_once = internal::EnableForMalloc(is_canary_dev, is_browser_process); ignore_result(init_once); +#else + DLOG(WARNING) << "base::allocator shims are unavailable for GWP-ASan."; +#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) } } // namespace gwp_asan
diff --git a/components/gwp_asan/client/sampling_allocator_shims.cc b/components/gwp_asan/client/sampling_allocator_shims.cc index a8d50acb..6a201fa5 100644 --- a/components/gwp_asan/client/sampling_allocator_shims.cc +++ b/components/gwp_asan/client/sampling_allocator_shims.cc
@@ -7,7 +7,6 @@ #include <algorithm> #include "base/allocator/allocator_shim.h" -#include "base/allocator/buildflags.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/no_destructor.h" @@ -326,17 +325,11 @@ size_t num_metadata, size_t total_pages, size_t sampling_frequency) { -#if BUILDFLAG(USE_ALLOCATOR_SHIM) gpa = new GuardedPageAllocator(); gpa->Init(max_allocated_pages, num_metadata, total_pages); RegisterAllocatorAddress(gpa->GetCrashKeyAddress()); sampling_state.Init(sampling_frequency); base::allocator::InsertAllocatorDispatch(&g_allocator_dispatch); -#else - ignore_result(g_allocator_dispatch); - ignore_result(gpa); - DLOG(WARNING) << "base::allocator shims are unavailable for GWP-ASan."; -#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) } } // namespace internal
diff --git a/components/gwp_asan/client/sampling_allocator_shims_unittest.cc b/components/gwp_asan/client/sampling_allocator_shims_unittest.cc index f287fe6..8892d85b 100644 --- a/components/gwp_asan/client/sampling_allocator_shims_unittest.cc +++ b/components/gwp_asan/client/sampling_allocator_shims_unittest.cc
@@ -10,7 +10,6 @@ #include <string> #include "base/allocator/allocator_shim.h" -#include "base/allocator/buildflags.h" #include "base/process/process_metrics.h" #include "base/strings/string_number_conversions.h" #include "base/test/gtest_util.h" @@ -26,8 +25,6 @@ // These tests install global allocator shims so they are not safe to run in // multi-threaded contexts. Instead they're implemented as multi-process tests. -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - #if defined(OS_WIN) #include <malloc.h> static size_t GetAllocatedSize(void *mem) { @@ -328,5 +325,3 @@ } // namespace internal } // namespace gwp_asan - -#endif // BUILDFLAG(USE_ALLOCATOR_SHIM)
diff --git a/components/offline_items_collection/core/android/java/src/org/chromium/components/offline_items_collection/OfflineItem.java b/components/offline_items_collection/core/android/java/src/org/chromium/components/offline_items_collection/OfflineItem.java index d1c2d937..71b25c5 100644 --- a/components/offline_items_collection/core/android/java/src/org/chromium/components/offline_items_collection/OfflineItem.java +++ b/components/offline_items_collection/core/android/java/src/org/chromium/components/offline_items_collection/OfflineItem.java
@@ -109,7 +109,7 @@ public OfflineItem() { id = new ContentId(); - filter = OfflineItemFilter.FILTER_OTHER; + filter = OfflineItemFilter.OTHER; state = OfflineItemState.COMPLETE; }
diff --git a/components/offline_items_collection/core/offline_item_filter.h b/components/offline_items_collection/core/offline_item_filter.h index 26c91b5..b773e17 100644 --- a/components/offline_items_collection/core/offline_item_filter.h +++ b/components/offline_items_collection/core/offline_item_filter.h
@@ -11,6 +11,7 @@ // A Java counterpart will be generated for this enum. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.offline_items_collection +// GENERATED_JAVA_PREFIX_TO_STRIP: FILTER_ enum OfflineItemFilter { FILTER_PAGE = 0, FILTER_VIDEO, @@ -19,8 +20,7 @@ FILTER_DOCUMENT, FILTER_OTHER, - // Maximum value. - FILTER_BOUNDARY, + FILTER_NUM_ENTRIES, }; // Implemented for test-only. See test_support/offline_item_test_support.cc.
diff --git a/components/offline_items_collection/core/offline_item_state.h b/components/offline_items_collection/core/offline_item_state.h index 780da574..b18bf60 100644 --- a/components/offline_items_collection/core/offline_item_state.h +++ b/components/offline_items_collection/core/offline_item_state.h
@@ -20,7 +20,7 @@ FAILED, PAUSED, // TODO(dtrainor): Make sure exposing a PAUSED state does not impact // downloads resumption. - MAX_DOWNLOAD_STATE, + NUM_ENTRIES, }; // Implemented for testing only. See test_support/offline_item_test_support.cc.
diff --git a/components/offline_items_collection/core/test_support/offline_item_test_support.cc b/components/offline_items_collection/core/test_support/offline_item_test_support.cc index a6f5646..e1989d7 100644 --- a/components/offline_items_collection/core/test_support/offline_item_test_support.cc +++ b/components/offline_items_collection/core/test_support/offline_item_test_support.cc
@@ -66,8 +66,8 @@ return os << "FAILED"; case PAUSED: return os << "PAUSED"; - case MAX_DOWNLOAD_STATE: - return os << "MAX_DOWNLOAD_STATE"; + case NUM_ENTRIES: + return os << "NUM_ENTRIES"; } CHECK(false) << "state=" << static_cast<int>(state); return os; @@ -171,8 +171,8 @@ return os << "FILTER_DOCUMENT"; case FILTER_OTHER: return os << "FILTER_OTHER"; - case FILTER_BOUNDARY: - return os << "FILTER_BOUNDARY"; + case FILTER_NUM_ENTRIES: + return os << "FILTER_NUM_ENTRIES"; } CHECK(false) << "state=" << static_cast<int>(state); return os;
diff --git a/components/resources/default_100_percent/autofill/googlepay.png b/components/resources/default_100_percent/autofill/googlepay.png deleted file mode 100644 index 51e11331..0000000 --- a/components/resources/default_100_percent/autofill/googlepay.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_100_percent/autofill/googlepay_dark.png b/components/resources/default_100_percent/autofill/googlepay_dark.png deleted file mode 100644 index 0299246..0000000 --- a/components/resources/default_100_percent/autofill/googlepay_dark.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_100_percent/autofill/infobar_autofill_googlepay_with_divider.png b/components/resources/default_100_percent/autofill/infobar_autofill_googlepay_with_divider.png deleted file mode 100644 index c2fd403..0000000 --- a/components/resources/default_100_percent/autofill/infobar_autofill_googlepay_with_divider.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_100_percent/autofill/migration_header.png b/components/resources/default_100_percent/autofill/migration_header.png deleted file mode 100644 index f72197d7..0000000 --- a/components/resources/default_100_percent/autofill/migration_header.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_100_percent/autofill/migration_header_dark.png b/components/resources/default_100_percent/autofill/migration_header_dark.png deleted file mode 100644 index 5893ea3..0000000 --- a/components/resources/default_100_percent/autofill/migration_header_dark.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_200_percent/autofill/googlepay.png b/components/resources/default_200_percent/autofill/googlepay.png deleted file mode 100644 index 5cb11e8..0000000 --- a/components/resources/default_200_percent/autofill/googlepay.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_200_percent/autofill/googlepay_dark.png b/components/resources/default_200_percent/autofill/googlepay_dark.png deleted file mode 100644 index 45b0e25..0000000 --- a/components/resources/default_200_percent/autofill/googlepay_dark.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_200_percent/autofill/infobar_autofill_googlepay_with_divider.png b/components/resources/default_200_percent/autofill/infobar_autofill_googlepay_with_divider.png deleted file mode 100644 index 23b408d..0000000 --- a/components/resources/default_200_percent/autofill/infobar_autofill_googlepay_with_divider.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_200_percent/autofill/migration_header.png b/components/resources/default_200_percent/autofill/migration_header.png deleted file mode 100644 index 1749cb5..0000000 --- a/components/resources/default_200_percent/autofill/migration_header.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_200_percent/autofill/migration_header_dark.png b/components/resources/default_200_percent/autofill/migration_header_dark.png deleted file mode 100644 index b39066c..0000000 --- a/components/resources/default_200_percent/autofill/migration_header_dark.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_300_percent/autofill/googlepay.png b/components/resources/default_300_percent/autofill/googlepay.png deleted file mode 100644 index 4c04b3f..0000000 --- a/components/resources/default_300_percent/autofill/googlepay.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_300_percent/autofill/googlepay_dark.png b/components/resources/default_300_percent/autofill/googlepay_dark.png deleted file mode 100644 index 18bbe69a..0000000 --- a/components/resources/default_300_percent/autofill/googlepay_dark.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_300_percent/autofill/infobar_autofill_googlepay_with_divider.png b/components/resources/default_300_percent/autofill/infobar_autofill_googlepay_with_divider.png deleted file mode 100644 index 76afa20..0000000 --- a/components/resources/default_300_percent/autofill/infobar_autofill_googlepay_with_divider.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_300_percent/autofill/migration_header.png b/components/resources/default_300_percent/autofill/migration_header.png deleted file mode 100644 index ca1d6fa..0000000 --- a/components/resources/default_300_percent/autofill/migration_header.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_300_percent/autofill/migration_header_dark.png b/components/resources/default_300_percent/autofill/migration_header_dark.png deleted file mode 100644 index 37992e47b..0000000 --- a/components/resources/default_300_percent/autofill/migration_header_dark.png +++ /dev/null Binary files differ
diff --git a/content/browser/renderer_host/clipboard_host_impl.cc b/content/browser/renderer_host/clipboard_host_impl.cc index dfeacca..a000b88 100644 --- a/content/browser/renderer_host/clipboard_host_impl.cc +++ b/content/browser/renderer_host/clipboard_host_impl.cc
@@ -141,23 +141,20 @@ std::move(callback).Run(result); } -void ClipboardHostImpl::WriteText(ui::ClipboardType, - const base::string16& text) { +void ClipboardHostImpl::WriteText(const base::string16& text) { clipboard_writer_->WriteText(text); } -void ClipboardHostImpl::WriteHtml(ui::ClipboardType, - const base::string16& markup, +void ClipboardHostImpl::WriteHtml(const base::string16& markup, const GURL& url) { clipboard_writer_->WriteHTML(markup, url.spec()); } -void ClipboardHostImpl::WriteSmartPasteMarker(ui::ClipboardType) { +void ClipboardHostImpl::WriteSmartPasteMarker() { clipboard_writer_->WriteWebSmartPaste(); } void ClipboardHostImpl::WriteCustomData( - ui::ClipboardType, const base::flat_map<base::string16, base::string16>& data) { base::Pickle pickle; ui::WriteCustomDataToPickle(data, &pickle); @@ -165,17 +162,16 @@ pickle, ui::ClipboardFormatType::GetWebCustomDataType()); } -void ClipboardHostImpl::WriteBookmark(ui::ClipboardType, - const std::string& url, +void ClipboardHostImpl::WriteBookmark(const std::string& url, const base::string16& title) { clipboard_writer_->WriteBookmark(title, url); } -void ClipboardHostImpl::WriteImage(ui::ClipboardType, const SkBitmap& bitmap) { +void ClipboardHostImpl::WriteImage(const SkBitmap& bitmap) { clipboard_writer_->WriteImage(bitmap); } -void ClipboardHostImpl::CommitWrite(ui::ClipboardType) { +void ClipboardHostImpl::CommitWrite() { clipboard_writer_.reset( new ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE)); }
diff --git a/content/browser/renderer_host/clipboard_host_impl.h b/content/browser/renderer_host/clipboard_host_impl.h index 82a0d6b..dd64dec7 100644 --- a/content/browser/renderer_host/clipboard_host_impl.h +++ b/content/browser/renderer_host/clipboard_host_impl.h
@@ -55,21 +55,15 @@ void ReadCustomData(ui::ClipboardType clipboard_type, const base::string16& type, ReadCustomDataCallback callback) override; - void WriteText(ui::ClipboardType clipboard_type, - const base::string16& text) override; - void WriteHtml(ui::ClipboardType clipboard_type, - const base::string16& markup, - const GURL& url) override; - void WriteSmartPasteMarker(ui::ClipboardType clipboard_type) override; + void WriteText(const base::string16& text) override; + void WriteHtml(const base::string16& markup, const GURL& url) override; + void WriteSmartPasteMarker() override; void WriteCustomData( - ui::ClipboardType clipboard_type, const base::flat_map<base::string16, base::string16>& data) override; - void WriteBookmark(ui::ClipboardType clipboard_type, - const std::string& url, + void WriteBookmark(const std::string& url, const base::string16& title) override; - void WriteImage(ui::ClipboardType clipboard_type, - const SkBitmap& bitmap) override; - void CommitWrite(ui::ClipboardType clipboard_type) override; + void WriteImage(const SkBitmap& bitmap) override; + void CommitWrite() override; #if defined(OS_MACOSX) void WriteStringToFindPboard(const base::string16& text) override; #endif
diff --git a/content/browser/renderer_host/clipboard_host_impl_unittest.cc b/content/browser/renderer_host/clipboard_host_impl_unittest.cc index 05cb4a12..62ee6894 100644 --- a/content/browser/renderer_host/clipboard_host_impl_unittest.cc +++ b/content/browser/renderer_host/clipboard_host_impl_unittest.cc
@@ -47,10 +47,10 @@ SkBitmap bitmap; bitmap.allocN32Pixels(3, 2); bitmap.eraseARGB(255, 0, 255, 0); - mojo_clipboard()->WriteImage(ui::CLIPBOARD_TYPE_COPY_PASTE, bitmap); + mojo_clipboard()->WriteImage(bitmap); uint64_t sequence_number = system_clipboard()->GetSequenceNumber(ui::CLIPBOARD_TYPE_COPY_PASTE); - mojo_clipboard()->CommitWrite(ui::CLIPBOARD_TYPE_COPY_PASTE); + mojo_clipboard()->CommitWrite(); base::RunLoop().RunUntilIdle(); EXPECT_NE(sequence_number, system_clipboard()->GetSequenceNumber(
diff --git a/content/renderer/pepper/pepper_webplugin_impl.cc b/content/renderer/pepper/pepper_webplugin_impl.cc index 8ce7bdc..f5d60db 100644 --- a/content/renderer/pepper/pepper_webplugin_impl.cc +++ b/content/renderer/pepper/pepper_webplugin_impl.cc
@@ -330,9 +330,9 @@ markup = instance_->GetSelectedText(true); text = instance_->GetSelectedText(false); } - clipboard_->WriteHtml(ui::CLIPBOARD_TYPE_COPY_PASTE, markup, GURL()); - clipboard_->WriteText(ui::CLIPBOARD_TYPE_COPY_PASTE, text); - clipboard_->CommitWrite(ui::CLIPBOARD_TYPE_COPY_PASTE); + clipboard_->WriteHtml(markup, GURL()); + clipboard_->WriteText(text); + clipboard_->CommitWrite(); instance_->ReplaceSelection(""); return true;
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn index 10a0dbb..ba909aa1 100644 --- a/content/shell/BUILD.gn +++ b/content/shell/BUILD.gn
@@ -946,18 +946,16 @@ if (is_win) { data_deps += [ "//build/win:copy_cdb_to_output" ] } - if (is_posix && !is_android) { + if (is_posix) { data_deps += [ - "//third_party/breakpad:dump_syms($host_toolchain)", - "//third_party/breakpad:minidump_stackwalk($host_toolchain)", + "//third_party/breakpad:dump_syms", + "//third_party/breakpad:minidump_stackwalk", ] } if (is_android) { data_deps += [ - "//third_party/breakpad:dump_syms", "//third_party/breakpad:microdump_stackwalk", "//third_party/breakpad:minidump_dump", - "//third_party/breakpad:minidump_stackwalk", "//third_party/breakpad:symupload", "//tools/android/forwarder2", ]
diff --git a/content/shell/tools/breakpad_integration_test.py b/content/shell/tools/breakpad_integration_test.py index 8822edc..478d545 100755 --- a/content/shell/tools/breakpad_integration_test.py +++ b/content/shell/tools/breakpad_integration_test.py
@@ -238,12 +238,7 @@ failure = 'Failed to run generate_breakpad_symbols.py.' subprocess.check_call(cmd) - print '# Running test without trap handler.' run_test(options, crash_dir, symbols_dir, platform) - print '# Running test with trap handler.' - run_test(options, crash_dir, symbols_dir, platform, - additional_arguments = - ['--enable-features=WebAssemblyTrapHandler']) except: print 'FAIL: %s' % failure
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py index 8cf984b..6c65cc8 100644 --- a/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py +++ b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
@@ -11,7 +11,10 @@ import sys import run_gpu_integration_test import gpu_project_config +import inspect +import itertools +from collections import defaultdict from telemetry.testing import browser_test_runner from telemetry.internal.platform import system_info @@ -19,8 +22,13 @@ from gpu_tests import context_lost_integration_test from gpu_tests import gpu_integration_test from gpu_tests import path_util +from gpu_tests import pixel_integration_test +from gpu_tests import pixel_test_pages from gpu_tests import webgl_conformance_integration_test +from py_utils import discover +from typ import expectations_parser + path_util.AddDirToPathIfNeeded(path_util.GetChromiumSrcDir(), 'tools', 'perf') from chrome_telemetry_build import chromium_config @@ -86,7 +94,7 @@ @classmethod def GenerateGpuTests(cls, options): - pass + return [] def RunActualGpuTest(self, test_path, *args): pass @@ -133,6 +141,90 @@ return set(MockTestCase.GenerateTags(args, possible_browser)) +def _checkTestExpectationsAreForExistingTests( + test_class, mock_options, test_names=None): + expectations_file = test_class.ExpectationsFiles()[0] + test_names = test_names or [ + args[0] for args in + test_class.GenerateGpuTests(mock_options)] + trie = {} + for test in test_names: + _trie = trie.setdefault(test[0], {}) + for l in test[1:]: + _trie = _trie.setdefault(l, {}) + f = open(expectations_file, 'r') + expectations_file = os.path.basename(expectations_file) + expectations = f.read() + f.close() + parser = expectations_parser.TaggedTestListParser(expectations) + for exp in parser.expectations: + _trie = trie + for i, l in enumerate(exp.test): + if l == '*': + assert i == len(exp.test)-1, ( + ("%s:%d: '*' can only be at the " + "end of a test expectation's patttern") + % (expectations_file, exp.lineno)) + break + assert l in _trie, ( + "%s:%d: Glob '%s' does not match with any tests in the %s test suite" % + (expectations_file, exp.lineno, exp.test, test_class.Name())) + _trie = _trie[l] + + +def _checkTestExpectationsForCollision(expectations, file_name): + parser = expectations_parser.TaggedTestListParser(expectations) + tests_to_exps = defaultdict(list) + conflicts_found = False + for exp in parser.expectations: + if not exp.test.endswith('*'): + tests_to_exps[exp.test].append(exp) + error_msg = '' + for pattern, exps in tests_to_exps.items(): + if len(exps) > 1: + error_msg += ('\n\nFound conflicts for test %s in %s:\n' % + (pattern, file_name)) + for e1, e2 in itertools.combinations(exps, 2): + if e1.tags.issubset(e2.tags) or e2.tags.issubset(e1.tags): + conflicts_found = True + error_msg += (' line %d conflicts with line %d\n' % + (e1.lineno, e2.lineno)) + assert not conflicts_found, error_msg + + +def _testCheckTestExpectationsAreForExistingTests(expectations): + options = MockArgs() + expectations_file = tempfile.NamedTemporaryFile(delete=False) + expectations_file.write(expectations) + expectations_file.close() + + class _MockTestCase(object): + @classmethod + def Name(cls): + return '_MockTestCase' + + @classmethod + def GenerateGpuTests(cls, options): + tests = [('a/b/c', ())] + for t in tests: + yield t + + @classmethod + def ExpectationsFiles(cls): + return [expectations_file.name] + + _checkTestExpectationsAreForExistingTests(_MockTestCase, options) + +def _FindTestCases(): + test_cases = [] + for start_dir in gpu_project_config.CONFIG.start_dirs: + modules_to_classes = discover.DiscoverClasses( + start_dir, + gpu_project_config.CONFIG.top_level_dir, + base_class=gpu_integration_test.GpuIntegrationTest) + test_cases.extend(modules_to_classes.values()) + return test_cases + class GpuIntegrationTestUnittest(unittest.TestCase): def setUp(self): self._test_state = {} @@ -158,6 +250,86 @@ finally: temp_file.close() + def testCollisionInTestExpectationCausesAssertion(self): + test_expectations = '''# tags: [ mac win linux ] + # tags: [ intel amd nvidia ] + # tags: [ debug release ] + [ intel win ] a/b/c/d [ Failure ] + [ intel win debug ] a/b/c/d [ Skip ] + [ intel ] a/b/c/d [ Failure ] + [ amd mac ] a/b [ RetryOnFailure ] + [ mac ] a/b [ Skip ] + ''' + with self.assertRaises(AssertionError) as context: + _checkTestExpectationsForCollision(test_expectations, 'test.txt') + self.assertIn("Found conflicts for test a/b/c/d in test.txt:", + str(context.exception)) + self.assertIn('line 4 conflicts with line 5', + str(context.exception)) + self.assertIn('line 4 conflicts with line 6', + str(context.exception)) + self.assertIn('line 5 conflicts with line 6', + str(context.exception)) + self.assertIn("Found conflicts for test a/b in test.txt:", + str(context.exception)) + self.assertIn('line 7 conflicts with line 8', + str(context.exception)) + + def testNoCollisionInTestExpectations(self): + test_expectations = '''# tags: [ mac win linux ] + # tags: [ intel amd nvidia ] + # tags: [ debug release ] + [ intel win release ] a/b/* [ Failure ] + [ intel debug ] a/b/c/d [ Failure ] + [ nvidia debug ] a/b/c/d [ Failure ] + ''' + _checkTestExpectationsForCollision(test_expectations, 'test.txt') + + def testNoCollisionsInGpuTestExpectations(self): + for test_case in _FindTestCases(): + if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__: + if test_case.ExpectationsFiles(): + with open(test_case.ExpectationsFiles()[0]) as f: + _checkTestExpectationsForCollision(f.read(), + os.path.basename(f.name)) + + def testGpuTestExpectationsAreForExistingTests(self): + options = MockArgs() + for test_case in _FindTestCases(): + if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__: + if (test_case.Name() not in ('pixel', 'webgl_conformance') + and test_case.ExpectationsFiles()): + _checkTestExpectationsAreForExistingTests(test_case, options) + + def testPixelTestsExpectationsAreForExistingTests(self): + pixel_test_names = [] + for _, method in inspect.getmembers( + pixel_test_pages.PixelTestPages, predicate=inspect.isfunction): + pixel_test_names.extend( + [p.name for p in method( + pixel_integration_test.PixelIntegrationTest.test_base_name)]) + _checkTestExpectationsAreForExistingTests( + pixel_integration_test.PixelIntegrationTest, + MockArgs(), pixel_test_names) + + def testStarMustBeAtEndOfTestPattern(self): + with self.assertRaises(AssertionError) as context: + _testCheckTestExpectationsAreForExistingTests( + 'a/b*/c [ Failure ]\n') + self.assertIn(("1: '*' can only be at the end of" + " a test expectation's patttern"), + str(context.exception)) + + def testExpectationPatternNotInGeneratedTests(self): + with self.assertRaises(AssertionError) as context: + _testCheckTestExpectationsAreForExistingTests('a/b/d [ Failure ]') + self.assertIn(("1: Glob 'a/b/d' does not match with any" + " tests in the _MockTestCase test suite"), + str(context.exception)) + + def testGlobMatchesTestName(self): + _testCheckTestExpectationsAreForExistingTests('a/b* [ Failure ]') + def testOverrideDefaultRetryArgumentsinRunGpuIntegrationTests(self): self._RunGpuIntegrationTests( 'run_tests_with_expectations_files', ['--retry-limit=1'])
diff --git a/content/test/gpu/gpu_tests/pixel_integration_test.py b/content/test/gpu/gpu_tests/pixel_integration_test.py index 0e13984..305c4d1 100644 --- a/content/test/gpu/gpu_tests/pixel_integration_test.py +++ b/content/test/gpu/gpu_tests/pixel_integration_test.py
@@ -55,6 +55,9 @@ class PixelIntegrationTest( cloud_storage_integration_test_base.CloudStorageIntegrationTestBase): + + test_base_name = 'Pixel' + @classmethod def Name(cls): """The name by which this test is invoked on the command line.""" @@ -113,18 +116,18 @@ @classmethod def GenerateGpuTests(cls, options): cls.SetParsedCommandLineOptions(options) - name = 'Pixel' - pages = pixel_test_pages.DefaultPages(name) - pages += pixel_test_pages.GpuRasterizationPages(name) - pages += pixel_test_pages.ExperimentalCanvasFeaturesPages(name) - # pages += pixel_test_pages.NoGpuProcessPages(name) + namespace = pixel_test_pages.PixelTestPages + pages = namespace.DefaultPages(cls.test_base_name) + pages += namespace.GpuRasterizationPages(cls.test_base_name) + pages += namespace.ExperimentalCanvasFeaturesPages(cls.test_base_name) + # pages += namespace.NoGpuProcessPages(cls.test_base_name) # The following pages should run only on platforms where SwiftShader is # enabled. They are skipped on other platforms through test expectations. - # pages += pixel_test_pages.SwiftShaderPages(name) + # pages += namespace.SwiftShaderPages(cls.test_base_name) if sys.platform.startswith('darwin'): - pages += pixel_test_pages.MacSpecificPages(name) + pages += namespace.MacSpecificPages(cls.test_base_name) if sys.platform.startswith('win'): - pages += pixel_test_pages.DirectCompositionPages(name) + pages += namespace.DirectCompositionPages(cls.test_base_name) for p in pages: yield(p.name, gpu_relative_path + p.url, (p))
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py index 5d74401..d65dc63 100644 --- a/content/test/gpu/gpu_tests/pixel_test_pages.py +++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -122,1443 +122,1452 @@ ] } +class PixelTestPages(object): -def DefaultPages(base_name): - sw_compositing_args = ['--disable-gpu-compositing'] + @staticmethod + def DefaultPages(base_name): + sw_compositing_args = ['--disable-gpu-compositing'] - tolerance = 3 - tolerance_vp9 = 5 # VP9 video requires larger tolerance - if sys.platform == 'darwin': - # On MacOSX, pixels are slightly off. - # https://crbug.com/911895 - tolerance = 10 - tolerance_vp9 = 20 + tolerance = 3 + tolerance_vp9 = 5 # VP9 video requires larger tolerance + if sys.platform == 'darwin': + # On MacOSX, pixels are slightly off. + # https://crbug.com/911895 + tolerance = 10 + tolerance_vp9 = 20 - return [ - PixelTestPage( - 'pixel_background_image.html', - base_name + '_BackgroundImage', - test_rect=[20, 20, 370, 370], - revision=3), + return [ + PixelTestPage( + 'pixel_background_image.html', + base_name + '_BackgroundImage', + test_rect=[20, 20, 370, 370], + revision=3), - PixelTestPage( - 'pixel_canvas2d.html', - base_name + '_Canvas2DRedBox', - test_rect=[0, 0, 300, 300], - revision=13), + PixelTestPage( + 'pixel_canvas2d.html', + base_name + '_Canvas2DRedBox', + test_rect=[0, 0, 300, 300], + revision=13), - PixelTestPage( - 'pixel_canvas2d_untagged.html', - base_name + '_Canvas2DUntagged', - test_rect=[0, 0, 257, 257], - revision=0), + PixelTestPage( + 'pixel_canvas2d_untagged.html', + base_name + '_Canvas2DUntagged', + test_rect=[0, 0, 257, 257], + revision=0), - PixelTestPage( - 'pixel_css3d.html', - base_name + '_CSS3DBlueBox', - test_rect=[0, 0, 300, 300], - revision=24), + PixelTestPage( + 'pixel_css3d.html', + base_name + '_CSS3DBlueBox', + test_rect=[0, 0, 300, 300], + revision=24), - PixelTestPage( - 'pixel_webgl_aa_alpha.html', - base_name + '_WebGLGreenTriangle_AA_Alpha', - test_rect=[0, 0, 300, 300], - revision=9), + PixelTestPage( + 'pixel_webgl_aa_alpha.html', + base_name + '_WebGLGreenTriangle_AA_Alpha', + test_rect=[0, 0, 300, 300], + revision=9), - PixelTestPage( - 'pixel_webgl_noaa_alpha.html', - base_name + '_WebGLGreenTriangle_NoAA_Alpha', - test_rect=[0, 0, 300, 300], - revision=6), + PixelTestPage( + 'pixel_webgl_noaa_alpha.html', + base_name + '_WebGLGreenTriangle_NoAA_Alpha', + test_rect=[0, 0, 300, 300], + revision=6), - PixelTestPage( - 'pixel_webgl_aa_noalpha.html', - base_name + '_WebGLGreenTriangle_AA_NoAlpha', - test_rect=[0, 0, 300, 300], - revision=10), + PixelTestPage( + 'pixel_webgl_aa_noalpha.html', + base_name + '_WebGLGreenTriangle_AA_NoAlpha', + test_rect=[0, 0, 300, 300], + revision=10), - PixelTestPage( - 'pixel_webgl_noaa_noalpha.html', - base_name + '_WebGLGreenTriangle_NoAA_NoAlpha', - test_rect=[0, 0, 300, 300], - revision=6), + PixelTestPage( + 'pixel_webgl_noaa_noalpha.html', + base_name + '_WebGLGreenTriangle_NoAA_NoAlpha', + test_rect=[0, 0, 300, 300], + revision=6), - PixelTestPage( - 'pixel_webgl_noalpha_implicit_clear.html', - base_name + '_WebGLTransparentGreenTriangle_NoAlpha_ImplicitClear', - test_rect=[0, 0, 300, 300], - revision=6), + PixelTestPage( + 'pixel_webgl_noalpha_implicit_clear.html', + base_name + '_WebGLTransparentGreenTriangle_NoAlpha_ImplicitClear', + test_rect=[0, 0, 300, 300], + revision=6), - PixelTestPage( - 'pixel_webgl_sad_canvas.html', - base_name + '_WebGLSadCanvas', - test_rect=[0, 0, 300, 300], - revision=1, - optional_action='CrashGpuProcess'), + PixelTestPage( + 'pixel_webgl_sad_canvas.html', + base_name + '_WebGLSadCanvas', + test_rect=[0, 0, 300, 300], + revision=1, + optional_action='CrashGpuProcess'), - PixelTestPage( - 'pixel_scissor.html', - base_name + '_ScissorTestWithPreserveDrawingBuffer', - test_rect=[0, 0, 300, 300], - revision=0, # Golden image revision is not used - tolerance=3, - expected_colors=[ - { - 'comment': 'red top', - 'location': [1, 1], - 'size': [198, 188], - 'color': [255, 0, 0], - }, - { - 'comment': 'green bottom left', - 'location': [1, 191], - 'size': [8, 8], - 'color': [0, 255, 0], - }, - { - 'comment': 'red bottom right', - 'location': [11, 191], - 'size': [188, 8], - 'color': [255, 0, 0], - } - ]), + PixelTestPage( + 'pixel_scissor.html', + base_name + '_ScissorTestWithPreserveDrawingBuffer', + test_rect=[0, 0, 300, 300], + revision=0, # Golden image revision is not used + tolerance=3, + expected_colors=[ + { + 'comment': 'red top', + 'location': [1, 1], + 'size': [198, 188], + 'color': [255, 0, 0], + }, + { + 'comment': 'green bottom left', + 'location': [1, 191], + 'size': [8, 8], + 'color': [0, 255, 0], + }, + { + 'comment': 'red bottom right', + 'location': [11, 191], + 'size': [188, 8], + 'color': [255, 0, 0], + } + ]), - PixelTestPage( - 'pixel_canvas2d_webgl.html', - base_name + '_2DCanvasWebGL', - test_rect=[0, 0, 300, 300], - revision=13), + PixelTestPage( + 'pixel_canvas2d_webgl.html', + base_name + '_2DCanvasWebGL', + test_rect=[0, 0, 300, 300], + revision=13), - PixelTestPage( - 'pixel_background.html', - base_name + '_SolidColorBackground', - test_rect=[500, 500, 100, 100], - revision=1), + PixelTestPage( + 'pixel_background.html', + base_name + '_SolidColorBackground', + test_rect=[500, 500, 100, 100], + revision=1), - PixelTestPage( - 'pixel_video_mp4.html', - base_name + '_Video_MP4', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - tolerance=tolerance, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + PixelTestPage( + 'pixel_video_mp4.html', + base_name + '_Video_MP4', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + tolerance=tolerance, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - PixelTestPage( - 'pixel_video_mp4.html', - base_name + '_Video_MP4_DXVA', - browser_args=['--disable-features=D3D11VideoDecoder'], - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - tolerance=tolerance, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + PixelTestPage( + 'pixel_video_mp4.html', + base_name + '_Video_MP4_DXVA', + browser_args=['--disable-features=D3D11VideoDecoder'], + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + tolerance=tolerance, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - PixelTestPage( - 'pixel_video_mp4_four_colors_aspect_4x3.html', - base_name + '_Video_MP4_FourColors_Aspect_4x3', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - tolerance=tolerance, - expected_colors=[ - { - 'comment': 'outside video content, left side, white', - 'location': [1, 1], - 'size': [28, 133], - 'color': [255, 255, 255], - }, - { - 'comment': 'outside video content, right side, white', - 'location': [211, 1], - 'size': [28, 133], - 'color': [255, 255, 255], - }, - { - 'comment': 'top left video, yellow', - 'location': [35, 5], - 'size': [80, 57], - 'color': [255, 255, 15], - }, - { - 'comment': 'top right video, red', - 'location': [125, 5], - 'size': [80, 57], - 'color': [255, 17, 24], - }, - { - 'comment': 'bottom left video, blue', - 'location': [35, 73], - 'size': [80, 57], - 'color': [12, 12, 255], - }, - { - 'comment': 'bottom right video, green', - 'location': [125, 73], - 'size': [80, 57], - 'color': [44, 255, 16], - } - ]), + PixelTestPage( + 'pixel_video_mp4_four_colors_aspect_4x3.html', + base_name + '_Video_MP4_FourColors_Aspect_4x3', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + tolerance=tolerance, + expected_colors=[ + { + 'comment': 'outside video content, left side, white', + 'location': [1, 1], + 'size': [28, 133], + 'color': [255, 255, 255], + }, + { + 'comment': 'outside video content, right side, white', + 'location': [211, 1], + 'size': [28, 133], + 'color': [255, 255, 255], + }, + { + 'comment': 'top left video, yellow', + 'location': [35, 5], + 'size': [80, 57], + 'color': [255, 255, 15], + }, + { + 'comment': 'top right video, red', + 'location': [125, 5], + 'size': [80, 57], + 'color': [255, 17, 24], + }, + { + 'comment': 'bottom left video, blue', + 'location': [35, 73], + 'size': [80, 57], + 'color': [12, 12, 255], + }, + { + 'comment': 'bottom right video, green', + 'location': [125, 73], + 'size': [80, 57], + 'color': [44, 255, 16], + } + ]), - PixelTestPage( - 'pixel_video_mp4_four_colors_rot_90.html', - base_name + '_Video_MP4_FourColors_Rot_90', - test_rect=[0, 0, 427, 240], - revision=0, # Golden image revision is not used - tolerance=tolerance, - expected_colors=[ - { - 'comment': 'outside video content, left side, white', - 'location': [1, 1], - 'size': [144, 238], - 'color': [255, 255, 255], - }, - { - 'comment': 'outside video content, right side, white', - 'location': [282, 1], - 'size': [144, 238], - 'color': [255, 255, 255], - }, - { - 'comment': 'top left video, red', - 'location': [152, 5], - 'size': [55, 110], - 'color': [255, 17, 24], - }, - { - 'comment': 'top right video, green', - 'location': [220, 5], - 'size': [55, 110], - 'color': [44, 255, 16], - }, - { - 'comment': 'bottom left video, yellow', - 'location': [152, 125], - 'size': [55, 110], - 'color': [255, 255, 15], - }, - { - 'comment': 'bottom right video, blue', - 'location': [220, 125], - 'size': [55, 110], - 'color': [12, 12, 255], - } - ]), + PixelTestPage( + 'pixel_video_mp4_four_colors_rot_90.html', + base_name + '_Video_MP4_FourColors_Rot_90', + test_rect=[0, 0, 427, 240], + revision=0, # Golden image revision is not used + tolerance=tolerance, + expected_colors=[ + { + 'comment': 'outside video content, left side, white', + 'location': [1, 1], + 'size': [144, 238], + 'color': [255, 255, 255], + }, + { + 'comment': 'outside video content, right side, white', + 'location': [282, 1], + 'size': [144, 238], + 'color': [255, 255, 255], + }, + { + 'comment': 'top left video, red', + 'location': [152, 5], + 'size': [55, 110], + 'color': [255, 17, 24], + }, + { + 'comment': 'top right video, green', + 'location': [220, 5], + 'size': [55, 110], + 'color': [44, 255, 16], + }, + { + 'comment': 'bottom left video, yellow', + 'location': [152, 125], + 'size': [55, 110], + 'color': [255, 255, 15], + }, + { + 'comment': 'bottom right video, blue', + 'location': [220, 125], + 'size': [55, 110], + 'color': [12, 12, 255], + } + ]), - PixelTestPage( - 'pixel_video_mp4_four_colors_rot_180.html', - base_name + '_Video_MP4_FourColors_Rot_180', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - tolerance=tolerance, - expected_colors=[ - { - 'comment': 'top left video, green', - 'location': [5, 5], - 'size': [110, 57], - 'color': [44, 255, 16], - }, - { - 'comment': 'top right video, blue', - 'location': [125, 5], - 'size': [110, 57], - 'color': [12, 12, 255], - }, - { - 'comment': 'bottom left video, red', - 'location': [5, 72], - 'size': [110, 57], - 'color': [255, 17, 24], - }, - { - 'comment': 'bottom right video, yellow', - 'location': [125, 72], - 'size': [110, 57], - 'color': [255, 255, 15], - } - ]), + PixelTestPage( + 'pixel_video_mp4_four_colors_rot_180.html', + base_name + '_Video_MP4_FourColors_Rot_180', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + tolerance=tolerance, + expected_colors=[ + { + 'comment': 'top left video, green', + 'location': [5, 5], + 'size': [110, 57], + 'color': [44, 255, 16], + }, + { + 'comment': 'top right video, blue', + 'location': [125, 5], + 'size': [110, 57], + 'color': [12, 12, 255], + }, + { + 'comment': 'bottom left video, red', + 'location': [5, 72], + 'size': [110, 57], + 'color': [255, 17, 24], + }, + { + 'comment': 'bottom right video, yellow', + 'location': [125, 72], + 'size': [110, 57], + 'color': [255, 255, 15], + } + ]), - PixelTestPage( - 'pixel_video_mp4_four_colors_rot_270.html', - base_name + '_Video_MP4_FourColors_Rot_270', - test_rect=[0, 0, 427, 240], - revision=0, # Golden image revision is not used - tolerance=tolerance, - expected_colors=[ - { - 'comment': 'outside video content, left side, white', - 'location': [1, 1], - 'size': [144, 238], - 'color': [255, 255, 255], - }, - { - 'comment': 'outside video content, right side, white', - 'location': [282, 1], - 'size': [144, 238], - 'color': [255, 255, 255], - }, - { - 'comment': 'top left video, blue', - 'location': [152, 5], - 'size': [55, 110], - 'color': [12, 12, 255], - }, - { - 'comment': 'top right video, yellow', - 'location': [220, 5], - 'size': [55, 110], - 'color': [255, 255, 15], - }, - { - 'comment': 'bottom left video, green', - 'location': [152, 125], - 'size': [55, 110], - 'color': [44, 255, 16], - }, - { - 'comment': 'bottom right video, red', - 'location': [220, 125], - 'size': [55, 110], - 'color': [255, 17, 24], - } - ]), + PixelTestPage( + 'pixel_video_mp4_four_colors_rot_270.html', + base_name + '_Video_MP4_FourColors_Rot_270', + test_rect=[0, 0, 427, 240], + revision=0, # Golden image revision is not used + tolerance=tolerance, + expected_colors=[ + { + 'comment': 'outside video content, left side, white', + 'location': [1, 1], + 'size': [144, 238], + 'color': [255, 255, 255], + }, + { + 'comment': 'outside video content, right side, white', + 'location': [282, 1], + 'size': [144, 238], + 'color': [255, 255, 255], + }, + { + 'comment': 'top left video, blue', + 'location': [152, 5], + 'size': [55, 110], + 'color': [12, 12, 255], + }, + { + 'comment': 'top right video, yellow', + 'location': [220, 5], + 'size': [55, 110], + 'color': [255, 255, 15], + }, + { + 'comment': 'bottom left video, green', + 'location': [152, 125], + 'size': [55, 110], + 'color': [44, 255, 16], + }, + { + 'comment': 'bottom right video, red', + 'location': [220, 125], + 'size': [55, 110], + 'color': [255, 17, 24], + } + ]), - PixelTestPage( - 'pixel_video_vp9.html', - base_name + '_Video_VP9', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - tolerance=tolerance_vp9, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + PixelTestPage( + 'pixel_video_vp9.html', + base_name + '_Video_VP9', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + tolerance=tolerance_vp9, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - PixelTestPage( - 'pixel_video_vp9.html', - base_name + '_Video_VP9_DXVA', - browser_args=['--disable-features=D3D11VideoDecoder'], - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - tolerance=tolerance_vp9, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + PixelTestPage( + 'pixel_video_vp9.html', + base_name + '_Video_VP9_DXVA', + browser_args=['--disable-features=D3D11VideoDecoder'], + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + tolerance=tolerance_vp9, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - # The MP4 contains H.264 which is primarily hardware decoded on bots. - PixelTestPage( - 'pixel_video_context_loss.html?src=/media/test/data/four-colors.mp4', - base_name + '_Video_Context_Loss_MP4', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - tolerance=tolerance, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + # The MP4 contains H.264 which is primarily hardware decoded on bots. + PixelTestPage( + 'pixel_video_context_loss.html?src=/media/test/data/four-colors.mp4', + base_name + '_Video_Context_Loss_MP4', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + tolerance=tolerance, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - # The VP9 test clip is primarily software decoded on bots. - PixelTestPage( - 'pixel_video_context_loss.html?src=/media/test/data/four-colors-vp9.webm', - base_name + '_Video_Context_Loss_VP9', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - tolerance=tolerance_vp9, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + # The VP9 test clip is primarily software decoded on bots. + PixelTestPage( + ('pixel_video_context_loss.html' + '?src=/media/test/data/four-colors-vp9.webm'), + base_name + '_Video_Context_Loss_VP9', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + tolerance=tolerance_vp9, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - PixelTestPage( - 'pixel_webgl_premultiplied_alpha_false.html', - base_name + '_WebGL_PremultipliedAlpha_False', - test_rect=[0, 0, 150, 150], - revision=0, # Golden image revision is not used - tolerance=3, - expected_colors=[ - SCALE_FACTOR_OVERRIDES, - { - 'comment': 'brown', - 'location': [1, 1], - 'size': [148, 148], - # This is the color on an NVIDIA based MacBook Pro if the - # sRGB profile's applied correctly. - 'color': [102, 77, 0], - # This is the color if it isn't. - # 'color': [101, 76, 12], - }, - ]), + PixelTestPage( + 'pixel_webgl_premultiplied_alpha_false.html', + base_name + '_WebGL_PremultipliedAlpha_False', + test_rect=[0, 0, 150, 150], + revision=0, # Golden image revision is not used + tolerance=3, + expected_colors=[ + SCALE_FACTOR_OVERRIDES, + { + 'comment': 'brown', + 'location': [1, 1], + 'size': [148, 148], + # This is the color on an NVIDIA based MacBook Pro if the + # sRGB profile's applied correctly. + 'color': [102, 77, 0], + # This is the color if it isn't. + # 'color': [101, 76, 12], + }, + ]), - PixelTestPage( - 'pixel_webgl2_blitframebuffer_result_displayed.html', - base_name + '_WebGL2_BlitFramebuffer_Result_Displayed', - test_rect=[0, 0, 200, 200], - revision=0, # Golden image revision is not used - tolerance=3, - expected_colors=[ - SCALE_FACTOR_OVERRIDES, - { - 'comment': 'green', - 'location': [1, 1], - 'size': [180, 180], - 'color': [0, 255, 0], - }, - ]), + PixelTestPage( + 'pixel_webgl2_blitframebuffer_result_displayed.html', + base_name + '_WebGL2_BlitFramebuffer_Result_Displayed', + test_rect=[0, 0, 200, 200], + revision=0, # Golden image revision is not used + tolerance=3, + expected_colors=[ + SCALE_FACTOR_OVERRIDES, + { + 'comment': 'green', + 'location': [1, 1], + 'size': [180, 180], + 'color': [0, 255, 0], + }, + ]), - PixelTestPage( - 'pixel_webgl2_clearbufferfv_result_displayed.html', - base_name + '_WebGL2_ClearBufferfv_Result_Displayed', - test_rect=[0, 0, 200, 200], - revision=0, # Golden image revision is not used - tolerance=3, - expected_colors=[ - SCALE_FACTOR_OVERRIDES, - { - 'comment': 'green', - 'location': [1, 1], - 'size': [180, 180], - 'color': [0, 255, 0], - }, - ]), + PixelTestPage( + 'pixel_webgl2_clearbufferfv_result_displayed.html', + base_name + '_WebGL2_ClearBufferfv_Result_Displayed', + test_rect=[0, 0, 200, 200], + revision=0, # Golden image revision is not used + tolerance=3, + expected_colors=[ + SCALE_FACTOR_OVERRIDES, + { + 'comment': 'green', + 'location': [1, 1], + 'size': [180, 180], + 'color': [0, 255, 0], + }, + ]), - PixelTestPage( - 'pixel_repeated_webgl_to_2d.html', - base_name + '_RepeatedWebGLTo2D', - test_rect=[0, 0, 256, 256], - revision=0, # Golden image revision is not used - tolerance=3, - expected_colors=[ - SCALE_FACTOR_OVERRIDES, - { - 'comment': 'green', - # 64x64 rectangle around the center at (128,128) - 'location': [96, 96], - 'size': [64, 64], - 'color': [0, 255, 0], - }, - ]), + PixelTestPage( + 'pixel_repeated_webgl_to_2d.html', + base_name + '_RepeatedWebGLTo2D', + test_rect=[0, 0, 256, 256], + revision=0, # Golden image revision is not used + tolerance=3, + expected_colors=[ + SCALE_FACTOR_OVERRIDES, + { + 'comment': 'green', + # 64x64 rectangle around the center at (128,128) + 'location': [96, 96], + 'size': [64, 64], + 'color': [0, 255, 0], + }, + ]), - PixelTestPage( - 'pixel_repeated_webgl_to_2d.html', - base_name + '_RepeatedWebGLTo2D_SoftwareCompositing', - test_rect=[0, 0, 256, 256], - revision=0, # Golden image revision is not used - browser_args=sw_compositing_args, - tolerance=3, - expected_colors=[ - SCALE_FACTOR_OVERRIDES, - { - 'comment': 'green', - # 64x64 rectangle around the center at (128,128) - 'location': [96, 96], - 'size': [64, 64], - 'color': [0, 255, 0], - }, - ]), - ] - - -# Pages that should be run with GPU rasterization enabled. -def GpuRasterizationPages(base_name): - browser_args = ['--force-gpu-rasterization'] - return [ - PixelTestPage( - 'pixel_background.html', - base_name + '_GpuRasterization_BlueBox', - test_rect=[0, 0, 220, 220], - revision=0, # Golden image revision is not used - browser_args=browser_args, - tolerance=0, - expected_colors=[ - { - 'comment': 'body-t', - 'location': [5, 5], - 'size': [1, 1], - 'color': [0, 128, 0], - }, - { - 'comment': 'body-r', - 'location': [215, 5], - 'size': [1, 1], - 'color': [0, 128, 0], - }, - { - 'comment': 'body-b', - 'location': [215, 215], - 'size': [1, 1], - 'color': [0, 128, 0], - }, - { - 'comment': 'body-l', - 'location': [5, 215], - 'size': [1, 1], - 'color': [0, 128, 0], - }, - { - 'comment': 'background-t', - 'location': [30, 30], - 'size': [1, 1], - 'color': [0, 0, 0], - }, - { - 'comment': 'background-r', - 'location': [170, 30], - 'size': [1, 1], - 'color': [0, 0, 0], - }, - { - 'comment': 'background-b', - 'location': [170, 170], - 'size': [1, 1], - 'color': [0, 0, 0], - }, - { - 'comment': 'background-l', - 'location': [30, 170], - 'size': [1, 1], - 'color': [0, 0, 0], - }, - { - 'comment': 'box-t', - 'location': [70, 70], - 'size': [1, 1], - 'color': [0, 0, 255], - }, - { - 'comment': 'box-r', - 'location': [140, 70], - 'size': [1, 1], - 'color': [0, 0, 255], - }, - { - 'comment': 'box-b', - 'location': [140, 140], - 'size': [1, 1], - 'color': [0, 0, 255], - }, - { - 'comment': 'box-l', - 'location': [70, 140], - 'size': [1, 1], - 'color': [0, 0, 255], - } - ]), - PixelTestPage( - 'concave_paths.html', - base_name + '_GpuRasterization_ConcavePaths', - test_rect=[0, 0, 100, 100], - revision=0, # Golden image revision is not used - browser_args=browser_args, - tolerance=0, - expected_colors=[ - { - 'comment': 'outside', - 'location': [80, 60], - 'size': [1, 1], - 'color': [255, 255, 255], - }, - { - 'comment': 'outside', - 'location': [28, 20], - 'size': [1, 1], - 'color': [255, 255, 255], - }, - { - 'comment': 'inside', - 'location': [32, 25], - 'size': [1, 1], - 'color': [255, 215, 0], - }, - { - 'comment': 'inside', - 'location': [80, 80], - 'size': [1, 1], - 'color': [255, 215, 0], - } - ]) - ] - -# Pages that should be run with experimental canvas features. -def ExperimentalCanvasFeaturesPages(base_name): - browser_args = [ - '--enable-experimental-web-platform-features'] # for lowLatency - unaccelerated_args = [ - '--disable-accelerated-2d-canvas', - '--disable-gpu-compositing'] - - return [ - PixelTestPage( - 'pixel_offscreenCanvas_transfer_after_style_resize.html', - base_name + '_OffscreenCanvasTransferAfterStyleResize', - test_rect=[0, 0, 350, 350], - revision=10, - browser_args=browser_args), - - PixelTestPage( - 'pixel_offscreenCanvas_transfer_before_style_resize.html', - base_name + '_OffscreenCanvasTransferBeforeStyleResize', - test_rect=[0, 0, 350, 350], - revision=10, - browser_args=browser_args), - - PixelTestPage( - 'pixel_offscreenCanvas_webgl_paint_after_resize.html', - base_name + '_OffscreenCanvasWebGLPaintAfterResize', - test_rect=[0, 0, 200, 200], - browser_args=browser_args, - revision=0, # Golden image revision is not used - tolerance=0, - expected_colors=[ - SCALE_FACTOR_OVERRIDES, - { - 'comment': 'resized area', - 'location': [1, 1], - 'size': [48, 98], - 'color': [0, 255, 0], - }, - { - 'comment': 'outside resized area', - 'location': [51, 1], - 'size': [48, 98], - 'color': [255, 255, 255], - }, - ]), - - PixelTestPage( - 'pixel_offscreenCanvas_transferToImageBitmap_main.html', - base_name + '_OffscreenCanvasTransferToImageBitmap', - test_rect=[0, 0, 300, 300], - revision=6, - browser_args=browser_args), - - PixelTestPage( - 'pixel_offscreenCanvas_transferToImageBitmap_worker.html', - base_name + '_OffscreenCanvasTransferToImageBitmapWorker', - test_rect=[0, 0, 300, 300], - revision=6, - browser_args=browser_args), - - PixelTestPage( - 'pixel_offscreenCanvas_webgl_commit_main.html', - base_name + '_OffscreenCanvasWebGLDefault', - test_rect=[0, 0, 360, 200], - revision=11, - browser_args=browser_args), - - PixelTestPage( - 'pixel_offscreenCanvas_webgl_commit_worker.html', - base_name + '_OffscreenCanvasWebGLDefaultWorker', - test_rect=[0, 0, 360, 200], - revision=11, - browser_args=browser_args), - - PixelTestPage( - 'pixel_offscreenCanvas_webgl_commit_main.html', - base_name + '_OffscreenCanvasWebGLSoftwareCompositing', - test_rect=[0, 0, 360, 200], - revision=7, - browser_args=browser_args + ['--disable-gpu-compositing']), - - PixelTestPage( - 'pixel_offscreenCanvas_webgl_commit_worker.html', - base_name + '_OffscreenCanvasWebGLSoftwareCompositingWorker', - test_rect=[0, 0, 360, 200], - revision=7, - browser_args=browser_args + ['--disable-gpu-compositing']), - - PixelTestPage( - 'pixel_offscreenCanvas_2d_commit_main.html', - base_name + '_OffscreenCanvasAccelerated2D', - test_rect=[0, 0, 360, 200], - revision=12, - browser_args=browser_args), - - PixelTestPage( - 'pixel_offscreenCanvas_2d_commit_worker.html', - base_name + '_OffscreenCanvasAccelerated2DWorker', - test_rect=[0, 0, 360, 200], - revision=12, - browser_args=browser_args), - - PixelTestPage( - 'pixel_offscreenCanvas_2d_commit_main.html', - base_name + '_OffscreenCanvasUnaccelerated2D', - test_rect=[0, 0, 360, 200], - revision=9, - browser_args=browser_args + unaccelerated_args), - - PixelTestPage( - 'pixel_offscreenCanvas_2d_commit_worker.html', - base_name + '_OffscreenCanvasUnaccelerated2DWorker', - test_rect=[0, 0, 360, 200], - revision=9, - browser_args=browser_args + unaccelerated_args), - - PixelTestPage( - 'pixel_offscreenCanvas_2d_commit_main.html', - base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositing', - test_rect=[0, 0, 360, 200], - revision=14, - browser_args=browser_args + ['--disable-accelerated-2d-canvas']), - - PixelTestPage( - 'pixel_offscreenCanvas_2d_commit_worker.html', - base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositingWorker', - test_rect=[0, 0, 360, 200], - revision=13, - browser_args=browser_args + ['--disable-accelerated-2d-canvas']), - - PixelTestPage( - 'pixel_offscreenCanvas_2d_resize_on_worker.html', - base_name + '_OffscreenCanvas2DResizeOnWorker', - test_rect=[0, 0, 200, 200], - revision=7, - browser_args=browser_args), - - PixelTestPage( - 'pixel_offscreenCanvas_webgl_resize_on_worker.html', - base_name + '_OffscreenCanvasWebglResizeOnWorker', - test_rect=[0, 0, 200, 200], - revision=9, - browser_args=browser_args), - - PixelTestPage( - 'pixel_canvas_display_linear-rgb.html', - base_name + '_CanvasDisplayLinearRGBAccelerated2D', - test_rect=[0, 0, 140, 140], - revision=10, - browser_args=browser_args), - - PixelTestPage( - 'pixel_canvas_display_linear-rgb.html', - base_name + '_CanvasDisplayLinearRGBUnaccelerated2D', - test_rect=[0, 0, 140, 140], - revision=2, - browser_args=browser_args + unaccelerated_args), - - PixelTestPage( - 'pixel_canvas_display_linear-rgb.html', - base_name + '_CanvasDisplayLinearRGBUnaccelerated2DGPUCompositing', - test_rect=[0, 0, 140, 140], - revision=8, - browser_args=browser_args + ['--disable-accelerated-2d-canvas']), - - PixelTestPage( - 'pixel_canvas_display_srgb.html', - base_name + '_CanvasDisplaySRGBAccelerated2D', - test_rect=[0, 0, 140, 140], - revision=0, # not used, unsupported - browser_args=browser_args), - - PixelTestPage( - 'pixel_canvas_display_srgb.html', - base_name + '_CanvasDisplaySRGBUnaccelerated2D', - test_rect=[0, 0, 140, 140], - revision=1, - browser_args=browser_args + unaccelerated_args), - - PixelTestPage( - 'pixel_canvas_display_srgb.html', - base_name + '_CanvasDisplaySRGBUnaccelerated2DGPUCompositing', - test_rect=[0, 0, 140, 140], - revision=1, - browser_args=browser_args + ['--disable-accelerated-2d-canvas']), - - PixelTestPage( - 'pixel_canvas_low_latency_2d.html', - base_name + '_CanvasLowLatency2D', - test_rect=[0, 0, 100, 100], - revision=8, - browser_args=browser_args), - - PixelTestPage( - 'pixel_canvas_low_latency_2d.html', - base_name + '_CanvasUnacceleratedLowLatency2D', - test_rect=[0, 0, 100, 100], - revision=3, - browser_args=browser_args + unaccelerated_args), - - PixelTestPage( - 'pixel_canvas_low_latency_webgl.html', - base_name + '_CanvasLowLatencyWebGL', - test_rect=[0, 0, 200, 200], - revision=0, # not used - browser_args=browser_args, - tolerance=0, - expected_colors=[ - SCALE_FACTOR_OVERRIDES, - { - 'comment': 'green', - 'location': [1, 1], - 'size': [98, 98], - 'color': [0, 255, 0], - }, - ]), - ] - -# Only add these tests on platforms where SwiftShader is enabled. -# Currently this is Windows and Linux. -def SwiftShaderPages(base_name): - browser_args = ['--disable-gpu'] - suffix = "_SwiftShader" - return [ - PixelTestPage( - 'pixel_canvas2d.html', - base_name + '_Canvas2DRedBox' + suffix, - test_rect=[0, 0, 300, 300], - revision=1, - browser_args=browser_args), - - PixelTestPage( - 'pixel_css3d.html', - base_name + '_CSS3DBlueBox' + suffix, - test_rect=[0, 0, 300, 300], - revision=2, - browser_args=browser_args), - - PixelTestPage( - 'pixel_webgl_aa_alpha.html', - base_name + '_WebGLGreenTriangle_AA_Alpha' + suffix, - test_rect=[0, 0, 300, 300], - revision=2, - browser_args=browser_args), - - PixelTestPage( - 'pixel_repeated_webgl_to_2d.html', - base_name + '_RepeatedWebGLTo2D' + suffix, - test_rect=[0, 0, 256, 256], - revision=0, # Golden image revision is not used - browser_args=browser_args, - tolerance=3, - expected_colors=[ - SCALE_FACTOR_OVERRIDES, - { - 'comment': 'green', - # 64x64 rectangle around the center at (128,128) - 'location': [96, 96], - 'size': [64, 64], - 'color': [0, 255, 0], - }, - ]), - ] - -# Test rendering where GPU process is blocked. -def NoGpuProcessPages(base_name): - browser_args = ['--disable-gpu', '--disable-software-rasterizer'] - suffix = "_NoGpuProcess" - return [ - PixelTestPage( - 'pixel_canvas2d.html', - base_name + '_Canvas2DRedBox' + suffix, - test_rect=[0, 0, 300, 300], - revision=2, - browser_args=browser_args, - gpu_process_disabled=True), - - PixelTestPage( - 'pixel_css3d.html', - base_name + '_CSS3DBlueBox' + suffix, - test_rect=[0, 0, 300, 300], - revision=2, - browser_args=browser_args, - gpu_process_disabled=True), - ] - -# Pages that should be run with various macOS specific command line -# arguments. -def MacSpecificPages(base_name): - iosurface_2d_canvas_args = [ - '--enable-accelerated-2d-canvas'] - - non_chromium_image_args = ['--disable-webgl-image-chromium'] - - # This disables the Core Animation compositor, falling back to the - # old GLRenderer path, but continuing to allocate IOSurfaces for - # WebGL's back buffer. - no_overlays_args = ['--disable-mac-overlays'] - - return [ - # On macOS, test the IOSurface 2D Canvas compositing path. - PixelTestPage( - 'pixel_canvas2d_accelerated.html', - base_name + '_IOSurface2DCanvas', - test_rect=[0, 0, 400, 400], - revision=1, - browser_args=iosurface_2d_canvas_args), - PixelTestPage( - 'pixel_canvas2d_webgl.html', - base_name + '_IOSurface2DCanvasWebGL', - test_rect=[0, 0, 300, 300], - revision=4, - browser_args=iosurface_2d_canvas_args), - - # On macOS, test WebGL non-Chromium Image compositing path. - PixelTestPage( - 'pixel_webgl_aa_alpha.html', - base_name + '_WebGLGreenTriangle_NonChromiumImage_AA_Alpha', - test_rect=[0, 0, 300, 300], - revision=3, - browser_args=non_chromium_image_args), - PixelTestPage( - 'pixel_webgl_noaa_alpha.html', - base_name + '_WebGLGreenTriangle_NonChromiumImage_NoAA_Alpha', - test_rect=[0, 0, 300, 300], - revision=1, - browser_args=non_chromium_image_args), - PixelTestPage( - 'pixel_webgl_aa_noalpha.html', - base_name + '_WebGLGreenTriangle_NonChromiumImage_AA_NoAlpha', - test_rect=[0, 0, 300, 300], - revision=3, - browser_args=non_chromium_image_args), - PixelTestPage( - 'pixel_webgl_noaa_noalpha.html', - base_name + '_WebGLGreenTriangle_NonChromiumImage_NoAA_NoAlpha', - test_rect=[0, 0, 300, 300], - revision=1, - browser_args=non_chromium_image_args), - - # On macOS, test CSS filter effects with and without the CA compositor. - PixelTestPage( - 'filter_effects.html', - base_name + '_CSSFilterEffects', - test_rect=[0, 0, 300, 300], - revision=11), - PixelTestPage( - 'filter_effects.html', - base_name + '_CSSFilterEffects_NoOverlays', - test_rect=[0, 0, 300, 300], - revision=11, - tolerance=10, - browser_args=no_overlays_args), - - # Test WebGL's premultipliedAlpha:false without the CA compositor. - PixelTestPage( - 'pixel_webgl_premultiplied_alpha_false.html', - base_name + '_WebGL_PremultipliedAlpha_False_NoOverlays', - test_rect=[0, 0, 150, 150], - revision=0, # Golden image revision is not used - browser_args=no_overlays_args, - tolerance=3, - expected_colors=[ - SCALE_FACTOR_OVERRIDES, - { - 'comment': 'brown', - 'location': [1, 1], - 'size': [148, 148], - # This is the color on an NVIDIA based MacBook Pro if the - # sRGB profile's applied correctly. - 'color': [102, 77, 0], - # This is the color if it isn't. - # 'color': [101, 76, 12], - }, - ]), - ] - -def DirectCompositionPages(base_name): - browser_args = ['--enable-direct-composition-layers'] - browser_args_Underlay = browser_args + [ - '--enable-features=DirectCompositionUnderlays'] - browser_args_Nonroot = browser_args +[ - '--enable-features=DirectCompositionNonrootOverlays,' + - 'DirectCompositionUnderlays'] - browser_args_Complex = browser_args + [ - '--enable-features=DirectCompositionComplexOverlays,' + - 'DirectCompositionNonrootOverlays,' + - 'DirectCompositionUnderlays'] - browser_args_YUY2 = browser_args + [ - '--disable-features=DirectCompositionPreferNV12Overlays'] - browser_args_DXVA = browser_args + [ - '--disable-features=D3D11VideoDecoder'] - browser_args_Underlay_DXVA = browser_args + [ - '--enable-features=DirectCompositionUnderlays', - '--disable-features=D3D11VideoDecoder'] - - tolerance_dc = 5 - - return [ - PixelTestPage( - 'pixel_video_mp4.html', - base_name + '_DirectComposition_Video_MP4', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - browser_args=browser_args, - tolerance=tolerance_dc, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - - PixelTestPage( - 'pixel_video_mp4.html', - base_name + '_DirectComposition_Video_MP4_DXVA', - browser_args=browser_args_DXVA, - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - tolerance=tolerance_dc, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - - PixelTestPage( - 'pixel_video_mp4_fullsize.html', - base_name + '_DirectComposition_Video_MP4_Fullsize', - browser_args=browser_args, - test_rect=[0, 0, 960, 540], - revision=0, # Golden image revision is not used - other_args={'zero_copy': True}, - tolerance=tolerance_dc, - expected_colors=[ - { - 'comment': 'top left video, yellow', - 'location': [10, 10], - 'size': [460, 250], - 'color': [255, 255, 15], - }, - { - 'comment': 'top right video, red', - 'location': [490, 10], - 'size': [460, 250], - 'color': [255, 17, 24], - }, - { - 'comment': 'bottom left video, blue', - 'location': [10, 280], - 'size': [460, 250], - 'color': [12, 12, 255], - }, - { - 'comment': 'bottom right video, green', - 'location': [490, 280], - 'size': [460, 250], - 'color': [44, 255, 16], - } - ]), - - PixelTestPage( - 'pixel_video_mp4.html', - base_name + '_DirectComposition_Video_MP4_YUY2', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - browser_args=browser_args_YUY2, - other_args={'expect_yuy2': True}, - tolerance=tolerance_dc, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - - PixelTestPage( - 'pixel_video_mp4_four_colors_aspect_4x3.html', - base_name + '_DirectComposition_Video_MP4_FourColors_Aspect_4x3', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - browser_args=browser_args, - tolerance=tolerance_dc, - expected_colors=[ - { - 'comment': 'outside video content, left side, white', - 'location': [1, 1], - 'size': [28, 133], - 'color': [255, 255, 255], - }, - { - 'comment': 'outside video content, right side, white', - 'location': [211, 1], - 'size': [28, 133], - 'color': [255, 255, 255], - }, - { - 'comment': 'top left video, yellow', - 'location': [35, 5], - 'size': [80, 57], - 'color': [255, 255, 15], - }, - { - 'comment': 'top right video, red', - 'location': [125, 5], - 'size': [80, 57], - 'color': [255, 17, 24], - }, - { - 'comment': 'bottom left video, blue', - 'location': [35, 73], - 'size': [80, 57], - 'color': [12, 12, 255], - }, - { - 'comment': 'bottom right video, green', - 'location': [125, 73], - 'size': [80, 57], - 'color': [44, 255, 16], - } - ]), - - PixelTestPage( - 'pixel_video_mp4_four_colors_rot_90.html', - base_name + '_DirectComposition_Video_MP4_FourColors_Rot_90', - test_rect=[0, 0, 427, 240], - revision=0, # Golden image revision is not used - browser_args=browser_args, - other_args={'video_is_rotated': True}, - tolerance=tolerance_dc, - expected_colors=[ - { - 'comment': 'outside video content, left side, white', - 'location': [1, 1], - 'size': [144, 238], - 'color': [255, 255, 255], - }, - { - 'comment': 'outside video content, right side, white', - 'location': [282, 1], - 'size': [144, 238], - 'color': [255, 255, 255], - }, - { - 'comment': 'top left video, red', - 'location': [152, 5], - 'size': [55, 110], - 'color': [255, 17, 24], - }, - { - 'comment': 'top right video, green', - 'location': [220, 5], - 'size': [55, 110], - 'color': [44, 255, 16], - }, - { - 'comment': 'bottom left video, yellow', - 'location': [152, 125], - 'size': [55, 110], - 'color': [255, 255, 15], - }, - { - 'comment': 'bottom right video, blue', - 'location': [220, 125], - 'size': [55, 110], - 'color': [12, 12, 255], - }]), - - PixelTestPage( - 'pixel_video_mp4_four_colors_rot_180.html', - base_name + '_DirectComposition_Video_MP4_FourColors_Rot_180', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - browser_args=browser_args, - other_args={'video_is_rotated': True}, - tolerance=tolerance_dc, - expected_colors=[ - { - 'comment': 'top left video, green', - 'location': [5, 5], - 'size': [110, 57], - 'color': [44, 255, 16], - }, - { - 'comment': 'top right video, blue', - 'location': [125, 5], - 'size': [110, 57], - 'color': [12, 12, 255], - }, - { - 'comment': 'bottom left video, red', - 'location': [5, 72], - 'size': [110, 57], - 'color': [255, 17, 24], - }, - { - 'comment': 'bottom right video, yellow', - 'location': [125, 72], - 'size': [110, 57], - 'color': [255, 255, 15], - }]), - - PixelTestPage( - 'pixel_video_mp4_four_colors_rot_270.html', - base_name + '_DirectComposition_Video_MP4_FourColors_Rot_270', - test_rect=[0, 0, 427, 240], - revision=0, # Golden image revision is not used - browser_args=browser_args, - other_args={'video_is_rotated': True}, - tolerance=tolerance_dc, - expected_colors=[ - { - 'comment': 'outside video content, left side, white', - 'location': [1, 1], - 'size': [144, 238], - 'color': [255, 255, 255], - }, - { - 'comment': 'outside video content, right side, white', - 'location': [282, 1], - 'size': [144, 238], - 'color': [255, 255, 255], - }, - { - 'comment': 'top left video, blue', - 'location': [152, 5], - 'size': [55, 110], - 'color': [12, 12, 255], - }, - { - 'comment': 'top right video, yellow', - 'location': [220, 5], - 'size': [55, 110], - 'color': [255, 255, 15], - }, - { - 'comment': 'bottom left video, green', - 'location': [152, 125], - 'size': [55, 110], - 'color': [44, 255, 16], - }, - { - 'comment': 'bottom right video, red', - 'location': [220, 125], - 'size': [55, 110], - 'color': [255, 17, 24], - }]), - - PixelTestPage( - 'pixel_video_vp9.html', - base_name + '_DirectComposition_Video_VP9', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - browser_args=browser_args, - tolerance=tolerance_dc, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - - PixelTestPage( - 'pixel_video_vp9.html', - base_name + '_DirectComposition_Video_VP9_DXVA', - browser_args=browser_args_DXVA, - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - tolerance=tolerance_dc, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - - PixelTestPage( - 'pixel_video_vp9_fullsize.html', - base_name + '_DirectComposition_Video_VP9_Fullsize', - test_rect=[0, 0, 960, 540], - revision=0, # Golden image revision is not used - browser_args=browser_args, - other_args={'zero_copy': True}, - tolerance=tolerance_dc, - expected_colors=[ - { - 'comment': 'top left video, yellow', - 'location': [10, 10], - 'size': [460, 250], - 'color': [255, 255, 15], - }, - { - 'comment': 'top right video, red', - 'location': [490, 10], - 'size': [460, 250], - 'color': [255, 17, 24], - }, - { - 'comment': 'bottom left video, blue', - 'location': [10, 280], - 'size': [460, 250], - 'color': [12, 12, 255], - }, - { - 'comment': 'bottom right video, green', - 'location': [490, 280], - 'size': [460, 250], - 'color': [44, 255, 16], - } - ]), - - PixelTestPage( - 'pixel_video_vp9.html', - base_name + '_DirectComposition_Video_VP9_YUY2', - test_rect=[0, 0, 240, 135], - revision=0, # Golden image revision is not used - browser_args=browser_args_YUY2, - other_args={'expect_yuy2': True}, - tolerance=tolerance_dc, - expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), - - PixelTestPage( - 'pixel_video_underlay.html', - base_name + '_DirectComposition_Underlay', - test_rect=[0, 0, 240, 136], - revision=0, # Golden image revision is not used - browser_args=browser_args_Underlay, - tolerance=tolerance_dc, - expected_colors=[ - { - 'comment': 'black top left', - 'location': [4, 4], - 'size': [20, 20], - 'color': [0, 0, 0], - }, - { - 'comment': 'yellow top left quadrant', - 'location': [4, 34], - 'size': [110, 30], - 'color': [255, 255, 15], - }, - { - 'comment': 'red top right quadrant', - 'location': [124, 4], - 'size': [110, 60], - 'color': [255, 17, 24], - }, - { - 'comment': 'blue bottom left quadrant', - 'location': [4, 72], - 'size': [110, 60], - 'color': [12, 12, 255], - }, - { - 'comment': 'green bottom right quadrant', - 'location': [124, 72], - 'size': [110, 60], - 'color': [44, 255, 16], - } - ]), - - PixelTestPage( - 'pixel_video_underlay.html', - base_name + '_DirectComposition_Underlay_DXVA', - test_rect=[0, 0, 240, 136], - revision=0, # Golden image revision is not used - browser_args=browser_args_Underlay_DXVA, - tolerance=tolerance_dc, - expected_colors=[ - { - 'comment': 'black top left', - 'location': [4, 4], - 'size': [20, 20], - 'color': [0, 0, 0], - }, - { - 'comment': 'yellow top left quadrant', - 'location': [4, 34], - 'size': [110, 30], - 'color': [255, 255, 15], - }, - { - 'comment': 'red top right quadrant', - 'location': [124, 4], - 'size': [110, 60], - 'color': [255, 17, 24], - }, - { - 'comment': 'blue bottom left quadrant', - 'location': [4, 72], - 'size': [110, 60], - 'color': [12, 12, 255], - }, - { - 'comment': 'green bottom right quadrant', - 'location': [124, 72], - 'size': [110, 60], - 'color': [44, 255, 16], - } - ]), - - PixelTestPage( - 'pixel_video_underlay_fullsize.html', - base_name + '_DirectComposition_Underlay_Fullsize', - test_rect=[0, 0, 960, 540], - revision=0, # Golden image revision is not used - browser_args=browser_args_Underlay, - other_args={'zero_copy': True}, - tolerance=tolerance_dc, - expected_colors=[ - { - 'comment': 'black top left', - 'location': [4, 4], - 'size': [20, 20], - 'color': [0, 0, 0], - }, - { - 'comment': 'yellow top left quadrant', - 'location': [10, 35], - 'size': [460, 225], - 'color': [255, 255, 15], - }, - { - 'comment': 'red top right quadrant', - 'location': [490, 10], - 'size': [460, 250], - 'color': [255, 17, 24], - }, - { - 'comment': 'blue bottom left quadrant', - 'location': [10, 280], - 'size': [460, 250], - 'color': [12, 12, 255], - }, - { - 'comment': 'green bottom right quadrant', - 'location': [490, 290], - 'size': [460, 250], - 'color': [44, 255, 16], - } - ]), - - PixelTestPage( - 'pixel_video_nonroot.html', - base_name + '_DirectComposition_Nonroot', - test_rect=[0, 0, 240, 136], - revision=0, # Golden image revision is not used - browser_args=browser_args_Nonroot, - tolerance=tolerance_dc, - expected_colors=[ - { - 'comment': 'black top left', - 'location': [4, 4], - 'size': [20, 20], - 'color': [0, 0, 0], - }, - { - 'comment': 'yellow top left quadrant', - 'location': [4, 34], - 'size': [110, 30], - 'color': [255, 255, 15], - }, - { - 'comment': 'red top right quadrant', - 'location': [124, 4], - 'size': [50, 60], - 'color': [255, 17, 24], - }, - { - 'comment': 'blue bottom left quadrant', - 'location': [4, 72], - 'size': [110, 60], - 'color': [12, 12, 255], - }, - { - 'comment': 'green bottom right quadrant', - 'location': [124, 72], - 'size': [50, 60], - 'color': [44, 255, 16], - } - ]), - - PixelTestPage( - 'pixel_video_complex_overlays.html', - base_name + '_DirectComposition_ComplexOverlays', - test_rect=[0, 0, 240, 136], - revision=0, # Golden image revision is not used - browser_args=browser_args_Complex, - other_args={'video_is_rotated': True}, - tolerance=tolerance_dc, - expected_colors=[ - { - 'comment': 'black top left', - 'location': [4, 4], - 'size': [20, 20], - 'color': [0, 0, 0], - }, - { - 'comment': 'yellow top left quadrant', - 'location': [60, 10], - 'size': [65, 30], - 'color': [255, 255, 15], - }, - { - 'comment': 'red top right quadrant', - 'location': [150, 45], - 'size': [65, 30], - 'color': [255, 17, 24], - }, - { - 'comment': 'blue bottom left quadrant', - 'location': [30, 70], - 'size': [65, 30], - 'color': [12, 12, 255], - }, - { - 'comment': 'green bottom right quadrant', - 'location': [130, 100], - 'size': [65, 30], - 'color': [44, 255, 16], - }]), + PixelTestPage( + 'pixel_repeated_webgl_to_2d.html', + base_name + '_RepeatedWebGLTo2D_SoftwareCompositing', + test_rect=[0, 0, 256, 256], + revision=0, # Golden image revision is not used + browser_args=sw_compositing_args, + tolerance=3, + expected_colors=[ + SCALE_FACTOR_OVERRIDES, + { + 'comment': 'green', + # 64x64 rectangle around the center at (128,128) + 'location': [96, 96], + 'size': [64, 64], + 'color': [0, 255, 0], + }, + ]), ] + + + # Pages that should be run with GPU rasterization enabled. + @staticmethod + def GpuRasterizationPages(base_name): + browser_args = ['--force-gpu-rasterization'] + return [ + PixelTestPage( + 'pixel_background.html', + base_name + '_GpuRasterization_BlueBox', + test_rect=[0, 0, 220, 220], + revision=0, # Golden image revision is not used + browser_args=browser_args, + tolerance=0, + expected_colors=[ + { + 'comment': 'body-t', + 'location': [5, 5], + 'size': [1, 1], + 'color': [0, 128, 0], + }, + { + 'comment': 'body-r', + 'location': [215, 5], + 'size': [1, 1], + 'color': [0, 128, 0], + }, + { + 'comment': 'body-b', + 'location': [215, 215], + 'size': [1, 1], + 'color': [0, 128, 0], + }, + { + 'comment': 'body-l', + 'location': [5, 215], + 'size': [1, 1], + 'color': [0, 128, 0], + }, + { + 'comment': 'background-t', + 'location': [30, 30], + 'size': [1, 1], + 'color': [0, 0, 0], + }, + { + 'comment': 'background-r', + 'location': [170, 30], + 'size': [1, 1], + 'color': [0, 0, 0], + }, + { + 'comment': 'background-b', + 'location': [170, 170], + 'size': [1, 1], + 'color': [0, 0, 0], + }, + { + 'comment': 'background-l', + 'location': [30, 170], + 'size': [1, 1], + 'color': [0, 0, 0], + }, + { + 'comment': 'box-t', + 'location': [70, 70], + 'size': [1, 1], + 'color': [0, 0, 255], + }, + { + 'comment': 'box-r', + 'location': [140, 70], + 'size': [1, 1], + 'color': [0, 0, 255], + }, + { + 'comment': 'box-b', + 'location': [140, 140], + 'size': [1, 1], + 'color': [0, 0, 255], + }, + { + 'comment': 'box-l', + 'location': [70, 140], + 'size': [1, 1], + 'color': [0, 0, 255], + } + ]), + PixelTestPage( + 'concave_paths.html', + base_name + '_GpuRasterization_ConcavePaths', + test_rect=[0, 0, 100, 100], + revision=0, # Golden image revision is not used + browser_args=browser_args, + tolerance=0, + expected_colors=[ + { + 'comment': 'outside', + 'location': [80, 60], + 'size': [1, 1], + 'color': [255, 255, 255], + }, + { + 'comment': 'outside', + 'location': [28, 20], + 'size': [1, 1], + 'color': [255, 255, 255], + }, + { + 'comment': 'inside', + 'location': [32, 25], + 'size': [1, 1], + 'color': [255, 215, 0], + }, + { + 'comment': 'inside', + 'location': [80, 80], + 'size': [1, 1], + 'color': [255, 215, 0], + } + ]) + ] + + # Pages that should be run with experimental canvas features. + @staticmethod + def ExperimentalCanvasFeaturesPages(base_name): + browser_args = [ + '--enable-experimental-web-platform-features'] # for lowLatency + unaccelerated_args = [ + '--disable-accelerated-2d-canvas', + '--disable-gpu-compositing'] + + return [ + PixelTestPage( + 'pixel_offscreenCanvas_transfer_after_style_resize.html', + base_name + '_OffscreenCanvasTransferAfterStyleResize', + test_rect=[0, 0, 350, 350], + revision=10, + browser_args=browser_args), + + PixelTestPage( + 'pixel_offscreenCanvas_transfer_before_style_resize.html', + base_name + '_OffscreenCanvasTransferBeforeStyleResize', + test_rect=[0, 0, 350, 350], + revision=10, + browser_args=browser_args), + + PixelTestPage( + 'pixel_offscreenCanvas_webgl_paint_after_resize.html', + base_name + '_OffscreenCanvasWebGLPaintAfterResize', + test_rect=[0, 0, 200, 200], + browser_args=browser_args, + revision=0, # Golden image revision is not used + tolerance=0, + expected_colors=[ + SCALE_FACTOR_OVERRIDES, + { + 'comment': 'resized area', + 'location': [1, 1], + 'size': [48, 98], + 'color': [0, 255, 0], + }, + { + 'comment': 'outside resized area', + 'location': [51, 1], + 'size': [48, 98], + 'color': [255, 255, 255], + }, + ]), + + PixelTestPage( + 'pixel_offscreenCanvas_transferToImageBitmap_main.html', + base_name + '_OffscreenCanvasTransferToImageBitmap', + test_rect=[0, 0, 300, 300], + revision=6, + browser_args=browser_args), + + PixelTestPage( + 'pixel_offscreenCanvas_transferToImageBitmap_worker.html', + base_name + '_OffscreenCanvasTransferToImageBitmapWorker', + test_rect=[0, 0, 300, 300], + revision=6, + browser_args=browser_args), + + PixelTestPage( + 'pixel_offscreenCanvas_webgl_commit_main.html', + base_name + '_OffscreenCanvasWebGLDefault', + test_rect=[0, 0, 360, 200], + revision=11, + browser_args=browser_args), + + PixelTestPage( + 'pixel_offscreenCanvas_webgl_commit_worker.html', + base_name + '_OffscreenCanvasWebGLDefaultWorker', + test_rect=[0, 0, 360, 200], + revision=11, + browser_args=browser_args), + + PixelTestPage( + 'pixel_offscreenCanvas_webgl_commit_main.html', + base_name + '_OffscreenCanvasWebGLSoftwareCompositing', + test_rect=[0, 0, 360, 200], + revision=7, + browser_args=browser_args + ['--disable-gpu-compositing']), + + PixelTestPage( + 'pixel_offscreenCanvas_webgl_commit_worker.html', + base_name + '_OffscreenCanvasWebGLSoftwareCompositingWorker', + test_rect=[0, 0, 360, 200], + revision=7, + browser_args=browser_args + ['--disable-gpu-compositing']), + + PixelTestPage( + 'pixel_offscreenCanvas_2d_commit_main.html', + base_name + '_OffscreenCanvasAccelerated2D', + test_rect=[0, 0, 360, 200], + revision=12, + browser_args=browser_args), + + PixelTestPage( + 'pixel_offscreenCanvas_2d_commit_worker.html', + base_name + '_OffscreenCanvasAccelerated2DWorker', + test_rect=[0, 0, 360, 200], + revision=12, + browser_args=browser_args), + + PixelTestPage( + 'pixel_offscreenCanvas_2d_commit_main.html', + base_name + '_OffscreenCanvasUnaccelerated2D', + test_rect=[0, 0, 360, 200], + revision=9, + browser_args=browser_args + unaccelerated_args), + + PixelTestPage( + 'pixel_offscreenCanvas_2d_commit_worker.html', + base_name + '_OffscreenCanvasUnaccelerated2DWorker', + test_rect=[0, 0, 360, 200], + revision=9, + browser_args=browser_args + unaccelerated_args), + + PixelTestPage( + 'pixel_offscreenCanvas_2d_commit_main.html', + base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositing', + test_rect=[0, 0, 360, 200], + revision=14, + browser_args=browser_args + ['--disable-accelerated-2d-canvas']), + + PixelTestPage( + 'pixel_offscreenCanvas_2d_commit_worker.html', + base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositingWorker', + test_rect=[0, 0, 360, 200], + revision=13, + browser_args=browser_args + ['--disable-accelerated-2d-canvas']), + + PixelTestPage( + 'pixel_offscreenCanvas_2d_resize_on_worker.html', + base_name + '_OffscreenCanvas2DResizeOnWorker', + test_rect=[0, 0, 200, 200], + revision=7, + browser_args=browser_args), + + PixelTestPage( + 'pixel_offscreenCanvas_webgl_resize_on_worker.html', + base_name + '_OffscreenCanvasWebglResizeOnWorker', + test_rect=[0, 0, 200, 200], + revision=9, + browser_args=browser_args), + + PixelTestPage( + 'pixel_canvas_display_linear-rgb.html', + base_name + '_CanvasDisplayLinearRGBAccelerated2D', + test_rect=[0, 0, 140, 140], + revision=10, + browser_args=browser_args), + + PixelTestPage( + 'pixel_canvas_display_linear-rgb.html', + base_name + '_CanvasDisplayLinearRGBUnaccelerated2D', + test_rect=[0, 0, 140, 140], + revision=2, + browser_args=browser_args + unaccelerated_args), + + PixelTestPage( + 'pixel_canvas_display_linear-rgb.html', + base_name + '_CanvasDisplayLinearRGBUnaccelerated2DGPUCompositing', + test_rect=[0, 0, 140, 140], + revision=8, + browser_args=browser_args + ['--disable-accelerated-2d-canvas']), + + PixelTestPage( + 'pixel_canvas_display_srgb.html', + base_name + '_CanvasDisplaySRGBAccelerated2D', + test_rect=[0, 0, 140, 140], + revision=0, # not used, unsupported + browser_args=browser_args), + + PixelTestPage( + 'pixel_canvas_display_srgb.html', + base_name + '_CanvasDisplaySRGBUnaccelerated2D', + test_rect=[0, 0, 140, 140], + revision=1, + browser_args=browser_args + unaccelerated_args), + + PixelTestPage( + 'pixel_canvas_display_srgb.html', + base_name + '_CanvasDisplaySRGBUnaccelerated2DGPUCompositing', + test_rect=[0, 0, 140, 140], + revision=1, + browser_args=browser_args + ['--disable-accelerated-2d-canvas']), + + PixelTestPage( + 'pixel_canvas_low_latency_2d.html', + base_name + '_CanvasLowLatency2D', + test_rect=[0, 0, 100, 100], + revision=8, + browser_args=browser_args), + + PixelTestPage( + 'pixel_canvas_low_latency_2d.html', + base_name + '_CanvasUnacceleratedLowLatency2D', + test_rect=[0, 0, 100, 100], + revision=3, + browser_args=browser_args + unaccelerated_args), + + PixelTestPage( + 'pixel_canvas_low_latency_webgl.html', + base_name + '_CanvasLowLatencyWebGL', + test_rect=[0, 0, 200, 200], + revision=0, # not used + browser_args=browser_args, + tolerance=0, + expected_colors=[ + SCALE_FACTOR_OVERRIDES, + { + 'comment': 'green', + 'location': [1, 1], + 'size': [98, 98], + 'color': [0, 255, 0], + }, + ]), + ] + + # Only add these tests on platforms where SwiftShader is enabled. + # Currently this is Windows and Linux. + @staticmethod + def SwiftShaderPages(base_name): + browser_args = ['--disable-gpu'] + suffix = "_SwiftShader" + return [ + PixelTestPage( + 'pixel_canvas2d.html', + base_name + '_Canvas2DRedBox' + suffix, + test_rect=[0, 0, 300, 300], + revision=1, + browser_args=browser_args), + + PixelTestPage( + 'pixel_css3d.html', + base_name + '_CSS3DBlueBox' + suffix, + test_rect=[0, 0, 300, 300], + revision=2, + browser_args=browser_args), + + PixelTestPage( + 'pixel_webgl_aa_alpha.html', + base_name + '_WebGLGreenTriangle_AA_Alpha' + suffix, + test_rect=[0, 0, 300, 300], + revision=2, + browser_args=browser_args), + + PixelTestPage( + 'pixel_repeated_webgl_to_2d.html', + base_name + '_RepeatedWebGLTo2D' + suffix, + test_rect=[0, 0, 256, 256], + revision=0, # Golden image revision is not used + browser_args=browser_args, + tolerance=3, + expected_colors=[ + SCALE_FACTOR_OVERRIDES, + { + 'comment': 'green', + # 64x64 rectangle around the center at (128,128) + 'location': [96, 96], + 'size': [64, 64], + 'color': [0, 255, 0], + }, + ]), + ] + + # Test rendering where GPU process is blocked. + @staticmethod + def NoGpuProcessPages(base_name): + browser_args = ['--disable-gpu', '--disable-software-rasterizer'] + suffix = "_NoGpuProcess" + return [ + PixelTestPage( + 'pixel_canvas2d.html', + base_name + '_Canvas2DRedBox' + suffix, + test_rect=[0, 0, 300, 300], + revision=2, + browser_args=browser_args, + gpu_process_disabled=True), + + PixelTestPage( + 'pixel_css3d.html', + base_name + '_CSS3DBlueBox' + suffix, + test_rect=[0, 0, 300, 300], + revision=2, + browser_args=browser_args, + gpu_process_disabled=True), + ] + + # Pages that should be run with various macOS specific command line + # arguments. + @staticmethod + def MacSpecificPages(base_name): + iosurface_2d_canvas_args = [ + '--enable-accelerated-2d-canvas'] + + non_chromium_image_args = ['--disable-webgl-image-chromium'] + + # This disables the Core Animation compositor, falling back to the + # old GLRenderer path, but continuing to allocate IOSurfaces for + # WebGL's back buffer. + no_overlays_args = ['--disable-mac-overlays'] + + return [ + # On macOS, test the IOSurface 2D Canvas compositing path. + PixelTestPage( + 'pixel_canvas2d_accelerated.html', + base_name + '_IOSurface2DCanvas', + test_rect=[0, 0, 400, 400], + revision=1, + browser_args=iosurface_2d_canvas_args), + PixelTestPage( + 'pixel_canvas2d_webgl.html', + base_name + '_IOSurface2DCanvasWebGL', + test_rect=[0, 0, 300, 300], + revision=4, + browser_args=iosurface_2d_canvas_args), + + # On macOS, test WebGL non-Chromium Image compositing path. + PixelTestPage( + 'pixel_webgl_aa_alpha.html', + base_name + '_WebGLGreenTriangle_NonChromiumImage_AA_Alpha', + test_rect=[0, 0, 300, 300], + revision=3, + browser_args=non_chromium_image_args), + PixelTestPage( + 'pixel_webgl_noaa_alpha.html', + base_name + '_WebGLGreenTriangle_NonChromiumImage_NoAA_Alpha', + test_rect=[0, 0, 300, 300], + revision=1, + browser_args=non_chromium_image_args), + PixelTestPage( + 'pixel_webgl_aa_noalpha.html', + base_name + '_WebGLGreenTriangle_NonChromiumImage_AA_NoAlpha', + test_rect=[0, 0, 300, 300], + revision=3, + browser_args=non_chromium_image_args), + PixelTestPage( + 'pixel_webgl_noaa_noalpha.html', + base_name + '_WebGLGreenTriangle_NonChromiumImage_NoAA_NoAlpha', + test_rect=[0, 0, 300, 300], + revision=1, + browser_args=non_chromium_image_args), + + # On macOS, test CSS filter effects with and without the CA compositor. + PixelTestPage( + 'filter_effects.html', + base_name + '_CSSFilterEffects', + test_rect=[0, 0, 300, 300], + revision=11), + PixelTestPage( + 'filter_effects.html', + base_name + '_CSSFilterEffects_NoOverlays', + test_rect=[0, 0, 300, 300], + revision=11, + tolerance=10, + browser_args=no_overlays_args), + + # Test WebGL's premultipliedAlpha:false without the CA compositor. + PixelTestPage( + 'pixel_webgl_premultiplied_alpha_false.html', + base_name + '_WebGL_PremultipliedAlpha_False_NoOverlays', + test_rect=[0, 0, 150, 150], + revision=0, # Golden image revision is not used + browser_args=no_overlays_args, + tolerance=3, + expected_colors=[ + SCALE_FACTOR_OVERRIDES, + { + 'comment': 'brown', + 'location': [1, 1], + 'size': [148, 148], + # This is the color on an NVIDIA based MacBook Pro if the + # sRGB profile's applied correctly. + 'color': [102, 77, 0], + # This is the color if it isn't. + # 'color': [101, 76, 12], + }, + ]), + ] + + @staticmethod + def DirectCompositionPages(base_name): + browser_args = ['--enable-direct-composition-layers'] + browser_args_Underlay = browser_args + [ + '--enable-features=DirectCompositionUnderlays'] + browser_args_Nonroot = browser_args +[ + '--enable-features=DirectCompositionNonrootOverlays,' + + 'DirectCompositionUnderlays'] + browser_args_Complex = browser_args + [ + '--enable-features=DirectCompositionComplexOverlays,' + + 'DirectCompositionNonrootOverlays,' + + 'DirectCompositionUnderlays'] + browser_args_YUY2 = browser_args + [ + '--disable-features=DirectCompositionPreferNV12Overlays'] + browser_args_DXVA = browser_args + [ + '--disable-features=D3D11VideoDecoder'] + browser_args_Underlay_DXVA = browser_args + [ + '--enable-features=DirectCompositionUnderlays', + '--disable-features=D3D11VideoDecoder'] + + tolerance_dc = 5 + + return [ + PixelTestPage( + 'pixel_video_mp4.html', + base_name + '_DirectComposition_Video_MP4', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + browser_args=browser_args, + tolerance=tolerance_dc, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + + PixelTestPage( + 'pixel_video_mp4.html', + base_name + '_DirectComposition_Video_MP4_DXVA', + browser_args=browser_args_DXVA, + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + tolerance=tolerance_dc, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + + PixelTestPage( + 'pixel_video_mp4_fullsize.html', + base_name + '_DirectComposition_Video_MP4_Fullsize', + browser_args=browser_args, + test_rect=[0, 0, 960, 540], + revision=0, # Golden image revision is not used + other_args={'zero_copy': True}, + tolerance=tolerance_dc, + expected_colors=[ + { + 'comment': 'top left video, yellow', + 'location': [10, 10], + 'size': [460, 250], + 'color': [255, 255, 15], + }, + { + 'comment': 'top right video, red', + 'location': [490, 10], + 'size': [460, 250], + 'color': [255, 17, 24], + }, + { + 'comment': 'bottom left video, blue', + 'location': [10, 280], + 'size': [460, 250], + 'color': [12, 12, 255], + }, + { + 'comment': 'bottom right video, green', + 'location': [490, 280], + 'size': [460, 250], + 'color': [44, 255, 16], + } + ]), + + PixelTestPage( + 'pixel_video_mp4.html', + base_name + '_DirectComposition_Video_MP4_YUY2', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + browser_args=browser_args_YUY2, + other_args={'expect_yuy2': True}, + tolerance=tolerance_dc, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + + PixelTestPage( + 'pixel_video_mp4_four_colors_aspect_4x3.html', + base_name + '_DirectComposition_Video_MP4_FourColors_Aspect_4x3', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + browser_args=browser_args, + tolerance=tolerance_dc, + expected_colors=[ + { + 'comment': 'outside video content, left side, white', + 'location': [1, 1], + 'size': [28, 133], + 'color': [255, 255, 255], + }, + { + 'comment': 'outside video content, right side, white', + 'location': [211, 1], + 'size': [28, 133], + 'color': [255, 255, 255], + }, + { + 'comment': 'top left video, yellow', + 'location': [35, 5], + 'size': [80, 57], + 'color': [255, 255, 15], + }, + { + 'comment': 'top right video, red', + 'location': [125, 5], + 'size': [80, 57], + 'color': [255, 17, 24], + }, + { + 'comment': 'bottom left video, blue', + 'location': [35, 73], + 'size': [80, 57], + 'color': [12, 12, 255], + }, + { + 'comment': 'bottom right video, green', + 'location': [125, 73], + 'size': [80, 57], + 'color': [44, 255, 16], + } + ]), + + PixelTestPage( + 'pixel_video_mp4_four_colors_rot_90.html', + base_name + '_DirectComposition_Video_MP4_FourColors_Rot_90', + test_rect=[0, 0, 427, 240], + revision=0, # Golden image revision is not used + browser_args=browser_args, + other_args={'video_is_rotated': True}, + tolerance=tolerance_dc, + expected_colors=[ + { + 'comment': 'outside video content, left side, white', + 'location': [1, 1], + 'size': [144, 238], + 'color': [255, 255, 255], + }, + { + 'comment': 'outside video content, right side, white', + 'location': [282, 1], + 'size': [144, 238], + 'color': [255, 255, 255], + }, + { + 'comment': 'top left video, red', + 'location': [152, 5], + 'size': [55, 110], + 'color': [255, 17, 24], + }, + { + 'comment': 'top right video, green', + 'location': [220, 5], + 'size': [55, 110], + 'color': [44, 255, 16], + }, + { + 'comment': 'bottom left video, yellow', + 'location': [152, 125], + 'size': [55, 110], + 'color': [255, 255, 15], + }, + { + 'comment': 'bottom right video, blue', + 'location': [220, 125], + 'size': [55, 110], + 'color': [12, 12, 255], + }]), + + PixelTestPage( + 'pixel_video_mp4_four_colors_rot_180.html', + base_name + '_DirectComposition_Video_MP4_FourColors_Rot_180', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + browser_args=browser_args, + other_args={'video_is_rotated': True}, + tolerance=tolerance_dc, + expected_colors=[ + { + 'comment': 'top left video, green', + 'location': [5, 5], + 'size': [110, 57], + 'color': [44, 255, 16], + }, + { + 'comment': 'top right video, blue', + 'location': [125, 5], + 'size': [110, 57], + 'color': [12, 12, 255], + }, + { + 'comment': 'bottom left video, red', + 'location': [5, 72], + 'size': [110, 57], + 'color': [255, 17, 24], + }, + { + 'comment': 'bottom right video, yellow', + 'location': [125, 72], + 'size': [110, 57], + 'color': [255, 255, 15], + }]), + + PixelTestPage( + 'pixel_video_mp4_four_colors_rot_270.html', + base_name + '_DirectComposition_Video_MP4_FourColors_Rot_270', + test_rect=[0, 0, 427, 240], + revision=0, # Golden image revision is not used + browser_args=browser_args, + other_args={'video_is_rotated': True}, + tolerance=tolerance_dc, + expected_colors=[ + { + 'comment': 'outside video content, left side, white', + 'location': [1, 1], + 'size': [144, 238], + 'color': [255, 255, 255], + }, + { + 'comment': 'outside video content, right side, white', + 'location': [282, 1], + 'size': [144, 238], + 'color': [255, 255, 255], + }, + { + 'comment': 'top left video, blue', + 'location': [152, 5], + 'size': [55, 110], + 'color': [12, 12, 255], + }, + { + 'comment': 'top right video, yellow', + 'location': [220, 5], + 'size': [55, 110], + 'color': [255, 255, 15], + }, + { + 'comment': 'bottom left video, green', + 'location': [152, 125], + 'size': [55, 110], + 'color': [44, 255, 16], + }, + { + 'comment': 'bottom right video, red', + 'location': [220, 125], + 'size': [55, 110], + 'color': [255, 17, 24], + }]), + + PixelTestPage( + 'pixel_video_vp9.html', + base_name + '_DirectComposition_Video_VP9', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + browser_args=browser_args, + tolerance=tolerance_dc, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + + PixelTestPage( + 'pixel_video_vp9.html', + base_name + '_DirectComposition_Video_VP9_DXVA', + browser_args=browser_args_DXVA, + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + tolerance=tolerance_dc, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + + PixelTestPage( + 'pixel_video_vp9_fullsize.html', + base_name + '_DirectComposition_Video_VP9_Fullsize', + test_rect=[0, 0, 960, 540], + revision=0, # Golden image revision is not used + browser_args=browser_args, + other_args={'zero_copy': True}, + tolerance=tolerance_dc, + expected_colors=[ + { + 'comment': 'top left video, yellow', + 'location': [10, 10], + 'size': [460, 250], + 'color': [255, 255, 15], + }, + { + 'comment': 'top right video, red', + 'location': [490, 10], + 'size': [460, 250], + 'color': [255, 17, 24], + }, + { + 'comment': 'bottom left video, blue', + 'location': [10, 280], + 'size': [460, 250], + 'color': [12, 12, 255], + }, + { + 'comment': 'bottom right video, green', + 'location': [490, 280], + 'size': [460, 250], + 'color': [44, 255, 16], + } + ]), + + PixelTestPage( + 'pixel_video_vp9.html', + base_name + '_DirectComposition_Video_VP9_YUY2', + test_rect=[0, 0, 240, 135], + revision=0, # Golden image revision is not used + browser_args=browser_args_YUY2, + other_args={'expect_yuy2': True}, + tolerance=tolerance_dc, + expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS), + + PixelTestPage( + 'pixel_video_underlay.html', + base_name + '_DirectComposition_Underlay', + test_rect=[0, 0, 240, 136], + revision=0, # Golden image revision is not used + browser_args=browser_args_Underlay, + tolerance=tolerance_dc, + expected_colors=[ + { + 'comment': 'black top left', + 'location': [4, 4], + 'size': [20, 20], + 'color': [0, 0, 0], + }, + { + 'comment': 'yellow top left quadrant', + 'location': [4, 34], + 'size': [110, 30], + 'color': [255, 255, 15], + }, + { + 'comment': 'red top right quadrant', + 'location': [124, 4], + 'size': [110, 60], + 'color': [255, 17, 24], + }, + { + 'comment': 'blue bottom left quadrant', + 'location': [4, 72], + 'size': [110, 60], + 'color': [12, 12, 255], + }, + { + 'comment': 'green bottom right quadrant', + 'location': [124, 72], + 'size': [110, 60], + 'color': [44, 255, 16], + } + ]), + + PixelTestPage( + 'pixel_video_underlay.html', + base_name + '_DirectComposition_Underlay_DXVA', + test_rect=[0, 0, 240, 136], + revision=0, # Golden image revision is not used + browser_args=browser_args_Underlay_DXVA, + tolerance=tolerance_dc, + expected_colors=[ + { + 'comment': 'black top left', + 'location': [4, 4], + 'size': [20, 20], + 'color': [0, 0, 0], + }, + { + 'comment': 'yellow top left quadrant', + 'location': [4, 34], + 'size': [110, 30], + 'color': [255, 255, 15], + }, + { + 'comment': 'red top right quadrant', + 'location': [124, 4], + 'size': [110, 60], + 'color': [255, 17, 24], + }, + { + 'comment': 'blue bottom left quadrant', + 'location': [4, 72], + 'size': [110, 60], + 'color': [12, 12, 255], + }, + { + 'comment': 'green bottom right quadrant', + 'location': [124, 72], + 'size': [110, 60], + 'color': [44, 255, 16], + } + ]), + + PixelTestPage( + 'pixel_video_underlay_fullsize.html', + base_name + '_DirectComposition_Underlay_Fullsize', + test_rect=[0, 0, 960, 540], + revision=0, # Golden image revision is not used + browser_args=browser_args_Underlay, + other_args={'zero_copy': True}, + tolerance=tolerance_dc, + expected_colors=[ + { + 'comment': 'black top left', + 'location': [4, 4], + 'size': [20, 20], + 'color': [0, 0, 0], + }, + { + 'comment': 'yellow top left quadrant', + 'location': [10, 35], + 'size': [460, 225], + 'color': [255, 255, 15], + }, + { + 'comment': 'red top right quadrant', + 'location': [490, 10], + 'size': [460, 250], + 'color': [255, 17, 24], + }, + { + 'comment': 'blue bottom left quadrant', + 'location': [10, 280], + 'size': [460, 250], + 'color': [12, 12, 255], + }, + { + 'comment': 'green bottom right quadrant', + 'location': [490, 290], + 'size': [460, 250], + 'color': [44, 255, 16], + } + ]), + + PixelTestPage( + 'pixel_video_nonroot.html', + base_name + '_DirectComposition_Nonroot', + test_rect=[0, 0, 240, 136], + revision=0, # Golden image revision is not used + browser_args=browser_args_Nonroot, + tolerance=tolerance_dc, + expected_colors=[ + { + 'comment': 'black top left', + 'location': [4, 4], + 'size': [20, 20], + 'color': [0, 0, 0], + }, + { + 'comment': 'yellow top left quadrant', + 'location': [4, 34], + 'size': [110, 30], + 'color': [255, 255, 15], + }, + { + 'comment': 'red top right quadrant', + 'location': [124, 4], + 'size': [50, 60], + 'color': [255, 17, 24], + }, + { + 'comment': 'blue bottom left quadrant', + 'location': [4, 72], + 'size': [110, 60], + 'color': [12, 12, 255], + }, + { + 'comment': 'green bottom right quadrant', + 'location': [124, 72], + 'size': [50, 60], + 'color': [44, 255, 16], + } + ]), + + PixelTestPage( + 'pixel_video_complex_overlays.html', + base_name + '_DirectComposition_ComplexOverlays', + test_rect=[0, 0, 240, 136], + revision=0, # Golden image revision is not used + browser_args=browser_args_Complex, + other_args={'video_is_rotated': True}, + tolerance=tolerance_dc, + expected_colors=[ + { + 'comment': 'black top left', + 'location': [4, 4], + 'size': [20, 20], + 'color': [0, 0, 0], + }, + { + 'comment': 'yellow top left quadrant', + 'location': [60, 10], + 'size': [65, 30], + 'color': [255, 255, 15], + }, + { + 'comment': 'red top right quadrant', + 'location': [150, 45], + 'size': [65, 30], + 'color': [255, 17, 24], + }, + { + 'comment': 'blue bottom left quadrant', + 'location': [30, 70], + 'size': [65, 30], + 'color': [12, 12, 255], + }, + { + 'comment': 'green bottom right quadrant', + 'location': [130, 100], + 'size': [65, 30], + 'color': [44, 255, 16], + }]), + ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt index 3235591..1c1d50c 100644 --- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -17,7 +17,6 @@ [ android ] Pixel_OffscreenCanvasWebGLSoftwareCompositing [ Skip ] [ android ] Pixel_OffscreenCanvasWebGLSoftwareCompositingWorker [ Skip ] [ android ] Pixel_CanvasDisplayLinearRGBUnaccelerated2D [ Skip ] -[ android ] Pixel_CanvasDisplaySRGBUnaccelerated2D [ Skip ] [ android ] Pixel_CanvasDisplayLinearRGBUnaccelerated2DGPUCompositing [ Skip ] [ android ] Pixel_CanvasDisplaySRGBUnaccelerated2DGPUCompositing [ Skip ] [ android ] Pixel_CanvasDisplaySRGBUnaccelerated2D [ Skip ]
diff --git a/content/test/gpu/gpu_tests/trace_integration_test.py b/content/test/gpu/gpu_tests/trace_integration_test.py index 24824658..aa1e326 100644 --- a/content/test/gpu/gpu_tests/trace_integration_test.py +++ b/content/test/gpu/gpu_tests/trace_integration_test.py
@@ -104,21 +104,22 @@ # Include the device level trace tests, even though they're # currently skipped on all platforms, to give a hint that they # should perhaps be enabled in the future. - for p in pixel_test_pages.DefaultPages('TraceTest'): + namespace = pixel_test_pages.PixelTestPages + for p in namespace.DefaultPages('TraceTest'): yield (p.name, gpu_relative_path + p.url, {'browser_args': [], 'category': cls._DisabledByDefaultTraceCategory('gpu.service'), 'test_harness_script': webgl_test_harness_script, 'finish_js_condition': 'domAutomationController._finished', 'success_eval_func': 'CheckGLCategory'}) - for p in pixel_test_pages.DefaultPages('DeviceTraceTest'): + for p in namespace.DefaultPages('DeviceTraceTest'): yield (p.name, gpu_relative_path + p.url, {'browser_args': [], 'category': cls._DisabledByDefaultTraceCategory('gpu.device'), 'test_harness_script': webgl_test_harness_script, 'finish_js_condition': 'domAutomationController._finished', 'success_eval_func': 'CheckGLCategory'}) - for p in pixel_test_pages.DirectCompositionPages('VideoPathTraceTest'): + for p in namespace.DirectCompositionPages('VideoPathTraceTest'): yield (p.name, gpu_relative_path + p.url, {'browser_args': p.browser_args, 'category': cls._DisabledByDefaultTraceCategory('gpu.service'), @@ -126,7 +127,7 @@ 'finish_js_condition': 'domAutomationController._finished', 'success_eval_func': 'CheckVideoPath', 'other_args': p.other_args}) - for p in pixel_test_pages.DirectCompositionPages('OverlayModeTraceTest'): + for p in namespace.DirectCompositionPages('OverlayModeTraceTest'): if p.other_args and p.other_args.get('video_is_rotated', False): # For all drivers we tested, when a video is rotated, frames won't # be promoted to hardware overlays.
diff --git a/content/test/mock_clipboard_host.cc b/content/test/mock_clipboard_host.cc index b794fc6..fab0d96 100644 --- a/content/test/mock_clipboard_host.cc +++ b/content/test/mock_clipboard_host.cc
@@ -98,15 +98,13 @@ : base::string16()); } -void MockClipboardHost::WriteText(ui::ClipboardType, - const base::string16& text) { +void MockClipboardHost::WriteText(const base::string16& text) { if (needs_reset_) Reset(); plain_text_ = text; } -void MockClipboardHost::WriteHtml(ui::ClipboardType, - const base::string16& markup, +void MockClipboardHost::WriteHtml(const base::string16& markup, const GURL& url) { if (needs_reset_) Reset(); @@ -114,14 +112,13 @@ url_ = url; } -void MockClipboardHost::WriteSmartPasteMarker(ui::ClipboardType) { +void MockClipboardHost::WriteSmartPasteMarker() { if (needs_reset_) Reset(); write_smart_paste_ = true; } void MockClipboardHost::WriteCustomData( - ui::ClipboardType, const base::flat_map<base::string16, base::string16>& data) { if (needs_reset_) Reset(); @@ -129,17 +126,16 @@ custom_data_[it.first] = it.second; } -void MockClipboardHost::WriteBookmark(ui::ClipboardType, - const std::string& url, +void MockClipboardHost::WriteBookmark(const std::string& url, const base::string16& title) {} -void MockClipboardHost::WriteImage(ui::ClipboardType, const SkBitmap& bitmap) { +void MockClipboardHost::WriteImage(const SkBitmap& bitmap) { if (needs_reset_) Reset(); image_ = bitmap; } -void MockClipboardHost::CommitWrite(ui::ClipboardType) { +void MockClipboardHost::CommitWrite() { ++sequence_number_; needs_reset_ = true; }
diff --git a/content/test/mock_clipboard_host.h b/content/test/mock_clipboard_host.h index 81f0ad025..da643701 100644 --- a/content/test/mock_clipboard_host.h +++ b/content/test/mock_clipboard_host.h
@@ -42,21 +42,15 @@ void ReadCustomData(ui::ClipboardType clipboard_type, const base::string16& type, ReadCustomDataCallback callback) override; - void WriteText(ui::ClipboardType clipboard_type, - const base::string16& text) override; - void WriteHtml(ui::ClipboardType clipboard_type, - const base::string16& markup, - const GURL& url) override; - void WriteSmartPasteMarker(ui::ClipboardType clipboard_type) override; + void WriteText(const base::string16& text) override; + void WriteHtml(const base::string16& markup, const GURL& url) override; + void WriteSmartPasteMarker() override; void WriteCustomData( - ui::ClipboardType clipboard_type, const base::flat_map<base::string16, base::string16>& data) override; - void WriteBookmark(ui::ClipboardType clipboard_type, - const std::string& url, + void WriteBookmark(const std::string& url, const base::string16& title) override; - void WriteImage(ui::ClipboardType clipboard_type, - const SkBitmap& bitmap) override; - void CommitWrite(ui::ClipboardType clipboard_type) override; + void WriteImage(const SkBitmap& bitmap) override; + void CommitWrite() override; #if defined(OS_MACOSX) void WriteStringToFindPboard(const base::string16& text) override; #endif
diff --git a/fuchsia/base/test_navigation_listener.cc b/fuchsia/base/test_navigation_listener.cc index 1cad513..d86f1a36 100644 --- a/fuchsia/base/test_navigation_listener.cc +++ b/fuchsia/base/test_navigation_listener.cc
@@ -59,10 +59,23 @@ void TestNavigationListener::RunUntilUrlAndTitleEquals( const GURL& expected_url, - const std::string& expected_title) { + const base::StringPiece expected_title) { fuchsia::web::NavigationState state; state.set_url(expected_url.spec()); - state.set_title(expected_title); + state.set_title(expected_title.as_string()); + RunUntilNavigationStateMatches(state); +} + +void TestNavigationListener::RunUntilUrlTitleBackForwardEquals( + const GURL& expected_url, + base::StringPiece expected_title, + bool expected_can_go_back, + bool expected_can_go_forward) { + fuchsia::web::NavigationState state; + state.set_url(expected_url.spec()); + state.set_title(expected_title.as_string()); + state.set_can_go_back(expected_can_go_back); + state.set_can_go_forward(expected_can_go_forward); RunUntilNavigationStateMatches(state); } @@ -76,10 +89,10 @@ current_state_.set_url(change.url()); if (change.has_title()) current_state_.set_title(change.title()); - if (change.has_can_go_forward()) - current_state_.set_can_go_forward(change.can_go_forward()); if (change.has_can_go_back()) current_state_.set_can_go_back(change.can_go_back()); + if (change.has_can_go_forward()) + current_state_.set_can_go_forward(change.can_go_forward()); // Signal readiness for the next navigation event. before_ack_.Run(change, std::move(callback)); @@ -105,18 +118,18 @@ all_equal = false; } } - if (expected.has_can_go_forward()) { - if (!current_state_.has_can_go_forward() || - expected.can_go_forward() != current_state_.can_go_forward()) { - all_equal = false; - } - } if (expected.has_can_go_back()) { if (!current_state_.has_can_go_back() || expected.can_go_back() != current_state_.can_go_back()) { all_equal = false; } } + if (expected.has_can_go_forward()) { + if (!current_state_.has_can_go_forward() || + expected.can_go_forward() != current_state_.can_go_forward()) { + all_equal = false; + } + } return all_equal; }
diff --git a/fuchsia/base/test_navigation_listener.h b/fuchsia/base/test_navigation_listener.h index e4414a9..9087f3d5 100644 --- a/fuchsia/base/test_navigation_listener.h +++ b/fuchsia/base/test_navigation_listener.h
@@ -37,7 +37,14 @@ // Calls RunUntilNavigationStateMatches with a NagivationState that has // |expected_url| and |expected_title|. void RunUntilUrlAndTitleEquals(const GURL& expected_url, - const std::string& expected_title); + base::StringPiece expected_title); + + // Calls RunUntilNavigationStateMatches with a NagivationState that has + // all the expected fields. + void RunUntilUrlTitleBackForwardEquals(const GURL& expected_url, + base::StringPiece expected_title, + bool expected_can_go_back, + bool expected_can_go_forward); // Register a callback which intercepts the execution of the event // acknowledgement callback. |before_ack| takes ownership of the
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn index b2cf15dd..f5cb6e0 100644 --- a/fuchsia/engine/BUILD.gn +++ b/fuchsia/engine/BUILD.gn
@@ -90,6 +90,9 @@ data_deps = [ ":web_engine_pak", ] + public_deps = [ + "//content/public/browser", + ] data = [ "$root_out_dir/web_engine.pak", ] @@ -214,6 +217,7 @@ test("web_engine_unittests") { sources = [ + "browser/frame_impl_unittest.cc", "context_provider_impl_unittest.cc", "fake_context.cc", "fake_context.h",
diff --git a/fuchsia/engine/browser/frame_impl.cc b/fuchsia/engine/browser/frame_impl.cc index e94f426..842a69a7 100644 --- a/fuchsia/engine/browser/frame_impl.cc +++ b/fuchsia/engine/browser/frame_impl.cc
@@ -78,7 +78,8 @@ }; fuchsia::web::NavigationState ConvertContentNavigationEntry( - content::NavigationEntry* entry) { + content::NavigationEntry* entry, + content::WebContents* web_contents) { DCHECK(entry); fuchsia::web::NavigationState converted; @@ -94,41 +95,13 @@ converted.set_page_type(fuchsia::web::PageType::ERROR); break; } + + converted.set_can_go_back(web_contents->GetController().CanGoBack()); + converted.set_can_go_forward(web_contents->GetController().CanGoForward()); + return converted; } -// Computes the observable differences between |old_entry| and |new_entry|. -// Returns true if they are different, |false| if their observable fields are -// identical. -bool DiffNavigationEntries(const fuchsia::web::NavigationState& old_entry, - const fuchsia::web::NavigationState& new_entry, - fuchsia::web::NavigationState* difference) { - DCHECK(difference); - - bool is_changed = false; - - DCHECK(new_entry.has_title()); - if (!old_entry.has_title() || (new_entry.title() != old_entry.title())) { - is_changed = true; - difference->set_title(new_entry.title()); - } - - DCHECK(new_entry.has_url()); - if (!old_entry.has_url() || (new_entry.url() != old_entry.url())) { - is_changed = true; - difference->set_url(new_entry.url()); - } - - DCHECK(new_entry.has_page_type()); - if (!old_entry.has_page_type() || - (new_entry.page_type() != old_entry.page_type())) { - is_changed = true; - difference->set_page_type(new_entry.page_type()); - } - - return is_changed; -} - class FrameFocusRules : public wm::BaseFocusRules { public: FrameFocusRules() = default; @@ -496,7 +469,7 @@ void FrameImpl::OnNavigationEntryChanged(content::NavigationEntry* entry) { fuchsia::web::NavigationState entry_converted = - ConvertContentNavigationEntry(entry); + ConvertContentNavigationEntry(entry, web_contents_.get()); pending_navigation_event_is_dirty_ |= DiffNavigationEntries( cached_navigation_state_, entry_converted, &pending_navigation_event_); cached_navigation_state_ = std::move(entry_converted); @@ -609,7 +582,7 @@ return; } - callback(ConvertContentNavigationEntry(entry)); + callback(ConvertContentNavigationEntry(entry, web_contents_.get())); } bool FrameImpl::ShouldCreateWebContents( @@ -712,3 +685,46 @@ void FrameImpl::TitleWasSet(content::NavigationEntry* entry) { OnNavigationEntryChanged(entry); } + +bool DiffNavigationEntries(const fuchsia::web::NavigationState& old_entry, + const fuchsia::web::NavigationState& new_entry, + fuchsia::web::NavigationState* difference) { + DCHECK(difference); + + bool is_changed = false; + + DCHECK(new_entry.has_title()); + if (!old_entry.has_title() || (new_entry.title() != old_entry.title())) { + is_changed = true; + difference->set_title(new_entry.title()); + } + + DCHECK(new_entry.has_url()); + if (!old_entry.has_url() || (new_entry.url() != old_entry.url())) { + is_changed = true; + difference->set_url(new_entry.url()); + } + + DCHECK(new_entry.has_page_type()); + if (!old_entry.has_page_type() || + (new_entry.page_type() != old_entry.page_type())) { + is_changed = true; + difference->set_page_type(new_entry.page_type()); + } + + DCHECK(new_entry.has_can_go_back()); + if (!old_entry.has_can_go_back() || + old_entry.can_go_back() != new_entry.can_go_back()) { + is_changed = true; + difference->set_can_go_back(new_entry.can_go_back()); + } + + DCHECK(new_entry.has_can_go_forward()); + if (!old_entry.has_can_go_forward() || + old_entry.can_go_forward() != new_entry.can_go_forward()) { + is_changed = true; + difference->set_can_go_forward(new_entry.can_go_forward()); + } + + return is_changed; +}
diff --git a/fuchsia/engine/browser/frame_impl.h b/fuchsia/engine/browser/frame_impl.h index 5d257e2..85e3f4a 100644 --- a/fuchsia/engine/browser/frame_impl.h +++ b/fuchsia/engine/browser/frame_impl.h
@@ -180,4 +180,10 @@ DISALLOW_COPY_AND_ASSIGN(FrameImpl); }; +// Computes the observable differences between |old_entry| and |new_entry|. +// Returns true if they are different, |false| if their observable fields are +// identical. +bool DiffNavigationEntries(const fuchsia::web::NavigationState& old_entry, + const fuchsia::web::NavigationState& new_entry, + fuchsia::web::NavigationState* difference); #endif // FUCHSIA_ENGINE_BROWSER_FRAME_IMPL_H_
diff --git a/fuchsia/engine/browser/frame_impl_browsertest.cc b/fuchsia/engine/browser/frame_impl_browsertest.cc index 41139ae..d75f0a7 100644 --- a/fuchsia/engine/browser/frame_impl_browsertest.cc +++ b/fuchsia/engine/browser/frame_impl_browsertest.cc
@@ -119,6 +119,21 @@ DISALLOW_COPY_AND_ASSIGN(FrameImplTest); }; +void VerifyCanGoBackAndForward(fuchsia::web::NavigationController* controller, + bool can_go_back_expected, + bool can_go_forward_expected) { + base::RunLoop run_loop; + cr_fuchsia::ResultReceiver<fuchsia::web::NavigationState> visible_entry( + run_loop.QuitClosure()); + controller->GetVisibleEntry( + cr_fuchsia::CallbackToFitFunction(visible_entry.GetReceiveCallback())); + run_loop.Run(); + EXPECT_TRUE(visible_entry->has_can_go_back()); + EXPECT_EQ(visible_entry->can_go_back(), can_go_back_expected); + EXPECT_TRUE(visible_entry->has_can_go_forward()); + EXPECT_EQ(visible_entry->can_go_forward(), can_go_forward_expected); +} + // Verifies that the browser will navigate and generate a navigation listener // event when LoadUrl() is called. IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigateFrame) { @@ -229,25 +244,33 @@ EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( &controller, fuchsia::web::LoadUrlParams(), title1.spec())); - navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title); + navigation_listener_.RunUntilUrlTitleBackForwardEquals(title1, kPage1Title, + false, false); EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( &controller, fuchsia::web::LoadUrlParams(), title2.spec())); - navigation_listener_.RunUntilUrlAndTitleEquals(title2, kPage2Title); + navigation_listener_.RunUntilUrlTitleBackForwardEquals(title2, kPage2Title, + true, false); + VerifyCanGoBackAndForward(controller.get(), true, false); controller->GoBack(); - navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title); + navigation_listener_.RunUntilUrlTitleBackForwardEquals(title1, kPage1Title, + false, true); // At the top of the navigation entry list; this should be a no-op. + VerifyCanGoBackAndForward(controller.get(), false, true); controller->GoBack(); // Process the navigation request message. base::RunLoop().RunUntilIdle(); + VerifyCanGoBackAndForward(controller.get(), false, true); controller->GoForward(); - navigation_listener_.RunUntilUrlAndTitleEquals(title2, kPage2Title); + navigation_listener_.RunUntilUrlTitleBackForwardEquals(title2, kPage2Title, + true, false); // At the end of the navigation entry list; this should be a no-op. + VerifyCanGoBackAndForward(controller.get(), true, false); controller->GoForward(); // Process the navigation request message.
diff --git a/fuchsia/engine/browser/frame_impl_unittest.cc b/fuchsia/engine/browser/frame_impl_unittest.cc new file mode 100644 index 0000000..a8a4098 --- /dev/null +++ b/fuchsia/engine/browser/frame_impl_unittest.cc
@@ -0,0 +1,78 @@ +// 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 "fuchsia/engine/browser/frame_impl.h" +#include "testing/gtest/include/gtest/gtest.h" + +using NavigationState = fuchsia::web::NavigationState; + +namespace { + +const char kUrl1[] = "http://www.url1.com/"; +const char kUrl2[] = "http://www.url2.com/"; +const char kTitle1[] = "title1"; +const char kTitle2[] = "title2"; + +NavigationState CreateNavigationState(const GURL& url, + base::StringPiece title, + fuchsia::web::PageType page_type, + bool can_go_back, + bool can_go_forward) { + NavigationState navigation_state; + + navigation_state.set_url(url.spec()); + navigation_state.set_title(title.as_string()); + navigation_state.set_page_type(fuchsia::web::PageType(page_type)); + navigation_state.set_can_go_back(can_go_back); + navigation_state.set_can_go_forward(can_go_forward); + + return navigation_state; +} + +} // namespace + +// Verifies that two NavigationStates that are the same are differenced +// correctly. +TEST(FrameImplUnitTest, DiffNavigationEntriesNoChange) { + fuchsia::web::NavigationState difference; + NavigationState state = CreateNavigationState( + GURL(kUrl1), kTitle1, fuchsia::web::PageType::NORMAL, true, true); + + EXPECT_FALSE(DiffNavigationEntries(state, state, &difference)); +} + +// Verifies that states with different URL and title are correctly checked. +TEST(FrameImplUnitTest, DiffNavigationEntriesTitleUrl) { + fuchsia::web::NavigationState difference; + NavigationState state1 = CreateNavigationState( + GURL(kUrl1), kTitle1, fuchsia::web::PageType::NORMAL, true, true); + NavigationState state2 = CreateNavigationState( + GURL(kUrl2), kTitle2, fuchsia::web::PageType::NORMAL, true, true); + + bool is_changed = DiffNavigationEntries(state1, state2, &difference); + + EXPECT_TRUE(is_changed); + EXPECT_TRUE(difference.has_title()); + EXPECT_EQ(difference.title(), kTitle2); + EXPECT_TRUE(difference.has_url()); + EXPECT_EQ(difference.url(), kUrl2); +} + +// Verifies that states with different can_go_back and can_go_forward are +// correctly checked. +TEST(FrameImplUnitTest, DiffNavigationEntriesGoBackAndForward) { + fuchsia::web::NavigationState difference; + NavigationState state1 = CreateNavigationState( + GURL(kUrl1), kTitle1, fuchsia::web::PageType::NORMAL, true, false); + NavigationState state2 = CreateNavigationState( + GURL(kUrl1), kTitle1, fuchsia::web::PageType::NORMAL, false, true); + + bool is_changed = DiffNavigationEntries(state1, state2, &difference); + + EXPECT_TRUE(difference.has_can_go_back()); + EXPECT_TRUE(difference.has_can_go_back()); + EXPECT_TRUE(is_changed); + EXPECT_TRUE(difference.can_go_forward()); + EXPECT_FALSE(difference.can_go_back()); +}
diff --git a/media/capture/video/chromeos/request_manager.cc b/media/capture/video/chromeos/request_manager.cc index 35f1ff1..3f3331b 100644 --- a/media/capture/video/chromeos/request_manager.cc +++ b/media/capture/video/chromeos/request_manager.cc
@@ -700,8 +700,8 @@ processing_buffer_ids_.erase(*pending_result.input_buffer_id); // If all reprocess tasks are done for this buffer, release the buffer. - if (base::ContainsKey(buffer_id_reprocess_tasks_map_, - *pending_result.input_buffer_id)) { + if (!base::ContainsKey(buffer_id_reprocess_tasks_map_, + *pending_result.input_buffer_id)) { stream_buffer_manager_->ReleaseBuffer( StreamType::kYUVOutput, *pending_result.input_buffer_id); }
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h index 2e9fd58..550b310 100644 --- a/media/gpu/v4l2/v4l2_device.h +++ b/media/gpu/v4l2/v4l2_device.h
@@ -302,8 +302,6 @@ // Callback to call in this queue's destructor. base::OnceClosure destroy_cb_; - base::WeakPtrFactory<V4L2Queue> weak_this_factory_; - V4L2Queue(scoped_refptr<V4L2Device> dev, enum v4l2_buf_type type, base::OnceClosure destroy_cb); @@ -312,6 +310,9 @@ friend class base::RefCountedThreadSafe<V4L2Queue>; SEQUENCE_CHECKER(sequence_checker_); + + base::WeakPtrFactory<V4L2Queue> weak_this_factory_; + DISALLOW_COPY_AND_ASSIGN(V4L2Queue); };
diff --git a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc index 74a2c58..0298556 100644 --- a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc +++ b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
@@ -1034,6 +1034,15 @@ base::Unretained(this), base::Passed(&job_record))); } +void V4L2JpegEncodeAccelerator::EncodeWithDmaBuf( + scoped_refptr<VideoFrame> input_frame, + scoped_refptr<VideoFrame> output_frame, + int quality, + int32_t buffer_id, + const BitstreamBuffer* exif_buffer) { + NOTIMPLEMENTED(); +} + void V4L2JpegEncodeAccelerator::EncodeTask( std::unique_ptr<JobRecord> job_record) { DCHECK(encoder_task_runner_->BelongsToCurrentThread());
diff --git a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h index 48b9d1f..a380330 100644 --- a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h +++ b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h
@@ -60,6 +60,12 @@ const BitstreamBuffer* exif_buffer, const BitstreamBuffer& output_buffer) override; + void EncodeWithDmaBuf(scoped_refptr<VideoFrame> input_frame, + scoped_refptr<VideoFrame> output_frame, + int quality, + int32_t buffer_id, + const BitstreamBuffer* exif_buffer) override; + private: // Record for input buffers. struct I420BufferRecord {
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc index 046b9348..26a6060 100644 --- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc +++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
@@ -337,4 +337,13 @@ base::Unretained(encoder_.get()), std::move(request))); } +void VaapiJpegEncodeAccelerator::EncodeWithDmaBuf( + scoped_refptr<VideoFrame> input_frame, + scoped_refptr<VideoFrame> output_frame, + int quality, + int32_t buffer_id, + const BitstreamBuffer* exif_buffer) { + NOTIMPLEMENTED(); +} + } // namespace media
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h index 315b439f..0f94d30 100644 --- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h +++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h
@@ -44,6 +44,12 @@ const BitstreamBuffer* exif_buffer, const BitstreamBuffer& output_buffer) override; + void EncodeWithDmaBuf(scoped_refptr<VideoFrame> input_frame, + scoped_refptr<VideoFrame> output_frame, + int quality, + int32_t buffer_id, + const BitstreamBuffer* exif_buffer) override; + private: // An input video frame and the corresponding output buffer awaiting // consumption, provided by the client.
diff --git a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.cc b/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.cc index b6b3c8b..9574313 100644 --- a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.cc +++ b/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.cc
@@ -4,7 +4,9 @@ #include "media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h" +#include <linux/videodev2.h> #include <stdint.h> +#include <sys/mman.h> #include <memory> #include <utility> @@ -20,17 +22,73 @@ #include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/system/platform_handle.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/linux/native_pixmap_dmabuf.h" + +namespace media { namespace { -#if defined(OS_CHROMEOS) const int kJpegQuality = 90; -#endif + +scoped_refptr<media::VideoFrame> ConstructVideoFrame( + std::vector<chromeos_camera::mojom::DmaBufPlanePtr> dma_buf_planes, + VideoPixelFormat pixel_format, + int32_t width, + int32_t height) { + size_t num_planes = media::VideoFrame::NumPlanes(pixel_format); + if (num_planes != dma_buf_planes.size()) { + DLOG(ERROR) << "The amount of DMA buf planes does not match the format."; + return nullptr; + } + if (width <= 0 || height <= 0) { + DLOG(ERROR) << "Width and height should > 0: " << width << ", " << height; + return nullptr; + } + gfx::Size coded_size(width, height); + gfx::Rect visible_rect(coded_size); + + std::vector<base::ScopedFD> dma_buf_fds(num_planes); + std::vector<size_t> buffer_sizes(num_planes); + std::vector<VideoFrameLayout::Plane> planes(num_planes); + + for (size_t i = 0; i < num_planes; ++i) { + dma_buf_fds[i] = + mojo::UnwrapPlatformHandle(std::move(dma_buf_planes[i]->fd_handle)) + .TakeFD(); + planes[i].stride = dma_buf_planes[i]->stride; + planes[i].offset = dma_buf_planes[i]->offset; + buffer_sizes[i] = dma_buf_planes[i]->size; + } + auto layout = VideoFrameLayout::CreateWithPlanes( + pixel_format, coded_size, std::move(planes), std::move(buffer_sizes)); + + return VideoFrame::WrapExternalDmabufs(*layout, // layout + visible_rect, // visible_rect + coded_size, // natural_size + std::move(dma_buf_fds), // dmabuf_fds + base::TimeDelta()); // timestamp +} + +VideoPixelFormat ToVideoPixelFormat(uint32_t fourcc_fmt) { + switch (fourcc_fmt) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12M: + return PIXEL_FORMAT_NV12; + + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YUV420M: + return PIXEL_FORMAT_I420; + + case V4L2_PIX_FMT_RGB32: + return PIXEL_FORMAT_ARGB; + + default: + return PIXEL_FORMAT_UNKNOWN; + } +} } // namespace -namespace media { - // static void CrOSMojoJpegEncodeAcceleratorService::Create( chromeos_camera::mojom::JpegEncodeAcceleratorRequest request) { @@ -101,7 +159,6 @@ mojo::ScopedHandle output_handle, uint32_t output_buffer_size, EncodeWithFDCallback callback) { -#if defined(OS_CHROMEOS) DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); base::PlatformFile input_fd; base::PlatformFile exif_fd; @@ -158,7 +215,14 @@ mojo::ReportBadMessage("buffer_id is already registered in encode_cb_map_"); return; } - encode_cb_map_.emplace(buffer_id, std::move(callback)); + auto wrapped_callback = base::BindOnce( + [](int32_t buffer_id, EncodeWithFDCallback callback, + uint32_t encoded_picture_size, + media::JpegEncodeAccelerator::Status error) { + std::move(callback).Run(buffer_id, encoded_picture_size, error); + }, + buffer_id, std::move(callback)); + encode_cb_map_.emplace(buffer_id, std::move(wrapped_callback)); auto input_shm = std::make_unique<base::SharedMemory>(input_shm_handle, true); if (!input_shm->Map(input_buffer_size)) { @@ -193,9 +257,66 @@ DCHECK(accelerator_); accelerator_->Encode(frame, kJpegQuality, exif_buffer.get(), output_buffer); -#else - NOTREACHED(); -#endif +} + +void CrOSMojoJpegEncodeAcceleratorService::EncodeWithDmaBuf( + int32_t buffer_id, + uint32_t input_format, + std::vector<chromeos_camera::mojom::DmaBufPlanePtr> input_planes, + std::vector<chromeos_camera::mojom::DmaBufPlanePtr> output_planes, + mojo::ScopedHandle exif_handle, + uint32_t exif_buffer_size, + int32_t coded_size_width, + int32_t coded_size_height, + EncodeWithDmaBufCallback callback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (coded_size_width <= 0 || coded_size_height <= 0) { + std::move(callback).Run( + 0, ::media::JpegEncodeAccelerator::Status::INVALID_ARGUMENT); + return; + } + if (encode_cb_map_.find(buffer_id) != encode_cb_map_.end()) { + mojo::ReportBadMessage("buffer_id is already registered in encode_cb_map_"); + return; + } + + base::PlatformFile exif_fd; + auto result = mojo::UnwrapPlatformFile(std::move(exif_handle), &exif_fd); + if (result != MOJO_RESULT_OK) { + std::move(callback).Run( + 0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE); + return; + } + + auto input_video_frame = ConstructVideoFrame( + std::move(input_planes), ToVideoPixelFormat(input_format), + coded_size_width, coded_size_height); + if (!input_video_frame) { + std::move(callback).Run( + 0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE); + return; + } + auto output_video_frame = + ConstructVideoFrame(std::move(output_planes), PIXEL_FORMAT_MJPEG, + coded_size_width, coded_size_height); + if (!output_video_frame) { + std::move(callback).Run( + 0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE); + return; + } + base::UnguessableToken exif_guid = base::UnguessableToken::Create(); + base::SharedMemoryHandle exif_shm_handle(base::FileDescriptor(exif_fd, true), + 0u, exif_guid); + std::unique_ptr<media::BitstreamBuffer> exif_buffer; + if (exif_buffer_size > 0) { + exif_buffer = std::make_unique<media::BitstreamBuffer>( + buffer_id, exif_shm_handle, exif_buffer_size); + } + encode_cb_map_.emplace(buffer_id, std::move(callback)); + + DCHECK(accelerator_); + accelerator_->EncodeWithDmaBuf(input_video_frame, output_video_frame, + kJpegQuality, buffer_id, exif_buffer.get()); } void CrOSMojoJpegEncodeAcceleratorService::NotifyEncodeStatus( @@ -206,9 +327,9 @@ auto iter = encode_cb_map_.find(bitstream_buffer_id); DCHECK(iter != encode_cb_map_.end()); - EncodeWithFDCallback encode_cb = std::move(iter->second); + EncodeWithDmaBufCallback encode_cb = std::move(iter->second); encode_cb_map_.erase(iter); - std::move(encode_cb).Run(bitstream_buffer_id, encoded_picture_size, error); + std::move(encode_cb).Run(encoded_picture_size, error); } } // namespace media
diff --git a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h b/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h index 049eb39..4374eaa 100644 --- a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h +++ b/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h
@@ -36,7 +36,8 @@ ::media::JpegEncodeAccelerator::Status status) override; private: - using EncodeCallbackMap = std::unordered_map<int32_t, EncodeWithFDCallback>; + using EncodeCallbackMap = + std::unordered_map<int32_t, EncodeWithDmaBufCallback>; // This constructor internally calls // GpuJpegEncodeAcceleratorFactory::GetAcceleratorFactories() to @@ -45,6 +46,8 @@ // chromeos_camera::mojom::JpegEncodeAccelerator implementation. void Initialize(InitializeCallback callback) override; + + // TODO(wtlee): To be deprecated. (crbug.com/944705) void EncodeWithFD(int32_t buffer_id, mojo::ScopedHandle input_fd, uint32_t input_buffer_size, @@ -56,6 +59,17 @@ uint32_t output_buffer_size, EncodeWithFDCallback callback) override; + void EncodeWithDmaBuf( + int32_t buffer_id, + uint32_t input_format, + std::vector<chromeos_camera::mojom::DmaBufPlanePtr> input_planes, + std::vector<chromeos_camera::mojom::DmaBufPlanePtr> output_planes, + mojo::ScopedHandle exif_handle, + uint32_t exif_buffer_size, + int32_t coded_size_width, + int32_t coded_size_height, + EncodeWithDmaBufCallback callback) override; + void NotifyEncodeStatus(int32_t bitstream_buffer_id, size_t encoded_picture_size, ::media::JpegEncodeAccelerator::Status status);
diff --git a/media/video/jpeg_encode_accelerator.h b/media/video/jpeg_encode_accelerator.h index 6653a03..9c2b289 100644 --- a/media/video/jpeg_encode_accelerator.h +++ b/media/video/jpeg_encode_accelerator.h
@@ -104,6 +104,23 @@ int quality, const BitstreamBuffer* exif_buffer, const BitstreamBuffer& output_buffer) = 0; + + // Encodes the given |video_frame| that contains a YUV image. Client will + // receive the encoded result in Client::VideoFrameReady() callback with the + // corresponding |output_buffer.id()|, or receive + // Client::NotifyError() callback. + // Parameters: + // |input_frame| contains the YUV image to be encoded. + // |output_frame| is used to represent the output Dma-buf layout. + // |quality| of JPEG image. The range is from 1~100. High value means high + // quality. + // |exif_buffer| contains Exif data to be inserted into JPEG image. If it's + // nullptr, the JFIF APP0 segment will be inserted. + virtual void EncodeWithDmaBuf(scoped_refptr<VideoFrame> input_frame, + scoped_refptr<VideoFrame> output_frame, + int quality, + int32_t buffer_id, + const BitstreamBuffer* exif_buffer) = 0; }; } // namespace media
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc index 099162e..a9e82c7 100644 --- a/net/quic/quic_chromium_client_session.cc +++ b/net/quic/quic_chromium_client_session.cc
@@ -101,6 +101,38 @@ NUM_LOCATIONS); } +void RecordConnectionCloseErrorCode(quic::QuicErrorCode error, + quic::ConnectionCloseSource source, + const std::string& hostname, + bool handshake_confirmed) { + bool is_google_host = HasGoogleHost(GURL("https://" + hostname)); + std::string histogram = "Net.QuicSession.ConnectionCloseErrorCode"; + + if (source == quic::ConnectionCloseSource::FROM_PEER) { + histogram += "Server"; + } else { + histogram += "Client"; + } + base::UmaHistogramSparse(histogram, error); + + if (handshake_confirmed) { + base::UmaHistogramSparse(histogram + ".HandshakeConfirmed", error); + } else { + base::UmaHistogramSparse(histogram + ".HandshakeNotConfirmed", error); + } + + if (is_google_host) { + histogram += "Google"; + base::UmaHistogramSparse(histogram, error); + + if (handshake_confirmed) { + base::UmaHistogramSparse(histogram + ".HandshakeConfirmed", error); + } else { + base::UmaHistogramSparse(histogram + ".HandshakeNotConfirmed", error); + } + } +} + NetLogParametersCallback NetLogQuicConnectionMigrationTriggerCallback( const char* trigger) { return NetLog::StringCallback("trigger", trigger); @@ -1556,7 +1588,9 @@ quic::ConnectionCloseSource source) { DCHECK(!connection()->connected()); logger_->OnConnectionClosed(error, error_details, source); - bool is_google_host = HasGoogleHost(GURL("https://" + session_key_.host())); + + RecordConnectionCloseErrorCode(error, source, session_key_.host(), + IsCryptoHandshakeConfirmed()); if (source == quic::ConnectionCloseSource::FROM_PEER) { if (error == quic::QUIC_PUBLIC_RESET) { // is_from_google_server will be true if the received EPID is @@ -1581,15 +1615,6 @@ } } if (IsCryptoHandshakeConfirmed()) { - if (is_google_host) { - base::UmaHistogramSparse( - "Net.QuicSession.ConnectionCloseErrorCodeServerGoogle." - "HandshakeConfirmed", - error); - } - base::UmaHistogramSparse( - "Net.QuicSession.ConnectionCloseErrorCodeServer.HandshakeConfirmed", - error); base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( "Net.QuicSession.StreamCloseErrorCodeServer.HandshakeConfirmed", base::HistogramBase::kUmaTargetedHistogramFlag); @@ -1597,23 +1622,8 @@ if (num_streams > 0) histogram->AddCount(error, num_streams); } - if (is_google_host) { - base::UmaHistogramSparse( - "Net.QuicSession.ConnectionCloseErrorCodeServerGoogle", error); - } - base::UmaHistogramSparse("Net.QuicSession.ConnectionCloseErrorCodeServer", - error); } else { if (IsCryptoHandshakeConfirmed()) { - if (is_google_host) { - base::UmaHistogramSparse( - "Net.QuicSession.ConnectionCloseErrorCodeClientGoogle." - "HandshakeConfirmed", - error); - } - base::UmaHistogramSparse( - "Net.QuicSession.ConnectionCloseErrorCodeClient.HandshakeConfirmed", - error); base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( "Net.QuicSession.StreamCloseErrorCodeClient.HandshakeConfirmed", base::HistogramBase::kUmaTargetedHistogramFlag); @@ -1627,12 +1637,6 @@ connection()->IsPathDegrading()); } } - if (is_google_host) { - base::UmaHistogramSparse( - "Net.QuicSession.ConnectionCloseErrorCodeClientGoogle", error); - } - base::UmaHistogramSparse("Net.QuicSession.ConnectionCloseErrorCodeClient", - error); if (error == quic::QUIC_TOO_MANY_RTOS) { UMA_HISTOGRAM_COUNTS_1000( "Net.QuicSession.ClosedByRtoAtClient.ReceivedPacketCount",
diff --git a/services/device/geolocation/network_location_request.cc b/services/device/geolocation/network_location_request.cc index 4497a19e..3b0c65d 100644 --- a/services/device/geolocation/network_location_request.cc +++ b/services/device/geolocation/network_location_request.cc
@@ -277,8 +277,10 @@ auto wifi_access_point_list = std::make_unique<base::ListValue>(); for (auto* ap_data : access_points_by_signal_strength) { auto wifi_dict = std::make_unique<base::DictionaryValue>(); - AddString("macAddress", base::UTF16ToUTF8(ap_data->mac_address), - wifi_dict.get()); + auto macAddress = base::UTF16ToUTF8(ap_data->mac_address); + if (macAddress.empty()) + continue; + AddString("macAddress", macAddress, wifi_dict.get()); AddInteger("signalStrength", ap_data->radio_signal_strength, wifi_dict.get()); AddInteger("age", age_milliseconds, wifi_dict.get());
diff --git a/services/service_manager/sandbox/mac/common.sb b/services/service_manager/sandbox/mac/common.sb index c0db1ebd..f786184 100644 --- a/services/service_manager/sandbox/mac/common.sb +++ b/services/service_manager/sandbox/mac/common.sb
@@ -182,7 +182,6 @@ (sysctl-name "hw.cachelinesize_compat") (sysctl-name "hw.cpufrequency_compat") (sysctl-name "hw.cputype") - (sysctl-name "hw.logicalcpu_max") (sysctl-name "hw.machine") (sysctl-name "hw.ncpu") (sysctl-name "hw.pagesize_compat") @@ -193,7 +192,6 @@ (sysctl-name "kern.maxfilesperproc") (sysctl-name "kern.osrelease") (sysctl-name "kern.ostype") - (sysctl-name "kern.osvariant_status") (sysctl-name "kern.osversion") (sysctl-name "kern.usrstack64") (sysctl-name "kern.version")
diff --git a/services/service_manager/sandbox/mac/renderer.sb b/services/service_manager/sandbox/mac/renderer.sb index c54fd4c..6e1426d 100644 --- a/services/service_manager/sandbox/mac/renderer.sb +++ b/services/service_manager/sandbox/mac/renderer.sb
@@ -4,17 +4,9 @@ ; --- The contents of common.sb implicitly included here. --- -; Put the denials first. -; crbug.com/799149: These operations are allowed by default. -(deny iokit-get-properties process-info* nvram*) - ; Allow cf prefs to work. (allow user-preference-read) -; process-info -(allow process-info-pidinfo) -(allow process-info-setcontrol (target self)) - ; File reads. ; Reads from the home directory. (allow file-read-data @@ -75,15 +67,3 @@ (global-name "com.apple.lsd.mapdb") (global-name "com.apple.system.notification_center") ; https://crbug.com/792217 ) - -; IOKit properties. -(allow iokit-get-properties - (iokit-property "CaseSensitive") - (iokit-property "Ejectable") - (iokit-property "Encrypted") - (iokit-property "IOClassNameOverride") - (iokit-property "IOMediaIcon") - (iokit-property "Protocol Characteristics") - (iokit-property "Removable") - (iokit-property "image-encrypted") -)
diff --git a/storage/browser/fileapi/obfuscated_file_util.cc b/storage/browser/fileapi/obfuscated_file_util.cc index be96319f..949377c 100644 --- a/storage/browser/fileapi/obfuscated_file_util.cc +++ b/storage/browser/fileapi/obfuscated_file_util.cc
@@ -213,9 +213,12 @@ : public ObfuscatedFileUtil::AbstractOriginEnumerator { public: using OriginRecord = SandboxOriginDatabase::OriginRecord; - ObfuscatedOriginEnumerator(SandboxOriginDatabaseInterface* origin_database, - const base::FilePath& base_file_path) - : base_file_path_(base_file_path) { + ObfuscatedOriginEnumerator( + SandboxOriginDatabaseInterface* origin_database, + base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util, + const base::FilePath& base_file_path) + : base_file_path_(base_file_path), + memory_file_util_(std::move(memory_file_util)) { if (origin_database) origin_database->ListAllOrigins(&origins_); } @@ -243,13 +246,17 @@ } base::FilePath path = base_file_path_.Append(current_.path).AppendASCII(type_string); - return base::DirectoryExists(path); + if (memory_file_util_) + return memory_file_util_->DirectoryExists(path); + else + return base::DirectoryExists(path); } private: std::vector<OriginRecord> origins_; OriginRecord current_; base::FilePath base_file_path_; + base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util_; }; ObfuscatedFileUtil::ObfuscatedFileUtil( @@ -947,8 +954,15 @@ std::vector<SandboxOriginDatabase::OriginRecord> origins; InitOriginDatabase(GURL(), false); - return std::make_unique<ObfuscatedOriginEnumerator>(origin_database_.get(), - file_system_directory_); + base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> file_util_delegate; + if (is_incognito() && + base::FeatureList::IsEnabled(features::kEnableFilesystemInIncognito)) { + file_util_delegate = + static_cast<ObfuscatedFileUtilMemoryDelegate*>(delegate()) + ->GetWeakPtr(); + } + return std::make_unique<ObfuscatedOriginEnumerator>( + origin_database_.get(), file_util_delegate, file_system_directory_); } void ObfuscatedFileUtil::DestroyDirectoryDatabase(
diff --git a/storage/browser/fileapi/obfuscated_file_util_unittest.cc b/storage/browser/fileapi/obfuscated_file_util_unittest.cc index 51a0d10..c2bd85b 100644 --- a/storage/browser/fileapi/obfuscated_file_util_unittest.cc +++ b/storage/browser/fileapi/obfuscated_file_util_unittest.cc
@@ -225,27 +225,13 @@ void TearDown() override { if (in_memory_test()) - CheckFilesInFileSystemDirectory(); + ASSERT_TRUE(IsDirectoryEmpty(data_dir_.GetPath())); quota_manager_ = nullptr; scoped_task_environment_.RunUntilIdle(); sandbox_file_system_.TearDown(); } - void CheckFilesInFileSystemDirectory() { - // Make sure there is no file on disk for in memory file system. Ignore - // directories created by the ObfuscatedFileUtil as they do not represent - // user directories. - // TODO(https://crbug.com/93417): Investigate why directories are created. - std::unique_ptr<storage::FileSystemFileUtil::AbstractFileEnumerator> - enumerator = storage::NativeFileUtil::CreateFileEnumerator( - data_dir_.GetPath(), true); - for (base::FilePath path = enumerator->Next(); !path.empty(); - path = enumerator->Next()) { - ASSERT_TRUE(storage::NativeFileUtil::DirectoryExists(path)); - } - } - std::unique_ptr<FileSystemOperationContext> LimitedContext( int64_t allowed_bytes_growth) { std::unique_ptr<FileSystemOperationContext> context(
diff --git a/storage/browser/test/sandbox_file_system_test_helper.cc b/storage/browser/test/sandbox_file_system_test_helper.cc index c353dd69..0646423 100644 --- a/storage/browser/test/sandbox_file_system_test_helper.cc +++ b/storage/browser/test/sandbox_file_system_test_helper.cc
@@ -165,9 +165,6 @@ file_system_context_->sandbox_delegate()-> GetBaseDirectoryForOriginAndType(origin_, type_, true /* create */); - // Initialize the usage cache file. The directory does not exist and should be - // created for in memory tests. - base::CreateDirectory(GetUsageCachePath().DirName()); base::FilePath usage_cache_path = GetUsageCachePath(); if (!usage_cache_path.empty()) usage_cache()->UpdateUsage(usage_cache_path, 0);
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json index 0d1f300..e729445 100644 --- a/testing/buildbot/chromium.linux.json +++ b/testing/buildbot/chromium.linux.json
@@ -4962,6 +4962,18 @@ } }, { + "isolate_name": "content_shell_crash_test", + "name": "content_shell_crash_test", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Ubuntu-14.04" + } + ] + } + }, + { "isolate_name": "devtools_closure_compile", "name": "devtools_closure_compile", "swarming": {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index 976158d..42772661 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -433,10 +433,6 @@ }, }, 'content_shell_crash_test': { - 'remove_from': [ - # chromium.linux - 'Linux Tests (dbg)(1)(32)', # https://crbug.com/859264 - ], 'modifications': { # chromium.win 'Win10 Tests x64 (dbg)': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 1bce82c..9718179d 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -5007,6 +5007,26 @@ ] } ], + "SyncUSSAutofillWalletMetadata": [ + { + "platforms": [ + "android", + "chromeos", + "ios", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "SyncUSSAutofillWalletMetadata" + ] + } + ] + } + ], "SyncUssBookmarks'": [ { "platforms": [
diff --git a/third_party/blink/public/mojom/clipboard/clipboard.mojom b/third_party/blink/public/mojom/clipboard/clipboard.mojom index 669cee5..0b38cb7 100644 --- a/third_party/blink/public/mojom/clipboard/clipboard.mojom +++ b/third_party/blink/public/mojom/clipboard/clipboard.mojom
@@ -55,34 +55,28 @@ ReadCustomData(ClipboardBuffer buffer, mojo_base.mojom.String16 type) => (mojo_base.mojom.BigString16 result); - // Writing to the clipboard via IPC is a two-phase operation. First, the + // Writing to the clipboard via mojo is a two-phase operation. First, the // sender sends the different types of data it'd like to write to the // receiver. Then, it sends a commit message to commit the data to the system // clipboard. - // TODO(dcheng): Remove |buffer| parameters from Write* functions. - WriteText(ClipboardBuffer buffer, mojo_base.mojom.BigString16 text); + WriteText(mojo_base.mojom.BigString16 text); - WriteHtml(ClipboardBuffer buffer, - mojo_base.mojom.BigString16 markup, - url.mojom.Url url); + WriteHtml(mojo_base.mojom.BigString16 markup, url.mojom.Url url); - WriteSmartPasteMarker(ClipboardBuffer buffer); + WriteSmartPasteMarker(); - WriteCustomData( - ClipboardBuffer buffer, - map<mojo_base.mojom.String16, mojo_base.mojom.BigString16> data); + WriteCustomData(map<mojo_base.mojom.String16, mojo_base.mojom.BigString16> data); // TODO(dcheng): The |url| parameter should really be a GURL, but <canvas>'s // copy as image tries to set very long data: URLs on the clipboard. Using // GURL causes the browser to kill the renderer for sending a bad IPC (GURLs // bigger than 2 megabytes are considered to be bad). https://crbug.com/459822 - WriteBookmark(ClipboardBuffer buffer, - string url, + WriteBookmark(string url, mojo_base.mojom.String16 title); - WriteImage(ClipboardBuffer buffer, skia.mojom.Bitmap image); + WriteImage(skia.mojom.Bitmap image); - CommitWrite(ClipboardBuffer buffer); + CommitWrite(); [EnableIf=is_mac] WriteStringToFindPboard(mojo_base.mojom.String16 text);
diff --git a/third_party/blink/renderer/core/clipboard/system_clipboard.cc b/third_party/blink/renderer/core/clipboard/system_clipboard.cc index 88aba4b..9865c84 100644 --- a/third_party/blink/renderer/core/clipboard/system_clipboard.cc +++ b/third_party/blink/renderer/core/clipboard/system_clipboard.cc
@@ -101,7 +101,7 @@ #if defined(OS_WIN) ReplaceNewlinesWithWindowsStyleNewlines(text); #endif - clipboard_->WriteText(mojom::ClipboardBuffer::kStandard, NonNullString(text)); + clipboard_->WriteText(NonNullString(text)); } String SystemClipboard::ReadHTML(KURL& url, @@ -131,11 +131,10 @@ #endif ReplaceNBSPWithSpace(text); - clipboard_->WriteHtml(mojom::ClipboardBuffer::kStandard, - NonNullString(markup), document_url); - clipboard_->WriteText(mojom::ClipboardBuffer::kStandard, NonNullString(text)); + clipboard_->WriteHtml(NonNullString(markup), document_url); + clipboard_->WriteText(NonNullString(text)); if (smart_replace_option == kCanSmartReplace) - clipboard_->WriteSmartPasteMarker(mojom::ClipboardBuffer::kStandard); + clipboard_->WriteSmartPasteMarker(); } String SystemClipboard::ReadRTF() { @@ -161,7 +160,7 @@ SkBitmap bitmap; if (sk_sp<SkImage> sk_image = paint_image.GetSkImage()) sk_image->asLegacyBitmap(&bitmap); - clipboard_->WriteImage(mojom::ClipboardBuffer::kStandard, bitmap); + clipboard_->WriteImage(bitmap); if (url.IsValid() && !url.IsEmpty()) { #if !defined(OS_MACOSX) @@ -169,21 +168,19 @@ // consistency between platforms, and to help fix errors in applications // which prefer text/plain content over image content for compatibility with // Microsoft Word. - clipboard_->WriteBookmark(mojom::ClipboardBuffer::kStandard, - url.GetString(), NonNullString(title)); + clipboard_->WriteBookmark(url.GetString(), NonNullString(title)); #endif // When writing the image, we also write the image markup so that pasting // into rich text editors, such as Gmail, reveals the image. We also don't // want to call writeText(), since some applications (WordPad) don't pick // the image if there is also a text format on the clipboard. - clipboard_->WriteHtml(mojom::ClipboardBuffer::kStandard, - URLToImageMarkup(url, title), KURL()); + clipboard_->WriteHtml(URLToImageMarkup(url, title), KURL()); } } void SystemClipboard::WriteImage(const SkBitmap& bitmap) { - clipboard_->WriteImage(mojom::ClipboardBuffer::kStandard, bitmap); + clipboard_->WriteImage(bitmap); } String SystemClipboard::ReadCustomData(const String& type) { @@ -211,24 +208,21 @@ for (const WebDragData::Item& item : data.Items()) { if (item.storage_type == WebDragData::Item::kStorageTypeString) { if (item.string_type == blink::kMimeTypeTextPlain) { - clipboard_->WriteText(mojom::ClipboardBuffer::kStandard, - NonNullString(item.string_data)); + clipboard_->WriteText(NonNullString(item.string_data)); } else if (item.string_type == blink::kMimeTypeTextHTML) { - clipboard_->WriteHtml(mojom::ClipboardBuffer::kStandard, - NonNullString(item.string_data), KURL()); + clipboard_->WriteHtml(NonNullString(item.string_data), KURL()); } else if (item.string_type != blink::kMimeTypeDownloadURL) { custom_data.insert(item.string_type, NonNullString(item.string_data)); } } } if (!custom_data.IsEmpty()) { - clipboard_->WriteCustomData(mojom::ClipboardBuffer::kStandard, - std::move(custom_data)); + clipboard_->WriteCustomData(std::move(custom_data)); } } void SystemClipboard::CommitWrite() { - clipboard_->CommitWrite(mojom::ClipboardBuffer::kStandard); + clipboard_->CommitWrite(); } bool SystemClipboard::IsValidBufferType(mojom::ClipboardBuffer buffer) {
diff --git a/third_party/blink/renderer/core/dom/whitespace_attacher_test.cc b/third_party/blink/renderer/core/dom/whitespace_attacher_test.cc index d6e4dec..79cca60 100644 --- a/third_party/blink/renderer/core/dom/whitespace_attacher_test.cc +++ b/third_party/blink/renderer/core/dom/whitespace_attacher_test.cc
@@ -21,7 +21,7 @@ UpdateAllLifecyclePhasesForTest(); Element* div = GetDocument().getElementById("block"); - Text* text = ToText(div->nextSibling()); + auto* text = To<Text>(div->nextSibling()); EXPECT_FALSE(text->GetLayoutObject()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); @@ -41,7 +41,7 @@ UpdateAllLifecyclePhasesForTest(); Element* span = GetDocument().getElementById("inline"); - Text* text = ToText(span->nextSibling()); + auto* text = To<Text>(span->nextSibling()); EXPECT_TRUE(text->GetLayoutObject()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); @@ -61,9 +61,9 @@ UpdateAllLifecyclePhasesForTest(); Element* span = GetDocument().getElementById("inline"); - Text* first_whitespace = ToText(span->nextSibling()); - Text* second_whitespace = - ToText(first_whitespace->nextSibling()->nextSibling()); + auto* first_whitespace = To<Text>(span->nextSibling()); + auto* second_whitespace = + To<Text>(first_whitespace->nextSibling()->nextSibling()); EXPECT_TRUE(first_whitespace->GetLayoutObject()); EXPECT_FALSE(second_whitespace->GetLayoutObject()); @@ -87,7 +87,7 @@ UpdateAllLifecyclePhasesForTest(); Element* div = GetDocument().getElementById("block"); - Text* text = ToText(div->nextSibling()); + auto* text = To<Text>(div->nextSibling()); EXPECT_FALSE(text->GetLayoutObject()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); @@ -105,7 +105,7 @@ UpdateAllLifecyclePhasesForTest(); Element* span = GetDocument().getElementById("inline"); - Text* text = ToText(span->nextSibling()); + auto* text = To<Text>(span->nextSibling()); EXPECT_TRUE(text->GetLayoutObject()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); @@ -125,8 +125,8 @@ GetDocument().body()->SetInnerHTMLFromString("Text<!-- --> "); UpdateAllLifecyclePhasesForTest(); - Text* text = ToText(GetDocument().body()->firstChild()); - Text* whitespace = ToText(text->nextSibling()->nextSibling()); + auto* text = To<Text>(GetDocument().body()->firstChild()); + auto* whitespace = To<Text>(text->nextSibling()->nextSibling()); EXPECT_TRUE(text->GetLayoutObject()); EXPECT_TRUE(whitespace->GetLayoutObject()); @@ -149,7 +149,7 @@ UpdateAllLifecyclePhasesForTest(); Element* div = GetDocument().getElementById("block"); - Text* text = ToText(div->firstChild()); + auto* text = To<Text>(div->firstChild()); EXPECT_FALSE(text->GetLayoutObject()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); @@ -171,7 +171,7 @@ UpdateAllLifecyclePhasesForTest(); Element* span = GetDocument().getElementById("inline"); - Text* text = ToText(span->firstChild()); + auto* text = To<Text>(span->firstChild()); EXPECT_TRUE(text->GetLayoutObject()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); @@ -198,7 +198,7 @@ UpdateAllLifecyclePhasesForTest(); Element* div = shadow_root.getElementById("block"); - Text* text = ToText(host->firstChild()); + auto* text = To<Text>(host->firstChild()); EXPECT_FALSE(text->GetLayoutObject()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); @@ -226,7 +226,7 @@ UpdateAllLifecyclePhasesForTest(); Element* span = shadow_root.getElementById("inline"); - Text* text = ToText(host->firstChild()); + auto* text = To<Text>(host->firstChild()); EXPECT_TRUE(text->GetLayoutObject()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); @@ -250,7 +250,7 @@ Element* div = GetDocument().getElementById("block"); Element* contents = ToElement(div->nextSibling()); - Text* text = ToText(contents->firstChild()); + auto* text = To<Text>(contents->firstChild()); EXPECT_FALSE(contents->GetLayoutObject()); EXPECT_FALSE(text->GetLayoutObject()); @@ -276,7 +276,7 @@ Element* span = GetDocument().getElementById("inline"); Element* contents = ToElement(span->nextSibling()); - Text* text = ToText(contents->firstChild()); + auto* text = To<Text>(contents->firstChild()); EXPECT_FALSE(contents->GetLayoutObject()); EXPECT_TRUE(text->GetLayoutObject()); @@ -301,7 +301,7 @@ Element* div = GetDocument().getElementById("block"); Element* contents = ToElement(div->nextSibling()); - Text* text = ToText(contents->nextSibling()); + auto* text = To<Text>(contents->nextSibling()); EXPECT_FALSE(contents->GetLayoutObject()); EXPECT_FALSE(text->GetLayoutObject()); @@ -329,7 +329,7 @@ Element* div = GetDocument().getElementById("block"); Element* contents = ToElement(div->nextSibling()); - Text* text = ToText(contents->nextSibling()); + auto* text = To<Text>(contents->nextSibling()); EXPECT_FALSE(contents->GetLayoutObject()); EXPECT_FALSE(text->GetLayoutObject()); @@ -357,7 +357,7 @@ Element* span = GetDocument().getElementById("inline"); Element* contents = ToElement(span->nextSibling()); - Text* text = ToText(GetDocument().getElementById("inner")->firstChild()); + auto* text = To<Text>(GetDocument().getElementById("inner")->firstChild()); EXPECT_TRUE(text->GetLayoutObject()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); @@ -385,7 +385,7 @@ Element* first_contents = ToElement(span->nextSibling()); Element* second_contents = ToElement(first_contents->nextSibling()); Element* last_contents = ToElement(second_contents->nextSibling()); - Text* text = ToText(last_contents->firstChild()); + auto* text = To<Text>(last_contents->firstChild()); EXPECT_TRUE(text->GetLayoutObject()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc); @@ -417,7 +417,7 @@ Element* span = shadow_root.getElementById("inline"); Element* contents = ToElement(span->nextSibling()); - Text* text = ToText(host->firstChild()); + auto* text = To<Text>(host->firstChild()); EXPECT_TRUE(text->GetLayoutObject()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
diff --git a/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc b/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc index 20fa4bd..a8e4c108 100644 --- a/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc +++ b/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc
@@ -219,15 +219,15 @@ } static bool IsNewLineAtPosition(const Position& position) { - Node* text_node = position.ComputeContainerNode(); + auto* text_node = DynamicTo<Text>(position.ComputeContainerNode()); int offset = position.OffsetInContainerNode(); - if (!text_node || !text_node->IsTextNode() || offset < 0 || - offset >= static_cast<int>(ToText(text_node)->length())) + if (!text_node || offset < 0 || + offset >= static_cast<int>(text_node->length())) return false; DummyExceptionStateForTesting exception_state; String text_at_position = - ToText(text_node)->substringData(offset, 1, exception_state); + text_node->substringData(offset, 1, exception_state); if (exception_state.HadException()) return false; @@ -276,7 +276,7 @@ if (!start_style->CollapseWhiteSpace() && start.OffsetInContainerNode() > 0) { int start_offset = start.OffsetInContainerNode(); - Text* start_text = ToText(start.ComputeContainerNode()); + auto* start_text = To<Text>(start.ComputeContainerNode()); SplitTextNode(start_text, start_offset); GetDocument().UpdateStyleAndLayoutTree(); @@ -301,7 +301,7 @@ // Include \n at the end of line if we're at an empty paragraph if (end_style->PreserveNewline() && start == end && end.OffsetInContainerNode() < - static_cast<int>(ToText(end.ComputeContainerNode())->length())) { + static_cast<int>(To<Text>(end.ComputeContainerNode())->length())) { int end_offset = end.OffsetInContainerNode(); // TODO(yosin) We should use |PositionMoveType::CodePoint| for // |previousPositionOf()|. @@ -319,8 +319,8 @@ if (end_style->UserModify() != EUserModify::kReadOnly && !end_style->CollapseWhiteSpace() && end.OffsetInContainerNode() && end.OffsetInContainerNode() < - static_cast<int>(ToText(end.ComputeContainerNode())->length())) { - Text* end_container = ToText(end.ComputeContainerNode()); + static_cast<int>(To<Text>(end.ComputeContainerNode())->length())) { + auto* end_container = To<Text>(end.ComputeContainerNode()); SplitTextNode(end_container, end.OffsetInContainerNode()); GetDocument().UpdateStyleAndLayoutTree(); @@ -361,8 +361,8 @@ if (!style) return end_of_next_paragraph; - Text* const end_of_next_paragraph_text = - ToText(end_of_next_paragraph_position.ComputeContainerNode()); + auto* const end_of_next_paragraph_text = + To<Text>(end_of_next_paragraph_position.ComputeContainerNode()); if (!style->PreserveNewline() || !end_of_next_paragraph_position.OffsetInContainerNode() || !IsNewLineAtPosition( @@ -376,10 +376,7 @@ SplitTextNode(end_of_next_paragraph_text, 1); GetDocument().UpdateStyleAndLayout(); Text* const previous_text = - end_of_next_paragraph_text->previousSibling() && - end_of_next_paragraph_text->previousSibling()->IsTextNode() - ? ToText(end_of_next_paragraph_text->previousSibling()) - : nullptr; + DynamicTo<Text>(end_of_next_paragraph_text->previousSibling()); if (end_of_next_paragraph_text == start.ComputeContainerNode() && previous_text) { DCHECK_LT(start.OffsetInContainerNode(),
diff --git a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc index 7a4c5575..9959418 100644 --- a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc +++ b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
@@ -1446,10 +1446,10 @@ // Position("world", 0) instead. const unsigned push_down_start_offset = push_down_start.ComputeOffsetInContainerNode(); - Node* push_down_start_container = push_down_start.ComputeContainerNode(); - if (push_down_start_container && push_down_start_container->IsTextNode() && - push_down_start_offset == - ToText(push_down_start_container)->length()) + auto* push_down_start_container = + DynamicTo<Text>(push_down_start.ComputeContainerNode()); + if (push_down_start_container && + push_down_start_offset == push_down_start_container->length()) push_down_start = NextVisuallyDistinctCandidate(push_down_start); // TODO(editing-dev): Use of UpdateStyleAndLayout @@ -1579,7 +1579,7 @@ else new_end = end; - Text* text = ToText(start.ComputeContainerNode()); + auto* text = To<Text>(start.ComputeContainerNode()); SplitTextNode(text, start.OffsetInContainerNode()); UpdateStartEnd(EphemeralRange(Position::FirstPositionInNode(*text), new_end)); } @@ -1591,19 +1591,19 @@ bool should_update_start = start.IsOffsetInAnchor() && start.ComputeContainerNode() == end.ComputeContainerNode(); - Text* text = ToText(end.AnchorNode()); + auto* text = To<Text>(end.AnchorNode()); SplitTextNode(text, end.OffsetInContainerNode()); - Node* prev_node = text->previousSibling(); - if (!prev_node || !prev_node->IsTextNode()) + auto* prev_text_node = DynamicTo<Text>(text->previousSibling()); + if (!prev_text_node) return; Position new_start = should_update_start - ? Position(ToText(prev_node), start.OffsetInContainerNode()) + ? Position(prev_text_node, start.OffsetInContainerNode()) : start; UpdateStartEnd( - EphemeralRange(new_start, Position::LastPositionInNode(*prev_node))); + EphemeralRange(new_start, Position::LastPositionInNode(*prev_text_node))); } void ApplyStyleCommand::SplitTextElementAtStart(const Position& start, @@ -1618,7 +1618,7 @@ else new_end = end; - SplitTextNodeContainingElement(ToText(start.ComputeContainerNode()), + SplitTextNodeContainingElement(To<Text>(start.ComputeContainerNode()), start.OffsetInContainerNode()); UpdateStartEnd(EphemeralRange( Position::BeforeNode(*start.ComputeContainerNode()), new_end)); @@ -1630,19 +1630,21 @@ bool should_update_start = start.ComputeContainerNode() == end.ComputeContainerNode(); - SplitTextNodeContainingElement(ToText(end.ComputeContainerNode()), + SplitTextNodeContainingElement(To<Text>(end.ComputeContainerNode()), end.OffsetInContainerNode()); Node* parent_element = end.ComputeContainerNode()->parentNode(); if (!parent_element || !parent_element->previousSibling()) return; - Node* first_text_node = parent_element->previousSibling()->lastChild(); - if (!first_text_node || !first_text_node->IsTextNode()) + + auto* first_text_node = + DynamicTo<Text>(parent_element->previousSibling()->lastChild()); + if (!first_text_node) return; Position new_start = should_update_start - ? Position(ToText(first_text_node), start.OffsetInContainerNode()) + ? Position(first_text_node, start.OffsetInContainerNode()) : start; UpdateStartEnd( EphemeralRange(new_start, Position::AfterNode(*first_text_node))); @@ -2054,20 +2056,18 @@ Position new_end = end; HeapVector<Member<Text>> text_nodes; - for (Node* curr = node->firstChild(); curr; curr = curr->nextSibling()) { - if (!curr->IsTextNode()) - continue; - - text_nodes.push_back(ToText(curr)); + for (Node& child : NodeTraversal::ChildrenOf(*node)) { + if (auto* child_text = DynamicTo<Text>(child)) + text_nodes.push_back(child_text); } for (const auto& text_node : text_nodes) { Text* child_text = text_node; Node* next = child_text->nextSibling(); - if (!next || !next->IsTextNode()) + auto* next_text = DynamicTo<Text>(next); + if (!next_text) continue; - Text* next_text = ToText(next); if (start.IsOffsetInAnchor() && next == start.ComputeContainerNode()) new_start = Position( child_text, child_text->length() + start.OffsetInContainerNode());
diff --git a/third_party/blink/renderer/core/editing/commands/break_blockquote_command.cc b/third_party/blink/renderer/core/editing/commands/break_blockquote_command.cc index 793dc9a..1e47940a 100644 --- a/third_party/blink/renderer/core/editing/commands/break_blockquote_command.cc +++ b/third_party/blink/renderer/core/editing/commands/break_blockquote_command.cc
@@ -184,8 +184,7 @@ DCHECK(start_node); // Split at pos if in the middle of a text node. - if (start_node->IsTextNode()) { - Text* text_node = ToText(start_node); + if (auto* text_node = DynamicTo<Text>(start_node)) { int text_offset = pos.ComputeOffsetInContainerNode(); if ((unsigned)text_offset >= text_node->length()) { start_node = NodeTraversal::Next(*start_node);
diff --git a/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc b/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc index 4d39a2b..6dde6f4 100644 --- a/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc +++ b/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc
@@ -329,6 +329,7 @@ Node* ref_child = p.AnchorNode(); int offset = p.OffsetInContainerNode(); + auto* ref_child_text_node = DynamicTo<Text>(ref_child); if (CanHaveChildrenForEditing(ref_child)) { Node* child = ref_child->firstChild(); for (int i = 0; child && i < offset; i++) @@ -339,11 +340,11 @@ AppendNode(insert_child, ToContainerNode(ref_child), editing_state); } else if (CaretMinOffset(ref_child) >= offset) { InsertNodeBefore(insert_child, ref_child, editing_state); - } else if (ref_child->IsTextNode() && CaretMaxOffset(ref_child) > offset) { - SplitTextNode(ToText(ref_child), offset); + } else if (ref_child_text_node && CaretMaxOffset(ref_child) > offset) { + SplitTextNode(ref_child_text_node, offset); - // Mutation events (bug 22634) from the text node insertion may have removed - // the refChild + // Mutation events (bug 22634) from the text node insertion may have + // removed the refChild if (!ref_child->isConnected()) return; InsertNodeBefore(insert_child, ref_child, editing_state); @@ -581,12 +582,11 @@ Position CompositeEditCommand::ReplaceSelectedTextInNode(const String& text) { const Position& start = EndingSelection().Start(); const Position& end = EndingSelection().End(); - if (start.ComputeContainerNode() != end.ComputeContainerNode() || - !start.ComputeContainerNode()->IsTextNode() || - IsTabHTMLSpanElementTextNode(start.ComputeContainerNode())) + auto* text_node = DynamicTo<Text>(start.ComputeContainerNode()); + if (!text_node || text_node != end.ComputeContainerNode() || + IsTabHTMLSpanElementTextNode(text_node)) return Position(); - Text* text_node = ToText(start.ComputeContainerNode()); ReplaceTextInNode(text_node, start.OffsetInContainerNode(), end.OffsetInContainerNode() - start.OffsetInContainerNode(), text); @@ -624,7 +624,7 @@ if (pos.OffsetInContainerNode() >= CaretMaxOffset(pos.ComputeContainerNode())) return Position::InParentAfterNode(*tab_span); - SplitTextNodeContainingElement(ToText(pos.ComputeContainerNode()), + SplitTextNodeContainingElement(To<Text>(pos.ComputeContainerNode()), pos.OffsetInContainerNode()); return Position::InParentBeforeNode(*tab_span); } @@ -683,12 +683,11 @@ // needs to be audited. See http://crbug.com/590369 for more details. GetDocument().UpdateStyleAndLayout(); - Node* node = position.ComputeContainerNode(); - if (!position.IsOffsetInAnchor() || !node || !node->IsTextNode() || - !HasRichlyEditableStyle(*node)) + auto* text_node = DynamicTo<Text>(position.ComputeContainerNode()); + if (!position.IsOffsetInAnchor() || !text_node || + !HasRichlyEditableStyle(*text_node)) return false; - Text* text_node = ToText(node); if (text_node->length() == 0) return false; @@ -709,14 +708,14 @@ // If the rebalance is for the single offset, and neither text[offset] nor // text[offset - 1] are some form of whitespace, do nothing. int offset = position.ComputeOffsetInContainerNode(); - String text = ToText(node)->data(); + String text = To<Text>(node)->data(); if (!IsWhitespace(text[offset])) { offset--; if (offset < 0 || !IsWhitespace(text[offset])) return; } - RebalanceWhitespaceOnTextSubstring(ToText(node), + RebalanceWhitespaceOnTextSubstring(To<Text>(node), position.OffsetInContainerNode(), position.OffsetInContainerNode()); } @@ -754,10 +753,10 @@ // current text node. However, if the next sibling node is a text node // (not empty, see http://crbug.com/632300), we should use a plain space. // See http://crbug.com/310149 + auto* next_text_node = DynamicTo<Text>(text_node->nextSibling()); const bool next_sibling_is_text_node = - text_node->nextSibling() && text_node->nextSibling()->IsTextNode() && - ToText(text_node->nextSibling())->data().length() && - !IsWhitespace(ToText(text_node->nextSibling())->data()[0]); + next_text_node && next_text_node->data().length() && + !IsWhitespace(next_text_node->data()[0]); const bool should_emit_nbs_pbefore_end = (IsEndOfParagraph(visible_downstream_pos) || (unsigned)downstream == text.length()) && @@ -774,10 +773,10 @@ Position& position) { if (!IsRichlyEditablePosition(position)) return; - Node* node = position.AnchorNode(); - if (!node || !node->IsTextNode()) + + auto* text_node = DynamicTo<Text>(position.AnchorNode()); + if (!text_node) return; - Text* text_node = ToText(node); if (text_node->length() == 0) return; @@ -807,10 +806,11 @@ if (!IsCollapsibleWhitespace(CharacterAfter(visible_position))) return; Position pos = MostForwardCaretPosition(visible_position.DeepEquivalent()); - if (!pos.ComputeContainerNode() || !pos.ComputeContainerNode()->IsTextNode()) + auto* container_text_node = DynamicTo<Text>(pos.ComputeContainerNode()); + if (!container_text_node) return; - ReplaceTextInNode(ToText(pos.ComputeContainerNode()), - pos.OffsetInContainerNode(), 1, NonBreakingSpaceString()); + ReplaceTextInNode(container_text_node, pos.OffsetInContainerNode(), 1, + NonBreakingSpaceString()); } void CompositeEditCommand::RebalanceWhitespace() { @@ -919,8 +919,8 @@ HeapVector<Member<Text>> nodes; for (Node& node : NodeTraversal::StartsAt(*start.AnchorNode())) { - if (node.IsTextNode()) - nodes.push_back(ToText(&node)); + if (auto* text_node = DynamicTo<Text>(&node)) + nodes.push_back(text_node); if (&node == end.AnchorNode()) break; } @@ -1015,7 +1015,7 @@ return; } - DeleteTextFromNode(ToText(p.AnchorNode()), p.OffsetInContainerNode(), 1); + DeleteTextFromNode(To<Text>(p.AnchorNode()), p.OffsetInContainerNode(), 1); } HTMLElement* CompositeEditCommand::InsertNewDefaultParagraphElementAt( @@ -1295,7 +1295,7 @@ } else if (LineBreakExistsAtPosition(position)) { // There is a preserved '\n' at caretAfterDelete. // We can safely assume this is a text node. - Text* text_node = ToText(node); + auto* text_node = To<Text>(node); if (text_node->length() == 1) RemoveNodeAndPruneAncestors(node, editing_state, destination_node); else @@ -1829,9 +1829,8 @@ RemoveNodeAndPruneAncestors(caret_pos.AnchorNode(), editing_state); if (editing_state->IsAborted()) return false; - } else if (caret_pos.AnchorNode()->IsTextNode()) { + } else if (auto* text_node = DynamicTo<Text>(caret_pos.AnchorNode())) { DCHECK_EQ(caret_pos.ComputeOffsetInContainerNode(), 0); - Text* text_node = ToText(caret_pos.AnchorNode()); ContainerNode* parent_node = text_node->parentNode(); // The preserved newline must be the first thing in the node, since // otherwise the previous paragraph would be quoted, and we verified that it
diff --git a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc index dab8b54..186f8223 100644 --- a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc +++ b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
@@ -605,8 +605,8 @@ } GetDocument().UpdateStyleAndLayout(); - if (start_offset >= CaretMaxOffset(start_node) && start_node->IsTextNode()) { - Text* text = ToText(start_node); + auto* text = DynamicTo<Text>(start_node); + if (start_offset >= CaretMaxOffset(start_node) && text) { if (text->length() > (unsigned)CaretMaxOffset(start_node)) DeleteTextFromNode(text, CaretMaxOffset(start_node), text->length() - CaretMaxOffset(start_node)); @@ -623,9 +623,8 @@ if (start_node == downstream_end_.AnchorNode()) { if (downstream_end_.ComputeEditingOffset() - start_offset > 0) { - if (start_node->IsTextNode()) { + if (auto* text = DynamicTo<Text>(start_node)) { // in a text node that needs to be trimmed - Text* text = ToText(start_node); DeleteTextFromNode( text, start_offset, downstream_end_.ComputeOffsetInContainerNode() - start_offset); @@ -654,20 +653,19 @@ downstream_end_.AnchorNode()); // The selection to delete spans more than one node. Node* node(start_node); - + auto* start_text_node = DynamicTo<Text>(start_node); if (start_offset > 0) { - if (start_node->IsTextNode()) { + if (start_text_node) { // in a text node that needs to be trimmed - Text* text = ToText(node); - DeleteTextFromNode(text, start_offset, text->length() - start_offset); + DeleteTextFromNode(start_text_node, start_offset, + start_text_node->length() - start_offset); node = NodeTraversal::Next(*node); } else { node = NodeTraversal::ChildAt(*start_node, start_offset); } - } else if (start_node == upstream_end_.AnchorNode() && - start_node->IsTextNode()) { - Text* text = ToText(upstream_end_.AnchorNode()); - DeleteTextFromNode(text, 0, upstream_end_.ComputeOffsetInContainerNode()); + } else if (start_node == upstream_end_.AnchorNode() && start_text_node) { + DeleteTextFromNode(start_text_node, 0, + upstream_end_.ComputeOffsetInContainerNode()); } // handle deleting all nodes that are completely selected @@ -716,9 +714,8 @@ // The node itself is fully selected, not just its contents. Delete it. RemoveNode(downstream_end_.AnchorNode(), editing_state); } else { - if (downstream_end_.AnchorNode()->IsTextNode()) { + if (auto* text = DynamicTo<Text>(downstream_end_.AnchorNode())) { // in a text node that needs to be trimmed - Text* text = ToText(downstream_end_.AnchorNode()); if (downstream_end_.ComputeEditingOffset() > 0) { DeleteTextFromNode(text, 0, downstream_end_.ComputeEditingOffset()); } @@ -756,26 +753,27 @@ void DeleteSelectionCommand::FixupWhitespace() { GetDocument().UpdateStyleAndLayout(); if (leading_whitespace_.IsNotNull() && - !IsRenderedCharacter(leading_whitespace_) && - leading_whitespace_.AnchorNode()->IsTextNode()) { - Text* text_node = ToText(leading_whitespace_.AnchorNode()); - DCHECK(!text_node->GetLayoutObject() || - text_node->GetLayoutObject()->Style()->CollapseWhiteSpace()) - << text_node; - ReplaceTextInNode(text_node, - leading_whitespace_.ComputeOffsetInContainerNode(), 1, - NonBreakingSpaceString()); + !IsRenderedCharacter(leading_whitespace_)) { + if (auto* text_node = DynamicTo<Text>(leading_whitespace_.AnchorNode())) { + DCHECK(!text_node->GetLayoutObject() || + text_node->GetLayoutObject()->Style()->CollapseWhiteSpace()) + << text_node; + ReplaceTextInNode(text_node, + leading_whitespace_.ComputeOffsetInContainerNode(), 1, + NonBreakingSpaceString()); + } } + if (trailing_whitespace_.IsNotNull() && - !IsRenderedCharacter(trailing_whitespace_) && - trailing_whitespace_.AnchorNode()->IsTextNode()) { - Text* text_node = ToText(trailing_whitespace_.AnchorNode()); - DCHECK(!text_node->GetLayoutObject() || - text_node->GetLayoutObject()->Style()->CollapseWhiteSpace()) - << text_node; - ReplaceTextInNode(text_node, - trailing_whitespace_.ComputeOffsetInContainerNode(), 1, - NonBreakingSpaceString()); + !IsRenderedCharacter(trailing_whitespace_)) { + if (auto* text_node = DynamicTo<Text>(trailing_whitespace_.AnchorNode())) { + DCHECK(!text_node->GetLayoutObject() || + text_node->GetLayoutObject()->Style()->CollapseWhiteSpace()) + << text_node; + ReplaceTextInNode(text_node, + trailing_whitespace_.ComputeOffsetInContainerNode(), 1, + NonBreakingSpaceString()); + } } }
diff --git a/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc b/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc index 32424ba..b5d99f71 100644 --- a/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc +++ b/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc
@@ -45,6 +45,7 @@ #include "third_party/blink/renderer/core/html/html_html_element.h" #include "third_party/blink/renderer/core/inspector/console_message.h" #include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/layout/layout_text.h" #include "third_party/blink/renderer/platform/heap/heap.h" namespace blink { @@ -255,11 +256,10 @@ if (!position.AnchorNode()->GetLayoutObject()) return false; - if (!position.AnchorNode()->IsTextNode() || - !position.AnchorNode()->GetLayoutObject()->Style()->PreserveNewline()) + const auto* text_node = DynamicTo<Text>(position.AnchorNode()); + if (!text_node || !text_node->GetLayoutObject()->Style()->PreserveNewline()) return false; - const Text* text_node = ToText(position.AnchorNode()); unsigned offset = position.OffsetInContainerNode(); return offset < text_node->length() && text_node->data()[offset] == '\n'; } @@ -317,7 +317,8 @@ if (prev == position) return Position(); const Node* const anchor_node = prev.AnchorNode(); - if (!anchor_node || !anchor_node->IsTextNode()) + auto* anchor_text_node = DynamicTo<Text>(anchor_node); + if (!anchor_text_node) return Position(); if (EnclosingBlockFlowElement(*anchor_node) != EnclosingBlockFlowElement(*position.AnchorNode())) @@ -326,7 +327,7 @@ anchor_node->GetLayoutObject() && !anchor_node->GetLayoutObject()->Style()->CollapseWhiteSpace()) return Position(); - const String& string = ToText(anchor_node)->data(); + const String& string = anchor_text_node->data(); const UChar previous_character = string[prev.ComputeOffsetInContainerNode()]; const bool is_space = option == kConsiderNonCollapsibleWhitespace ? (IsSpaceOrNewline(previous_character) ||
diff --git a/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc b/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc index a1ef62ef..edc26d5 100644 --- a/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc +++ b/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc
@@ -163,9 +163,8 @@ SelectionInDOMTree::Builder() .Collapse(Position::InParentAfterNode(*node_to_insert)) .Build())); - } else if (pos.AnchorNode()->IsTextNode()) { + } else if (auto* text_node = DynamicTo<Text>(pos.AnchorNode())) { // Split a text node - Text* text_node = ToText(pos.AnchorNode()); SplitTextNode(text_node, pos.ComputeOffsetInContainerNode()); InsertNodeBefore(node_to_insert, text_node, editing_state); if (editing_state->IsAborted())
diff --git a/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc b/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc index 432da89f..5af3a30d 100644 --- a/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc +++ b/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc
@@ -489,31 +489,33 @@ // FIXME: leadingCollapsibleWhitespacePosition is returning the position // before preserved newlines for positions after the preserved newline, // causing the newline to be turned into a nbsp. - if (leading_whitespace.IsNotNull() && - leading_whitespace.AnchorNode()->IsTextNode()) { - Text* text_node = ToText(leading_whitespace.AnchorNode()); - DCHECK(!text_node->GetLayoutObject() || - text_node->GetLayoutObject()->Style()->CollapseWhiteSpace()) - << text_node; - ReplaceTextInNode(text_node, - leading_whitespace.ComputeOffsetInContainerNode(), 1, - NonBreakingSpaceString()); - GetDocument().UpdateStyleAndLayout(); + if (leading_whitespace.IsNotNull()) { + if (auto* text_node = DynamicTo<Text>(leading_whitespace.AnchorNode())) { + DCHECK(!text_node->GetLayoutObject() || + text_node->GetLayoutObject()->Style()->CollapseWhiteSpace()) + << text_node; + ReplaceTextInNode(text_node, + leading_whitespace.ComputeOffsetInContainerNode(), 1, + NonBreakingSpaceString()); + GetDocument().UpdateStyleAndLayout(); + } } // Split at pos if in the middle of a text node. Position position_after_split; - if (insertion_position.IsOffsetInAnchor() && - insertion_position.ComputeContainerNode()->IsTextNode()) { - Text* text_node = ToText(insertion_position.ComputeContainerNode()); - int text_offset = insertion_position.OffsetInContainerNode(); - bool at_end = static_cast<unsigned>(text_offset) >= text_node->length(); - if (text_offset > 0 && !at_end) { - SplitTextNode(text_node, text_offset); - GetDocument().UpdateStyleAndLayout(); + if (insertion_position.IsOffsetInAnchor()) { + if (auto* text_node = + DynamicTo<Text>(insertion_position.ComputeContainerNode())) { + int text_offset = insertion_position.OffsetInContainerNode(); + bool at_end = static_cast<unsigned>(text_offset) >= text_node->length(); + if (text_offset > 0 && !at_end) { + SplitTextNode(text_node, text_offset); + GetDocument().UpdateStyleAndLayout(); - position_after_split = Position::FirstPositionInNode(*text_node); - insertion_position = Position(text_node->previousSibling(), text_offset); + position_after_split = Position::FirstPositionInNode(*text_node); + insertion_position = + Position(text_node->previousSibling(), text_offset); + } } }
diff --git a/third_party/blink/renderer/core/editing/commands/insert_text_command.cc b/third_party/blink/renderer/core/editing/commands/insert_text_command.cc index a7c2b33..ff0ac87 100644 --- a/third_party/blink/renderer/core/editing/commands/insert_text_command.cc +++ b/third_party/blink/renderer/core/editing/commands/insert_text_command.cc
@@ -119,11 +119,8 @@ bool InsertTextCommand::PerformOverwrite(const String& text) { Position start = EndingVisibleSelection().Start(); - if (start.IsNull() || !start.IsOffsetInAnchor() || - !start.ComputeContainerNode()->IsTextNode()) - return false; - Text* text_node = ToText(start.ComputeContainerNode()); - if (!text_node) + auto* text_node = DynamicTo<Text>(start.ComputeContainerNode()); + if (start.IsNull() || !start.IsOffsetInAnchor() || !text_node) return false; unsigned count = std::min( @@ -312,11 +309,11 @@ return pos; Node* node = insert_pos.ComputeContainerNode(); - unsigned offset = node->IsTextNode() ? insert_pos.OffsetInContainerNode() : 0; + auto* text_node = DynamicTo<Text>(node); + unsigned offset = text_node ? insert_pos.OffsetInContainerNode() : 0; // keep tabs coalesced in tab span if (IsTabHTMLSpanElementTextNode(node)) { - Text* text_node = ToText(node); InsertTextIntoNode(text_node, offset, "\t"); return Position(text_node, offset + 1); } @@ -325,7 +322,7 @@ HTMLSpanElement* span_element = CreateTabSpanElement(GetDocument()); // place it - if (!node->IsTextNode()) { + if (!text_node) { InsertNodeAt(span_element, insert_pos, editing_state); } else { Text* text_node = ToText(node);
diff --git a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc index d3116ab..6c716da1 100644 --- a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc +++ b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
@@ -777,9 +777,8 @@ InsertedNodes& inserted_nodes) { GetDocument().UpdateStyleAndLayout(); - Node* last_leaf_inserted = inserted_nodes.LastLeafInserted(); - if (last_leaf_inserted && last_leaf_inserted->IsTextNode() && - !NodeHasVisibleLayoutText(ToText(*last_leaf_inserted)) && + auto* last_leaf_inserted = DynamicTo<Text>(inserted_nodes.LastLeafInserted()); + if (last_leaf_inserted && !NodeHasVisibleLayoutText(*last_leaf_inserted) && !EnclosingElementWithTag(FirstPositionInOrBeforeNode(*last_leaf_inserted), kSelectTag) && !EnclosingElementWithTag(FirstPositionInOrBeforeNode(*last_leaf_inserted), @@ -792,9 +791,9 @@ // We don't have to make sure that firstNodeInserted isn't inside a select or // script element, because it is a top level node in the fragment and the user // can't insert into those elements. - Node* first_node_inserted = inserted_nodes.FirstNodeInserted(); - if (first_node_inserted && first_node_inserted->IsTextNode() && - !NodeHasVisibleLayoutText(ToText(*first_node_inserted))) { + auto* first_node_inserted = + DynamicTo<Text>(inserted_nodes.FirstNodeInserted()); + if (first_node_inserted && !NodeHasVisibleLayoutText(*first_node_inserted)) { inserted_nodes.WillRemoveNode(*first_node_inserted); // Removing a Text node won't dispatch synchronous events. RemoveNode(first_node_inserted, ASSERT_NO_EDITING_ABORT); @@ -1299,11 +1298,10 @@ // our style spans and for positions inside list items // since insertAsListItems already does the right thing. if (!match_style_ && !EnclosingList(insertion_pos.ComputeContainerNode())) { - if (insertion_pos.ComputeContainerNode()->IsTextNode() && - insertion_pos.OffsetInContainerNode() && + auto* text_node = DynamicTo<Text>(insertion_pos.ComputeContainerNode()); + if (text_node && insertion_pos.OffsetInContainerNode() && !insertion_pos.AtLastEditingPositionForNode()) { - SplitTextNode(ToText(insertion_pos.ComputeContainerNode()), - insertion_pos.OffsetInContainerNode()); + SplitTextNode(text_node, insertion_pos.OffsetInContainerNode()); insertion_pos = Position::FirstPositionInNode(*insertion_pos.ComputeContainerNode()); } @@ -1708,8 +1706,8 @@ Position end_upstream = MostBackwardCaretPosition(end_of_inserted_content.DeepEquivalent()); Node* end_node = end_upstream.ComputeNodeBeforePosition(); - int end_offset = - end_node && end_node->IsTextNode() ? ToText(end_node)->length() : 0; + auto* end_text_node = DynamicTo<Text>(end_node); + int end_offset = end_text_node ? end_text_node->length() : 0; if (end_upstream.IsOffsetInAnchor()) { end_node = end_upstream.ComputeContainerNode(); end_offset = end_upstream.OffsetInContainerNode(); @@ -1723,8 +1721,8 @@ bool collapse_white_space = !end_node->GetLayoutObject() || end_node->GetLayoutObject()->Style()->CollapseWhiteSpace(); - if (end_node->IsTextNode()) { - InsertTextIntoNode(ToText(end_node), end_offset, + if (auto* end_text_node = DynamicTo<Text>(end_node)) { + InsertTextIntoNode(end_text_node, end_offset, collapse_white_space ? NonBreakingSpaceString() : " "); if (end_of_inserted_content_.ComputeContainerNode() == end_node) end_of_inserted_content_ = Position( @@ -1763,8 +1761,8 @@ bool collapse_white_space = !start_node->GetLayoutObject() || start_node->GetLayoutObject()->Style()->CollapseWhiteSpace(); - if (start_node->IsTextNode()) { - InsertTextIntoNode(ToText(start_node), start_offset, + if (auto* start_text_node = DynamicTo<Text>(start_node)) { + InsertTextIntoNode(start_text_node, start_offset, collapse_white_space ? NonBreakingSpaceString() : " "); if (end_of_inserted_content_.ComputeContainerNode() == start_node && end_of_inserted_content_.OffsetInContainerNode()) @@ -1852,19 +1850,17 @@ bool position_only_to_be_updated_is_offset_in_anchor = position_only_to_be_updated.IsOffsetInAnchor(); Text* text = nullptr; - if (position_is_offset_in_anchor && position.ComputeContainerNode() && - position.ComputeContainerNode()->IsTextNode()) { - text = ToText(position.ComputeContainerNode()); - } else { - Node* before = position.ComputeNodeBeforePosition(); - if (before && before->IsTextNode()) { - text = ToText(before); - } else { - Node* after = position.ComputeNodeAfterPosition(); - if (after && after->IsTextNode()) - text = ToText(after); - } + auto* container_text_node = DynamicTo<Text>(position.ComputeContainerNode()); + if (position_is_offset_in_anchor && container_text_node) { + text = container_text_node; + } else if (auto* before = + DynamicTo<Text>(position.ComputeNodeBeforePosition())) { + text = before; + } else if (auto* after = + DynamicTo<Text>(position.ComputeNodeAfterPosition())) { + text = after; } + if (!text) return; @@ -1878,8 +1874,7 @@ U16_IS_LEAD(text->data()[text->data().length() - 1])); if (!has_incomplete_surrogate && text->data().length() > kMergeSizeLimit) return; - if (text->previousSibling() && text->previousSibling()->IsTextNode()) { - Text* previous = ToText(text->previousSibling()); + if (auto* previous = DynamicTo<Text>(text->previousSibling())) { if (has_incomplete_surrogate || previous->data().length() <= kMergeSizeLimit) { InsertTextIntoNode(text, 0, previous->data()); @@ -1966,8 +1961,9 @@ // list items and insert these nodes between them. if (is_middle) { int text_node_offset = insert_pos.OffsetInContainerNode(); - if (insert_pos.AnchorNode()->IsTextNode() && text_node_offset > 0) - SplitTextNode(ToText(insert_pos.AnchorNode()), text_node_offset); + auto* text_node = DynamicTo<Text>(insert_pos.AnchorNode()); + if (text_node && text_node_offset > 0) + SplitTextNode(text_node, text_node_offset); SplitTreeToNode(insert_pos.AnchorNode(), last_node, true); } @@ -2036,7 +2032,7 @@ Node* node_after_insertion_pos = MostForwardCaretPosition(EndingSelection().End()).AnchorNode(); - Text* text_node = ToText(fragment.FirstChild()); + auto* text_node = To<Text>(fragment.FirstChild()); // Our fragment creation code handles tabs, spaces, and newlines, so we don't // have to worry about those here.
diff --git a/third_party/blink/renderer/core/editing/commands/set_character_data_command_test.cc b/third_party/blink/renderer/core/editing/commands/set_character_data_command_test.cc index 6852ce3..fc1cf77 100644 --- a/third_party/blink/renderer/core/editing/commands/set_character_data_command_test.cc +++ b/third_party/blink/renderer/core/editing/commands/set_character_data_command_test.cc
@@ -16,51 +16,53 @@ SetBodyContent("<div contenteditable>This is a good test case</div>"); SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>( - ToText(GetDocument().body()->firstChild()->firstChild()), 10, 4, "lame"); + To<Text>(GetDocument().body()->firstChild()->firstChild()), 10, 4, + "lame"); command->DoReapply(); EXPECT_EQ( "This is a lame test case", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); command->DoUnapply(); EXPECT_EQ( "This is a good test case", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); } TEST_F(SetCharacterDataCommandTest, replaceTextWithLongerText) { SetBodyContent("<div contenteditable>This is a good test case</div>"); SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>( - ToText(GetDocument().body()->firstChild()->firstChild()), 10, 4, "lousy"); + To<Text>(GetDocument().body()->firstChild()->firstChild()), 10, 4, + "lousy"); command->DoReapply(); EXPECT_EQ( "This is a lousy test case", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); command->DoUnapply(); EXPECT_EQ( "This is a good test case", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); } TEST_F(SetCharacterDataCommandTest, replaceTextWithShorterText) { SetBodyContent("<div contenteditable>This is a good test case</div>"); SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>( - ToText(GetDocument().body()->firstChild()->firstChild()), 10, 4, "meh"); + To<Text>(GetDocument().body()->firstChild()->firstChild()), 10, 4, "meh"); command->DoReapply(); EXPECT_EQ( "This is a meh test case", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); command->DoUnapply(); EXPECT_EQ( "This is a good test case", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); } TEST_F(SetCharacterDataCommandTest, insertTextIntoEmptyNode) { @@ -70,52 +72,53 @@ GetDocument().CreateEditingTextNode("")); SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>( - ToText(GetDocument().body()->firstChild()->firstChild()), 0, 0, "hello"); + To<Text>(GetDocument().body()->firstChild()->firstChild()), 0, 0, + "hello"); command->DoReapply(); EXPECT_EQ( "hello", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); command->DoUnapply(); EXPECT_EQ( "", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); } TEST_F(SetCharacterDataCommandTest, insertTextAtEndOfNonEmptyNode) { SetBodyContent("<div contenteditable>Hello</div>"); SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>( - ToText(GetDocument().body()->firstChild()->firstChild()), 5, 0, + To<Text>(GetDocument().body()->firstChild()->firstChild()), 5, 0, ", world!"); command->DoReapply(); EXPECT_EQ( "Hello, world!", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); command->DoUnapply(); EXPECT_EQ( "Hello", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); } TEST_F(SetCharacterDataCommandTest, replaceEntireNode) { SetBodyContent("<div contenteditable>Hello</div>"); SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>( - ToText(GetDocument().body()->firstChild()->firstChild()), 0, 5, "Bye"); + To<Text>(GetDocument().body()->firstChild()->firstChild()), 0, 5, "Bye"); command->DoReapply(); EXPECT_EQ( "Bye", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); command->DoUnapply(); EXPECT_EQ( "Hello", - ToText(GetDocument().body()->firstChild()->firstChild())->wholeText()); + To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText()); } TEST_F(SetCharacterDataCommandTest, CombinedText) { @@ -123,7 +126,7 @@ "<div contenteditable style='writing-mode:vertical-lr; " "-webkit-text-combine:horizontal' />"); - Text* text_node = ToText(GetDocument().body()->firstChild()->appendChild( + auto* text_node = To<Text>(GetDocument().body()->firstChild()->appendChild( GetDocument().CreateEditingTextNode(""))); UpdateAllLifecyclePhasesForTest();
diff --git a/third_party/blink/renderer/core/editing/commands/split_text_node_command_test.cc b/third_party/blink/renderer/core/editing/commands/split_text_node_command_test.cc index a8786bc..83fe25745 100644 --- a/third_party/blink/renderer/core/editing/commands/split_text_node_command_test.cc +++ b/third_party/blink/renderer/core/editing/commands/split_text_node_command_test.cc
@@ -32,13 +32,13 @@ range, TextMatchMarker::MatchStatus::kInactive); SimpleEditCommand* command = MakeGarbageCollected<SplitTextNodeCommand>( - ToText(GetDocument().body()->firstChild()->firstChild()), 8); + To<Text>(GetDocument().body()->firstChild()->firstChild()), 8); EditingState editingState; command->DoApply(&editingState); - const Text& text1 = ToText(*div->firstChild()); - const Text& text2 = ToText(*text1.nextSibling()); + const Text& text1 = To<Text>(*div->firstChild()); + const Text& text2 = To<Text>(*text1.nextSibling()); // The first marker should end up in text1, the second marker should be // truncated and end up text1, the third marker should end up in text2 @@ -60,7 +60,7 @@ // Test undo command->DoUnapply(); - const Text& text = ToText(*div->firstChild()); + const Text& text = To<Text>(*div->firstChild()); EXPECT_EQ(3u, GetDocument().Markers().MarkersFor(text).size()); @@ -78,8 +78,8 @@ // Test redo command->DoReapply(); - const Text& text3 = ToText(*div->firstChild()); - const Text& text4 = ToText(*text3.nextSibling()); + const Text& text3 = To<Text>(*div->firstChild()); + const Text& text4 = To<Text>(*text3.nextSibling()); EXPECT_EQ(2u, GetDocument().Markers().MarkersFor(text3).size());
diff --git a/third_party/blink/renderer/core/editing/editing_style_utilities.cc b/third_party/blink/renderer/core/editing/editing_style_utilities.cc index 1dd9498..ebfeb55 100644 --- a/third_party/blink/renderer/core/editing/editing_style_utilities.cc +++ b/third_party/blink/renderer/core/editing/editing_style_utilities.cc
@@ -153,10 +153,10 @@ // want Position("world", 0) instead. // We only do this for range because caret at Position("hello", 5) in // <b>hello</b>world should give you font-weight: bold. - Node* position_node = position.ComputeContainerNode(); - if (selection.IsRange() && position_node && position_node->IsTextNode() && + auto* position_node = DynamicTo<Text>(position.ComputeContainerNode()); + if (selection.IsRange() && position_node && position.ComputeOffsetInContainerNode() == - static_cast<int>(ToText(position_node)->length())) + static_cast<int>(position_node->length())) position = NextVisuallyDistinctCandidate(position); Element* element = AssociatedElementOf(position);
diff --git a/third_party/blink/renderer/core/editing/editing_utilities.cc b/third_party/blink/renderer/core/editing/editing_utilities.cc index f64beba..ce0457c 100644 --- a/third_party/blink/renderer/core/editing/editing_utilities.cc +++ b/third_party/blink/renderer/core/editing/editing_utilities.cc
@@ -803,9 +803,10 @@ int PreviousGraphemeBoundaryOf(const Node& node, int current) { // TODO(yosin): Need to support grapheme crossing |Node| boundary. DCHECK_GE(current, 0); - if (current <= 1 || !node.IsTextNode()) + auto* text_node = DynamicTo<Text>(node); + if (current <= 1 || !text_node) return current - 1; - const String& text = ToText(node).data(); + const String& text = text_node->data(); // TODO(yosin): Replace with DCHECK for out-of-range request. if (static_cast<unsigned>(current) > text.length()) return current - 1; @@ -817,19 +818,21 @@ DCHECK_GE(current, 0); if (current <= 1) return 0; - if (!node.IsTextNode()) + auto* text_node = DynamicTo<Text>(node); + if (!text_node) return current - 1; - const String& text = ToText(node).data(); + const String& text = text_node->data(); DCHECK_LT(static_cast<unsigned>(current - 1), text.length()); return FindNextBoundaryOffset<BackspaceStateMachine>(text, current); } int NextGraphemeBoundaryOf(const Node& node, int current) { // TODO(yosin): Need to support grapheme crossing |Node| boundary. - if (!node.IsTextNode()) + auto* text_node = DynamicTo<Text>(node); + if (!text_node) return current + 1; - const String& text = ToText(node).data(); + const String& text = text_node->data(); const int length = text.length(); DCHECK_LE(current, length); if (current >= length - 1)
diff --git a/third_party/blink/renderer/core/editing/editor.cc b/third_party/blink/renderer/core/editing/editor.cc index 21d575b..beb4b73 100644 --- a/third_party/blink/renderer/core/editing/editor.cc +++ b/third_party/blink/renderer/core/editing/editor.cc
@@ -154,11 +154,11 @@ // character is not a space, but typing another space will do. Position prev = PreviousPositionOf(position, PositionMoveType::kGraphemeCluster); - const Node* prev_node = prev.ComputeContainerNode(); - if (!prev_node || !prev_node->IsTextNode()) + const auto* prev_node = DynamicTo<Text>(prev.ComputeContainerNode()); + if (!prev_node) return false; int prev_offset = prev.ComputeOffsetInContainerNode(); - UChar prev_char = ToText(prev_node)->data()[prev_offset]; + UChar prev_char = prev_node->data()[prev_offset]; return prev_char == kSpaceCharacter; }
diff --git a/third_party/blink/renderer/core/editing/editor_test.cc b/third_party/blink/renderer/core/editing/editor_test.cc index 941d900..50bc89f 100644 --- a/third_party/blink/renderer/core/editing/editor_test.cc +++ b/third_party/blink/renderer/core/editing/editor_test.cc
@@ -113,7 +113,7 @@ const SelectionInDOMTree selection = SetSelectionTextToBody( "<div contenteditable><div></div><b>^abc|</b></div>"); Selection().SetSelection(selection, SetSelectionOptions()); - Text& abc = ToText(*selection.Base().ComputeContainerNode()); + auto& abc = To<Text>(*selection.Base().ComputeContainerNode()); // Push Text node "abc" into undo stack GetDocument().execCommand("italic", false, "", ASSERT_NO_EXCEPTION); // Change Text node "abc" in undo stack
diff --git a/third_party/blink/renderer/core/editing/element_inner_text.cc b/third_party/blink/renderer/core/editing/element_inner_text.cc index 2d176eeb..1979656 100644 --- a/third_party/blink/renderer/core/editing/element_inner_text.cc +++ b/third_party/blink/renderer/core/editing/element_inner_text.cc
@@ -291,8 +291,9 @@ } // 4. If node is a Text node, then for each CSS text box produced by node. - if (node.IsTextNode()) - return ProcessTextNode(ToText(node)); + auto* text_node = DynamicTo<Text>(node); + if (text_node) + return ProcessTextNode(*text_node); // 5. If node is a br element, then append a string containing a single U+000A // LINE FEED (LF) character to items.
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer.cc b/third_party/blink/renderer/core/editing/finder/find_buffer.cc index 68390d28..77df41b7 100644 --- a/third_party/blink/renderer/core/editing/finder/find_buffer.cc +++ b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
@@ -399,17 +399,17 @@ continue; } // This node is in its own sub-block separate from our starting position. - if (first_traversed_node != node && !node->IsTextNode() && + const auto* text_node = DynamicTo<Text>(node); + if (first_traversed_node != node && !text_node && IsBlock(style->Display())) { break; } - if (style->Visibility() == EVisibility::kVisible && node->IsTextNode() && + if (style->Visibility() == EVisibility::kVisible && text_node && node->GetLayoutObject()) { - const Text& text_node = ToText(*node); LayoutBlockFlow& block_flow = *NGOffsetMapping::GetInlineFormattingContextOf( - *text_node.GetLayoutObject()); + *text_node->GetLayoutObject()); if (last_block_flow && last_block_flow != block_flow) { // We enter another block flow. break; @@ -417,7 +417,7 @@ if (!last_block_flow) { last_block_flow = &block_flow; } - AddTextToBuffer(text_node, block_flow, range); + AddTextToBuffer(*text_node, block_flow, range); } if (node == end_node) { node = FlatTreeTraversal::Next(*node);
diff --git a/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc b/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc index 5177fdf9..90d4f1da 100644 --- a/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc +++ b/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc
@@ -1477,16 +1477,16 @@ PlainTextRange(2).CreateRange(*div).StartPosition(); const Position& second_line_position = PlainTextRange(8).CreateRange(*div).StartPosition(); - ASSERT_EQ(0u, - GetDocument() - .Markers() - .MarkersFor(ToText(*first_line_position.ComputeContainerNode())) - .size()); ASSERT_EQ( - 1u, GetDocument() + 0u, GetDocument() .Markers() - .MarkersFor(ToText(*second_line_position.ComputeContainerNode())) + .MarkersFor(To<Text>(*first_line_position.ComputeContainerNode())) .size()); + ASSERT_EQ(1u, GetDocument() + .Markers() + .MarkersFor( + To<Text>(*second_line_position.ComputeContainerNode())) + .size()); // Verify marker has correct start/end offsets (measured from the beginning // of the node, which is the beginning of the line) @@ -1628,7 +1628,7 @@ // either space around it EXPECT_EQ( 1u, - GetDocument().Markers().MarkersFor(ToText(*div->firstChild())).size()); + GetDocument().Markers().MarkersFor(To<Text>(*div->firstChild())).size()); EXPECT_STREQ("text", GetMarkedText(GetDocument().Markers(), div->firstChild(), 0) .Utf8() @@ -2450,8 +2450,8 @@ Controller().SetComposition("test", ime_text_spans, 0, 4); Node* b = div->firstChild(); - Text* text1 = ToText(b->firstChild()); - Text* text2 = ToText(b->nextSibling()); + auto* text1 = To<Text>(b->firstChild()); + auto* text2 = To<Text>(b->nextSibling()); const DocumentMarkerVector& text1_markers = GetDocument().Markers().MarkersFor( @@ -2482,7 +2482,7 @@ Controller().SetComposition("t", Vector<ImeTextSpan>(), 0, 1); EXPECT_EQ(1u, div->CountChildren()); - Text* text = ToText(div->firstChild()); + auto* text = To<Text>(div->firstChild()); EXPECT_EQ("t", text->data()); } @@ -2498,7 +2498,7 @@ Controller().SetComposition("t", Vector<ImeTextSpan>(), 0, 1); EXPECT_EQ(1u, div->CountChildren()); - Text* text = ToText(div->firstChild()); + auto* text = To<Text>(div->firstChild()); EXPECT_EQ("t", text->data()); } @@ -2515,7 +2515,7 @@ Controller().SetComposition("t", Vector<ImeTextSpan>(), 0, 1); EXPECT_EQ(1u, div->CountChildren()); - Text* text = ToText(div->firstChild()); + auto* text = To<Text>(div->firstChild()); EXPECT_EQ("t", text->data()); } @@ -2572,7 +2572,7 @@ Vector<ImeTextSpan>(), 1, 1); EXPECT_EQ(1u, div->CountChildren()); - Text* text = ToText(div->firstChild()); + auto* text = To<Text>(div->firstChild()); EXPECT_STREQ("\xE0\xAE\x9A\xE0\xAF\x8D\xE0\xAE\x9A", text->data().Utf8().data());
diff --git a/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc b/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc index f589536..e3fdfb2e 100644 --- a/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc +++ b/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc
@@ -241,7 +241,7 @@ const int text_length = position_end_offset - position_start_offset; const int text_offset = position_start_offset - offset_in_node; CHECK_LE(static_cast<unsigned>(text_offset + text_length), text.length()); - text_state_.EmitText(ToText(*node_), position_start_offset, + text_state_.EmitText(To<Text>(*node_), position_start_offset, position_end_offset, text, text_offset, text_offset + text_length); return !should_handle_first_letter_;
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc index e04a90b..2e84763a 100644 --- a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc +++ b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
@@ -477,7 +477,7 @@ DCHECK_NE(last_text_node_, node_) << "We should never call HandleTextNode on the same node twice"; - const Text* text = ToText(node_); + const auto* text = To<Text>(node_.Get()); last_text_node_ = text; // TODO(editing-dev): Introduce a |DOMOffsetRange| class so that we can pass @@ -919,9 +919,9 @@ return PositionTemplate<Strategy>( node, text_state_.PositionStartOffset() + char16_offset); } - if (node.IsTextNode()) { + if (auto* text_node = DynamicTo<Text>(node)) { if (text_state_.IsAfterPositionNode()) - return PositionTemplate<Strategy>(node, ToText(node).length()); + return PositionTemplate<Strategy>(node, text_node->length()); return PositionTemplate<Strategy>(node, 0); } if (text_state_.IsAfterPositionNode()) @@ -950,10 +950,10 @@ return PositionTemplate<Strategy>( node, text_state_.PositionStartOffset() + char16_offset + 1); } - if (node.IsTextNode()) { + if (auto* text_node = DynamicTo<Text>(node)) { if (text_state_.IsBeforePositionNode()) return PositionTemplate<Strategy>(node, 0); - return PositionTemplate<Strategy>(node, ToText(node).length()); + return PositionTemplate<Strategy>(node, text_node->length()); } if (text_state_.IsBeforePositionNode()) return PositionTemplate<Strategy>::BeforeNode(node);
diff --git a/third_party/blink/renderer/core/editing/layout_selection.cc b/third_party/blink/renderer/core/editing/layout_selection.cc index 5cbe5c5..e45524c 100644 --- a/third_party/blink/renderer/core/editing/layout_selection.cc +++ b/third_party/blink/renderer/core/editing/layout_selection.cc
@@ -457,12 +457,13 @@ static base::Optional<unsigned> ComputeEndOffset( const Node& node, const PositionInFlatTree& selection_end) { - if (!node.IsTextNode()) + auto* text_node = DynamicTo<Text>(node); + if (!text_node) return base::nullopt; if (&node == selection_end.AnchorNode()) return selection_end.OffsetInContainerNode(); - return ToText(node).length(); + return text_node->length(); } #if DCHECK_IS_ON() @@ -597,7 +598,7 @@ if (const LayoutTextFragment* fragment = ToLayoutTextFragmentOrNull(text)) return fragment->AssociatedTextNode(); if (Node* node = text.GetNode()) - return ToTextOrNull(node); + return DynamicTo<Text>(node); return nullptr; }
diff --git a/third_party/blink/renderer/core/editing/layout_selection_test.cc b/third_party/blink/renderer/core/editing/layout_selection_test.cc index 83c9b67..28b1d5b 100644 --- a/third_party/blink/renderer/core/editing/layout_selection_test.cc +++ b/third_party/blink/renderer/core/editing/layout_selection_test.cc
@@ -81,7 +81,7 @@ std::ostream& ostream, const Node& node, wtf_size_t depth) { - if (const Text* text = ToTextOrNull(node)) + if (const Text* text = DynamicTo<Text>(node)) PrintText(ostream, *text); else if (const Element* element = ToElementOrNull(node)) ostream << element->tagName().Utf8().data(); @@ -981,8 +981,8 @@ const Text* GetFirstTextNode() { for (const Node& runner : NodeTraversal::StartsAt(*GetDocument().body())) { - if (runner.IsTextNode()) - return &ToText(runner); + if (auto* text_node = DynamicTo<Text>(runner)) + return text_node; } NOTREACHED(); return nullptr;
diff --git a/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc b/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc index 09c7724c..7df3613 100644 --- a/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc +++ b/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
@@ -227,11 +227,12 @@ return; DCHECK(!markers_.IsEmpty()); const Node& node = marked_text.CurrentContainer(); - if (!node.IsTextNode()) + auto* text_node = DynamicTo<Text>(node); + if (!text_node) continue; int start_offset = marked_text.StartOffsetInCurrentContainer(); int end_offset = marked_text.EndOffsetInCurrentContainer(); - RemoveMarkersInternal(ToText(node), start_offset, end_offset - start_offset, + RemoveMarkersInternal(*text_node, start_offset, end_offset - start_offset, marker_types); } } @@ -266,13 +267,13 @@ // Ignore text emitted by TextIterator for non-text nodes (e.g. implicit // newlines) - const Node& node = marked_text.CurrentContainer(); - if (!node.IsTextNode()) + const auto* text_node = DynamicTo<Text>(marked_text.CurrentContainer()); + if (!text_node) continue; DocumentMarker* const new_marker = create_marker_from_offsets( start_offset_in_current_container, end_offset_in_current_container); - AddMarkerToNode(ToText(node), new_marker); + AddMarkerToNode(*text_node, new_marker); } } @@ -423,15 +424,16 @@ const unsigned end_offset = end.ComputeOffsetInContainerNode(); for (const Node& node : EphemeralRangeInFlatTree(start, end).Nodes()) { - if (!node.IsTextNode()) + auto* text_node = DynamicTo<Text>(node); + if (!text_node) continue; const unsigned start_range_offset = node == start_node ? start_offset : 0; const unsigned end_range_offset = - node == end_node ? end_offset : ToText(node).length(); + node == end_node ? end_offset : text_node->length(); DocumentMarker* const found_marker = FirstMarkerIntersectingOffsetRange( - ToText(node), start_range_offset, end_range_offset, types); + *text_node, start_range_offset, end_range_offset, types); if (found_marker) return found_marker; } @@ -459,7 +461,8 @@ if (start_container != end_container) return nullptr; - if (!start_container->IsTextNode()) + auto* text_node = DynamicTo<Text>(start_container); + if (!text_node) return nullptr; const unsigned start_offset = @@ -467,8 +470,8 @@ const unsigned end_offset = range.EndPosition().ComputeOffsetInContainerNode(); - return FirstMarkerIntersectingOffsetRange(ToText(*start_container), - start_offset, end_offset, types); + return FirstMarkerIntersectingOffsetRange(*text_node, start_offset, + end_offset, types); } DocumentMarker* DocumentMarkerController::FirstMarkerIntersectingOffsetRange( @@ -524,9 +527,10 @@ range.EndPosition().ComputeOffsetInContainerNode(); for (Node& node : range.Nodes()) { - if (!node.IsTextNode()) + auto* text_node = DynamicTo<Text>(node); + if (!text_node) continue; - MarkerLists* const markers = markers_.at(&ToText(node)); + MarkerLists* const markers = markers_.at(text_node); if (!markers) continue; @@ -551,7 +555,7 @@ const DocumentMarkerVector& markers_from_this_list = list->MarkersIntersectingRange(start_offset, end_offset); for (DocumentMarker* marker : markers_from_this_list) - node_marker_pairs.push_back(std::make_pair(&ToText(node), marker)); + node_marker_pairs.push_back(std::make_pair(&To<Text>(node), marker)); } } @@ -925,12 +929,13 @@ bool marker_found = false; for (Node& node : range.Nodes()) { - if (!node.IsTextNode()) + auto* text_node = DynamicTo<Text>(node); + if (!text_node) continue; int start_offset = node == start_container ? container_start_offset : 0; int end_offset = node == end_container ? container_end_offset : INT_MAX; - marker_found |= SetTextMatchMarkersActive(ToText(node), start_offset, - end_offset, active); + marker_found |= + SetTextMatchMarkersActive(*text_node, start_offset, end_offset, active); } return marker_found; } @@ -996,9 +1001,10 @@ if (!PossiblyHasMarkers(DocumentMarker::MarkerTypes::All())) return; DCHECK(!markers_.IsEmpty()); - if (!node->IsTextNode()) + auto* text_node = DynamicTo<Text>(node); + if (!text_node) return; - MarkerLists* markers = markers_.at(ToText(node)); + MarkerLists* markers = markers_.at(text_node); if (!markers) return; @@ -1015,7 +1021,7 @@ return; if (!node->GetLayoutObject()) return; - InvalidateRectsForTextMatchMarkersInNode(ToText(*node)); + InvalidateRectsForTextMatchMarkersInNode(*text_node); InvalidatePaintForNode(*node); }
diff --git a/third_party/blink/renderer/core/editing/markers/document_marker_controller_test.cc b/third_party/blink/renderer/core/editing/markers/document_marker_controller_test.cc index 9478047..b86a0d3 100644 --- a/third_party/blink/renderer/core/editing/markers/document_marker_controller_test.cc +++ b/third_party/blink/renderer/core/editing/markers/document_marker_controller_test.cc
@@ -373,7 +373,8 @@ ASSERT_EQ(1u, MarkerController().Markers().size()); auto* marker = To<SuggestionMarker>(MarkerController().Markers()[0].Get()); - MarkerController().RemoveSuggestionMarkerByTag(*ToText(text), marker->Tag()); + MarkerController().RemoveSuggestionMarkerByTag(*To<Text>(text), + marker->Tag()); EXPECT_EQ(0u, MarkerController().Markers().size()); } @@ -397,7 +398,8 @@ const auto* marker = To<SuggestionMarker>(MarkerController().Markers()[0].Get()); - MarkerController().RemoveSuggestionMarkerByTag(*ToText(text), marker->Tag()); + MarkerController().RemoveSuggestionMarkerByTag(*To<Text>(text), + marker->Tag()); ASSERT_EQ(0u, MarkerController().Markers().size()); // Add a suggestion marker which need to be removed after finish composing, @@ -420,7 +422,7 @@ SetBodyContent("<div contenteditable>123456789</div>"); GetDocument().UpdateStyleAndLayout(); Element* div = GetDocument().QuerySelector("div"); - Text* text = ToText(div->firstChild()); + auto* text = To<Text>(div->firstChild()); // Add a spelling marker on "123" MarkerController().AddSpellingMarker( @@ -441,7 +443,7 @@ SetBodyContent("<div contenteditable>123456789</div>"); GetDocument().UpdateStyleAndLayout(); Element* div = GetDocument().QuerySelector("div"); - Text* text = ToText(div->firstChild()); + auto* text = To<Text>(div->firstChild()); // Add a spelling marker on "123" MarkerController().AddSpellingMarker(
diff --git a/third_party/blink/renderer/core/editing/selection_editor.cc b/third_party/blink/renderer/core/editing/selection_editor.cc index 63349454..a79885a1 100644 --- a/third_party/blink/renderer/core/editing/selection_editor.cc +++ b/third_party/blink/renderer/core/editing/selection_editor.cc
@@ -344,7 +344,8 @@ unsigned old_length = old_node.length(); if (position_offset <= old_length) return position; - return Position(ToText(old_node.nextSibling()), position_offset - old_length); + return Position(To<Text>(old_node.nextSibling()), + position_offset - old_length); } void SelectionEditor::DidSplitTextNode(const Text& old_node) {
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc index 15ca52a..91a4955e 100644 --- a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc +++ b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
@@ -153,7 +153,7 @@ void MarkupAccumulator::AppendStartMarkup(const Node& node) { switch (node.getNodeType()) { case Node::kTextNode: - formatter_.AppendText(markup_, ToText(node)); + formatter_.AppendText(markup_, To<Text>(node)); break; case Node::kElementNode: NOTREACHED();
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc index 89f7d6d5..e9b7a33 100644 --- a/third_party/blink/renderer/core/editing/serializers/serialization.cc +++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
@@ -757,11 +757,10 @@ void MergeWithNextTextNode(Text* text_node, ExceptionState& exception_state) { DCHECK(text_node); - Node* next = text_node->nextSibling(); - if (!next || !next->IsTextNode()) + auto* text_next = DynamicTo<Text>(text_node->nextSibling()); + if (!text_next) return; - Text* text_next = ToText(next); text_node->appendData(text_next->data()); if (text_next->parentNode()) // Might have been removed by mutation event. text_next->remove(exception_state);
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc index 9074d30..ed12147 100644 --- a/third_party/blink/renderer/core/html/media/html_media_element.cc +++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -55,6 +55,7 @@ #include "third_party/blink/renderer/core/dom/events/event.h" #include "third_party/blink/renderer/core/dom/events/event_queue.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" +#include "third_party/blink/renderer/core/events/keyboard_event.h" #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame_client.h" @@ -369,6 +370,10 @@ histogram.Count(static_cast<int>(reason)); } +bool IsValidPlaybackRate(double rate) { + return rate == 0.0 || (rate > kMinRate && rate < kMaxRate); +} + } // anonymous namespace MIMETypeRegistry::SupportsType HTMLMediaElement::GetSupportsType( @@ -2236,7 +2241,7 @@ if (GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream) return; - if (default_playback_rate_ == rate) + if (default_playback_rate_ == rate || !IsValidPlaybackRate(rate)) return; default_playback_rate_ = rate; @@ -2255,7 +2260,7 @@ if (GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream) return; - if (rate != 0.0 && (rate < kMinRate || rate > kMaxRate)) { + if (!IsValidPlaybackRate(rate)) { UseCounter::Count(GetDocument(), WebFeature::kHTMLMediaElementMediaPlaybackRateOutOfRange); @@ -4066,6 +4071,20 @@ PauseInternal(); } +void HTMLMediaElement::DefaultEventHandler(Event& event) { + if (event.IsKeyboardEvent() && ShouldShowControls()) { + const String& key = ToKeyboardEvent(event).key(); + if (key == "SoftRight") { + // We need to handle the event here rather than in + // MediaControlsTouchlessImpl because right soft key + // event is not sent to JS. + GetMediaControls()->ShowContextMenu(); + event.SetDefaultHandled(); + } + } + HTMLElement::DefaultEventHandler(event); +} + void HTMLMediaElement::AudioSourceProviderImpl::Wrap( WebAudioSourceProvider* provider) { MutexLocker locker(provide_input_lock);
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h index 1397814..5785932 100644 --- a/third_party/blink/renderer/core/html/media/html_media_element.h +++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -553,6 +553,8 @@ void OnRemovedFromDocumentTimerFired(TimerBase*); + void DefaultEventHandler(Event&) override; + TaskRunnerTimer<HTMLMediaElement> load_timer_; TaskRunnerTimer<HTMLMediaElement> progress_event_timer_; TaskRunnerTimer<HTMLMediaElement> playback_progress_timer_;
diff --git a/third_party/blink/renderer/core/html/media/media_controls.h b/third_party/blink/renderer/core/html/media/media_controls.h index 57c9b422..057adfa 100644 --- a/third_party/blink/renderer/core/html/media/media_controls.h +++ b/third_party/blink/renderer/core/html/media/media_controls.h
@@ -77,6 +77,10 @@ virtual HTMLDivElement* PanelElement() = 0; virtual void OnMediaControlsEnabledChange() = 0; + // This is required for showing a context menu upon pressing right soft key + // on a touchless device. + virtual void ShowContextMenu() = 0; + void Trace(Visitor*) override; private:
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc index 9ff24c3..7e6a637e 100644 --- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc +++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -808,12 +808,12 @@ CurrentTimeTicksInSeconds(), CurrentTime(), std::move(initiator_object), BuildObjectForResourceResponse(redirect_response), resource_type, std::move(maybe_frame_id), request.HasUserGesture()); + if (is_handling_sync_xhr_) + GetFrontend()->flush(); if (pending_xhr_replay_data_) { resources_data_->SetXHRReplayData(request_id, pending_xhr_replay_data_.Get()); - if (!pending_xhr_replay_data_->Async()) - GetFrontend()->flush(); pending_xhr_replay_data_.Clear(); } pending_request_type_ = base::nullopt; @@ -1057,6 +1057,7 @@ if (monotonic_finish_time.is_null()) monotonic_finish_time = CurrentTimeTicks(); + is_handling_sync_xhr_ = false; // TODO(npm): Use TimeTicks in Network.h. GetFrontend()->loadingFinished( request_id, monotonic_finish_time.since_origin().InSecondsF(), @@ -1086,6 +1087,7 @@ blocked_reason = BuildBlockedReason(resource_request_blocked_reason.value()); } + is_handling_sync_xhr_ = false; GetFrontend()->loadingFailed( request_id, CurrentTimeTicksInSeconds(), InspectorPageAgent::ResourceTypeJson( @@ -1124,6 +1126,9 @@ form_data ? form_data->DeepCopy() : nullptr, include_credentials); for (const auto& header : headers) pending_xhr_replay_data_->AddHeader(header.key, header.value); + DCHECK(!is_handling_sync_xhr_); + if (!async) + is_handling_sync_xhr_ = true; } void InspectorNetworkAgent::DidFinishXHR(XMLHttpRequest* xhr) {
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.h b/third_party/blink/renderer/core/inspector/inspector_network_agent.h index 71244f7..6be907a 100644 --- a/third_party/blink/renderer/core/inspector/inspector_network_agent.h +++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
@@ -277,6 +277,7 @@ base::Optional<InspectorPageAgent::ResourceType> pending_request_type_; Member<XHRReplayData> pending_xhr_replay_data_; + bool is_handling_sync_xhr_ = false; HashMap<String, std::unique_ptr<protocol::Network::Initiator>> frame_navigation_initiator_map_;
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc index 2012c88..e6f881d 100644 --- a/third_party/blink/renderer/core/layout/layout_box.cc +++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -2318,7 +2318,8 @@ scoped_refptr<const NGLayoutResult> LayoutBox::CachedLayoutResult( const NGConstraintSpace& new_space, - const NGBreakToken* break_token) { + const NGBreakToken* break_token, + base::Optional<NGFragmentGeometry>* initial_fragment_geometry) { if (!RuntimeEnabledFeatures::LayoutNGFragmentCachingEnabled()) return nullptr; @@ -2343,21 +2344,9 @@ return nullptr; NGBlockNode node(this); - if (!MaySkipLayout(node, *cached_layout_result, new_space)) + if (!MaySkipLayout(node, *cached_layout_result, new_space, + initial_fragment_geometry)) return nullptr; - // It is possible that our intrinsic size has changed; check for that here. - // TODO(cbiesinger): Move this to ::MaySkipLayout. - if (new_space.IsShrinkToFit() || NeedMinMaxSize(StyleRef())) { - NGBoxFragment fragment( - new_space.GetWritingMode(), StyleRef().Direction(), - To<NGPhysicalBoxFragment>(*cached_layout_result->PhysicalFragment())); - // If we get here, we know that border and padding haven't changed. - NGBoxStrut border_padding = fragment.Borders() + fragment.Padding(); - LayoutUnit size = - ComputeInlineSizeForFragment(new_space, node, border_padding); - if (size != fragment.InlineSize()) - return nullptr; - } const NGConstraintSpace& old_space = cached_layout_result->GetConstraintSpaceForCaching();
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h index 1bc47aa..e723b72 100644 --- a/third_party/blink/renderer/core/layout/layout_box.h +++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -43,6 +43,7 @@ class ShapeOutsideInfo; struct BoxLayoutExtraInput; class NGBreakToken; +struct NGFragmentGeometry; class NGLayoutResult; struct NGPhysicalBoxStrut; struct PaintInfo; @@ -913,9 +914,15 @@ // Returns the last layout result for this block flow with the given // constraint space and break token, or null if it is not up-to-date or // otherwise unavailable. + // + // This method (while determining if the layout result can be reused), *may* + // calculate the |initial_fragment_geometry| of the node. + // + // TODO(ikilpatrick): Move this function into NGBlockNode. scoped_refptr<const NGLayoutResult> CachedLayoutResult( const NGConstraintSpace&, - const NGBreakToken*); + const NGBreakToken*, + base::Optional<NGFragmentGeometry>* initial_fragment_geometry); void SetSpannerPlaceholder(LayoutMultiColumnSpannerPlaceholder&); void ClearSpannerPlaceholder();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc index 8d66c3c..4ad6699 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
@@ -28,7 +28,7 @@ has_last_resort_break_ = false; has_floating_descendants_ = false; has_orthogonal_flow_roots_ = false; - has_child_that_depends_on_percentage_block_size_ = false; + has_descendant_that_depends_on_percentage_block_size_ = false; has_block_fragmentation_ = false; may_have_descendant_above_block_start_ = false; }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc index 8305e292..5739245 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc
@@ -68,8 +68,9 @@ scoped_refptr<const NGLayoutResult> RunCachedLayoutResult( const NGConstraintSpace& space, const NGBlockNode& node) { + base::Optional<NGFragmentGeometry> initial_fragment_geometry; return To<LayoutBlockFlow>(node.GetLayoutBox()) - ->CachedLayoutResult(space, nullptr); + ->CachedLayoutResult(space, nullptr, &initial_fragment_geometry); } String DumpFragmentTree(const NGPhysicalBoxFragment* fragment) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc index 9186d43..3b3eca6 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -232,8 +232,9 @@ previous_result->GetConstraintSpaceForCaching().ExclusionSpace()); } - scoped_refptr<const NGLayoutResult> layout_result = - box_->CachedLayoutResult(constraint_space, break_token); + base::Optional<NGFragmentGeometry> fragment_geometry; + scoped_refptr<const NGLayoutResult> layout_result = box_->CachedLayoutResult( + constraint_space, break_token, &fragment_geometry); if (layout_result) { // We may have to update the margins on box_; we reuse the layout result // even if a percentage margin may have changed. @@ -248,17 +249,20 @@ return layout_result; } + if (!fragment_geometry) { + fragment_geometry = + CalculateInitialFragmentGeometry(constraint_space, *this); + } + PrepareForLayout(); - NGFragmentGeometry fragment_geometry = - CalculateInitialFragmentGeometry(constraint_space, *this); - NGLayoutAlgorithmParams params(*this, fragment_geometry, constraint_space, + NGLayoutAlgorithmParams params(*this, *fragment_geometry, constraint_space, To<NGBlockBreakToken>(break_token)); layout_result = LayoutWithAlgorithm(params); FinishLayout(block_flow, constraint_space, break_token, layout_result); NGBoxStrut after_layout_scrollbars = GetScrollbarSizes(); - if (fragment_geometry.scrollbar != after_layout_scrollbars) { + if (fragment_geometry->scrollbar != after_layout_scrollbars) { // If our scrollbars have changed, we need to relayout because either: // - Our size has changed (if shrinking to fit), or // - Space available to our children has changed. @@ -898,7 +902,8 @@ // We need to force a layout on the child if the constraint space given will // change the layout. bool needs_force_relayout = - layout_result && !MaySkipLayout(*this, *layout_result, constraint_space); + layout_result && + !MaySkipLegacyLayout(*this, *layout_result, constraint_space); if (box_->NeedsLayout() || !layout_result || needs_force_relayout) { BoxLayoutExtraInput input(*box_);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h index c635639..1be6878c 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h +++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -61,6 +61,7 @@ const NGFragmentGeometry& initial_fragment_geometry) { initial_fragment_geometry_ = &initial_fragment_geometry; size_ = initial_fragment_geometry_->border_box_size; + is_initial_block_size_indefinite_ = size_.block_size == kIndefiniteSize; return *this; } @@ -237,6 +238,7 @@ NGPhysicalFragment::NGBoxType box_type_; bool is_fieldset_container_ = false; + bool is_initial_block_size_indefinite_ = false; bool did_break_; bool has_forced_break_ = false; bool is_new_fc_ = false;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h index ef77aa0..410c459 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h +++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
@@ -60,19 +60,16 @@ public: enum ConstraintSpaceFlags { kOrthogonalWritingModeRoot = 1 << 0, - kFixedSizeInline = 1 << 1, - kFixedSizeBlock = 1 << 2, - kFixedSizeBlockIsDefinite = 1 << 3, - kShrinkToFit = 1 << 4, - kIntermediateLayout = 1 << 5, - kSeparateLeadingFragmentainerMargins = 1 << 6, - kNewFormattingContext = 1 << 7, - kAnonymous = 1 << 8, - kUseFirstLineStyle = 1 << 9, - kForceClearance = 1 << 10, + kFixedSizeBlockIsDefinite = 1 << 1, + kIntermediateLayout = 1 << 2, + kSeparateLeadingFragmentainerMargins = 1 << 3, + kNewFormattingContext = 1 << 4, + kAnonymous = 1 << 5, + kUseFirstLineStyle = 1 << 6, + kForceClearance = 1 << 7, // Size of bitfield used to store the flags. - kNumberOfConstraintSpaceFlags = 11 + kNumberOfConstraintSpaceFlags = 8 }; // To ensure that the bfc_offset_, rare_data_ union doesn't get polluted, @@ -286,9 +283,9 @@ // // If these flags are true, the AvailableSize() is interpreted as the fixed // border-box size of this box in the respective dimension. - bool IsFixedSizeInline() const { return HasFlag(kFixedSizeInline); } + bool IsFixedSizeInline() const { return bitfields_.is_fixed_size_inline; } - bool IsFixedSizeBlock() const { return HasFlag(kFixedSizeBlock); } + bool IsFixedSizeBlock() const { return bitfields_.is_fixed_size_block; } // Whether a fixed block size should be considered definite. bool FixedSizeBlockIsDefinite() const { @@ -297,7 +294,7 @@ // Whether an auto inline-size should be interpreted as shrink-to-fit // (ie. fit-content). This is used for inline-block, floats, etc. - bool IsShrinkToFit() const { return HasFlag(kShrinkToFit); } + bool IsShrinkToFit() const { return bitfields_.is_shrink_to_fit; } // Whether this constraint space is used for an intermediate layout in a // multi-pass layout. In such a case, we should not copy back the resulting @@ -436,6 +433,12 @@ return other.rare_data_->IsInitialForMaySkipLayout(); } + // Returns true if the size constraints (shrink-to-fit, fixed-inline-size) + // are equal. + bool AreSizeConstraintsEqual(const NGConstraintSpace& other) const { + return bitfields_.AreSizeConstraintsEqual(other.bitfields_); + } + bool AreSizesEqual(const NGConstraintSpace& other) const { if (available_size_ != other.available_size_) return false; @@ -553,29 +556,44 @@ explicit Bitfields(WritingMode writing_mode) : has_rare_data(false), - table_cell_child_layout_phase(static_cast<unsigned>( - NGTableCellChildLayoutPhase::kNotTableCellChild)), adjoining_floats(static_cast<unsigned>(kFloatTypeNone)), writing_mode(static_cast<unsigned>(writing_mode)), direction(static_cast<unsigned>(TextDirection::kLtr)), + is_shrink_to_fit(false), + is_fixed_size_inline(false), + is_fixed_size_block(false), + table_cell_child_layout_phase(static_cast<unsigned>( + NGTableCellChildLayoutPhase::kNotTableCellChild)), flags(kFixedSizeBlockIsDefinite), percentage_inline_storage(kSameAsAvailable), percentage_block_storage(kSameAsAvailable), replaced_percentage_block_storage(kSameAsAvailable) {} bool MaySkipLayout(const Bitfields& other) const { - return table_cell_child_layout_phase == - other.table_cell_child_layout_phase && - adjoining_floats == other.adjoining_floats && + return adjoining_floats == other.adjoining_floats && writing_mode == other.writing_mode && flags == other.flags && baseline_requests == other.baseline_requests; } + bool AreSizeConstraintsEqual(const Bitfields& other) const { + return is_shrink_to_fit == other.is_shrink_to_fit && + is_fixed_size_inline == other.is_fixed_size_inline && + is_fixed_size_block == other.is_fixed_size_block && + table_cell_child_layout_phase == + other.table_cell_child_layout_phase; + } + unsigned has_rare_data : 1; - unsigned table_cell_child_layout_phase : 2; // NGTableCellChildLayoutPhase unsigned adjoining_floats : 2; // NGFloatTypes unsigned writing_mode : 3; unsigned direction : 1; + + // Size constraints. + unsigned is_shrink_to_fit : 1; + unsigned is_fixed_size_inline : 1; + unsigned is_fixed_size_block : 1; + unsigned table_cell_child_layout_phase : 2; // NGTableCellChildLayoutPhase + unsigned flags : kNumberOfConstraintSpaceFlags; // ConstraintSpaceFlags unsigned baseline_requests : NGBaselineRequestList::kSerializedBits;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h index 0274906..676a5340 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h +++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
@@ -123,18 +123,18 @@ NGConstraintSpaceBuilder& SetIsFixedSizeInline(bool b) { if (LIKELY(is_in_parallel_flow_)) - SetFlag(NGConstraintSpace::kFixedSizeInline, b); + space_.bitfields_.is_fixed_size_inline = b; else - SetFlag(NGConstraintSpace::kFixedSizeBlock, b); + space_.bitfields_.is_fixed_size_block = b; return *this; } NGConstraintSpaceBuilder& SetIsFixedSizeBlock(bool b) { if (LIKELY(is_in_parallel_flow_)) - SetFlag(NGConstraintSpace::kFixedSizeBlock, b); + space_.bitfields_.is_fixed_size_block = b; else - SetFlag(NGConstraintSpace::kFixedSizeInline, b); + space_.bitfields_.is_fixed_size_inline = b; return *this; } @@ -147,7 +147,7 @@ } NGConstraintSpaceBuilder& SetIsShrinkToFit(bool b) { - SetFlag(NGConstraintSpace::kShrinkToFit, b); + space_.bitfields_.is_shrink_to_fit = b; return *this; }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc index 46d38c2..f6bb83ca9 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -83,7 +83,7 @@ // percentages against the "final" size of their parent. if (child.DependsOnPercentageBlockSize() && !child.PhysicalFragment()->IsOutOfFlowPositioned()) - has_child_that_depends_on_percentage_block_size_ = true; + has_descendant_that_depends_on_percentage_block_size_ = true; if (child.MayHaveDescendantAboveBlockStart() && !child.PhysicalFragment()->IsBlockFormattingContextRoot())
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h index ab2c77f62..5553bba9 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h +++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -245,7 +245,7 @@ bool has_last_resort_break_ = false; bool has_floating_descendants_ = false; bool has_orthogonal_flow_roots_ = false; - bool has_child_that_depends_on_percentage_block_size_ = false; + bool has_descendant_that_depends_on_percentage_block_size_ = false; bool has_block_fragmentation_ = false; bool may_have_descendant_above_block_start_ = false; };
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h index d2ee13a..7ce4d13 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
@@ -97,6 +97,9 @@ bool IsBody() const { return IsBlock() && box_->IsBody(); } bool IsDocumentElement() const { return box_->IsDocumentElement(); } bool IsFlexItem() const { return IsBlock() && box_->IsFlexItemIncludingNG(); } + bool IsFlexibleBox() const { + return IsBlock() && box_->IsFlexibleBoxIncludingNG(); + } bool ShouldBeConsideredAsReplaced() const { return box_->ShouldBeConsideredAsReplaced(); }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc index 8456c05..efc8e3c 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
@@ -18,6 +18,8 @@ scoped_refptr<const NGPhysicalFragment> physical_fragment, NGBoxFragmentBuilder* builder) : NGLayoutResult(builder, /* cache_space */ true) { + is_initial_block_size_indefinite_ = + builder->is_initial_block_size_indefinite_; intrinsic_block_size_ = builder->intrinsic_block_size_; minimal_space_shortage_ = builder->minimal_space_shortage_; initial_break_before_ = builder->initial_break_before_; @@ -41,6 +43,7 @@ : NGLayoutResult(builder, /* cache_space */ false) { adjoining_floats_ = kFloatTypeNone; depends_on_percentage_block_size_ = false; + has_descendant_that_depends_on_percentage_block_size_ = false; status_ = status; DCHECK_NE(status, kSuccess) << "Use the other constructor for successful layout"; @@ -69,11 +72,15 @@ has_forced_break_(other.has_forced_break_), is_pushed_by_floats_(other.is_pushed_by_floats_), adjoining_floats_(other.adjoining_floats_), + is_initial_block_size_indefinite_( + other.is_initial_block_size_indefinite_), has_orthogonal_flow_roots_(other.has_orthogonal_flow_roots_), may_have_descendant_above_block_start_( other.may_have_descendant_above_block_start_), depends_on_percentage_block_size_( other.depends_on_percentage_block_size_), + has_descendant_that_depends_on_percentage_block_size_( + other.has_descendant_that_depends_on_percentage_block_size_), status_(other.status_) {} NGLayoutResult::NGLayoutResult(NGContainerFragmentBuilder* builder, @@ -90,10 +97,13 @@ has_forced_break_(false), is_pushed_by_floats_(builder->is_pushed_by_floats_), adjoining_floats_(builder->adjoining_floats_), + is_initial_block_size_indefinite_(false), has_orthogonal_flow_roots_(builder->has_orthogonal_flow_roots_), may_have_descendant_above_block_start_( builder->may_have_descendant_above_block_start_), depends_on_percentage_block_size_(DependsOnPercentageBlockSize(*builder)), + has_descendant_that_depends_on_percentage_block_size_( + builder->has_descendant_that_depends_on_percentage_block_size_), status_(kSuccess) {} // Define the destructor here, so that we can forward-declare more in the @@ -105,7 +115,7 @@ NGLayoutInputNode node = builder.node_; if (!node || node.IsInline()) - return builder.has_child_that_depends_on_percentage_block_size_; + return builder.has_descendant_that_depends_on_percentage_block_size_; // NOTE: If an element is OOF positioned, and has top/bottom constraints // which are percentage based, this function will return false. @@ -118,7 +128,7 @@ // element if it has a percentage block-size however, but this will return // the correct result from below. - if ((builder.has_child_that_depends_on_percentage_block_size_ || + if ((builder.has_descendant_that_depends_on_percentage_block_size_ || builder.is_legacy_layout_root_) && node.UseParentPercentageResolutionBlockSizeForChildren()) { // Quirks mode has different %-block-size behaviour, than standards mode.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h index ffa5734..21140c3 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
@@ -115,12 +115,27 @@ bool HasOrthogonalFlowRoots() const { return has_orthogonal_flow_roots_; } + // Returns true if the initial (pre-layout) block-size of this fragment was + // indefinite. (e.g. it has "height: auto"). + bool IsInitialBlockSizeIndefinite() const { + return is_initial_block_size_indefinite_; + } + // Returns true if we aren't able to re-use this layout result if the // PercentageResolutionBlockSize changes. bool DependsOnPercentageBlockSize() const { return depends_on_percentage_block_size_; } + // Returns true if there is a descendant that depends on percentage + // resolution block-size changes. + // Some layout modes (flex-items, table-cells) have more complex child + // percentage sizing behaviour (typically when their parent layout forces a + // block-size on them). + bool HasDescendantThatDependsOnPercentageBlockSize() const { + return has_descendant_that_depends_on_percentage_block_size_; + } + // Returns true if we have a descendant within this formatting context, which // is potentially above our block-start edge. bool MayHaveDescendantAboveBlockStart() const { @@ -219,9 +234,11 @@ unsigned is_pushed_by_floats_ : 1; unsigned adjoining_floats_ : 2; // NGFloatTypes + unsigned is_initial_block_size_indefinite_ : 1; unsigned has_orthogonal_flow_roots_ : 1; unsigned may_have_descendant_above_block_start_ : 1; unsigned depends_on_percentage_block_size_ : 1; + unsigned has_descendant_that_depends_on_percentage_block_size_ : 1; unsigned status_ : 1; };
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc index 2ee0d2b75..23ae3a4 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h" @@ -42,10 +43,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_NE(result.get(), nullptr); EXPECT_EQ(result->BfcBlockOffset().value(), LayoutUnit(50)); @@ -82,10 +84,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_NE(result.get(), nullptr); EXPECT_EQ(result->BfcBlockOffset().value(), LayoutUnit(40)); @@ -143,10 +146,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_EQ(result.get(), nullptr); } @@ -180,10 +184,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_EQ(result.get(), nullptr); } @@ -215,10 +220,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_EQ(result.get(), nullptr); } @@ -250,10 +256,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_EQ(result.get(), nullptr); } @@ -284,10 +291,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_EQ(result.get(), nullptr); } @@ -318,10 +326,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_EQ(result.get(), nullptr); } @@ -352,10 +361,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_NE(result.get(), nullptr); } @@ -386,10 +396,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_NE(result.get(), nullptr); } @@ -421,10 +432,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_EQ(result.get(), nullptr); } @@ -456,10 +468,11 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_EQ(result.get(), nullptr); } @@ -484,10 +497,244 @@ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + base::Optional<NGFragmentGeometry> fragment_geometry; const NGConstraintSpace& space = src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); scoped_refptr<const NGLayoutResult> result = - test->CachedLayoutResult(space, nullptr); + test->CachedLayoutResult(space, nullptr, &fragment_geometry); + + EXPECT_NE(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, HitPercentageMinWidth) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // min-width calculates to different values, but doesn't change size. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .inflow { width: 100px; min-width: 25%; } + </style> + <div class="bfc"> + <div id="test" class="inflow"></div> + </div> + <div class="bfc" style="width: 200px; height: 200px;"> + <div id="src" class="inflow"></div> + </div> + )HTML"); + + auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); + auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + + base::Optional<NGFragmentGeometry> fragment_geometry; + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr, &fragment_geometry); + + EXPECT_NE(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, HitFixedMinWidth) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // min-width is always larger than the available size. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .inflow { min-width: 300px; } + </style> + <div class="bfc"> + <div id="test" class="inflow"></div> + </div> + <div class="bfc" style="width: 200px; height: 200px;"> + <div id="src" class="inflow"></div> + </div> + )HTML"); + + auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); + auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + + base::Optional<NGFragmentGeometry> fragment_geometry; + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr, &fragment_geometry); + + EXPECT_NE(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, HitShrinkToFitSameIntrinsicSizes) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // We have a shrink-to-fit node, with the min, and max intrinsic sizes being + // equal (the available size doesn't affect the final size). + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .shrink { width: fit-content; } + .child { width: 250px; } + </style> + <div class="bfc"> + <div id="test" class="shrink"> + <div class="child"></div> + </div> + </div> + <div class="bfc" style="width: 200px; height: 200px;"> + <div id="src" class="shrink"> + <div class="child"></div> + </div> + </div> + )HTML"); + + auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); + auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + + base::Optional<NGFragmentGeometry> fragment_geometry; + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr, &fragment_geometry); + + EXPECT_NE(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, HitShrinkToFitDifferentParent) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // The parent "bfc" node changes from shrink-to-fit, to a fixed width. But + // these calculate as the same available space to the "test" element. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; } + .child { width: 250px; } + </style> + <div class="bfc" style="width: fit-content; height: 100px;"> + <div id="test"> + <div class="child"></div> + </div> + </div> + <div class="bfc" style="width: 250px; height: 100px;"> + <div id="src"> + <div class="child"></div> + </div> + </div> + )HTML"); + + auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); + auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + + base::Optional<NGFragmentGeometry> fragment_geometry; + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr, &fragment_geometry); + + EXPECT_NE(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, MissQuirksModePercentageBasedChild) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Quirks-mode %-block-size child. + GetDocument().SetCompatibilityMode(Document::kQuirksMode); + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .child { height: 50%; } + </style> + <div class="bfc"> + <div id="test"> + <div class="child"></div> + </div> + </div> + <div class="bfc" style="height: 200px;"> + <div id="src"> + <div class="child"></div> + </div> + </div> + )HTML"); + + auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); + auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + + base::Optional<NGFragmentGeometry> fragment_geometry; + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr, &fragment_geometry); + + EXPECT_EQ(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, HitQuirksModePercentageBasedParentAndChild) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Quirks-mode %-block-size parent *and* child. Here we mark the parent as + // depending on %-block-size changes, however itself doesn't change in + // height. + // We are able to hit the cache as we detect that the height for the child + // *isn't* indefinite, and results in the same height as before. + GetDocument().SetCompatibilityMode(Document::kQuirksMode); + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .parent { height: 50%; min-height: 200px; } + .child { height: 50%; } + </style> + <div class="bfc"> + <div id="test" class="parent"> + <div class="child"></div> + </div> + </div> + <div class="bfc" style="height: 200px;"> + <div id="src" class="parent"> + <div class="child"></div> + </div> + </div> + )HTML"); + + auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); + auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + + base::Optional<NGFragmentGeometry> fragment_geometry; + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr, &fragment_geometry); + + EXPECT_NE(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, HitStandardsModePercentageBasedChild) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Standards-mode %-block-size child. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .child { height: 50%; } + </style> + <div class="bfc"> + <div id="test"> + <div class="child"></div> + </div> + </div> + <div class="bfc" style="height: 200px;"> + <div id="src"> + <div class="child"></div> + </div> + </div> + )HTML"); + + auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test")); + auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src")); + + base::Optional<NGFragmentGeometry> fragment_geometry; + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr, &fragment_geometry); EXPECT_NE(result.get(), nullptr); }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc index fce051a0d..78c7aa7f 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc
@@ -4,15 +4,23 @@ #include "third_party/blink/renderer/core/layout/ng/ng_layout_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" -#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" namespace blink { namespace { +// LengthResolveType indicates what type length the function is being passed +// based on its CSS property. E.g. +// kMinSize - min-width / min-height +// kMaxSize - max-width / max-height +// kMainSize - width / height +enum class LengthResolveType { kMinSize, kMaxSize, kMainSize }; + bool ContentShrinkToFitMayChange(const ComputedStyle& style, const NGConstraintSpace& new_space, const NGConstraintSpace& old_space, @@ -100,6 +108,8 @@ type == LengthResolveType::kMainSize && (new_space.IsShrinkToFit() || length.IsFitContent()); + // TODO(ikilpatrick): Test if we can remove this optimization now that we + // compute the initial size of the fragment. if (is_content_shrink_to_fit) { return ContentShrinkToFitMayChange(style, new_space, old_space, layout_result); @@ -127,18 +137,27 @@ old_space.AvailableSize().block_size) return true; } + return false; } // Return true if it's possible (but not necessarily guaranteed) that the new // constraint space will give a different size compared to the old one, when // computed style and child content remain unchanged. -bool SizeMayChange(const ComputedStyle& style, +bool SizeMayChange(const NGBlockNode& node, const NGConstraintSpace& new_space, const NGConstraintSpace& old_space, const NGLayoutResult& layout_result) { + if (node.IsQuirkyAndFillsViewport()) + return true; + DCHECK_EQ(new_space.IsFixedSizeInline(), old_space.IsFixedSizeInline()); DCHECK_EQ(new_space.IsFixedSizeBlock(), old_space.IsFixedSizeBlock()); + DCHECK_EQ(new_space.IsShrinkToFit(), old_space.IsShrinkToFit()); + DCHECK_EQ(new_space.TableCellChildLayoutPhase(), + old_space.TableCellChildLayoutPhase()); + + const ComputedStyle& style = node.Style(); // Go through all length properties, and, depending on length type // (percentages, auto, etc.), check whether the constraint spaces differ in @@ -203,11 +222,129 @@ return false; } +// Return true if the new constraint space will produce a different sized +// fragment. This will also return true if any %-block-size children will +// change size. +bool SizeWillChange(const NGBlockNode& node, + const NGFragmentGeometry& fragment_geometry, + const NGLayoutResult& layout_result, + const NGConstraintSpace& new_space, + const NGConstraintSpace& old_space) { + const ComputedStyle& style = node.Style(); + NGBoxFragment fragment( + style.GetWritingMode(), style.Direction(), + To<NGPhysicalBoxFragment>(*layout_result.PhysicalFragment())); + + if (fragment_geometry.border_box_size.inline_size != fragment.InlineSize()) + return true; + + LayoutUnit block_size = fragment_geometry.border_box_size.block_size; + bool is_initial_block_size_indefinite = block_size == kIndefiniteSize; + if (is_initial_block_size_indefinite) { + // The intrinsic size of column flex-boxes can depend on the + // %-resolution-block-size. This occurs when a flex-box has "max-height: + // 100%" or similar on itself. + // + // Due to this we can't use cached |NGLayoutResult::IntrinsicBlockSize| + // value, as the following |block_size| calculation would be incorrect. + if (node.IsFlexibleBox() && style.IsColumnFlexDirection() && + layout_result.DependsOnPercentageBlockSize()) { + if (new_space.PercentageResolutionBlockSize() != + old_space.PercentageResolutionBlockSize()) + return true; + } + + block_size = ComputeBlockSizeForFragment( + new_space, style, fragment_geometry.border + fragment_geometry.padding, + layout_result.IntrinsicBlockSize()); + } + + if (block_size != fragment.BlockSize()) + return true; + + if (layout_result.HasDescendantThatDependsOnPercentageBlockSize()) { + // %-block-size children of flex-items sometimes don't resolve their + // percentages against a fixed block-size. + // We miss the cache if the %-resolution block-size changes from indefinite + // to definite (or visa-versa). + bool is_new_initial_block_size_indefinite = + new_space.IsFixedSizeBlock() ? !new_space.FixedSizeBlockIsDefinite() + : is_initial_block_size_indefinite; + + bool is_old_initial_block_size_indefinite = + old_space.IsFixedSizeBlock() + ? !old_space.FixedSizeBlockIsDefinite() + : layout_result.IsInitialBlockSizeIndefinite(); + + if (is_old_initial_block_size_indefinite != + is_new_initial_block_size_indefinite) + return true; + + // %-block-size children of table-cells have different behaviour if they + // are in the "measure" or "layout" phase. + // Instead of trying to capture that logic here, we always miss the cache. + if (node.IsTableCell() && + new_space.IsFixedSizeBlock() != old_space.IsFixedSizeBlock()) + return true; + + // At this point we must have the same block-size for our fragment, so this + // is really only checking if any %-block-size children will change size. + // This is checks for the quirks-mode %-block-size behaviour. + // + // The |NGLayoutResult::DependsOnPercentageBlockSize| flag will returns true + // if we are in quirks mode, and have a descendant that depends on a + // percentage block-size, however it will also return true if the node + // itself depends on the %-block-size. + // + // We remove this false-positive by checking if we have an initial + // indefinite block-size. + if (is_new_initial_block_size_indefinite && + layout_result.DependsOnPercentageBlockSize()) { + DCHECK(is_old_initial_block_size_indefinite); + if (new_space.PercentageResolutionBlockSize() != + old_space.PercentageResolutionBlockSize()) + return true; + if (new_space.ReplacedPercentageResolutionBlockSize() != + old_space.ReplacedPercentageResolutionBlockSize()) + return true; + } + } + + if (style.MayHavePadding() && fragment_geometry.padding != fragment.Padding()) + return true; + + return false; +} + +bool IntrinsicSizeWillChange( + const NGBlockNode& node, + const NGLayoutResult& cached_layout_result, + const NGConstraintSpace& new_space, + base::Optional<NGFragmentGeometry>* fragment_geometry) { + const ComputedStyle& style = node.Style(); + if (!new_space.IsShrinkToFit() && !NeedMinMaxSize(style)) + return false; + + if (!*fragment_geometry) + *fragment_geometry = CalculateInitialFragmentGeometry(new_space, node); + + NGBoxFragment fragment( + style.GetWritingMode(), style.Direction(), + To<NGPhysicalBoxFragment>(*cached_layout_result.PhysicalFragment())); + + if ((*fragment_geometry)->border_box_size.inline_size != + fragment.InlineSize()) + return true; + + return false; +} + } // namespace -bool MaySkipLayout(const NGBlockNode node, +bool MaySkipLayout(const NGBlockNode& node, const NGLayoutResult& cached_layout_result, - const NGConstraintSpace& new_space) { + const NGConstraintSpace& new_space, + base::Optional<NGFragmentGeometry>* fragment_geometry) { DCHECK_EQ(cached_layout_result.Status(), NGLayoutResult::kSuccess); DCHECK(cached_layout_result.HasValidConstraintSpaceForCaching()); @@ -216,18 +353,53 @@ if (!new_space.MaySkipLayout(old_space)) return false; - if (!new_space.AreSizesEqual(old_space)) { - // We need to descend all the way down into BODY if we're in quirks mode, - // since it magically follows the viewport size. - if (node.IsQuirkyAndFillsViewport()) + if (new_space.AreSizeConstraintsEqual(old_space)) { + // It is possible that our intrinsic size has changed, check for that here. + // TODO(cbiesinger): Investigate why this check doesn't apply to + // |MaySkipLegacyLayout|. + if (IntrinsicSizeWillChange(node, cached_layout_result, new_space, + fragment_geometry)) return false; - // If the available / percentage sizes have changed in a way that may affect - // layout, we cannot re-use the previous result. - if (SizeMayChange(node.Style(), new_space, old_space, cached_layout_result)) - return false; + // We don't have to check our style if we know the constraint space sizes + // will remain the same. + if (new_space.AreSizesEqual(old_space)) + return true; + + if (!SizeMayChange(node, new_space, old_space, cached_layout_result)) + return true; } + if (!*fragment_geometry) + *fragment_geometry = CalculateInitialFragmentGeometry(new_space, node); + + if (SizeWillChange(node, **fragment_geometry, cached_layout_result, new_space, + old_space)) + return false; + + return true; +} + +bool MaySkipLegacyLayout(const NGBlockNode& node, + const NGLayoutResult& cached_layout_result, + const NGConstraintSpace& new_space) { + DCHECK_EQ(cached_layout_result.Status(), NGLayoutResult::kSuccess); + DCHECK(cached_layout_result.HasValidConstraintSpaceForCaching()); + + const NGConstraintSpace& old_space = + cached_layout_result.GetConstraintSpaceForCaching(); + if (!new_space.MaySkipLayout(old_space)) + return false; + + if (!new_space.AreSizeConstraintsEqual(old_space)) + return false; + + if (new_space.AreSizesEqual(old_space)) + return true; + + if (SizeMayChange(node, new_space, old_space, cached_layout_result)) + return false; + return true; }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h index 1a880d9..486cddb8 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h
@@ -15,9 +15,16 @@ // Returns true if for a given |new_space|, the |node| will provide the same // |NGLayoutResult| as |cached_layout_result|, and therefore might be able to // skip layout. -bool MaySkipLayout(const NGBlockNode node, +bool MaySkipLayout(const NGBlockNode& node, const NGLayoutResult& cached_layout_result, - const NGConstraintSpace& new_space); + const NGConstraintSpace& new_space, + base::Optional<NGFragmentGeometry>* fragment_geometry); + +// Similar to |MaySkipLayout| but for legacy layout roots. Doesn't attempt to +// pre-compute the geometry of the fragment. +bool MaySkipLegacyLayout(const NGBlockNode& node, + const NGLayoutResult& cached_layout_result, + const NGConstraintSpace& new_space); // Return true if layout is considered complete. In some cases we require more // than one layout pass.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h index fefcd6c..07ab2f59 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h +++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
@@ -31,13 +31,6 @@ // intrinsic sizes pass, and kLayout must be used during the layout pass. enum class LengthResolvePhase { kIntrinsic, kLayout }; -// LengthResolveType indicates what type length the function is being passed -// based on its CSS property. E.g. -// kMinSize - min-width / min-height -// kMaxSize - max-width / max-height -// kMainSize - width / height -enum class LengthResolveType { kMinSize, kMaxSize, kMainSize }; - inline bool NeedMinMaxSize(const ComputedStyle& style) { // This check is technically too broad (fill-available does not need intrinsic // size computation) but that's a rare case and only affects performance, not
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl.h b/third_party/blink/renderer/modules/media_controls/media_controls_impl.h index c660d99..36f8f881 100644 --- a/third_party/blink/renderer/modules/media_controls/media_controls_impl.h +++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl.h
@@ -119,6 +119,7 @@ // There is no update because only the overlay is expected to change. RefreshCastButtonVisibilityWithoutUpdate(); } + void ShowContextMenu() override {} // Called by the fullscreen buttons to toggle fulllscreen on/off. void EnterFullscreen();
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc index 3efbc4c..4542382 100644 --- a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc +++ b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc
@@ -16,19 +16,31 @@ SetShadowPseudoId( AtomicString("-internal-media-controls-touchless-bottom-container")); - MediaControlsTouchlessTimeDisplayElement* time_display_element = + time_display_element_ = MakeGarbageCollected<MediaControlsTouchlessTimeDisplayElement>( media_controls); - MediaControlsTouchlessTimelineElement* timeline_element = + timeline_element_ = MakeGarbageCollected<MediaControlsTouchlessTimelineElement>( media_controls); - ParserAppendChild(time_display_element); - ParserAppendChild(timeline_element); + ParserAppendChild(time_display_element_); + ParserAppendChild(timeline_element_); +} + +LayoutObject* +MediaControlsTouchlessBottomContainerElement::TimelineLayoutObject() { + return timeline_element_->GetLayoutObject(); +} + +LayoutObject* +MediaControlsTouchlessBottomContainerElement::TimeDisplayLayoutObject() { + return time_display_element_->GetLayoutObject(); } void MediaControlsTouchlessBottomContainerElement::Trace( blink::Visitor* visitor) { + visitor->Trace(timeline_element_); + visitor->Trace(time_display_element_); MediaControlsTouchlessElement::Trace(visitor); }
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.h b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.h index bfe01bc..34d04de 100644 --- a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.h +++ b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.h
@@ -10,12 +10,21 @@ namespace blink { class MediaControlsTouchlessImpl; +class MediaControlsTouchlessTimelineElement; +class MediaControlsTouchlessTimeDisplayElement; +class LayoutObject; class MediaControlsTouchlessBottomContainerElement : public MediaControlsTouchlessElement { public: MediaControlsTouchlessBottomContainerElement(MediaControlsTouchlessImpl&); + LayoutObject* TimelineLayoutObject(); + LayoutObject* TimeDisplayLayoutObject(); void Trace(blink::Visitor*) override; + + private: + Member<MediaControlsTouchlessTimelineElement> timeline_element_; + Member<MediaControlsTouchlessTimeDisplayElement> time_display_element_; }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc index f7bb2b8..7a2c2a8 100644 --- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc +++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
@@ -14,6 +14,8 @@ #include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/events/keyboard_event.h" #include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/frame/local_frame_client.h" +#include "third_party/blink/renderer/core/fullscreen/fullscreen.h" #include "third_party/blink/renderer/core/html/media/html_media_element.h" #include "third_party/blink/renderer/core/html/media/html_video_element.h" #include "third_party/blink/renderer/core/html/track/text_track.h" @@ -141,6 +143,22 @@ SetInlineStyleProperty(CSSPropertyID::kDisplay, CSSValueID::kNone); } +LayoutObject* MediaControlsTouchlessImpl::PanelLayoutObject() { + return nullptr; +} + +LayoutObject* MediaControlsTouchlessImpl::TimelineLayoutObject() { + return bottom_container_->TimelineLayoutObject(); +} + +LayoutObject* MediaControlsTouchlessImpl::ButtonPanelLayoutObject() { + return bottom_container_->TimeDisplayLayoutObject(); +} + +LayoutObject* MediaControlsTouchlessImpl::ContainerLayoutObject() { + return GetLayoutObject(); +} + MediaControlsTouchlessMediaEventListener& MediaControlsTouchlessImpl::MediaEventListener() const { return *media_event_listener_; @@ -272,7 +290,42 @@ } void MediaControlsTouchlessImpl::OnMediaMenuResult( - mojom::blink::MenuResponsePtr reponse) {} + mojom::blink::MenuResponsePtr response) { + if (response.is_null()) + return; + + switch (response->clicked) { + case mojom::blink::MenuItem::FULLSCREEN: + if (MediaElement().IsFullscreen()) + Fullscreen::ExitFullscreen(GetDocument()); + else + Fullscreen::RequestFullscreen(MediaElement()); + break; + case mojom::blink::MenuItem::MUTE: + MediaElement().setMuted(!MediaElement().muted()); + break; + case mojom::blink::MenuItem::DOWNLOAD: + Download(); + break; + case mojom::blink::MenuItem::CAPTIONS: + text_track_manager_->DisableShowingTextTracks(); + if (response->track_index >= 0) + text_track_manager_->ShowTextTrackAtIndex(response->track_index); + break; + } +} + +void MediaControlsTouchlessImpl::Download() { + const KURL& url = MediaElement().currentSrc(); + if (url.IsNull() || url.IsEmpty()) + return; + ResourceRequest request(url); + request.SetSuggestedFilename(MediaElement().title()); + request.SetRequestContext(mojom::RequestContextType::DOWNLOAD); + request.SetRequestorOrigin(SecurityOrigin::Create(GetDocument().Url())); + GetDocument().GetFrame()->Client()->DownloadURL( + request, DownloadCrossOriginRedirects::kFollow); +} void MediaControlsTouchlessImpl::OnMediaControlsMenuHostConnectionError() { media_controls_host_.reset(); @@ -395,4 +448,9 @@ HTMLDivElement::Trace(visitor); } +void MediaControlsTouchlessImpl::OnMediaMenuResultForTest( + mojom::blink::MenuResponsePtr response) { + OnMediaMenuResult(std::move(response)); +} + } // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h index 0b13425d..14fc333 100644 --- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h +++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h
@@ -43,10 +43,11 @@ void OnControlsListUpdated() override {} void OnTrackElementFailedToLoad() override {} void NetworkStateChanged() override {} - LayoutObject* PanelLayoutObject() override { return nullptr; } - LayoutObject* TimelineLayoutObject() override { return nullptr; } - LayoutObject* ButtonPanelLayoutObject() override { return nullptr; } - LayoutObject* ContainerLayoutObject() override { return nullptr; } + LayoutObject* PanelLayoutObject() override; + LayoutObject* TimelineLayoutObject() override; + LayoutObject* ButtonPanelLayoutObject() override; + LayoutObject* ContainerLayoutObject() override; + void ShowContextMenu() override; void SetTestMode(bool) override {} HTMLDivElement* PanelElement() override { return nullptr; } void OnMediaControlsEnabledChange() override {} @@ -68,6 +69,9 @@ MediaControlsTouchlessMediaEventListener& MediaEventListener() const; + // Test functions + void OnMediaMenuResultForTest(mojom::blink::MenuResponsePtr); + void Trace(blink::Visitor*) override; private: @@ -87,13 +91,14 @@ void MaybeJump(int); void MaybeChangeVolume(double); + void Download(); + // Node bool IsMediaControls() const override { return true; } void EnsureMediaControlsMenuHost(); mojom::blink::VideoStatePtr GetVideoState(); WTF::Vector<mojom::blink::TextTrackMetadataPtr> GetTextTracks(); - void ShowContextMenu(); void OnMediaMenuResult(mojom::blink::MenuResponsePtr); void OnMediaControlsMenuHostConnectionError();
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc index d3bff22..9215321d 100644 --- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc +++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
@@ -19,6 +19,8 @@ #include "third_party/blink/renderer/core/html/media/html_media_test_helper.h" #include "third_party/blink/renderer/core/html/media/html_video_element.h" #include "third_party/blink/renderer/core/html/time_ranges.h" +#include "third_party/blink/renderer/core/html/track/text_track.h" +#include "third_party/blink/renderer/core/html/track/text_track_list.h" #include "third_party/blink/renderer/core/loader/empty_clients.h" #include "third_party/blink/renderer/core/testing/page_test_base.h" #include "third_party/blink/renderer/platform/keyboard_codes.h" @@ -124,6 +126,14 @@ chrome_client_->SetOrientation(orientation_type); } + void SimulateClickOnMenuItem(mojom::blink::MenuItem menu_item, + int track_index) { + mojom::blink::MenuResponsePtr response(mojom::blink::MenuResponse::New()); + response->clicked = menu_item; + response->track_index = track_index; + media_controls_->OnMediaMenuResultForTest(std::move(response)); + } + void CheckControlKeys(int seek_forward_key, int seek_backward_key, int volume_up_key, @@ -315,6 +325,48 @@ volume_bar_height / volume_bar_background_height, error); } +/** (jazzhsu@) TODO: Add mojom binding test and fix the following test. +TEST_F(MediaControlsTouchlessImplTest, ContextMenuTest) { + // Fullscreen buttom test. + EXPECT_FALSE(MediaElement().IsFullscreen()); + SimulateClickOnMenuItem(mojom::blink::MenuItem::FULLSCREEN, -1); + test::RunPendingTasks(); + EXPECT_TRUE(MediaElement().IsFullscreen()); + SimulateClickOnMenuItem(mojom::blink::MenuItem::FULLSCREEN, -1); + test::RunPendingTasks(); + EXPECT_FALSE(MediaElement().IsFullscreen()); + + // Mute buttom test. + EXPECT_FALSE(MediaElement().muted()); + SimulateClickOnMenuItem(mojom::blink::MenuItem::MUTE, -1); + EXPECT_TRUE(MediaElement().muted()); + SimulateClickOnMenuItem(mojom::blink::MenuItem::MUTE, -1); + EXPECT_FALSE(MediaElement().muted()); + + // Text track test. + TextTrack* track1 = MediaElement().addTextTrack("subtitle", "english", + "en", NASSERT_NO_EXCEPTION); + TextTrack* track2 = MediaElement().addTextTrack("subtitle", "english2", + "en", ASSERT_NO_EXCEPTION); + EXPECT_NE(track1->mode(), TextTrack::ShowingKeyword()); + EXPECT_NE(track2->mode(), TextTrack::ShowingKeyword()); + + // Select first track. + SimulateClickOnMenuItem(mojom::blink::MenuItem::CAPTIONS, 0); + EXPECT_EQ(track1->mode(), TextTrack::ShowingKeyword()); + + // Select second track. + SimulateClickOnMenuItem(mojom::blink::MenuItem::CAPTIONS, 1); + EXPECT_NE(track1->mode(), TextTrack::ShowingKeyword()); + EXPECT_EQ(track2->mode(), TextTrack::ShowingKeyword()); + + // Turn all tracks off. + SimulateClickOnMenuItem(mojom::blink::MenuItem::CAPTIONS, -1); + EXPECT_NE(track1->mode(), TextTrack::ShowingKeyword()); + EXPECT_NE(track2->mode(), TextTrack::ShowingKeyword()); +} +*/ + TEST_F(MediaControlsTouchlessImplTestWithMockScheduler, MidOverlayHideTimerTest) { Element* overlay =
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css b/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css index 4f50c83..1ef89f2 100644 --- a/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css +++ b/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css
@@ -213,3 +213,79 @@ video::-webkit-media-controls-touchless.inline div[pseudo="-internal-media-controls-touchless-volume-icon" i] { display: none; } + +/** + * Text Tracks + */ +video::-webkit-media-text-track-container { + position: relative; + width: inherit; + height: inherit; + overflow: hidden; + + font: 22px sans-serif; + text-align: center; + color: rgba(255, 255, 255, 1); + + letter-spacing: normal; + word-spacing: normal; + text-transform: none; + text-indent: 0; + text-decoration: none; + pointer-events: none; + -webkit-user-select: none; + word-break: break-word; +} + +video::cue { + display: inline; + + background-color: rgba(0, 0, 0, 0.8); +} + +video::-webkit-media-text-track-region { + position: absolute; + line-height: 5.33vh; + writing-mode: horizontal-tb; + background: rgba(0, 0, 0, 0.8); + color: rgba(255, 255, 255, 1); + word-wrap: break-word; + overflow-wrap: break-word; + overflow: hidden; +} + +video::-webkit-media-text-track-region-container { + position: relative; + + display: flex; + flex-flow: column; + flex-direction: column; +} + +video::-webkit-media-text-track-region-container.scrolling { + transition: top 433ms linear; +} + +video::-webkit-media-text-track-display { + position: absolute; + overflow: hidden; + white-space: pre-wrap; + -webkit-box-sizing: border-box; + flex: 0 0 auto; +} + +video::cue(:future) { + color: gray; +} + +video::cue(b) { + font-weight: bold; +} + +video::cue(u) { + text-decoration: underline; +} + +video::cue(i) { + font-style: italic; +}
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService b/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService index ff0cd3f..5f24440 100644 --- a/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService +++ b/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService
@@ -26,3 +26,4 @@ Bug(none) virtual/omt-worker-fetch [ Skip ] Bug(none) virtual/outofblink-cors [ Skip ] Bug(none) http/tests/inspector-protocol/fetch [ Skip ] +Bug(none) http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs.js [ Skip ]
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 567e9fd7..8730070 100644 --- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG +++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -280,7 +280,7 @@ crbug.com/591099 fast/css/outline-offset-large.html [ Failure ] crbug.com/855279 fast/css/text-overflow-ellipsis-vertical-hittest.html [ Pass ] crbug.com/591099 fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-under.html [ Failure ] -crbug.com/591099 fast/events/before-unload-return-value-from-listener.html [ Pass ] +crbug.com/591099 fast/events/before-unload-return-value-from-listener.html [ Pass Timeout ] crbug.com/591099 fast/events/touch/compositor-touch-hit-rects-continuation.html [ Failure ] crbug.com/591099 fast/events/touch/compositor-touch-hit-rects-list-translate.html [ Failure ] crbug.com/591099 fast/events/touch/compositor-touch-hit-rects.html [ Failure ] @@ -300,7 +300,7 @@ crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-replaced-003.html [ Pass ] crbug.com/591099 external/wpt/css/css-text/shaping/shaping-023.html [ Pass ] crbug.com/591099 external/wpt/css/css-text/white-space/control-chars-00C.html [ Pass ] -crbug.com/835484 fast/spatial-navigation/snav-iframe-with-offscreen-focusable-element.html [ Failure Pass ] +crbug.com/835484 fast/spatial-navigation/snav-iframe-with-offscreen-focusable-element.html [ Failure ] crbug.com/899902 fast/text/ellipsis-with-self-painting-layer.html [ Pass ] crbug.com/591099 fast/text/emoji-vertical-origin-visual.html [ Failure ] crbug.com/591099 fast/text/font-format-support-color-cff2-vertical.html [ Failure ] @@ -313,15 +313,17 @@ crbug.com/591099 http/tests/appcache/non-html.xhtml [ Crash Pass ] crbug.com/591099 http/tests/csspaint/invalidation-border-image.html [ Pass ] crbug.com/591099 http/tests/devtools/sources/debugger-frameworks/frameworks-jquery.js [ Crash Pass ] -crbug.com/591099 http/tests/devtools/sources/debugger/debugger-proto-property.js [ Pass Timeout ] +crbug.com/591099 http/tests/devtools/sources/debugger/debugger-proto-property.js [ Pass ] crbug.com/591099 http/tests/devtools/tracing-session-id.js [ Pass ] crbug.com/591099 http/tests/devtools/tracing/console-timeline.js [ Pass ] crbug.com/591099 http/tests/html/validation-bubble-oopif-clip.html [ Pass ] -crbug.com/591099 http/tests/media/autoplay/document-user-activation-cross-origin-feature-policy-disabled.html [ Failure ] +crbug.com/591099 http/tests/media/autoplay/document-user-activation-cross-origin-feature-policy-disabled.html [ Failure Pass ] crbug.com/591099 http/tests/media/video-load-metadata-decode-error.html [ Pass ] -crbug.com/591099 http/tests/security/inactive-document-with-empty-security-origin.html [ Pass Timeout ] +crbug.com/591099 http/tests/security/inactive-document-with-empty-security-origin.html [ Pass ] +crbug.com/591099 http/tests/security/mixedContent/insecure-css-resources.html [ Failure Pass ] crbug.com/591099 images/feature-policy-oversized-images-resize.html [ Pass ] crbug.com/591099 paint/invalidation/flexbox/scrollbars-changed.html [ Failure ] +crbug.com/591099 paint/invalidation/media-audio-no-spurious-repaints.html [ Failure ] crbug.com/835484 paint/invalidation/outline/inline-focus.html [ Failure ] crbug.com/591099 paint/invalidation/overflow/opacity-change-on-overflow-float.html [ Failure ] crbug.com/591099 paint/invalidation/scroll/fixed-under-composited-fixed-scrolled.html [ Failure ] @@ -349,7 +351,7 @@ crbug.com/591099 virtual/exotic-color-space/ [ Skip ] crbug.com/591099 virtual/fractional_scrolling_threaded/fast/scrolling/unscrollable-layer-subpixel-size-with-negative-overflow.html [ Failure Pass ] crbug.com/591099 virtual/gpu-rasterization/images/color-profile-image-filter-all.html [ Pass ] -crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Pass ] +crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Failure Pass ] crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-filter.html [ Pass ] crbug.com/591099 virtual/gpu/fast/canvas/canvas-blend-image.html [ Pass ] crbug.com/591099 virtual/gpu/fast/canvas/canvas-blending-color-over-pattern.html [ Pass ]
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs-expected.txt new file mode 100644 index 0000000..3450bb2 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs-expected.txt
@@ -0,0 +1,6 @@ +Tests that Network.requestWillBeSent is dispatched for redirects inside sync XHRs +Network.requestWillBeSent: http://127.0.0.1:8000/inspector-protocol/network/initial +Network.requestWillBeSent: http://127.0.0.1:8000/inspector-protocol/network/redirect1 +Network.requestWillBeSent: http://127.0.0.1:8000/inspector-protocol/network/redirect2 +sync XHR body: thisisxhrbody +
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs.js new file mode 100644 index 0000000..3920134 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs.js
@@ -0,0 +1,59 @@ +(async function(testRunner) { + const {page, session, dp} = await testRunner.startBlank( + `Tests that Network.requestWillBeSent is dispatched for redirects inside sync XHRs`); + + await dp.Network.enable(); + await dp.Fetch.enable(); + + dp.Network.onRequestWillBeSent(event => testRunner.log('Network.requestWillBeSent: ' + event.params.request.url)); + + const url = testRunner.url('./initial'); + + const evaluationPromise = session.evaluate(url => { + const request = new XMLHttpRequest(); + request.open('GET', url, false); // `false` makes the request synchronous + request.send(null); + return request.responseText; + }, url); + + // Wait for initial request. + const [interception1] = await Promise.all([ + dp.Fetch.onceRequestPaused(), + dp.Network.onceRequestWillBeSent(), + ]); + await dp.Fetch.fulfillRequest({ + requestId: interception1.params.requestId, + responseCode: 302, + responseHeaders: [ + {name: 'Location', value: testRunner.url('./redirect1')}, + ], + }); + + // First redirect should emit both Fetch.requestPaused and Network.requestWillBeSent. + const [interception2] = await Promise.all([ + dp.Fetch.onceRequestPaused(), + dp.Network.onceRequestWillBeSent(), + ]); + await dp.Fetch.fulfillRequest({ + requestId: interception2.params.requestId, + responseCode: 302, + responseHeaders: [ + {name: 'Location', value: testRunner.url('./redirect2')}, + ], + }); + + // Second redirect should emit both Fetch.requestPaused and Network.requestWillBeSent. + const [interception3] = await Promise.all([ + dp.Fetch.onceRequestPaused(), + dp.Network.onceRequestWillBeSent(), + ]); + await dp.Fetch.fulfillRequest({ + requestId: interception3.params.requestId, + responseCode: 200, + responseHeaders: [], + body: btoa('thisisxhrbody'), + }); + + testRunner.log('sync XHR body: ' + (await evaluationPromise)); + testRunner.completeTest(); +})
diff --git a/third_party/blink/web_tests/media/video-defaultplaybackrate.html b/third_party/blink/web_tests/media/video-defaultplaybackrate.html new file mode 100644 index 0000000..f28a052 --- /dev/null +++ b/third_party/blink/web_tests/media/video-defaultplaybackrate.html
@@ -0,0 +1,23 @@ +<!DOCTYPE html> +<title>Test playbackRate and defaultPlaybackRate.</title> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<video></video> +<script> +async_test(t => { + let video = document.querySelector("video"); + video.src = "content/test.ogv"; + + video.addEventListener('canplaythrough', t.step_func(() => { + assert_equals(video.defaultPlaybackRate, 1.0); + video.defaultPlaybackRate = Number.MAX_VALUE; + + assert_equals(video.defaultPlaybackRate, 1.0); + video.load(); + + video.addEventListener('canplaythrough', t.step_func_done(() => { + assert_equals(video.defaultPlaybackRate, 1.0); + }), { once: true }); + }), { once: true }); +}); +</script>
diff --git a/third_party/blink/web_tests/media/video-playbackrate.html b/third_party/blink/web_tests/media/video-playbackrate.html index 922a1a6..db15b09 100644 --- a/third_party/blink/web_tests/media/video-playbackrate.html +++ b/third_party/blink/web_tests/media/video-playbackrate.html
@@ -28,10 +28,10 @@ // Test extreme playback rates. video.defaultPlaybackRate = Number.MIN_VALUE; - assert_equals(video.defaultPlaybackRate, Number.MIN_VALUE); + assert_equals(video.defaultPlaybackRate, 2); video.defaultPlaybackRate = Number.MAX_VALUE; - assert_equals(video.defaultPlaybackRate, Number.MAX_VALUE); + assert_equals(video.defaultPlaybackRate, 2); assert_throws("NotSupportedError", function() { video.playbackRate = Number.MIN_VALUE; }); assert_throws("NotSupportedError", function() { video.playbackRate = Number.MAX_VALUE; }); @@ -79,4 +79,4 @@ video.src = "content/test.ogv"; }); -</script> \ No newline at end of file +</script>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index ff32836..6137ecb 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -32758,6 +32758,8 @@ <int value="-1385221197" label="AllowSignedHTTPExchangeCertsWithoutExtension:enabled"/> <int value="-1383597259" label="SyncUserConsentSeparateType:disabled"/> + <int value="-1383145700" + label="AutofillDoNotMigrateUnsupportedLocalCards:enabled"/> <int value="-1382671832" label="OmniboxUIExperimentVerticalMargin:enabled"/> <int value="-1377186702" label="DesktopIOSPromotion:disabled"/> <int value="-1376510363" label="ServiceWorkerScriptFullCodeCache:disabled"/> @@ -33400,6 +33402,8 @@ <int value="-385337473" label="enable-fast-unload"/> <int value="-384589459" label="disable-supervised-user-safesites"/> <int value="-381181808" label="DragAppsInTabletMode:enabled"/> + <int value="-379809954" + label="AutofillDoNotMigrateUnsupportedLocalCards:disabled"/> <int value="-378218969" label="VaapiJpegImageDecodeAcceleration:disabled"/> <int value="-378180863" label="disable-panels"/> <int value="-378033324" label="disable-win32k-renderer-lockdown"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index ab73af3..7f4f2ea 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -147121,6 +147121,17 @@ name="Net.QuicSession.ConnectionCloseErrorCodeServerGoogle"/> </histogram_suffixes> +<histogram_suffixes name="HandshakeNotConfirmed" separator="."> + <suffix name="HandshakeNotConfirmed" label="the handshake was confirmed"/> + <affected-histogram name="Net.QuicSession.ClosedByPublicReset"/> + <affected-histogram name="Net.QuicSession.ConnectionCloseErrorCodeClient"/> + <affected-histogram + name="Net.QuicSession.ConnectionCloseErrorCodeClientGoogle"/> + <affected-histogram name="Net.QuicSession.ConnectionCloseErrorCodeServer"/> + <affected-histogram + name="Net.QuicSession.ConnectionCloseErrorCodeServerGoogle"/> +</histogram_suffixes> + <histogram_suffixes name="HighDownloadBandwidth" separator="."> <suffix name="HighDownloadBandwidth" label="download with high bandwidth."/> <affected-histogram name="Download.Parallelizable.DownloadTime"/>
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp index 87241afa..70b5ecd 100644 --- a/ui/chromeos/file_manager_strings.grdp +++ b/ui/chromeos/file_manager_strings.grdp
@@ -77,6 +77,9 @@ <message name="IDS_FILE_BROWSER_MEDIA_VIEW_AUDIO_ROOT_LABEL" desc="A label for the 'Audio' root of media views."> Audio </message> + <message name="IDS_FILE_BROWSER_PLUGIN_VM_DIRECTORY_LABEL" desc="PluginVm local directory label."> + Plugin VM + </message> <message name="IDS_FILE_BROWSER_RECENT_ROOT_LABEL" desc="A label for the 'Recent' root which shows files recently modified by the user."> Recent </message>
diff --git a/ui/file_manager/file_manager/common/js/file_type.js b/ui/file_manager/file_manager/common/js/file_type.js index ba2337b..6f024c5 100644 --- a/ui/file_manager/file_manager/common/js/file_type.js +++ b/ui/file_manager/file_manager/common/js/file_type.js
@@ -620,6 +620,7 @@ const overrides = { [VolumeManagerCommon.RootType.DOWNLOADS]: { '/Downloads': VolumeManagerCommon.VolumeType.DOWNLOADS, + '/PluginVm': 'plugin_vm', }, }; const root = overrides[opt_rootType];
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js index ff75ff4..04c5145 100644 --- a/ui/file_manager/file_manager/common/js/util.js +++ b/ui/file_manager/file_manager/common/js/util.js
@@ -1151,11 +1151,15 @@ return util.getRootTypeLabel(locationInfo); } - // Special case for MyFiles/Downloads. - if (locationInfo && util.isMyFilesVolumeEnabled() && - locationInfo.rootType == VolumeManagerCommon.RootType.DOWNLOADS && - entry.fullPath == '/Downloads') { - return str('DOWNLOADS_DIRECTORY_LABEL'); + // Special case for MyFiles/Downloads and MyFiles/PluginVm. + if (locationInfo && + locationInfo.rootType == VolumeManagerCommon.RootType.DOWNLOADS) { + if (util.isMyFilesVolumeEnabled() && entry.fullPath == '/Downloads') { + return str('DOWNLOADS_DIRECTORY_LABEL'); + } + if (util.isPluginVmEnabled() && entry.fullPath == '/PluginVm') { + return str('PLUGIN_VM_DIRECTORY_LABEL'); + } } return entry.name; @@ -1510,6 +1514,12 @@ loadTimeData.getBoolean('MY_FILES_VOLUME_ENABLED'); }; +/** @return {boolean} */ +util.isPluginVmEnabled = () => { + return loadTimeData.valueExists('PLUGIN_VM_ENABLED') && + loadTimeData.getBoolean('PLUGIN_VM_ENABLED'); +}; + /** * Used for logs and debugging. It tries to tell what type is the entry, its * path and URL.
diff --git a/ui/file_manager/file_manager/foreground/css/file_types.css b/ui/file_manager/file_manager/foreground/css/file_types.css index a2a3b76..66d646b3 100644 --- a/ui/file_manager/file_manager/foreground/css/file_types.css +++ b/ui/file_manager/file_manager/foreground/css/file_types.css
@@ -529,3 +529,11 @@ url(../images/volumes/android_active.png) 1x, url(../images/volumes/2x/android_active.png) 2x); } + +[file-type-icon='plugin_vm'] { + background-image: url(../images/volumes/plugin_vm.svg); +} + +.tree-row[selected] [file-type-icon='plugin_vm'] { + background-image: url(../images/volumes/plugin_vm_active.svg); +}
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm.svg b/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm.svg new file mode 100644 index 0000000..7cdd3608 --- /dev/null +++ b/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm.svg
@@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" fill="rgb(90,90,90)"> + <path d="M7 12v4H4V4a1 1 0 0 1 1-1h8a3 3 0 0 1 3 3v3a3 3 0 0 1-3 3H7zm0-6v3h5a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H7z"/> +</svg>
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm_active.svg b/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm_active.svg new file mode 100644 index 0000000..66d714c --- /dev/null +++ b/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm_active.svg
@@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" fill="rgb(51,103,214)"> + <path d="M7 12v4H4V4a1 1 0 0 1 1-1h8a3 3 0 0 1 3 3v3a3 3 0 0 1-3 3H7zm0-6v3h5a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H7z"/> +</svg>
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js index 900ec81..8682728 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -272,12 +272,13 @@ }; /** - * If entry is MyFiles/Downloads, we don't allow cut/delete/rename. + * If entry is MyFiles/Downloads or MyFiles/PluginVm, we don't allow + * cut/delete/rename. * @param {!VolumeManager} volumeManager * @param {(Entry|FakeEntry)} entry Entry or a fake entry. * @return {boolean} */ -CommandUtil.isDownloads = (volumeManager, entry) => { +CommandUtil.isReadOnly = (volumeManager, entry) => { if (!entry) { return false; } @@ -295,10 +296,13 @@ return false; } - if (util.isMyFilesVolumeEnabled() && - volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS && - entry.fullPath === '/Downloads') { - return true; + if (volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS) { + if (util.isMyFilesVolumeEnabled() && entry.fullPath === '/Downloads') { + return true; + } + if (util.isPluginVmEnabled() && entry.fullPath === '/PluginVm') { + return true; + } } return false; }; @@ -1031,7 +1035,7 @@ /** * Returns True if any entry belongs to a read-only volume or is - * MyFiles>Downloads. + * forced to be read-only like MyFiles>Downloads. * @param {!Array<!Entry>} entries * @param {!CommandHandlerDeps} fileManager * @return {boolean} True if entries contain read only entry. @@ -1040,7 +1044,7 @@ return entries.some(entry => { const locationInfo = fileManager.volumeManager.getLocationInfo(entry); return (locationInfo && locationInfo.isReadOnly) || - CommandUtil.isDownloads(fileManager.volumeManager, entry); + CommandUtil.isReadOnly(fileManager.volumeManager, entry); }); } }; @@ -1195,7 +1199,7 @@ */ execute: function(event, fileManager) { const entry = CommandUtil.getCommandEntry(fileManager, event.target); - if (CommandUtil.isDownloads(fileManager.volumeManager, entry)) { + if (CommandUtil.isReadOnly(fileManager.volumeManager, entry)) { return; } if (event.target instanceof DirectoryTree || @@ -1270,7 +1274,7 @@ !CommandUtil.shouldShowMenuItemsForEntry( fileManager.volumeManager, entries[0]) || entries.some( - CommandUtil.isDownloads.bind(null, fileManager.volumeManager))) { + CommandUtil.isReadOnly.bind(null, fileManager.volumeManager))) { event.canExecute = false; event.command.setHidden(true); return; @@ -2309,8 +2313,9 @@ }); }, canExecute: function(event, fileManager) { - event.canExecute = fileManager.dialogType === DialogType.FULL_PAGE; - event.command.setHidden(!event.canExecute); + const isFullPage = fileManager.dialogType === DialogType.FULL_PAGE; + event.canExecute = isFullPage && navigator.onLine; + event.command.setHidden(!isFullPage); } });
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js index 51d9afe..8ab46bf4 100644 --- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js +++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -1430,7 +1430,7 @@ } // For MyFiles/Downloads we only allow copy. - if (isMove && this.isDownloads_(entry)) { + if (isMove && this.isCopyOnly_(entry)) { return false; } @@ -1464,7 +1464,7 @@ // For MyFiles/Downloads we only allow copy. if (isMove && - this.selectionHandler_.selection.entries.some(this.isDownloads_, this)) { + this.selectionHandler_.selection.entries.some(this.isCopyOnly_, this)) { return false; } @@ -1831,11 +1831,12 @@ }; /** - * Returns True if entry is MyFiles>Downloads. + * Returns True if entry is folder which we enforce to be read-only + * or copy-only such as MyFiles>Downloads or MyFiles>PluginVm. * @param {(!Entry|!FakeEntry)} entry Entry or a fake entry. * @return {boolean} */ -FileTransferController.prototype.isDownloads_ = function(entry) { +FileTransferController.prototype.isCopyOnly_ = function(entry) { if (util.isFakeEntry(entry)) { return false; } @@ -1845,10 +1846,13 @@ return false; } - if (util.isMyFilesVolumeEnabled() && - volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS && - entry.fullPath === '/Downloads') { - return true; + if (volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS) { + if (util.isMyFilesVolumeEnabled() && entry.fullPath === '/Downloads') { + return true; + } + if (util.isPluginVmEnabled() && entry.fullPath === '/PluginVm') { + return true; + } } return false; };
diff --git a/ui/file_manager/file_manager/test/BUILD.gn b/ui/file_manager/file_manager/test/BUILD.gn index d65a38d..16cf61f 100644 --- a/ui/file_manager/file_manager/test/BUILD.gn +++ b/ui/file_manager/file_manager/test/BUILD.gn
@@ -43,6 +43,7 @@ ":crostini_share", ":crostini_tasks", ":menu", + ":plugin_vm", ":progress_center", ":uma", ] @@ -102,6 +103,13 @@ ] } +js_library("plugin_vm") { + deps = [ + "js:test_util", + "//ui/webui/resources/js:webui_resource_test", + ] +} + js_library("progress_center") { deps = [ "js:test_util",
diff --git a/ui/file_manager/file_manager/test/check_select.js b/ui/file_manager/file_manager/test/check_select.js index 3fb682dc..de358342 100644 --- a/ui/file_manager/file_manager/test/check_select.js +++ b/ui/file_manager/file_manager/test/check_select.js
@@ -4,100 +4,80 @@ const checkselect = {}; -checkselect.testCancelCheckSelectModeAfterAction = (done) => { - test.setupAndWaitUntilReady() - .then(() => { - // Click 2nd last file on checkmark to start check-select-mode. - assertTrue(test.fakeMouseClick( - '#file-list li.table-row:nth-of-type(4) .detail-checkmark')); - return test.waitForElement( - '#file-list li[selected].table-row:nth-of-type(4)'); - }) - .then(result => { - // Click last file on checkmark, adds to selection. - assertTrue(test.fakeMouseClick( - '#file-list li.table-row:nth-of-type(5) .detail-checkmark')); - return test.waitForElement( - '#file-list li[selected].table-row:nth-of-type(5)'); - }) - .then(result => { - assertEquals( - 2, document.querySelectorAll('#file-list li[selected]').length); - // Click selection menu (3-dots). - assertTrue(test.fakeMouseClick('#selection-menu-button')); - return test.waitForElement( - '#file-context-menu:not([hidden]) ' + - 'cr-menu-item[command="#cut"]:not([disabled])'); - }) - .then(result => { - // Click 'cut'. - test.fakeMouseClick('#file-context-menu cr-menu-item[command="#cut"]'); - return test.waitForElement('#file-context-menu[hidden]'); - }) - .then(result => { - // Click first photos dir in checkmark and make sure 4 and 5 not - // selected. - assertTrue(test.fakeMouseClick( - '#file-list li.table-row:nth-of-type(1) .detail-checkmark')); - return test.waitForElement( - '#file-list li[selected].table-row:nth-of-type(1)'); - }) - .then(result => { - assertEquals( - 1, document.querySelectorAll('#file-list li[selected]').length); - done(); - }); +checkselect.testCancelCheckSelectModeAfterAction = async (done) => { + await test.setupAndWaitUntilReady(); + + // Click 2nd last file on checkmark to start check-select-mode. + assertTrue(test.fakeMouseClick( + '#file-list li.table-row:nth-of-type(4) .detail-checkmark')); + await test.waitForElement('#file-list li[selected].table-row:nth-of-type(4)'); + + // Click last file on checkmark, adds to selection. + assertTrue(test.fakeMouseClick( + '#file-list li.table-row:nth-of-type(5) .detail-checkmark')); + await test.waitForElement('#file-list li[selected].table-row:nth-of-type(5)'); + assertEquals(2, document.querySelectorAll('#file-list li[selected]').length); + + // Click selection menu (3-dots). + assertTrue(test.fakeMouseClick('#selection-menu-button')); + await test.waitForElement( + '#file-context-menu:not([hidden]) ' + + 'cr-menu-item[command="#cut"]:not([disabled])'); + + // Click 'cut'. + test.fakeMouseClick('#file-context-menu cr-menu-item[command="#cut"]'); + await test.waitForElement('#file-context-menu[hidden]'); + + // Click first photos dir in checkmark and make sure 4 and 5 not + // selected. + assertTrue(test.fakeMouseClick( + '#file-list li.table-row:nth-of-type(1) .detail-checkmark')); + await test.waitForElement('#file-list li[selected].table-row:nth-of-type(1)'); + + assertEquals(1, document.querySelectorAll('#file-list li[selected]').length); + done(); }; -checkselect.testCheckSelectModeAfterSelectAllOneFile = (done) => { +checkselect.testCheckSelectModeAfterSelectAllOneFile = async (done) => { const gearMenu = document.querySelector('#gear-menu'); const cancel = document.querySelector('#cancel-selection-button-wrapper'); const selectAll = '#gear-menu:not([hidden]) #gear-menu-select-all:not([disabled])'; // Load a single file. - test.setupAndWaitUntilReady([test.ENTRIES.hello]) - .then(() => { - // Click gear menu, ensure 'Select all' is shown. - assertTrue(test.fakeMouseClick('#gear-button')); - return test.waitForElement(selectAll); - }) - .then(result => { - // Click 'Select all', gear menu now replaced with file context menu. - assertTrue(test.fakeMouseClick('#gear-menu-select-all')); - return test.repeatUntil(() => { - return getComputedStyle(gearMenu).opacity == 0 && - getComputedStyle(cancel).display == 'block' || - test.pending('waiting for check select mode from click'); - }); - }) - .then(result => { - // Cancel selection, ensure no items selected. - assertTrue(test.fakeMouseClick('#cancel-selection-button')); - return test.repeatUntil(() => { - return document.querySelectorAll('#file-list li[selected]').length == - 0 || - test.pending('waiting for no files selected after click'); - }); - }) - .then(result => { - // 'Ctrl+a' to select all. - assertTrue(test.fakeKeyDown('#file-list', 'a', true, false, false)); - return test.repeatUntil(() => { - return getComputedStyle(cancel).display == 'block' || - test.pending('waiting for check select mode from key'); - }); - }) - .then(result => { - // Cancel selection, ensure no items selected. - assertTrue(test.fakeMouseClick('#cancel-selection-button')); - return test.repeatUntil(() => { - return document.querySelectorAll('#file-list li[selected]').length == - 0 || - test.pending('waiting for no files selected after key'); - }); - }) - .then(result => { - done(); - }); + await test.setupAndWaitUntilReady([test.ENTRIES.hello]); + // Click gear menu, ensure 'Select all' is shown. + assertTrue(test.fakeMouseClick('#gear-button')); + await test.waitForElement(selectAll); + + // Click 'Select all', gear menu now replaced with file context menu. + assertTrue(test.fakeMouseClick('#gear-menu-select-all')); + await test.repeatUntil(() => { + return getComputedStyle(gearMenu).opacity == 0 && + getComputedStyle(cancel).display == 'block' || + test.pending('waiting for check select mode from click'); + }); + + // Cancel selection, ensure no items selected. + assertTrue(test.fakeMouseClick('#cancel-selection-button')); + await test.repeatUntil(() => { + return document.querySelectorAll('#file-list li[selected]').length == 0 || + test.pending('waiting for no files selected after click'); + }); + + // 'Ctrl+a' to select all. + assertTrue(test.fakeKeyDown('#file-list', 'a', true, false, false)); + await test.repeatUntil(() => { + return getComputedStyle(cancel).display == 'block' || + test.pending('waiting for check select mode from key'); + }); + + // Cancel selection, ensure no items selected. + assertTrue(test.fakeMouseClick('#cancel-selection-button')); + await test.repeatUntil(() => { + return document.querySelectorAll('#file-list li[selected]').length == 0 || + test.pending('waiting for no files selected after key'); + }); + + done(); };
diff --git a/ui/file_manager/file_manager/test/crostini_mount.js b/ui/file_manager/file_manager/test/crostini_mount.js index 60ee027..5ea485e 100644 --- a/ui/file_manager/file_manager/test/crostini_mount.js +++ b/ui/file_manager/file_manager/test/crostini_mount.js
@@ -18,60 +18,53 @@ done(); }; -crostiniMount.testMountCrostiniSuccess = (done) => { +crostiniMount.testMountCrostiniSuccess = async (done) => { const fakeRoot = '#directory-tree [root-type-icon="crostini"]'; const oldMount = chrome.fileManagerPrivate.mountCrostini; let mountCallback = null; chrome.fileManagerPrivate.mountCrostini = (callback) => { mountCallback = callback; }; - test.setupAndWaitUntilReady() - .then(() => { - // Linux files fake root is shown. - return test.waitForElement(fakeRoot); - }) - .then(() => { - // Click on Linux files. - assertTrue(test.fakeMouseClick(fakeRoot, 'click linux files')); - return test.waitForElement('paper-progress:not([hidden])'); - }) - .then(() => { - // Ensure mountCrostini is called. - return test.repeatUntil(() => { - if (!mountCallback) { - return test.pending('Waiting for mountCrostini'); - } - return mountCallback; - }); - }) - .then(() => { - // Intercept the fileManagerPrivate.mountCrostini call - // and add crostini disk mount. - test.mountCrostini(); - // Continue from fileManagerPrivate.mountCrostini callback - // and ensure expected files are shown. - mountCallback(); - return test.waitForFiles( - test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET)); - }) - .then(() => { - // Reset fileManagerPrivate.mountCrostini and remove mount. - chrome.fileManagerPrivate.mountCrostini = oldMount; - chrome.fileManagerPrivate.removeMount('crostini'); - // Linux Files fake root is shown. - return test.waitForElement(fakeRoot); - }) - .then(() => { - // Downloads folder should be shown when crostini goes away. - return test.waitForFiles( - test.TestEntryInfo.getExpectedRows(test.BASIC_LOCAL_ENTRY_SET)); - }) - .then(() => { - done(); - }); + await test.setupAndWaitUntilReady(); + + // Linux files fake root is shown. + await test.waitForElement(fakeRoot); + + // Click on Linux files. + assertTrue(test.fakeMouseClick(fakeRoot, 'click linux files')); + await test.waitForElement('paper-progress:not([hidden])'); + + // Ensure mountCrostini is called. + await test.repeatUntil(() => { + if (!mountCallback) { + return test.pending('Waiting for mountCrostini'); + } + return mountCallback; + }); + + // Intercept the fileManagerPrivate.mountCrostini call + // and add crostini disk mount. + test.mountCrostini(); + // Continue from fileManagerPrivate.mountCrostini callback + // and ensure expected files are shown. + mountCallback(); + await test.waitForFiles( + test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET)); + + // Reset fileManagerPrivate.mountCrostini and remove mount. + chrome.fileManagerPrivate.mountCrostini = oldMount; + chrome.fileManagerPrivate.removeMount('crostini'); + // Linux Files fake root is shown. + await test.waitForElement(fakeRoot); + + // MyFiles folder should be shown when crostini goes away. + await test.waitForFiles(test.TestEntryInfo.getExpectedRows( + test.BASIC_MY_FILES_ENTRY_SET_WITH_LINUX_FILES)); + + done(); }; -crostiniMount.testMountCrostiniError = (done) => { +crostiniMount.testMountCrostiniError = async (done) => { const fakeRoot = '#directory-tree [root-type-icon="crostini"]'; const oldMount = chrome.fileManagerPrivate.mountCrostini; // Override fileManagerPrivate.mountCrostini to return error. @@ -80,47 +73,32 @@ callback(); delete chrome.runtime.lastError; }; - test.setupAndWaitUntilReady() - .then(() => { - return test.waitForElement(fakeRoot); - }) - .then(() => { - // Click on Linux Files, ensure error dialog is shown. - assertTrue(test.fakeMouseClick(fakeRoot)); - return test.waitForElement('.cr-dialog-container.shown'); - }) - .then(() => { - // Click OK button to close. - assertTrue(test.fakeMouseClick('button.cr-dialog-ok')); - return test.waitForElementLost('.cr-dialog-container'); - }) - .then(() => { - // Reset chrome.fileManagerPrivate.mountCrostini. - chrome.fileManagerPrivate.mountCrostini = oldMount; - done(); - }); + await test.setupAndWaitUntilReady(); + await test.waitForElement(fakeRoot); + + // Click on Linux Files, ensure error dialog is shown. + assertTrue(test.fakeMouseClick(fakeRoot)); + await test.waitForElement('.cr-dialog-container.shown'); + + // Click OK button to close. + assertTrue(test.fakeMouseClick('button.cr-dialog-ok')); + await test.waitForElementLost('.cr-dialog-container'); + + // Reset chrome.fileManagerPrivate.mountCrostini. + chrome.fileManagerPrivate.mountCrostini = oldMount; + done(); }; -crostiniMount.testCrostiniMountOnDrag = (done) => { +crostiniMount.testCrostiniMountOnDrag = async (done) => { const fakeRoot = '#directory-tree [root-type-icon="crostini"]'; chrome.fileManagerPrivate.mountCrostiniDelay_ = 0; - test.setupAndWaitUntilReady() - .then(() => { - return test.waitForElement(fakeRoot); - }) - .then(() => { - assertTrue( - test.sendEvent(fakeRoot, new Event('dragenter', {bubbles: true}))); - assertTrue( - test.sendEvent(fakeRoot, new Event('dragleave', {bubbles: true}))); - return test.waitForFiles( - test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET)); - }) - .then(() => { - chrome.fileManagerPrivate.removeMount('crostini'); - return test.waitForElement(fakeRoot); - }) - .then(() => { - done(); - }); + await test.setupAndWaitUntilReady(); + await test.waitForElement(fakeRoot); + assertTrue(test.sendEvent(fakeRoot, new Event('dragenter', {bubbles: true}))); + assertTrue(test.sendEvent(fakeRoot, new Event('dragleave', {bubbles: true}))); + await test.waitForFiles( + test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET)); + chrome.fileManagerPrivate.removeMount('crostini'); + await test.waitForElement(fakeRoot); + done(); };
diff --git a/ui/file_manager/file_manager/test/crostini_share.js b/ui/file_manager/file_manager/test/crostini_share.js index 688fb1d..3db134b 100644 --- a/ui/file_manager/file_manager/test/crostini_share.js +++ b/ui/file_manager/file_manager/test/crostini_share.js
@@ -36,7 +36,7 @@ const shareWithDirTree = '#directory-tree-context-menu [command="#' + share + '"]'; const photos = '#file-list [file-name="photos"]'; - const downloadsDirTree = '#directory-tree [volume-type-icon="downloads"]'; + const myFilesDirTree = '#directory-tree [root-type-icon="my_files"]'; const oldSharePaths = chrome.fileManagerPrivate.sharePathsWithCrostini; let sharePathsCalled = false; let sharePathsPersist; @@ -116,10 +116,9 @@ assertTrue(test.fakeMouseRightClick(photos), 'right-click photos'); await test.waitForElement(menuShareWith); - // Verify dialog is shown for Downloads root. + // Verify dialog is shown for MyFiles root. // Check 'Share with <VM>' is shown in menu. - assertTrue( - test.fakeMouseRightClick(downloadsDirTree), 'right-click downloads'); + assertTrue(test.fakeMouseRightClick(myFilesDirTree), 'right-click MyFiles'); await test.waitForElement(menuShareWithDirTree); // Click 'Share with <VM>', verify dialog. @@ -167,7 +166,6 @@ '[command="#' + share + '"][hidden][disabled="disabled"]'; const menuShareWith = '#file-context-menu:not([hidden]) ' + '[command="#' + share + '"]:not([hidden]):not([disabled])'; - const downloadsDirTree = '#directory-tree [volume-type-icon="downloads"]'; const removableVolumeRoot = '#directory-tree [volume-type-icon="removable"]'; const menuShareWithDirTree = '#directory-tree-context-menu:not([hidden]) ' + '[command="#' + share + '"]:not([hidden]):not([disabled])'; @@ -203,9 +201,9 @@ assertTrue(test.fakeMouseRightClick(downloads), 'right-click downloads'); await test.waitForElement(menuShareWith); - // Right-click 'Downloads' directory in directory tree. + // Right-click 'MyFiles' in directory tree. // Check 'Share with <VM>' is shown in menu. - assertTrue(test.fakeMouseRightClick(downloadsDirTree), 'downloads dirtree'); + assertTrue(test.fakeMouseRightClick(myFiles), 'MyFiles dirtree'); await test.waitForElement(menuShareWithDirTree); // Select removable root.
diff --git a/ui/file_manager/file_manager/test/crostini_tasks.js b/ui/file_manager/file_manager/test/crostini_tasks.js index 2dab4d18..a2f5bef 100644 --- a/ui/file_manager/file_manager/test/crostini_tasks.js +++ b/ui/file_manager/file_manager/test/crostini_tasks.js
@@ -4,7 +4,9 @@ const crostiniTasks = {}; -crostiniTasks.testShareBeforeOpeningDownloadsWithCrostiniApp = (done) => { +crostiniTasks.testShareBeforeOpeningDownloadsWithCrostiniApp = async (done) => { + const fakeRoot = '#directory-tree [root-type-icon="crostini"]'; + // Save old fmp.getFileTasks and replace with version that returns // crostini app and chrome Text app. let oldGetFileTasks = chrome.fileManagerPrivate.getFileTasks; @@ -44,70 +46,62 @@ }; chrome.metricsPrivate.values_ = []; - test.setupAndWaitUntilReady([], [], []) - .then(() => { - // Add '/A', and '/A/hello.txt', refresh, 'A' is shown. - test.addEntries( - [test.ENTRIES.directoryA, test.ENTRIES.helloInA], [], []); - assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh'); - return test.waitForFiles( - test.TestEntryInfo.getExpectedRows([test.ENTRIES.directoryA])); - }) - .then(() => { - // Change to 'A' directory, hello.txt is shown. - assertTrue(test.fakeMouseDoubleClick('[file-name="A"]')); - return test.waitForFiles( - test.TestEntryInfo.getExpectedRows([test.ENTRIES.hello])); - }) - .then(() => { - // Right click on 'hello.txt' file, wait for dialog with 'Open with'. - assertTrue(test.fakeMouseRightClick('[file-name="hello.txt"]')); - return test.waitForElement( - 'cr-menu-item[command="#open-with"]:not([hidden]'); - }) - .then(() => { - // Click 'Open with', wait for picker. - assertTrue(test.fakeMouseClick('cr-menu-item[command="#open-with"]')); - return test.waitForElement('#default-tasks-list'); - }) - .then(() => { - // Ensure that the default tasks label is shown correctly. - const item = document.querySelector('#default-task-menu-item span'); - assertEquals('Open with Crostini App', item.innerText); - }) - .then(() => { - // Ensure picker shows both options. Click on 'Crostini App'. Ensure - // share path dialog is shown. - const list = document.querySelectorAll('#default-tasks-list li div'); - assertEquals(2, list.length); - assertEquals('Crostini App (default)', list[0].innerText); - assertEquals('Open with Text', list[1].innerText); - assertTrue(test.fakeMouseClick('#default-tasks-list li')); - // Ensure fmp.sharePathsWithCrostini, fmp.executeTask called. - return test.repeatUntil(() => { - return sharePathsCalled && executeTaskCalled || - test.pending('Waiting to share and open'); - }); - }) - .then(() => { - // Share should not persist as a result of open with crostini app. - assertFalse(sharePathsPersist); - // Validate UMAs. - const lastEnumUma = chrome.metricsPrivate.values_.pop(); - assertEquals( - 'FileBrowser.CrostiniShareDialog', lastEnumUma[0].metricName); - assertEquals(1 /* ShareBeforeOpen */, lastEnumUma[1]); + await test.setupAndWaitUntilReady([], [], []); - // Restore fmp.*. - chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks; - chrome.fileManagerPrivate.sharePathsWithCrostini = oldSharePaths; - chrome.fileManagerPrivate.executeTask = oldExecuteTask; - done(); - }); + // Add '/A', and '/A/hello.txt', refresh, 'A' is shown. + test.addEntries([test.ENTRIES.directoryA, test.ENTRIES.helloInA], [], []); + assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh'); + await test.waitForFiles(test.TestEntryInfo.getExpectedRows( + [test.ENTRIES.directoryA, test.ENTRIES.linuxFiles])); + + // Change to 'A' directory, hello.txt is shown. + assertTrue(test.fakeMouseDoubleClick('[file-name="A"]')); + await test.waitForFiles( + test.TestEntryInfo.getExpectedRows([test.ENTRIES.hello])); + + // Right click on 'hello.txt' file, wait for dialog with 'Open with'. + assertTrue(test.fakeMouseRightClick('[file-name="hello.txt"]')); + await test.waitForElement('cr-menu-item[command="#open-with"]:not([hidden]'); + + // Click 'Open with', wait for picker. + assertTrue(test.fakeMouseClick('cr-menu-item[command="#open-with"]')); + await test.waitForElement('#default-tasks-list'); + + // Ensure that the default tasks label is shown correctly. + const item = document.querySelector('#default-task-menu-item span'); + assertEquals('Open with Crostini App', item.innerText); + + // Ensure picker shows both options. Click on 'Crostini App'. Ensure + // share path dialog is shown. + const list = document.querySelectorAll('#default-tasks-list li div'); + assertEquals(2, list.length); + assertEquals('Crostini App (default)', list[0].innerText); + assertEquals('Open with Text', list[1].innerText); + assertTrue(test.fakeMouseClick('#default-tasks-list li')); + // Ensure fmp.sharePathsWithCrostini, fmp.executeTask called. + await test.repeatUntil(() => { + return sharePathsCalled && executeTaskCalled || + test.pending('Waiting to share and open'); + }); + + // Share should not persist as a result of open with crostini app. + assertFalse(sharePathsPersist); + // Validate UMAs. + const lastEnumUma = chrome.metricsPrivate.values_.pop(); + assertEquals('FileBrowser.CrostiniShareDialog', lastEnumUma[0].metricName); + assertEquals(1 /* ShareBeforeOpen */, lastEnumUma[1]); + + // Restore fmp.*. + chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks; + chrome.fileManagerPrivate.sharePathsWithCrostini = oldSharePaths; + chrome.fileManagerPrivate.executeTask = oldExecuteTask; + chrome.fileManagerPrivate.removeMount('crostini'); + await test.waitForElement(fakeRoot); + done(); }; -crostiniTasks.testErrorLoadingLinuxPackageInfo = (done) => { - const linuxFiles = '#directory-tree .tree-item [root-type-icon="crostini"]'; +crostiniTasks.testErrorLoadingLinuxPackageInfo = async (done) => { + const fakeRoot = '#directory-tree [root-type-icon="crostini"]'; const dialog = '#install-linux-package-dialog'; const detailsFrame = '.install-linux-package-details-frame'; @@ -132,48 +126,43 @@ packageInfoCallback = callback; }; - test.setupAndWaitUntilReady([], [], [test.ENTRIES.debPackage]) - .then(() => { - return test.waitForElement(linuxFiles); - }) - .then(() => { - // Select 'Linux files' in directory tree. - assertTrue(test.fakeMouseClick(linuxFiles), 'click Linux files'); - return test.waitForFiles( - test.TestEntryInfo.getExpectedRows([test.ENTRIES.debPackage])); - }) - .then(() => { - // Double click on 'package.deb' file to open the install dialog. - assertTrue(test.fakeMouseDoubleClick('[file-name="package.deb"]')); - return test.waitForElement(dialog); - }) - .then(() => { - // Verify the loading state is shown. - assertEquals( - 'Details\nLoading information...', - document.querySelector(detailsFrame).innerText); - return test.repeatUntil(() => { - return packageInfoCallback || - test.pending('Waiting for package info request'); - }); - }) - .then(() => { - // Call the callback with an error. - chrome.runtime.lastError = {message: 'error message'}; - packageInfoCallback(undefined); - delete chrome.runtime.lastError; - assertEquals( - 'Details\nFailed to retrieve app info.', - document.querySelector(detailsFrame).innerText); + await test.setupAndWaitUntilReady([], [], [test.ENTRIES.debPackage]); + await test.waitForElement(fakeRoot); - // Click 'cancel' to close. Ensure dialog closes. - assertTrue(test.fakeMouseClick('button.cr-dialog-cancel')); - return test.waitForElementLost(dialog); - }) - .then(() => { - // Restore fmp.getFileTasks, fmp.getLinuxPackageInfo. - chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks; - chrome.fileManagerPrivate.getLinuxPackageInfo = oldGetLinuxPackageInfo; - done(); - }); + // Select 'Linux files' in directory tree. + assertTrue(test.fakeMouseClick(fakeRoot), 'click Linux files'); + await test.waitForFiles( + test.TestEntryInfo.getExpectedRows([test.ENTRIES.debPackage])); + + // Double click on 'package.deb' file to open the install dialog. + assertTrue(test.fakeMouseDoubleClick('[file-name="package.deb"]')); + await test.waitForElement(dialog); + + // Verify the loading state is shown. + assertEquals( + 'Details\nLoading information...', + document.querySelector(detailsFrame).innerText); + await test.repeatUntil(() => { + return packageInfoCallback || + test.pending('Waiting for package info request'); + }); + + // Call the callback with an error. + chrome.runtime.lastError = {message: 'error message'}; + packageInfoCallback(undefined); + delete chrome.runtime.lastError; + assertEquals( + 'Details\nFailed to retrieve app info.', + document.querySelector(detailsFrame).innerText); + + // Click 'cancel' to close. Ensure dialog closes. + assertTrue(test.fakeMouseClick('button.cr-dialog-cancel')); + await test.waitForElementLost(dialog); + + // Restore fmp.getFileTasks, fmp.getLinuxPackageInfo. + chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks; + chrome.fileManagerPrivate.getLinuxPackageInfo = oldGetLinuxPackageInfo; + chrome.fileManagerPrivate.removeMount('crostini'); + await test.waitForElement(fakeRoot); + done(); };
diff --git a/ui/file_manager/file_manager/test/js/strings.js b/ui/file_manager/file_manager/test/js/strings.js index 9d7fbcc..5d8a144 100644 --- a/ui/file_manager/file_manager/test/js/strings.js +++ b/ui/file_manager/file_manager/test/js/strings.js
@@ -8,7 +8,7 @@ loadTimeData.data = $GRDP; // Extend with additional fields not found in grdp files. -Object.setPrototypeOf(loadTimeData.data_, { +loadTimeData.overrideValues({ 'CROSTINI_ENABLED': true, 'DRIVE_FS_ENABLED': false, 'GOOGLE_DRIVE_REDEEM_URL': 'http://www.google.com/intl/en/chrome/devices' + @@ -16,6 +16,7 @@ 'GOOGLE_DRIVE_OVERVIEW_URL': 'https://support.google.com/chromebook/?p=filemanager_drive', 'HIDE_SPACE_INFO': false, + 'MY_FILES_VOLUME_ENABLED': true, 'PLUGIN_VM_ENABLED': true, 'UI_LOCALE': 'en_US', 'language': 'en-US', @@ -25,4 +26,4 @@ // Overwrite LoadTimeData.prototype.data setter as nop. // Default implementation throws errors when both background and // foreground re-set loadTimeData.data. -Object.defineProperty(LoadTimeData.prototype, 'data', {set: () => {}}); +Object.defineProperty(LoadTimeData.prototype, 'data', {set: () => {}}); \ No newline at end of file
diff --git a/ui/file_manager/file_manager/test/js/test_util.js b/ui/file_manager/file_manager/test/js/test_util.js index 6e64ec3e..9328787 100644 --- a/ui/file_manager/file_manager/test/js/test_util.js +++ b/ui/file_manager/file_manager/test/js/test_util.js
@@ -127,7 +127,7 @@ var content = test.DATA[this.sourceFileName]; var size = content && content.size || 0; return { - fullPath: prefix + this.nameText + suffix, + fullPath: prefix + this.targetPath + suffix, metadata: { size: size, modificationTime: new Date(Date.parse(this.lastModifiedTime)), @@ -188,12 +188,12 @@ 'Jan 1, 1980, 11:59 PM', 'photos', '--', 'Folder'), testDocument: new test.TestEntryInfo( - test.EntryType.FILE, '', 'Test Document', + test.EntryType.FILE, '', 'Test Document.gdoc', 'application/vnd.google-apps.document', test.SharedOption.NONE, 'Apr 10, 2013, 4:20 PM', 'Test Document.gdoc', '--', 'Google document'), testSharedDocument: new test.TestEntryInfo( - test.EntryType.FILE, '', 'Test Shared Document', + test.EntryType.FILE, '', 'Test Shared Document.gdoc', 'application/vnd.google-apps.document', test.SharedOption.SHARED, 'Mar 20, 2013, 10:40 PM', 'Test Shared Document.gdoc', '--', 'Google document'), @@ -207,26 +207,6 @@ test.EntryType.DIRECTORY, '', 'A', '', test.SharedOption.NONE, 'Jan 1, 2000, 1:00 AM', 'A', '--', 'Folder'), - directoryB: new test.TestEntryInfo( - test.EntryType.DIRECTORY, '', 'A/B', '', test.SharedOption.NONE, - 'Jan 1, 2000, 1:00 AM', 'B', '--', 'Folder'), - - directoryC: new test.TestEntryInfo( - test.EntryType.DIRECTORY, '', 'A/B/C', '', test.SharedOption.NONE, - 'Jan 1, 2000, 1:00 AM', 'C', '--', 'Folder'), - - directoryD: new test.TestEntryInfo( - test.EntryType.DIRECTORY, '', 'D', '', test.SharedOption.NONE, - 'Jan 1, 2000, 1:00 AM', 'D', '--', 'Folder'), - - directoryE: new test.TestEntryInfo( - test.EntryType.DIRECTORY, '', 'D/E', '', test.SharedOption.NONE, - 'Jan 1, 2000, 1:00 AM', 'E', '--', 'Folder'), - - directoryF: new test.TestEntryInfo( - test.EntryType.DIRECTORY, '', 'D/E/F', '', test.SharedOption.NONE, - 'Jan 1, 2000, 1:00 AM', 'F', '--', 'Folder'), - zipArchive: new test.TestEntryInfo( test.EntryType.FILE, 'archive.zip', 'archive.zip', 'application/x-zip', test.SharedOption.NONE, 'Jan 1, 2014, 1:00 AM', 'archive.zip', @@ -243,25 +223,51 @@ '51 bytes', 'Plain text'), helloInA: new test.TestEntryInfo( - test.EntryType.FILE, 'text.txt', 'hello.txt', 'text/plain', - test.SharedOption.NONE, 'Sep 4, 1998, 12:34 PM', 'A/hello.txt', - '51 bytes', 'Plain text'), + test.EntryType.FILE, 'text.txt', 'A/hello.txt', 'text/plain', + test.SharedOption.NONE, 'Sep 4, 1998, 12:34 PM', 'hello.txt', '51 bytes', + 'Plain text'), + + downloads: new test.TestEntryInfo( + test.EntryType.DIRECTORY, '', 'Downloads', '', test.SharedOption.NONE, + 'Jan 1, 2000, 1:00 AM', 'Downloads', '--', 'Folder'), + + linuxFiles: new test.TestEntryInfo( + test.EntryType.DIRECTORY, '', 'Linux files', '', test.SharedOption.NONE, + '...', 'Linux files', '--', 'Folder'), + + pluginVm: new test.TestEntryInfo( + test.EntryType.DIRECTORY, '', 'PluginVm', '', test.SharedOption.NONE, + 'Jan 1, 2000, 1:00 AM', 'Plugin VM', '--', 'Folder'), + + photosInPluginVm: new test.TestEntryInfo( + test.EntryType.DIRECTORY, '', 'PluginVm/photos', '', + test.SharedOption.NONE, 'Jan 1, 1980, 11:59 PM', 'photos', '--', + 'Folder'), }; /** - * Basic entry set for the local volume. + * Basic entry set for the MyFiles volume. * @type {!Array<!test.TestEntryInfo>} * @const */ -test.BASIC_LOCAL_ENTRY_SET = [ +test.BASIC_MY_FILES_ENTRY_SET = [ + test.ENTRIES.downloads, test.ENTRIES.hello, test.ENTRIES.world, test.ENTRIES.desktop, test.ENTRIES.beautiful, - test.ENTRIES.photos + test.ENTRIES.photos, ]; /** + * MyFiles plus the fake item 'Linux files'. + * @type {!Array<!test.TestEntryInfo>} + * @const + */ +test.BASIC_MY_FILES_ENTRY_SET_WITH_LINUX_FILES = + test.BASIC_MY_FILES_ENTRY_SET.concat([test.ENTRIES.linuxFiles]); + +/** * Basic entry set for the drive volume. * * TODO(hirono): Add a case for an entry cached by FileCache. For testing @@ -532,14 +538,15 @@ * Opens a Files app's main window and waits until it is initialized. Fills * the window with initial files. Should be called for the first window only. * - * @param {Array<!test.TestEntryInfo>=} opt_downloads Entries for downloads. + * @param {Array<!test.TestEntryInfo>=} opt_myFiles Entries for MyFiles. * @param {Array<!test.TestEntryInfo>=} opt_drive Entries for drive. * @param {Array<!test.TestEntryInfo>=} opt_crostini Entries for crostini. * @return {Promise} Promise to be fulfilled with the result object, which * contains the file list. */ -test.setupAndWaitUntilReady = function(opt_downloads, opt_drive, opt_crostini) { - const entriesDownloads = opt_downloads || test.BASIC_LOCAL_ENTRY_SET; +test.setupAndWaitUntilReady = + async function(opt_myFiles, opt_drive, opt_crostini) { + const entriesMyFiles = opt_myFiles || test.BASIC_MY_FILES_ENTRY_SET; const entriesDrive = opt_drive || test.BASIC_DRIVE_ENTRY_SET; const entriesCrostini = opt_crostini || test.BASIC_CROSTINI_ENTRY_SET; @@ -555,22 +562,19 @@ test.inputText = test.util.sync.inputText.bind(null, window); test.selectFile = test.util.sync.selectFile.bind(null, window); - const downloadsElement = '#directory-tree [volume-type-icon="downloads"]'; + const myFilesElement = '#directory-tree [root-type-icon="my_files"]'; - return test.loadData() - .then(() => { - test.addEntries(entriesDownloads, entriesDrive, entriesCrostini); - return test.waitForElement(downloadsElement); - }) - .then((downloadsIcon) => { - // Click Downloads if not already on Downloads, then refresh button. - if (!downloadsIcon.parentElement.hasAttribute('selected')) { - assertTrue(test.fakeMouseClick(downloadsElement), 'click downloads'); - } - assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh'); - return test.waitForFiles( - test.TestEntryInfo.getExpectedRows(entriesDownloads)); - }); + await test.loadData(); + test.addEntries(entriesMyFiles, entriesDrive, entriesCrostini); + const myFiles = await test.waitForElement(myFilesElement); + + // Click MyFiles if not already on MyFiles, then refresh button. + if (!myFiles.parentElement.hasAttribute('selected')) { + assertTrue(test.fakeMouseClick(myFilesElement), 'click MyFiles'); + } + assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh'); + const filesShown = entriesMyFiles.concat([test.ENTRIES.linuxFiles]); + return test.waitForFiles(test.TestEntryInfo.getExpectedRows(filesShown)); }; /** @@ -580,29 +584,3 @@ test.done = function(opt_failed) { window.endTests(!opt_failed); }; - -/** - * @return {number} Maximum listitem-? id from #file-list. - */ -test.maxListItemId = function() { - var listItems = document.querySelectorAll('#file-list .table-row'); - if (!listItems) { - return 0; - } - return Math.max(...Array.from(listItems).map(e => { - return e.id.replace('listitem-', ''); - })); -}; - -/** - * @return {number} Minium listitem-? id from #file-list. - */ -test.minListItemId = function() { - var listItems = document.querySelectorAll('#file-list .table-row'); - if (!listItems) { - return 0; - } - return Math.min(...Array.from(listItems).map(e => { - return e.id.replace('listitem-', ''); - })); -};
diff --git a/ui/file_manager/file_manager/test/plugin_vm.js b/ui/file_manager/file_manager/test/plugin_vm.js new file mode 100644 index 0000000..05c978c --- /dev/null +++ b/ui/file_manager/file_manager/test/plugin_vm.js
@@ -0,0 +1,83 @@ +// 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. + +const pluginVm = {}; + +pluginVm.testLabelIconContextMenu = async (done) => { + const fileMenu = [ + ['#cut', false], + ['#copy', true], + ['#paste-into-folder', false], + ['#get-info', true], + ['#delete', false], + ['#zip-selection', true], + ['#share-with-linux', true], + ['#manage-plugin-vm-sharing', true], + ['#new-folder', true], + ]; + + const fileMenuSubfolder = [ + ['#cut', true], + ['#copy', true], + ['#paste-into-folder', false], + ['#get-info', true], + ['#rename', true], + ['#delete', true], + ['#zip-selection', true], + ['#share-with-linux', true], + ['#manage-plugin-vm-sharing', true], + ['#new-folder', true], + ]; + + const pluginVmFolder = '#file-list [file-name="PluginVm"]'; + const iconFolder = + '#file-list [file-name="PluginVm"] [file-type-icon="plugin_vm"]'; + const fileMenuShown = '#file-context-menu:not([hidden])'; + + const iconDirTree = '#directory-tree [file-type-icon="plugin_vm"]'; + const dirTreeMenuShown = '#directory-tree-context-menu:not([hidden])'; + const itemsShown = ' cr-menu-item:not([hidden])'; + + async function menuItems(menuTypeShown) { + await test.waitForElement(menuTypeShown); + const list = document.querySelectorAll(menuTypeShown + itemsShown); + return Array.from(document.querySelectorAll(menuTypeShown + itemsShown)) + .map(e => [e.attributes['command'].value, !e.disabled]); + } + + // Verify that /PluginVm has label 'Plugin VM'. + await test.setupAndWaitUntilReady([], [], []); + test.addEntries( + [test.ENTRIES.pluginVm, test.ENTRIES.photosInPluginVm], [], []); + assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh'); + await test.waitForFiles(test.TestEntryInfo.getExpectedRows( + [test.ENTRIES.pluginVm, test.ENTRIES.linuxFiles])); + + // Register /PluginVm as shared. + const pluginVmEntry = + mockVolumeManager + .getCurrentProfileVolumeInfo(VolumeManagerCommon.VolumeType.DOWNLOADS) + .fileSystem.entries['/PluginVm']; + fileManager.crostini.registerSharedPath('PluginVm', pluginVmEntry); + + // Verify folder icon. + await test.waitForElement(iconFolder); + + // Verify /PluginVm folder context menu. + assertTrue(test.fakeMouseRightClick(pluginVmFolder)); + let items = await menuItems(fileMenuShown); + assertEquals(JSON.stringify(fileMenu), JSON.stringify(items)); + + // Change to 'PluginVm' directory, photos folder is shown. + assertTrue(test.fakeMouseDoubleClick(pluginVmFolder)); + await test.waitForFiles( + test.TestEntryInfo.getExpectedRows([test.ENTRIES.photos])); + + // Verify /PluginVm/photos folder context menu. + assertTrue(test.fakeMouseRightClick('#file-list [file-name="photos"]')); + items = await menuItems(fileMenuShown); + assertEquals(JSON.stringify(fileMenuSubfolder), JSON.stringify(items)); + + done(); +}; \ No newline at end of file
diff --git a/ui/file_manager/file_manager/test/progress_center.js b/ui/file_manager/file_manager/test/progress_center.js index db21f04..bd8248c1 100644 --- a/ui/file_manager/file_manager/test/progress_center.js +++ b/ui/file_manager/file_manager/test/progress_center.js
@@ -16,7 +16,7 @@ return item; }; -progressCenter.testScrollWhenManyMessages = (done) => { +progressCenter.testScrollWhenManyMessages = async (done) => { const visibleClosed = '#progress-center:not([hidden]):not(.opened)'; const visibleOpen = '#progress-center:not([hidden]).opened'; const openIcon = '#progress-center-close-view .open'; @@ -25,32 +25,29 @@ const items = []; const center = fileManager.fileBrowserBackground_.progressCenter; // Load a single file. - test.setupAndWaitUntilReady() - .then(() => { - // Add lots of messages. - for (let i = 0; i < 100; i++) { - const item = progressCenter.createItem('id' + i, 'msg ' + i); - items.push(item); - center.updateItem(item); - } - // Wait for notification expand icon. - return test.waitForElement(visibleClosed); - }) - .then(result => { - // Click open icon, ensure progress center is open. - assertTrue(test.fakeMouseClick(openIcon)); - return test.waitForElement(visibleOpen); - }) - .then(result => { - // Ensure progress center is scrollable. - const footer = document.querySelector(navListFooter); - assertTrue(footer.scrollHeight > footer.clientHeight); + await test.setupAndWaitUntilReady(); - // Clear items. - items.forEach((item) => { - item.state = ProgressItemState.COMPLETED; - center.updateItem(item); - }); - done(); - }); + // Add lots of messages. + for (let i = 0; i < 100; i++) { + const item = progressCenter.createItem('id' + i, 'msg ' + i); + items.push(item); + center.updateItem(item); + } + // Wait for notification expand icon. + await test.waitForElement(visibleClosed); + + // Click open icon, ensure progress center is open. + assertTrue(test.fakeMouseClick(openIcon)); + await test.waitForElement(visibleOpen); + + // Ensure progress center is scrollable. + const footer = document.querySelector(navListFooter); + assertTrue(footer.scrollHeight > footer.clientHeight); + + // Clear items. + items.forEach((item) => { + item.state = ProgressItemState.COMPLETED; + center.updateItem(item); + }); + done(); };
diff --git a/ui/file_manager/file_manager/test/uma.js b/ui/file_manager/file_manager/test/uma.js index 11b172e..ad62fe9 100644 --- a/ui/file_manager/file_manager/test/uma.js +++ b/ui/file_manager/file_manager/test/uma.js
@@ -4,27 +4,23 @@ const uma = {}; -uma.testClickBreadcrumb = (done) => { - test.setupAndWaitUntilReady() - .then(() => { - // Reset metrics. - chrome.metricsPrivate.userActions_ = []; - // Click first row which is 'photos' dir, wait for breadcrumb to show. - assertTrue(test.fakeMouseDoubleClick('#file-list li.table-row')); - return test.waitForElement( - '#location-breadcrumbs .breadcrumb-path:nth-of-type(2)'); - }) - .then(result => { - // Click breadcrumb to return to parent dir. - assertTrue(test.fakeMouseClick( - '#location-breadcrumbs .breadcrumb-path:nth-of-type(1)')); - return test.waitForFiles( - test.TestEntryInfo.getExpectedRows(test.BASIC_LOCAL_ENTRY_SET)); - }) - .then(result => { - assertArrayEquals( - ['FileBrowser.ClickBreadcrumbs'], - chrome.metricsPrivate.userActions_); - done(); - }); +uma.testClickBreadcrumb = async (done) => { + await test.setupAndWaitUntilReady(); + + // Reset metrics. + chrome.metricsPrivate.userActions_ = []; + // Click first row which is 'photos' dir, wait for breadcrumb to show. + assertTrue(test.fakeMouseDoubleClick('#file-list li.table-row')); + await test.waitForElement( + '#location-breadcrumbs .breadcrumb-path:nth-of-type(2)'); + + // Click breadcrumb to return to parent dir. + assertTrue(test.fakeMouseClick( + '#location-breadcrumbs .breadcrumb-path:nth-of-type(1)')); + await test.waitForFiles(test.TestEntryInfo.getExpectedRows( + test.BASIC_MY_FILES_ENTRY_SET_WITH_LINUX_FILES)); + + assertArrayEquals( + ['FileBrowser.ClickBreadcrumbs'], chrome.metricsPrivate.userActions_); + done(); };
diff --git a/ui/file_manager/gallery/css/gallery.css b/ui/file_manager/gallery/css/gallery.css index 41d67ce9..2710c4e 100644 --- a/ui/file_manager/gallery/css/gallery.css +++ b/ui/file_manager/gallery/css/gallery.css
@@ -173,6 +173,12 @@ .gallery .video-container > .video { max-height: 100%; max-width: 100%; + + /* + * Since r614513, <video> elements take focus on click which causes yellow + * lines to appear unless the outline is disabled. See crbug/917503. + */ + outline: none; } /*
diff --git a/ui/file_manager/image_loader/piex/.gitignore b/ui/file_manager/image_loader/piex/.gitignore index 53d1e6ef..2ae8f3f 100644 --- a/ui/file_manager/image_loader/piex/.gitignore +++ b/ui/file_manager/image_loader/piex/.gitignore
@@ -3,5 +3,5 @@ package-lock.json tests.result.txt tests.log -a.out.* +piex.out* *.bc
diff --git a/ui/file_manager/image_loader/piex/Makefile b/ui/file_manager/image_loader/piex/Makefile index 7e264c0a..4c489bc 100644 --- a/ui/file_manager/image_loader/piex/Makefile +++ b/ui/file_manager/image_loader/piex/Makefile
@@ -15,6 +15,7 @@ WASM = -s WASM=1 -fno-exceptions -Wall WOPT = -Os --llvm-opts 3 \ -s STRICT=1 \ + -s SINGLE_FILE=1 \ -s ALLOW_MEMORY_GROWTH=1 \ -s ENVIRONMENT='web' \ -s NO_DYNAMIC_EXECUTION=1 \ @@ -22,11 +23,12 @@ -s RESERVED_FUNCTION_POINTERS=$(shell echo $$((10*2))) \ -s TOTAL_STACK=$(shell echo $$((8*1024))) -all: a.out.js - $(shell cp a.out.* extension) +.PHONY: all clean release -a.out.js: piex.cpp.bc piex.src.bc - em++ --bind --std=c++11 $(WASM) $(WOPT) $^ +all: piex.out.js release + +piex.out.js: piex.cpp.bc piex.src.bc + em++ --bind --std=c++11 $(WASM) $(WOPT) $^ -o $@ piex.cpp.bc: piex.cpp Makefile em++ --bind --std=c++11 $(WASM) $(WOPT) $(INCS) piex.cpp -o $@ @@ -34,8 +36,10 @@ piex.src.bc: $(PSRC) Makefile em++ --bind --std=c++11 $(WASM) $(WOPT) $(INCS) $(PSRC) -o $@ -.PHONY: clean +release: + $(shell cp piex.out.js piex.wasm) + $(shell cp piex.wasm extension) clean: - $(shell rm -f tests.{result*,log} package-lock* *.bc a.out.*) - $(shell rm -f extension/a.out.*) + $(shell rm -f tests.{result*,log} package-lock* *.bc piex.out*) + $(shell rm -f piex.wasm extension/piex.wasm)
diff --git a/ui/file_manager/image_loader/piex/README.md b/ui/file_manager/image_loader/piex/README.md index 256ee901..2b93f673 100644 --- a/ui/file_manager/image_loader/piex/README.md +++ b/ui/file_manager/image_loader/piex/README.md
@@ -15,15 +15,21 @@ npm install ``` -Build piexwasm code a.out.js a.out.wasm +Build piexwasm code: piex.wasm ```shell npm run build ``` -Run tests +Run tests: they must PASS ```shell npm run test ``` +Release: submit piex.wasm to the Chromium repository + +```shell + git commit -a -m "Release piexwasm ..." + git cl upload +```
diff --git a/ui/file_manager/image_loader/piex/extension/background.js b/ui/file_manager/image_loader/piex/extension/background.js index 70cab29..83eaafdc 100644 --- a/ui/file_manager/image_loader/piex/extension/background.js +++ b/ui/file_manager/image_loader/piex/extension/background.js
@@ -20,5 +20,5 @@ }, }; - script.src = 'a.out.js'; + script.src = '/piex.wasm'; };
diff --git a/ui/file_manager/image_loader/piex/piex.wasm b/ui/file_manager/image_loader/piex/piex.wasm new file mode 100644 index 0000000..5be8714 --- /dev/null +++ b/ui/file_manager/image_loader/piex/piex.wasm
@@ -0,0 +1 @@ +var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=function(status,toThrow){throw toThrow};Module["preRun"]=[];Module["postRun"]=[];var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}else{return scriptDirectory+path}}if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}Module["read"]=function shell_read(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText}catch(err){var data=tryParseAsDataURI(url);if(data){return intArrayToString(data)}throw err}};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}catch(err){var data=tryParseAsDataURI(url);if(data){return data}throw err}}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}var data=tryParseAsDataURI(url);if(data){onload(data.buffer);return}onerror()};xhr.onerror=onerror;xhr.send(null)};Module["setWindowTitle"]=function(title){document.title=title}}else{}var out=Module["print"]||(typeof console!=="undefined"?console.log.bind(console):typeof print!=="undefined"?print:null);var err=Module["printErr"]||(typeof printErr!=="undefined"?printErr:typeof console!=="undefined"&&console.warn.bind(console)||out);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var asm2wasmImports={"f64-rem":function(x,y){return x%y},"debugger":function(){debugger}};var jsCallStartIndex=1;var functionPointers=new Array(20);function addFunction(func,sig){var base=0;for(var i=base;i<base+20;i++){if(!functionPointers[i]){functionPointers[i]=func;return jsCallStartIndex+i}}throw"Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS."}function removeFunction(index){functionPointers[index-jsCallStartIndex]=null}if(typeof WebAssembly!=="object"){err("no native wasm support detected")}var wasmMemory;var wasmTable;var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function getCFunc(ident){var func=Module["_"+ident];assert(func,"Cannot call unknown function "+ident+", make sure it is exported");return func}function ccall(ident,returnType,argTypes,args,opts){var toC={"string":function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret},"array":function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string")return UTF8ToString(ret);if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func.apply(null,cArgs);ret=convertReturnValue(ret);if(stack!==0)stackRestore(stack);return ret}function cwrap(ident,returnType,argTypes,opts){argTypes=argTypes||[];var numericArgs=argTypes.every(function(type){return type==="number"});var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return function(){return ccall(ident,returnType,argTypes,arguments,opts)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(u8Array,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(u8Array[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&u8Array.subarray&&UTF8Decoder){return UTF8Decoder.decode(u8Array.subarray(idx,endPtr))}else{var str="";while(idx<endPtr){var u0=u8Array[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|u8Array[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}var WASM_PAGE_SIZE=65536;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBuffer(buf){Module["buffer"]=buffer=buf}function updateGlobalBufferViews(){Module["HEAP8"]=HEAP8=new Int8Array(buffer);Module["HEAP16"]=HEAP16=new Int16Array(buffer);Module["HEAP32"]=HEAP32=new Int32Array(buffer);Module["HEAPU8"]=HEAPU8=new Uint8Array(buffer);Module["HEAPU16"]=HEAPU16=new Uint16Array(buffer);Module["HEAPU32"]=HEAPU32=new Uint32Array(buffer);Module["HEAPF32"]=HEAPF32=new Float32Array(buffer);Module["HEAPF64"]=HEAPF64=new Float64Array(buffer)}var DYNAMIC_BASE=17216,DYNAMICTOP_PTR=8768;var TOTAL_STACK=8192;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||16777216;if(TOTAL_MEMORY<TOTAL_STACK)err("TOTAL_MEMORY should be larger than TOTAL_STACK, was "+TOTAL_MEMORY+"! (TOTAL_STACK="+TOTAL_STACK+")");if(Module["buffer"]){buffer=Module["buffer"]}else{if(typeof WebAssembly==="object"&&typeof WebAssembly.Memory==="function"){wasmMemory=new WebAssembly.Memory({"initial":TOTAL_MEMORY/WASM_PAGE_SIZE});buffer=wasmMemory.buffer}else{buffer=new ArrayBuffer(TOTAL_MEMORY)}Module["buffer"]=buffer}updateGlobalBufferViews();HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}var wasmBinaryFile="data:application/octet-stream;base64,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eHFGBEAgACEBBQJAIAJBAEEZIAFBAXZrIAFBH0YbdCEEA0AgAEEQaiAEQR92QQJ0aiIGKAIAIgEEQCAEQQF0IQQgAiABKAIEQXhxRg0CIAEhAAwBCwsgBiADNgIAIAMgADYCGCADIAM2AgwgAyADNgIIDAILCyABQQhqIgAoAgAiAiADNgIMIAAgAzYCACADIAI2AgggAyABNgIMIANBADYCGAsFQcw4IAQgBnI2AgAgACADNgIAIAMgADYCGCADIAM2AgwgAyADNgIIC0HoOEHoOCgCAEF/aiIANgIAIAAEQA8LQZA8IQADQCAAKAIAIgNBCGohACADDQALQeg4QX82AgALDAAgACAAKAIEEIIBCxoAIABBADYCBCAAQQA2AgggACAAQQRqNgIACxkBAX8gACgCACIBBEAgACABNgIEIAEQMQsLbwEDfyMDIQQjA0EgaiQDIAQgASgCADYCACAEQQxqIgUgBCgCADYCACAAIAUgBEEIaiIGIARBBGogAhCvASICKAIAIgFFBEAgBSAAIAMQwQMgACAGKAIAIAIgBSgCABCDASAFKAIAIQELIAQkAyABCwoAIABB+A42AgALmwEBBn8jAyEFIwNBIGokAyAFIQNB/////wMgACgCBCAAKAIAIgJrQQJ1IgdBAWoiBkkEQBAABSADIAYgACgCCCACayIEQQF1IgIgAiAGSRtB/////wMgBEECdUH/////AUkbIAcgAEEIahDHASADQQhqIgQoAgAiAiABKAIANgIAIAQgAkEEajYCACAAIAMQxgEgAxDFASAFJAMLC6oCAQZ/IAFBb0sEQBAACyAAQQtqIgcsAAAiA0EASCIEBH8gACgCBCEFIAAoAghB/////wdxQX9qBSADQf8BcSEFQQoLIQIgBSABIAUgAUsbIgZBC0khAUEKIAZBEGpBcHFBf2ogARsiBiACRwRAAkACQAJAIAEEQCAAKAIAIQEgBAR/QQAhBCABIQIgAAUgACABIANB/wFxQQFqEEQaIAEQMQwDCyEBBSAGQQFqIgIQKyEBIAQEf0EBIQQgACgCAAUgASAAIANB/wFxQQFqEEQaIABBBGohAwwCCyECCyABIAIgAEEEaiIDKAIAQQFqEEQaIAIQMSAERQ0BIAZBAWohAgsgACACQYCAgIB4cjYCCCADIAU2AgAgACABNgIADAELIAcg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IAEsAAAiBEYEQCAAQQFqIQAgAUEBaiEBQQAgAkF/aiICRQ0CGgwBCwsgA0H/AXEgBEH/AXFrCwVBAAsLhQEBB38jAyEDIwNBMGokAyADQRBqIQQgA0EMaiEHIAMiBiADQSRqEDMgA0EEaiEIQbwUIQUDQCAHIAg2AgAgBCAHKAIANgIAIAYgBCAFIAUQNRogBUEEaiIFQcQURw0ACyAEEJIBIAYgAEECIAEgBCACEJEBIQkgBBCQASAGEDIgAyQDIAkLHwAgAQRAIAAgASgCABCCASAAIAEoAgQQggEgARAxCwtVACADQQA2AgAgA0EANgIEIAMgATYCCCACIAM2AgAgACgCACgCACIBBEAgACABNgIAIAIoAgAhAwsgACgCBCADENMDIABBCGoiACAAKAIAQQFqNgIAC6MBAQJ/IABBBGoiBCgCACEDIABBBGohACADBEACQCACKAIAIQQgACECIAMhAAJAAkADQAJAIAQgACgCECIDSQR/IAAoAgAiA0UNASAAIQIgAwUgAyAETw0EIABBBGoiAigCACIDRQ0DIAMLIQAMAQsLIAEgADYCAAwCCyABIAA2AgAgAiEADAELIAEgADYCACACIQALBSABIAQ2AgAgBCEACyAAC3IBA38jAyEDIwNBEGokAyABKAIAKAIIIQQgASAAQQIgAyIAIARBH3FBoAFqEQQABH9BAAUCfyAAQZIsQQIQgAFFBEAgAkEANgIAQQEMAQsgAEGULEECEIABBH9BAAUgAkEBNgIAQQELCwshBSADJAMgBQs1ACAAQQRqIQAgAiABayICQQBKBEAgACgCACABIAIQSRogACAAKAIAIAJBAnZBAnRqNgIACwspACAAQQA2AgAgAEEANgIEIABBADYCCCABBEAgACABEGwgACABEOADCwsVACAALwEAQYAETCAALwECQYAETHEL+QMBCn8jAyEHIwNBMGokAyAHQRRqIQUgB0EIaiEGIAdBBGohBCAHIgNBJGoiCEEANgIAIANBIGoiCUEANgIAAn8CQAJAAkAgAEGBBBAqRQ0AIABBggQQKkUNACAAQYEEIAkQMARAIABBggQgCBAwDQILQQAMAwsgAEGRAhAqBEAgAEGXAhAqBEAgBUEANgIAIAVBADYCBCAFQQA2AgggBkEANgIAIAZBBGoiCkEANgIAIAZBADYCCAJ/IABB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NgIAIAEQNiABQZwSNgIAIAMgATYCACACKAIAIgQgBSgCAEkEQCAEIAE2AgAgAiACKAIAQQRqNgIABSAAIAMQNwtBBBArIgFBADYCACABEDYgAUGAEjYCACADIAE2AgAgAigCACIEIAUoAgBJBEAgBCABNgIAIAIgAigCAEEEajYCAAUgACADEDcLQQQQKyIBQQA2AgAgARA2IAFB5BE2AgAgAyABNgIAIAIoAgAiBCAFKAIASQRAIAQgATYCACACIAIoAgBBBGo2AgAFIAAgAxA3C0EEECsiAUEANgIAIAEQNiABQcgRNgIAIAMgATYCACACKAIAIgQgBSgCAEkEQCAEIAE2AgAgAiACKAIAQQRqNgIABSAAIAMQNwtBBBArIgFBADYCACABEDYgAUGsETYCACADIAE2AgAgAigCACIEIAUoAgBJBEAgBCABNgIAIAIgAigCAEEEajYCAAUgACADEDcLQQQQKyIBQQA2AgAgARA2IAFBkBE2AgAgAyABNgIAIAIoAgAiBCAFKAIASQRAIAQgATYCACACIAIoAgBBBGo2AgAFIAAgAxA3C0EEECsiAUEANgIAIAEQNiABQfQQNgIAIAMgATYCACACKAIAIgQgBSgCAEkEQCAEIAE2AgAgAiACKAIAQQRqNgIABSAAIAMQNwtBBBArIgFBADYCACABEDYgAUHYEDYCACADIAE2AgAgAigCACIEIAUoAgBJBEAgBCABNgIAIAIgAigCAEEEajYCAAUgACADEDcLQQQQKyIBQQA2AgAgARA2IAFBvBA2AgAgAyABNgIAIAIoAgAiBCAFKAIASQRAIAQgATYCACACIAIoAgBBBGo2AgAFIAAgAxA3C0EEECsiAUEANgIAIAEQNiABQaAQNgIAIAMgATYCACACKAIAIgQgBSgCAEkEQCAEIAE2AgAgAiACKAIAQQRqNgIABSAAIAMQNwtBBBArIgFBADYCACABEDYgAUGEEDYCACADIAE2AgAgAigCACIEIAUoAgBJBEAgBCABNgIAIAIgAigCAEEEajYCAAUgACADEDcLQQQQKyIBQQA2AgAgARA2IAFB6A82AgAgAyABNgIAIAIoAgAiBCAFKAIASQRAIAQgATYCACACIAIoAgBBBGo2AgAFIAAgAxA3C0EEECsiAUEANgIAIAEQNiABQcwPNgIAIAMgATYCACAC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AgQgBBAtIAoQLUEAIAMoAgAgAEEDdGooAgRFDQIaCyAAQQFqIgAgCygCAEkNAAsgCCgCAEUNAUEACwwBCyACIANHBEAgAiADKAIAIAMoAgQQ5QMLQQELIQAgAxA0IAcQLQVBACEACwVBACEACyAGJAMgAAvOAQEGfyAAKAIAIgMhBSACIAEiBmsiBCAAQQhqIgcoAgAgA2tLBEAgABCLAUH/////ByAESQRAEAAFIAAgBCAHKAIAIAAoAgBrIgNBAXQiBSAFIARJG0H/////ByADQf////8DSRsQkwEgACABIAIgBBCMAQsFIAQgAEEEaiIHKAIAIANrIghLIQMgASAIaiACIAMbIgggBmsiBgRAIAUgASAGEHoaCyADBEAgACAIIAIgACgCACAEIAcoAgBrahCMAQUgByAFIAZqNgIACwsLCQAgAEEMahA0C0sBBH8gACgCACIBBEACfyABIABBBGoiAygCACICRgR/IAEFA0AgAkFYaiICEDsgASACRw0ACyAAKAIACyEEIAMgATYCACAECxAxCwsyAQF/IABBBGoiAigCACEAA0AgAEEAOgAAIAIgAigCAEEBaiIANgIAIAFBf2oiAQ0ACwsHACAAKAIIC0IBA38gACgCBCICIABBCGoiAygCACIBRwRAIAMgAUF8aiACa0ECdkF/c0ECdCABajYCAAsgACgCACIABEAgABAxCwu2AQEFfyABQQRqIgIoAgBBACAAQQRqIgUoAgAgACgCACIEayIGQQJ1a0ECdGohAyACIAM2AgAgBkEASgR/IAMgBCAGEEkaIAIhBCACKAIABSACIQQgAwshAiAAKAIAIQMgACACNgIAIAQgAzYCACAFKAIAIQMgBSABQQhqIgIoAgA2AgAgAiADNgIAIABBCGoiACgCACECIAAgAUEMaiIAKAIANgIAIAAgAjYCACABIAQoAgA2AgALXgECfyAAQQxqIgVBADYCACAAIAM2AhAgAQRAIAFB/////wNLBEAQAAUgAUECdBArIQQLCyAAIAQ2AgAgACACQQJ0IARqIgI2AgggACACNgIEIAUgAUECdCAEajYCAAs7AQJ/IwMhASMDQRBqJAMgAUIANwIAIAFBADYCCCABQa8iQQYQKCAAQaAfIAEQPiECIAEQJyABJAMgAguTAgEHfyMDIQIjA0FAayQDIAJBMGoiBUIA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EgBBDSAAIAEgAiADIAQgBRABCxIAQQwgACABIAIgAyAEIAUQAQsSAEELIAAgASACIAMgBCAFEAELEgBBCiAAIAEgAiADIAQgBRABCxIAQQkgACABIAIgAyAEIAUQAQsSAEEIIAAgASACIAMgBCAFEAELEgBBByAAIAEgAiADIAQgBRABCxIAQQYgACABIAIgAyAEIAUQAQsSAEEFIAAgASACIAMgBCAFEAELEgBBBCAAIAEgAiADIAQgBRABCxIAQQMgACABIAIgAyAEIAUQAQsSAEECIAAgASACIAMgBCAFEAELEgBBASAAIAEgAiADIAQgBRABCxIAQQAgACABIAIgAyAEIAUQAQsQAEETIAAgASACIAMgBBACCxAAQRIgACABIAIgAyAEEAILEABBESAAIAEgAiADIAQQAgsQAEEQIAAgASACIAMgBBACCxAAQQ8gACABIAIgAyAEEAILEABBDiAAIAEgAiADIAQQAgsQAEENIAAgASACIAMgBBACCxAAQQwgACABIAIgAyAEEAILEABBCyAAIAEgAiADIAQQAgsQAEEKIAAgASACIAMgBBACCxAAQQkgACABIAIgAyAEEAILEABBCCAAIAEgAiADIAQQAgsQAEEHIAAgASACIAMgBBACCxAAQQYgACABIAIgAyAEEAILEABBBSAAIAEgAiADIAQQAgsQAEEEIAAgASACIAMgBBACCxAAQQMgACABIAIgAyAEEAILEABBAiAAIAEgAiADIAQQAgsQAEEBIAAgASACIAMgBBACCxAAQQAgACABIAIgAyAEEAILDgBBEyAAIAEgAiADEAMLDgBBEiAAIAEgAiADEAMLDgBBESAAIAEgAiADEAMLDgBBECAAIAEgAiADEAMLLgEBfyMDIQMjA0EQaiQDIAMgADYCACADIAEQnAEQnQEgAyACEJwBEJ0BIAMkAwsOAEEPIAAgASACIAMQAwsOAEEOIAAgASACIAMQAwsOAEENIAAgASACIAMQAwsOAEEMIAAgASACIAMQAwsOAEELIAAgASACIAMQAwsOAEEKIAAgASACIAMQAwsOAEEJIAAgASACIAMQAwsOAEEIIAAgASACIAMQAwsOAEEHIAAgASACIAMQAwsOAEEGIAAgASACIAMQAwt6AQV/IwMhBCMDQRBqJANBuDgsAABFBEBBuDgsAABBAUYEf0EABUG4OEEBOgAAQQELBEACfyMDIQcjA0EQaiQDQQNBrA4QHiEGIAcLJANBwDggBjYCAAsLAn9BwDgoAgAhCCAEIAIgAxCOAiAICyAAIAEgBBAgIAQkAwsOAEEFIAAgASACIAMQAwsOAEEEIAAgASACIAMQAwsOAEEDIAAgASACIAMQAwsOAEECIAAgASACIAMQAwsOAEEBIAAgASACIAMQAwsOAEEAIAAgASACIAMQAwsIAEETIAAQBAsIAEESIAAQBAsIAEERIAAQBAsmAQF/IwMhAiMDQRBqJAMgAiABEJ4BIABBwA0gAhAPNgIAIAIkAwsIAEEQIAAQBAsIAEEPIAAQBAsIAEEOIAAQBAsIAEENIAAQBAsIAEEMIAAQBAsIAEELIAAQBAsIAEEKIAAQBAsIAEEJIAAQBAsIAEEIIAAQBAsIAEEHIAAQBAsyAQF/IwMhAyMDQRBqJAMgASgCACEBIAMgAhCjAiAAIAEgAygCABAdEG4gAxAsIAMkAwsIAEEGIAAQBAsIAEEFIAAQBAsIAEEEIAAQBAsIAEEDIAAQBAsIAEECIAAQBAsIAEEBIAAQBAsIAEEAIAAQBAsQACABIABBP3FB4AFqEQAACwYAQRMQBQsGAEESEAULSwIBfwJ8IwMhASMDQRBqJAMCfCAAKAIAQagOKAIAIAFBBGoiABAhIQMgASAAKAIANgIAIAEoAgAQGiABJAMgAwtEAAAAAAAAAABiCwYAQREQBQsGAEEQEAULBgBBDxAFCwYAQQ4QBQsGAEENEAULBgBBDBAFCwYAQQsQBQsGAEEKEAULBgBBCRAFCwYAQQgQBQs0AQF/IwMhAiMDQRBqJAMgAiAANgIAIAIoAgAgAS8BADYCACACIAIoAgBBCGo2AgAgAiQDCwYAQQcQBQsGAEEGEAULBgBBBRAFCwYAQQQQBQsGAEEDEAULBgBBAhAFCwYAQQEQBQsGAEEAEAULDgAgAEEfcUHAAWoRBQALDgBBEyAAIAEgAiADEAYLDgBBEiAAIAEgAiADEAYLDgBBESAAIAEgAiADEAYLDgBBECAAIAEgAiADEAYLDgBBDyAAIAEgAiADEAYLDgBBDiAAIAEgAiADEAYLDgBBDSAAIAEgAiADEAYLDgBBDCAAIAEgAiADEAYLDgBBCyAAIAEgAiADEAYLDgBBCiAAIAEgAiADEAYLDgBBCSAAIAEgAiADEAYLDgBBCCAAIAEgAiADEAYLDgBBByAAIAEgAiADEAYLDgBBBiAAIAEgAiADEAYLDgBBBSAAIAEgAiADEAYLDgBBBCAAIAEgAiADEAYLDgBBAyAAIAEgAiADEAYLDgBBAiAAIAEgAiADEAYLDgBBASAAIAEgAiADEAYLDgBBACAAIAEgAiADEAYLDABBEyAAIAEgAhAHCwwAQRIgACABIAIQBwsMAEERIAAgASACEAcLDABBECAAIAEgAhAHCwwAQQ8gACABIAIQBwsMAEEOIAAgASACEAcLDABBDSAAIAEgAhAHCwwAQQwgACABIAIQBwsMAEELIAAgASACEAcLDABBCiAAIAEgAhAHCwwAQQkgACABIAIQBwsMAEEIIAAgASACEAcLDABBByAAIAEgAhAHCwwAQQYgACABIAIQBwsMAEEFIAAgASACEAcLDABBBCAAIAEgAhAHCwwAQQMgACABIAIQBwsMAEECIAAgASACEAcLDABBASAAIAEgAhAHCwwAQQAgACABIAIQBwsKAEETIAAgARAICwoAQRIgACABEAgLCgBBESAAIAEQCAsKAEEQIAAgARAICwoAQQ8gACABEAgLCgBBDiAAIAEQCAsKAEENIAAgARAICwoAQQwgACABEAgLCgBBCyAAIAEQCAsKAEEKIAAgARAICwoAQQkgACABEAgLCgBBCCAAIAEQCAsKAEEHIAAgARAICwoAQQYgACABEAgLCgBBBSAAIAEQCAsKAEEEIAAgARAICwoAQQMgACABEAgLCgBBAiAAIAEQCAsKAEEBIAAgARAICwoAQQAgACABEAgLCABBEyAAEAkLCABBEiAAEAkLCABBESAAEAkLCABBECAAEAkLCABBDyAAEAkLCABBDiAAEAkLCABBDSAAEAkLCABBDCAAEAkLCABBCyAAEAkLCABBCiAAEAkLCABBCSAAEAkLCABBCCAAEAkLCABBByAAEAkLCABBBiAAEAkLCABBBSAAEAkLCABBBCAAEAkLCABBAyAAEAkLCABBAiAAEAkLCABBASAAEAkLCABBACAAEAkLWAEDfyAALAALIgFBAEgEfyAAKAIEIgJBBGoQYSIBIAI2AgAgACgCACEDIAIFIAFB/wFxIgJBBGoQYSIBIAI2AgAgACEDIAILIQAgAUEEaiADIAAQSRogAQtzAQJ/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NgIEIABBADYCCCABQQRqIgMoAgAgASgCAGsiBEEobSECIAQEQCAAIAIQxgMgACABKAIAIAMoAgAQxQMLC0wBA38gAEEANgIAIABBADYCBCAAQQA2AgggAUEEaiIDKAIAIAEoAgBrIgRBAnUhAiAEBEAgACACEGwgACABKAIAIAMoAgAgAhCGAQsLbwEDfyMDIQQjA0EgaiQDIAQgASgCADYCACAEQQxqIgUgBCgCADYCACAAIAUgBEEIaiIGIARBBGogAhCvASICKAIAIgFFBEAgBSAAIAMQxAMgACAGKAIAIAIgBSgCABCDASAFKAIAIQELIAQkAyABC2ABBH8gACABKAIANgIAIAAgASgCBCIDNgIEIAAgAUEIaiIEKAIAIgU2AgggAEEEaiECIAUEQCADIAI2AgggASABQQRqIgA2AgAgAEEANgIAIARBADYCAAUgACACNgIACwvhAQEDfyAAIAEQygMgAEEMaiICQQA2AgAgAEEQaiIDQQA2AgAgAEEUaiIEQQA2AgAgAiABQQxqIgIoAgA2AgAgAyABQRBqIgMoAgA2AgAgBCABQRRqIgQoAgA2AgAgBEEANgIAIANBADYCACACQQA2AgAgAEEYaiICQQA2AgAgAEEcaiIDQQA2AgAgAEEgaiIEQQA2AgAgAiABQRhqIgIoAgA2AgAgAyABQRxqIgMoAgA2AgAgBCABQSBqIgQoAgA2AgAgBEEANgIAIANBADYCACACQQA2AgAgACABKAIkNgIkC0gBA38gACgCBCIDIABBCGoiAigCACIBRwRAA0AgAiABQVhqIgE2AgAgARA7IAIoAgAiASADRw0ACwsgACgCACIABEAgABAxCwvFAQEGfyABQQRqIQIgACgCACIFIABBBGoiBigCACIERgR/IAAhByACKAIAIQMgAgUgAigCACEDA0AgA0FYaiAEQVhqIgQQywMgAiACKAIAQVhqIgM2AgAgBCAFRw0ACyAAIQcgACgCACEFIAILIQQgByADNgIAIAQgBTYCACAGKAIAIQMgBiABQQhqIgIoAgA2AgAgAiADNgIAIABBCGoiACgCACEDIAAgAUEMaiIAKAIANgIAIAAgAzYCACABIAQoAgA2AgALXQECfyAAQQxqIgVBADYCACAAIAM2AhAgAQRAIAFB5syZM0sEQBAABSABQShsECshBAsLIAAg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AEEgaiEBA0AgARBVIAFBCGoiASACRw0ACyAAQQA6ADggAEE8ahBVIABB3ABqIQIgAEHEAGohAQNAIAEQVSABQQhqIgEgAkcNAAsgAEHcAGoiAEIANwIAIABBADYCCAtgAQN/IwMhAyMDQRBqJAMgA0EEaiIEQQA2AgAgAyIFQQA2AgAgACAEIAMQugEEQCAEKAIAIgRB//8DTQRAIAUoAgAiAEH//wNNBEAgASAEOwEAIAIgADsBAAsLCyADJAMLNQACQAJAAkAgAigCDA4CAQACCyAAIAIgAkECahDdAwwBCyACKAIIIAEgAiACQQJqELgBGgsLhgEBA38gASgCACICIQMgASgCBCACa0ECdSAAKAIEIgEgACgCACICa0ECdSIARyABIAJGcgR/QQAFIABBf2oiBAR/An9BACEAIAIoAgAhAQN/QQAgAEECdCADaigCACABaiIBIABBAWoiAEECdCACaigCAEcNARogACAESQ0AQQELCwVBAQsLCygBAX8gAEEEaiIAKAIAIgJBACABQQJ0EKMBGiAAIAFBAnQgAmo2AgAL6QEBB38gACgCACIFIQkgAiABIgZrQQJ1IgMgAEEIaiIEKAIAIAVrQQJ1SwRAIAAQiwFB/////wMgA0kEQBAABSAAIAMgBCgCACAAKAIAayIFQQF1IgQgBCADSRtB/////wMgBUECdUH/////AUkbEGwgACABIAIgAxCGAQsFIAMgAEEEaiIEKAIAIAVrQQJ1IgdLIQggB0ECdCABaiACIAgbIgcgBmsiBgRAIAUgASAGEHoaCyAGQQJ1IQEgCARAIAAgByACIAMgBCgCACAAKAIAa0ECdWsQhgEFIAQgAUECdCAJajYCAAsLCwQAIwMLwgEBBX8jAyEHIwNBEGokAyACKAIAIgUgASgCACICayIDQW9LBEAQAAsgA0ELSQRAIAAgAzoACwUgACADQRBqQXBxIgYQKyIENgIAIAAgBkGAgICAeHI2AgggACADNgIEIAQhAAsgByEDIAUgAiIERwRAIAUgAmshBiAAIQIDQCADIAQsAAA6AAAgAiADECkgASAEQQFqIgQ2AgAgAkEBaiECIAQgBUcNAAsgACAGaiEACyADQQA6AAAgACADECkgByQDCzIBAX8gAEEEaiICKAIAIQADQCAAQgA3AgAgAiACKAIAQQhqIgA2AgAg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IAAgACgCACgCDEE/cREBACAEED4EQCAFQgA3AgAgBUEANgIIIAVBnShBCBAoIAJCADcDACACQQA2AgggAkGmKEECECggAkEMaiIBQgA3AgAgAUEANgIIIAFBqShBAhAoIAJBGGoiAUIANwMAIAFBADYCCCABQawoQQIQKCACQSRqIgFCADcCACABQQA2AgggAUGvKEECECggAkEwaiIBQgA3AwAgAUEANgIIIAFBsihBAhAoQQAhAQNAIAEEQEEBIQEFIAAgACgCACgCDEE/cREBACEBIAcgBSAIQQxsIAJqEP0DIAMgASAHED4hASAHECcLIAhBAWoiCEEFRw0ACyACQTxqIQADQCAAQXRqIgAQJyAAIAJHDQALIAUQJwVBACEBCyAEECcFQQAhAQsFQQAhAQsFQQAhAQsgAxAtIAYkAyABCwQAQQELdgEDfyMDIQQjA0FAayQDIAQiAkEMaiIDIAAgARA6IAMgAkE4aiIAED8EQCADIAAsAABBAEdBKhBLBEAgAkIANwIAIAJBADYCCCACQdUnQQQQKCADQQggAhBHIQAgAhAnBUEAIQALBUEAIQALIAMQLSAEJAMgAAsEAEECC5ECAQZ/IwMhBCMDQdAAaiQDIARBxQBqIQUgBEEMaiEDIAQiAkEYaiIGIAAgARA6IAYgAkHEAGoiABA/BEAgA0IANwIAIANBADYCCCAALAAABEAgAkIANwIAIAJBADYCCCACQYUnQQgQKAUgAkIANwIAIAJBADYCCCACQY4nQY4nEFEQKAsgA0ELaiIALAAAQQBIBEACfyADKAIAIQcgBUEAOgAAIAcLIAUQKSADQQA2AgQFIAVBADoAACADIAUQKSAAQQA6AAALIANBABA4IAMgAikCADcCACADIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcgBkEGIAMQRyEAIAMQJwVBACEACyAGEC0gBCQDIAALBABBAwurBQELfyMDIQgjA0HgAGokAyAIQd0AaiEEIAhBJGohBiAIIgNBGGohAiADQTBqIgcgACABEDogByADQdwAaiIBED8EQCAGQgA3AgAgBkEANgIIIAZBoiZBEBAoIAdBECAGEEcEf0EBBSADQgA3AwAgA0IANwMIIANCADcDECABLAAABEAgAkIANwIAIAJBADYCCCACQbMmQQQQKCADQQtqIgEsAABBAEgEQAJ/IAMoAgAhCSAEQQA6AAAgCQsgBBApIANBADYCBAUgBEEAOgAAIAMgBBApIAFBADoAAAsgA0EAEDggAyACKQIANwIAIAMgAigCCDYCCCACQgA3AgAgAkEANgIIIAIQJyACQgA3AgAgAkEANgIIIAJBuCZBBBAoBSACQgA3AgAgAkEANgIIIAJBvSZBBBAoIANBC2oiASwAAEEASARAAn8gAygCACEKIARBADoAACAKCyAEECkgA0EANgIEBSAEQQA6AAAgAyAEECkgAUEAOgAACyADQQAQOCADIAIpAgA3AgAgAyACKAIINgIIIAJCADcCACACQQA2AgggAhAnIAJCADcCACACQQA2AgggAkHCJkEEECgLIANBDGoiBUELaiIBLAAAQQBIBH8CfyAFKAIAIQsgBEEAOgAAIAsLIAQQKSADQQA2AhAgBQUgBEEAOgAAIAUgBBApIAFBADoAACAFCyEBIAVBABA4IAEgAikCADcCACABIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcgACgCACgCDCEBIAcgACABQT9xEQEAIAMQPgR/IAAoAgAoAgwhASAHIAAgAUE/cREBACADQQxqED4FQQALIQwgA0EYaiEAA0AgAEF0aiIAECcgACADRw0ACyAMCyEAIAYQJwVBACEACyAHEC0gCCQDIAALjQwBEX8jAyEGIwNBgAFqJAMgBkH1AGohBSAGIgRBPGohAiAEQcgAaiIHIAAgARA6IAcgBEH0AGoiARA/BH8gBEIANwMAIARCADcDCCAEQgA3AxAgBEIANwMYIARCADcDICAEQgA3AyggBEIANwMwIARBADYCOCABLAAABEAgAkIANwIAIAJBADYCCCACQZ4lQQgQKCAEQQtqIgEsAABBAEgEQAJ/IAQoAgAhCCAFQQA6AAAgCAsgBRApIARBADYCBAUgBUEAOgAAIAQgBRApIAFBADoAAAsgBEEAEDggBCACKQIANwIAIAQgAigCCDYCCCACQgA3AgAgAkEANgIIIAIQJyACQgA3AgAgAkEANgIIIAJBpyVBCBAoIARBDGoiAUELaiIDLAAAQQBIBH8CfyABKAIAIQkgBUEAOgAAIAkLIAUQKSAEQQA2AhAgAQUgBUEAOgAAIAEgBRApIANBADoAACABCyEDIAFBABA4IAMgAikCADcCACADIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcgAkIANwIAIAJBADYCCCACQbAlQQQQKCAEQRhqIgFBC2oiAywAAEEASAR/An8gASgCACEKIAVBADoAACAKCyAFECkgBEEANgIcIAEFIAVBADoAACABIAUQKSADQQA6AAAgAQshAyABQQAQOCADIAIpAgA3AgAgAyACKAIINgIIIAJCADcCACACQQA2AgggAhAnIAJCADcCACACQQA2AgggAkG1JUECECggBEEkaiIBQQtqIgMsAABBAEgEfwJ/IAEoAgAhCyAFQQA6AAAgCwsgBRApIARBADYCKCABBSAFQQA6AAAgASAFECkgA0EAOgAAIAELIQMgAUEAEDggAyACKQIANwIAIAMgAigCCDYCCCACQgA3AgAgAkEANgIIIAIQJyACQgA3AgAgAkEANgIIIAJBuCVBCBAoBSACQgA3AgAgAkEANgIIIAJBwSVBCBAoIARBC2oiASwAAEEASARAAn8gBCgCACEMIAVBADoAACAMCyAFECkgBEEANgIEBSAFQQA6AAAgBCAFECkgAUEAOgAACyAEQQAQOCAEIAIpAgA3AgAgBCACKAIINgIIIAJCADcCACACQQA2AgggAhAnIAJCADcCACACQQA2AgggAkHKJUEIECggBEEMaiIBQQtqIgMsAABBAEgEfwJ/IAEoAgAhDSAFQQA6AAAgDQsgBRApIARBADYCECABBSAFQQA6AAAgASAFECkgA0EAOgAAIAELIQMgAUEAEDggAyACKQIANwIAIAMgAigCCDYCCCACQgA3AgAgAkEANgIIIAIQJyACQgA3AgAgAkEANgIIIAJB0yVBBBAoIARBGGoiAUELaiIDLAAAQQBIBH8CfyABKAIAIQ4gBUEAOgAAIA4LIAUQKSAEQQA2AhwgAQUgBUEAOgAAIAEgBRApIANBADoAACABCyEDIAFBABA4IAMgAikCADcCACADIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcgAkIANwIAIAJBADYCCCACQdglQQIQKCAEQSRqIgFBC2oiAywAAEEASAR/An8gASgCACEPIAVBADoAACAPCyAFECkgBEEANgIoIAEFIAVBADoAACABIAUQKSADQQA6AAAgAQshAyABQQAQOCADIAIpAgA3AgAgAyACKAIINgIIIAJCADcCACACQQA2AgggAhAnIAJCADcCACACQQA2AgggAkHbJUEIECgLIARBMGoiAUELaiIDLAAAQQBIBH8CfyABKAIAIRAgBUEAOgAAIBALIAUQKSAEQQA2AjQgAQUgBUEAOgAAIAEgBRApIANBADoAACABCyEDIAFBABA4IAMgAikCADcCACADIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcCfyAEQTxqIRFBACEDQQAhAQNAIAAoAgAoAgwhBSADIAcgACAFQT9xEQEAIAFBDGwgBGoQPkEBcWohAyABQQFqIgFBBUcNAAsgEQshAANAIABBdGoiABAnIAAgBEcNAAsgA0EBSwVBAAshEiAHEC0gBiQDIBILBQBBgAgLegEEfyMDIQIjA0EQaiQDIAJBBGohAyACIQQgAkEMaiIFIABBf2o2AgAgAkEIaiIAQckZEB8QbiAAELkCBEAgAxB5IAQgACAFEK4CIAQoAgBB/hkgAyABEJkCIAQQLCADECwgABAsIAIkAwVB2hlB+RZBxABB7xkQCwsLBABBBQv3BAEJfyMDIQcjA0HgAGokAyAHQdEAaiEEIAciA0EYaiECIANBJGoiBiAAIAEQOiAGIANB0ABqIgEQPwRAIANCADcDACADQgA3AwggA0IANwMQIAEsAAAEQCACQgA3AgAgAkEANgIIIAJBzCRBBBAoIANBC2oiASwAAEEASARAAn8gAygCACEIIARBADoAACAICyAEECkgA0EANgIEBSAEQQA6AAAgAyAEECkgAUEAOgAACyADQQAQOCADIAIpAgA3AgAgAyACKAIINgIIIAJCADcCACACQQA2AgggAhAnIAJCADcCACACQQA2AgggAkHRJEEEECgFIAJCADcCACACQQA2AgggAkHWJEEEECggA0ELaiIBLAAAQQBIBEACfyADKAIAIQkgBEEAOgAAIAkLIAQQKSADQQA2AgQFIARBADoAACADIAQQKSABQQA6AAALIANBABA4IAMgAikCADcCACADIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcgAkIANwIAIAJBADYCCCACQdskQQQQKAsgA0EMaiIBQQtqIgUsAABBAEgEfwJ/IAEoAgAhCiAEQQA6AAAgCgsgBBApIANBADYCECABBSAEQQA6AAAgASAEECkgBUEAOgAAIAELIQQgAUEAEDggBCACKQIANwIAIAQgAigCCDYCCCACQgA3AgAgAkEANgIIIAIQJyAAKAIAKAIMIQEgBiAAIAFBP3ERAQAgAxA+BH8gACgCACgCDCEBIAYgACABQT9xEQEAIANBDGoQPgVBAAshASADQRhqIQADQCAAQXRqIgAQJyAAIANHDQALBUEAIQELIAYQLSAHJAMgAQsEAEEGC3ABA38jAyEDIwNBQGskAyADIgJBDGoiBCAAIAEQOiABIAJBOGoQPwRAIAJCADcCACACQQA2AgggAkGFJEEIECggACgCACgCDCEBIAQgACABQT9xEQEAIAIQPiEAIAIQJwVBACEACyAEEC0gAyQDIAALBABBBwteAQR/IwMhAiMDQUBrJAMgACgCACgCDCEEIAJBDGoiAyABIAAgBEE/cREBABCXASACQgA3AgAgAkEANgIIIAJBwiNBBBAoIANBACACEEchBSACECcgAxAtIAIkAyAFC2EBA38jAyEDIwNBMGokAyADIgIgACABEDogAiACQSxqIgAQPwR/IAIgACwAAEEARyIAQSoQSwR/IAIgABDJAQR/IAIQyAFBAXMFQQALBUEACwVBAAshBCACEC0gAyQDIAQLBABBCQtpAQF/IwMhAiMDQRBqJAMgABBnIAIgAUEwahCwASAAQf8XIAIQPCACECwgAiABQTxqELABIABBhRggAhA8IAIQLCACIAEQuAMgACACEL4DIAIQLCACIAEQsAMgACACELUDIAIQLCACJAMLSwEEfyAAKAIAIgEEQAJ/IAEgAEEEaiIDKAIAIgJGBH8gAQUDQCACQXRqIgIQJyABIAJHDQALIAAoAgALIQQgAyABNgIAIAQLEDELCz4BAX8gAEEEaiEDIAEgAkcEQCADKAIAIQADQCAAIAEQfyADIAMoAgBBDGoiADYCACABQQxqIgEgAkcNAAsLC2oBA38jAyEDIwNBMGokAyADIgIgACABEDogAiACQSxqIgAQPwR/IAIgACwAAEEARyIAQSoQSwR/IAIgABBmBH8gAiAAEMkBBH8gAhDIAQVBAAsFQQALBUEACwVBAAshBCACEC0gAyQDIAQLBABBCgueAQEDfyMDIQQjA0FAayQDIAQiAkEMaiIDIAAgARA6IAMgAkE4aiIBED8EQAJAIAMgASwAAEEARyIBQdKeARBLRQRAIAMgAUHSpgEQS0UEQEEAIQAM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SW50ZXJmYWNlRQBtYWtlcgBtb2RlbABwcmV2aWV3AHRodW1ibmFpbABOU3QzX18yMTJiYXNpY19zdHJpbmdJY05TXzExY2hhcl90cmFpdHNJY0VFTlNfOWFsbG9jYXRvckljRUVFRQBOU3QzX18yMjFfX2Jhc2ljX3N0cmluZ19jb21tb25JTGIxRUVFAGNvbG9yU3BhY2UAb3JpZW50YXRpb24AZm9ybWF0AG9mZnNldABsZW5ndGgAd2lkdGgAaGVpZ2h0AGFkb2JlUmdiAHNSZ2IAZnVuY3Rpb25Qb2ludGVycwBjYWxsYmFja3MuYXM8Ym9vbD4oKQBDYWxsYmFja1Jlc3VsdABjYWxsAE4xMGVtc2NyaXB0ZW4zdmFsRQBONHBpZXgxMmJpbmFyeV9wYXJzZTE0UGFnZWRCeXRlQXJyYXlFACFjaGVja2Vyc18uZW1wdHkoKQBub2RlX21vZHVsZXMvcGlleC9zcmMvaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbi9pbWFnZV90eXBlX3JlY29nbml0aW9uX2xpdGUuY2MAUmVxdWVzdGVkU2l6ZQBhAENvbXBhcmUAYgBGT1ZiAHBvc19pbl9wYWdlIDwgY3VycmVudF9wYWdlX2xlbl8Abm9kZV9tb2R1bGVzL3BpZXgvc3JjL2JpbmFyeV9wYXJzZS9yYW5nZV9jaGVja2VkX2J5dGVfcHRyLmgAb3BlcmF0b3JbXQBvZmZzZXQgPj0gc3ViX2FycmF5X2JlZ2luXyAmJiBvZmZzZXQgPCBzdWJfYXJyYXlfZW5kXwBub2RlX21vZHVsZXMvcGlleC9zcmMvYmluYXJ5X3BhcnNlL3JhbmdlX2NoZWNrZWRfYnl0ZV9wdHIuY2MAbG9hZFBhZ2VGb3JPZmZzZXQAZmFsc2UAcmVtYWluaW5nTGVuZ3RoAE40cGlleDIyaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbjEyX0dMT0JBTF9fTl8xMTRYM2ZUeXBlQ2hlY2tlckUATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExMVR5cGVDaGVja2VyRQBTQU1TVU5HAE40cGlleDIyaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbjEyX0dMT0JBTF9fTl8xMTRTcndUeXBlQ2hlY2tlckUATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNFJ3MlR5cGVDaGVja2VyRQBBUkVDT1lLAE40cGlleDIyaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbjEyX0dMT0JBTF9fTl8xMjFSYXdDb250YXhOVHlwZUNoZWNrZXJFAEZVSklGSUxNAE40cGlleDIyaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbjEyX0dMT0JBTF9fTl8xMTRSYWZUeXBlQ2hlY2tlckUAcWt0awAAAAgAcWt0bgAAAAgATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNFF0a1R5cGVDaGVja2VyRQBBT0MATU0AUEVOVEFYIAAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNFBlZlR5cGVDaGVja2VyRQBPTFlNUABONHBpZXgyMmltYWdlX3R5cGVfcmVjb2duaXRpb24xMl9HTE9CQUxfX05fMTE0T3JmVHlwZUNoZWNrZXJFAE5SVyAgIABOSUtPTgACFAAFABQCBQAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNE5yd1R5cGVDaGVja2VyRQBONHBpZXgyMmltYWdlX3R5cGVfcmVjb2duaXRpb24xMl9HTE9CQUxfX05fMTE0TmVmVHlwZUNoZWNrZXJFAABNUk0ATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNE1yd1R5cGVDaGVja2VyRQBQS1RTAAAAAQBONHBpZXgyMmltYWdlX3R5cGVfcmVjb2duaXRpb24xMl9HTE9CQUxfX05fMTE0TW9zVHlwZUNoZWNrZXJFAPoNAAEA+gAAAgAN+gEAAAD6AgAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNEtkY1R5cGVDaGVja2VyRQDGEgABAAAABADGEwABAAAABADGFAACAMYgAMYtAAQAAAABABLGAQAEAAAAABPGAQAEAAAAABTGAgAAIMYALcYEAAEAAAAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNERuZ1R5cGVDaGVja2VyRQBLT0RBSyAgICAgICAgICAgAAPpAAIADOUAAgDpAwIAAOUMAgAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNERjclR5cGVDaGVja2VyRQAAELqwrLsAAgBIRUFQQ0NEUgBONHBpZXgyMmltYWdlX3R5cGVfcmVjb2duaXRpb24xMl9HTE9CQUxfX05fMTE0Q3J3VHlwZUNoZWNrZXJFAENSAgAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNENyMlR5cGVDaGVja2VyRQBTT05ZAACwAQAEAAAAAAIAAAMAAAMBAAMCAAMDAE40cGlleDIyaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbjEyX0dMT0JBTF9fTl8xMTRBcndUeXBlQ2hlY2tlckUAYXJyYXkAUmFuZ2VDaGVja2VkQnl0ZVB0cgBOU3QzX18yMTRkZWZhdWx0X2RlbGV0ZUlONHBpZXgxMmJpbmFyeV9wYXJzZTEyX0dMT0JBTF9fTl8xMjBNZW1vcnlQYWdlZEJ5dGVBcnJheUVFRQBOU3QzX18yMjBfX3NoYXJlZF9wdHJfcG9pbnRlcklQTjRwaWV4MTJiaW5hcnlfcGFyc2UxMl9HTE9CQUxfX05fMTIwTWVtb3J5UGFnZWRCeXRlQXJyYXlFTlNfMTRkZWZhdWx0X2RlbGV0ZUlTNF9FRU5TXzlhbGxvY2F0b3JJUzRfRUVFRQBONHBpZXgxMmJpbmFyeV9wYXJzZTEyX0dMT0JBTF9fTl8xMjBNZW1vcnlQYWdlZEJ5dGVBcnJheUUAU2l6ZU9mVHlwZSh0eXBlLCBOVUxMICkgKiBjb3VudCA9PSB2YWx1ZS5zaXplKCkAbm9kZV9tb2R1bGVzL3BpZXgvc3JjL3RpZmZfZGlyZWN0b3J5L3RpZmZfZGlyZWN0b3J5LmNjAEFkZEVudHJ5AElJTU12b2lkAGJvb2wAc3RkOjpzdHJpbmcAc3RkOjpiYXNpY19zdHJpbmc8dW5zaWduZWQgY2hhcj4Ac3RkOjp3c3RyaW5nAGVtc2NyaXB0ZW46OnZhbABlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxzaWduZWQgY2hhcj4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8dW5zaWduZWQgY2hhcj4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8c2hvcnQ+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PHVuc2lnbmVkIHNob3J0PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxpbnQ+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PHVuc2lnbmVkIGludD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8aW50OF90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1aW50OF90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxpbnQxNl90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1aW50MTZfdD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8aW50MzJfdD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8dWludDMyX3Q+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PGxvbmcgZG91YmxlPgBOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0llRUUAZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8ZG91YmxlPgBOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0lkRUUAZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8ZmxvYXQ+AE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SWZFRQBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1bnNpZ25lZCBsb25nPgBOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0ltRUUAZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8bG9uZz4ATjEwZW1zY3JpcHRlbjExbWVtb3J5X3ZpZXdJbEVFAE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SWpFRQBOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0lpRUUATjEwZW1zY3JpcHRlbjExbWVtb3J5X3ZpZXdJdEVFAE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SXNFRQBOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0loRUUATjEwZW1zY3JpcHRlbjExbWVtb3J5X3ZpZXdJYUVFAGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PGNoYXI+AE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SWNFRQBOU3QzX18yMTJiYXNpY19zdHJpbmdJd05TXzExY2hhcl90cmFpdHNJd0VFTlNfOWFsbG9jYXRvckl3RUVFRQBOU3QzX18yMTJiYXNpY19zdHJpbmdJaE5TXzExY2hhcl90cmFpdHNJaEVFTlNfOWFsbG9jYXRvckloRUVFRQBkb3VibGUAZmxvYXQAdW5zaWduZWQgbG9uZwBsb25nAHVuc2lnbmVkIGludABpbnQAdW5zaWduZWQgc2hvcnQAc2hvcnQAdW5zaWduZWQgY2hhcgBzaWduZWQgY2hhcgBjaGFyAE5TdDNfXzIxNF9fc2hhcmVkX2NvdW50RQBOU3QzX18yMTlfX3NoYXJlZF93ZWFrX2NvdW50RQBOMTBfX2N4eGFiaXYxMTZfX3NoaW1fdHlwZV9pbmZvRQBTdDl0eXBlX2luZm8ATjEwX19jeHhhYml2MTIwX19zaV9jbGFzc190eXBlX2luZm9FAE4xMF9fY3h4YWJpdjExN19fY2xhc3NfdHlwZV9pbmZvRQBOMTBfX2N4eGFiaXYxMjNfX2Z1bmRhbWVudGFsX3R5cGVfaW5mb0UAdgBjAGgAcwB0AGkAagBsAG0AZgBkAE4xMF9fY3h4YWJpdjEyMV9fdm1pX2NsYXNzX3R5cGVfaW5mb0U=";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(Module["wasmBinary"]){return new Uint8Array(Module["wasmBinary"])}var binary=tryParseAsDataURI(wasmBinaryFile);if(binary){return binary}if(Module["readBinary"]){return Module["readBinary"](wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!Module["wasmBinary"]&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return new Promise(function(resolve,reject){resolve(getBinary())})}function createWasm(env){var info={"env":env,"global":{"NaN":NaN,Infinity:Infinity},"global.Math":Math,"asm2wasm":asm2wasmImports};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}if(!Module["wasmBinary"]&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){WebAssembly.instantiateStreaming(fetch(wasmBinaryFile,{credentials:"same-origin"}),info).then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)})}else{instantiateArrayBuffer(receiveInstantiatedSource)}return{}}Module["asm"]=function(global,env,providedBuffer){env["memory"]=wasmMemory;env["table"]=wasmTable=new WebAssembly.Table({"initial":384,"maximum":384,"element":"anyfunc"});env["__memory_base"]=1024;env["__table_base"]=0;var exports=createWasm(env);return exports};__ATINIT__.push({func:function(){__GLOBAL__sub_I_bind_cpp()}});function ___assert_fail(condition,filename,line,func){abort("Assertion failed: "+UTF8ToString(condition)+", at: "+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])}function ___cxa_pure_virtual(){ABORT=true;throw"Pure virtual function called!"}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function embind_init_charCodes(){var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes}var embind_charCodes=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return function(){"use strict";return body.apply(this,arguments)}}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}var InternalError=undefined;function registerType(rawType,registeredInstance,options){options=options||{};if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}var name=registeredInstance.name;if(!rawType){throwBindingError('type "'+name+'" must have a positive integer typeid pointer')}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError("Cannot register type '"+name+"' twice")}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(function(cb){cb()})}}function __embind_register_bool(rawType,name,size,trueValue,falseValue){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":8,"readValueFromPointer":function(pointer){var heap;if(size===1){heap=HEAP8}else if(size===2){heap=HEAP16}else if(size===4){heap=HEAP32}else{throw new TypeError("Unknown boolean type size: "+name)}return this["fromWireType"](heap[pointer>>shift])},destructorFunction:null})}var emval_free_list=[];var emval_handle_array=[{},{value:undefined},{value:null},{value:true},{value:false}];function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){++count}}return count}function get_first_emval(){for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){return emval_handle_array[i]}}return null}function init_emval(){Module["count_emval_handles"]=count_emval_handles;Module["get_first_emval"]=get_first_emval}function __emval_register(value){switch(value){case undefined:{return 1}case null:{return 2}case true:{return 3}case false:{return 4}default:{var handle=emval_free_list.length?emval_free_list.pop():emval_handle_array.length;emval_handle_array[handle]={refcount:1,value:value};return handle}}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}function __embind_register_emval(rawType,name){name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(handle){var rv=emval_handle_array[handle].value;__emval_decref(handle);return rv},"toWireType":function(destructors,value){return __emval_register(value)},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<<bitshift>>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(value<minRange||value>maxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(heap["buffer"],data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var endChar=HEAPU8[value+4+length];var endCharSwap=0;if(endChar!=0){endCharSwap=endChar;HEAPU8[value+4+length]=0}var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(HEAPU8[currentBytePtr]==0){var stringSegment=UTF8ToString(decodeStartPtr);if(str===undefined)str=stringSegment;else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}if(endCharSwap!=0)HEAPU8[value+4+length]=endCharSwap}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAPU8[value+4+i])}str=a.join("")}_free(value);return str},"toWireType":function(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var getLength;var valueIsOfTypeString=typeof value==="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){getLength=function(){return lengthBytesUTF8(value)}}else{getLength=function(){return value.length}}var length=getLength();var ptr=_malloc(4+length+1);HEAPU32[ptr>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i<length;++i){HEAPU8[ptr+4+i]=value[i]}}}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_std_wstring(rawType,charSize,name){name=readLatin1String(name);var getHeap,shift;if(charSize===2){getHeap=function(){return HEAPU16};shift=1}else if(charSize===4){getHeap=function(){return HEAPU32};shift=2}registerType(rawType,{name:name,"fromWireType":function(value){var HEAP=getHeap();var length=HEAPU32[value>>2];var a=new Array(length);var start=value+4>>shift;for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAP[start+i])}_free(value);return a.join("")},"toWireType":function(destructors,value){var HEAP=getHeap();var length=value.length;var ptr=_malloc(4+length*charSize);HEAPU32[ptr>>2]=length;var start=ptr+4>>shift;for(var i=0;i<length;++i){HEAP[start+i]=value.charCodeAt(i)}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function getTypeName(type){var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function emval_get_global(){function testGlobal(obj){obj["$$$embind_global$$$"]=obj;var success=typeof $$$embind_global$$$==="object"&&obj["$$$embind_global$$$"]===obj;if(!success){delete obj["$$$embind_global$$$"]}return success}if(typeof $$$embind_global$$$==="object"){return $$$embind_global$$$}if(typeof global==="object"&&testGlobal(global)){$$$embind_global$$$=global}else if(typeof window==="object"&&testGlobal(window)){$$$embind_global$$$=window}if(typeof $$$embind_global$$$==="object"){return $$$embind_global$$$}throw Error("unable to get global object.")}function __emval_get_global(name){if(name===0){return __emval_register(emval_get_global())}else{name=getStringOrSymbol(name);return __emval_register(emval_get_global()[name])}}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes,argWireTypes){var a=new Array(argCount);for(var i=0;i<argCount;++i){a[i]=requireRegisteredType(HEAP32[(argTypes>>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var argN=new Array(argCount-1);var invokerFunction=function(handle,name,destructors,args){var offset=0;for(var i=0;i<argCount-1;++i){argN[i]=types[i+1].readValueFromPointer(args+offset);offset+=types[i+1].argPackAdvance}var rv=handle[name].apply(handle,argN);for(var i=0;i<argCount-1;++i){if(types[i+1].deleteObject){types[i+1].deleteObject(argN[i])}}if(!retType.isVoid){return retType.toWireType(destructors,rv)}};return __emval_addMethodCaller(invokerFunction)}function __emval_get_property(handle,key){handle=requireHandle(handle);key=requireHandle(key);return __emval_register(handle[key])}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_new_object(){return __emval_register({})}function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function __emval_set_property(handle,key,value){handle=requireHandle(handle);key=requireHandle(key);value=requireHandle(value);handle[key]=value}function __emval_take_value(type,argv){type=requireRegisteredType(type,"_emval_take_value");var v=type["readValueFromPointer"](argv);return __emval_register(v)}function _abort(){Module["abort"]()}function _emscripten_get_heap_size(){return TOTAL_MEMORY}function abortOnCannotGrowMemory(requestedSize){abort("Cannot enlarge memory arrays to size "+requestedSize+" bytes. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+TOTAL_MEMORY+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")}function emscripten_realloc_buffer(size){var PAGE_MULTIPLE=65536;size=alignUp(size,PAGE_MULTIPLE);var old=Module["buffer"];var oldSize=old.byteLength;try{var result=wasmMemory.grow((size-oldSize)/65536);if(result!==(-1|0)){return Module["buffer"]=wasmMemory.buffer}else{return null}}catch(e){return null}}function _emscripten_resize_heap(requestedSize){var oldSize=_emscripten_get_heap_size();var PAGE_MULTIPLE=65536;var LIMIT=2147483648-PAGE_MULTIPLE;if(requestedSize>LIMIT){return false}var MIN_TOTAL_MEMORY=16777216;var newSize=Math.max(oldSize,MIN_TOTAL_MEMORY);while(newSize<requestedSize){if(newSize<=536870912){newSize=alignUp(2*newSize,PAGE_MULTIPLE)}else{newSize=Math.min(alignUp((3*newSize+2147483648)/4,PAGE_MULTIPLE),LIMIT)}}var replacement=emscripten_realloc_buffer(newSize);if(!replacement||replacement.byteLength!=newSize){return false}updateGlobalBuffer(replacement);updateGlobalBufferViews();TOTAL_MEMORY=newSize;HEAPU32[DYNAMICTOP_PTR>>2]=requestedSize;return true}function _llvm_trap(){abort("trap!")}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest)}function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");InternalError=Module["InternalError"]=extendError(Error,"InternalError");init_emval();var ASSERTIONS=false;function intArrayToString(array){var ret=[];for(var i=0;i<array.length;i++){var chr=array[i];if(chr>255){if(ASSERTIONS){assert(false,"Character code "+chr+" ("+String.fromCharCode(chr)+") at offset "+i+" not in 0x00-0xFF.")}chr&=255}ret.push(String.fromCharCode(chr))}return ret.join("")}var decodeBase64=typeof atob==="function"?atob:function(input){var keyStr="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";var output="";var chr1,chr2,chr3;var enc1,enc2,enc3,enc4;var i=0;input=input.replace(/[^A-Za-z0-9\+\/\=]/g,"");do{enc1=keyStr.indexOf(input.charAt(i++));enc2=keyStr.indexOf(input.charAt(i++));enc3=keyStr.indexOf(input.charAt(i++));enc4=keyStr.indexOf(input.charAt(i++));chr1=enc1<<2|enc2>>4;chr2=(enc2&15)<<4|enc3>>2;chr3=(enc3&3)<<6|enc4;output=output+String.fromCharCode(chr1);if(enc3!==64){output=output+String.fromCharCode(chr2)}if(enc4!==64){output=output+String.fromCharCode(chr3)}}while(i<input.length);return output};function intArrayFromBase64(s){try{var decoded=decodeBase64(s);var bytes=new Uint8Array(decoded.length);for(var i=0;i<decoded.length;++i){bytes[i]=decoded.charCodeAt(i)}return bytes}catch(_){throw new Error("Converting base64 string to bytes failed.")}}function tryParseAsDataURI(filename){if(!isDataURI(filename)){return}return intArrayFromBase64(filename.slice(dataURIPrefix.length))}function jsCall_ii(index,a1){return functionPointers[index](a1)}function jsCall_iii(index,a1,a2){return functionPointers[index](a1,a2)}function jsCall_iiii(index,a1,a2,a3){return functionPointers[index](a1,a2,a3)}function jsCall_iiiii(index,a1,a2,a3,a4){return functionPointers[index](a1,a2,a3,a4)}function jsCall_v(index){functionPointers[index]()}function jsCall_vi(index,a1){functionPointers[index](a1)}function jsCall_viiii(index,a1,a2,a3,a4){functionPointers[index](a1,a2,a3,a4)}function jsCall_viiiii(index,a1,a2,a3,a4,a5){functionPointers[index](a1,a2,a3,a4,a5)}function jsCall_viiiiii(index,a1,a2,a3,a4,a5,a6){functionPointers[index](a1,a2,a3,a4,a5,a6)}var asmGlobalArg={};var asmLibraryArg={"o":abort,"k":jsCall_ii,"j":jsCall_iii,"i":jsCall_iiii,"h":jsCall_iiiii,"g":jsCall_v,"f":jsCall_vi,"e":jsCall_viiii,"d":jsCall_viiiii,"c":jsCall_viiiiii,"m":___assert_fail,"N":___cxa_pure_virtual,"v":___setErrNo,"M":__embind_register_bool,"L":__embind_register_emval,"u":__embind_register_float,"n":__embind_register_integer,"l":__embind_register_memory_view,"t":__embind_register_std_string,"K":__embind_register_std_wstring,"J":__embind_register_void,"I":__emval_as,"H":__emval_call_void_method,"s":__emval_decref,"G":__emval_get_global,"F":__emval_get_method_caller,"E":__emval_get_property,"r":__emval_incref,"D":__emval_new_cstring,"C":__emval_new_object,"B":__emval_run_destructors,"p":__emval_set_property,"q":__emval_take_value,"b":_abort,"A":_emscripten_get_heap_size,"z":_emscripten_memcpy_big,"y":_emscripten_resize_heap,"x":_llvm_trap,"w":abortOnCannotGrowMemory,"a":DYNAMICTOP_PTR};var asm=Module["asm"](asmGlobalArg,asmLibraryArg,buffer);Module["asm"]=asm;var __GLOBAL__sub_I_bind_cpp=Module["__GLOBAL__sub_I_bind_cpp"]=function(){return Module["asm"]["O"].apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return Module["asm"]["P"].apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return Module["asm"]["Q"].apply(null,arguments)};var _free=Module["_free"]=function(){return Module["asm"]["R"].apply(null,arguments)};var _image=Module["_image"]=function(){return Module["asm"]["S"].apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return Module["asm"]["T"].apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return Module["asm"]["W"].apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return Module["asm"]["X"].apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return Module["asm"]["Y"].apply(null,arguments)};var dynCall_v=Module["dynCall_v"]=function(){return Module["asm"]["U"].apply(null,arguments)};var dynCall_vi=Module["dynCall_vi"]=function(){return Module["asm"]["V"].apply(null,arguments)};Module["asm"]=asm;Module["cwrap"]=cwrap;Module["addFunction"]=addFunction;Module["removeFunction"]=removeFunction;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};function run(args){args=args||Module["arguments"];if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}if(what!==undefined){out(what);err(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;throw"abort("+what+"). Build with -s ASSERTIONS=1 for more info."}Module["abort"]=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}Module["noExitRuntime"]=true;run();
diff --git a/ui/file_manager/image_loader/piex/tests.html b/ui/file_manager/image_loader/piex/tests.html index 62b00b6..9d5a8ee 100644 --- a/ui/file_manager/image_loader/piex/tests.html +++ b/ui/file_manager/image_loader/piex/tests.html
@@ -229,7 +229,7 @@ window.onload = function loadPiexModule() { let script = document.createElement('script'); document.head.appendChild(script); - script.src = '/a.out.js'; + script.src = '/piex.wasm'; }; window.onerror = (error) => {
diff --git a/ui/file_manager/integration_tests/file_manager/providers.js b/ui/file_manager/integration_tests/file_manager/providers.js index 0c15e2f..c9a49e7 100644 --- a/ui/file_manager/integration_tests/file_manager/providers.js +++ b/ui/file_manager/integration_tests/file_manager/providers.js
@@ -275,4 +275,32 @@ // ejected. return IGNORE_APP_ERRORS; }; + + /** + * Tests that when online, the install new service button is enabled. + */ + testcase.installNewServiceOnline = async () => { + const appId = await setUpProvider('manifest.json'); + await showProvidersMenu(appId); + + const selector = '#add-new-services-menu:not([hidden]) ' + + 'cr-menu-item[command="#install-new-extension"]:not([disabled])'; + const element = await remoteCall.waitForElement(appId, selector); + chrome.test.assertEq('Install new service', element.text); + chrome.test.assertFalse(element.hidden); + }; + + /** + * Tests that when offline, the install new service button is disabled. + */ + testcase.installNewServiceOffline = async () => { + const appId = await setUpProvider('manifest.json'); + await showProvidersMenu(appId); + + const selector = '#add-new-services-menu:not([hidden]) ' + + 'cr-menu-item[command="#install-new-extension"][disabled]'; + const element = await remoteCall.waitForElement(appId, selector); + chrome.test.assertEq('Install new service', element.text); + chrome.test.assertFalse(element.hidden); + }; })();
diff --git a/ui/file_manager/video_player/css/video_player.css b/ui/file_manager/video_player/css/video_player.css index 5abcbbe..de18f48 100644 --- a/ui/file_manager/video_player/css/video_player.css +++ b/ui/file_manager/video_player/css/video_player.css
@@ -67,6 +67,13 @@ video { height: 100%; left: 0; + + /* + * Since r614513, <video> elements take focus on click which causes yellow + * lines to appear unless the outline is disabled. See crbug/917503. + */ + outline: none; + pointer-events: none; position: absolute; top: 0;
diff --git a/ui/keyboard/keyboard_controller.cc b/ui/keyboard/keyboard_controller.cc index c59a02b..ad85247 100644 --- a/ui/keyboard/keyboard_controller.cc +++ b/ui/keyboard/keyboard_controller.cc
@@ -288,15 +288,16 @@ time_of_last_blur_ = base::Time::UnixEpoch(); UpdateInputMethodObserver(); - for (KeyboardControllerObserver& observer : observer_list_) - observer.OnKeyboardEnabledChanged(true); - ActivateKeyboardInContainer( layout_delegate_->GetContainerForDefaultDisplay()); // Start preloading the virtual keyboard UI in the background, so that it // shows up faster when needed. LoadKeyboardWindowInBackground(); + + // Notify observers after the keyboard window has a root window. + for (KeyboardControllerObserver& observer : observer_list_) + observer.OnKeyboardEnabledChanged(true); } void KeyboardController::DisableKeyboard() { @@ -323,10 +324,12 @@ animation_observer_.reset(); ime_observer_.RemoveAll(); - for (KeyboardControllerObserver& observer : observer_list_) - observer.OnKeyboardEnabledChanged(false); ui_->SetController(nullptr); ui_.reset(); + + // Notify observers after |ui_| is reset so that IsEnabled() is false. + for (KeyboardControllerObserver& observer : observer_list_) + observer.OnKeyboardEnabledChanged(false); } void KeyboardController::ActivateKeyboardInContainer(aura::Window* parent) {
diff --git a/ui/keyboard/keyboard_controller_unittest.cc b/ui/keyboard/keyboard_controller_unittest.cc index 4df6483..e411155 100644 --- a/ui/keyboard/keyboard_controller_unittest.cc +++ b/ui/keyboard/keyboard_controller_unittest.cc
@@ -772,4 +772,55 @@ EXPECT_TRUE(IsKeyboardDisabled()); } +class MockKeyboardControllerObserver : public KeyboardControllerObserver { + public: + MockKeyboardControllerObserver() = default; + ~MockKeyboardControllerObserver() override = default; + + // KeyboardControllerObserver: + MOCK_METHOD1(OnKeyboardEnabledChanged, void(bool is_enabled)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockKeyboardControllerObserver); +}; + +TEST_F(KeyboardControllerTest, OnKeyboardEnabledChangedToEnabled) { + // Start with the keyboard disabled. + keyboard::SetTouchKeyboardEnabled(false); + + MockKeyboardControllerObserver mock_observer; + controller().AddObserver(&mock_observer); + + EXPECT_CALL(mock_observer, OnKeyboardEnabledChanged(true)) + .WillOnce(testing::InvokeWithoutArgs([]() { + auto* controller = keyboard::KeyboardController::Get(); + ASSERT_TRUE(controller); + EXPECT_TRUE(controller->IsEnabled()); + EXPECT_TRUE(controller->GetKeyboardWindow()); + EXPECT_TRUE(controller->GetRootWindow()); + })); + + keyboard::SetTouchKeyboardEnabled(true); + + controller().RemoveObserver(&mock_observer); +} + +TEST_F(KeyboardControllerTest, OnKeyboardEnabledChangedToDisabled) { + MockKeyboardControllerObserver mock_observer; + controller().AddObserver(&mock_observer); + + EXPECT_CALL(mock_observer, OnKeyboardEnabledChanged(false)) + .WillOnce(testing::InvokeWithoutArgs([]() { + auto* controller = keyboard::KeyboardController::Get(); + ASSERT_TRUE(controller); + EXPECT_FALSE(controller->IsEnabled()); + EXPECT_FALSE(controller->GetKeyboardWindow()); + EXPECT_FALSE(controller->GetRootWindow()); + })); + + keyboard::SetTouchKeyboardEnabled(false); + + controller().RemoveObserver(&mock_observer); +} + } // namespace keyboard
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn index bc651e1..1e5aa6f 100644 --- a/ui/ozone/platform/wayland/BUILD.gn +++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -32,6 +32,8 @@ "gpu/wayland_surface_factory.h", "host/wayland_buffer_manager.cc", "host/wayland_buffer_manager.h", + "host/wayland_clipboard.cc", + "host/wayland_clipboard.h", "host/wayland_connection.cc", "host/wayland_connection.h", "host/wayland_connection_connector.cc", @@ -127,7 +129,7 @@ ] if (is_linux && !is_chromeos) { - deps += ["//ui/base/ime/linux"] + deps += [ "//ui/base/ime/linux" ] } defines = [ "OZONE_IMPLEMENTATION" ]
diff --git a/ui/ozone/platform/wayland/host/wayland_clipboard.cc b/ui/ozone/platform/wayland/host/wayland_clipboard.cc new file mode 100644 index 0000000..ae4fb06 --- /dev/null +++ b/ui/ozone/platform/wayland/host/wayland_clipboard.cc
@@ -0,0 +1,87 @@ +// 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 "ui/ozone/platform/wayland/host/wayland_clipboard.h" +#include "ui/ozone/platform/wayland/host/wayland_data_device.h" +#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h" + +namespace ui { + +WaylandClipboard::WaylandClipboard( + WaylandDataDeviceManager* data_device_manager, + WaylandDataDevice* data_device) + : data_device_manager_(data_device_manager), data_device_(data_device) { + DCHECK(data_device_manager_); + DCHECK(data_device_); +} + +WaylandClipboard::~WaylandClipboard() = default; + +void WaylandClipboard::OfferClipboardData( + const PlatformClipboard::DataMap& data_map, + PlatformClipboard::OfferDataClosure callback) { + if (!clipboard_data_source_) { + clipboard_data_source_ = data_device_manager_->CreateSource(); + clipboard_data_source_->WriteToClipboard(data_map); + } + clipboard_data_source_->UpdateDataMap(data_map); + std::move(callback).Run(); +} + +void WaylandClipboard::RequestClipboardData( + const std::string& mime_type, + PlatformClipboard::DataMap* data_map, + PlatformClipboard::RequestDataClosure callback) { + read_clipboard_closure_ = std::move(callback); + + DCHECK(data_map); + data_map_ = data_map; + if (!data_device_->RequestSelectionData(mime_type)) + SetData({}, mime_type); +} + +bool WaylandClipboard::IsSelectionOwner() { + return !!clipboard_data_source_; +} + +void WaylandClipboard::SetSequenceNumberUpdateCb( + PlatformClipboard::SequenceNumberUpdateCb cb) { + CHECK(update_sequence_cb_.is_null()) + << " The callback can be installed only once."; + update_sequence_cb_ = std::move(cb); +} + +void WaylandClipboard::GetAvailableMimeTypes( + PlatformClipboard::GetMimeTypesClosure callback) { + std::move(callback).Run(data_device_->GetAvailableMimeTypes()); +} + +void WaylandClipboard::DataSourceCancelled() { + DCHECK(clipboard_data_source_); + SetData({}, {}); + clipboard_data_source_.reset(); +} + +void WaylandClipboard::SetData(const std::string& contents, + const std::string& mime_type) { + if (!data_map_) + return; + + (*data_map_)[mime_type] = + std::vector<uint8_t>(contents.begin(), contents.end()); + + if (!read_clipboard_closure_.is_null()) { + auto it = data_map_->find(mime_type); + DCHECK(it != data_map_->end()); + std::move(read_clipboard_closure_).Run(it->second); + } + data_map_ = nullptr; +} + +void WaylandClipboard::UpdateSequenceNumber() { + if (!update_sequence_cb_.is_null()) + update_sequence_cb_.Run(); +} + +} // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_clipboard.h b/ui/ozone/platform/wayland/host/wayland_clipboard.h new file mode 100644 index 0000000..61daf6a --- /dev/null +++ b/ui/ozone/platform/wayland/host/wayland_clipboard.h
@@ -0,0 +1,68 @@ +// 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 UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_ + +#include "base/callback.h" +#include "ui/ozone/platform/wayland/host/wayland_data_source.h" +#include "ui/ozone/public/platform_clipboard.h" + +namespace ui { + +class WaylandDataDevice; +class WaylandDataDeviceManager; + +// Handles clipboard operations. +// +// Owned by WaylandConnection, which provides a data device and a data device +// manager. +class WaylandClipboard : public PlatformClipboard { + public: + WaylandClipboard(WaylandDataDeviceManager* data_device_manager, + WaylandDataDevice* data_device); + virtual ~WaylandClipboard(); + + // PlatformClipboard. + void OfferClipboardData( + const PlatformClipboard::DataMap& data_map, + PlatformClipboard::OfferDataClosure callback) override; + void RequestClipboardData( + const std::string& mime_type, + PlatformClipboard::DataMap* data_map, + PlatformClipboard::RequestDataClosure callback) override; + void GetAvailableMimeTypes( + PlatformClipboard::GetMimeTypesClosure callback) override; + bool IsSelectionOwner() override; + void SetSequenceNumberUpdateCb( + PlatformClipboard::SequenceNumberUpdateCb cb) override; + + void DataSourceCancelled(); + void SetData(const std::string& contents, const std::string& mime_type); + void UpdateSequenceNumber(); + + private: + // Holds a temporary instance of the client's clipboard content + // so that we can asynchronously write to it. + PlatformClipboard::DataMap* data_map_ = nullptr; + + // Notifies whenever clipboard sequence number is changed. Can be empty if not + // set. + PlatformClipboard::SequenceNumberUpdateCb update_sequence_cb_; + + // Stores the callback to be invoked upon data reading from clipboard. + PlatformClipboard::RequestDataClosure read_clipboard_closure_; + + std::unique_ptr<WaylandDataSource> clipboard_data_source_; + + // These two instances are owned by the connection. + WaylandDataDeviceManager* const data_device_manager_ = nullptr; + WaylandDataDevice* const data_device_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(WaylandClipboard); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc index 0be2a976..0c72f2d 100644 --- a/ui/ozone/platform/wayland/host/wayland_connection.cc +++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -265,44 +265,6 @@ client_associated_ptr_->OnPresentation(widget, buffer_id, feedback); } -PlatformClipboard* WaylandConnection::GetPlatformClipboard() { - return this; -} - -void WaylandConnection::OfferClipboardData( - const PlatformClipboard::DataMap& data_map, - PlatformClipboard::OfferDataClosure callback) { - if (!clipboard_data_source_) { - clipboard_data_source_ = data_device_manager_->CreateSource(); - clipboard_data_source_->WriteToClipboard(data_map); - } - clipboard_data_source_->UpdateDataMap(data_map); - std::move(callback).Run(); -} - -void WaylandConnection::RequestClipboardData( - const std::string& mime_type, - PlatformClipboard::DataMap* data_map, - PlatformClipboard::RequestDataClosure callback) { - read_clipboard_closure_ = std::move(callback); - - DCHECK(data_map); - data_map_ = data_map; - if (!data_device_->RequestSelectionData(mime_type)) - SetClipboardData({}, mime_type); -} - -bool WaylandConnection::IsSelectionOwner() { - return !!clipboard_data_source_; -} - -void WaylandConnection::SetSequenceNumberUpdateCb( - PlatformClipboard::SequenceNumberUpdateCb cb) { - CHECK(update_sequence_cb_.is_null()) - << " The callback can be installed only once."; - update_sequence_cb_ = std::move(cb); -} - ozone::mojom::WaylandConnectionPtr WaylandConnection::BindInterface() { DCHECK(!binding_.is_bound()); ozone::mojom::WaylandConnectionPtr ptr; @@ -365,38 +327,6 @@ pointer_->ResetFlags(); } -void WaylandConnection::GetAvailableMimeTypes( - PlatformClipboard::GetMimeTypesClosure callback) { - std::move(callback).Run(data_device_->GetAvailableMimeTypes()); -} - -void WaylandConnection::DataSourceCancelled() { - DCHECK(clipboard_data_source_); - SetClipboardData({}, {}); - clipboard_data_source_.reset(); -} - -void WaylandConnection::SetClipboardData(const std::string& contents, - const std::string& mime_type) { - if (!data_map_) - return; - - (*data_map_)[mime_type] = - std::vector<uint8_t>(contents.begin(), contents.end()); - - if (!read_clipboard_closure_.is_null()) { - auto it = data_map_->find(mime_type); - DCHECK(it != data_map_->end()); - std::move(read_clipboard_closure_).Run(it->second); - } - data_map_ = nullptr; -} - -void WaylandConnection::UpdateClipboardSequenceNumber() { - if (!update_sequence_cb_.is_null()) - update_sequence_cb_.Run(); -} - void WaylandConnection::OnDispatcherListChanged() { StartProcessingEvents(); } @@ -429,6 +359,8 @@ DCHECK(!data_device_); wl_data_device* data_device = data_device_manager_->GetDevice(); data_device_ = std::make_unique<WaylandDataDevice>(this, data_device); + clipboard_ = std::make_unique<WaylandClipboard>(data_device_manager_.get(), + data_device_.get()); } // static
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.h b/ui/ozone/platform/wayland/host/wayland_connection.h index 2edbdb7..e38f5e1 100644 --- a/ui/ozone/platform/wayland/host/wayland_connection.h +++ b/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -17,6 +17,7 @@ #include "ui/gfx/buffer_types.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" +#include "ui/ozone/platform/wayland/host/wayland_clipboard.h" #include "ui/ozone/platform/wayland/host/wayland_cursor_position.h" #include "ui/ozone/platform/wayland/host/wayland_data_device.h" #include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h" @@ -26,7 +27,6 @@ #include "ui/ozone/platform/wayland/host/wayland_pointer.h" #include "ui/ozone/platform/wayland/host/wayland_touch.h" #include "ui/ozone/public/interfaces/wayland/wayland_connection.mojom.h" -#include "ui/ozone/public/platform_clipboard.h" namespace ui { @@ -36,9 +36,7 @@ class WaylandWindow; class WaylandZwpLinuxDmabuf; -// TODO(crbug.com/942203): factor out PlatformClipboard to a separate class. class WaylandConnection : public PlatformEventSource, - public PlatformClipboard, public ozone::mojom::WaylandConnection, public base::MessagePumpLibevent::FdWatcher { public: @@ -134,6 +132,8 @@ // Returns the current pointer, which may be null. WaylandPointer* pointer() const { return pointer_.get(); } + WaylandClipboard* clipboard() const { return clipboard_.get(); } + WaylandDataSource* drag_data_source() const { return dragdrop_data_source_.get(); } @@ -151,27 +151,6 @@ WaylandZwpLinuxDmabuf* zwp_dmabuf() const { return zwp_dmabuf_.get(); } - // Clipboard implementation. - PlatformClipboard* GetPlatformClipboard(); - void DataSourceCancelled(); - void SetClipboardData(const std::string& contents, - const std::string& mime_type); - void UpdateClipboardSequenceNumber(); - - // PlatformClipboard. - void OfferClipboardData( - const PlatformClipboard::DataMap& data_map, - PlatformClipboard::OfferDataClosure callback) override; - void RequestClipboardData( - const std::string& mime_type, - PlatformClipboard::DataMap* data_map, - PlatformClipboard::RequestDataClosure callback) override; - void GetAvailableMimeTypes( - PlatformClipboard::GetMimeTypesClosure callback) override; - bool IsSelectionOwner() override; - void SetSequenceNumberUpdateCb( - PlatformClipboard::SequenceNumberUpdateCb cb) override; - // Returns bound pointer to own mojo interface. ozone::mojom::WaylandConnectionPtr BindInterface(); @@ -265,7 +244,7 @@ std::unique_ptr<WaylandDataDeviceManager> data_device_manager_; std::unique_ptr<WaylandDataDevice> data_device_; - std::unique_ptr<WaylandDataSource> clipboard_data_source_; + std::unique_ptr<WaylandClipboard> clipboard_; std::unique_ptr<WaylandDataSource> dragdrop_data_source_; std::unique_ptr<WaylandKeyboard> keyboard_; std::unique_ptr<WaylandOutputManager> wayland_output_manager_; @@ -284,17 +263,6 @@ uint32_t serial_ = 0; - // Holds a temporary instance of the client's clipboard content - // so that we can asynchronously write to it. - PlatformClipboard::DataMap* data_map_ = nullptr; - - // Notifies whenever clipboard sequence number is changed. Can be empty if not - // set. - PlatformClipboard::SequenceNumberUpdateCb update_sequence_cb_; - - // Stores the callback to be invoked upon data reading from clipboard. - RequestDataClosure read_clipboard_closure_; - ozone::mojom::WaylandConnectionClientAssociatedPtr client_associated_ptr_; mojo::Binding<ozone::mojom::WaylandConnection> binding_;
diff --git a/ui/ozone/platform/wayland/host/wayland_data_device.cc b/ui/ozone/platform/wayland/host/wayland_data_device.cc index a8e16cd..f258e8d 100644 --- a/ui/ozone/platform/wayland/host/wayland_data_device.cc +++ b/ui/ozone/platform/wayland/host/wayland_data_device.cc
@@ -175,7 +175,7 @@ const std::string& mime_type) { std::string contents; ReadDataFromFD(std::move(fd), &contents); - connection_->SetClipboardData(contents, mime_type); + connection_->clipboard()->SetData(contents, mime_type); } void WaylandDataDevice::ReadDragDataFromFD( @@ -208,7 +208,7 @@ wl_data_offer* offer) { auto* self = static_cast<WaylandDataDevice*>(data); - self->connection_->UpdateClipboardSequenceNumber(); + self->connection_->clipboard()->UpdateSequenceNumber(); DCHECK(!self->new_offer_); self->new_offer_.reset(new WaylandDataOffer(offer)); @@ -335,7 +335,7 @@ self->selection_offer_.reset(); // Clear Clipboard cache. - self->connection_->SetClipboardData(std::string(), std::string()); + self->connection_->clipboard()->SetData(std::string(), std::string()); return; }
diff --git a/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc b/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc index 406ca31..d5b25bb 100644 --- a/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc
@@ -35,7 +35,7 @@ DCHECK(connection); // See comment above for reasoning to access the WaylandConnection // directly from here. - delegate_ = connection->GetPlatformClipboard(); + delegate_ = connection->clipboard(); DCHECK(delegate_); }
diff --git a/ui/ozone/platform/wayland/host/wayland_data_source.cc b/ui/ozone/platform/wayland/host/wayland_data_source.cc index 592b51f..f8ba38a0 100644 --- a/ui/ozone/platform/wayland/host/wayland_data_source.cc +++ b/ui/ozone/platform/wayland/host/wayland_data_source.cc
@@ -112,7 +112,7 @@ self->connection_->FinishDragSession(self->dnd_action_, self->source_window_); } else { - self->connection_->DataSourceCancelled(); + self->connection_->clipboard()->DataSourceCancelled(); } }
diff --git a/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc b/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc index 08d5dbf8b..8e99460 100644 --- a/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc +++ b/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc
@@ -12,6 +12,10 @@ namespace ui { +namespace { +constexpr uint32_t kImmedVerstion = 3; +} + WaylandZwpLinuxDmabuf::WaylandZwpLinuxDmabuf( zwp_linux_dmabuf_v1* zwp_linux_dmabuf, WaylandConnection* connection) @@ -45,10 +49,6 @@ struct zwp_linux_buffer_params_v1* params = zwp_linux_dmabuf_v1_create_params(zwp_linux_dmabuf_.get()); - // Store the |params| with the corresponding |callback| to identify newly - // created buffer and notify the client about it via the |callback|. - pending_params_.insert(std::make_pair(params, std::move(callback))); - base::ScopedFD fd(file.TakePlatformFile()); for (size_t i = 0; i < planes_count; i++) { @@ -65,10 +65,23 @@ offsets[i], strides[i], modifier_hi, modifier_lo); } - zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, this); - zwp_linux_buffer_params_v1_create(params, size.width(), size.height(), format, - 0); + // It's possible to avoid waiting until the buffer is created and have it + // immediately. This method is only available since the protocol version 3. + if (zwp_linux_dmabuf_v1_get_version(zwp_linux_dmabuf_.get()) >= + kImmedVerstion) { + wl::Object<wl_buffer> buffer(zwp_linux_buffer_params_v1_create_immed( + params, size.width(), size.height(), format, 0)); + std::move(callback).Run(std::move(buffer)); + } else { + // Store the |params| with the corresponding |callback| to identify newly + // created buffer and notify the client about it via the |callback|. + pending_params_.insert(std::make_pair(params, std::move(callback))); + + zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, this); + zwp_linux_buffer_params_v1_create(params, size.width(), size.height(), + format, 0); + } connection_->ScheduleFlush(); }
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc index 55a51f9..d4f26d8b 100644 --- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc +++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -126,7 +126,7 @@ PlatformClipboard* GetPlatformClipboard() override { DCHECK(connection_); - return connection_->GetPlatformClipboard(); + return connection_->clipboard(); } bool IsNativePixmapConfigSupported(gfx::BufferFormat format,
diff --git a/ui/views/cocoa/drag_drop_client_mac.mm b/ui/views/cocoa/drag_drop_client_mac.mm index 34aeec3..da67163 100644 --- a/ui/views/cocoa/drag_drop_client_mac.mm +++ b/ui/views/cocoa/drag_drop_client_mac.mm
@@ -125,7 +125,9 @@ } gfx::Point DragDropClientMac::LocationInView(NSPoint point) const { - return gfx::Point(point.x, NSHeight([bridge_->ns_window() frame]) - point.y); + NSRect content_rect = [bridge_->ns_window() + contentRectForFrameRect:[bridge_->ns_window() frame]]; + return gfx::Point(point.x, NSHeight(content_rect) - point.y); } } // namespace views