diff --git a/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java
index a07d349..4aa7a48 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java
@@ -611,10 +611,12 @@
         return mAwSettings.getOffscreenPreRaster();
     }
 
+    @Override
     public void setDisabledActionModeMenuItems(int menuItems) {
         mAwSettings.setDisabledActionModeMenuItems(menuItems);
     }
 
+    @Override
     public int getDisabledActionModeMenuItems() {
         return mAwSettings.getDisabledActionModeMenuItems();
     }
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/ServiceWorkerClientAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/ServiceWorkerClientAdapter.java
index a1e272dc..8f44de5 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/ServiceWorkerClientAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/ServiceWorkerClientAdapter.java
@@ -82,8 +82,7 @@
             return mRequest.requestHeaders;
         }
 
-        // TODO(mnaganov): Uncomment when we completely switch builds to the next API level.
-        //@Override
+        @Override
         public boolean isRedirect() {
             return mRequest.isRedirect;
         }
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebMessagePortAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/WebMessagePortAdapter.java
index 31a293d..7d343b8 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebMessagePortAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebMessagePortAdapter.java
@@ -25,18 +25,22 @@
         mPort = port;
     }
 
+    @Override
     public void postMessage(WebMessage message) {
         mPort.postMessage(message.getData(), toMessagePorts(message.getPorts()));
     }
 
+    @Override
     public void close() {
         mPort.close();
     }
 
+    @Override
     public void setWebMessageCallback(WebMessageCallback callback) {
         setWebMessageCallback(callback, null);
     }
 
+    @Override
     public void setWebMessageCallback(final WebMessageCallback callback, final Handler handler) {
         mPort.setMessageCallback(new MessagePort.MessageCallback() {
             @Override
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
index 2cde933..a6747a0fa 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
@@ -1541,6 +1541,7 @@
         return (TextClassifier) mAwContents.getTextClassifier();
     }
 
+    @Override
     public void autofill(final SparseArray<AutofillValue> values) {
         mFactory.startYourEngines(false);
         if (checkNeedsPost()) {
@@ -2128,26 +2129,31 @@
 
     // Overrides method added to WebViewProvider.ViewDelegate interface
     // (not called in M and below)
+    @Override
     public Handler getHandler(Handler originalHandler) {
         return originalHandler;
     }
 
     // Overrides method added to WebViewProvider.ViewDelegate interface
     // (not called in M and below)
+    @Override
     public View findFocus(View originalFocusedView) {
         return originalFocusedView;
     }
 
     // Remove from superclass
+    @Override
     public void preDispatchDraw(Canvas canvas) {
         // TODO(leandrogracia): remove this method from WebViewProvider if we think
         // we won't need it again.
     }
 
+    @Override
     public void onStartTemporaryDetach() {
         mAwContents.onStartTemporaryDetach();
     }
 
+    @Override
     public void onFinishTemporaryDetach() {
         mAwContents.onFinishTemporaryDetach();
     }
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index 32419e1f..5d1e621 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -687,6 +687,7 @@
         return (ServiceWorkerController) mServiceWorkerController;
     }
 
+    @Override
     public TokenBindingService getTokenBindingService() {
         synchronized (mLock) {
             if (mTokenBindingManager == null) {
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
index 9b727c6..62fd224 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
@@ -295,8 +295,7 @@
             return mRequest.requestHeaders;
         }
 
-        // TODO(mnaganov): Uncomment when we completely switch builds to the next API level.
-        //@Override
+        @Override
         public boolean isRedirect() {
             return mRequest.isRedirect;
         }
@@ -310,10 +309,12 @@
             mError = error;
         }
 
+        @Override
         public int getErrorCode() {
             return mError.errorCode;
         }
 
+        @Override
         public CharSequence getDescription() {
             return mError.description;
         }
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index 6ebbf37..8a9d929 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -65,6 +65,11 @@
              blink::kWebScreenOrientationLockPortraitSecondary;
 }
 
+// Transpose the given |rect|.
+void TransposeRect(gfx::Rect* rect) {
+  rect->SetRect(rect->y(), rect->x(), rect->height(), rect->width());
+}
+
 }  // namespace
 
 SplitViewController::SplitViewController() {
@@ -235,6 +240,14 @@
             IsCurrentScreenOrientationLandscape(), &left_or_top_rect,
             &right_or_bottom_rect);
 
+  // Only need to adjust the bounds for |left_or_top_rect| since the origin of
+  // the left or top snapped window's bounds is always the origin of the work
+  // area's bounds. It can not be moved to outside of the work area when the
+  // window's minimum size is larger than current acquired window bounds, which
+  // will lead to the divider pass over the window. This is no need for
+  // |right_or_bottom_rect| since its origin of the bounds is flexible.
+  AdjustLeftOrTopSnappedWindowBoundsDuringResizing(&left_or_top_rect);
+
   if (IsLeftWindowOnTopOrLeftOfScreen(screen_orientation_))
     return (snap_position == LEFT) ? left_or_top_rect : right_or_bottom_rect;
   else
@@ -274,13 +287,9 @@
       GetBoundedPosition(location_in_screen, work_area_bounds);
 
   // Update |divider_position_|.
-  const int previous_divider_position = divider_position_;
   UpdateDividerPosition(modified_location_in_screen);
   NotifyDividerPositionChanged();
 
-  // Restack windows order if necessary.
-  RestackWindows(previous_divider_position, divider_position_);
-
   // Update the black scrim layer's bounds and opacity.
   UpdateBlackScrim(modified_location_in_screen);
 
@@ -580,29 +589,6 @@
   black_scrim_layer_->SetOpacity(opacity);
 }
 
-void SplitViewController::RestackWindows(const int previous_divider_position,
-                                         const int current_divider_position) {
-  if (!left_window_ || !right_window_)
-    return;
-  DCHECK(IsSplitViewModeActive());
-  DCHECK_EQ(left_window_->parent(), right_window_->parent());
-
-  const int mid_position = GetDefaultDividerPosition(GetDefaultSnappedWindow());
-  if (std::signbit(previous_divider_position - mid_position) ==
-      std::signbit(current_divider_position - mid_position)) {
-    // No need to restack windows if the divider position doesn't pass over the
-    // middle position.
-    return;
-  }
-
-  if ((current_divider_position < mid_position) ==
-      IsLeftWindowOnTopOrLeftOfScreen(screen_orientation_)) {
-    left_window_->parent()->StackChildAbove(right_window_, left_window_);
-  } else {
-    left_window_->parent()->StackChildAbove(left_window_, right_window_);
-  }
-}
-
 void SplitViewController::UpdateSnappedWindowsAndDividerBounds() {
   DCHECK(IsSplitViewModeActive());
 
@@ -776,4 +762,32 @@
   }
 }
 
+void SplitViewController::AdjustLeftOrTopSnappedWindowBoundsDuringResizing(
+    gfx::Rect* left_or_top_rect) {
+  if (!is_resizing_)
+    return;
+
+  aura::Window* left_or_top_window =
+      IsLeftWindowOnTopOrLeftOfScreen(screen_orientation_) ? left_window_
+                                                           : right_window_;
+  bool is_landscape = IsCurrentScreenOrientationLandscape();
+  int minimum_width = 0;
+  if (left_or_top_window && left_or_top_window->delegate()) {
+    gfx::Size minimum_size = left_or_top_window->delegate()->GetMinimumSize();
+    minimum_width = is_landscape ? minimum_size.width() : minimum_size.height();
+  }
+
+  if (!is_landscape)
+    TransposeRect(left_or_top_rect);
+
+  if (left_or_top_rect->width() < minimum_width) {
+    left_or_top_rect->set_x(left_or_top_rect->x() -
+                            (minimum_width - left_or_top_rect->width()));
+    left_or_top_rect->set_width(minimum_width);
+  }
+
+  if (!is_landscape)
+    TransposeRect(left_or_top_rect);
+}
+
 }  // namespace ash
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index a50d3f3..33e9218 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -164,14 +164,6 @@
   // of the screen.
   void UpdateBlackScrim(const gfx::Point& location_in_screen);
 
-  // Restacks the two snapped windows while dragging the divider. If the divider
-  // was in the left side of the screen, stack |right_window_| above
-  // |left_window_|, otherwise, stack |left_window_| above |right_window_|. It's
-  // necessary since we want the top window increasingly cover the entire
-  // screen as the divider gets closer to the edge of the screen.
-  void RestackWindows(const int previous_divider_position,
-                      const int current_divider_position);
-
   // Updates the bounds for the snapped windows and divider according to the
   // current snap direction.
   void UpdateSnappedWindowsAndDividerBounds();
@@ -215,6 +207,14 @@
   // overview mode is active at that moment.
   void OnSnappedWindowMinimizedOrDestroyed(aura::Window* window);
 
+  // Adjust the bounds of the left or top snapped window during resizing when
+  // its minimum size is larger than current window bounds to make sure it can
+  // be moved outside of the work area in this case. Note, no need to adjust
+  // when it is not during resizing since the window will not be snapped to a
+  // position that smaller than its minimum size.
+  void AdjustLeftOrTopSnappedWindowBoundsDuringResizing(
+      gfx::Rect* left_or_top_rect);
+
   // The current left/right snapped window.
   aura::Window* left_window_ = nullptr;
   aura::Window* right_window_ = nullptr;
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index 950485a..2aa4db7 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -837,4 +837,126 @@
   EXPECT_FALSE(split_view_controller()->CanSnap(window1.get()));
 }
 
+// Tests that the left or top snapped window can be moved outside of work area
+// when its minimum size is larger than its current bounds.
+TEST_F(SplitViewControllerTest, SnapWindowBoundsWithMinimumSizeTest) {
+  int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
+  display::DisplayManager* display_manager = Shell::Get()->display_manager();
+  display::test::ScopedSetInternalDisplayId set_internal(display_manager,
+                                                         display_id);
+  ScreenOrientationControllerTestApi test_api(
+      Shell::Get()->screen_orientation_controller());
+
+  const gfx::Rect bounds(0, 0, 300, 200);
+  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
+  aura::test::TestWindowDelegate* delegate1 =
+      static_cast<aura::test::TestWindowDelegate*>(window1->delegate());
+
+  // Set the screen orientation to LANDSCAPE_PRIMARY
+  test_api.SetDisplayRotation(display::Display::ROTATE_0,
+                              display::Display::ROTATION_SOURCE_ACTIVE);
+  EXPECT_EQ(test_api.GetCurrentOrientation(),
+            blink::kWebScreenOrientationLockLandscapePrimary);
+
+  gfx::Rect display_bounds =
+      split_view_controller()->GetDisplayWorkAreaBoundsInScreen(window1.get());
+  EXPECT_TRUE(split_view_controller()->CanSnap(window1.get()));
+  split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT);
+  delegate1->set_minimum_size(
+      gfx::Size(display_bounds.width() * 0.4f, display_bounds.height()));
+
+  gfx::Rect divider_bounds =
+      split_view_divider()->GetDividerBoundsInScreen(false);
+  split_view_controller()->StartResize(divider_bounds.CenterPoint());
+  gfx::Point resize_point(display_bounds.width() * 0.33f, 0);
+  split_view_controller()->Resize(resize_point);
+
+  gfx::Rect snapped_window_bounds =
+      split_view_controller()->GetSnappedWindowBoundsInScreen(
+          window1.get(), SplitViewController::LEFT);
+  EXPECT_LT(snapped_window_bounds.x(), display_bounds.x());
+  EXPECT_EQ(snapped_window_bounds.width(),
+            window1->delegate()->GetMinimumSize().width());
+  EndSplitView();
+
+  // Rotate the screen by 90 degree.
+  test_api.SetDisplayRotation(display::Display::ROTATE_90,
+                              display::Display::ROTATION_SOURCE_ACTIVE);
+  EXPECT_EQ(test_api.GetCurrentOrientation(),
+            blink::kWebScreenOrientationLockPortraitPrimary);
+
+  display_bounds =
+      split_view_controller()->GetDisplayWorkAreaBoundsInScreen(window1.get());
+  delegate1->set_minimum_size(
+      gfx::Size(display_bounds.width(), display_bounds.height() * 0.4f));
+  EXPECT_TRUE(split_view_controller()->CanSnap(window1.get()));
+  split_view_controller()->SnapWindow(window1.get(),
+                                      SplitViewController::RIGHT);
+  divider_bounds = split_view_divider()->GetDividerBoundsInScreen(false);
+  split_view_controller()->StartResize(divider_bounds.CenterPoint());
+  resize_point.SetPoint(0, display_bounds.height() * 0.33f);
+  split_view_controller()->Resize(resize_point);
+
+  snapped_window_bounds =
+      split_view_controller()->GetSnappedWindowBoundsInScreen(
+          window1.get(), SplitViewController::RIGHT);
+  EXPECT_LT(snapped_window_bounds.y(), display_bounds.y());
+  EXPECT_EQ(snapped_window_bounds.height(),
+            window1->delegate()->GetMinimumSize().height());
+  EndSplitView();
+
+  // Rotate the screen by 180 degree.
+  test_api.SetDisplayRotation(display::Display::ROTATE_180,
+                              display::Display::ROTATION_SOURCE_ACTIVE);
+  EXPECT_EQ(test_api.GetCurrentOrientation(),
+            blink::kWebScreenOrientationLockLandscapeSecondary);
+
+  display_bounds =
+      split_view_controller()->GetDisplayWorkAreaBoundsInScreen(window1.get());
+  delegate1->set_minimum_size(
+      gfx::Size(display_bounds.width() * 0.4f, display_bounds.height()));
+  EXPECT_TRUE(split_view_controller()->CanSnap(window1.get()));
+  split_view_controller()->SnapWindow(window1.get(),
+                                      SplitViewController::RIGHT);
+
+  divider_bounds = split_view_divider()->GetDividerBoundsInScreen(false);
+  split_view_controller()->StartResize(divider_bounds.CenterPoint());
+  resize_point.SetPoint(display_bounds.width() * 0.33f, 0);
+  split_view_controller()->Resize(resize_point);
+
+  snapped_window_bounds =
+      split_view_controller()->GetSnappedWindowBoundsInScreen(
+          window1.get(), SplitViewController::RIGHT);
+  EXPECT_LT(snapped_window_bounds.x(), display_bounds.x());
+  EXPECT_EQ(snapped_window_bounds.width(),
+            window1->delegate()->GetMinimumSize().width());
+  EndSplitView();
+
+  // Rotate the screen by 270 degree.
+  test_api.SetDisplayRotation(display::Display::ROTATE_270,
+                              display::Display::ROTATION_SOURCE_ACTIVE);
+  EXPECT_EQ(test_api.GetCurrentOrientation(),
+            blink::kWebScreenOrientationLockPortraitSecondary);
+
+  display_bounds =
+      split_view_controller()->GetDisplayWorkAreaBoundsInScreen(window1.get());
+  delegate1->set_minimum_size(
+      gfx::Size(display_bounds.width(), display_bounds.height() * 0.4f));
+  EXPECT_TRUE(split_view_controller()->CanSnap(window1.get()));
+  split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT);
+
+  divider_bounds = split_view_divider()->GetDividerBoundsInScreen(false);
+  split_view_controller()->StartResize(divider_bounds.CenterPoint());
+  resize_point.SetPoint(0, display_bounds.height() * 0.33f);
+  split_view_controller()->Resize(resize_point);
+
+  snapped_window_bounds =
+      split_view_controller()->GetSnappedWindowBoundsInScreen(
+          window1.get(), SplitViewController::LEFT);
+  EXPECT_LT(snapped_window_bounds.y(), display_bounds.y());
+  EXPECT_EQ(snapped_window_bounds.height(),
+            window1->delegate()->GetMinimumSize().height());
+  EndSplitView();
+}
+
 }  // namespace ash
diff --git a/base/metrics/histogram_samples.cc b/base/metrics/histogram_samples.cc
index f12cdc9..10243bb 100644
--- a/base/metrics/histogram_samples.cc
+++ b/base/metrics/histogram_samples.cc
@@ -7,6 +7,7 @@
 #include <limits>
 
 #include "base/compiler_specific.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/numerics/safe_math.h"
 #include "base/pickle.h"
@@ -251,6 +252,16 @@
   subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, count);
 }
 
+void HistogramSamples::RecordNegativeSample(NegativeSampleReason reason,
+                                            HistogramBase::Count increment) {
+  UMA_HISTOGRAM_ENUMERATION("UMA.NegativeSamples.Reason", reason,
+                            MAX_NEGATIVE_SAMPLE_REASONS);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("UMA.NegativeSamples.Increment", increment, 1,
+                              1 << 30, 100);
+  UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.NegativeSamples.Histogram",
+                              static_cast<int32_t>(id()));
+}
+
 SampleCountIterator::~SampleCountIterator() {}
 
 bool SampleCountIterator::GetBucketIndex(size_t* index) const {
diff --git a/base/metrics/histogram_samples.h b/base/metrics/histogram_samples.h
index d1f95a4..23237b0 100644
--- a/base/metrics/histogram_samples.h
+++ b/base/metrics/histogram_samples.h
@@ -160,6 +160,19 @@
   }
 
  protected:
+  enum NegativeSampleReason {
+    SAMPLES_HAVE_LOGGED_BUT_NOT_SAMPLE,
+    SAMPLES_SAMPLE_LESS_THAN_LOGGED,
+    SAMPLES_ADDED_NEGATIVE_COUNT,
+    SAMPLES_ADD_WENT_NEGATIVE,
+    SAMPLES_ADD_OVERFLOW,
+    SAMPLES_ACCUMULATE_NEGATIVE_COUNT,
+    SAMPLES_ACCUMULATE_WENT_NEGATIVE,
+    DEPRECATED_SAMPLES_ACCUMULATE_OVERFLOW,
+    SAMPLES_ACCUMULATE_OVERFLOW,
+    MAX_NEGATIVE_SAMPLE_REASONS
+  };
+
   // Based on |op| type, add or subtract sample counts data from the iterator.
   enum Operator { ADD, SUBTRACT };
   virtual bool AddSubtractImpl(SampleCountIterator* iter, Operator op) = 0;
@@ -174,6 +187,10 @@
   // Atomically adjust the sum and redundant-count.
   void IncreaseSumAndCount(int64_t sum, HistogramBase::Count count);
 
+  // Record a negative-sample observation and the reason why.
+  void RecordNegativeSample(NegativeSampleReason reason,
+                            HistogramBase::Count increment);
+
   AtomicSingleSample& single_sample() { return meta_->single_sample; }
   const AtomicSingleSample& single_sample() const {
     return meta_->single_sample;
diff --git a/base/metrics/persistent_sample_map.cc b/base/metrics/persistent_sample_map.cc
index 1b319a9..817702f 100644
--- a/base/metrics/persistent_sample_map.cc
+++ b/base/metrics/persistent_sample_map.cc
@@ -18,19 +18,6 @@
 
 namespace {
 
-enum NegativeSampleReason {
-  PERSISTENT_SPARSE_HAVE_LOGGED_BUT_NOT_SAMPLE,
-  PERSISTENT_SPARSE_SAMPLE_LESS_THAN_LOGGED,
-  PERSISTENT_SPARSE_ADDED_NEGATIVE_COUNT,
-  PERSISTENT_SPARSE_ADD_WENT_NEGATIVE,
-  PERSISTENT_SPARSE_ADD_OVERFLOW,
-  PERSISTENT_SPARSE_ACCUMULATE_NEGATIVE_COUNT,
-  PERSISTENT_SPARSE_ACCUMULATE_WENT_NEGATIVE,
-  DEPRECATED_PERSISTENT_SPARSE_ACCUMULATE_OVERFLOW,
-  PERSISTENT_SPARSE_ACCUMULATE_OVERFLOW,
-  MAX_NEGATIVE_SAMPLE_REASONS
-};
-
 // An iterator for going through a PersistentSampleMap. The logic here is
 // identical to that of SampleMapIterator but with different data structures.
 // Changes here likely need to be duplicated there.
@@ -125,27 +112,19 @@
 #if 0  // TODO(bcwhite) Re-enable efficient version after crbug.com/682680.
   *GetOrCreateSampleCountStorage(value) += count;
 #else
-  NegativeSampleReason reason = MAX_NEGATIVE_SAMPLE_REASONS;
   Count* local_count_ptr = GetOrCreateSampleCountStorage(value);
   if (count < 0) {
-    reason = PERSISTENT_SPARSE_ACCUMULATE_NEGATIVE_COUNT;
     if (*local_count_ptr < -count)
-      reason = PERSISTENT_SPARSE_ACCUMULATE_WENT_NEGATIVE;
+      RecordNegativeSample(SAMPLES_ACCUMULATE_WENT_NEGATIVE, -count);
+    else
+      RecordNegativeSample(SAMPLES_ACCUMULATE_NEGATIVE_COUNT, -count);
     *local_count_ptr += count;
   } else {
     Sample old_value = *local_count_ptr;
     Sample new_value = old_value + count;
     *local_count_ptr = new_value;
     if ((new_value >= 0) != (old_value >= 0))
-      reason = PERSISTENT_SPARSE_ACCUMULATE_OVERFLOW;
-  }
-  if (reason != MAX_NEGATIVE_SAMPLE_REASONS) {
-    UMA_HISTOGRAM_ENUMERATION("UMA.NegativeSamples.Reason", reason,
-                              MAX_NEGATIVE_SAMPLE_REASONS);
-    UMA_HISTOGRAM_CUSTOM_COUNTS("UMA.NegativeSamples.Increment", count, 1,
-                                1 << 30, 100);
-    UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.NegativeSamples.Histogram",
-                                static_cast<int32_t>(id()));
+      RecordNegativeSample(SAMPLES_ACCUMULATE_OVERFLOW, count);
   }
 #endif
   IncreaseSumAndCount(strict_cast<int64_t>(count) * value, count);
diff --git a/base/metrics/sample_vector.cc b/base/metrics/sample_vector.cc
index c484ed74..c8b1ac7a 100644
--- a/base/metrics/sample_vector.cc
+++ b/base/metrics/sample_vector.cc
@@ -55,8 +55,14 @@
   }
 
   // Handle the multi-sample case.
-  subtle::NoBarrier_AtomicIncrement(&counts()[bucket_index], count);
+  Count new_value =
+      subtle::NoBarrier_AtomicIncrement(&counts()[bucket_index], count);
   IncreaseSumAndCount(strict_cast<int64_t>(count) * value, count);
+
+  // TODO(bcwhite) Remove after crbug.com/682680.
+  Count old_value = new_value - count;
+  if ((new_value >= 0) != (old_value >= 0) && count > 0)
+    RecordNegativeSample(SAMPLES_ACCUMULATE_OVERFLOW, count);
 }
 
 Count SampleVectorBase::GetCount(Sample value) const {
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index a3227d5..22412e3 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -7,6 +7,7 @@
 import("//build/config/chromecast_build.gni")
 import("//build/config/compiler/compiler.gni")
 import("//build/config/coverage/coverage.gni")
+import("//build/config/host_byteorder.gni")
 import("//build/toolchain/cc_wrapper.gni")
 import("//build/toolchain/toolchain.gni")
 import("//build_overrides/build.gni")
@@ -890,6 +891,25 @@
           "--target=armv7-unknown-nacl-gnueabihf",
         ]
       }
+    } else if (current_cpu == "ppc64") {
+      if (v8_current_cpu == "ppc") {
+        cflags += [ "-m32" ]
+        ldflags += [ "-m32" ]
+      } else if (v8_current_cpu == "ppc64") {
+        cflags += [ "-m64" ]
+        ldflags += [ "-m64" ]
+      }
+    } else if (current_cpu == "s390x") {
+      if (v8_current_cpu == "s390" && host_byteorder == "little") {
+        cflags += [ "-m32" ]
+        ldflags += [ "-m32" ]
+      } else if (v8_current_cpu == "s390") {
+        cflags += [ "-m31" ]
+        ldflags += [ "-m31" ]
+      } else if (v8_current_cpu == "s390x") {
+        cflags += [ "-m64" ]
+        ldflags += [ "-m64" ]
+      }
     }
   }
 
diff --git a/chrome/VERSION b/chrome/VERSION
index 2dc2585..8c9901d 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=64
 MINOR=0
-BUILD=3255
+BUILD=3256
 PATCH=0
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index a2d918dc..834d298 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -71,6 +71,11 @@
 #define IDC_USE_SYSTEM_TITLE_BAR        34051
 #endif
 
+// Hosted app commands
+#define IDC_COPY_URL                    34060
+#define IDC_OPEN_IN_CHROME              34061
+#define IDC_SITE_SETTINGS               34062
+
 // Page-related commands
 #define IDC_BOOKMARK_PAGE               35000
 #define IDC_BOOKMARK_ALL_TABS           35001
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 736f8a2..16cbf49 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -86,7 +86,7 @@
     Zip Archiver - Open and pack ZIP files in Files app.
   </message>
   <message desc="Title of the password input dialog." name="IDS_ZIP_ARCHIVER_PASSPHRASE_TITLE">
-    Enter your password to gain access
+    Enter your password
   </message>
   <message desc="Label of the password input field." name="IDS_ZIP_ARCHIVER_PASSPHRASE_INPUT_LABEL">
     Password
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 123f90e..0b7c680 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -606,6 +606,10 @@
         Customize and control Chromium
       </message>
 
+      <message name="IDS_OPEN_IN_CHROME" desc="The text label of the Open in Chrome menu item for the Hosted App app menu">
+        &amp;Open in Chromium
+      </message>
+
       <if expr="use_titlecase and not chromeos">
         <message name="IDS_ABOUT" desc="In Title Case: The text label of the About Chrome menu item">
           About &amp;Chromium
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 754156c..b3342fc 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -867,6 +867,12 @@
           <message name="IDS_ZOOM_MINUS2" desc="The text label of the Make Text Smaller menu item in the merged menu">
             &#8722;
           </message>
+          <message name="IDS_COPY_URL" desc="The text label of the Copy URL menu item for the Hosted App app menu">
+            Copy &amp;URL
+          </message>
+          <message name="IDS_SITE_SETTINGS" desc="The text label of the Site Settings menu item for the Hosted App app menu">
+            &amp;Site settings
+          </message>
         </if>
         <if expr="use_titlecase">
           <message name="IDS_NEW_TAB" desc="In Title Case: The text label of a menu item for opening a new tab">
@@ -932,6 +938,12 @@
           <message name="IDS_ZOOM_MINUS2" desc="The text label of the Make Text Smaller menu item in the merged menu">
             &#8722;
           </message>
+          <message name="IDS_COPY_URL" desc="In Title Case: The text label of the Copy URL menu item for the Hosted App app menu">
+            Copy &amp;URL
+          </message>
+          <message name="IDS_SITE_SETTINGS" desc="In Title Case: The text label of the Site Settings menu item for the Hosted App app menu">
+            &amp;Site Settings
+          </message>
         </if>
       </if>
 
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index a80472b..4d43bb7f 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -616,6 +616,10 @@
         Customize and control Google Chrome
       </message>
 
+      <message name="IDS_OPEN_IN_CHROME" desc="The text label of the Open in Chrome menu item for the Hosted App app menu">
+        &amp;Open in Chrome
+      </message>
+
       <if expr="use_titlecase and not chromeos">
         <message name="IDS_ABOUT" desc="In Title Case: The text label of the About Chrome menu item">
           About &amp;Google Chrome
diff --git a/chrome/browser/chrome_content_gpu_manifest_overlay.json b/chrome/browser/chrome_content_gpu_manifest_overlay.json
index 6398526a..0d7c7c1 100644
--- a/chrome/browser/chrome_content_gpu_manifest_overlay.json
+++ b/chrome/browser/chrome_content_gpu_manifest_overlay.json
@@ -4,12 +4,12 @@
     "service_manager:connector": {
       "provides": {
         "browser": [
+          "arc::mojom::ProtectedBufferManager",
           "arc::mojom::VideoDecodeAccelerator",
           "arc::mojom::VideoDecodeClient",
           "arc::mojom::VideoEncodeAccelerator",
           "arc::mojom::VideoEncodeClient",
           "chrome::mojom::ResourceUsageReporter",
-          "media::mojom::ProtectedBufferManager",
           "profiling::mojom::ProfilingClient"
         ]
       }
diff --git a/chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.cc b/chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.cc
index 030cad8..4505e33 100644
--- a/chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.cc
+++ b/chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.cc
@@ -149,7 +149,7 @@
     mojom::OemCryptoServiceRequest request) {
   // Get the Mojo interface from the GPU for dealing with secure buffers and
   // pass that to the daemon as well in our Connect call.
-  media::mojom::ProtectedBufferManagerPtr gpu_buffer_manager;
+  mojom::ProtectedBufferManagerPtr gpu_buffer_manager;
   content::BindInterfaceInGpuProcess(mojo::MakeRequest(&gpu_buffer_manager));
   oemcrypto_host_daemon_ptr_->Connect(std::move(request),
                                       std::move(gpu_buffer_manager));
diff --git a/chrome/browser/profiles/profile_window_browsertest.cc b/chrome/browser/profiles/profile_window_browsertest.cc
index 4d6db15..9bd8286 100644
--- a/chrome/browser/profiles/profile_window_browsertest.cc
+++ b/chrome/browser/profiles/profile_window_browsertest.cc
@@ -225,6 +225,7 @@
   EmptyAcceleratorHandler accelerator_handler;
   // Verify the normal browser has a bookmark menu.
   AppMenuModel model_normal_profile(&accelerator_handler, browser());
+  model_normal_profile.Init();
   EXPECT_NE(-1, model_normal_profile.GetIndexOfCommandId(IDC_BOOKMARKS_MENU));
 
   // Guest browser has no bookmark menu.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index d42b724fa..0032699 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3421,6 +3421,8 @@
       "extensions/extension_message_bubble_factory.h",
       "extensions/hosted_app_browser_controller.cc",
       "extensions/hosted_app_browser_controller.h",
+      "extensions/hosted_app_menu_model.cc",
+      "extensions/hosted_app_menu_model.h",
       "extensions/icon_with_badge_image_source.cc",
       "extensions/icon_with_badge_image_source.h",
       "extensions/settings_api_bubble_helpers.cc",
diff --git a/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm b/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm
index d5159bd..a61925c 100644
--- a/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm
+++ b/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm
@@ -481,6 +481,7 @@
   DCHECK(browser_);
   recentTabsMenuModelDelegate_.reset();
   appMenuModel_.reset(new AppMenuModel(acceleratorDelegate_.get(), browser_));
+  appMenuModel_->Init();
   [self setModel:appMenuModel_.get()];
 
   buttonViewController_.reset(
diff --git a/chrome/browser/ui/cocoa/app_menu/app_menu_controller_unittest.mm b/chrome/browser/ui/cocoa/app_menu/app_menu_controller_unittest.mm
index 8be7748..e013e5a 100644
--- a/chrome/browser/ui/cocoa/app_menu/app_menu_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/app_menu/app_menu_controller_unittest.mm
@@ -51,7 +51,7 @@
 
 class MockAppMenuModel : public AppMenuModel {
  public:
-  MockAppMenuModel() : AppMenuModel() {}
+  MockAppMenuModel() : AppMenuModel(nullptr, nullptr) {}
   ~MockAppMenuModel() {}
   MOCK_METHOD2(ExecuteCommand, void(int command_id, int event_flags));
 };
diff --git a/chrome/browser/ui/extensions/hosted_app_menu_model.cc b/chrome/browser/ui/extensions/hosted_app_menu_model.cc
new file mode 100644
index 0000000..bf26c02
--- /dev/null
+++ b/chrome/browser/ui/extensions/hosted_app_menu_model.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/extensions/hosted_app_menu_model.h"
+
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/media/router/media_router_feature.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+
+HostedAppMenuModel::HostedAppMenuModel(ui::AcceleratorProvider* provider,
+                                       Browser* browser)
+    : AppMenuModel(provider, browser) {}
+
+HostedAppMenuModel::~HostedAppMenuModel() {}
+
+void HostedAppMenuModel::Build() {
+  AddItemWithStringId(IDC_COPY_URL, IDS_COPY_URL);
+  AddItemWithStringId(IDC_OPEN_IN_CHROME, IDS_OPEN_IN_CHROME);
+  CreateActionToolbarOverflowMenu();
+  CreateZoomMenu();
+  AddItemWithStringId(IDC_PRINT, IDS_PRINT);
+  AddItemWithStringId(IDC_FIND, IDS_FIND);
+  if (media_router::MediaRouterEnabled(browser()->profile()))
+    AddItemWithStringId(IDC_ROUTE_MEDIA, IDS_MEDIA_ROUTER_MENU_ITEM_TITLE);
+  CreateCutCopyPasteMenu();
+  AddItemWithStringId(IDC_SITE_SETTINGS, IDS_SITE_SETTINGS);
+}
diff --git a/chrome/browser/ui/extensions/hosted_app_menu_model.h b/chrome/browser/ui/extensions/hosted_app_menu_model.h
new file mode 100644
index 0000000..a79026bc
--- /dev/null
+++ b/chrome/browser/ui/extensions/hosted_app_menu_model.h
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_EXTENSIONS_HOSTED_APP_MENU_MODEL_H_
+#define CHROME_BROWSER_UI_EXTENSIONS_HOSTED_APP_MENU_MODEL_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/toolbar/app_menu_model.h"
+
+// Menu model for the menu button in a hosted app browser window.
+class HostedAppMenuModel : public AppMenuModel {
+ public:
+  HostedAppMenuModel(ui::AcceleratorProvider* provider, Browser* browser);
+  ~HostedAppMenuModel() override;
+
+ private:
+  // AppMenuModel:
+  void Build() override;
+
+  DISALLOW_COPY_AND_ASSIGN(HostedAppMenuModel);
+};
+
+#endif  // CHROME_BROWSER_UI_EXTENSIONS_HOSTED_APP_MENU_MODEL_H_
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index 5f2158a..b900df0b 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -216,12 +216,19 @@
     : ui::SimpleMenuModel(this),
       uma_action_recorded_(false),
       provider_(provider),
-      browser_(browser) {
+      browser_(browser) {}
+
+AppMenuModel::~AppMenuModel() {
+  if (browser_)  // Null in Cocoa tests.
+    browser_->tab_strip_model()->RemoveObserver(this);
+}
+
+void AppMenuModel::Init() {
   Build();
   UpdateZoomControls();
 
   browser_zoom_subscription_ =
-      zoom::ZoomEventManager::GetForBrowserContext(browser->profile())
+      zoom::ZoomEventManager::GetForBrowserContext(browser_->profile())
           ->AddZoomLevelChangedCallback(base::Bind(
               &AppMenuModel::OnZoomLevelChanged, base::Unretained(this)));
 
@@ -231,11 +238,6 @@
                  content::NotificationService::AllSources());
 }
 
-AppMenuModel::~AppMenuModel() {
-  if (browser_)  // Null in tests.
-    browser_->tab_strip_model()->RemoveObserver(this);
-}
-
 bool AppMenuModel::DoesCommandIdDismissMenu(int command_id) const {
   return command_id != IDC_ZOOM_MINUS && command_id != IDC_ZOOM_PLUS;
 }
@@ -661,21 +663,6 @@
   UpdateZoomControls();
 }
 
-// For testing.
-AppMenuModel::AppMenuModel()
-    : ui::SimpleMenuModel(this),
-      uma_action_recorded_(false),
-      provider_(nullptr),
-      browser_(nullptr) {}
-
-bool AppMenuModel::ShouldShowNewIncognitoWindowMenuItem() {
-  if (browser_->profile()->IsGuestSession())
-    return false;
-
-  return IncognitoModePrefs::GetAvailability(browser_->profile()->GetPrefs()) !=
-      IncognitoModePrefs::DISABLED;
-}
-
 // Note: When adding new menu items please place under an appropriate section.
 // Menu is organised as follows:
 // - Extension toolbar overflow.
@@ -766,32 +753,6 @@
   uma_action_recorded_ = false;
 }
 
-bool AppMenuModel::AddGlobalErrorMenuItems() {
-  // TODO(sail): Currently we only build the app menu once per browser
-  // window. This means that if a new error is added after the menu is built
-  // it won't show in the existing app menu. To fix this we need to some
-  // how update the menu if new errors are added.
-  const GlobalErrorService::GlobalErrorList& errors =
-      GlobalErrorServiceFactory::GetForProfile(browser_->profile())->errors();
-  bool menu_items_added = false;
-  for (GlobalErrorService::GlobalErrorList::const_iterator
-       it = errors.begin(); it != errors.end(); ++it) {
-    GlobalError* error = *it;
-    DCHECK(error);
-    if (error->HasMenuItem()) {
-      AddItem(error->MenuItemCommandID(), error->MenuItemLabel());
-      SetIcon(GetIndexOfCommandId(error->MenuItemCommandID()),
-              error->MenuItemIcon());
-      menu_items_added = true;
-      if (IDC_SHOW_SIGNIN_ERROR == error->MenuItemCommandID()) {
-        base::RecordAction(
-            base::UserMetricsAction("Signin_Impression_FromMenu"));
-      }
-    }
-  }
-  return menu_items_added;
-}
-
 void AppMenuModel::CreateActionToolbarOverflowMenu() {
   // We only add the extensions overflow container if there are any icons that
   // aren't shown in the main container.
@@ -858,6 +819,40 @@
   zoom_label_ = base::FormatPercent(zoom_percent);
 }
 
+bool AppMenuModel::ShouldShowNewIncognitoWindowMenuItem() {
+  if (browser_->profile()->IsGuestSession())
+    return false;
+
+  return IncognitoModePrefs::GetAvailability(browser_->profile()->GetPrefs()) !=
+         IncognitoModePrefs::DISABLED;
+}
+
+bool AppMenuModel::AddGlobalErrorMenuItems() {
+  // TODO(sail): Currently we only build the app menu once per browser
+  // window. This means that if a new error is added after the menu is built
+  // it won't show in the existing app menu. To fix this we need to some
+  // how update the menu if new errors are added.
+  const GlobalErrorService::GlobalErrorList& errors =
+      GlobalErrorServiceFactory::GetForProfile(browser_->profile())->errors();
+  bool menu_items_added = false;
+  for (GlobalErrorService::GlobalErrorList::const_iterator it = errors.begin();
+       it != errors.end(); ++it) {
+    GlobalError* error = *it;
+    DCHECK(error);
+    if (error->HasMenuItem()) {
+      AddItem(error->MenuItemCommandID(), error->MenuItemLabel());
+      SetIcon(GetIndexOfCommandId(error->MenuItemCommandID()),
+              error->MenuItemIcon());
+      menu_items_added = true;
+      if (IDC_SHOW_SIGNIN_ERROR == error->MenuItemCommandID()) {
+        base::RecordAction(
+            base::UserMetricsAction("Signin_Impression_FromMenu"));
+      }
+    }
+  }
+  return menu_items_added;
+}
+
 void AppMenuModel::OnZoomLevelChanged(
     const content::HostZoomMap::ZoomLevelChange& change) {
   UpdateZoomControls();
diff --git a/chrome/browser/ui/toolbar/app_menu_model.h b/chrome/browser/ui/toolbar/app_menu_model.h
index 980ba8b..47eb2f7 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.h
+++ b/chrome/browser/ui/toolbar/app_menu_model.h
@@ -109,9 +109,14 @@
   static const int kMinRecentTabsCommandId = 1001;
   static const int kMaxRecentTabsCommandId = 1200;
 
+  // Creates an app menu model for the given browser. Init() must be called
+  // before passing this to an AppMenu.
   AppMenuModel(ui::AcceleratorProvider* provider, Browser* browser);
   ~AppMenuModel() override;
 
+  // Runs Build() and registers observers.
+  void Init();
+
   // Overridden for ButtonMenuItemModel::Delegate:
   bool DoesCommandIdDismissMenu(int command_id) const override;
 
@@ -151,19 +156,9 @@
   // Calculates |zoom_label_| in response to a zoom change.
   void UpdateZoomControls();
 
- private:
-  class HelpMenuModel;
-  // Testing constructor used for mocking.
-  friend class ::MockAppMenuModel;
-
-  AppMenuModel();
-
-  void Build();
-
-  // Adds actionable global error menu items to the menu.
-  // Examples: Extension permissions and sign in errors.
-  // Returns a boolean indicating whether any menu items were added.
-  bool AddGlobalErrorMenuItems();
+ protected:
+  // Builds the menu model, adding appropriate menu items.
+  virtual void Build();
 
   // Appends everything needed for the clipboard menu: a menu break, the
   // clipboard menu content and the finalizing menu break.
@@ -176,10 +171,19 @@
   // menu content and then another menu break.
   void CreateZoomMenu();
 
-  void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change);
+ private:
+  class HelpMenuModel;
+  friend class ::MockAppMenuModel;
 
   bool ShouldShowNewIncognitoWindowMenuItem();
 
+  // Adds actionable global error menu items to the menu.
+  // Examples: Extension permissions and sign in errors.
+  // Returns a boolean indicating whether any menu items were added.
+  bool AddGlobalErrorMenuItems();
+
+  void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change);
+
   // Called when a command is selected.
   // Logs UMA metrics about which command was chosen and how long the user
   // took to select the command.
diff --git a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
index 5c8659c..c16bd77 100644
--- a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
@@ -116,6 +116,7 @@
 
 TEST_F(AppMenuModelTest, Basics) {
   TestAppMenuModel model(this, browser());
+  model.Init();
   int itemCount = model.GetItemCount();
 
   // Verify it has items. The number varies by platform, so we don't check
@@ -182,6 +183,7 @@
   service->AddGlobalError(base::WrapUnique(error2));
 
   AppMenuModel model(this, browser());
+  model.Init();
   int index1 = model.GetIndexOfCommandId(command1);
   EXPECT_GT(index1, -1);
   int index2 = model.GetIndexOfCommandId(command2);
diff --git a/chrome/browser/ui/views/frame/hosted_app_button_container.cc b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
index f5b3462c..f011b041 100644
--- a/chrome/browser/ui/views/frame/hosted_app_button_container.cc
+++ b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/ui/views/frame/hosted_app_button_container.h"
 
 #include "chrome/app/vector_icons/vector_icons.h"
-#include "chrome/browser/ui/toolbar/app_menu_model.h"
+#include "chrome/browser/ui/extensions/hosted_app_menu_model.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/toolbar/app_menu.h"
 #include "ui/gfx/color_palette.h"
@@ -31,9 +31,9 @@
     const gfx::Point& point,
     const ui::Event* event) {
   Browser* browser = browser_view_->browser();
-  menu_.reset(new AppMenu(browser, 0));
-  // TODO(calamity): Use custom menu model here.
-  menu_model_.reset(new AppMenuModel(browser_view_, browser));
+  menu_ = std::make_unique<AppMenu>(browser, 0);
+  menu_model_ = std::make_unique<HostedAppMenuModel>(browser_view_, browser);
+  menu_model_->Init();
   menu_->Init(menu_model_.get());
 
   menu_->RunMenu(this);
diff --git a/chrome/browser/ui/views/frame/hosted_app_button_container.h b/chrome/browser/ui/views/frame/hosted_app_button_container.h
index fe13c87..aae9a09 100644
--- a/chrome/browser/ui/views/frame/hosted_app_button_container.h
+++ b/chrome/browser/ui/views/frame/hosted_app_button_container.h
@@ -13,7 +13,7 @@
 #include "ui/views/view.h"
 
 class AppMenu;
-class AppMenuModel;
+class HostedAppMenuModel;
 class BrowserView;
 
 // A container for hosted app buttons in the title bar.
@@ -48,7 +48,7 @@
     // App model and menu.
     // Note that the menu should be destroyed before the model it uses, so the
     // menu should be listed later.
-    std::unique_ptr<AppMenuModel> menu_model_;
+    std::unique_ptr<HostedAppMenuModel> menu_model_;
     std::unique_ptr<AppMenu> menu_;
 
     DISALLOW_COPY_AND_ASSIGN(AppMenuButton);
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index 20a197c..b47b416 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -770,6 +770,25 @@
     selector_rows_.push_back(std::move(selector));
   }
 
+  // In Harmony, ensure most comboboxes are the same width by setting them all
+  // to the widest combobox size, provided it does not exceed a maximum width.
+  // For selected options that are over the maximum width, allow them to assume
+  // their full width. If the combobox selection is changed, this may make the
+  // widths inconsistent again, but that is OK since the widths will be updated
+  // on the next time the bubble is opened.
+  if (ui::MaterialDesignController::IsSecondaryUiMaterial()) {
+    const int maximum_width = ChromeLayoutProvider::Get()->GetDistanceMetric(
+        views::DISTANCE_BUTTON_MAX_LINKABLE_WIDTH);
+    int combobox_width = 0;
+    for (const auto& selector : selector_rows_) {
+      int curr_width = selector->GetComboboxWidth();
+      if (maximum_width >= curr_width)
+        combobox_width = std::max(combobox_width, curr_width);
+    }
+    for (const auto& selector : selector_rows_)
+      selector->SetMinComboboxWidth(combobox_width);
+  }
+
   for (auto& object : chosen_object_info_list) {
     // Since chosen objects are presented after permissions in the same list,
     // make sure its height is the same as the permissions row's minimum height
diff --git a/chrome/browser/ui/views/page_info/permission_selector_row.cc b/chrome/browser/ui/views/page_info/permission_selector_row.cc
index 43b399a..195d898 100644
--- a/chrome/browser/ui/views/page_info/permission_selector_row.cc
+++ b/chrome/browser/ui/views/page_info/permission_selector_row.cc
@@ -213,11 +213,21 @@
 
   void UpdateSelectedIndex(bool use_default);
 
+  void set_min_width(int width) { min_width_ = width; }
+
+  // views::Combobox:
+  gfx::Size CalculatePreferredSize() const override;
+
  private:
   // views::ComboboxListener:
   void OnPerformAction(Combobox* combobox) override;
 
   ComboboxModelAdapter* model_;
+
+  // Minimum width for |PermissionCombobox|.
+  int min_width_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(PermissionCombobox);
 };
 
 PermissionCombobox::PermissionCombobox(ComboboxModelAdapter* model,
@@ -243,6 +253,12 @@
   SetSelectedIndex(index);
 }
 
+gfx::Size PermissionCombobox::CalculatePreferredSize() const {
+  gfx::Size preferred_size = Combobox::CalculatePreferredSize();
+  preferred_size.SetToMax(gfx::Size(min_width_, 0));
+  return preferred_size;
+}
+
 void PermissionCombobox::OnPerformAction(Combobox* combobox) {
   model_->OnPerformAction(combobox->selected_index());
 }
@@ -404,6 +420,16 @@
   }
 }
 
+int PermissionSelectorRow::GetComboboxWidth() const {
+  DCHECK(combobox_);
+  return combobox_->Combobox::GetPreferredSize().width();
+}
+
+void PermissionSelectorRow::SetMinComboboxWidth(int width) {
+  DCHECK(combobox_);
+  combobox_->set_min_width(width);
+}
+
 views::View* PermissionSelectorRow::button() {
   // These casts are required because the two arms of a ?: cannot have different
   // types T1 and T2, even if the resulting value of the ?: is about to be a T
diff --git a/chrome/browser/ui/views/page_info/permission_selector_row.h b/chrome/browser/ui/views/page_info/permission_selector_row.h
index d9cea6f..17ff487 100644
--- a/chrome/browser/ui/views/page_info/permission_selector_row.h
+++ b/chrome/browser/ui/views/page_info/permission_selector_row.h
@@ -57,6 +57,13 @@
 
   void PermissionChanged(const PageInfoUI::PermissionInfo& permission);
 
+  // Returns the preferred width for the currently selected combobox option
+  // (unchanged by any minimum width set using SetMinComboboxWidth()).
+  int GetComboboxWidth() const;
+
+  // Sets the minimum width for |combobox_|.
+  void SetMinComboboxWidth(int width);
+
  private:
   friend class test::PageInfoBubbleViewTestApi;
 
diff --git a/chrome/browser/ui/views/toolbar/app_menu_button.cc b/chrome/browser/ui/views/toolbar/app_menu_button.cc
index a11dde4..70f6884 100644
--- a/chrome/browser/ui/views/toolbar/app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu_button.cc
@@ -94,6 +94,7 @@
 
   menu_.reset(new AppMenu(browser, for_drop ? AppMenu::FOR_DROP : 0));
   menu_model_.reset(new AppMenuModel(toolbar_view_, browser));
+  menu_model_->Init();
   menu_->Init(menu_model_.get());
 
   for (views::MenuListener& observer : menu_listeners_)
diff --git a/chrome/gpu/BUILD.gn b/chrome/gpu/BUILD.gn
index 93bdd8a..4581690 100644
--- a/chrome/gpu/BUILD.gn
+++ b/chrome/gpu/BUILD.gn
@@ -27,6 +27,10 @@
       "gpu_arc_video_decode_accelerator.h",
       "gpu_arc_video_encode_accelerator.cc",
       "gpu_arc_video_encode_accelerator.h",
+      "protected_buffer_manager.cc",
+      "protected_buffer_manager.h",
+      "protected_buffer_manager_proxy.cc",
+      "protected_buffer_manager_proxy.h",
     ]
   }
 }
diff --git a/chrome/gpu/arc_video_decode_accelerator.h b/chrome/gpu/arc_video_decode_accelerator.h
index c56d5df..bd1104e 100644
--- a/chrome/gpu/arc_video_decode_accelerator.h
+++ b/chrome/gpu/arc_video_decode_accelerator.h
@@ -72,6 +72,8 @@
   struct Config {
     size_t num_input_buffers = 0;
     uint32_t input_pixel_format = 0;
+    // If true, only buffers created via AllocateProtectedBuffer() may be used.
+    bool secure_mode = false;
     // TODO(owenlin): Add output_pixel_format. For now only the native pixel
     //                format of each VDA on Chromium is supported.
   };
@@ -111,10 +113,27 @@
   // returns SUCCESS iff initialization is successful.
   virtual Result Initialize(const Config& config, Client* client) = 0;
 
+  // Allocates a new protected buffer on accelerator side for the given |port|
+  // and |index|, the contents of which will be inaccessible to the client.
+  // The protected buffer will remain valid for at least as long as the resource
+  // backing the passed |handle_fd| is not released (i.e. there is at least one
+  // reference on the file backing |handle_fd|.
+  //
+  // Usable only if the accelerator has been initialized to run in secure mode.
+  // Allocation for input will create a protected buffer of at least |size|;
+  // for output, |size| is ignored, and the currently configured output format
+  // is used instead to determine the required buffer size and format.
+  virtual bool AllocateProtectedBuffer(PortType port,
+                                       uint32_t index,
+                                       base::ScopedFD handle_fd,
+                                       size_t size) = 0;
+
   // Assigns a shared memory to be used for the accelerator at the specified
   // port and index. A buffer must be successfully bound before it can be passed
   // to the accelerator via UseBuffer(). Already bound buffers may be reused
   // multiple times without additional bindings.
+  // Not allowed in secure_mode, where protected buffers have to be allocated
+  // instead.
   virtual void BindSharedMemory(PortType port,
                                 uint32_t index,
                                 base::ScopedFD ashmem_fd,
@@ -125,6 +144,8 @@
   // port and index. A buffer must be successfully bound before it can be
   // passed to the accelerator via UseBuffer(). Already bound buffers may be
   // reused multiple times without additional bindings.
+  // Not allowed in secure_mode, where protected buffers have to be allocated
+  // instead.
   virtual void BindDmabuf(
       PortType port,
       uint32_t index,
@@ -134,6 +155,8 @@
   // Passes a buffer to the accelerator. For input buffer, the accelerator
   // will process it. For output buffer, the accelerator will output content
   // to it.
+  // In secure mode, |port| and |index| must correspond to a protected buffer
+  // allocated using AllocateProtectedBuffer().
   virtual void UseBuffer(PortType port,
                          uint32_t index,
                          const BufferMetadata& metadata) = 0;
diff --git a/chrome/gpu/chrome_arc_video_decode_accelerator.cc b/chrome/gpu/chrome_arc_video_decode_accelerator.cc
index 16715b663..1cda9c3 100644
--- a/chrome/gpu/chrome_arc_video_decode_accelerator.cc
+++ b/chrome/gpu/chrome_arc_video_decode_accelerator.cc
@@ -10,7 +10,9 @@
 #include "base/numerics/safe_math.h"
 #include "base/run_loop.h"
 #include "base/unguessable_token.h"
+#include "chrome/gpu/protected_buffer_manager.h"
 #include "media/base/video_frame.h"
+#include "media/gpu/format_utils.h"
 #include "media/gpu/gpu_video_decode_accelerator_factory.h"
 
 namespace chromeos {
@@ -41,27 +43,33 @@
 
 ChromeArcVideoDecodeAccelerator::InputBufferInfo::InputBufferInfo() = default;
 
-ChromeArcVideoDecodeAccelerator::InputBufferInfo::InputBufferInfo(
-    InputBufferInfo&& other) = default;
-
-ChromeArcVideoDecodeAccelerator::InputBufferInfo::~InputBufferInfo() = default;
+ChromeArcVideoDecodeAccelerator::InputBufferInfo::~InputBufferInfo() {
+  if (shm_handle.OwnershipPassesToIPC())
+    shm_handle.Close();
+}
 
 ChromeArcVideoDecodeAccelerator::OutputBufferInfo::OutputBufferInfo() = default;
 
-ChromeArcVideoDecodeAccelerator::OutputBufferInfo::OutputBufferInfo(
-    OutputBufferInfo&& other) = default;
-
-ChromeArcVideoDecodeAccelerator::OutputBufferInfo::~OutputBufferInfo() =
-    default;
+ChromeArcVideoDecodeAccelerator::OutputBufferInfo::~OutputBufferInfo() {
+  if (!gpu_memory_buffer_handle.is_null()) {
+    for (const auto& fd : gpu_memory_buffer_handle.native_pixmap_handle.fds) {
+      // Close the fd by wrapping it in a ScopedFD and letting
+      // it fall out of scope.
+      base::ScopedFD scoped_fd(fd.fd);
+    }
+  }
+}
 
 ChromeArcVideoDecodeAccelerator::ChromeArcVideoDecodeAccelerator(
-    const gpu::GpuPreferences& gpu_preferences)
+    const gpu::GpuPreferences& gpu_preferences,
+    ProtectedBufferManager* protected_buffer_manager)
     : arc_client_(nullptr),
       next_bitstream_buffer_id_(0),
       output_pixel_format_(media::PIXEL_FORMAT_UNKNOWN),
       output_buffer_size_(0),
       requested_num_of_output_buffers_(0),
-      gpu_preferences_(gpu_preferences) {}
+      gpu_preferences_(gpu_preferences),
+      protected_buffer_manager_(protected_buffer_manager) {}
 
 ChromeArcVideoDecodeAccelerator::~ChromeArcVideoDecodeAccelerator() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -95,13 +103,20 @@
     return ILLEGAL_STATE;
   }
 
+  arc_client_ = client;
+
   if (client_count_ >= kMaxConcurrentClients) {
     LOG(WARNING) << "Reject to Initialize() due to too many clients: "
                  << client_count_;
     return INSUFFICIENT_RESOURCES;
   }
 
-  arc_client_ = client;
+  if (config.secure_mode && !protected_buffer_manager_) {
+    DLOG(ERROR) << "Secure mode unsupported";
+    return PLATFORM_FAILURE;
+  }
+
+  secure_mode_ = config.secure_mode;
 
   if (config.num_input_buffers > kMaxBufferCount) {
     DLOG(ERROR) << "Request too many buffers: " << config.num_input_buffers;
@@ -168,6 +183,60 @@
   buffers_pending_import_.resize(number);
 }
 
+bool ChromeArcVideoDecodeAccelerator::AllocateProtectedBuffer(
+    PortType port,
+    uint32_t index,
+    base::ScopedFD handle_fd,
+    size_t size) {
+  DVLOG(5) << "port=" << port << " index=" << index
+           << " handle=" << handle_fd.get() << " size=" << size;
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (!secure_mode_) {
+    DLOG(ERROR) << "Not in secure mode";
+    arc_client_->OnError(INVALID_ARGUMENT);
+    return false;
+  }
+
+  if (!ValidatePortAndIndex(port, index)) {
+    arc_client_->OnError(INVALID_ARGUMENT);
+    return false;
+  }
+
+  if (port == PORT_INPUT) {
+    auto protected_shmem =
+        protected_buffer_manager_->AllocateProtectedSharedMemory(
+            std::move(handle_fd), size);
+    if (!protected_shmem) {
+      DLOG(ERROR) << "Failed allocating protected shared memory";
+      return false;
+    }
+
+    auto input_info = std::make_unique<InputBufferInfo>();
+    input_info->shm_handle = protected_shmem->shm_handle();
+    input_info->protected_buffer_handle = std::move(protected_shmem);
+    input_buffer_info_[index] = std::move(input_info);
+  } else if (port == PORT_OUTPUT) {
+    auto protected_pixmap =
+        protected_buffer_manager_->AllocateProtectedNativePixmap(
+            std::move(handle_fd),
+            media::VideoPixelFormatToGfxBufferFormat(output_pixel_format_),
+            coded_size_);
+    if (!protected_pixmap) {
+      DLOG(ERROR) << "Failed allocating a protected pixmap";
+      return false;
+    }
+    auto output_info = std::make_unique<OutputBufferInfo>();
+    output_info->gpu_memory_buffer_handle.type = gfx::NATIVE_PIXMAP;
+    output_info->gpu_memory_buffer_handle.native_pixmap_handle =
+        gfx::CloneHandleForIPC(protected_pixmap->native_pixmap_handle());
+    output_info->protected_buffer_handle = std::move(protected_pixmap);
+    buffers_pending_import_[index] = std::move(output_info);
+  }
+
+  return true;
+}
+
 void ChromeArcVideoDecodeAccelerator::BindSharedMemory(PortType port,
                                                        uint32_t index,
                                                        base::ScopedFD ashmem_fd,
@@ -176,6 +245,13 @@
   DVLOG(5) << "ArcGVDA::BindSharedMemory, offset: " << offset
            << ", length: " << length;
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (secure_mode_) {
+    DLOG(ERROR) << "not allowed in secure mode";
+    arc_client_->OnError(INVALID_ARGUMENT);
+    return;
+  }
+
   if (!vda_) {
     DLOG(ERROR) << "VDA not initialized";
     return;
@@ -190,10 +266,14 @@
     arc_client_->OnError(INVALID_ARGUMENT);
     return;
   }
-  InputBufferInfo* input_info = &input_buffer_info_[index];
-  input_info->handle = std::move(ashmem_fd);
+
+  auto input_info = std::make_unique<InputBufferInfo>();
+  input_info->shm_handle =
+      base::SharedMemoryHandle(base::FileDescriptor(ashmem_fd.release(), true),
+                               length, base::UnguessableToken::Create());
+  DCHECK(input_info->shm_handle.OwnershipPassesToIPC());
   input_info->offset = offset;
-  input_info->length = length;
+  input_buffer_info_[index] = std::move(input_info);
 }
 
 bool ChromeArcVideoDecodeAccelerator::VerifyDmabuf(
@@ -241,6 +321,12 @@
     const std::vector<::arc::VideoFramePlane>& planes) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+  if (secure_mode_) {
+    DLOG(ERROR) << "not allowed in secure mode";
+    arc_client_->OnError(INVALID_ARGUMENT);
+    return;
+  }
+
   if (!vda_) {
     DLOG(ERROR) << "VDA not initialized";
     return;
@@ -260,9 +346,19 @@
     return;
   }
 
-  OutputBufferInfo& info = buffers_pending_import_[index];
-  info.handle = std::move(dmabuf_fd);
-  info.planes = planes;
+#if defined(USE_OZONE)
+  auto output_info = std::make_unique<OutputBufferInfo>();
+  output_info->gpu_memory_buffer_handle.type = gfx::NATIVE_PIXMAP;
+  output_info->gpu_memory_buffer_handle.native_pixmap_handle.fds.emplace_back(
+      base::FileDescriptor(dmabuf_fd.release(), true));
+  for (const auto& plane : planes) {
+    output_info->gpu_memory_buffer_handle.native_pixmap_handle.planes
+        .emplace_back(plane.stride, plane.offset, 0, 0);
+  }
+  buffers_pending_import_[index] = std::move(output_info);
+#else
+  arc_client_->OnError(PLATFORM_FAILURE);
+#endif
 }
 
 void ChromeArcVideoDecodeAccelerator::UseBuffer(
@@ -283,41 +379,44 @@
   }
   switch (port) {
     case PORT_INPUT: {
-      InputBufferInfo* input_info = &input_buffer_info_[index];
+      auto& input_info = input_buffer_info_[index];
+      if (!input_info) {
+        DLOG(ERROR) << "Buffer not initialized";
+        arc_client_->OnError(INVALID_ARGUMENT);
+        return;
+      }
+
       int32_t bitstream_buffer_id = next_bitstream_buffer_id_;
       // Mask against 30 bits, to avoid (undefined) wraparound on signed
       // integer.
       next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF;
-      int dup_fd = HANDLE_EINTR(dup(input_info->handle.get()));
-      if (dup_fd < 0) {
-        DLOG(ERROR) << "dup() failed.";
+
+      auto duplicated_handle = input_info->shm_handle.Duplicate();
+      if (!duplicated_handle.IsValid()) {
         arc_client_->OnError(PLATFORM_FAILURE);
         return;
       }
+
       CreateInputRecord(bitstream_buffer_id, index, metadata.timestamp);
-      base::UnguessableToken guid = base::UnguessableToken::Create();
-      vda_->Decode(media::BitstreamBuffer(
-          bitstream_buffer_id,
-          base::SharedMemoryHandle(base::FileDescriptor(dup_fd, true), 0u,
-                                   guid),
-          metadata.bytes_used, input_info->offset));
+      vda_->Decode(
+          media::BitstreamBuffer(bitstream_buffer_id, duplicated_handle,
+                                 metadata.bytes_used, input_info->offset));
       break;
     }
     case PORT_OUTPUT: {
-      // is_valid() is true for the first time the buffer is passed to the VDA.
+      auto& output_info = buffers_pending_import_[index];
+      if (!output_info) {
+        DLOG(ERROR) << "Buffer not initialized";
+        arc_client_->OnError(INVALID_ARGUMENT);
+        return;
+      }
+      // is_null() is false the first time the buffer is passed to the VDA.
       // In that case, VDA needs to import the buffer first.
-      OutputBufferInfo& info = buffers_pending_import_[index];
-      if (info.handle.is_valid()) {
-        gfx::GpuMemoryBufferHandle handle;
-#if defined(USE_OZONE)
-        handle.native_pixmap_handle.fds.emplace_back(
-            base::FileDescriptor(info.handle.release(), true));
-        for (const auto& plane : info.planes) {
-          handle.native_pixmap_handle.planes.emplace_back(plane.stride,
-                                                          plane.offset, 0, 0);
-        }
-#endif
-        vda_->ImportBufferForPicture(index, handle);
+      if (!output_info->gpu_memory_buffer_handle.is_null()) {
+        vda_->ImportBufferForPicture(index,
+                                     output_info->gpu_memory_buffer_handle);
+        // VDA takes ownership, so just clear out, don't close the handle.
+        output_info->gpu_memory_buffer_handle = gfx::GpuMemoryBufferHandle();
       } else {
         vda_->ReusePictureBuffer(index);
       }
diff --git a/chrome/gpu/chrome_arc_video_decode_accelerator.h b/chrome/gpu/chrome_arc_video_decode_accelerator.h
index a520398..2da28c2 100644
--- a/chrome/gpu/chrome_arc_video_decode_accelerator.h
+++ b/chrome/gpu/chrome_arc_video_decode_accelerator.h
@@ -19,6 +19,9 @@
 namespace chromeos {
 namespace arc {
 
+class ProtectedBufferManager;
+class ProtectedBufferHandle;
+
 // This class is executed in the GPU process. It takes decoding requests from
 // ARC via IPC channels and translates and sends those requests to an
 // implementation of media::VideoDecodeAccelerator. It also returns the decoded
@@ -28,8 +31,9 @@
       public media::VideoDecodeAccelerator::Client,
       public base::SupportsWeakPtr<ChromeArcVideoDecodeAccelerator> {
  public:
-  explicit ChromeArcVideoDecodeAccelerator(
-      const gpu::GpuPreferences& gpu_preferences);
+  ChromeArcVideoDecodeAccelerator(
+      const gpu::GpuPreferences& gpu_preferences,
+      ProtectedBufferManager* protected_buffer_manager);
   ~ChromeArcVideoDecodeAccelerator() override;
 
   // Implementation of the ArcVideoDecodeAccelerator interface.
@@ -37,6 +41,10 @@
       const Config& config,
       ArcVideoDecodeAccelerator::Client* client) override;
   void SetNumberOfOutputBuffers(size_t number) override;
+  bool AllocateProtectedBuffer(PortType port,
+                               uint32_t index,
+                               base::ScopedFD handle_fd,
+                               size_t size) override;
   void BindSharedMemory(PortType port,
                         uint32_t index,
                         base::ScopedFD ashmem_fd,
@@ -80,28 +88,34 @@
 
   // The information about the shared memory used as an input buffer.
   struct InputBufferInfo {
-    // The file handle to access the buffer. It is owned by this class and
-    // should be closed after use.
-    base::ScopedFD handle;
+    // SharedMemoryHandle for this buffer to be passed to accelerator.
+    // In non-secure mode, received via BindSharedMemory from the client,
+    // in secure mode, a handle for the SharedMemory in protected_shmem.
+    base::SharedMemoryHandle shm_handle;
 
-    // The offset of the payload to the beginning of the shared memory.
+    // Used only in secure mode; handle to the protected buffer backing
+    // this input buffer.
+    std::unique_ptr<ProtectedBufferHandle> protected_buffer_handle;
+
+    // Offset to the payload from the beginning of the shared memory buffer.
     off_t offset = 0;
 
-    // The size of the payload in bytes.
-    size_t length = 0;
-
     InputBufferInfo();
-    InputBufferInfo(InputBufferInfo&& other);
     ~InputBufferInfo();
   };
 
-  // The information about the dmabuf used as an output buffer.
+  // The information about the native pixmap used as an output buffer.
   struct OutputBufferInfo {
-    base::ScopedFD handle;
-    std::vector<::arc::VideoFramePlane> planes;
+    // GpuMemoryBufferHandle for this buffer to be passed to accelerator.
+    // In non-secure mode, received via BindDmabuf from the client,
+    // in secure mode, a handle to the NativePixmap in protected_pixmap.
+    gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle;
+
+    // Used only in secure mode; handle to the protected buffer backing
+    // this output buffer.
+    std::unique_ptr<ProtectedBufferHandle> protected_buffer_handle;
 
     OutputBufferInfo();
-    OutputBufferInfo(OutputBufferInfo&& other);
     ~OutputBufferInfo();
   };
 
@@ -155,12 +169,12 @@
   std::list<InputRecord> input_records_;
 
   // The details of the shared memory of each input buffers.
-  std::vector<InputBufferInfo> input_buffer_info_;
+  std::vector<std::unique_ptr<InputBufferInfo>> input_buffer_info_;
 
   // To keep those output buffers which have been bound by bindDmabuf() but
   // haven't been passed to VDA yet. Will call VDA::ImportBufferForPicture()
   // when those buffers are used for the first time.
-  std::vector<OutputBufferInfo> buffers_pending_import_;
+  std::vector<std::unique_ptr<OutputBufferInfo>> buffers_pending_import_;
 
   THREAD_CHECKER(thread_checker_);
   size_t output_buffer_size_;
@@ -168,7 +182,10 @@
   // The minimal number of requested output buffers.
   uint32_t requested_num_of_output_buffers_;
 
+  bool secure_mode_ = false;
+
   gpu::GpuPreferences gpu_preferences_;
+  ProtectedBufferManager* protected_buffer_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeArcVideoDecodeAccelerator);
 };
diff --git a/chrome/gpu/chrome_content_gpu_client.cc b/chrome/gpu/chrome_content_gpu_client.cc
index bcd0060..ae7e058c 100644
--- a/chrome/gpu/chrome_content_gpu_client.cc
+++ b/chrome/gpu/chrome_content_gpu_client.cc
@@ -22,8 +22,14 @@
 #if defined(OS_CHROMEOS)
 #include "chrome/gpu/gpu_arc_video_decode_accelerator.h"
 #include "chrome/gpu/gpu_arc_video_encode_accelerator.h"
+#include "chrome/gpu/protected_buffer_manager.h"
+#include "chrome/gpu/protected_buffer_manager_proxy.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+#endif
 #endif
 
 namespace {
@@ -46,6 +52,10 @@
                   metrics::CallStackProfileParams::MAY_SHUFFLE))) {
   if (StackSamplingConfiguration::Get()->IsProfilerEnabledForCurrentProcess())
     stack_sampling_profiler_.Start();
+
+#if defined(OS_CHROMEOS)
+  protected_buffer_manager_.reset(new chromeos::arc::ProtectedBufferManager());
+#endif
 }
 
 ChromeContentGpuClient::~ChromeContentGpuClient() {}
@@ -61,6 +71,10 @@
       base::Bind(&ChromeContentGpuClient::CreateArcVideoEncodeAccelerator,
                  base::Unretained(this)),
       base::ThreadTaskRunnerHandle::Get());
+  registry->AddInterface(
+      base::Bind(&ChromeContentGpuClient::CreateProtectedBufferManager,
+                 base::Unretained(this)),
+      base::ThreadTaskRunnerHandle::Get());
 #endif
 }
 
@@ -68,6 +82,13 @@
     const gpu::GpuPreferences& gpu_preferences) {
 #if defined(OS_CHROMEOS)
   gpu_preferences_ = gpu_preferences;
+#if defined(USE_OZONE)
+  ui::OzonePlatform::GetInstance()
+      ->GetSurfaceFactoryOzone()
+      ->SetGetProtectedNativePixmapDelegate(base::Bind(
+          &chromeos::arc::ProtectedBufferManager::GetProtectedNativePixmapFor,
+          base::Unretained(protected_buffer_manager_.get())));
+#endif
 #endif
 
   metrics::mojom::CallStackProfileCollectorPtr browser_interface;
@@ -78,12 +99,11 @@
 }
 
 #if defined(OS_CHROMEOS)
-
 void ChromeContentGpuClient::CreateArcVideoDecodeAccelerator(
     ::arc::mojom::VideoDecodeAcceleratorRequest request) {
   mojo::MakeStrongBinding(
       base::MakeUnique<chromeos::arc::GpuArcVideoDecodeAccelerator>(
-          gpu_preferences_),
+          gpu_preferences_, protected_buffer_manager_.get()),
       std::move(request));
 }
 
@@ -94,4 +114,12 @@
           gpu_preferences_),
       std::move(request));
 }
+
+void ChromeContentGpuClient::CreateProtectedBufferManager(
+    ::arc::mojom::ProtectedBufferManagerRequest request) {
+  mojo::MakeStrongBinding(
+      base::MakeUnique<chromeos::arc::GpuArcProtectedBufferManagerProxy>(
+          protected_buffer_manager_.get()),
+      std::move(request));
+}
 #endif
diff --git a/chrome/gpu/chrome_content_gpu_client.h b/chrome/gpu/chrome_content_gpu_client.h
index 78abc0e..bbc731f 100644
--- a/chrome/gpu/chrome_content_gpu_client.h
+++ b/chrome/gpu/chrome_content_gpu_client.h
@@ -12,10 +12,16 @@
 #include "content/public/gpu/content_gpu_client.h"
 
 #if defined(OS_CHROMEOS)
+#include "components/arc/common/protected_buffer_manager.mojom.h"
 #include "components/arc/common/video_decode_accelerator.mojom.h"
 #include "components/arc/common/video_encode_accelerator.mojom.h"
 #include "gpu/command_buffer/service/gpu_preferences.h"
 
+namespace chromeos {
+namespace arc {
+class ProtectedBufferManager;
+}  // namespace arc
+}  // namespace chromeos
 #endif
 
 class ChromeContentGpuClient : public content::ContentGpuClient {
@@ -35,6 +41,9 @@
 
   void CreateArcVideoEncodeAccelerator(
       ::arc::mojom::VideoEncodeAcceleratorRequest request);
+
+  void CreateProtectedBufferManager(
+      ::arc::mojom::ProtectedBufferManagerRequest request);
 #endif
 
   // Used to profile process startup.
@@ -42,6 +51,8 @@
 
 #if defined(OS_CHROMEOS)
   gpu::GpuPreferences gpu_preferences_;
+  std::unique_ptr<chromeos::arc::ProtectedBufferManager>
+      protected_buffer_manager_;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(ChromeContentGpuClient);
diff --git a/chrome/gpu/gpu_arc_video_decode_accelerator.cc b/chrome/gpu/gpu_arc_video_decode_accelerator.cc
index d53c3c0..b94aa8f 100644
--- a/chrome/gpu/gpu_arc_video_decode_accelerator.cc
+++ b/chrome/gpu/gpu_arc_video_decode_accelerator.cc
@@ -98,6 +98,7 @@
     chromeos::arc::ArcVideoDecodeAccelerator::Config result;
     result.num_input_buffers = input->num_input_buffers;
     result.input_pixel_format = input->input_pixel_format;
+    result.secure_mode = input->secure_mode;
     return result;
   }
 };
@@ -108,9 +109,12 @@
 namespace arc {
 
 GpuArcVideoDecodeAccelerator::GpuArcVideoDecodeAccelerator(
-    const gpu::GpuPreferences& gpu_preferences)
+    const gpu::GpuPreferences& gpu_preferences,
+    ProtectedBufferManager* protected_buffer_manager)
     : gpu_preferences_(gpu_preferences),
-      accelerator_(new ChromeArcVideoDecodeAccelerator(gpu_preferences_)) {}
+      accelerator_(std::make_unique<ChromeArcVideoDecodeAccelerator>(
+          gpu_preferences_,
+          protected_buffer_manager)) {}
 
 GpuArcVideoDecodeAccelerator::~GpuArcVideoDecodeAccelerator() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -166,7 +170,9 @@
     LOG(ERROR) << "only decoder is supported";
     std::move(callback).Run(
         ::arc::mojom::VideoDecodeAccelerator::Result::INVALID_ARGUMENT);
+    return;
   }
+
   client_ = std::move(client);
   ArcVideoDecodeAccelerator::Result result = accelerator_->Initialize(
       config.To<ArcVideoDecodeAccelerator::Config>(), this);
@@ -197,6 +203,26 @@
   return base::ScopedFD(platform_file);
 }
 
+void GpuArcVideoDecodeAccelerator::AllocateProtectedBuffer(
+    ::arc::mojom::PortType port,
+    uint32_t index,
+    mojo::ScopedHandle handle,
+    uint64_t size,
+    AllocateProtectedBufferCallback callback) {
+  DVLOG(2) << "port=" << port << ", index=" << index << ", size=" << size;
+
+  base::ScopedFD fd = UnwrapFdFromMojoHandle(std::move(handle));
+  if (!fd.is_valid()) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  bool result = accelerator_->AllocateProtectedBuffer(
+      static_cast<PortType>(port), index, std::move(fd), size);
+
+  std::move(callback).Run(result);
+}
+
 void GpuArcVideoDecodeAccelerator::BindSharedMemory(
     ::arc::mojom::PortType port,
     uint32_t index,
diff --git a/chrome/gpu/gpu_arc_video_decode_accelerator.h b/chrome/gpu/gpu_arc_video_decode_accelerator.h
index 078c17d1..daf6088 100644
--- a/chrome/gpu/gpu_arc_video_decode_accelerator.h
+++ b/chrome/gpu/gpu_arc_video_decode_accelerator.h
@@ -19,6 +19,8 @@
 namespace chromeos {
 namespace arc {
 
+class ProtectedBufferManager;
+
 // GpuArcVideoDecodeAccelerator manages life-cycle and IPC message translation
 // for ArcVideoDecodeAccelerator.
 //
@@ -28,8 +30,9 @@
     : public ::arc::mojom::VideoDecodeAccelerator,
       public ArcVideoDecodeAccelerator::Client {
  public:
-  explicit GpuArcVideoDecodeAccelerator(
-      const gpu::GpuPreferences& gpu_preferences);
+  GpuArcVideoDecodeAccelerator(
+      const gpu::GpuPreferences& gpu_preferences,
+      ProtectedBufferManager* protected_buffer_manager);
   ~GpuArcVideoDecodeAccelerator() override;
 
  private:
@@ -46,6 +49,14 @@
   void Initialize(::arc::mojom::VideoDecodeAcceleratorConfigPtr config,
                   ::arc::mojom::VideoDecodeClientPtr client,
                   InitializeCallback callback) override;
+
+  void AllocateProtectedBuffer(
+      ::arc::mojom::PortType port,
+      uint32_t index,
+      mojo::ScopedHandle handle,
+      uint64_t size,
+      AllocateProtectedBufferCallback callback) override;
+
   void BindSharedMemory(::arc::mojom::PortType port,
                         uint32_t index,
                         mojo::ScopedHandle ashmem_handle,
diff --git a/chrome/gpu/protected_buffer_manager.cc b/chrome/gpu/protected_buffer_manager.cc
new file mode 100644
index 0000000..47bcdaf
--- /dev/null
+++ b/chrome/gpu/protected_buffer_manager.cc
@@ -0,0 +1,411 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/gpu/protected_buffer_manager.h"
+
+#include "base/bits.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/shared_memory.h"
+#include "base/sys_info.h"
+#include "mojo/public/cpp/system/buffer.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+#define VLOGF(level) VLOG(level) << __func__ << "(): "
+
+namespace chromeos {
+namespace arc {
+
+namespace {
+// Size of the pixmap to be used as the dummy handle for protected buffers.
+constexpr gfx::Size kDummyBufferSize(32, 32);
+}  // namespace
+
+ProtectedBufferHandle::ProtectedBufferHandle(
+    base::OnceClosure destruction_cb,
+    const base::SharedMemoryHandle& shm_handle)
+    : shm_handle_(shm_handle), destruction_cb_(std::move(destruction_cb)) {
+  DCHECK(shm_handle_.IsValid());
+  DCHECK(shm_handle.OwnershipPassesToIPC());
+}
+
+ProtectedBufferHandle::ProtectedBufferHandle(
+    base::OnceClosure destruction_cb,
+    const gfx::NativePixmapHandle& native_pixmap_handle)
+    : native_pixmap_handle_(native_pixmap_handle),
+      destruction_cb_(std::move(destruction_cb)) {
+  DCHECK(!native_pixmap_handle_.fds.empty());
+  for (const auto& fd : native_pixmap_handle_.fds)
+    DCHECK(fd.auto_close);
+}
+
+ProtectedBufferHandle::~ProtectedBufferHandle() {
+  if (shm_handle_.OwnershipPassesToIPC())
+    shm_handle_.Close();
+
+  for (const auto& fd : native_pixmap_handle_.fds) {
+    // Close the fd by wrapping it in a ScopedFD and letting
+    // it fall out of scope.
+    base::ScopedFD scoped_fd(fd.fd);
+  }
+
+  std::move(destruction_cb_).Run();
+}
+
+base::SharedMemoryHandle ProtectedBufferHandle::shm_handle() const {
+  base::SharedMemoryHandle handle = shm_handle_;
+  handle.SetOwnershipPassesToIPC(false);
+  return handle;
+}
+
+gfx::NativePixmapHandle ProtectedBufferHandle::native_pixmap_handle() const {
+  return native_pixmap_handle_;
+}
+
+class ProtectedBufferManager::ProtectedBuffer {
+ public:
+  virtual ~ProtectedBuffer() {}
+
+  // Downcasting methods to return duplicated handles to the underlying
+  // protected buffers for each buffer type, or empty/null handles if not
+  // applicable.
+  virtual base::SharedMemoryHandle DuplicateSharedMemoryHandle() const {
+    return base::SharedMemoryHandle();
+  }
+  virtual gfx::NativePixmapHandle DuplicateNativePixmapHandle() const {
+    return gfx::NativePixmapHandle();
+  }
+
+  // Downcasting method to return a scoped_refptr to the underlying
+  // NativePixmap, or null if not applicable.
+  virtual scoped_refptr<gfx::NativePixmap> GetNativePixmap() const {
+    return nullptr;
+  }
+
+ protected:
+  explicit ProtectedBuffer(scoped_refptr<gfx::NativePixmap> dummy_handle)
+      : dummy_handle_(std::move(dummy_handle)) {}
+
+ private:
+  scoped_refptr<gfx::NativePixmap> dummy_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProtectedBuffer);
+};
+
+class ProtectedBufferManager::ProtectedSharedMemory
+    : public ProtectedBufferManager::ProtectedBuffer {
+ public:
+  ~ProtectedSharedMemory() override;
+
+  // Allocate a ProtectedSharedMemory buffer of |size| bytes.
+  static std::unique_ptr<ProtectedSharedMemory> Create(
+      scoped_refptr<gfx::NativePixmap> dummy_handle,
+      size_t size);
+
+  base::SharedMemoryHandle DuplicateSharedMemoryHandle() const override {
+    return base::SharedMemory::DuplicateHandle(shmem_->handle());
+  }
+
+ private:
+  explicit ProtectedSharedMemory(scoped_refptr<gfx::NativePixmap> dummy_handle);
+
+  std::unique_ptr<base::SharedMemory> shmem_;
+};
+
+ProtectedBufferManager::ProtectedSharedMemory::ProtectedSharedMemory(
+    scoped_refptr<gfx::NativePixmap> dummy_handle)
+    : ProtectedBuffer(std::move(dummy_handle)) {}
+
+ProtectedBufferManager::ProtectedSharedMemory::~ProtectedSharedMemory() {}
+
+// static
+std::unique_ptr<ProtectedBufferManager::ProtectedSharedMemory>
+ProtectedBufferManager::ProtectedSharedMemory::Create(
+    scoped_refptr<gfx::NativePixmap> dummy_handle,
+    size_t size) {
+  std::unique_ptr<ProtectedSharedMemory> protected_shmem(
+      new ProtectedSharedMemory(std::move(dummy_handle)));
+
+  size_t aligned_size =
+      base::bits::Align(size, base::SysInfo::VMAllocationGranularity());
+
+  mojo::ScopedSharedBufferHandle mojo_shared_buffer =
+      mojo::SharedBufferHandle::Create(aligned_size);
+  if (!mojo_shared_buffer->is_valid()) {
+    VLOGF(1) << "Failed to allocate shared memory";
+    return nullptr;
+  }
+
+  base::SharedMemoryHandle shm_handle;
+  MojoResult mojo_result = mojo::UnwrapSharedMemoryHandle(
+      std::move(mojo_shared_buffer), &shm_handle, nullptr, nullptr);
+  if (mojo_result != MOJO_RESULT_OK) {
+    VLOGF(1) << "Failed to unwrap a mojo shared memory handle";
+    return nullptr;
+  }
+
+  protected_shmem->shmem_ =
+      std::make_unique<base::SharedMemory>(shm_handle, false);
+  return protected_shmem;
+}
+
+class ProtectedBufferManager::ProtectedNativePixmap
+    : public ProtectedBufferManager::ProtectedBuffer {
+ public:
+  ~ProtectedNativePixmap() override;
+
+  // Allocate a ProtectedNativePixmap of |format| and |size|.
+  static std::unique_ptr<ProtectedNativePixmap> Create(
+      scoped_refptr<gfx::NativePixmap> dummy_handle,
+      gfx::BufferFormat format,
+      const gfx::Size& size);
+
+  gfx::NativePixmapHandle DuplicateNativePixmapHandle() const override {
+    return native_pixmap_->ExportHandle();
+  }
+
+  scoped_refptr<gfx::NativePixmap> GetNativePixmap() const override {
+    return native_pixmap_;
+  }
+
+ private:
+  explicit ProtectedNativePixmap(scoped_refptr<gfx::NativePixmap> dummy_handle);
+
+  scoped_refptr<gfx::NativePixmap> native_pixmap_;
+};
+
+ProtectedBufferManager::ProtectedNativePixmap::ProtectedNativePixmap(
+    scoped_refptr<gfx::NativePixmap> dummy_handle)
+    : ProtectedBuffer(std::move(dummy_handle)) {}
+
+ProtectedBufferManager::ProtectedNativePixmap::~ProtectedNativePixmap() {}
+
+// static
+std::unique_ptr<ProtectedBufferManager::ProtectedNativePixmap>
+ProtectedBufferManager::ProtectedNativePixmap::Create(
+    scoped_refptr<gfx::NativePixmap> dummy_handle,
+    gfx::BufferFormat format,
+    const gfx::Size& size) {
+  std::unique_ptr<ProtectedNativePixmap> protected_pixmap(
+      new ProtectedNativePixmap(std::move(dummy_handle)));
+
+  ui::OzonePlatform* platform = ui::OzonePlatform::GetInstance();
+  ui::SurfaceFactoryOzone* factory = platform->GetSurfaceFactoryOzone();
+  protected_pixmap->native_pixmap_ =
+      factory->CreateNativePixmap(gfx::kNullAcceleratedWidget, size, format,
+                                  gfx::BufferUsage::SCANOUT_VDA_WRITE);
+
+  if (!protected_pixmap->native_pixmap_) {
+    VLOGF(1) << "Failed allocating a native pixmap";
+    return nullptr;
+  }
+
+  return protected_pixmap;
+}
+
+ProtectedBufferManager::ProtectedBufferManager() : weak_factory_(this) {
+  VLOGF(2);
+  weak_this_ = weak_factory_.GetWeakPtr();
+}
+
+ProtectedBufferManager::~ProtectedBufferManager() {
+  VLOGF(2);
+}
+
+std::unique_ptr<ProtectedBufferHandle>
+ProtectedBufferManager::AllocateProtectedSharedMemory(base::ScopedFD dummy_fd,
+                                                      size_t size) {
+  VLOGF(2) << "dummy_fd: " << dummy_fd.get() << ", size: " << size;
+
+  // Import the |dummy_fd| to produce a unique id for it.
+  uint32_t id;
+  auto pixmap = ImportDummyFd(std::move(dummy_fd), &id);
+  if (!pixmap)
+    return nullptr;
+
+  base::AutoLock lock(buffer_map_lock_);
+
+  if (buffer_map_.find(id) != buffer_map_.end()) {
+    VLOGF(1) << "A protected buffer for this handle already exists";
+    return nullptr;
+  }
+
+  // Allocate a protected buffer and associate it with the dummy pixmap.
+  // The pixmap needs to be stored to ensure the id remains the same for
+  // the entire lifetime of the dummy pixmap.
+  auto protected_shmem = ProtectedSharedMemory::Create(pixmap, size);
+  if (!protected_shmem) {
+    VLOGF(1) << "Failed allocating a protected shared memory buffer";
+    return nullptr;
+  }
+
+  auto shm_handle = protected_shmem->DuplicateSharedMemoryHandle();
+  if (!shm_handle.IsValid()) {
+    VLOGF(1) << "Failed duplicating SharedMemoryHandle";
+    return nullptr;
+  }
+
+  // Store the buffer in the buffer_map_, and return a handle to it to the
+  // client. The buffer will be permanently removed from the map when the
+  // handle is destroyed.
+  VLOGF(2) << "New protected shared memory buffer, handle id: " << id;
+  auto protected_buffer_handle = base::MakeUnique<ProtectedBufferHandle>(
+      base::BindOnce(&ProtectedBufferManager::RemoveEntry, weak_this_, id),
+      shm_handle);
+
+  // This will always succeed as we find() first above.
+  buffer_map_.emplace(id, std::move(protected_shmem));
+
+  return protected_buffer_handle;
+}
+
+std::unique_ptr<ProtectedBufferHandle>
+ProtectedBufferManager::AllocateProtectedNativePixmap(base::ScopedFD dummy_fd,
+                                                      gfx::BufferFormat format,
+                                                      const gfx::Size& size) {
+  VLOGF(2) << "dummy_fd: " << dummy_fd.get() << ", format: " << (int)format
+           << ", size: " << size.ToString();
+
+  // Import the |dummy_fd| to produce a unique id for it.
+  uint32_t id = 0;
+  auto pixmap = ImportDummyFd(std::move(dummy_fd), &id);
+  if (!pixmap)
+    return nullptr;
+
+  base::AutoLock lock(buffer_map_lock_);
+
+  if (buffer_map_.find(id) != buffer_map_.end()) {
+    VLOGF(1) << "A protected buffer for this handle already exists";
+    return nullptr;
+  }
+
+  // Allocate a protected buffer and associate it with the dummy pixmap.
+  // The pixmap needs to be stored to ensure the id remains the same for
+  // the entire lifetime of the dummy pixmap.
+  auto protected_pixmap = ProtectedNativePixmap::Create(pixmap, format, size);
+  if (!protected_pixmap) {
+    VLOGF(1) << "Failed allocating a protected native pixmap";
+    return nullptr;
+  }
+
+  auto native_pixmap_handle = protected_pixmap->DuplicateNativePixmapHandle();
+  if (native_pixmap_handle.planes.empty()) {
+    VLOGF(1) << "Failed duplicating NativePixmapHandle";
+    return nullptr;
+  }
+
+  // Store the buffer in the buffer_map_, and return a handle to it to the
+  // client. The buffer will be permanently removed from the map when the
+  // handle is destroyed.
+  VLOGF(2) << "New protected native pixmap, handle id: " << id;
+  auto protected_buffer_handle = base::MakeUnique<ProtectedBufferHandle>(
+      base::BindOnce(&ProtectedBufferManager::RemoveEntry, weak_this_, id),
+      native_pixmap_handle);
+
+  // This will always succeed as we find() first above.
+  buffer_map_.emplace(id, std::move(protected_pixmap));
+
+  return protected_buffer_handle;
+}
+
+base::SharedMemoryHandle
+ProtectedBufferManager::GetProtectedSharedMemoryHandleFor(
+    base::ScopedFD dummy_fd) {
+  uint32_t id = 0;
+  auto pixmap = ImportDummyFd(std::move(dummy_fd), &id);
+
+  base::AutoLock lock(buffer_map_lock_);
+  const auto& iter = buffer_map_.find(id);
+  if (iter == buffer_map_.end())
+    return base::SharedMemoryHandle();
+
+  return iter->second->DuplicateSharedMemoryHandle();
+}
+
+gfx::NativePixmapHandle
+ProtectedBufferManager::GetProtectedNativePixmapHandleFor(
+    base::ScopedFD dummy_fd) {
+  uint32_t id = 0;
+  auto pixmap = ImportDummyFd(std::move(dummy_fd), &id);
+
+  base::AutoLock lock(buffer_map_lock_);
+  const auto& iter = buffer_map_.find(id);
+  if (iter == buffer_map_.end())
+    return gfx::NativePixmapHandle();
+
+  return iter->second->DuplicateNativePixmapHandle();
+}
+
+scoped_refptr<gfx::NativePixmap>
+ProtectedBufferManager::GetProtectedNativePixmapFor(
+    const gfx::NativePixmapHandle& handle) {
+  // Only the first fd is used for lookup.
+  if (handle.fds.empty())
+    return nullptr;
+
+  base::ScopedFD dummy_fd(HANDLE_EINTR(dup(handle.fds[0].fd)));
+  uint32_t id = 0;
+  auto pixmap = ImportDummyFd(std::move(dummy_fd), &id);
+
+  base::AutoLock lock(buffer_map_lock_);
+  const auto& iter = buffer_map_.find(id);
+  if (iter == buffer_map_.end())
+    return nullptr;
+
+  auto native_pixmap = iter->second->GetNativePixmap();
+  if (native_pixmap) {
+    for (const auto& fd : handle.fds)
+      base::ScopedFD scoped_fd(fd.fd);
+  }
+
+  return native_pixmap;
+}
+
+scoped_refptr<gfx::NativePixmap> ProtectedBufferManager::ImportDummyFd(
+    base::ScopedFD dummy_fd,
+    uint32_t* id) const {
+  // 0 is an invalid handle id.
+  *id = 0;
+
+  // Import dummy_fd to acquire its unique id.
+  // CreateNativePixmapFromHandle() takes ownership and will close the handle
+  // also on failure.
+  gfx::NativePixmapHandle pixmap_handle;
+  pixmap_handle.fds.emplace_back(
+      base::FileDescriptor(dummy_fd.release(), true));
+  pixmap_handle.planes.emplace_back(gfx::NativePixmapPlane());
+  ui::OzonePlatform* platform = ui::OzonePlatform::GetInstance();
+  ui::SurfaceFactoryOzone* factory = platform->GetSurfaceFactoryOzone();
+  scoped_refptr<gfx::NativePixmap> pixmap =
+      factory->CreateNativePixmapForProtectedBufferHandle(
+          gfx::kNullAcceleratedWidget, kDummyBufferSize, gfx::BufferFormat::R_8,
+          pixmap_handle);
+  if (!pixmap) {
+    VLOGF(1) << "Failed importing dummy handle";
+    return nullptr;
+  }
+
+  *id = pixmap->GetUniqueId();
+  if (*id == 0) {
+    VLOGF(1) << "Failed acquiring unique id for handle";
+    return nullptr;
+  }
+
+  return pixmap;
+}
+
+void ProtectedBufferManager::RemoveEntry(uint32_t id) {
+  VLOGF(2) << "id: " << id;
+
+  base::AutoLock lock(buffer_map_lock_);
+  auto num_erased = buffer_map_.erase(id);
+  if (num_erased != 1)
+    VLOGF(1) << "No buffer id " << id << " to destroy";
+}
+
+}  // namespace arc
+}  // namespace chromeos
diff --git a/chrome/gpu/protected_buffer_manager.h b/chrome/gpu/protected_buffer_manager.h
new file mode 100644
index 0000000..40a5e01
--- /dev/null
+++ b/chrome/gpu/protected_buffer_manager.h
@@ -0,0 +1,145 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_GPU_PROTECTED_BUFFER_MANAGER_H_
+#define CHROME_GPU_PROTECTED_BUFFER_MANAGER_H_
+
+#include <map>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory.h"
+#include "base/memory/weak_ptr.h"
+#include "base/synchronization/lock.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+#include "ui/gfx/native_pixmap.h"
+
+namespace chromeos {
+namespace arc {
+
+// A ProtectedBufferHandle is returned to the owning client that requested
+// the underlying ProtectedBuffer to be allocated.
+//
+// A ProtectedBuffer is a buffer that can be referred to via a handle (a dummy
+// handle), which does not provide access to the actual contents of the buffer.
+//
+// The client should release this handle once the buffer is no longer needed.
+// Releasing triggers destruction of the ProtectedBuffer instance stored in
+// the ProtectedBufferManager, via the destruction callback passed to the
+// ProtectedBufferHandle's constructor.
+class ProtectedBufferHandle {
+ public:
+  // ProtectedBufferHandle takes ownership of the passed |shm_handle|.
+  ProtectedBufferHandle(base::OnceClosure destruction_cb,
+                        const base::SharedMemoryHandle& shm_handle);
+
+  // ProtectedBufferHandle takes ownership of the passed |native_pixmap_handle|.
+  ProtectedBufferHandle(base::OnceClosure destruction_cb,
+                        const gfx::NativePixmapHandle& native_pixmap_handle);
+
+  // Closes the underlying handle.
+  ~ProtectedBufferHandle();
+
+  // Return a non-owned SharedMemoryHandle or NativePixmapHandle for this
+  // ProtectedBufferHandle, or an invalid/null handle if not applicable for the
+  // underlying type.
+  base::SharedMemoryHandle shm_handle() const;
+  gfx::NativePixmapHandle native_pixmap_handle() const;
+
+ private:
+  // The underlying, owning handles to the protected buffer.
+  // Only one of the handles is valid for each instance of this class.
+  // Closed on destruction of this ProtectedBufferHandle.
+  base::SharedMemoryHandle shm_handle_;
+  gfx::NativePixmapHandle native_pixmap_handle_;
+
+  base::OnceClosure destruction_cb_;
+};
+
+class ProtectedBufferManager {
+ public:
+  ProtectedBufferManager();
+  ~ProtectedBufferManager();
+
+  // Allocate a ProtectedSharedMemory buffer of |size| bytes, to be referred to
+  // via |dummy_fd| as the dummy handle, returning a ProtectedBufferHandle to
+  // it.
+  // Destroying the ProtectedBufferHandle will result in permanently
+  // disassociating the |dummy_fd| with the underlying ProtectedBuffer, but may
+  // not free the underlying protected memory, which will remain valid as long
+  // as any SharedMemoryHandles to it are still in use.
+  // Return nullptr on failure.
+  std::unique_ptr<ProtectedBufferHandle> AllocateProtectedSharedMemory(
+      base::ScopedFD dummy_fd,
+      size_t size);
+
+  // Allocate a ProtectedNativePixmap of |format| and |size|, to be referred to
+  // via |dummy_fd| as the dummy handle, returning a ProtectedBufferHandle to
+  // it.
+  // Destroying the ProtectedBufferHandle will result in permanently
+  // disassociating the |dummy_fd| with the underlying ProtectedBuffer, but may
+  // not free the underlying protected memory, which will remain valid as long
+  // as any NativePixmapHandles to it are still in use.
+  // Return nullptr on failure.
+  std::unique_ptr<ProtectedBufferHandle> AllocateProtectedNativePixmap(
+      base::ScopedFD dummy_fd,
+      gfx::BufferFormat format,
+      const gfx::Size& size);
+
+  // Return a duplicated SharedMemoryHandle associated with the |dummy_fd|,
+  // if one exists, or an invalid handle otherwise.
+  // The client is responsible for closing the handle after use.
+  base::SharedMemoryHandle GetProtectedSharedMemoryHandleFor(
+      base::ScopedFD dummy_fd);
+
+  // Return a duplicated NativePixmapHandle associated with the |dummy_fd|,
+  // if one exists, or an empty handle otherwise.
+  // The client is responsible for closing the handle after use.
+  gfx::NativePixmapHandle GetProtectedNativePixmapHandleFor(
+      base::ScopedFD dummy_fd);
+
+  // Return a protected NativePixmap for a dummy |handle|, if one exists, or
+  // nullptr otherwise. On success, the |handle| is closed.
+  scoped_refptr<gfx::NativePixmap> GetProtectedNativePixmapFor(
+      const gfx::NativePixmapHandle& handle);
+
+ private:
+  // Used internally to maintain the association between the dummy handle and
+  // the underlying buffer.
+  class ProtectedBuffer;
+  class ProtectedSharedMemory;
+  class ProtectedNativePixmap;
+
+  // Imports the |dummy_fd| as a NativePixmap. This returns a unique |id|,
+  // which is guaranteed to be the same for all future imports of any fd
+  // referring to the buffer to which |dummy_fd| refers to, regardless of
+  // whether it is the same fd as the original one, or not, for the lifetime
+  // of the buffer.
+  //
+  // This allows us to have an unambiguous mapping from any fd referring to
+  // the same memory buffer to the same unique id.
+  //
+  // Returns nullptr on failure, in which case the returned id is not valid.
+  scoped_refptr<gfx::NativePixmap> ImportDummyFd(base::ScopedFD dummy_fd,
+                                                 uint32_t* id) const;
+
+  // Removes an entry for given |id| from buffer_map_, to be called when the
+  // last reference to the buffer is dropped.
+  void RemoveEntry(uint32_t id);
+
+  // A map of unique ids to the ProtectedBuffers associated with them.
+  using ProtectedBufferMap =
+      std::map<uint32_t, std::unique_ptr<ProtectedBuffer>>;
+  ProtectedBufferMap buffer_map_;
+  base::Lock buffer_map_lock_;
+
+  base::WeakPtr<ProtectedBufferManager> weak_this_;
+  base::WeakPtrFactory<ProtectedBufferManager> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProtectedBufferManager);
+};
+
+}  // namespace arc
+}  // namespace chromeos
+
+#endif  // CHROME_GPU_PROTECTED_BUFFER_MANAGER_H_
diff --git a/chrome/gpu/protected_buffer_manager_proxy.cc b/chrome/gpu/protected_buffer_manager_proxy.cc
new file mode 100644
index 0000000..8080ed1
--- /dev/null
+++ b/chrome/gpu/protected_buffer_manager_proxy.cc
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/gpu/protected_buffer_manager_proxy.h"
+
+#include "chrome/gpu/protected_buffer_manager.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+#define VLOGF(level) VLOG(level) << __func__ << "(): "
+
+namespace chromeos {
+namespace arc {
+
+GpuArcProtectedBufferManagerProxy::GpuArcProtectedBufferManagerProxy(
+    chromeos::arc::ProtectedBufferManager* protected_buffer_manager)
+    : protected_buffer_manager_(protected_buffer_manager) {
+  DCHECK(protected_buffer_manager_);
+}
+
+base::ScopedFD GpuArcProtectedBufferManagerProxy::UnwrapFdFromMojoHandle(
+    mojo::ScopedHandle handle) {
+  base::PlatformFile platform_file;
+  MojoResult mojo_result =
+      mojo::UnwrapPlatformFile(std::move(handle), &platform_file);
+  if (mojo_result != MOJO_RESULT_OK) {
+    VLOGF(1) << "UnwrapPlatformFile failed: " << mojo_result;
+    return base::ScopedFD();
+  }
+
+  return base::ScopedFD(platform_file);
+}
+
+mojo::ScopedHandle GpuArcProtectedBufferManagerProxy::WrapFdInMojoHandle(
+    base::ScopedFD fd) {
+  return mojo::WrapPlatformFile(fd.release());
+}
+
+void GpuArcProtectedBufferManagerProxy::GetProtectedSharedMemoryFromHandle(
+    mojo::ScopedHandle dummy_handle,
+    GetProtectedSharedMemoryFromHandleCallback callback) {
+  base::ScopedFD unwrapped_fd = UnwrapFdFromMojoHandle(std::move(dummy_handle));
+
+  base::ScopedFD shmem_fd(
+      protected_buffer_manager_
+          ->GetProtectedSharedMemoryHandleFor(std::move(unwrapped_fd))
+          .Release());
+
+  std::move(callback).Run(WrapFdInMojoHandle(std::move(shmem_fd)));
+}
+
+}  // namespace arc
+}  // namespace chromeos
diff --git a/chrome/gpu/protected_buffer_manager_proxy.h b/chrome/gpu/protected_buffer_manager_proxy.h
new file mode 100644
index 0000000..324065c
--- /dev/null
+++ b/chrome/gpu/protected_buffer_manager_proxy.h
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_GPU_PROTECTED_BUFFER_MANAGER_PROXY_H_
+#define CHROME_GPU_PROTECTED_BUFFER_MANAGER_PROXY_H_
+
+#include "components/arc/common/protected_buffer_manager.mojom.h"
+
+namespace chromeos {
+namespace arc {
+
+class ProtectedBufferManager;
+
+// Manages mojo IPC translation for chromeos::arc::ProtectedBufferManager.
+class GpuArcProtectedBufferManagerProxy
+    : public ::arc::mojom::ProtectedBufferManager {
+ public:
+  explicit GpuArcProtectedBufferManagerProxy(
+      chromeos::arc::ProtectedBufferManager* protected_buffer_manager);
+
+  // arc::mojom::ProtectedBufferManager implementation.
+  void GetProtectedSharedMemoryFromHandle(
+      mojo::ScopedHandle dummy_handle,
+      GetProtectedSharedMemoryFromHandleCallback callback) override;
+
+ private:
+  base::ScopedFD UnwrapFdFromMojoHandle(mojo::ScopedHandle handle);
+  mojo::ScopedHandle WrapFdInMojoHandle(base::ScopedFD fd);
+
+  chromeos::arc::ProtectedBufferManager* protected_buffer_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(GpuArcProtectedBufferManagerProxy);
+};
+
+}  // namespace arc
+}  // namespace chromeos
+
+#endif  // CHROME_GPU_PROTECTED_BUFFER_MANAGER_PROXY_H_
diff --git a/components/arc/common/oemcrypto_daemon.mojom b/components/arc/common/oemcrypto_daemon.mojom
index 5b2b782..b75db2d 100644
--- a/components/arc/common/oemcrypto_daemon.mojom
+++ b/components/arc/common/oemcrypto_daemon.mojom
@@ -21,5 +21,5 @@
 // Next Method ID: 1
 interface OemCryptoHostDaemon {
   Connect@0(arc.mojom.OemCryptoService& oemcryptor,
-            media.mojom.ProtectedBufferManager protected_buffer_manager);
+            arc.mojom.ProtectedBufferManager protected_buffer_manager);
 };
diff --git a/components/arc/common/protected_buffer_manager.mojom b/components/arc/common/protected_buffer_manager.mojom
index b226ea3d..298f6d3 100644
--- a/components/arc/common/protected_buffer_manager.mojom
+++ b/components/arc/common/protected_buffer_manager.mojom
@@ -5,7 +5,7 @@
 // The original version of this file lives in the Chromium repository at:
 // src/components/arc/common/protected_buffer_manager.mojom
 
-module media.mojom;
+module arc.mojom;
 
 // This interface is exposed by the GPU process for translating dummy handles
 // for secure buffers into a usable shared memory handle. The output of a
diff --git a/components/arc/common/video_decode_accelerator.mojom b/components/arc/common/video_decode_accelerator.mojom
index bb732f9..5307594 100644
--- a/components/arc/common/video_decode_accelerator.mojom
+++ b/components/arc/common/video_decode_accelerator.mojom
@@ -40,6 +40,7 @@
   uint32 crop_height;
 };
 
+// Next MinVersion: 2
 struct VideoDecodeAcceleratorConfig {
   // Deprecated. This config struct is used for decoder only.
   enum DeviceTypeDeprecated {
@@ -51,11 +52,12 @@
   DeviceTypeDeprecated device_type_deprecated;
   uint32 num_input_buffers;
   uint32 input_pixel_format;
+  [MinVersion=1] bool secure_mode;
 };
 
-// Next MinVersion: 4
+// Next MinVersion: 5
 // Deprecated method IDs: 2, 7
-// Next method ID: 10
+// Next method ID: 11
 interface VideoDecodeAccelerator {
   enum Result {
     SUCCESS = 0,
@@ -70,6 +72,10 @@
   Initialize@8(VideoDecodeAcceleratorConfig config,
                VideoDecodeClient client) => (Result result);
 
+  [MinVersion=4]
+  AllocateProtectedBuffer@10(PortType port, uint32 index, handle handle_fd,
+                             uint64 size) => (bool result);
+
   BindSharedMemory@1(PortType port, uint32 index, handle ashmem_fd,
                      uint32 offset, uint32 length);
 
diff --git a/components/viz/host/server_gpu_memory_buffer_manager.cc b/components/viz/host/server_gpu_memory_buffer_manager.cc
index dc4a48fc..3a045ac 100644
--- a/components/viz/host/server_gpu_memory_buffer_manager.cc
+++ b/components/viz/host/server_gpu_memory_buffer_manager.cc
@@ -75,7 +75,7 @@
       gpu::GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(size,
                                                                  format)) {
     buffer_handle = gpu::GpuMemoryBufferImplSharedMemory::CreateGpuMemoryBuffer(
-        id, size, format);
+        id, size, format, usage);
     BufferInfo buffer_info;
     DCHECK_EQ(gfx::SHARED_MEMORY_BUFFER, buffer_handle.type);
     buffer_info.type = gfx::SHARED_MEMORY_BUFFER;
diff --git a/content/browser/gpu/browser_gpu_memory_buffer_manager.cc b/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
index ba34e09..bab50551 100644
--- a/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
+++ b/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
@@ -136,7 +136,7 @@
   }
 
   auto handle = gpu::GpuMemoryBufferImplSharedMemory::CreateGpuMemoryBuffer(
-      id, size, format);
+      id, size, format, usage);
   buffers.find(id)->second.shared_memory_guid = handle.handle.GetGUID();
   callback.Run(handle);
 }
@@ -292,7 +292,7 @@
   // Note: Unretained is safe as IO thread is stopped before manager is
   // destroyed.
   request->result = gpu::GpuMemoryBufferImplSharedMemory::Create(
-      new_id, request->size, request->format,
+      new_id, request->size, request->format, request->usage,
       base::Bind(
           &GpuMemoryBufferDeleted,
           BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 6978fc34..0c7e2e9 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1792,17 +1792,6 @@
 
   AddUIThreadInterface(
       registry.get(),
-      base::Bind(&PermissionServiceContext::CreateService,
-                 base::Unretained(permission_service_context_.get())));
-
-  AddUIThreadInterface(
-      registry.get(),
-      base::Bind(
-          &PaymentAppContextImpl::CreatePaymentManager,
-          base::Unretained(storage_partition_impl_->GetPaymentAppContext())));
-
-  AddUIThreadInterface(
-      registry.get(),
       base::Bind(&RenderProcessHostImpl::CreateOffscreenCanvasProvider,
                  base::Unretained(this)));
 
@@ -1912,12 +1901,6 @@
   registry->AddInterface(
       base::Bind(&CreateReportingServiceProxy, storage_partition_impl_));
 
-  // This is to support usage of WebSockets in cases in which there is no
-  // associated RenderFrame (e.g., Shared Workers).
-  AddUIThreadInterface(registry.get(),
-                       base::Bind(&WebSocketManager::CreateWebSocket, GetID(),
-                                  MSG_ROUTING_NONE));
-
   AddUIThreadInterface(registry.get(), base::Bind(&FieldTrialRecorder::Create));
 
   associated_interfaces_.reset(new AssociatedInterfaceRegistryImpl());
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 005ca0d..d13ab263 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -388,6 +388,10 @@
   // globally-used spare RenderProcessHost at any time.
   static RenderProcessHost* GetSpareRenderProcessHostForTesting();
 
+  PermissionServiceContext& permission_service_context() {
+    return *permission_service_context_;
+  };
+
  protected:
   // A proxy for our IPC::Channel that lives on the IO thread.
   std::unique_ptr<IPC::ChannelProxy> channel_;
diff --git a/content/browser/worker_interface_binders.cc b/content/browser/worker_interface_binders.cc
index f6338ae..5485a55 100644
--- a/content/browser/worker_interface_binders.cc
+++ b/content/browser/worker_interface_binders.cc
@@ -6,6 +6,12 @@
 
 #include <utility>
 
+#include "base/bind.h"
+#include "content/browser/payments/payment_manager.h"
+#include "content/browser/permissions/permission_service_context.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/browser/storage_partition_impl.h"
+#include "content/browser/websockets/websocket_manager.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
@@ -71,6 +77,26 @@
   parameterized_binder_registry_.AddInterface(
       base::Bind(&ForwardRequest<shape_detection::mojom::TextDetection>,
                  shape_detection::mojom::kServiceName));
+  parameterized_binder_registry_.AddInterface(
+      base::Bind([](blink::mojom::WebSocketRequest request,
+                    RenderProcessHost* host, const url::Origin& origin) {
+        WebSocketManager::CreateWebSocket(host->GetID(), MSG_ROUTING_NONE,
+                                          std::move(request));
+      }));
+  parameterized_binder_registry_.AddInterface(
+      base::Bind([](payments::mojom::PaymentManagerRequest request,
+                    RenderProcessHost* host, const url::Origin& origin) {
+        static_cast<StoragePartitionImpl*>(host->GetStoragePartition())
+            ->GetPaymentAppContext()
+            ->CreatePaymentManager(std::move(request));
+      }));
+  parameterized_binder_registry_.AddInterface(
+      base::Bind([](blink::mojom::PermissionServiceRequest request,
+                    RenderProcessHost* host, const url::Origin& origin) {
+        static_cast<RenderProcessHostImpl*>(host)
+            ->permission_service_context()
+            .CreateService(std::move(request));
+      }));
 }
 
 }  // namespace
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index b18a7a8..987ac0594 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -26,10 +26,8 @@
           "blink::mojom::MimeRegistry",
           "blink::mojom::NotificationService",
           "blink::mojom::OffscreenCanvasProvider",
-          "blink::mojom::PermissionService",
           "blink::mojom::ReportingServiceProxy",
           "blink::mojom::WebDatabaseHost",
-          "blink::mojom::WebSocket",
           "content::mojom::FieldTrialRecorder",
           "content::mojom::FileUtilitiesHost",
           "content::mojom::FrameSinkProvider",
@@ -49,11 +47,7 @@
           "media::mojom::VideoDecodePerfHistory",
           "memory_coordinator::mojom::MemoryCoordinatorHandle",
           "metrics::mojom::SingleSampleMetricsProvider",
-          "payments::mojom::PaymentManager",
           "resource_coordinator::mojom::ProcessCoordinationUnit",
-          "shape_detection::mojom::BarcodeDetection",
-          "shape_detection::mojom::FaceDetectionProvider",
-          "shape_detection::mojom::TextDetection",
           "ui::mojom::Gpu",
           "viz::mojom::SharedBitmapAllocationNotifier",
           "viz::mojom::CompositingModeReporter"
@@ -177,6 +171,8 @@
     "navigation:dedicated_worker": {
       "provides": {
         "renderer": [
+          "blink::mojom::PermissionService",
+          "payments::mojom::PaymentManager",
           "shape_detection::mojom::BarcodeDetection",
           "shape_detection::mojom::FaceDetectionProvider",
           "shape_detection::mojom::TextDetection"
@@ -186,6 +182,9 @@
     "navigation:service_worker": {
       "provides": {
         "renderer": [
+          "blink::mojom::PermissionService",
+          "blink::mojom::WebSocket",
+          "payments::mojom::PaymentManager",
           "shape_detection::mojom::BarcodeDetection",
           "shape_detection::mojom::FaceDetectionProvider",
           "shape_detection::mojom::TextDetection"
@@ -195,6 +194,9 @@
     "navigation:shared_worker": {
       "provides": {
         "renderer": [
+          "blink::mojom::PermissionService",
+          "blink::mojom::WebSocket",
+          "payments::mojom::PaymentManager",
           "shape_detection::mojom::BarcodeDetection",
           "shape_detection::mojom::FaceDetectionProvider",
           "shape_detection::mojom::TextDetection"
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 7fa3ad2c..2a37e0f 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -100,45 +100,45 @@
     self.Flaky('deqp/functional/gles3/textureshadow/*.html',
         ['win', 'nvidia', 'd3d11'], bug=735464)
 
-    # Win10 / NVIDIA Quadro P400 / D3D11 flaky failures
+    # Win / NVIDIA Quadro P400 / D3D11 flaky failures
     self.Fail('deqp/functional/gles3/transformfeedback/' +
         'basic_types_interleaved_lines.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
     self.Fail('deqp/functional/gles3/transformfeedback/' +
         'basic_types_interleaved_triangles.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
     self.Fail('deqp/functional/gles3/transformfeedback/' +
         'basic_types_separate_lines.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
     self.Fail('deqp/functional/gles3/transformfeedback/' +
         'basic_types_separate_triangles.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
     self.Fail('deqp/functional/gles3/transformfeedback/' +
         'random_interleaved_lines.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
     self.Fail('deqp/functional/gles3/transformfeedback/' +
         'random_interleaved_triangles.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
     self.Fail('deqp/functional/gles3/transformfeedback/' +
         'random_separate_lines.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
     self.Fail('deqp/functional/gles3/transformfeedback/' +
         'random_separate_triangles.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
     self.Fail('deqp/functional/gles3/transformfeedback/interpolation_flat.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=680754)
     self.Flaky('conformance/textures/image_bitmap_from_video/' +
         'tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=728670)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=728670)
     self.Flaky('conformance/textures/image_bitmap_from_video/' +
         'tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=728670)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=728670)
     self.Flaky('conformance2/textures/video/*',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=728670)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=728670)
     self.Flaky('conformance2/textures/image_bitmap_from_video/*',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=728670)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=728670)
     self.Flaky('conformance/extensions/oes-texture-half-float-with-video.html',
-        ['win10', ('nvidia', 0x1cb3), 'd3d11'], bug=728670)
+        ['win', ('nvidia', 0x1cb3), 'd3d11'], bug=728670)
 
     # Win / NVIDIA / OpenGL
     self.Fail('conformance2/glsl3/vector-dynamic-indexing-nv-driver-bug.html',
@@ -725,6 +725,17 @@
     self.Skip('conformance2/rendering/blitframebuffer-size-overflow.html',
         ['linux', ('nvidia', 0x1cb3)], bug=709320)
 
+    # Linux NVIDIA Quadro P400, OpenGL backend
+    self.Fail('conformance/limits/gl-max-texture-dimensions.html',
+        ['linux', ('nvidia', 0x1cb3)], bug=715001)
+    self.Fail('conformance/textures/misc/texture-size.html',
+        ['linux', ('nvidia', 0x1cb3), 'opengl'], bug=703779)
+    self.Fail('conformance/extensions/webgl-compressed-texture-size-limit.html',
+        ['linux', ('nvidia', 0x1cb3), 'opengl'], bug=703779)
+    self.Fail('conformance/textures/misc/texture-size-limit.html',
+        ['linux', ('nvidia', 0x1cb3), 'opengl'], bug=703779)
+
+
     # Linux Intel
     self.Fail('conformance2/extensions/ext-color-buffer-float.html',
         ['linux', 'intel'], bug=640389)
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index d9eb018d..117f907 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -300,11 +300,11 @@
 
     # WIN / OpenGL / NVIDIA failures
     self.Fail('conformance/textures/misc/texture-size.html',
-        ['win10', ('nvidia', 0x1cb3), 'opengl'], bug=703779)
+        ['win', ('nvidia', 0x1cb3), 'opengl'], bug=703779)
     self.Fail('conformance/extensions/webgl-compressed-texture-size-limit.html',
-        ['win10', ('nvidia', 0x1cb3), 'opengl'], bug=703779)
+        ['win', ('nvidia', 0x1cb3), 'opengl'], bug=703779)
     self.Fail('conformance/textures/misc/texture-size-limit.html',
-        ['win10', ('nvidia', 0x1cb3), 'opengl'], bug=703779)
+        ['win', ('nvidia', 0x1cb3), 'opengl'], bug=703779)
 
     # Mark ANGLE's OpenGL as flaky on Windows Nvidia
     self.Flaky('conformance/*', ['win', 'nvidia', 'opengl'], bug=582083)
diff --git a/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.cc b/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.cc
index 0ffa7560..e629a5f 100644
--- a/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.cc
+++ b/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.cc
@@ -22,6 +22,7 @@
     gfx::GpuMemoryBufferId id,
     const gfx::Size& size,
     gfx::BufferFormat format,
+    gfx::BufferUsage usage,
     const DestructionCallback& callback,
     std::unique_ptr<base::SharedMemory> shared_memory,
     size_t offset,
@@ -30,6 +31,7 @@
       shared_memory_(std::move(shared_memory)),
       offset_(offset),
       stride_(stride) {
+  DCHECK(IsUsageSupported(usage));
   DCHECK(IsSizeValidForFormat(size, format));
 }
 
@@ -40,7 +42,10 @@
 GpuMemoryBufferImplSharedMemory::Create(gfx::GpuMemoryBufferId id,
                                         const gfx::Size& size,
                                         gfx::BufferFormat format,
+                                        gfx::BufferUsage usage,
                                         const DestructionCallback& callback) {
+  if (!IsUsageSupported(usage))
+    return nullptr;
   size_t buffer_size = 0u;
   if (!gfx::BufferSizeForBufferFormatChecked(size, format, &buffer_size))
     return nullptr;
@@ -50,7 +55,7 @@
     return nullptr;
 
   return base::WrapUnique(new GpuMemoryBufferImplSharedMemory(
-      id, size, format, callback, std::move(shared_memory), 0,
+      id, size, format, usage, callback, std::move(shared_memory), 0,
       gfx::RowSizeForBufferFormat(size.width(), format, 0)));
 }
 
@@ -59,7 +64,10 @@
 GpuMemoryBufferImplSharedMemory::CreateGpuMemoryBuffer(
     gfx::GpuMemoryBufferId id,
     const gfx::Size& size,
-    gfx::BufferFormat format) {
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage) {
+  if (!IsUsageSupported(usage))
+    return gfx::GpuMemoryBufferHandle();
   size_t buffer_size = 0u;
   if (!gfx::BufferSizeForBufferFormatChecked(size, format, &buffer_size))
     return gfx::GpuMemoryBufferHandle();
@@ -89,7 +97,7 @@
   DCHECK(base::SharedMemory::IsHandleValid(handle.handle));
 
   return base::WrapUnique(new GpuMemoryBufferImplSharedMemory(
-      handle.id, size, format, callback,
+      handle.id, size, format, usage, callback,
       std::make_unique<base::SharedMemory>(handle.handle, false), handle.offset,
       handle.stride));
 }
@@ -167,7 +175,7 @@
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
     gfx::GpuMemoryBufferHandle* handle) {
-  *handle = CreateGpuMemoryBuffer(handle->id, size, format);
+  *handle = CreateGpuMemoryBuffer(handle->id, size, format, usage);
   return base::Bind(&base::DoNothing);
 }
 
diff --git a/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h b/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h
index 47577fa..3c2504d 100644
--- a/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h
+++ b/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h
@@ -24,12 +24,14 @@
       gfx::GpuMemoryBufferId id,
       const gfx::Size& size,
       gfx::BufferFormat format,
+      gfx::BufferUsage usage,
       const DestructionCallback& callback);
 
   static gfx::GpuMemoryBufferHandle CreateGpuMemoryBuffer(
       gfx::GpuMemoryBufferId id,
       const gfx::Size& size,
-      gfx::BufferFormat format);
+      gfx::BufferFormat format,
+      gfx::BufferUsage usage);
 
   static std::unique_ptr<GpuMemoryBufferImplSharedMemory> CreateFromHandle(
       const gfx::GpuMemoryBufferHandle& handle,
@@ -63,6 +65,7 @@
       gfx::GpuMemoryBufferId id,
       const gfx::Size& size,
       gfx::BufferFormat format,
+      gfx::BufferUsage usage,
       const DestructionCallback& callback,
       std::unique_ptr<base::SharedMemory> shared_memory,
       size_t offset,
diff --git a/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory_unittest.cc b/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory_unittest.cc
index 257b64e..268d5f8 100644
--- a/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory_unittest.cc
+++ b/gpu/ipc/client/gpu_memory_buffer_impl_shared_memory_unittest.cc
@@ -22,12 +22,13 @@
   const gfx::GpuMemoryBufferId kBufferId(1);
 
   gfx::Size buffer_size(8, 8);
+  gfx::BufferUsage usage = gfx::BufferUsage::GPU_READ;
 
   for (auto format : gfx::GetBufferFormatsForTesting()) {
     bool destroyed = false;
     std::unique_ptr<GpuMemoryBufferImplSharedMemory> buffer(
         GpuMemoryBufferImplSharedMemory::Create(
-            kBufferId, buffer_size, format,
+            kBufferId, buffer_size, format, usage,
             base::Bind(&BufferDestroyed, base::Unretained(&destroyed))));
     ASSERT_TRUE(buffer);
     EXPECT_EQ(buffer->GetFormat(), format);
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 0135d9c..d600ff1 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -132,6 +132,8 @@
     "fake_jpeg_decode_accelerator.h",
     "fake_video_decode_accelerator.cc",
     "fake_video_decode_accelerator.h",
+    "format_utils.cc",
+    "format_utils.h",
     "gles2_decoder_helper.cc",
     "gles2_decoder_helper.h",
     "gpu_video_accelerator_util.cc",
diff --git a/media/gpu/format_utils.cc b/media/gpu/format_utils.cc
new file mode 100644
index 0000000..70745366b
--- /dev/null
+++ b/media/gpu/format_utils.cc
@@ -0,0 +1,51 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/format_utils.h"
+#include "base/logging.h"
+
+namespace media {
+
+VideoPixelFormat GfxBufferFormatToVideoPixelFormat(gfx::BufferFormat format) {
+  switch (format) {
+    case gfx::BufferFormat::BGRX_8888:
+      return PIXEL_FORMAT_XRGB;
+
+    case gfx::BufferFormat::BGRA_8888:
+      return PIXEL_FORMAT_ARGB;
+
+    case gfx::BufferFormat::YVU_420:
+      return PIXEL_FORMAT_YV12;
+
+    case gfx::BufferFormat::YUV_420_BIPLANAR:
+      return PIXEL_FORMAT_NV12;
+
+    default:
+      LOG(FATAL) << "Add more cases as needed";
+      return PIXEL_FORMAT_UNKNOWN;
+  }
+}
+
+gfx::BufferFormat VideoPixelFormatToGfxBufferFormat(
+    VideoPixelFormat pixel_format) {
+  switch (pixel_format) {
+    case PIXEL_FORMAT_ARGB:
+      return gfx::BufferFormat::BGRA_8888;
+
+    case PIXEL_FORMAT_XRGB:
+      return gfx::BufferFormat::BGRX_8888;
+
+    case PIXEL_FORMAT_YV12:
+      return gfx::BufferFormat::YVU_420;
+
+    case PIXEL_FORMAT_NV12:
+      return gfx::BufferFormat::YUV_420_BIPLANAR;
+
+    default:
+      LOG(FATAL) << "Add more cases as needed";
+      return gfx::BufferFormat::BGRX_8888;
+  }
+}
+
+}  // namespace media
diff --git a/media/gpu/format_utils.h b/media/gpu/format_utils.h
new file mode 100644
index 0000000..7044bba
--- /dev/null
+++ b/media/gpu/format_utils.h
@@ -0,0 +1,22 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_FORMAT_UTILS_H_
+#define MEDIA_GPU_FORMAT_UTILS_H_
+
+#include "media/base/video_types.h"
+#include "media/gpu/media_gpu_export.h"
+#include "ui/gfx/buffer_types.h"
+
+namespace media {
+
+MEDIA_GPU_EXPORT VideoPixelFormat
+GfxBufferFormatToVideoPixelFormat(gfx::BufferFormat format);
+
+MEDIA_GPU_EXPORT gfx::BufferFormat VideoPixelFormatToGfxBufferFormat(
+    VideoPixelFormat pixel_format);
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_FORMAT_UTILS_H_
diff --git a/media/gpu/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi_video_decode_accelerator.cc
index 10bcbde..608959e 100644
--- a/media/gpu/vaapi_video_decode_accelerator.cc
+++ b/media/gpu/vaapi_video_decode_accelerator.cc
@@ -23,6 +23,7 @@
 #include "gpu/ipc/service/gpu_channel.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/gpu/accelerated_video_decoder.h"
+#include "media/gpu/format_utils.h"
 #include "media/gpu/h264_decoder.h"
 #include "media/gpu/vaapi_picture.h"
 #include "media/gpu/vp8_decoder.h"
@@ -706,24 +707,6 @@
   TryFinishSurfaceSetChange();
 }
 
-static VideoPixelFormat BufferFormatToVideoPixelFormat(
-    gfx::BufferFormat format) {
-  switch (format) {
-    case gfx::BufferFormat::BGRX_8888:
-      return PIXEL_FORMAT_XRGB;
-
-    case gfx::BufferFormat::BGRA_8888:
-      return PIXEL_FORMAT_ARGB;
-
-    case gfx::BufferFormat::YVU_420:
-      return PIXEL_FORMAT_YV12;
-
-    default:
-      LOG(FATAL) << "Add more cases as needed";
-      return PIXEL_FORMAT_UNKNOWN;
-  }
-}
-
 void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() {
   DCHECK(task_runner_->BelongsToCurrentThread());
 
@@ -763,7 +746,7 @@
   VLOGF(2) << "Requesting " << requested_num_pics_
            << " pictures of size: " << requested_pic_size_.ToString();
 
-  VideoPixelFormat format = BufferFormatToVideoPixelFormat(output_format_);
+  VideoPixelFormat format = GfxBufferFormatToVideoPixelFormat(output_format_);
   task_runner_->PostTask(
       FROM_HERE, base::Bind(&Client::ProvidePictureBuffers, client_,
                             requested_num_pics_, format, 1, requested_pic_size_,
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc
index 4fd451b..8cf966aa 100644
--- a/media/gpu/video_decode_accelerator_unittest.cc
+++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -61,6 +61,7 @@
 #include "media/base/test_data_util.h"
 #include "media/gpu/fake_video_decode_accelerator.h"
 #include "media/gpu/features.h"
+#include "media/gpu/format_utils.h"
 #include "media/gpu/gpu_video_decode_accelerator_factory.h"
 #include "media/gpu/rendering_helper.h"
 #include "media/gpu/video_accelerator_unittest_helpers.h"
@@ -340,23 +341,6 @@
   return base::WrapRefCounted(new TextureRef(texture_id, no_longer_needed_cb));
 }
 
-#if defined(OS_CHROMEOS)
-gfx::BufferFormat VideoPixelFormatToGfxBufferFormat(
-    VideoPixelFormat pixel_format) {
-  switch (pixel_format) {
-    case VideoPixelFormat::PIXEL_FORMAT_ARGB:
-      return gfx::BufferFormat::BGRA_8888;
-    case VideoPixelFormat::PIXEL_FORMAT_XRGB:
-      return gfx::BufferFormat::BGRX_8888;
-    case VideoPixelFormat::PIXEL_FORMAT_NV12:
-      return gfx::BufferFormat::YUV_420_BIPLANAR;
-    default:
-      LOG_ASSERT(false) << "Unknown VideoPixelFormat";
-      return gfx::BufferFormat::BGRX_8888;
-  }
-}
-#endif
-
 // static
 scoped_refptr<TextureRef> TextureRef::CreatePreallocated(
     uint32_t texture_id,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 2be015e4..6490106 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -4128,10 +4128,7 @@
       "gl_unittests",
       "headless_shell",
       "media_unittests",
-      "net_unittests",
-      "skia_unittests",
-      "service_manager_unittests",
-      "ui_base_unittests"
+      "net_unittests"
     ],
     "gtest_tests": [
       {
@@ -4193,6 +4190,30 @@
           "can_use_on_swarming_builders": true
         },
         "test": "mojo_system_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.service_manager_unittests.filter"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "service_manager_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.ui_base_unittests.filter"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ui_base_unittests"
       }
     ]
   },
diff --git a/testing/buildbot/filters/fuchsia.mojo_system_unittests.filter b/testing/buildbot/filters/fuchsia.mojo_system_unittests.filter
index bfce535..9379ac5 100644
--- a/testing/buildbot/filters/fuchsia.mojo_system_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.mojo_system_unittests.filter
@@ -7,3 +7,7 @@
 # These tests require support for named channels. See crbug.com/754038.
 -MultiprocessMessagePipeTestWithPeerSupport*/2
 -MultiprocessMessagePipeTestWithPeerSupport*/3
+
+# crbug.com/780317 - These timeout under QEMU s/w emulation of ARM64.
+-MultiprocessMessagePipeTestWithPeerSupport.PingPongPipe*
+-MessagePipeTest.SharedBufferHandlePingPong
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls b/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls
index c4d909f..d496e27 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls
@@ -30,11 +30,9 @@
 crbug.com/417782 compositing/squashing/squash-above-fixed-3.html [ Failure ]
 crbug.com/417782 compositing/squashing/squashing-print.html [ Failure ]
 crbug.com/417782 compositing/visibility/visibility-image-layers-dynamic.html [ Failure ]
-crbug.com/417782 css3/filters/nested-filter.html [ Failure ]
 crbug.com/417782 editing/input/scroll-viewport-page-up-down.html [ Failure ]
 crbug.com/417782 external/wpt/css/css-position-3/position-sticky-root-scroller.html [ Failure ]
 crbug.com/417782 [ Mac ] fast/body-propagation/background-image/007.html [ Failure ]
-crbug.com/417782 fast/css/resize-corner-tracking-transformed.html [ Failure ]
 crbug.com/417782 fast/css/sticky/replaced-sticky.html [ Failure ]
 crbug.com/417782 fast/css/sticky/sticky-style-change.html [ Failure ]
 crbug.com/417782 fast/css/zoom-body-scroll.html [ Failure ]
@@ -61,7 +59,6 @@
 crbug.com/417782 [ Linux Win ] fast/forms/select-popup/popup-menu-appearance-transform.html [ Failure ]
 crbug.com/417782 fast/frames/iframe-scaling-with-scroll.html [ Failure ]
 crbug.com/417782 fast/layers/scroll-rect-to-visible.html [ Failure ]
-crbug.com/417782 fast/layers/scroll-with-transform-layer.html [ Failure ]
 crbug.com/417782 fast/layout/scroll-anchoring/history-restore-anchors.html [ Failure ]
 crbug.com/417782 fast/loader/scroll-position-restoration-without-premature-clamping.html [ Failure ]
 crbug.com/417782 fast/loader/scroll-position-restored-on-back-at-load-event.html [ Failure ]
@@ -105,7 +102,6 @@
 crbug.com/417782 paint/invalidation/overflow-scroll-body-appear.html [ Failure ]
 crbug.com/417782 paint/invalidation/positioned-document-element.html [ Crash ]
 crbug.com/417782 paint/invalidation/resize-iframe-text.html [ Failure ]
-crbug.com/417782 paint/invalidation/scroll-in-transformed-layer.html [ Failure ]
 crbug.com/417782 paint/invalidation/subpixel-shadow-included-in-invalidation.html [ Failure ]
 crbug.com/417782 paint/invalidation/svg/absolute-sized-document-no-scrollbars.svg [ Failure ]
 crbug.com/417782 paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-1.html [ Failure ]
@@ -140,7 +136,6 @@
 crbug.com/417782 svg/W3C-SVG-1.1/masking-path-04-b.svg [ Failure ]
 crbug.com/417782 svg/custom/mask-with-default-value.svg [ Failure ]
 crbug.com/417782 [ Mac ] svg/custom/masking-clipping-hidpi.svg [ Failure ]
-crbug.com/417782 transforms/overflow-with-transform.html [ Failure ]
 crbug.com/417782 transforms/selection-bounds-in-transformed-view.html [ Failure ]
 crbug.com/417782 [ Linux ] virtual/android/fast/rootscroller/browser-controls-background-iframe.html [ Failure ]
 crbug.com/417782 [ Linux ] virtual/android/fast/rootscroller/browser-controls-gradient-background-iframe-scroller.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/nested-overflow-clip-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/nested-overflow-clip-expected.html
new file mode 100644
index 0000000..e891fac7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/nested-overflow-clip-expected.html
@@ -0,0 +1,2 @@
+<!doctype html>
+This test passes if there is no red.
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/nested-overflow-clip.html b/third_party/WebKit/LayoutTests/compositing/overflow/nested-overflow-clip.html
new file mode 100644
index 0000000..4990c85
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/nested-overflow-clip.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<style>
+  ::-webkit-scrollbar {
+    display: none;
+  }
+</style>
+This test passes if there is no red.
+<div id="scroller" style="position: absolute; width: 400px; height: 400px; overflow-y: scroll; will-change: transform;">
+  <div id="transformed" style="width: 100px; height: 100px; top: 100px; left: 50px; position: absolute; overflow: hidden; transform: rotate(45deg);">
+    <!-- This rect should be entirely clipped out. -->
+    <div style="width: 100px; height: 100px; top: 100px; left: 50px; position: absolute; background: red;"></div>
+  </div>
+  <div id="forcescroll" style="height: 1000px;"></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/copy-null-characters-expected.txt b/third_party/WebKit/LayoutTests/editing/pasteboard/copy-null-characters-expected.txt
deleted file mode 100644
index 04b3208..0000000
--- a/third_party/WebKit/LayoutTests/editing/pasteboard/copy-null-characters-expected.txt
+++ /dev/null
@@ -1,54 +0,0 @@
-If there are NULL characters in text nodes, they should not be copied to the clipboard.  This test requires DumpRenderTree.
-| "
-"
-| <div>
-|   id="outerSource"
-|   <p>
-|     <b>
-|       "bold"
-|   "
-  "
-|   "
-  "
-|   <p>
-|     style="color: green"
-|     "green"
-|   "
-"
-| "
-"
-| <div>
-|   contenteditable="true"
-|   id="destination-rich-text"
-|   <p>
-|     <b>
-|       "bold"
-|   <div>
-|     id="source"
-|     "Copy paste me"
-|   <span>
-|     style="color: green;"
-|     "green"
-|   "Copy paste me"
-| "
-"
-| <textarea>
-|   id="destination-plain-text"
-|   this.value="Copy paste mebold
-
-Copy paste me
-green"
-|   <shadow:root>
-|     <div>
-|       "Copy paste mebold
-
-Copy paste me
-green"
-| "
-"
-| <div>
-|   id="results"
-|   "PASSED"
-| "
-
-"
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/copy-null-characters.html b/third_party/WebKit/LayoutTests/editing/pasteboard/copy-null-characters.html
index 189c8c95..1ebad7eef 100644
--- a/third_party/WebKit/LayoutTests/editing/pasteboard/copy-null-characters.html
+++ b/third_party/WebKit/LayoutTests/editing/pasteboard/copy-null-characters.html
@@ -1,89 +1,14 @@
-<head>
-<script src="../../resources/dump-as-markup.js"></script>
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
 <script>
-Markup.description('If there are NULL characters in text nodes, they should not be copied to the clipboard.  This test requires DumpRenderTree.');
-Markup.noAutoDump();
-
-function runTest()
-{
-    var sel = window.getSelection();
-
-    var source = document.getElementById("source");
-    var textWithNull = "Copy\0 paste me";
-    source.textContent = textWithNull;
-
-    var results = document.getElementById("results");
-    // Make sure innerHTML still has the NULL.
-    if (source.innerHTML != textWithNull) {
-        results.innerText = "source.innerHTML has the wrong value (expected " +
-            JSON.stringify(textWithNull) + " but found " +
-            JSON.stringify(source.innerHTML) + ").";
-        Markup.dump(document.body);
-        Markup.notifyDone();
-        return;
-    }
-
-    sel.collapse(source, 0);
-    document.execCommand("SelectAll");
-    document.execCommand("Copy");
-
-    var destinationRichText = document.getElementById("destination-rich-text");
-    sel.collapse(destinationRichText, 0);
-    document.execCommand("Paste");
-
-    var destinationPlainText = document.getElementById("destination-plain-text");
-    destinationPlainText.focus();
-    document.execCommand("Paste");
-
-    var expectedPlainTextValue = "Copy paste me";
-    if (expectedPlainTextValue != destinationPlainText.value) {
-        results.innerText = "Plain text field has the wrong value (expected " +
-            JSON.stringify(expectedPlainTextValue) + " but found " +
-            JSON.stringify(destinationPlainText.value) + ").";
-        Markup.dump(document.body);
-        Markup.notifyDone();
-        return;
-    }
-
-    // Run the same test but include some richly formatted text.
-    var outerSource = document.getElementById("outerSource");
-    sel.setBaseAndExtent(outerSource, 0, destinationRichText, 0);
-    document.execCommand("Copy");
-
-    // Remove the source text so we don't end up with a null character in the
-    // expected output file.
-    source.parentNode.removeChild(source);
-
-    sel.collapse(destinationRichText, 0);
-    document.execCommand("Paste");
-
-    destinationPlainText.focus();
-    document.execCommand("Paste");
-
-    var expectedPlainTextValue2 = "Copy paste mebold\n\nCopy paste me\ngreen";
-    if (expectedPlainTextValue2 != destinationPlainText.value) {
-        results.innerText = "Plain text field has the wrong value (expected " +
-            JSON.stringify(expectedPlainTextValue2) + " but found " +
-            JSON.stringify(destinationPlainText.value) + ").";
-        Markup.dump(document.body);
-        Markup.notifyDone();
-        return;
-    }
-
-    results.innerText = "PASSED";
-
-    Markup.dump(document.body);
-    Markup.notifyDone();
-}
+selection_test(
+  '<div contenteditable>|</div>',
+  selection => {
+    selection.setClipboardData('foo\0bar');
+    selection.document.execCommand("paste");
+  },
+  '<div contenteditable>foobar|</div>',
+  'NULL characters should not be copied');
 </script>
-</head>
-
-<body onload="runTest()">
-<div id="outerSource"><p><b>bold</b></p>
-  <div id="source" contentEditable="true"></div>
-  <p style="color: green">green</p>
-</div>
-<div id="destination-rich-text" contentEditable="true"></div>
-<textarea id="destination-plain-text"></textarea>
-<div id="results">FAILED</div>
-</body>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/stylevalue-objects/parse-parseAll-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/stylevalue-objects/parse-parseAll-expected.txt
index 072db6d..f5a4c69 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/stylevalue-objects/parse-parseAll-expected.txt
+++ b/third_party/WebKit/LayoutTests/typedcssom/stylevalue-objects/parse-parseAll-expected.txt
@@ -3,17 +3,17 @@
 PASS Calling CSSStyleValue.parse with an unsupported CSS property throws a TypeError
 PASS Calling CSSStyleValue.parse with a CSS shorthand throws a TypeError
 PASS Calling CSSStyleValue.parse with an invalid cssText for the given property throws a SyntaxError
-PASS Calling CSSStyleValue.parse with a valid cssText for the given property returns a valid CSSStyleValue
-PASS Calling CSSStyleValue.parse with a mixed case cssText returns a valid CSSStyleValue
-FAIL Calling CSSStyleValue.parse with a custom property returns a valid CSSStyleValue Failed to execute 'parse' on 'CSSStyleValue': Invalid property name
 PASS Calling CSSStyleValue.parseAll with an empty string throws a TypeError
 PASS Calling CSSStyleValue.parseAll with an unsupported CSS property throws a TypeError
 PASS Calling CSSStyleValue.parseAll with a CSS shorthand throws a TypeError
-FAIL Calling CSSStyleValue.parseAll with an invalid cssText for the given property throws a SyntaxError assert_throws: function "() => CSSStyleValue[parseMethod]('width', '10deg')" threw object "TypeError: CSSStyleValue[parseMethod] is not a function" ("TypeError") expected object "SyntaxError" ("SyntaxError")
-FAIL Calling CSSStyleValue.parseAll with a valid cssText for the given property returns a valid CSSStyleValue CSSStyleValue[parseMethod] is not a function
-FAIL Calling CSSStyleValue.parseAll with a mixed case cssText returns a valid CSSStyleValue CSSStyleValue[parseMethod] is not a function
-FAIL Calling CSSStyleValue.parseAll with a custom property returns a valid CSSStyleValue CSSStyleValue[parseMethod] is not a function
+PASS Calling CSSStyleValue.parseAll with an invalid cssText for the given property throws a SyntaxError
+PASS Calling CSSStyleValue.parse with a valid cssText for the given property returns a valid CSSStyleValue
+PASS Calling CSSStyleValue.parse with a mixed case cssText returns a valid CSSStyleValue
+FAIL Calling CSSStyleValue.parse with a custom property returns a valid CSSStyleValue Failed to execute 'parse' on 'CSSStyleValue': Invalid property name
+PASS Calling CSSStyleValue.parseAll with a valid cssText for the given property returns a single-element list containing a valid CSSStyleValue
+PASS Calling CSSStyleValue.parseAll with a mixed case cssText returns a single-element list containing a valid CSSStyleValue
+FAIL Calling CSSStyleValue.parseAll with a custom property returns a single-element list containing a valid CSSStyleValue Failed to execute 'parseAll' on 'CSSStyleValue': Invalid property name
 PASS Calling CSSStyleValue.parse with a list-value property returns first list value
-FAIL Calling CSSStyleValue.parseAll with a list-value property returns a sequence of values CSSStyleValue.parseAll is not a function
+PASS Calling CSSStyleValue.parseAll with a list-value property returns a sequence of values
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/typedcssom/stylevalue-objects/parse-parseAll.html b/third_party/WebKit/LayoutTests/typedcssom/stylevalue-objects/parse-parseAll.html
index 97c12c08..1a9530e 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/stylevalue-objects/parse-parseAll.html
+++ b/third_party/WebKit/LayoutTests/typedcssom/stylevalue-objects/parse-parseAll.html
@@ -25,21 +25,26 @@
   test(() => {
     assert_throws(new SyntaxError(), () => CSSStyleValue[parseMethod]('width', '10deg'));
   }, 'Calling CSSStyleValue.' + parseMethod + ' with an invalid cssText for the given property throws a SyntaxError');
+}
 
-  test(() => {
-    const result = CSSStyleValue[parseMethod]('width', '10px');
-    assert_style_value_equals(result, CSS.px(10));
-  }, 'Calling CSSStyleValue.' + parseMethod + ' with a valid cssText for the given property returns a valid CSSStyleValue');
+const gValidNonListTests = [
+  { property: 'width', value: '10px', expected: CSS.px(10), desc: 'a valid cssText for the given property' },
+  { property: 'wIdTh', value: '10px', expected: CSS.px(10), desc: 'a mixed case cssText' },
+  { property: '--foo', value: '10%', expected: CSS.percent(10), desc: 'a custom property' },
+];
 
+for (const {property, value, expected, desc} of gValidNonListTests) {
   test(() => {
-    const result = CSSStyleValue[parseMethod]('wIdTh', '10px');
-    assert_style_value_equals(result, CSS.px(10));
-  }, 'Calling CSSStyleValue.' + parseMethod + ' with a mixed case cssText returns a valid CSSStyleValue');
+    const result = CSSStyleValue.parse(property, value);
+    assert_style_value_equals(result, expected);
+  }, 'Calling CSSStyleValue.parse with ' + desc + ' returns a valid CSSStyleValue');
+}
 
+for (const {property, value, expected, desc} of gValidNonListTests) {
   test(() => {
-    const result = CSSStyleValue[parseMethod]('--foo', '10%');
-    assert_style_value_equals(result, CSS.percent(10));
-  }, 'Calling CSSStyleValue.' + parseMethod + ' with a custom property returns a valid CSSStyleValue');
+    const result = CSSStyleValue.parseAll(property, value);
+    assert_style_value_array_equals(result, [expected]);
+  }, 'Calling CSSStyleValue.parseAll with ' + desc + ' returns a single-element list containing a valid CSSStyleValue');
 }
 
 test(() => {
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 009196c..b88f7dc 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -794,6 +794,7 @@
     method removeRule
 interface CSSStyleValue
     static method parse
+    static method parseAll
     attribute @@toStringTag
     method constructor
     method toString
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index 57ee773..1e44dc4 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1847,6 +1847,7 @@
       "//third_party/WebKit/Source/platform:blink_fuzzer_test_support",
       "//third_party/libprotobuf-mutator",
     ]
+    dict = "//third_party/WebKit/Source/core/css/css.dict"
   }
 
   proto_library("css_proto") {
diff --git a/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.cpp b/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.cpp
index c21de49..b878230 100644
--- a/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.cpp
+++ b/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.cpp
@@ -13,25 +13,23 @@
 
 namespace blink {
 
-ScriptValue CSSStyleValue::parse(ScriptState* script_state,
-                                 const String& property_name,
-                                 const String& value,
-                                 ExceptionState& exception_state) {
-  if (property_name.IsEmpty()) {
-    exception_state.ThrowTypeError("Property name cannot be empty");
-    return ScriptValue::CreateNull(script_state);
-  }
+namespace {
 
-  CSSPropertyID property_id = cssPropertyID(property_name);
-  // TODO(timloh): Handle custom properties
+CSSStyleValueVector ParseCSSStyleValue(const String& property_name,
+                                       const String& value,
+                                       ExceptionState& exception_state) {
+  const CSSPropertyID property_id = cssPropertyID(property_name);
+
+  // TODO(775804): Handle custom properties
   if (property_id == CSSPropertyInvalid || property_id == CSSPropertyVariable) {
     exception_state.ThrowTypeError("Invalid property name");
-    return ScriptValue::CreateNull(script_state);
+    return CSSStyleValueVector();
   }
+
   if (isShorthandProperty(property_id)) {
     exception_state.ThrowTypeError(
         "Parsing shorthand properties is not supported");
-    return ScriptValue::CreateNull(script_state);
+    return CSSStyleValueVector();
   }
 
   const CSSValue* css_value =
@@ -41,17 +39,38 @@
         kSyntaxError, "The value provided ('" + value +
                           "') could not be parsed as a '" + property_name +
                           "'.");
-    return ScriptValue::CreateNull(script_state);
+    return CSSStyleValueVector();
   }
 
   CSSStyleValueVector style_value_vector =
       StyleValueFactory::CssValueToStyleValueVector(property_id, *css_value);
   DCHECK(!style_value_vector.IsEmpty());
+  return style_value_vector;
+}
 
-  v8::Local<v8::Value> wrapped_value =
-      ToV8(style_value_vector[0], script_state->GetContext()->Global(),
-           script_state->GetIsolate());
-  return ScriptValue(script_state, wrapped_value);
+}  // namespace
+
+CSSStyleValue* CSSStyleValue::parse(const String& property_name,
+                                    const String& value,
+                                    ExceptionState& exception_state) {
+  CSSStyleValueVector style_value_vector =
+      ParseCSSStyleValue(property_name, value, exception_state);
+  if (style_value_vector.IsEmpty())
+    return nullptr;
+
+  return style_value_vector[0];
+}
+
+Nullable<CSSStyleValueVector> CSSStyleValue::parseAll(
+    const String& property_name,
+    const String& value,
+    ExceptionState& exception_state) {
+  CSSStyleValueVector style_value_vector =
+      ParseCSSStyleValue(property_name, value, exception_state);
+  if (style_value_vector.IsEmpty())
+    return nullptr;
+
+  return style_value_vector;
 }
 
 String CSSStyleValue::StyleValueTypeToString(StyleValueType type) {
diff --git a/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.h b/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.h
index 422dd9fa..7770b19 100644
--- a/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.h
+++ b/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.h
@@ -5,6 +5,7 @@
 #ifndef CSSStyleValue_h
 #define CSSStyleValue_h
 
+#include "bindings/core/v8/Nullable.h"
 #include "core/CSSPropertyNames.h"
 #include "core/CoreExport.h"
 #include "core/css/CSSValue.h"
@@ -14,8 +15,9 @@
 namespace blink {
 
 class ExceptionState;
-class ScriptState;
-class ScriptValue;
+
+class CSSStyleValue;
+using CSSStyleValueVector = HeapVector<Member<CSSStyleValue>>;
 
 // The base class for all CSS values returned by the Typed OM.
 // See CSSStyleValue.idl for additional documentation about this class.
@@ -48,10 +50,12 @@
     kInvalidType,
   };
 
-  static ScriptValue parse(ScriptState*,
-                           const String& property_name,
-                           const String& value,
-                           ExceptionState&);
+  static CSSStyleValue* parse(const String& property_name,
+                              const String& value,
+                              ExceptionState&);
+  static Nullable<CSSStyleValueVector> parseAll(const String& property_name,
+                                                const String& value,
+                                                ExceptionState&);
 
   virtual ~CSSStyleValue() {}
 
@@ -75,8 +79,6 @@
   CSSStyleValue() {}
 };
 
-typedef HeapVector<Member<CSSStyleValue>> CSSStyleValueVector;
-
 }  // namespace blink
 
 #endif
diff --git a/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.idl b/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.idl
index cf09319..6eabaa9 100644
--- a/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.idl
+++ b/third_party/WebKit/Source/core/css/cssom/CSSStyleValue.idl
@@ -10,7 +10,7 @@
   Exposed(Window CSSTypedOM, PaintWorklet CSSPaintAPI)
 ] interface CSSStyleValue {
   stringifier;
-  // TODO(meade): Should be (CSSStyleValue or sequence<CSSStyleValue>)? instead of object?. Fix when the code generator supports this.
   // Putting Exposed=Window in the next line makes |parse| not exposed to PaintWorklet.
-  [RaisesException, CallWith=ScriptState, Exposed=Window] static object? parse(DOMString property, DOMString cssText);
+  [RaisesException, Exposed=Window] static CSSStyleValue? parse(DOMString property, DOMString cssText);
+  [RaisesException, Exposed=Window] static sequence<CSSStyleValue>? parseAll(DOMString property, DOMString cssText);
 };
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
index 2a0065a..f12bc688 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
@@ -853,9 +853,12 @@
   transformed_painting_info.ancestor_has_clip_path_clipping =
       painting_info.ancestor_has_clip_path_clipping;
 
-  // Remove skip root background flag when we're painting with a new root.
-  if (&paint_layer_ != painting_info.root_layer)
+  if (&paint_layer_ != painting_info.root_layer) {
+    // Remove skip root background flag when we're painting with a new root.
     paint_flags &= ~kPaintLayerPaintingSkipRootBackground;
+    // When painting a new root we are no longer painting overflow contents.
+    paint_flags &= ~kPaintLayerPaintingOverflowContents;
+  }
 
   return PaintLayerContentsCompositingAllPhases(
       context, transformed_painting_info, paint_flags, kForceSingleFragment);
diff --git a/third_party/WebKit/Source/core/workers/WorkerThread.cpp b/third_party/WebKit/Source/core/workers/WorkerThread.cpp
index a6f70fda..ee79a78 100644
--- a/third_party/WebKit/Source/core/workers/WorkerThread.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerThread.cpp
@@ -58,7 +58,6 @@
 #include "platform/wtf/PtrUtil.h"
 #include "platform/wtf/Threading.h"
 #include "platform/wtf/text/WTFString.h"
-#include "public/platform/InterfaceProvider.h"
 #include "public/platform/Platform.h"
 
 namespace blink {
@@ -70,12 +69,6 @@
 // TODO(nhiroki): Adjust the delay based on UMA.
 constexpr TimeDelta kForcibleTerminationDelay = TimeDelta::FromSeconds(2);
 
-void ForwardInterfaceRequest(const std::string& name,
-                             mojo::ScopedMessagePipeHandle handle) {
-  Platform::Current()->GetInterfaceProvider()->GetInterface(name.c_str(),
-                                                            std::move(handle));
-}
-
 }  // namespace
 
 static Mutex& ThreadSetMutex() {
@@ -307,13 +300,6 @@
   return exit_code_;
 }
 
-service_manager::InterfaceProvider& WorkerThread::GetInterfaceProvider() {
-  // TODO(https://crbug.com/734210): Instead of forwarding to the process-wide
-  // interface provider a worker-specific interface provider pipe should be
-  // passed in as part of the WorkerThreadStartupData.
-  return interface_provider_;
-}
-
 WorkerThread::WorkerThread(ThreadableLoadingContext* loading_context,
                            WorkerReportingProxy& worker_reporting_proxy)
     : time_origin_(MonotonicallyIncreasingTime()),
@@ -329,8 +315,6 @@
   DCHECK(IsMainThread());
   MutexLocker lock(ThreadSetMutex());
   WorkerThreads().insert(this);
-  interface_provider_.Forward(
-      ConvertToBaseCallback(WTF::Bind(&ForwardInterfaceRequest)));
 }
 
 void WorkerThread::ScheduleToTerminateScriptExecution() {
diff --git a/third_party/WebKit/Source/core/workers/WorkerThread.h b/third_party/WebKit/Source/core/workers/WorkerThread.h
index 3c73e845..5c7ccd8 100644
--- a/third_party/WebKit/Source/core/workers/WorkerThread.h
+++ b/third_party/WebKit/Source/core/workers/WorkerThread.h
@@ -45,7 +45,6 @@
 #include "platform/wtf/Optional.h"
 #include "platform/wtf/RefPtr.h"
 #include "public/platform/WebThread.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
 #include "v8/include/v8.h"
 
 namespace blink {
@@ -171,8 +170,6 @@
     return global_scope_scheduler_.get();
   }
 
-  service_manager::InterfaceProvider& GetInterfaceProvider();
-
   // For ServiceWorkerScriptStreaming. Returns nullptr otherwise.
   virtual InstalledScriptsManager* GetInstalledScriptsManager() {
     return nullptr;
@@ -284,10 +281,6 @@
 
   CrossThreadPersistent<ParentFrameTaskRunners> parent_frame_task_runners_;
 
-  // Mojo interface provider serving interface requests scoped to this worker
-  // context.
-  service_manager::InterfaceProvider interface_provider_;
-
   // Tasks managed by this scheduler are canceled when the global scope is
   // closed.
   std::unique_ptr<scheduler::WorkerGlobalScopeScheduler>
diff --git a/third_party/WebKit/Source/modules/cookie_store/GlobalCookieStore.cpp b/third_party/WebKit/Source/modules/cookie_store/GlobalCookieStore.cpp
index 2aab272..fa1cd4a 100644
--- a/third_party/WebKit/Source/modules/cookie_store/GlobalCookieStore.cpp
+++ b/third_party/WebKit/Source/modules/cookie_store/GlobalCookieStore.cpp
@@ -12,8 +12,8 @@
 #include "modules/cookie_store/CookieStore.h"
 #include "platform/Supplementable.h"
 #include "platform/heap/Handle.h"
-#include "public/platform/InterfaceProvider.h"
 #include "services/network/public/interfaces/restricted_cookie_manager.mojom-blink.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/modules/payments/PaymentManager.cpp b/third_party/WebKit/Source/modules/payments/PaymentManager.cpp
index 2ebf80b5..0eaeb0f 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentManager.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentManager.cpp
@@ -6,15 +6,10 @@
 
 #include "bindings/core/v8/ScriptPromise.h"
 #include "core/dom/DOMException.h"
-#include "core/dom/Document.h"
-#include "core/frame/LocalFrame.h"
-#include "core/workers/WorkerGlobalScope.h"
-#include "core/workers/WorkerThread.h"
 #include "modules/payments/PaymentInstruments.h"
 #include "modules/serviceworkers/ServiceWorkerRegistration.h"
 #include "platform/bindings/ScriptState.h"
-#include "platform/mojo/MojoHelper.h"
-#include "public/platform/Platform.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 
 namespace blink {
 
@@ -49,14 +44,10 @@
   DCHECK(registration);
 
   auto request = mojo::MakeRequest(&manager_);
-  ExecutionContext* context = registration->GetExecutionContext();
-  if (context && context->IsDocument()) {
-    LocalFrame* frame = ToDocument(context)->GetFrame();
-    if (frame)
-      frame->GetInterfaceProvider().GetInterface(std::move(request));
-  } else if (context && context->IsWorkerGlobalScope()) {
-    WorkerThread* thread = ToWorkerGlobalScope(context)->GetThread();
-    thread->GetInterfaceProvider().GetInterface(std::move(request));
+  if (ExecutionContext* context = registration->GetExecutionContext()) {
+    if (auto* interface_provider = context->GetInterfaceProvider()) {
+      interface_provider->GetInterface(std::move(request));
+    }
   }
 
   manager_.set_connection_error_handler(ConvertToBaseCallback(WTF::Bind(
diff --git a/third_party/WebKit/Source/modules/permissions/PermissionUtils.cpp b/third_party/WebKit/Source/modules/permissions/PermissionUtils.cpp
index b0c7094..bf61ed7 100644
--- a/third_party/WebKit/Source/modules/permissions/PermissionUtils.cpp
+++ b/third_party/WebKit/Source/modules/permissions/PermissionUtils.cpp
@@ -21,21 +21,11 @@
 using mojom::blink::PermissionDescriptorPtr;
 using mojom::blink::PermissionName;
 
-bool ConnectToPermissionService(
+void ConnectToPermissionService(
     ExecutionContext* execution_context,
     mojom::blink::PermissionServiceRequest request) {
-  if (execution_context->IsWorkerGlobalScope()) {
-    WorkerThread* thread = ToWorkerGlobalScope(execution_context)->GetThread();
-    thread->GetInterfaceProvider().GetInterface(std::move(request));
-    return true;
-  }
-
-  LocalFrame* frame = ToDocument(execution_context)->GetFrame();
-  if (!frame)
-    return false;
-
-  frame->GetInterfaceProvider().GetInterface(std::move(request));
-  return true;
+  if (auto* interface_provider = execution_context->GetInterfaceProvider())
+    interface_provider->GetInterface(std::move(request));
 }
 
 PermissionDescriptorPtr CreatePermissionDescriptor(PermissionName name) {
diff --git a/third_party/WebKit/Source/modules/permissions/PermissionUtils.h b/third_party/WebKit/Source/modules/permissions/PermissionUtils.h
index 50379cd8..ec76be7 100644
--- a/third_party/WebKit/Source/modules/permissions/PermissionUtils.h
+++ b/third_party/WebKit/Source/modules/permissions/PermissionUtils.h
@@ -11,7 +11,7 @@
 
 class ExecutionContext;
 
-bool ConnectToPermissionService(ExecutionContext*,
+void ConnectToPermissionService(ExecutionContext*,
                                 mojom::blink::PermissionServiceRequest);
 
 mojom::blink::PermissionDescriptorPtr CreatePermissionDescriptor(
diff --git a/third_party/WebKit/Source/modules/permissions/Permissions.cpp b/third_party/WebKit/Source/modules/permissions/Permissions.cpp
index 558c8dcc..d882ea4 100644
--- a/third_party/WebKit/Source/modules/permissions/Permissions.cpp
+++ b/third_party/WebKit/Source/modules/permissions/Permissions.cpp
@@ -157,16 +157,6 @@
   if (exception_state.HadException())
     return exception_state.Reject(script_state);
 
-  // This must be called after `parsePermission` because the website might
-  // be able to run code.
-  PermissionService* service = GetService(ExecutionContext::From(script_state));
-  if (!service)
-    return ScriptPromise::RejectWithDOMException(
-        script_state,
-        DOMException::Create(
-            kInvalidStateError,
-            "In its current state, the global scope can't query permissions."));
-
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
 
@@ -175,12 +165,13 @@
   // permission prompt will be shown even if the returned permission will most
   // likely be "prompt".
   PermissionDescriptorPtr descriptor_copy = descriptor->Clone();
-  service->HasPermission(
-      std::move(descriptor),
-      ExecutionContext::From(script_state)->GetSecurityOrigin(),
-      ConvertToBaseCallback(WTF::Bind(
-          &Permissions::TaskComplete, WrapPersistent(this),
-          WrapPersistent(resolver), WTF::Passed(std::move(descriptor_copy)))));
+  GetService(ExecutionContext::From(script_state))
+      .HasPermission(std::move(descriptor),
+                     ExecutionContext::From(script_state)->GetSecurityOrigin(),
+                     ConvertToBaseCallback(WTF::Bind(
+                         &Permissions::TaskComplete, WrapPersistent(this),
+                         WrapPersistent(resolver),
+                         WTF::Passed(std::move(descriptor_copy)))));
   return promise;
 }
 
@@ -196,27 +187,20 @@
 
   ExecutionContext* context = ExecutionContext::From(script_state);
 
-  // This must be called after `parsePermission` because the website might
-  // be able to run code.
-  PermissionService* service = GetService(context);
-  if (!service)
-    return ScriptPromise::RejectWithDOMException(
-        script_state, DOMException::Create(kInvalidStateError,
-                                           "In its current state, the global "
-                                           "scope can't request permissions."));
-
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
 
   PermissionDescriptorPtr descriptor_copy = descriptor->Clone();
   Document* doc = ToDocumentOrNull(context);
   Frame* frame = doc ? doc->GetFrame() : nullptr;
-  service->RequestPermission(
-      std::move(descriptor), context->GetSecurityOrigin(),
-      Frame::HasTransientUserActivation(frame, true /* checkIfMainThread */),
-      ConvertToBaseCallback(WTF::Bind(
-          &Permissions::TaskComplete, WrapPersistent(this),
-          WrapPersistent(resolver), WTF::Passed(std::move(descriptor_copy)))));
+  GetService(ExecutionContext::From(script_state))
+      .RequestPermission(std::move(descriptor), context->GetSecurityOrigin(),
+                         Frame::HasTransientUserActivation(
+                             frame, true /* checkIfMainThread */),
+                         ConvertToBaseCallback(WTF::Bind(
+                             &Permissions::TaskComplete, WrapPersistent(this),
+                             WrapPersistent(resolver),
+                             WTF::Passed(std::move(descriptor_copy)))));
   return promise;
 }
 
@@ -230,25 +214,18 @@
   if (exception_state.HadException())
     return exception_state.Reject(script_state);
 
-  // This must be called after `parsePermission` because the website might
-  // be able to run code.
-  PermissionService* service = GetService(ExecutionContext::From(script_state));
-  if (!service)
-    return ScriptPromise::RejectWithDOMException(
-        script_state, DOMException::Create(kInvalidStateError,
-                                           "In its current state, the global "
-                                           "scope can't revoke permissions."));
-
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
 
   PermissionDescriptorPtr descriptor_copy = descriptor->Clone();
-  service->RevokePermission(
-      std::move(descriptor),
-      ExecutionContext::From(script_state)->GetSecurityOrigin(),
-      ConvertToBaseCallback(WTF::Bind(
-          &Permissions::TaskComplete, WrapPersistent(this),
-          WrapPersistent(resolver), WTF::Passed(std::move(descriptor_copy)))));
+  GetService(ExecutionContext::From(script_state))
+      .RevokePermission(
+          std::move(descriptor),
+          ExecutionContext::From(script_state)->GetSecurityOrigin(),
+          ConvertToBaseCallback(
+              WTF::Bind(&Permissions::TaskComplete, WrapPersistent(this),
+                        WrapPersistent(resolver),
+                        WTF::Passed(std::move(descriptor_copy)))));
   return promise;
 }
 
@@ -286,15 +263,6 @@
 
   ExecutionContext* context = ExecutionContext::From(script_state);
 
-  // This must be called after `parsePermission` because the website might
-  // be able to run code.
-  PermissionService* service = GetService(context);
-  if (!service)
-    return ScriptPromise::RejectWithDOMException(
-        script_state, DOMException::Create(kInvalidStateError,
-                                           "In its current state, the global "
-                                           "scope can't request permissions."));
-
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
 
@@ -305,24 +273,27 @@
 
   Document* doc = ToDocumentOrNull(context);
   Frame* frame = doc ? doc->GetFrame() : nullptr;
-  service->RequestPermissions(
-      std::move(internal_permissions), context->GetSecurityOrigin(),
-      Frame::HasTransientUserActivation(frame, true /* checkIfMainThread */),
-      ConvertToBaseCallback(
-          WTF::Bind(&Permissions::BatchTaskComplete, WrapPersistent(this),
-                    WrapPersistent(resolver),
-                    WTF::Passed(std::move(internal_permissions_copy)),
-                    WTF::Passed(std::move(caller_index_to_internal_index)))));
+  GetService(ExecutionContext::From(script_state))
+      .RequestPermissions(
+          std::move(internal_permissions), context->GetSecurityOrigin(),
+          Frame::HasTransientUserActivation(frame,
+                                            true /* checkIfMainThread */),
+          ConvertToBaseCallback(WTF::Bind(
+              &Permissions::BatchTaskComplete, WrapPersistent(this),
+              WrapPersistent(resolver),
+              WTF::Passed(std::move(internal_permissions_copy)),
+              WTF::Passed(std::move(caller_index_to_internal_index)))));
   return promise;
 }
 
-PermissionService* Permissions::GetService(
+PermissionService& Permissions::GetService(
     ExecutionContext* execution_context) {
-  if (!service_ && ConnectToPermissionService(execution_context,
-                                              mojo::MakeRequest(&service_)))
+  if (!service_) {
+    ConnectToPermissionService(execution_context, mojo::MakeRequest(&service_));
     service_.set_connection_error_handler(ConvertToBaseCallback(WTF::Bind(
         &Permissions::ServiceConnectionError, WrapWeakPersistent(this))));
-  return service_.get();
+  }
+  return *service_;
 }
 
 void Permissions::ServiceConnectionError() {
diff --git a/third_party/WebKit/Source/modules/permissions/Permissions.h b/third_party/WebKit/Source/modules/permissions/Permissions.h
index cf6022b8..69c966e 100644
--- a/third_party/WebKit/Source/modules/permissions/Permissions.h
+++ b/third_party/WebKit/Source/modules/permissions/Permissions.h
@@ -27,7 +27,7 @@
   ScriptPromise requestAll(ScriptState*, const Vector<Dictionary>&);
 
  private:
-  mojom::blink::PermissionService* GetService(ExecutionContext*);
+  mojom::blink::PermissionService& GetService(ExecutionContext*);
   void ServiceConnectionError();
   void TaskComplete(ScriptPromiseResolver*,
                     mojom::blink::PermissionDescriptorPtr,
diff --git a/third_party/WebKit/Source/modules/quota/StorageManager.cpp b/third_party/WebKit/Source/modules/quota/StorageManager.cpp
index 4ac1ab6..ab56273d 100644
--- a/third_party/WebKit/Source/modules/quota/StorageManager.cpp
+++ b/third_party/WebKit/Source/modules/quota/StorageManager.cpp
@@ -77,23 +77,15 @@
   }
 
   DCHECK(execution_context->IsDocument());
-  PermissionService* permission_service =
-      GetPermissionService(ExecutionContext::From(script_state));
-  if (!permission_service) {
-    resolver->Reject(DOMException::Create(
-        kInvalidStateError,
-        "In its current state, the global scope can't request permissions."));
-    return promise;
-  }
-
   Document* doc = ToDocumentOrNull(execution_context);
-  permission_service->RequestPermission(
-      CreatePermissionDescriptor(PermissionName::DURABLE_STORAGE),
-      ExecutionContext::From(script_state)->GetSecurityOrigin(),
-      Frame::HasTransientUserActivation(doc ? doc->GetFrame() : nullptr),
-      ConvertToBaseCallback(
-          WTF::Bind(&StorageManager::PermissionRequestComplete,
-                    WrapPersistent(this), WrapPersistent(resolver))));
+  GetPermissionService(ExecutionContext::From(script_state))
+      .RequestPermission(
+          CreatePermissionDescriptor(PermissionName::DURABLE_STORAGE),
+          ExecutionContext::From(script_state)->GetSecurityOrigin(),
+          Frame::HasTransientUserActivation(doc ? doc->GetFrame() : nullptr),
+          ConvertToBaseCallback(
+              WTF::Bind(&StorageManager::PermissionRequestComplete,
+                        WrapPersistent(this), WrapPersistent(resolver))));
 
   return promise;
 }
@@ -110,20 +102,13 @@
     return promise;
   }
 
-  PermissionService* permission_service =
-      GetPermissionService(ExecutionContext::From(script_state));
-  if (!permission_service) {
-    resolver->Reject(DOMException::Create(
-        kInvalidStateError,
-        "In its current state, the global scope can't query permissions."));
-    return promise;
-  }
-  permission_service->HasPermission(
-      CreatePermissionDescriptor(PermissionName::DURABLE_STORAGE),
-      ExecutionContext::From(script_state)->GetSecurityOrigin(),
-      ConvertToBaseCallback(
-          WTF::Bind(&StorageManager::PermissionRequestComplete,
-                    WrapPersistent(this), WrapPersistent(resolver))));
+  GetPermissionService(ExecutionContext::From(script_state))
+      .HasPermission(
+          CreatePermissionDescriptor(PermissionName::DURABLE_STORAGE),
+          ExecutionContext::From(script_state)->GetSecurityOrigin(),
+          ConvertToBaseCallback(
+              WTF::Bind(&StorageManager::PermissionRequestComplete,
+                        WrapPersistent(this), WrapPersistent(resolver))));
   return promise;
 }
 
@@ -146,15 +131,16 @@
   return promise;
 }
 
-PermissionService* StorageManager::GetPermissionService(
+PermissionService& StorageManager::GetPermissionService(
     ExecutionContext* execution_context) {
-  if (!permission_service_ &&
-      ConnectToPermissionService(execution_context,
-                                 mojo::MakeRequest(&permission_service_)))
+  if (!permission_service_) {
+    ConnectToPermissionService(execution_context,
+                               mojo::MakeRequest(&permission_service_));
     permission_service_.set_connection_error_handler(ConvertToBaseCallback(
         WTF::Bind(&StorageManager::PermissionServiceConnectionError,
                   WrapWeakPersistent(this))));
-  return permission_service_.get();
+  }
+  return *permission_service_;
 }
 
 void StorageManager::PermissionServiceConnectionError() {
diff --git a/third_party/WebKit/Source/modules/quota/StorageManager.h b/third_party/WebKit/Source/modules/quota/StorageManager.h
index 8c87c5f..db7e71f9 100644
--- a/third_party/WebKit/Source/modules/quota/StorageManager.h
+++ b/third_party/WebKit/Source/modules/quota/StorageManager.h
@@ -26,7 +26,7 @@
   ScriptPromise estimate(ScriptState*);
 
  private:
-  mojom::blink::PermissionService* GetPermissionService(ExecutionContext*);
+  mojom::blink::PermissionService& GetPermissionService(ExecutionContext*);
   void PermissionServiceConnectionError();
   void PermissionRequestComplete(ScriptPromiseResolver*,
                                  mojom::blink::PermissionStatus);
diff --git a/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.cpp b/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.cpp
index 414f22b9..d5bbb93c 100644
--- a/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.cpp
+++ b/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.cpp
@@ -46,8 +46,8 @@
 #include "platform/wtf/PtrUtil.h"
 #include "platform/wtf/text/CString.h"
 #include "platform/wtf/text/WTFString.h"
-#include "public/platform/InterfaceProvider.h"
 #include "public/platform/TaskType.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 
 namespace blink {
 
@@ -414,7 +414,7 @@
   // SSL interstitial decision to WebSocket connections from the worker.
   mojom::blink::WebSocketPtrInfo socket_ptr_info;
   if (!worker_global_scope_->IsDedicatedWorkerGlobalScope()) {
-    worker_thread->GetInterfaceProvider().GetInterface(
+    worker_global_scope_->GetInterfaceProvider()->GetInterface(
         mojo::MakeRequest(&socket_ptr_info));
   }
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
index a72c0453..c09e80bb 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
@@ -90,6 +90,7 @@
     DEBUG_STRING_CASE(PrintedContentPDFURLRect);
     DEBUG_STRING_CASE(Resizer);
     DEBUG_STRING_CASE(SVGClip);
+    DEBUG_STRING_CASE(SVGClipBoundsHack);
     DEBUG_STRING_CASE(SVGFilter);
     DEBUG_STRING_CASE(SVGMask);
     DEBUG_STRING_CASE(ScrollbarBackButtonEnd);
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 257c7d9..5ccabe5 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -352,7 +352,6 @@
       'TSAN Release': 'tsan_disable_nacl_release_bot',
       'UBSan Release': 'ubsan_release_bot',
       'UBSan vptr Release': 'ubsan_vptr_edge_release_bot',
-      'Win ASan Release Coverage': 'asan_edge_fuzzer_v8_heap_release_bot_x86',
       'Win ASan Release Media': 'asan_fuzzer_v8_heap_chrome_with_codecs_release_bot_x86',
       'Win ASan Release': 'asan_fuzzer_v8_heap_release_bot_x86',
       'Win SyzyASAN LKGR': 'syzyasan_no_pch_release_x86',
@@ -1013,10 +1012,6 @@
       'asan', 'edge', 'fuzzer', 'v8_heap', 'release_bot', 'hybrid',
     ],
 
-    'asan_edge_fuzzer_v8_heap_release_bot_x86': [
-      'clang', 'asan', 'edge', 'fuzzer', 'v8_heap', 'release_bot', 'x86',
-    ],
-
     'asan_edge_v8_heap_debug_bot_hybrid': [
       'asan', 'edge', 'v8_heap', 'debug_bot', 'hybrid',
     ],
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a950f86..a722760 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -27793,32 +27793,22 @@
 </enum>
 
 <enum name="NegativeSampleReason">
-  <int value="0"
-      label="Persistent sparse histogram had logged value but no active
-             sample."/>
-  <int value="1"
-      label="Persistent sparse histogram active sample less than logged
-             value."/>
-  <int value="2"
-      label="Persistent sparse histogram added a negative count during
-             iteration."/>
+  <int value="0" label="Histogram had logged value but no active sample."/>
+  <int value="1" label="Histogram active sample less than logged value."/>
+  <int value="2" label="Histogram added a negative count during iteration."/>
   <int value="3"
-      label="Persistent sparse histogram added negative count that caused
-             negative value."/>
+      label="Histogram added negative count that caused negative value."/>
   <int value="4"
-      label="Persistent sparse histogram active sample overflowed and became
-             negative."/>
-  <int value="5"
-      label="Persistent sparse histogram accumulated a negative count."/>
+      label="Histogram active sample overflowed and became negative."/>
+  <int value="5" label="Histogram accumulated a negative count."/>
   <int value="6"
-      label="Persistent sparse histogram accumulated a negative count that
-             caused a negative value."/>
+      label="Histogram accumulated a negative count that caused a negative
+             value."/>
   <int value="7"
-      label="Persistent sparse histogram active sample was negative after
-             accumulation (deprecated)."/>
+      label="Histogram active sample was negative after accumulation
+             (deprecated)."/>
   <int value="8"
-      label="Persistent sparse histogram active sample wrapped 2^31 during
-             accumulation."/>
+      label="Histogram active sample wrapped 2^31 during accumulation."/>
 </enum>
 
 <enum name="NetCacheState">
diff --git a/ui/gfx/native_pixmap.h b/ui/gfx/native_pixmap.h
index 1677dc0..c168001 100644
--- a/ui/gfx/native_pixmap.h
+++ b/ui/gfx/native_pixmap.h
@@ -34,6 +34,14 @@
   virtual gfx::BufferFormat GetBufferFormat() const = 0;
   virtual gfx::Size GetBufferSize() const = 0;
 
+  // Return an id that is guaranteed to be unique and equal for all instances
+  // of this NativePixmap backed by the same buffer, for the duration of its
+  // lifetime. If such id cannot be generated, 0 (an invalid id) is returned.
+  //
+  // TODO(posciak): crbug.com/771863, remove this once a different mechanism
+  // for protected shared memory buffers is implemented.
+  virtual uint32_t GetUniqueId() const = 0;
+
   // Sets the overlay plane to switch to at the next page flip.
   // |widget| specifies the screen to display this overlay plane on.
   // |plane_z_order| specifies the stacking order of the plane relative to the
diff --git a/ui/gfx/native_pixmap_handle.h b/ui/gfx/native_pixmap_handle.h
index 827a7fe..b4210dc 100644
--- a/ui/gfx/native_pixmap_handle.h
+++ b/ui/gfx/native_pixmap_handle.h
@@ -57,7 +57,8 @@
 // Returns an instance of |handle| which can be sent over IPC. This duplicates
 // the file-handles, so that the IPC code take ownership of them, without
 // invalidating |handle|.
-NativePixmapHandle CloneHandleForIPC(const NativePixmapHandle& handle);
+GFX_EXPORT NativePixmapHandle
+CloneHandleForIPC(const NativePixmapHandle& handle);
 #endif
 
 }  // namespace gfx
diff --git a/ui/ozone/platform/cast/surface_factory_cast.cc b/ui/ozone/platform/cast/surface_factory_cast.cc
index 6cc5b50a..234c3f9 100644
--- a/ui/ozone/platform/cast/surface_factory_cast.cc
+++ b/ui/ozone/platform/cast/surface_factory_cast.cc
@@ -63,6 +63,7 @@
     return gfx::BufferFormat::BGRA_8888;
   }
   gfx::Size GetBufferSize() const override { return gfx::Size(); }
+  uint32_t GetUniqueId() const override { return 0; }
 
   bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
                             int plane_z_order,
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.cc b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
index e7b24aa..38ad6cb 100644
--- a/ui/ozone/platform/drm/gpu/gbm_buffer.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
@@ -261,11 +261,11 @@
 
   // Try to use scanout if supported.
   int gbm_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING;
-  bool try_scanout =
-      gbm_device_is_format_supported(gbm->device(), format, gbm_flags);
+  if (!gbm_device_is_format_supported(gbm->device(), format, gbm_flags))
+    gbm_flags &= ~GBM_BO_USE_SCANOUT;
 
   gbm_bo* bo = nullptr;
-  if (try_scanout) {
+  if (gbm_device_is_format_supported(gbm->device(), format, gbm_flags)) {
     struct gbm_import_fd_planar_data fd_data;
     fd_data.width = size.width();
     fd_data.height = size.height();
@@ -287,8 +287,6 @@
       LOG(ERROR) << "nullptr returned from gbm_bo_import";
       return nullptr;
     }
-  } else {
-    gbm_flags &= ~GBM_BO_USE_SCANOUT;
   }
 
   scoped_refptr<GbmBuffer> buffer(new GbmBuffer(gbm, bo, format, gbm_flags, 0,
@@ -365,6 +363,10 @@
   return buffer_->GetSize();
 }
 
+uint32_t GbmPixmap::GetUniqueId() const {
+  return buffer_->GetHandle();
+}
+
 bool GbmPixmap::ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
                                      int plane_z_order,
                                      gfx::OverlayTransform plane_transform,
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.h b/ui/ozone/platform/drm/gpu/gbm_buffer.h
index 6715274..4891b8f 100644
--- a/ui/ozone/platform/drm/gpu/gbm_buffer.h
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer.h
@@ -119,6 +119,7 @@
   uint64_t GetDmaBufModifier(size_t plane) const override;
   gfx::BufferFormat GetBufferFormat() const override;
   gfx::Size GetBufferSize() const override;
+  uint32_t GetUniqueId() const override;
   bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
                             int plane_z_order,
                             gfx::OverlayTransform plane_transform,
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
index cbb3e077..ca8a520 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
@@ -160,7 +160,7 @@
 }
 
 scoped_refptr<gfx::NativePixmap>
-GbmSurfaceFactory::CreateNativePixmapFromHandle(
+GbmSurfaceFactory::CreateNativePixmapFromHandleInternal(
     gfx::AcceleratedWidget widget,
     gfx::Size size,
     gfx::BufferFormat format,
@@ -176,7 +176,6 @@
   }
 
   std::vector<gfx::NativePixmapPlane> planes;
-
   for (const auto& plane : handle.planes) {
     planes.push_back(plane);
   }
@@ -185,7 +184,44 @@
       widget, size, format, std::move(scoped_fds), planes);
   if (!buffer)
     return nullptr;
+
   return base::MakeRefCounted<GbmPixmap>(this, buffer);
 }
 
+scoped_refptr<gfx::NativePixmap>
+GbmSurfaceFactory::CreateNativePixmapFromHandle(
+    gfx::AcceleratedWidget widget,
+    gfx::Size size,
+    gfx::BufferFormat format,
+    const gfx::NativePixmapHandle& handle) {
+  // Query the external service (if available), whether it recognizes this
+  // NativePixmapHandle, and whether it can provide a corresponding NativePixmap
+  // backing it. If so, the handle is consumed. Otherwise, the handle remains
+  // valid and can be further importer by standard means.
+  if (!get_protected_native_pixmap_callback_.is_null()) {
+    auto protected_pixmap = get_protected_native_pixmap_callback_.Run(handle);
+    if (protected_pixmap)
+      return protected_pixmap;
+  }
+
+  return CreateNativePixmapFromHandleInternal(widget, size, format, handle);
+}
+
+scoped_refptr<gfx::NativePixmap>
+GbmSurfaceFactory::CreateNativePixmapForProtectedBufferHandle(
+    gfx::AcceleratedWidget widget,
+    gfx::Size size,
+    gfx::BufferFormat format,
+    const gfx::NativePixmapHandle& handle) {
+  // Create a new NativePixmap without querying the external service for any
+  // existing mappings.
+  return CreateNativePixmapFromHandleInternal(widget, size, format, handle);
+}
+
+void GbmSurfaceFactory::SetGetProtectedNativePixmapDelegate(
+    const GetProtectedNativePixmapCallback&
+        get_protected_native_pixmap_callback) {
+  get_protected_native_pixmap_callback_ = get_protected_native_pixmap_callback;
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.h b/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
index ce1abfdc..3955c1c1 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
@@ -50,8 +50,22 @@
       gfx::Size size,
       gfx::BufferFormat format,
       const gfx::NativePixmapHandle& handle) override;
+  void SetGetProtectedNativePixmapDelegate(
+      const GetProtectedNativePixmapCallback&
+          get_protected_native_pixmap_callback) override;
+  scoped_refptr<gfx::NativePixmap> CreateNativePixmapForProtectedBufferHandle(
+      gfx::AcceleratedWidget widget,
+      gfx::Size size,
+      gfx::BufferFormat format,
+      const gfx::NativePixmapHandle& handle) override;
 
  private:
+  scoped_refptr<gfx::NativePixmap> CreateNativePixmapFromHandleInternal(
+      gfx::AcceleratedWidget widget,
+      gfx::Size size,
+      gfx::BufferFormat format,
+      const gfx::NativePixmapHandle& handle);
+
   std::unique_ptr<GLOzone> egl_implementation_;
   std::unique_ptr<GLOzone> osmesa_implementation_;
 
@@ -61,6 +75,8 @@
 
   std::map<gfx::AcceleratedWidget, GbmSurfaceless*> widget_to_surface_map_;
 
+  GetProtectedNativePixmapCallback get_protected_native_pixmap_callback_;
+
   DISALLOW_COPY_AND_ASSIGN(GbmSurfaceFactory);
 };
 
diff --git a/ui/ozone/platform/headless/headless_surface_factory.cc b/ui/ozone/platform/headless/headless_surface_factory.cc
index dcfcf54..4ba3929 100644
--- a/ui/ozone/platform/headless/headless_surface_factory.cc
+++ b/ui/ozone/platform/headless/headless_surface_factory.cc
@@ -83,6 +83,7 @@
   uint64_t GetDmaBufModifier(size_t plane) const override { return 0; }
   gfx::BufferFormat GetBufferFormat() const override { return format_; }
   gfx::Size GetBufferSize() const override { return gfx::Size(); }
+  uint32_t GetUniqueId() const override { return 0; }
   bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
                             int plane_z_order,
                             gfx::OverlayTransform plane_transform,
diff --git a/ui/ozone/public/surface_factory_ozone.cc b/ui/ozone/public/surface_factory_ozone.cc
index 527aa29..c7c2dc3 100644
--- a/ui/ozone/public/surface_factory_ozone.cc
+++ b/ui/ozone/public/surface_factory_ozone.cc
@@ -52,4 +52,17 @@
   return nullptr;
 }
 
+scoped_refptr<gfx::NativePixmap>
+SurfaceFactoryOzone::CreateNativePixmapForProtectedBufferHandle(
+    gfx::AcceleratedWidget widget,
+    gfx::Size size,
+    gfx::BufferFormat format,
+    const gfx::NativePixmapHandle& handle) {
+  return nullptr;
+}
+
+void SurfaceFactoryOzone::SetGetProtectedNativePixmapDelegate(
+    const GetProtectedNativePixmapCallback&
+        get_protected_native_pixmap_callback) {}
+
 }  // namespace ui
diff --git a/ui/ozone/public/surface_factory_ozone.h b/ui/ozone/public/surface_factory_ozone.h
index 6e7726a..1b7ba91 100644
--- a/ui/ozone/public/surface_factory_ozone.h
+++ b/ui/ozone/public/surface_factory_ozone.h
@@ -99,6 +99,38 @@
       gfx::BufferFormat format,
       const gfx::NativePixmapHandle& handle);
 
+  // A temporary solution that allows protected NativePixmap management to be
+  // handled outside the Ozone platform (crbug.com/771863).
+  // The current implementation uses dummy NativePixmaps as transparent handles
+  // to separate NativePixmaps with actual contents. This method takes
+  // a NativePixmapHandle to such a dummy pixmap, and creates a NativePixmap
+  // instance for it.
+  virtual scoped_refptr<gfx::NativePixmap>
+  CreateNativePixmapForProtectedBufferHandle(
+      gfx::AcceleratedWidget widget,
+      gfx::Size size,
+      gfx::BufferFormat format,
+      const gfx::NativePixmapHandle& handle);
+
+  // This callback can be used by implementations of this interface to query
+  // for a NativePixmap for the given NativePixmapHandle, instead of importing
+  // it via standard means. This happens if an external service is maintaining
+  // a separate mapping of NativePixmapHandles to NativePixmaps.
+  // If this callback returns non-nullptr, the returned NativePixmap should
+  // be used instead of the NativePixmap that would have been produced by the
+  // standard, implementation-specific NativePixmapHandle import mechanism.
+  using GetProtectedNativePixmapCallback =
+      base::Callback<scoped_refptr<gfx::NativePixmap>(
+          const gfx::NativePixmapHandle&)>;
+  // Called by an external service to set the GetProtectedNativePixmapCallback,
+  // to be used by the implementation when importing NativePixmapHandles.
+  // TODO(posciak): crbug.com/778555, move this to platform-specific
+  // implementation(s) and make protected pixmap handling transparent to the
+  // clients of this interface, removing the need for this callback.
+  virtual void SetGetProtectedNativePixmapDelegate(
+      const GetProtectedNativePixmapCallback&
+          get_protected_native_pixmap_callback);
+
  protected:
   SurfaceFactoryOzone();
   virtual ~SurfaceFactoryOzone();
diff --git a/ui/views/controls/scrollbar/cocoa_scroll_bar.h b/ui/views/controls/scrollbar/cocoa_scroll_bar.h
index 91de43a2..a78bc84 100644
--- a/ui/views/controls/scrollbar/cocoa_scroll_bar.h
+++ b/ui/views/controls/scrollbar/cocoa_scroll_bar.h
@@ -51,6 +51,9 @@
   // Returns the scroller style.
   NSScrollerStyle GetScrollerStyle() const { return scroller_style_; }
 
+  // Returns the thickness of the scrollbar.
+  int ScrollbarThickness() const;
+
   // Returns true if the opacity is 0.0.
   bool IsScrollbarFullyHidden() const;
 
@@ -85,9 +88,6 @@
   // and is not in a hover/pressed state.
   void ResetOverlayScrollbar();
 
-  // Returns the thickness of the scrollbar.
-  int ScrollbarThickness() const;
-
   // Sets the scrolltrack's visibility and then repaints it.
   void SetScrolltrackVisible(bool visible);
 
diff --git a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
index 6cab3ad..b5364da3 100644
--- a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
+++ b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
@@ -31,16 +31,12 @@
 const int kScrollbarThickness = 12;
 const int kExpandedScrollbarThickness = 16;
 
-// The thickness of the scrollbar thumb.
-const int kScrollbarThumbThickness = 8;
-
 // The width of the scroller track border.
 const int kScrollerTrackBorderWidth = 1;
 
-// The amount the thumb is inset from both the ends and the sides of the normal
-// and expanded tracks.
+// The amount the thumb is inset from the ends and the inside edge of track
+// border.
 const int kScrollbarThumbInset = 2;
-const int kExpandedScrollbarThumbInset = 3;
 
 // Scrollbar thumb colors.
 const SkColor kScrollerDefaultThumbColor = SkColorSetARGB(0x38, 0, 0, 0);
@@ -82,16 +78,14 @@
   void OnMouseExited(const ui::MouseEvent& event) override;
 
  private:
-  // Converts scroll_bar() into a CocoaScrollBar object and returns it.
-  CocoaScrollBar* cocoa_scroll_bar() {
-    return static_cast<CocoaScrollBar*>(scroll_bar());
-  }
+  // The CocoaScrollBar that owns us.
+  CocoaScrollBar* cocoa_scroll_bar_;  // weak.
 
   DISALLOW_COPY_AND_ASSIGN(CocoaScrollBarThumb);
 };
 
 CocoaScrollBarThumb::CocoaScrollBarThumb(CocoaScrollBar* scroll_bar)
-    : BaseScrollBarThumb(scroll_bar) {
+    : BaseScrollBarThumb(scroll_bar), cocoa_scroll_bar_(scroll_bar) {
   DCHECK(scroll_bar);
 
   // This is necessary, otherwise the thumb will be rendered below the views if
@@ -111,30 +105,37 @@
 }
 
 gfx::Size CocoaScrollBarThumb::CalculatePreferredSize() const {
-  return gfx::Size(kScrollbarThumbThickness, kScrollbarThumbThickness);
+  int thickness = cocoa_scroll_bar_->ScrollbarThickness();
+  return gfx::Size(thickness, thickness);
 }
 
 void CocoaScrollBarThumb::OnPaint(gfx::Canvas* canvas) {
   SkColor thumb_color = kScrollerDefaultThumbColor;
-  if (cocoa_scroll_bar()->GetScrollerStyle() == NSScrollerStyleOverlay ||
-      IsStateHovered() ||
-      IsStatePressed()) {
+  if (cocoa_scroll_bar_->GetScrollerStyle() == NSScrollerStyleOverlay ||
+      IsStateHovered() || IsStatePressed()) {
     thumb_color = kScrollerHoverThumbColor;
   }
 
-  gfx::Rect local_bounds(GetLocalBounds());
+  gfx::Rect bounds(GetLocalBounds());
+  bounds.Inset(kScrollbarThumbInset, kScrollbarThumbInset);
+  if (IsHorizontal())
+    bounds.Inset(0, kScrollerTrackBorderWidth, 0, 0);
+  else if (base::i18n::IsRTL())
+    bounds.Inset(0, 0, kScrollerTrackBorderWidth, 0);
+  else
+    bounds.Inset(kScrollerTrackBorderWidth, 0, 0, 0);
+
   cc::PaintFlags flags;
   flags.setAntiAlias(true);
   flags.setStyle(cc::PaintFlags::kFill_Style);
   flags.setColor(thumb_color);
-  const SkScalar radius =
-      std::min(local_bounds.width(), local_bounds.height());
-  canvas->DrawRoundRect(local_bounds, radius, flags);
+  const SkScalar radius = std::min(bounds.width(), bounds.height());
+  canvas->DrawRoundRect(bounds, radius, flags);
 }
 
 bool CocoaScrollBarThumb::OnMousePressed(const ui::MouseEvent& event) {
   // Ignore the mouse press if the scrollbar is hidden.
-  if (cocoa_scroll_bar()->IsScrollbarFullyHidden())
+  if (cocoa_scroll_bar_->IsScrollbarFullyHidden())
     return false;
 
   return BaseScrollBarThumb::OnMousePressed(event);
@@ -191,19 +192,7 @@
 // CocoaScrollBar, BaseScrollBar:
 
 gfx::Rect CocoaScrollBar::GetTrackBounds() const {
-  gfx::Rect local_bounds(GetLocalBounds());
-
-  int inset = kScrollbarThumbInset;
-  if (is_expanded_) {
-    inset = thickness_animation_.CurrentValueBetween(
-        kScrollbarThumbInset, kExpandedScrollbarThumbInset);
-  }
-  local_bounds.Inset(inset, inset);
-
-  gfx::Size track_size = local_bounds.size();
-  track_size.SetToMax(GetThumb()->GetPreferredSize());
-  local_bounds.set_size(track_size);
-  return local_bounds;
+  return GetLocalBounds();
 }
 
 //////////////////////////////////////////////////////////////////
@@ -448,6 +437,14 @@
 //////////////////////////////////////////////////////////////////
 // CocoaScrollBar, public:
 
+int CocoaScrollBar::ScrollbarThickness() const {
+  if (scroller_style_ == NSScrollerStyleLegacy)
+    return kScrollbarThickness;
+
+  return thickness_animation_.CurrentValueBetween(kScrollbarThickness,
+                                                  kExpandedScrollbarThickness);
+}
+
 bool CocoaScrollBar::IsScrollbarFullyHidden() const {
   return layer()->opacity() == 0.0f;
 }
@@ -521,14 +518,6 @@
   }
 }
 
-int CocoaScrollBar::ScrollbarThickness() const {
-  if (scroller_style_ == NSScrollerStyleLegacy)
-    return kScrollbarThickness;
-
-  return thickness_animation_.CurrentValueBetween(kScrollbarThickness,
-                                                  kExpandedScrollbarThickness);
-}
-
 void CocoaScrollBar::SetScrolltrackVisible(bool visible) {
   has_scrolltrack_ = visible;
   SchedulePaint();