diff --git a/DEPS b/DEPS
index 4c815e0..6c26d1d 100644
--- a/DEPS
+++ b/DEPS
@@ -39,11 +39,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '6db0a7bdceb6be85721bfb0db8dea7fd27db5970',
+  'skia_revision': 'cf9bafceafaf6c0bf8b0eac8de509aa1d8407fca',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '8e18e51a50775594c8cbda14fca898c2923ac30d',
+  'v8_revision': 'd71ff17b88337c131cecdbc00f093763e897f321',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
diff --git a/cc/layers/video_frame_provider.h b/cc/layers/video_frame_provider.h
index e7619a3..f747a88 100644
--- a/cc/layers/video_frame_provider.h
+++ b/cc/layers/video_frame_provider.h
@@ -47,6 +47,9 @@
     // BeginFrameObserver based approach. http://crbug.com/336733
     virtual void DidReceiveFrame() = 0;
 
+    // Notifies the client of a new UV transform matrix to be used.
+    virtual void DidUpdateMatrix(const float* matrix) = 0;
+
    protected:
     virtual ~Client() {}
   };
diff --git a/cc/layers/video_frame_provider_client_impl.cc b/cc/layers/video_frame_provider_client_impl.cc
index d9734d09..f2c5eb2 100644
--- a/cc/layers/video_frame_provider_client_impl.cc
+++ b/cc/layers/video_frame_provider_client_impl.cc
@@ -32,6 +32,14 @@
   // frame provider client that does not require a lock. The same is true of
   // the call to Stop().
   provider_->SetVideoFrameProviderClient(this);
+
+  // This matrix is the default transformation for stream textures, and flips
+  // on the Y axis.
+  stream_texture_matrix_ = gfx::Transform(
+      1.0, 0.0, 0.0, 0.0,
+      0.0, -1.0, 0.0, 1.0,
+      0.0, 0.0, 1.0, 0.0,
+      0.0, 0.0, 0.0, 1.0);
 }
 
 VideoFrameProviderClientImpl::~VideoFrameProviderClientImpl() {
@@ -97,6 +105,12 @@
   return provider_ && provider_->HasCurrentFrame();
 }
 
+const gfx::Transform& VideoFrameProviderClientImpl::StreamTextureMatrix()
+    const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return stream_texture_matrix_;
+}
+
 void VideoFrameProviderClientImpl::StopUsingProvider() {
   {
     // Block the provider from shutting down until this client is done
@@ -137,6 +151,17 @@
     active_video_layer_->SetNeedsRedraw();
 }
 
+void VideoFrameProviderClientImpl::DidUpdateMatrix(const float* matrix) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  stream_texture_matrix_ = gfx::Transform(
+      matrix[0], matrix[4], matrix[8], matrix[12],
+      matrix[1], matrix[5], matrix[9], matrix[13],
+      matrix[2], matrix[6], matrix[10], matrix[14],
+      matrix[3], matrix[7], matrix[11], matrix[15]);
+  if (active_video_layer_)
+    active_video_layer_->SetNeedsRedraw();
+}
+
 void VideoFrameProviderClientImpl::OnBeginFrame(const BeginFrameArgs& args) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(rendering_);
diff --git a/cc/layers/video_frame_provider_client_impl.h b/cc/layers/video_frame_provider_client_impl.h
index f7892ee..17525ec 100644
--- a/cc/layers/video_frame_provider_client_impl.h
+++ b/cc/layers/video_frame_provider_client_impl.h
@@ -45,6 +45,8 @@
   void ReleaseLock();
   bool HasCurrentFrame();
 
+  const gfx::Transform& StreamTextureMatrix() const;
+
   // VideoFrameController implementation.
   void OnBeginFrame(const BeginFrameArgs& args) override;
   void DidDrawFrame() override;
@@ -56,6 +58,7 @@
   void StartRendering() override;
   void StopRendering() override;
   void DidReceiveFrame() override;
+  void DidUpdateMatrix(const float* matrix) override;
 
   const VideoFrameProvider* get_provider_for_testing() const {
     return provider_;
@@ -82,6 +85,8 @@
   base::Lock provider_lock_;
   base::ThreadChecker thread_checker_;
 
+  gfx::Transform stream_texture_matrix_;
+
   DISALLOW_COPY_AND_ASSIGN(VideoFrameProviderClientImpl);
 };
 
diff --git a/cc/layers/video_frame_provider_client_impl_unittest.cc b/cc/layers/video_frame_provider_client_impl_unittest.cc
index eaa0e6f..e337baa9 100644
--- a/cc/layers/video_frame_provider_client_impl_unittest.cc
+++ b/cc/layers/video_frame_provider_client_impl_unittest.cc
@@ -140,4 +140,29 @@
   StopRendering();
 }
 
+TEST_F(VideoFrameProviderClientImplTest, StreamTextureMatrix) {
+  const float kIdentityMatrix[] = {
+      1.0,
+      0.0,
+      0.0,
+      0.0,
+      0.0,
+      1.0,
+      0.0,
+      0.0,
+      0.0,
+      0.0,
+      1.0,
+      0.0,
+      0.0,
+      0.0,
+      0.0,
+      1.0,
+  };
+
+  EXPECT_FALSE(client_impl_->StreamTextureMatrix().IsIdentity());
+  client_impl_->DidUpdateMatrix(kIdentityMatrix);
+  EXPECT_TRUE(client_impl_->StreamTextureMatrix().IsIdentity());
+}
+
 }  // namespace cc
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc
index 8fa674f5..f22860c 100644
--- a/cc/layers/video_layer_impl.cc
+++ b/cc/layers/video_layer_impl.cc
@@ -319,9 +319,10 @@
       scale.Scale(tex_width_scale, tex_height_scale);
       StreamVideoDrawQuad* stream_video_quad =
           render_pass->CreateAndAppendDrawQuad<StreamVideoDrawQuad>();
-      stream_video_quad->SetNew(shared_quad_state, quad_rect, opaque_rect,
-                                visible_quad_rect, frame_resources_[0].id,
-                                frame_resources_[0].size_in_pixels, scale);
+      stream_video_quad->SetNew(
+          shared_quad_state, quad_rect, opaque_rect, visible_quad_rect,
+          frame_resources_[0].id, frame_resources_[0].size_in_pixels,
+          scale * provider_client_impl_->StreamTextureMatrix());
       ValidateQuadResources(stream_video_quad);
       break;
     }
diff --git a/chrome/VERSION b/chrome/VERSION
index a7cd6de..8001a3e 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=51
 MINOR=0
-BUILD=2698
+BUILD=2699
 PATCH=0
diff --git a/chrome/browser/ui/cocoa/url_drop_target.mm b/chrome/browser/ui/cocoa/url_drop_target.mm
index 55d5e2bd..00b11c1 100644
--- a/chrome/browser/ui/cocoa/url_drop_target.mm
+++ b/chrome/browser/ui/cocoa/url_drop_target.mm
@@ -6,17 +6,9 @@
 
 #include "chrome/browser/ui/cocoa/drag_util.h"
 #import "third_party/mozilla/NSPasteboard+Utils.h"
+#include "ui/base/clipboard/clipboard_util_mac.h"
 #include "url/gurl.h"
 
-namespace {
-
-// Mac WebKit uses this type, declared in
-// WebKit/mac/History/WebURLsWithTitles.h.
-NSString* const kCrWebURLsWithTitlesPboardType =
-    @"WebURLsWithTitlesPboardType";
-
-}  // namespace
-
 @interface URLDropTargetHandler(Private)
 
 // Gets the appropriate drag operation given the |NSDraggingInfo|.
@@ -30,11 +22,10 @@
 @implementation URLDropTargetHandler
 
 + (NSArray*)handledDragTypes {
-  return [NSArray arrayWithObjects:kCrWebURLsWithTitlesPboardType,
-                                   NSURLPboardType,
-                                   NSStringPboardType,
-                                   NSFilenamesPboardType,
-                                   nil];
+  return @[
+    ui::ClipboardUtil::UTIForWebURLsAndTitles(), NSURLPboardType,
+    NSStringPboardType, NSFilenamesPboardType
+  ];
 }
 
 - (id)initWithView:(NSView<URLDropTarget>*)view {
diff --git a/components/bookmarks/browser/bookmark_pasteboard_helper_mac.mm b/components/bookmarks/browser/bookmark_pasteboard_helper_mac.mm
index 1aac917..a2abbe8 100644
--- a/components/bookmarks/browser/bookmark_pasteboard_helper_mac.mm
+++ b/components/bookmarks/browser/bookmark_pasteboard_helper_mac.mm
@@ -12,22 +12,19 @@
 #include "base/strings/sys_string_conversions.h"
 #include "components/bookmarks/browser/bookmark_node.h"
 #include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/clipboard_util_mac.h"
 
 NSString* const kBookmarkDictionaryListPboardType =
-    @"BookmarkDictionaryListPboardType";
+    @"com.google.chrome.BookmarkDictionaryListPboardType";
 
 namespace bookmarks {
 
 namespace {
 
-// An unofficial standard pasteboard title type to be provided alongside the
-// |NSURLPboardType|.
-NSString* const kNSURLTitlePboardType = @"public.url-name";
-
 // Pasteboard type used to store profile path to determine which profile
 // a set of bookmarks came from.
 NSString* const kChromiumProfilePathPboardType =
-    @"ChromiumProfilePathPboardType";
+    @"com.google.chrome.ChromiumProfilePathPboardType";
 
 // Internal bookmark ID for a bookmark node.  Used only when moving inside
 // of one profile.
@@ -36,10 +33,6 @@
 // Internal bookmark meta info dictionary for a bookmark node.
 NSString* const kChromiumBookmarkMetaInfo = @"ChromiumBookmarkMetaInfo";
 
-// Mac WebKit uses this type, declared in
-// WebKit/mac/History/WebURLsWithTitles.h.
-NSString* const kCrWebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType";
-
 // Keys for the type of node in BookmarkDictionaryListPboardType.
 NSString* const kWebBookmarkType = @"WebBookmarkType";
 
@@ -100,8 +93,9 @@
 bool ReadBookmarkDictionaryListPboardType(
     NSPasteboard* pb,
     std::vector<BookmarkNodeData::Element>& elements) {
-  NSArray* bookmarks =
-      [pb propertyListForType:kBookmarkDictionaryListPboardType];
+  NSString* uti = ui::ClipboardUtil::UTIForPasteboardType(
+      kBookmarkDictionaryListPboardType);
+  NSArray* bookmarks = [pb propertyListForType:uti];
   if (!bookmarks)
     return false;
   ConvertPlistToElements(bookmarks, elements);
@@ -111,16 +105,9 @@
 bool ReadWebURLsWithTitlesPboardType(
     NSPasteboard* pb,
     std::vector<BookmarkNodeData::Element>& elements) {
-  NSArray* bookmarkPairs =
-      [pb propertyListForType:kCrWebURLsWithTitlesPboardType];
-  if (![bookmarkPairs isKindOfClass:[NSArray class]])
-    return false;
-
-  NSArray* urlsArr = [bookmarkPairs objectAtIndex:0];
-  NSArray* titlesArr = [bookmarkPairs objectAtIndex:1];
-  if ([urlsArr count] < 1)
-    return false;
-  if ([urlsArr count] != [titlesArr count])
+  NSArray* urlsArr = nil;
+  NSArray* titlesArr = nil;
+  if (!ui::ClipboardUtil::URLsAndTitlesFromPasteboard(pb, &urlsArr, &titlesArr))
     return false;
 
   NSUInteger len = [titlesArr count];
@@ -139,25 +126,6 @@
   return true;
 }
 
-bool ReadNSURLPboardType(NSPasteboard* pb,
-                         std::vector<BookmarkNodeData::Element>& elements) {
-  NSURL* url = [NSURL URLFromPasteboard:pb];
-  if (url == nil)
-    return false;
-
-  std::string urlString = base::SysNSStringToUTF8([url absoluteString]);
-  NSString* title = [pb stringForType:kNSURLTitlePboardType];
-  if (!title)
-    title = [pb stringForType:NSStringPboardType];
-
-  BookmarkNodeData::Element element;
-  element.is_url = true;
-  element.url = GURL(urlString);
-  element.title = base::SysNSStringToUTF16(title);
-  elements.push_back(element);
-  return true;
-}
-
 NSDictionary* DictionaryFromBookmarkMetaInfo(
     const BookmarkNode::MetaInfoMap& meta_info_map) {
   NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
@@ -215,7 +183,10 @@
     NSPasteboard* pb,
     const std::vector<BookmarkNodeData::Element>& elements) {
   NSArray* plist = GetPlistForBookmarkList(elements);
-  [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType];
+  NSString* uti = ui::ClipboardUtil::UTIForPasteboardType(
+      kBookmarkDictionaryListPboardType);
+  [pb addTypes:@[ uti ] owner:nil];
+  [pb setPropertyList:plist forType:uti];
 }
 
 void FillFlattenedArraysForBookmarks(
@@ -248,23 +219,22 @@
   FillFlattenedArraysForBookmarks(
       elements, url_titles, urls, toplevel_string_data);
 
+  if ([urls count] > 0) {
+    base::scoped_nsobject<NSPasteboardItem> item;
+    if ([urls count] == 1) {
+      item = ui::ClipboardUtil::PasteboardItemFromUrl([urls firstObject],
+                                                      [url_titles firstObject]);
+    } else {
+      item = ui::ClipboardUtil::PasteboardItemFromUrls(urls, url_titles);
+    }
+
+    ui::ClipboardUtil::AddDataToPasteboard(pb, item);
+  }
+
   // Write NSStringPboardType.
+  [pb addTypes:@[ NSStringPboardType ] owner:nil];
   [pb setString:[toplevel_string_data componentsJoinedByString:@"\n"]
         forType:NSStringPboardType];
-
-  // The following pasteboard types only act on urls, not folders.
-  if ([urls count] < 1)
-    return;
-
-  // Write WebURLsWithTitlesPboardType.
-  [pb setPropertyList:[NSArray arrayWithObjects:urls, url_titles, nil]
-              forType:kCrWebURLsWithTitlesPboardType];
-
-  // Write NSURLPboardType (with title).
-  NSURL* url = [NSURL URLWithString:[urls objectAtIndex:0]];
-  [url writeToPasteboard:pb];
-  NSString* titleString = [url_titles objectAtIndex:0];
-  [pb setString:titleString forType:kNSURLTitlePboardType];
 }
 
 NSPasteboard* PasteboardFromType(ui::ClipboardType type) {
@@ -295,16 +265,10 @@
 
   NSPasteboard* pb = PasteboardFromType(type);
 
-  NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
-                                             kCrWebURLsWithTitlesPboardType,
-                                             NSStringPboardType,
-                                             NSURLPboardType,
-                                             kNSURLTitlePboardType,
-                                             kChromiumProfilePathPboardType,
-                                             nil];
-  [pb declareTypes:types owner:nil];
-  [pb setString:base::SysUTF8ToNSString(profile_path.value())
-        forType:kChromiumProfilePathPboardType];
+  NSString* uti =
+      ui::ClipboardUtil::UTIForPasteboardType(kChromiumProfilePathPboardType);
+  [pb declareTypes:@[ uti ] owner:nil];
+  [pb setString:base::SysUTF8ToNSString(profile_path.value()) forType:uti];
   WriteBookmarkDictionaryListPboardType(pb, elements);
   WriteSimplifiedBookmarkTypes(pb, elements);
 }
@@ -316,21 +280,21 @@
   NSPasteboard* pb = PasteboardFromType(type);
 
   elements.clear();
-  NSString* profile = [pb stringForType:kChromiumProfilePathPboardType];
+  NSString* uti =
+      ui::ClipboardUtil::UTIForPasteboardType(kChromiumProfilePathPboardType);
+  NSString* profile = [pb stringForType:uti];
   *profile_path = base::FilePath(base::SysNSStringToUTF8(profile));
   return ReadBookmarkDictionaryListPboardType(pb, elements) ||
-         ReadWebURLsWithTitlesPboardType(pb, elements) ||
-         ReadNSURLPboardType(pb, elements);
+         ReadWebURLsWithTitlesPboardType(pb, elements);
 }
 
 bool PasteboardContainsBookmarks(ui::ClipboardType type) {
   NSPasteboard* pb = PasteboardFromType(type);
 
-  NSArray* availableTypes =
-      [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
-                                kCrWebURLsWithTitlesPboardType,
-                                NSURLPboardType,
-                                nil];
+  NSArray* availableTypes = @[
+    ui::ClipboardUtil::UTIForWebURLsAndTitles(),
+    ui::ClipboardUtil::UTIForPasteboardType(kBookmarkDictionaryListPboardType)
+  ];
   return [pb availableTypeFromArray:availableTypes] != nil;
 }
 
diff --git a/content/common/gpu/stream_texture_android.cc b/content/common/gpu/stream_texture_android.cc
index 15ebb53..ae135d3 100644
--- a/content/common/gpu/stream_texture_android.cc
+++ b/content/common/gpu/stream_texture_android.cc
@@ -42,16 +42,16 @@
 
     // TODO: Ideally a valid image id was returned to the client so that
     // it could then call glBindTexImage2D() for doing the following.
-    scoped_refptr<gpu::gles2::GLStreamTextureImage> gl_image(
+    scoped_refptr<gl::GLImage> gl_image(
         new StreamTexture(owner_stub, stream_id, texture->service_id()));
     gfx::Size size = gl_image->GetSize();
     texture_manager->SetTarget(texture, GL_TEXTURE_EXTERNAL_OES);
     texture_manager->SetLevelInfo(texture, GL_TEXTURE_EXTERNAL_OES, 0, GL_RGBA,
                                   size.width(), size.height(), 1, 0, GL_RGBA,
                                   GL_UNSIGNED_BYTE, gfx::Rect(size));
-    texture_manager->SetLevelStreamTextureImage(
-        texture, GL_TEXTURE_EXTERNAL_OES, 0, gl_image.get(),
-        gpu::gles2::Texture::UNBOUND);
+    texture_manager->SetLevelImage(texture, GL_TEXTURE_EXTERNAL_OES, 0,
+                                   gl_image.get(),
+                                   gpu::gles2::Texture::UNBOUND);
     return true;
   }
 
@@ -63,6 +63,7 @@
                              uint32_t texture_id)
     : surface_texture_(gfx::SurfaceTexture::Create(texture_id)),
       size_(0, 0),
+      has_valid_frame_(false),
       has_pending_frame_(false),
       owner_stub_(owner_stub),
       route_id_(route_id),
@@ -89,12 +90,6 @@
   }
 }
 
-// gpu::gles2::GLStreamTextureMatrix implementation
-void StreamTexture::GetTextureMatrix(float xform[16]) {
-  UpdateTexImage();
-  surface_texture_->GetTransformMatrix(xform);
-}
-
 void StreamTexture::OnWillDestroyStub() {
   owner_stub_->RemoveDestructionObserver(this);
   owner_stub_->channel()->RemoveRoute(route_id_);
@@ -147,8 +142,23 @@
 
   surface_texture_->UpdateTexImage();
 
+  has_valid_frame_ = true;
   has_pending_frame_ = false;
 
+  float mtx[16];
+  surface_texture_->GetTransformMatrix(mtx);
+
+  if (memcmp(current_matrix_, mtx, sizeof(mtx)) != 0) {
+    memcpy(current_matrix_, mtx, sizeof(mtx));
+
+    if (has_listener_) {
+      GpuStreamTextureMsg_MatrixChanged_Params params;
+      memcpy(&params.m00, mtx, sizeof(mtx));
+      owner_stub_->channel()->Send(
+          new GpuStreamTextureMsg_MatrixChanged(route_id_, params));
+    }
+  }
+
   if (scoped_make_current.get()) {
     // UpdateTexImage() implies glBindTexture().
     // The cmd decoder takes care of restoring the binding for this GLImage as
@@ -166,6 +176,12 @@
 }
 
 bool StreamTexture::CopyTexImage(unsigned target) {
+  if (target == GL_TEXTURE_2D) {
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size_.width(), size_.height(), 0,
+                 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    return CopyTexSubImage(GL_TEXTURE_2D, gfx::Point(), gfx::Rect(size_));
+  }
+
   if (target != GL_TEXTURE_EXTERNAL_OES)
     return false;
 
@@ -193,8 +209,8 @@
     // By setting image state to UNBOUND instead of COPIED we ensure that
     // CopyTexImage() is called each time the surface texture is used for
     // drawing.
-    texture->SetLevelStreamTextureImage(GL_TEXTURE_EXTERNAL_OES, 0, this,
-                                        gpu::gles2::Texture::UNBOUND);
+    texture->SetLevelImage(GL_TEXTURE_EXTERNAL_OES, 0, this,
+                           gpu::gles2::Texture::UNBOUND);
   }
 
   return true;
@@ -256,7 +272,94 @@
 bool StreamTexture::CopyTexSubImage(unsigned target,
                                     const gfx::Point& offset,
                                     const gfx::Rect& rect) {
-  return false;
+  if (target != GL_TEXTURE_2D)
+    return false;
+
+  if (!owner_stub_ || !surface_texture_.get())
+    return true;
+
+  if (!offset.IsOrigin()) {
+    LOG(ERROR) << "Non-origin offset is not supported";
+    return false;
+  }
+
+  if (rect != gfx::Rect(size_)) {
+    LOG(ERROR) << "Sub-rectangle is not supported";
+    return false;
+  }
+
+  GLint target_texture = 0;
+  glGetIntegerv(GL_TEXTURE_BINDING_2D, &target_texture);
+  DCHECK(target_texture);
+
+  UpdateTexImage();
+
+  if (!framebuffer_) {
+    glGenFramebuffersEXT(1, &framebuffer_);
+
+    // This vertex shader introduces a y flip before applying the stream
+    // texture matrix.  This is required because the stream texture matrix
+    // Android provides is intended to  be used in a y-up coordinate system,
+    // whereas Chromium expects y-down.
+
+    // clang-format off
+    const char kVertexShader[] = STRINGIZE(
+      attribute vec2 a_position;
+      varying vec2 v_texCoord;
+      uniform mat4 u_xform;
+      void main() {
+        gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);
+        vec2 uv_untransformed = a_position * vec2(0.5, -0.5) + vec2(0.5, 0.5);
+        v_texCoord = (u_xform * vec4(uv_untransformed, 0.0, 1.0)).xy;
+      }
+    );
+    const char kFragmentShader[] =
+      "#extension GL_OES_EGL_image_external : require\n" STRINGIZE(
+      precision mediump float;
+      uniform samplerExternalOES a_texture;
+      varying vec2 v_texCoord;
+      void main() {
+        gl_FragColor = texture2D(a_texture, v_texCoord);
+      }
+    );
+    // clang-format on
+
+    vertex_buffer_ = gfx::GLHelper::SetupQuadVertexBuffer();
+    vertex_shader_ = gfx::GLHelper::LoadShader(GL_VERTEX_SHADER, kVertexShader);
+    fragment_shader_ =
+        gfx::GLHelper::LoadShader(GL_FRAGMENT_SHADER, kFragmentShader);
+    program_ = gfx::GLHelper::SetupProgram(vertex_shader_, fragment_shader_);
+    gfx::ScopedUseProgram use_program(program_);
+    int sampler_location = glGetUniformLocation(program_, "a_texture");
+    DCHECK_NE(-1, sampler_location);
+    glUniform1i(sampler_location, 0);
+    u_xform_location_ = glGetUniformLocation(program_, "u_xform");
+    DCHECK_NE(-1, u_xform_location_);
+  }
+
+  gfx::ScopedActiveTexture active_texture(GL_TEXTURE0);
+  // UpdateTexImage() call below will bind the surface texture to
+  // TEXTURE_EXTERNAL_OES. This scoped texture binder will restore the current
+  // binding before this function returns.
+  gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_EXTERNAL_OES, texture_id_);
+
+  {
+    gfx::ScopedFrameBufferBinder framebuffer_binder(framebuffer_);
+    gfx::ScopedViewport viewport(0, 0, size_.width(), size_.height());
+    glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                              GL_TEXTURE_2D, target_texture, 0);
+    DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
+              glCheckFramebufferStatusEXT(GL_FRAMEBUFFER));
+    gfx::ScopedUseProgram use_program(program_);
+
+    glUniformMatrix4fv(u_xform_location_, 1, false, current_matrix_);
+    gfx::GLHelper::DrawQuad(vertex_buffer_);
+
+    // Detach the output texture from the fbo.
+    glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                              GL_TEXTURE_2D, 0, 0);
+  }
+  return true;
 }
 
 bool StreamTexture::ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
diff --git a/content/common/gpu/stream_texture_android.h b/content/common/gpu/stream_texture_android.h
index 8852965..e19fc1b 100644
--- a/content/common/gpu/stream_texture_android.h
+++ b/content/common/gpu/stream_texture_android.h
@@ -10,7 +10,6 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "content/common/gpu/gpu_command_buffer_stub.h"
-#include "gpu/command_buffer/service/gl_stream_texture_image.h"
 #include "ipc/ipc_listener.h"
 #include "ui/gl/android/surface_texture.h"
 #include "ui/gl/gl_image.h"
@@ -25,7 +24,7 @@
 
 namespace content {
 
-class StreamTexture : public gpu::gles2::GLStreamTextureImage,
+class StreamTexture : public gl::GLImage,
                       public IPC::Listener,
                       public GpuCommandBufferStub::DestructionObserver {
  public:
@@ -58,9 +57,6 @@
                     uint64_t process_tracing_id,
                     const std::string& dump_name) override;
 
-  // gpu::gles2::GLStreamTextureMatrix implementation
-  void GetTextureMatrix(float xform[16]) override;
-
   // GpuCommandBufferStub::DestructionObserver implementation.
   void OnWillDestroyStub() override;
 
@@ -87,6 +83,9 @@
   // Current size of the surface texture.
   gfx::Size size_;
 
+  // Whether we ever bound a valid frame.
+  bool has_valid_frame_;
+
   // Whether a new frame is available that we should update to.
   bool has_pending_frame_;
 
diff --git a/content/renderer/gpu/stream_texture_host_android.cc b/content/renderer/gpu/stream_texture_host_android.cc
index 3e62f98..08c4c2b 100644
--- a/content/renderer/gpu/stream_texture_host_android.cc
+++ b/content/renderer/gpu/stream_texture_host_android.cc
@@ -42,6 +42,8 @@
   IPC_BEGIN_MESSAGE_MAP(StreamTextureHost, message)
     IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_FrameAvailable,
                         OnFrameAvailable);
+    IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_MatrixChanged,
+                        OnMatrixChanged);
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   DCHECK(handled);
@@ -56,4 +58,12 @@
     listener_->OnFrameAvailable();
 }
 
+void StreamTextureHost::OnMatrixChanged(
+    const GpuStreamTextureMsg_MatrixChanged_Params& params) {
+  static_assert(sizeof(params) == sizeof(float) * 16,
+                "bad GpuStreamTextureMsg MatrixChanged_Params format");
+  if (listener_)
+    listener_->OnMatrixChanged((const float*)&params);
+}
+
 }  // namespace content
diff --git a/content/renderer/gpu/stream_texture_host_android.h b/content/renderer/gpu/stream_texture_host_android.h
index d03151b..4c88ded 100644
--- a/content/renderer/gpu/stream_texture_host_android.h
+++ b/content/renderer/gpu/stream_texture_host_android.h
@@ -37,6 +37,7 @@
   class Listener {
    public:
     virtual void OnFrameAvailable() = 0;
+    virtual void OnMatrixChanged(const float mtx[16]) = 0;
     virtual ~Listener() {}
   };
 
@@ -49,6 +50,7 @@
  private:
   // Message handlers:
   void OnFrameAvailable();
+  void OnMatrixChanged(const GpuStreamTextureMsg_MatrixChanged_Params& param);
 
   int stream_id_;
   Listener* listener_;
diff --git a/content/renderer/media/android/stream_texture_factory_impl.cc b/content/renderer/media/android/stream_texture_factory_impl.cc
index 81de90a..e38b6de 100644
--- a/content/renderer/media/android/stream_texture_factory_impl.cc
+++ b/content/renderer/media/android/stream_texture_factory_impl.cc
@@ -31,6 +31,7 @@
 
   // StreamTextureHost::Listener implementation:
   void OnFrameAvailable() override;
+  void OnMatrixChanged(const float matrix[16]) override;
 
  private:
   void BindOnThread(int32_t stream_id);
@@ -101,6 +102,12 @@
     client_->DidReceiveFrame();
 }
 
+void StreamTextureProxyImpl::OnMatrixChanged(const float matrix[16]) {
+  base::AutoLock lock(lock_);
+  if (client_)
+    client_->DidUpdateMatrix(matrix);
+}
+
 }  // namespace
 
 // static
diff --git a/content/renderer/media/android/stream_texture_factory_synchronous_impl.cc b/content/renderer/media/android/stream_texture_factory_synchronous_impl.cc
index 513432a..86a0e37 100644
--- a/content/renderer/media/android/stream_texture_factory_synchronous_impl.cc
+++ b/content/renderer/media/android/stream_texture_factory_synchronous_impl.cc
@@ -54,13 +54,16 @@
   scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
       context_provider_;
   scoped_refptr<gfx::SurfaceTexture> surface_texture_;
+  float current_matrix_[16];
+  bool has_updated_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(StreamTextureProxyImpl);
 };
 
 StreamTextureProxyImpl::StreamTextureProxyImpl(
     StreamTextureFactorySynchronousImpl::ContextProvider* provider)
-    : client_(NULL), context_provider_(provider) {
+    : client_(NULL), context_provider_(provider), has_updated_(false) {
+  std::fill(current_matrix_, current_matrix_ + 16, 0);
 }
 
 StreamTextureProxyImpl::~StreamTextureProxyImpl() {}
@@ -119,6 +122,25 @@
 }
 
 void StreamTextureProxyImpl::OnFrameAvailable() {
+  // GetTransformMatrix only returns something valid after both is true:
+  // - OnFrameAvailable was called
+  // - we called UpdateTexImage
+  if (has_updated_) {
+    float matrix[16];
+    surface_texture_->GetTransformMatrix(matrix);
+
+    if (memcmp(current_matrix_, matrix, sizeof(matrix)) != 0) {
+      memcpy(current_matrix_, matrix, sizeof(matrix));
+
+      base::AutoLock lock(lock_);
+      if (client_)
+        client_->DidUpdateMatrix(current_matrix_);
+    }
+  }
+  // OnFrameAvailable being called a second time implies that we called
+  // updateTexImage since after we received the first frame.
+  has_updated_ = true;
+
   base::AutoLock lock(lock_);
   if (client_)
     client_->DidReceiveFrame();
diff --git a/content/renderer/media/webmediaplayer_ms_unittest.cc b/content/renderer/media/webmediaplayer_ms_unittest.cc
index 5c890cec..beaad89 100644
--- a/content/renderer/media/webmediaplayer_ms_unittest.cc
+++ b/content/renderer/media/webmediaplayer_ms_unittest.cc
@@ -395,6 +395,7 @@
   void StartRendering() override;
   void StopRendering() override;
   void DidReceiveFrame() override {}
+  void DidUpdateMatrix(const float* matrix) override {}
 
   // For test use
   void SetBackgroundRendering(bool background_rendering) {
diff --git a/gpu/command_buffer/service/gl_stream_texture_image.h b/gpu/command_buffer/service/gl_stream_texture_image.h
index b5bc370c..a6a517d 100644
--- a/gpu/command_buffer/service/gl_stream_texture_image.h
+++ b/gpu/command_buffer/service/gl_stream_texture_image.h
@@ -19,18 +19,8 @@
 
   // Get the matrix.
   // Copy the texture matrix for this image into |matrix|.
-  // Subclasses must return a matrix appropriate for a coordinate system where
-  // UV=(0,0) corresponds to the bottom left corner of the image.
   virtual void GetTextureMatrix(float matrix[16]) = 0;
 
-  // Copy the texture matrix for this image into |matrix|, returning a matrix
-  // for which UV=(0,0) corresponds to the top left of corner of the image,
-  // which is what Chromium generally expects.
-  void GetFlippedTextureMatrix(float matrix[16]) {
-    GetTextureMatrix(matrix);
-    matrix[13] += matrix[5];
-    matrix[5] = -matrix[5];
-  }
  protected:
   ~GLStreamTextureImage() override {}
 
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
index dad3d03..407f1baa 100644
--- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
+++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
@@ -487,39 +487,10 @@
     return;
   }
 
-  DoCopySubTextureWithTransform(
-      decoder, source_target, source_id, source_internal_format, dest_target,
-      dest_id, dest_internal_format, xoffset, yoffset, x, y, width, height,
-      dest_width, dest_height, source_width, source_height, flip_y,
-      premultiply_alpha, unpremultiply_alpha, kIdentityMatrix);
-}
-
-void CopyTextureCHROMIUMResourceManager::DoCopySubTextureWithTransform(
-    const gles2::GLES2Decoder* decoder,
-    GLenum source_target,
-    GLuint source_id,
-    GLenum source_internal_format,
-    GLenum dest_target,
-    GLuint dest_id,
-    GLenum dest_internal_format,
-    GLint xoffset,
-    GLint yoffset,
-    GLint x,
-    GLint y,
-    GLsizei width,
-    GLsizei height,
-    GLsizei dest_width,
-    GLsizei dest_height,
-    GLsizei source_width,
-    GLsizei source_height,
-    bool flip_y,
-    bool premultiply_alpha,
-    bool unpremultiply_alpha,
-    const GLfloat transform_matrix[16]) {
   DoCopyTextureInternal(decoder, source_target, source_id, dest_target, dest_id,
       xoffset, yoffset, x, y, width, height, dest_width, dest_height,
       source_width, source_height, flip_y, premultiply_alpha,
-      unpremultiply_alpha, transform_matrix);
+      unpremultiply_alpha, kIdentityMatrix);
 }
 
 void CopyTextureCHROMIUMResourceManager::DoCopyTextureWithTransform(
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h
index 95b25a0..aae3741 100644
--- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h
+++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h
@@ -67,28 +67,6 @@
                         bool premultiply_alpha,
                         bool unpremultiply_alpha);
 
-  void DoCopySubTextureWithTransform(const gles2::GLES2Decoder* decoder,
-                                     GLenum source_target,
-                                     GLuint source_id,
-                                     GLenum source_internal_format,
-                                     GLenum dest_target,
-                                     GLuint dest_id,
-                                     GLenum dest_internal_format,
-                                     GLint xoffset,
-                                     GLint yoffset,
-                                     GLint x,
-                                     GLint y,
-                                     GLsizei width,
-                                     GLsizei height,
-                                     GLsizei dest_width,
-                                     GLsizei dest_height,
-                                     GLsizei source_width,
-                                     GLsizei source_height,
-                                     bool flip_y,
-                                     bool premultiply_alpha,
-                                     bool unpremultiply_alpha,
-                                     const GLfloat transform_matrix[16]);
-
   // This will apply a transform on the texture coordinates before sampling
   // the source texture and copying to the destination texture. The transform
   // matrix should be given in column-major form, so it can be passed
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index f28cc43..d9381f2 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -13911,29 +13911,34 @@
 
   DoCopyTexImageIfNeeded(source_texture, source_target);
 
-  // GL_TEXTURE_EXTERNAL_OES texture requires that we apply a transform matrix
+  // GL_TEXTURE_EXTERNAL_OES texture requires apply a transform matrix
   // before presenting.
   if (source_target == GL_TEXTURE_EXTERNAL_OES) {
+    // TODO(hkuang): get the StreamTexture transform matrix in GPU process
+    // instead of using kIdentityMatrix crbug.com/226218.  AVDACodecImage does
+    // this correctly, but others (e.g., stream_texture_android.cc) don't.
+    // (crbug.com/371500, crbug.com/588837)
+    GLfloat transform_matrix[16];
+    memcpy(transform_matrix, kIdentityMatrix, sizeof(transform_matrix));
     if (GLStreamTextureImage* image =
             source_texture->GetLevelStreamTextureImage(GL_TEXTURE_EXTERNAL_OES,
                                                        0)) {
-      // The coordinate system of this matrix is y-up, not y-down, so a flip is
-      // needed.
-      GLfloat transform_matrix[16];
-      image->GetFlippedTextureMatrix(transform_matrix);
-      copy_texture_CHROMIUM_->DoCopyTextureWithTransform(
-          this, source_target, source_texture->service_id(), dest_target,
-          dest_texture->service_id(), source_width, source_height,
-          unpack_flip_y == GL_TRUE, unpack_premultiply_alpha == GL_TRUE,
-          unpack_unmultiply_alpha == GL_TRUE, transform_matrix);
-      return;
+      image->GetTextureMatrix(transform_matrix);
     }
+    copy_texture_CHROMIUM_->DoCopyTextureWithTransform(
+        this, source_target, source_texture->service_id(), dest_target,
+        dest_texture->service_id(), source_width, source_height,
+        unpack_flip_y == GL_TRUE, unpack_premultiply_alpha == GL_TRUE,
+        unpack_unmultiply_alpha == GL_TRUE, transform_matrix);
+  } else {
+    copy_texture_CHROMIUM_->DoCopyTexture(
+        this, source_target, source_texture->service_id(),
+        source_internal_format, dest_target, dest_texture->service_id(),
+        internal_format, source_width, source_height,
+        unpack_flip_y == GL_TRUE,
+        unpack_premultiply_alpha == GL_TRUE,
+        unpack_unmultiply_alpha == GL_TRUE);
   }
-  copy_texture_CHROMIUM_->DoCopyTexture(
-      this, source_target, source_texture->service_id(), source_internal_format,
-      dest_target, dest_texture->service_id(), internal_format, source_width,
-      source_height, unpack_flip_y == GL_TRUE,
-      unpack_premultiply_alpha == GL_TRUE, unpack_unmultiply_alpha == GL_TRUE);
 }
 
 void GLES2DecoderImpl::DoCopySubTextureCHROMIUM(
@@ -14104,33 +14109,16 @@
 
   DoCopyTexImageIfNeeded(source_texture, source_target);
 
-
-  // GL_TEXTURE_EXTERNAL_OES texture requires apply a transform matrix
-  // before presenting.
-  if (source_target == GL_TEXTURE_EXTERNAL_OES) {
-    if (GLStreamTextureImage* image =
-            source_texture->GetLevelStreamTextureImage(GL_TEXTURE_EXTERNAL_OES,
-                                                       0)) {
-      // The coordinate system of this matrix is y-up, not y-down, so a flip is
-      // needed.
-      GLfloat transform_matrix[16];
-      image->GetFlippedTextureMatrix(transform_matrix);
-      copy_texture_CHROMIUM_->DoCopySubTextureWithTransform(
-          this, source_target, source_texture->service_id(),
-          source_internal_format, dest_target, dest_texture->service_id(),
-          dest_internal_format, xoffset, yoffset, x, y, width, height,
-          dest_width, dest_height, source_width, source_height,
-          unpack_flip_y == GL_TRUE, unpack_premultiply_alpha == GL_TRUE,
-          unpack_unmultiply_alpha == GL_TRUE, transform_matrix);
-      return;
-    }
-  }
+  // TODO(hkuang): get the StreamTexture transform matrix in GPU process.
+  // crbug.com/226218.
   copy_texture_CHROMIUM_->DoCopySubTexture(
       this, source_target, source_texture->service_id(), source_internal_format,
-      dest_target, dest_texture->service_id(), dest_internal_format, xoffset,
-      yoffset, x, y, width, height, dest_width, dest_height, source_width,
-      source_height, unpack_flip_y == GL_TRUE,
-      unpack_premultiply_alpha == GL_TRUE, unpack_unmultiply_alpha == GL_TRUE);
+      dest_target, dest_texture->service_id(), dest_internal_format,
+      xoffset, yoffset, x, y, width, height, dest_width, dest_height,
+      source_width, source_height,
+      unpack_flip_y == GL_TRUE,
+      unpack_premultiply_alpha == GL_TRUE,
+      unpack_unmultiply_alpha == GL_TRUE);
 }
 
 void GLES2DecoderImpl::DoCompressedCopyTextureCHROMIUM(GLuint source_id,
@@ -14291,11 +14279,22 @@
       source_height, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
       gfx::Rect(source_width, source_height));
 
-  copy_texture_CHROMIUM_->DoCopyTexture(
-      this, source_texture->target(), source_texture->service_id(),
-      source_internal_format, dest_texture->target(),
-      dest_texture->service_id(), GL_RGBA, source_width, source_height, false,
-      false, false);
+  // GL_TEXTURE_EXTERNAL_OES texture requires apply a transform matrix
+  // before presenting.
+  if (source_texture->target() == GL_TEXTURE_EXTERNAL_OES) {
+    // TODO(hkuang): get the StreamTexture transform matrix in GPU process
+    // instead of using kIdentityMatrix crbug.com/226218.
+    copy_texture_CHROMIUM_->DoCopyTextureWithTransform(
+        this, source_texture->target(), source_texture->service_id(),
+        dest_texture->target(), dest_texture->service_id(), source_width,
+        source_height, false, false, false, kIdentityMatrix);
+  } else {
+    copy_texture_CHROMIUM_->DoCopyTexture(
+        this, source_texture->target(), source_texture->service_id(),
+        source_internal_format, dest_texture->target(),
+        dest_texture->service_id(), GL_RGBA, source_width, source_height, false,
+        false, false);
+  }
 }
 
 void GLES2DecoderImpl::DoTexStorage2DEXT(
diff --git a/gpu/ipc/common/gpu_messages.h b/gpu/ipc/common/gpu_messages.h
index 61cb08e..00cd334 100644
--- a/gpu/ipc/common/gpu_messages.h
+++ b/gpu/ipc/common/gpu_messages.h
@@ -70,6 +70,27 @@
   IPC_STRUCT_MEMBER(uint64_t, image_release_count)
 IPC_STRUCT_END()
 
+#if defined(OS_ANDROID)
+IPC_STRUCT_BEGIN(GpuStreamTextureMsg_MatrixChanged_Params)
+  IPC_STRUCT_MEMBER(float, m00)
+  IPC_STRUCT_MEMBER(float, m01)
+  IPC_STRUCT_MEMBER(float, m02)
+  IPC_STRUCT_MEMBER(float, m03)
+  IPC_STRUCT_MEMBER(float, m10)
+  IPC_STRUCT_MEMBER(float, m11)
+  IPC_STRUCT_MEMBER(float, m12)
+  IPC_STRUCT_MEMBER(float, m13)
+  IPC_STRUCT_MEMBER(float, m20)
+  IPC_STRUCT_MEMBER(float, m21)
+  IPC_STRUCT_MEMBER(float, m22)
+  IPC_STRUCT_MEMBER(float, m23)
+  IPC_STRUCT_MEMBER(float, m30)
+  IPC_STRUCT_MEMBER(float, m31)
+  IPC_STRUCT_MEMBER(float, m32)
+  IPC_STRUCT_MEMBER(float, m33)
+IPC_STRUCT_END()
+#endif
+
 //------------------------------------------------------------------------------
 // GPU Channel Messages
 // These are messages from a renderer process to the GPU process.
@@ -115,6 +136,10 @@
 
 // Inform the renderer that a new frame is available.
 IPC_MESSAGE_ROUTED0(GpuStreamTextureMsg_FrameAvailable)
+
+// Inform the renderer process that the transform matrix has changed.
+IPC_MESSAGE_ROUTED1(GpuStreamTextureMsg_MatrixChanged,
+                    GpuStreamTextureMsg_MatrixChanged_Params /* params */)
 #endif
 
 //------------------------------------------------------------------------------
diff --git a/media/blink/video_frame_compositor_unittest.cc b/media/blink/video_frame_compositor_unittest.cc
index 29274d38..b2e1695 100644
--- a/media/blink/video_frame_compositor_unittest.cc
+++ b/media/blink/video_frame_compositor_unittest.cc
@@ -70,6 +70,7 @@
   MOCK_METHOD0(StartRendering, void());
   MOCK_METHOD0(StopRendering, void());
   void DidReceiveFrame() override { ++did_receive_frame_count_; }
+  void DidUpdateMatrix(const float* matrix) override {}
 
   // VideoRendererSink::RenderCallback implementation.
   MOCK_METHOD3(Render,
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 165abbcf..2f44a84 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1203,6 +1203,14 @@
 crbug.com/521730 [ Win10 ] svg/as-image/svg-canvas-xhtml-tainted.html [ Failure ]
 crbug.com/521730 [ Win10 ] svg/css/text-shadow-multiple.xhtml [ Failure ]
 crbug.com/521730 [ Win10 ] svg/custom/textPath-change-id2-pattern.svg [ Failure ]
+crbug.com/521730 [ Win10 ] printing/quirks-percentage-height-body.html [ Failure ]
+crbug.com/521730 [ Win10 ] printing/quirks-percentage-height.html [ Failure ]
+crbug.com/521730 [ Win10 ] printing/standards-percentage-heights.html [ Failure ]
+crbug.com/521730 [ Win10 ] printing/subframes-percentage-height.html [ Failure ]
+crbug.com/521730 [ Win10 ] virtual/threaded/printing/quirks-percentage-height-body.html [ Failure ]
+crbug.com/521730 [ Win10 ] virtual/threaded/printing/quirks-percentage-height.html [ Failure ]
+crbug.com/521730 [ Win10 ] virtual/threaded/printing/standards-percentage-heights.html [ Failure ]
+crbug.com/521730 [ Win10 ] virtual/threaded/printing/subframes-percentage-height.html [ Failure ]
 
 crbug.com/591500 [ Win10 ] virtual/threaded/printing/ellipsis-printing-style.html [ Skip ]
 crbug.com/591500 [ Win10 ] printing/ellipsis-printing-style.html [ Skip ]
diff --git a/third_party/WebKit/Source/wtf/LinkedHashSet.h b/third_party/WebKit/Source/wtf/LinkedHashSet.h
index f2b7902..c0319b1 100644
--- a/third_party/WebKit/Source/wtf/LinkedHashSet.h
+++ b/third_party/WebKit/Source/wtf/LinkedHashSet.h
@@ -369,7 +369,7 @@
 
     typedef LinkedHashSetConstIterator<LinkedHashSetType> const_iterator;
 
-    Node* getNode() { return const_cast<Node*>(m_iterator.node()); }
+    Node* getNode() { return const_cast<Node*>(m_iterator.getNode()); }
 
 protected:
     LinkedHashSetIterator(const Node* position, LinkedHashSetType* m_container)
@@ -410,7 +410,7 @@
     typedef const typename LinkedHashSetType::Value& ReferenceType;
     typedef const typename LinkedHashSetType::Value* PointerType;
 
-    const Node* node() const { return static_cast<const Node*>(m_position); }
+    const Node* getNode() const { return static_cast<const Node*>(m_position); }
 
 protected:
     LinkedHashSetConstIterator(const LinkedHashSetNodeBase* position, const LinkedHashSetType* container)
diff --git a/third_party/WebKit/Source/wtf/ListHashSet.h b/third_party/WebKit/Source/wtf/ListHashSet.h
index bd15b8b..2f3ae61 100644
--- a/third_party/WebKit/Source/wtf/ListHashSet.h
+++ b/third_party/WebKit/Source/wtf/ListHashSet.h
@@ -468,7 +468,7 @@
     operator const_iterator() const { return m_iterator; }
 
 private:
-    Node* getNode() { return m_iterator.node(); }
+    Node* getNode() { return m_iterator.getNode(); }
 
     const_iterator m_iterator;
 
@@ -536,7 +536,7 @@
     }
 
 private:
-    Node* node() { return m_position; }
+    Node* getNode() { return m_position; }
 
     const Set* m_set;
     Node* m_position;
@@ -578,7 +578,7 @@
     operator const_reverse_iterator() const { return m_iterator; }
 
 private:
-    Node* node() { return m_iterator.node(); }
+    Node* getNode() { return m_iterator.node(); }
 
     const_reverse_iterator m_iterator;
 
@@ -645,7 +645,7 @@
     }
 
 private:
-    Node* node() { return m_position; }
+    Node* getNode() { return m_position; }
 
     const Set* m_set;
     Node* m_position;
diff --git a/third_party/WebKit/Source/wtf/WTFThreadData.h b/third_party/WebKit/Source/wtf/WTFThreadData.h
index 6f7b0d7..9ddda8f5 100644
--- a/third_party/WebKit/Source/wtf/WTFThreadData.h
+++ b/third_party/WebKit/Source/wtf/WTFThreadData.h
@@ -59,7 +59,7 @@
     WTFThreadData();
     ~WTFThreadData();
 
-    AtomicStringTable* atomicStringTable()
+    AtomicStringTable* getAtomicStringTable()
     {
         return m_atomicStringTable;
     }
diff --git a/third_party/WebKit/Source/wtf/text/AtomicString.cpp b/third_party/WebKit/Source/wtf/text/AtomicString.cpp
index 6d102b4..17e6c44 100644
--- a/third_party/WebKit/Source/wtf/text/AtomicString.cpp
+++ b/third_party/WebKit/Source/wtf/text/AtomicString.cpp
@@ -95,11 +95,11 @@
     HashSet<StringImpl*> m_table;
 };
 
-static inline AtomicStringTable& atomicStringTable()
+static inline AtomicStringTable& getAtomicStringTable()
 {
     // Once possible we should make this non-lazy (constructed in WTFThreadData's constructor).
     WTFThreadData& data = wtfThreadData();
-    AtomicStringTable* table = data.atomicStringTable();
+    AtomicStringTable* table = data.getAtomicStringTable();
     if (UNLIKELY(!table))
         table = AtomicStringTable::create(data);
     return *table;
@@ -107,12 +107,12 @@
 
 static inline HashSet<StringImpl*>& atomicStrings()
 {
-    return atomicStringTable().table();
+    return getAtomicStringTable().table();
 }
 
 void AtomicString::reserveTableCapacity(size_t size)
 {
-    atomicStringTable().table().reserveCapacityForSize(size);
+    getAtomicStringTable().table().reserveCapacityForSize(size);
 }
 
 template<typename T, typename HashTranslator>
@@ -371,7 +371,7 @@
 
 PassRefPtr<StringImpl> AtomicString::addSlowCase(StringImpl* string)
 {
-    return atomicStringTable().addStringImpl(string);
+    return getAtomicStringTable().addStringImpl(string);
 }
 
 template<typename CharacterType>
diff --git a/ui/base/clipboard/clipboard_mac.mm b/ui/base/clipboard/clipboard_mac.mm
index 08a6041..20cdb95 100644
--- a/ui/base/clipboard/clipboard_mac.mm
+++ b/ui/base/clipboard/clipboard_mac.mm
@@ -426,18 +426,7 @@
   base::scoped_nsobject<NSPasteboardItem> item(
       ClipboardUtil::PasteboardItemFromUrl(url, title));
   NSPasteboard* pb = GetPasteboard();
-
-  NSSet* oldTypes = [NSSet setWithArray:[pb types]];
-  NSMutableSet* newTypes = [NSMutableSet setWithArray:[item types]];
-  [newTypes minusSet:oldTypes];
-
-  [pb addTypes:[newTypes allObjects] owner:nil];
-  for (NSString* type in newTypes) {
-    // Technically, the object associated with |type| might be an NSString or a
-    // property list. It doesn't matter though, since the type gets pulled from
-    // and shoved into an NSDictionary.
-    [pb setData:[item dataForType:type] forType:type];
-  }
+  ui::ClipboardUtil::AddDataToPasteboard(pb, item);
 }
 
 void ClipboardMac::WriteBitmap(const SkBitmap& bitmap) {
diff --git a/ui/base/clipboard/clipboard_util_mac.h b/ui/base/clipboard/clipboard_util_mac.h
index 5d62cfa..1c4ff0a 100644
--- a/ui/base/clipboard/clipboard_util_mac.h
+++ b/ui/base/clipboard/clipboard_util_mac.h
@@ -35,6 +35,11 @@
       NSString* url,
       NSString* title);
 
+  // Returns an NSPasteboardItem that represents the given |urls| and |titles|.
+  static base::scoped_nsobject<NSPasteboardItem> PasteboardItemFromUrls(
+      NSArray* urls,
+      NSArray* titles);
+
   // Returns an NSPasteboardItem that represents the given string.
   // |string| must not be nil.
   static base::scoped_nsobject<NSPasteboardItem> PasteboardItemFromString(
@@ -44,6 +49,21 @@
   // url NSPasteboardItem.
   static NSString* GetTitleFromPasteboardURL(NSPasteboard* pboard);
   static NSString* GetURLFromPasteboardURL(NSPasteboard* pboard);
+
+  // Returns the UTI of a pasteboard type.
+  static NSString* UTIForPasteboardType(NSString* type);
+  static NSString* UTIForWebURLsAndTitles();
+
+  // For each pasteboard type in |item| that is not in |pboard|, add the type
+  // and its associated data.
+  static void AddDataToPasteboard(NSPasteboard* pboard, NSPasteboardItem* item);
+
+  // Returns whether the operation was succesful. On success, the two arrays are
+  // guaranteed to be equal length, and are populated with strings of |urls| and
+  // |titles|.
+  static bool URLsAndTitlesFromPasteboard(NSPasteboard* pboard,
+                                          NSArray** urls,
+                                          NSArray** titles);
 };
 }
 
diff --git a/ui/base/clipboard/clipboard_util_mac.mm b/ui/base/clipboard/clipboard_util_mac.mm
index cff59788..590ebf8 100644
--- a/ui/base/clipboard/clipboard_util_mac.mm
+++ b/ui/base/clipboard/clipboard_util_mac.mm
@@ -78,6 +78,20 @@
 }
 
 // static
+base::scoped_nsobject<NSPasteboardItem> ClipboardUtil::PasteboardItemFromUrls(
+    NSArray* urls,
+    NSArray* titles) {
+  base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]);
+
+  // Set Safari's URL + title arrays Pboard type.
+  NSArray* urlsAndTitles = @[ urls, titles ];
+  [item setPropertyList:urlsAndTitles
+                forType:UTIFromPboardType(kWebURLsWithTitlesPboardType)];
+
+  return item;
+}
+
+// static
 base::scoped_nsobject<NSPasteboardItem> ClipboardUtil::PasteboardItemFromString(
     NSString* string) {
   base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]);
@@ -97,4 +111,69 @@
       [pboard stringForType:UTIFromPboardType(kCorePasteboardFlavorType_url)];
 }
 
+// static
+NSString* ClipboardUtil::UTIForPasteboardType(NSString* type) {
+  return UTIFromPboardType(type);
+}
+
+// static
+NSString* ClipboardUtil::UTIForWebURLsAndTitles() {
+  return UTIFromPboardType(kWebURLsWithTitlesPboardType);
+}
+
+// static
+void ClipboardUtil::AddDataToPasteboard(NSPasteboard* pboard,
+                                        NSPasteboardItem* item) {
+  NSSet* oldTypes = [NSSet setWithArray:[pboard types]];
+  NSMutableSet* newTypes = [NSMutableSet setWithArray:[item types]];
+  [newTypes minusSet:oldTypes];
+
+  [pboard addTypes:[newTypes allObjects] owner:nil];
+  for (NSString* type in newTypes) {
+    // Technically, the object associated with |type| might be an NSString or a
+    // property list. It doesn't matter though, since the type gets pulled from
+    // and shoved into an NSDictionary.
+    [pboard setData:[item dataForType:type] forType:type];
+  }
+}
+
+// static
+bool ClipboardUtil::URLsAndTitlesFromPasteboard(NSPasteboard* pboard,
+                                                NSArray** urls,
+                                                NSArray** titles) {
+  NSArray* bookmarkPairs = base::mac::ObjCCast<NSArray>([pboard
+      propertyListForType:UTIFromPboardType(kWebURLsWithTitlesPboardType)]);
+  if (!bookmarkPairs)
+    return false;
+
+  if ([bookmarkPairs count] != 2)
+    return false;
+
+  NSArray* urlsArr =
+      base::mac::ObjCCast<NSArray>([bookmarkPairs objectAtIndex:0]);
+  NSArray* titlesArr =
+      base::mac::ObjCCast<NSArray>([bookmarkPairs objectAtIndex:1]);
+
+  if (!urlsArr || !titlesArr)
+    return false;
+  if ([urlsArr count] < 1)
+    return false;
+  if ([urlsArr count] != [titlesArr count])
+    return false;
+
+  for (id obj in urlsArr) {
+    if (![obj isKindOfClass:[NSString class]])
+      return false;
+  }
+
+  for (id obj in titlesArr) {
+    if (![obj isKindOfClass:[NSString class]])
+      return false;
+  }
+
+  *urls = urlsArr;
+  *titles = titlesArr;
+  return true;
+}
+
 }  // namespace ui