Merge commit '1940731af14148e5a80066fe0ff471f40b9182a5' into HEAD
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 3549929..0c430ec 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -27,6 +27,7 @@
 #include "content/public/common/url_constants.h"
 #include "grit/ui_resources.h"
 #include "net/android/network_library.h"
+#include "net/ssl/ssl_cert_request_info.h"
 #include "net/ssl/ssl_info.h"
 #include "ui/base/l10n/l10n_util_android.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -339,6 +340,18 @@
     *result = content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY;
 }
 
+void AwContentBrowserClient::SelectClientCertificate(
+      int render_process_id,
+      int render_view_id,
+      const net::HttpNetworkSession* network_session,
+      net::SSLCertRequestInfo* cert_request_info,
+      const base::Callback<void(net::X509Certificate*)>& callback) {
+  LOG(INFO) << "Client certificate request from "
+        << cert_request_info->host_and_port
+        << " rejected. (Client certificates not supported in WebView)";
+  callback.Run(NULL);
+}
+
 WebKit::WebNotificationPresenter::Permission
     AwContentBrowserClient::CheckDesktopNotificationPermission(
         const GURL& source_url,
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index 38a6b03..cf92d3a 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -103,6 +103,12 @@
       bool strict_enforcement,
       const base::Callback<void(bool)>& callback,
       content::CertificateRequestResultType* result) OVERRIDE;
+  virtual void SelectClientCertificate(
+      int render_process_id,
+      int render_view_id,
+      const net::HttpNetworkSession* network_session,
+      net::SSLCertRequestInfo* cert_request_info,
+      const base::Callback<void(net::X509Certificate*)>& callback) OVERRIDE;
   virtual WebKit::WebNotificationPresenter::Permission
       CheckDesktopNotificationPermission(
           const GURL& source_url,
diff --git a/android_webview/browser/aw_request_interceptor.cc b/android_webview/browser/aw_request_interceptor.cc
index 0e5e283..72673d2 100644
--- a/android_webview/browser/aw_request_interceptor.cc
+++ b/android_webview/browser/aw_request_interceptor.cc
@@ -24,27 +24,7 @@
 
 namespace {
 
-const void* kURLRequestUserDataKey = &kURLRequestUserDataKey;
-
-class URLRequestUserData : public base::SupportsUserData::Data {
- public:
-    URLRequestUserData(
-        scoped_ptr<InterceptedRequestData> intercepted_request_data)
-        : intercepted_request_data_(intercepted_request_data.Pass()) {
-    }
-
-    static URLRequestUserData* Get(net::URLRequest* request) {
-      return reinterpret_cast<URLRequestUserData*>(
-          request->GetUserData(kURLRequestUserDataKey));
-    }
-
-    const InterceptedRequestData* intercepted_request_data() const {
-      return intercepted_request_data_.get();
-    }
-
- private:
-  scoped_ptr<InterceptedRequestData> intercepted_request_data_;
-};
+const void* kRequestAlreadyQueriedDataKey = &kRequestAlreadyQueriedDataKey;
 
 } // namespace
 
@@ -82,26 +62,23 @@
   // request.
   // This is done not only for efficiency reasons, but also for correctness
   // as it is possible for the Interceptor chain to be invoked more than once
-  // (in which case we don't want to query the embedder multiple times).
-  URLRequestUserData* user_data = URLRequestUserData::Get(request);
+  // in which case we don't want to query the embedder multiple times.
+  // Note: The Interceptor chain is not invoked more than once if we create a
+  // URLRequestJob in this method, so this is only caching negative hits.
+  if (request->GetUserData(kRequestAlreadyQueriedDataKey))
+    return NULL;
+  request->SetUserData(kRequestAlreadyQueriedDataKey,
+                       new base::SupportsUserData::Data());
 
-  if (!user_data) {
-    // To ensure we only query the embedder once, we rely on the fact that the
-    // user_data object will be created and attached to the URLRequest after a
-    // call to QueryForInterceptedRequestData is made (regardless of whether
-    // the result of that call is a valid InterceptedRequestData* pointer or
-    // NULL.
-    user_data = new URLRequestUserData(
-        QueryForInterceptedRequestData(request->url(), request));
-    request->SetUserData(kURLRequestUserDataKey, user_data);
-  }
-
-  const InterceptedRequestData* intercepted_request_data =
-      user_data->intercepted_request_data();
+  scoped_ptr<InterceptedRequestData> intercepted_request_data =
+      QueryForInterceptedRequestData(request->url(), request);
 
   if (!intercepted_request_data)
     return NULL;
-  return intercepted_request_data->CreateJobFor(request, network_delegate);
+
+  // The newly created job will own the InterceptedRequestData.
+  return InterceptedRequestData::CreateJobFor(
+      intercepted_request_data.Pass(), request, network_delegate);
 }
 
 } // namespace android_webview
diff --git a/android_webview/browser/in_process_view_renderer.cc b/android_webview/browser/in_process_view_renderer.cc
index 5339e09..e384584 100644
--- a/android_webview/browser/in_process_view_renderer.cc
+++ b/android_webview/browser/in_process_view_renderer.cc
@@ -147,7 +147,7 @@
 
 // Used to calculate memory and resource allocation. Determined experimentally.
 size_t g_memory_multiplier = 10;
-const size_t kMaxNumTilesToFillDisplay = 20;
+size_t g_num_gralloc_limit = 150;
 const size_t kBytesPerPixel = 4;
 const size_t kMemoryAllocationStep = 5 * 1024 * 1024;
 
@@ -290,23 +290,29 @@
   if (cl->HasSwitch(switches::kTileMemoryMultiplier)) {
     std::string string_value =
         cl->GetSwitchValueASCII(switches::kTileMemoryMultiplier);
-    int int_value;
+    int int_value = 0;
     if (base::StringToInt(string_value, &int_value) &&
         int_value >= 2 && int_value <= 50) {
       g_memory_multiplier = int_value;
     }
   }
 
-  if (cl->HasSwitch(switches::kDefaultTileWidth) ||
-      cl->HasSwitch(switches::kDefaultTileHeight)) {
-    return;
+  if (cl->HasSwitch(switches::kNumGrallocBuffersPerWebview)) {
+    std::string string_value =
+        cl->GetSwitchValueASCII(switches::kNumGrallocBuffersPerWebview);
+    int int_value = 0;
+    if (base::StringToInt(string_value, &int_value) &&
+        int_value >= 50 && int_value <= 500) {
+      g_num_gralloc_limit = int_value;
+    }
   }
 
-  const int default_tile_size = 512;
-  std::stringstream size;
-  size << default_tile_size;
-  cl->AppendSwitchASCII(switches::kDefaultTileWidth, size.str());
-  cl->AppendSwitchASCII(switches::kDefaultTileHeight, size.str());
+  const char kDefaultTileSize[] = "384";
+  if (!cl->HasSwitch(switches::kDefaultTileWidth))
+    cl->AppendSwitchASCII(switches::kDefaultTileWidth, kDefaultTileSize);
+
+  if (!cl->HasSwitch(switches::kDefaultTileHeight))
+    cl->AppendSwitchASCII(switches::kDefaultTileHeight, kDefaultTileSize);
 }
 
 bool InProcessViewRenderer::RequestProcessGL() {
@@ -478,7 +484,7 @@
   // Round up to a multiple of kMemoryAllocationStep.
   policy.bytes_limit =
       (policy.bytes_limit / kMemoryAllocationStep + 1) * kMemoryAllocationStep;
-  policy.num_resources_limit = kMaxNumTilesToFillDisplay * g_memory_multiplier;
+  policy.num_resources_limit = g_num_gralloc_limit;
   SetMemoryPolicy(policy);
 
   DCHECK(gl_surface_);
@@ -630,6 +636,7 @@
 
 void InProcessViewRenderer::EnableOnNewPicture(bool enabled) {
   on_new_picture_enable_ = enabled;
+  EnsureContinuousInvalidation(NULL, false);
 }
 
 void InProcessViewRenderer::SetIsPaused(bool paused) {
@@ -886,8 +893,7 @@
   // This method should be called again when any of these conditions change.
   bool need_invalidate =
       compositor_needs_continuous_invalidate_ || invalidate_ignore_compositor;
-  bool throttle = is_paused_ || (attached_to_window_ && !window_visible_);
-  if (!need_invalidate || block_invalidates_ || throttle)
+  if (!need_invalidate || block_invalidates_)
     return;
 
   if (draw_info) {
@@ -900,6 +906,11 @@
     client_->PostInvalidate();
   }
 
+  bool throttle_fallback_tick = (is_paused_ && !on_new_picture_enable_) ||
+                                (attached_to_window_ && !window_visible_);
+  if (throttle_fallback_tick)
+    return;
+
   block_invalidates_ = compositor_needs_continuous_invalidate_;
 
   // Unretained here is safe because the callback is cancelled when
diff --git a/android_webview/browser/intercepted_request_data.h b/android_webview/browser/intercepted_request_data.h
index 672833c..c4e01c3 100644
--- a/android_webview/browser/intercepted_request_data.h
+++ b/android_webview/browser/intercepted_request_data.h
@@ -26,10 +26,11 @@
   // This creates a URLRequestJob for the |request| wich will read data from
   // the |intercepted_request_data| structure (instead of going to the network
   // or to the cache).
-  // The newly created job does not take ownership of |this|.
-  virtual net::URLRequestJob* CreateJobFor(
+  // The newly created job takes ownership of |intercepted_request_data|.
+  static net::URLRequestJob* CreateJobFor(
+      scoped_ptr<InterceptedRequestData> intercepted_request_data,
       net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const = 0;
+      net::NetworkDelegate* network_delegate);
 
  protected:
   InterceptedRequestData() {}
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
index 0650c54..9204181 100644
--- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
+++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
@@ -66,7 +66,6 @@
   virtual void WillStartRequest(bool* defer) OVERRIDE;
   virtual void WillRedirectRequest(const GURL& new_url, bool* defer) OVERRIDE;
 
-  bool MaybeDeferRequest(bool* defer);
   void OnIoThreadClientReady(int new_child_id, int new_route_id);
   bool MaybeBlockRequest();
   bool ShouldBlockRequest();
@@ -100,19 +99,7 @@
     return;
   }
   DCHECK(child_id_);
-  if (!MaybeDeferRequest(defer)) {
-    MaybeBlockRequest();
-  }
-}
-
-void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
-                                                 bool* defer) {
-  WillStartRequest(defer);
-}
-
-bool IoThreadClientThrottle::MaybeDeferRequest(bool* defer) {
   *defer = false;
-
   // Defer all requests of a pop up that is still not associated with Java
   // client so that the client will get a chance to override requests.
   scoped_ptr<AwContentsIoThreadClient> io_client =
@@ -121,8 +108,14 @@
     *defer = true;
     AwResourceDispatcherHostDelegate::AddPendingThrottle(
         child_id_, route_id_, this);
+  } else {
+    MaybeBlockRequest();
   }
-  return *defer;
+}
+
+void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
+                                                 bool* defer) {
+  WillStartRequest(defer);
 }
 
 void IoThreadClientThrottle::OnIoThreadClientReady(int new_child_id,
@@ -212,15 +205,12 @@
     int route_id,
     bool is_continuation_of_transferred_request,
     ScopedVector<content::ResourceThrottle>* throttles) {
-  // If io_client is NULL, then the browser side objects have already been
-  // destroyed, so do not do anything to the request. Conversely if the
-  // request relates to a not-yet-created popup window, then the client will
-  // be non-NULL but PopupPendingAssociation() will be set.
-  scoped_ptr<AwContentsIoThreadClient> io_client =
-      AwContentsIoThreadClient::FromID(child_id, route_id);
-  if (!io_client)
-    return;
 
+  // We always push the throttles here. Checking the existence of io_client
+  // is racy when a popup window is created. That is because RequestBeginning
+  // is called whether or not requests are blocked via BlockRequestForRoute()
+  // however io_client may or may not be ready at the time depending on whether
+  // webcontents is created.
   throttles->push_back(new IoThreadClientThrottle(
       child_id, route_id, request));
 
@@ -280,6 +270,19 @@
   return true;
 }
 
+bool AwResourceDispatcherHostDelegate::AcceptSSLClientCertificateRequest(
+    net::URLRequest* request,
+    net::SSLCertRequestInfo* cert_info) {
+  // WebView does not support client certificate selection, however it does
+  // send a no-certificate response to the server to allow it decide how to
+  // proceed. The base class returns false here, which causes the entire
+  // resource request to be abort. We don't want that, so we must return true
+  // here (and subsequently complete the request in
+  // AwContentBrowserClient::SelectClientCertificate) to get the intended
+  // behavior.
+  return true;
+}
+
 content::ResourceDispatcherHostLoginDelegate*
     AwResourceDispatcherHostDelegate::CreateLoginDelegate(
         net::AuthChallengeInfo* auth_info,
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h
index 8790ea3..949aa29 100644
--- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h
+++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h
@@ -49,6 +49,10 @@
       ScopedVector<content::ResourceThrottle>* throttles) OVERRIDE;
   virtual bool AcceptAuthRequest(net::URLRequest* request,
                                  net::AuthChallengeInfo* auth_info) OVERRIDE;
+  virtual bool AcceptSSLClientCertificateRequest(
+      net::URLRequest* request,
+      net::SSLCertRequestInfo* cert_info) OVERRIDE;
+
   virtual content::ResourceDispatcherHostLoginDelegate* CreateLoginDelegate(
       net::AuthChallengeInfo* auth_info,
       net::URLRequest* request) OVERRIDE;
diff --git a/android_webview/common/aw_switches.cc b/android_webview/common/aw_switches.cc
index b2663fa..216b4e3 100644
--- a/android_webview/common/aw_switches.cc
+++ b/android_webview/common/aw_switches.cc
@@ -12,4 +12,6 @@
 
 const char kTileMemoryMultiplier[] = "tile-memory-multiplier";
 
+const char kNumGrallocBuffersPerWebview[] = "num-gralloc-buffers-per-webview";
+
 }  // namespace switches
diff --git a/android_webview/common/aw_switches.h b/android_webview/common/aw_switches.h
index e855bb7..c718c05 100644
--- a/android_webview/common/aw_switches.h
+++ b/android_webview/common/aw_switches.h
@@ -17,6 +17,9 @@
 // displays that a single layer will have enough memory for.
 extern const char kTileMemoryMultiplier[];
 
+// Maximum number of gralloc allocations per webview.
+extern const char kNumGrallocBuffersPerWebview[];
+
 }  // namespace switches
 
 #endif  // ANDROID_WEBVIEW_COMMON_AW_SWITCHES_H_
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 09c337f..e521cde 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -4,6 +4,7 @@
 
 package org.chromium.android_webview;
 
+import android.app.Activity;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -52,6 +53,8 @@
 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
 import org.chromium.components.navigation_interception.NavigationParams;
 import org.chromium.net.GURLUtils;
+import org.chromium.ui.ActivityWindowAndroid;
+import org.chromium.ui.WindowAndroid;
 import org.chromium.ui.gfx.DeviceDisplayInfo;
 
 import java.io.File;
@@ -162,6 +165,7 @@
     private OverScrollGlow mOverScrollGlow;
     // This can be accessed on any thread after construction. See AwContentsIoThreadClient.
     private final AwSettings mSettings;
+    private final ScrollAccessibilityHelper mScrollAccessibilityHelper;
 
     private boolean mIsPaused;
     private boolean mIsViewVisible;
@@ -200,6 +204,9 @@
 
     private ComponentCallbacks2 mComponentCallbacks;
 
+    // This flag is to ShouldOverrideUrlNavigation through the resourcethrottle.
+    private boolean mDeferredShouldOverrideUrlLoadingIsPendingForPopup;
+
     private static final class DestroyRunnable implements Runnable {
         private int mNativeAwContents;
         private DestroyRunnable(int nativeAwContents) {
@@ -289,21 +296,40 @@
     }
 
     //--------------------------------------------------------------------------------------------
-    // Use this delegate only to post onPageStarted messages, since using WebContentsObserver
-    // causes bugs with existing applications. The three problems observed are stale URLs,
-    // out of order onPageStarted's and double onPageStarted's.
+    // When the navigation is for a newly created WebView (i.e. a popup), intercept the navigation
+    // here for implementing shouldOverrideUrlLoading. This is to send the shouldOverrideUrlLoading
+    // callback to the correct WebViewClient that is associated with the WebView.
+    // Otherwise, use this delegate only to post onPageStarted messages. The navigations are
+    // intercepted via handleNavigation API of ContentRendererClient and communicated via a SYNC
+    // IPC. This is to prevent cancelling XHRs that may have already started.
+    //
+    // As for posting the onPageStarted, we could have used WebContentsObserver. However using
+    // it causes these problems at the time: stale URLs, out of order onPageStarted's and double
+    // onPageStarted's.
+    //
     // TODO(sgurun) implementing onPageStarted via a resource throttle has a performance hit
     // waiting for UI thread. Let's try to find a non-resource-throttle solution.
     private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate {
         @Override
         public boolean shouldIgnoreNavigation(NavigationParams navigationParams) {
             final String url = navigationParams.url;
-            mContentsClient.getCallbackHelper().postOnPageStarted(url);
-            return false;
+            boolean ignoreNavigation = false;
+            if (mDeferredShouldOverrideUrlLoadingIsPendingForPopup) {
+                mDeferredShouldOverrideUrlLoadingIsPendingForPopup = false;
+                // If this is used for all navigations in future, cases for application initiated
+                // load, redirect and backforward should also be filtered out.
+                if (!navigationParams.isPost) {
+                    ignoreNavigation = mContentsClient.shouldOverrideUrlLoading(url);
+                }
+            }
+            if (!ignoreNavigation) {
+                mContentsClient.getCallbackHelper().postOnPageStarted(url);
+            }
+            return ignoreNavigation;
         }
     }
 
-    //--------------------------------------------------------------------------------------------
+    //-------------------------------------------`-------------------------------------------------
     private class AwLayoutSizerDelegate implements AwLayoutSizer.Delegate {
         @Override
         public void requestLayout() {
@@ -317,8 +343,15 @@
 
         @Override
         public void setFixedLayoutSize(int widthDip, int heightDip) {
+            if (mNativeAwContents == 0) return;
             nativeSetFixedLayoutSize(mNativeAwContents, widthDip, heightDip);
         }
+
+        @Override
+        public boolean isLayoutParamsHeightWrapContent() {
+            return mContainerView.getLayoutParams() != null &&
+                mContainerView.getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
+        }
     }
 
     //--------------------------------------------------------------------------------------------
@@ -380,7 +413,6 @@
             mScrollOffsetManager.onFlingStartGesture(velocityX, velocityY);
         }
 
-
         @Override
         public void onFlingCancelGesture() {
             mScrollOffsetManager.onFlingCancelGesture();
@@ -390,6 +422,11 @@
         public void onUnhandledFlingStartEvent() {
             mScrollOffsetManager.onUnhandledFlingStartEvent();
         }
+
+        @Override
+        public void onScrollUpdateGestureConsumed() {
+            mScrollAccessibilityHelper.postViewScrolledAccessibilityEventCallback();
+        }
     }
 
     //--------------------------------------------------------------------------------------------
@@ -447,10 +484,13 @@
             ContentViewCore.GestureStateListener pinchGestureStateListener,
             ContentViewClient contentViewClient,
             ContentViewCore.ZoomControlsDelegate zoomControlsDelegate) {
-      ContentViewCore contentViewCore = new ContentViewCore(containerView.getContext());
+      Context context = containerView.getContext();
+      ContentViewCore contentViewCore = new ContentViewCore(context);
       // Note INPUT_EVENTS_DELIVERED_IMMEDIATELY is passed to avoid triggering vsync in the
       // compositor, not because input events are delivered immediately.
-      contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents, null,
+      contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents,
+              context instanceof Activity ?
+                      new ActivityWindowAndroid((Activity) context) : new WindowAndroid(context),
                 ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY);
       contentViewCore.setGestureStateListener(pinchGestureStateListener);
       contentViewCore.setContentViewClient(contentViewClient);
@@ -507,6 +547,7 @@
         mSettings.setDIPScale(mDIPScale);
         mScrollOffsetManager = new AwScrollOffsetManager(new AwScrollOffsetManagerDelegate(),
                 new OverScroller(mContainerView.getContext()));
+        mScrollAccessibilityHelper = new ScrollAccessibilityHelper(mContainerView);
 
         setOverScrollMode(mContainerView.getOverScrollMode());
         setScrollBarStyle(mInternalAccessAdapter.super_getScrollBarStyle());
@@ -549,7 +590,7 @@
                 mZoomControls);
         nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge,
                 mIoThreadClient, mInterceptNavigationDelegate);
-        mContentsClient.installWebContentsObserver(mContentViewCore);
+        mContentsClient.installWebContentsObserver(mContentViewCore, mSettings);
         mSettings.setWebContents(nativeWebContents);
         nativeSetDipScale(mNativeAwContents, (float) mDIPScale);
         updateGlobalVisibleRect();
@@ -580,6 +621,7 @@
     // Recap: supplyContentsForPopup() is called on the parent window's content, this method is
     // called on the popup window's content.
     private void receivePopupContents(int popupNativeAwContents) {
+        mDeferredShouldOverrideUrlLoadingIsPendingForPopup = true;
         // Save existing view state.
         final boolean wasAttached = mIsAttachedToWindow;
         final boolean wasViewVisible = mIsViewVisible;
@@ -803,6 +845,7 @@
     }
 
     private void syncOnNewPictureStateToNative() {
+        if (mNativeAwContents == 0) return;
         nativeEnableOnNewPicture(mNativeAwContents, mPictureListenerEnabled || mClearViewActive);
     }
 
@@ -1001,6 +1044,10 @@
      * @see View#onScrollChanged(int,int)
      */
     public void onContainerViewScrollChanged(int l, int t, int oldl, int oldt) {
+        // A side-effect of View.onScrollChanged is that the scroll accessibility event being sent
+        // by the base class implementation. This is completely hidden from the base classes and
+        // cannot be prevented, which is why we need the code below.
+        mScrollAccessibilityHelper.removePostedViewScrolledAccessibilityEventCallback();
         mScrollOffsetManager.onContainerViewScrollChanged(l, t);
     }
 
@@ -1183,10 +1230,27 @@
         return mContentViewCore.onKeyUp(keyCode, event);
     }
 
+    private boolean isDpadEvent(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            switch (event.getKeyCode()) {
+                case KeyEvent.KEYCODE_DPAD_CENTER:
+                case KeyEvent.KEYCODE_DPAD_DOWN:
+                case KeyEvent.KEYCODE_DPAD_UP:
+                case KeyEvent.KEYCODE_DPAD_LEFT:
+                case KeyEvent.KEYCODE_DPAD_RIGHT:
+                    return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * @see android.webkit.WebView#dispatchKeyEvent(KeyEvent)
      */
     public boolean dispatchKeyEvent(KeyEvent event) {
+        if (isDpadEvent(event)) {
+            mSettings.setSpatialNavigationEnabled(true);
+        }
         return mContentViewCore.dispatchKeyEvent(event);
     }
 
@@ -1445,6 +1509,10 @@
     public boolean onTouchEvent(MotionEvent event) {
         if (mNativeAwContents == 0) return false;
 
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            mSettings.setSpatialNavigationEnabled(false);
+        }
+
         mScrollOffsetManager.setProcessingTouchEvent(true);
         boolean rv = mContentViewCore.onTouchEvent(event);
         mScrollOffsetManager.setProcessingTouchEvent(false);
@@ -1521,6 +1589,8 @@
           mComponentCallbacks = null;
         }
 
+        mScrollAccessibilityHelper.removePostedCallbacks();
+
         if (mPendingDetachCleanupReferences != null) {
             for (int i = 0; i < mPendingDetachCleanupReferences.size(); ++i) {
                 mPendingDetachCleanupReferences.get(i).cleanupNow();
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java
index e856586..a3070ba 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java
@@ -20,6 +20,7 @@
 import android.webkit.ConsoleMessage;
 import android.webkit.GeolocationPermissions;
 import android.webkit.SslErrorHandler;
+import android.webkit.URLUtil;
 import android.webkit.ValueCallback;
 import android.webkit.WebChromeClient;
 
@@ -57,6 +58,8 @@
 
     private static final int INVALID_COLOR = 0;
 
+    private AwSettings mSettings;
+
     class AwWebContentsObserver extends WebContentsObserverAndroid {
         public AwWebContentsObserver(ContentViewCore contentViewCore) {
             super(contentViewCore);
@@ -133,13 +136,20 @@
         final public ContentVideoViewClient getContentVideoViewClient() {
             return new AwContentVideoViewClient();
         }
+
+        @Override
+        public boolean shouldBlockMediaRequest(String url) {
+            return mSettings != null ?
+                    mSettings.getBlockNetworkLoads() && URLUtil.isNetworkUrl(url) : true;
+        }
     }
 
-    final void installWebContentsObserver(ContentViewCore contentViewCore) {
+    final void installWebContentsObserver(ContentViewCore contentViewCore, AwSettings settings) {
         if (mWebContentsObserver != null) {
             mWebContentsObserver.detachFromWebContents();
         }
         mWebContentsObserver = new AwWebContentsObserver(contentViewCore);
+        mSettings = settings;
     }
 
     private class AwContentVideoViewClient implements ContentVideoViewClient {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwLayoutSizer.java b/android_webview/java/src/org/chromium/android_webview/AwLayoutSizer.java
index 527c39e..3b996a0 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwLayoutSizer.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwLayoutSizer.java
@@ -58,6 +58,7 @@
         void requestLayout();
         void setMeasuredDimension(int measuredWidth, int measuredHeight);
         void setFixedLayoutSize(int widthDip, int heightDip);
+        boolean isLayoutParamsHeightWrapContent();
     }
 
     /**
@@ -225,14 +226,34 @@
     // call from onSizeChanged, since onSizeChanged won't fire if the view's physical size doesn't
     // change.
     private void updateFixedLayoutSize(int w, int h, float pageScaleFactor) {
-        // If the WebView's measuredDimension depends on the size of it's contents (which is the
-        // case if any of the measurement modes are AT_MOST or UNSPECIFIED) the viewport size
-        // cannot be directly calculated from the size as that can result in the layout being
-        // unstable or unpredictable.
-        // If both the width and height are fixed (specified by the parent) then content size
+        boolean wrapContentForHeight = mDelegate.isLayoutParamsHeightWrapContent();
+        // If the WebView's size in the Android view system depends on the size of its contents then
+        // the viewport size cannot be directly calculated from the WebView's physical size as that
+        // can result in the layout being unstable (for example loading the following contents
+        //   <div style="height:150%">a</a>
+        // would cause the WebView to indefinitely attempt to increase its height by 50%).
+        // If both the width and height are fixed (specified by the parent View) then content size
         // changes will not cause subsequent layout passes and so we don't need to do anything
         // special.
-        if ((mWidthMeasurementIsFixed && mHeightMeasurementIsFixed) || pageScaleFactor == 0) {
+        // We assume the width is 'fixed' if the parent View specified an EXACT or an AT_MOST
+        // measureSpec for the width (in which case the AT_MOST upper bound is the width).
+        // That means that the WebView will ignore LayoutParams.width set to WRAP_CONTENT and will
+        // instead try to take up as much width as possible. This is necessary because it's not
+        // practical to do web layout without a set width.
+        // For height the behavior is different because for a given width it is possible to
+        // calculate the minimum height required to display all of the content. As such the WebView
+        // can size itself vertically to match the content height. Because certain container views
+        // (LinearLayout with a WRAP_CONTENT height, for example) can result in onMeasure calls with
+        // both EXACTLY and AT_MOST height measureSpecs it is not possible to infer the sizing
+        // policy for the whole subtree based on the parameters passed to the onMeasure call.
+        // For that reason the LayoutParams.height property of the WebView is used. This behaves
+        // more predictably and means that toggling the fixedLayoutSize mode (which can have
+        // significant impact on how the web contents is laid out) is a direct consequence of the
+        // developer's choice. The downside is that it could result in the Android layout being
+        // unstable if a parent of the WebView has a wrap_content height while the WebView itself
+        // has height set to match_parent. Unfortunately addressing this edge case is costly so it
+        // will have to stay as is (this is compatible with Classic behavior).
+        if ((mWidthMeasurementIsFixed && !wrapContentForHeight) || pageScaleFactor == 0) {
             setFixedLayoutSize(0, 0);
             return;
         }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
index d9a979a..0a7452b 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
@@ -84,6 +84,7 @@
     private boolean mMediaPlaybackRequiresUserGesture = true;
     private String mDefaultVideoPosterURL;
     private float mInitialPageScalePercent = 0;
+    private boolean mSpatialNavigationEnabled;  // Default depends on device features.
 
     private final boolean mSupportLegacyQuirks;
 
@@ -210,6 +211,10 @@
 
             mUserAgent = LazyDefaultUserAgent.sInstance;
 
+            // Best-guess a sensible initial value based on the features supported on the device.
+            mSpatialNavigationEnabled = !context.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_TOUCHSCREEN);
+
             // Respect the system setting for password echoing.
             mPasswordEchoEnabled = Settings.System.getInt(context.getContentResolver(),
                     Settings.System.TEXT_SHOW_PASSWORD, 1) == 1;
@@ -374,6 +379,20 @@
         return mInitialPageScalePercent;
     }
 
+    void setSpatialNavigationEnabled(boolean enable) {
+        synchronized (mAwSettingsLock) {
+            if (mSpatialNavigationEnabled != enable) {
+                mSpatialNavigationEnabled = enable;
+                mEventHandler.updateWebkitPreferencesLocked();
+            }
+        }
+    }
+
+    @CalledByNative
+    private boolean getSpatialNavigationLocked() {
+        return mSpatialNavigationEnabled;
+    }
+
     /**
      * See {@link android.webkit.WebSettings#setNeedInitialFocus}.
      */
@@ -1100,7 +1119,7 @@
     }
 
     @CalledByNative
-    private boolean getPasswordEchoEnabled() {
+    private boolean getPasswordEchoEnabledLocked() {
         return mPasswordEchoEnabled;
     }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/ScrollAccessibilityHelper.java b/android_webview/java/src/org/chromium/android_webview/ScrollAccessibilityHelper.java
new file mode 100644
index 0000000..d866dc9
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/ScrollAccessibilityHelper.java
@@ -0,0 +1,79 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview;
+
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * Helper used to post the VIEW_SCROLLED accessibility event.
+ *
+ * TODO(mkosiba): Investigate whether this is behavior we want to share with the chrome/ layer.
+ * TODO(mkosiba): We currently don't handle JS-initiated scrolling for layers other than the root
+ * layer.
+ */
+class ScrollAccessibilityHelper {
+    // This is copied straight out of android.view.ViewConfiguration.
+    private static final long SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS = 100;
+
+    private class HandlerCallback implements Handler.Callback {
+        public static final int MSG_VIEW_SCROLLED = 1;
+
+        private View mEventSender;
+
+        public HandlerCallback(View eventSender) {
+            mEventSender = eventSender;
+        }
+
+        @Override
+        public boolean handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_VIEW_SCROLLED:
+                    mMsgViewScrolledQueued = false;
+                    mEventSender.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+                    break;
+                default:
+                    throw new IllegalStateException(
+                            "AccessibilityInjector: unhandled message: " + msg.what);
+            }
+            return true;
+        }
+    }
+
+    private Handler mHandler;
+    private boolean mMsgViewScrolledQueued;
+
+    public ScrollAccessibilityHelper(View eventSender) {
+        mHandler = new Handler(new HandlerCallback(eventSender));
+    }
+
+    /**
+     * Post a callback to send a {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
+     * This event is sent at most once every
+     * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
+     */
+    public void postViewScrolledAccessibilityEventCallback() {
+        if (mMsgViewScrolledQueued)
+            return;
+        mMsgViewScrolledQueued = true;
+
+        Message msg = mHandler.obtainMessage(HandlerCallback.MSG_VIEW_SCROLLED);
+        mHandler.sendMessageDelayed(msg, SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS);
+    }
+
+    public void removePostedViewScrolledAccessibilityEventCallback() {
+        if (!mMsgViewScrolledQueued)
+            return;
+        mMsgViewScrolledQueued = false;
+
+        mHandler.removeMessages(HandlerCallback.MSG_VIEW_SCROLLED);
+    }
+
+    public void removePostedCallbacks() {
+        removePostedViewScrolledAccessibilityEventCallback();
+    }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
index 2dad113..08c6c08 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
@@ -280,9 +280,6 @@
         final int targetScrollYPix = (int) Math.ceil(targetScrollYCss * deviceDIPScale);
         final JavascriptEventObserver onscrollObserver = new JavascriptEventObserver();
 
-        Log.w("AndroidScrollIntegrationTest", String.format("scroll in Js (%d, %d) -> (%d, %d)",
-                    targetScrollXCss, targetScrollYCss, targetScrollXPix, targetScrollYPix));
-
         getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
@@ -691,4 +688,77 @@
                 break;
         }
     }
+
+    private static class TestGestureStateListener implements ContentViewCore.GestureStateListener {
+        private CallbackHelper mOnScrollUpdateGestureConsumedHelper = new CallbackHelper();
+
+        public CallbackHelper getOnScrollUpdateGestureConsumedHelper() {
+            return mOnScrollUpdateGestureConsumedHelper;
+        }
+
+        @Override
+        public void onPinchGestureStart() {
+        }
+
+        @Override
+        public void onPinchGestureEnd() {
+        }
+
+        @Override
+        public void onFlingStartGesture(int velocityX, int velocityY) {
+        }
+
+        @Override
+        public void onFlingCancelGesture() {
+        }
+
+        @Override
+        public void onUnhandledFlingStartEvent() {
+        }
+
+        @Override
+        public void onScrollUpdateGestureConsumed() {
+            mOnScrollUpdateGestureConsumedHelper.notifyCalled();
+        }
+    }
+
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testTouchScrollingConsumesScrollByGesture() throws Throwable {
+        final TestAwContentsClient contentsClient = new TestAwContentsClient();
+        final ScrollTestContainerView testContainerView =
+            (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient);
+        final TestGestureStateListener testGestureStateListener = new TestGestureStateListener();
+        enableJavaScriptOnUiThread(testContainerView.getAwContents());
+
+        final int dragSteps = 10;
+        final int dragStepSize = 24;
+        // Watch out when modifying - if the y or x delta aren't big enough vertical or horizontal
+        // scroll snapping will kick in.
+        final int targetScrollXPix = dragStepSize * dragSteps;
+        final int targetScrollYPix = dragStepSize * dragSteps;
+
+        loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, null,
+                "<div>" +
+                "  <div style=\"width:10000px; height: 10000px;\"> force scrolling </div>" +
+                "</div>");
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                testContainerView.getContentViewCore().setGestureStateListener(
+                        testGestureStateListener);
+            }
+        });
+        final CallbackHelper onScrollUpdateGestureConsumedHelper =
+            testGestureStateListener.getOnScrollUpdateGestureConsumedHelper();
+
+        final int callCount = onScrollUpdateGestureConsumedHelper.getCallCount();
+        AwTestTouchUtils.dragCompleteView(testContainerView,
+                0, -targetScrollXPix, // these need to be negative as we're scrolling down.
+                0, -targetScrollYPix,
+                dragSteps,
+                null /* completionLatch */);
+        onScrollUpdateGestureConsumedHelper.waitForCallback(callCount);
+    }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
index 8e4cef5..83db1db 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
@@ -25,7 +25,9 @@
 import java.io.InputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
 import java.util.List;
 import java.util.Random;
 
@@ -255,6 +257,70 @@
         mContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
     }
 
+    private static class SlowInterceptedRequestData extends InterceptedRequestData {
+        private CallbackHelper mReadStartedCallbackHelper = new CallbackHelper();
+        private CountDownLatch mLatch = new CountDownLatch(1);
+
+        public SlowInterceptedRequestData(String mimeType, String encoding, InputStream data) {
+            super(mimeType, encoding, data);
+        }
+
+        @Override
+        public InputStream getData() {
+            mReadStartedCallbackHelper.notifyCalled();
+            try {
+                mLatch.await();
+            } catch (InterruptedException e) {
+                // ignore
+            }
+            return super.getData();
+        }
+
+        public void unblockReads() {
+            mLatch.countDown();
+        }
+
+        public CallbackHelper getReadStartedCallbackHelper() {
+            return mReadStartedCallbackHelper;
+        }
+    }
+
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testDoesNotCrashOnSlowStream() throws Throwable {
+        final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
+        final String aboutPageData = makePageWithTitle("some title");
+        final String encoding = "UTF-8";
+        final SlowInterceptedRequestData slowInterceptedRequestData =
+            new SlowInterceptedRequestData("text/html", encoding,
+                    new ByteArrayInputStream(aboutPageData.getBytes(encoding)));
+
+        mShouldInterceptRequestHelper.setReturnValue(slowInterceptedRequestData);
+        int callCount = slowInterceptedRequestData.getReadStartedCallbackHelper().getCallCount();
+        loadUrlAsync(mAwContents, aboutPageUrl);
+        slowInterceptedRequestData.getReadStartedCallbackHelper().waitForCallback(callCount);
+
+        // Now the AwContents is "stuck" waiting for the SlowInputStream to finish reading so we
+        // delete it to make sure that the dangling 'read' task doesn't cause a crash. Unfortunately
+        // this will not always lead to a crash but it should happen often enough for us to notice.
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                getActivity().removeAllViews();
+            }
+        });
+        destroyAwContentsOnMainSync(mAwContents);
+        pollOnUiThread(new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return AwContents.getNativeInstanceCount() == 0;
+            }
+        });
+
+        slowInterceptedRequestData.unblockReads();
+    }
+
     @SmallTest
     @Feature({"AndroidWebView"})
     public void testHttpStatusField() throws Throwable {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwLayoutSizerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwLayoutSizerTest.java
index 5d8b56b..357e922 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwLayoutSizerTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwLayoutSizerTest.java
@@ -20,6 +20,7 @@
         public int measuredHeight;
         public int fixedLayoutWidth;
         public int fixedLayoutHeight;
+        public boolean heightWrapContent;
 
         @Override
         public void requestLayout() {
@@ -38,6 +39,11 @@
             fixedLayoutWidth = widthDip;
             fixedLayoutHeight = heightDip;
         }
+
+        @Override
+        public boolean isLayoutParamsHeightWrapContent() {
+            return heightWrapContent;
+        }
     }
 
     private static final int FIRST_CONTENT_WIDTH = 101;
@@ -382,7 +388,7 @@
 
     @SmallTest
     @Feature({"AndroidWebView"})
-    public void testViewportWithWrapContentMeasureSpec() {
+    public void testViewportWithUnspecifiedMeasureSpec() {
         AwLayoutSizer layoutSizer = new AwLayoutSizer();
         LayoutSizerDelegate delegate = new LayoutSizerDelegate();
         layoutSizer.setDelegate(delegate);
@@ -391,7 +397,6 @@
         final int pageScale = 2;
         final int dipAndPageScale = (int) (dipScale * pageScale);
 
-
         int contentWidth = 800;
         int contentHeight = 400;
         int atMostWidth = contentWidth * dipAndPageScale;
@@ -431,6 +436,41 @@
 
     @SmallTest
     @Feature({"AndroidWebView"})
+    public void testViewportWithAtMostMeasureSpec() {
+        AwLayoutSizer layoutSizer = new AwLayoutSizer();
+        LayoutSizerDelegate delegate = new LayoutSizerDelegate();
+        delegate.heightWrapContent = true;
+        layoutSizer.setDelegate(delegate);
+
+        final float dipScale = 1.5f;
+        final int pageScale = 2;
+        final int dipAndPageScale = (int) (dipScale * pageScale);
+
+        int contentWidth = 800;
+        int contentHeight = 400;
+        int contentWidthPix = contentWidth * dipAndPageScale;
+        int contentHeightPix = contentHeight * dipAndPageScale;
+
+        layoutSizer.setDIPScale(dipScale);
+        layoutSizer.onContentSizeChanged(contentWidth, contentHeight);
+        layoutSizer.onPageScaleChanged(pageScale);
+        layoutSizer.onMeasure(MeasureSpec.makeMeasureSpec(contentWidthPix, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(contentHeightPix * 2, MeasureSpec.AT_MOST));
+
+        assertTrue(delegate.setMeasuredDimensionCalled);
+        int measuredWidth = delegate.measuredWidth & View.MEASURED_SIZE_MASK;
+        int measuredHeight = delegate.measuredHeight & View.MEASURED_SIZE_MASK;
+
+        int sizeWidth = measuredWidth;
+        int sizeHeight = measuredHeight;
+        layoutSizer.onSizeChanged(sizeWidth, sizeHeight, 0, 0);
+
+        assertEquals(contentWidth, delegate.fixedLayoutWidth);
+        assertEquals(AwLayoutSizer.FIXED_LAYOUT_HEIGHT, delegate.fixedLayoutHeight);
+    }
+
+    @SmallTest
+    @Feature({"AndroidWebView"})
     public void testFixedLayoutViewportGoesBackToZeroWithWrapContentMeasureSpec() {
         AwLayoutSizer layoutSizer = new AwLayoutSizer();
         LayoutSizerDelegate delegate = new LayoutSizerDelegate();
@@ -465,6 +505,7 @@
     public void testFixedLayoutSizeUpdatedOnPageScaleChangeItNoLayoutRequest() {
         AwLayoutSizer layoutSizer = new AwLayoutSizer();
         LayoutSizerDelegate delegate = new LayoutSizerDelegate();
+        delegate.heightWrapContent = true;
         layoutSizer.setDelegate(delegate);
         layoutSizer.setDIPScale(DIP_SCALE);
 
@@ -514,4 +555,24 @@
 
         assertEquals(fixedLayoutWidth * 2, delegate.fixedLayoutWidth);
     }
+
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testFixedLayoutSizeDoesNotDependOnMeasureSpec() {
+        AwLayoutSizer layoutSizer = new AwLayoutSizer();
+        LayoutSizerDelegate delegate = new LayoutSizerDelegate();
+        delegate.heightWrapContent = false;
+        layoutSizer.setDelegate(delegate);
+        layoutSizer.setDIPScale(DIP_SCALE);
+
+        layoutSizer.onContentSizeChanged(TOO_LARGE_CONTENT_SIZE, TOO_LARGE_CONTENT_SIZE);
+        layoutSizer.onPageScaleChanged(INITIAL_PAGE_SCALE);
+        layoutSizer.onMeasure(
+                MeasureSpec.makeMeasureSpec(AT_MOST_MEASURE_SIZE, MeasureSpec.AT_MOST),
+                MeasureSpec.makeMeasureSpec(AT_MOST_MEASURE_SIZE, MeasureSpec.AT_MOST));
+        layoutSizer.onSizeChanged(AT_MOST_MEASURE_SIZE, AT_MOST_MEASURE_SIZE, 0, 0);
+
+        assertEquals(0, delegate.fixedLayoutWidth);
+        assertEquals(0, delegate.fixedLayoutHeight);
+    }
 }
diff --git a/android_webview/lib/main/aw_main_delegate.cc b/android_webview/lib/main/aw_main_delegate.cc
index 9537a6d..c951b21 100644
--- a/android_webview/lib/main/aw_main_delegate.cc
+++ b/android_webview/lib/main/aw_main_delegate.cc
@@ -73,9 +73,6 @@
   // File system API not supported (requires some new API; internal bug 6930981)
   cl->AppendSwitch(switches::kDisableFileSystem);
 
-  // Enable D-PAD navigation for application compatibility.
-  cl->AppendSwitch(switches::kEnableSpatialNavigation);
-
   // Disable compositor touch hit testing for now to mitigate risk of bugs.
   cl->AppendSwitch(cc::switches::kDisableCompositorTouchHitTesting);
 
diff --git a/android_webview/native/aw_autofill_manager_delegate.cc b/android_webview/native/aw_autofill_manager_delegate.cc
index 37640cb..1f3a546 100644
--- a/android_webview/native/aw_autofill_manager_delegate.cc
+++ b/android_webview/native/aw_autofill_manager_delegate.cc
@@ -141,7 +141,9 @@
 void AwAutofillManagerDelegate::UpdateAutofillPopupDataListValues(
     const std::vector<base::string16>& values,
     const std::vector<base::string16>& labels) {
-  NOTIMPLEMENTED();
+  // Leaving as an empty method since updating autofill popup window
+  // dynamically does not seem to be a useful feature for android webview.
+  // See crrev.com/18102002 if need to implement.
 }
 
 void AwAutofillManagerDelegate::HideAutofillPopup() {
diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc
index e707d0b..6670432 100644
--- a/android_webview/native/aw_contents.cc
+++ b/android_webview/native/aw_contents.cc
@@ -191,7 +191,6 @@
                              new AwContentsUserData(this));
   render_view_host_ext_.reset(
       new AwRenderViewHostExt(this, web_contents_.get()));
-  AwContentsIoThreadClientImpl::RegisterPendingContents(web_contents_.get());
 
   AwAutofillManagerDelegate* autofill_manager_delegate =
       AwAutofillManagerDelegate::FromWebContents(web_contents_.get());
@@ -224,15 +223,16 @@
 
   AwContentsIoThreadClientImpl::Associate(
       web_contents_.get(), ScopedJavaLocalRef<jobject>(env, io_thread_client));
-  int child_id = web_contents_->GetRenderProcessHost()->GetID();
-  int route_id = web_contents_->GetRoutingID();
-  AwResourceDispatcherHostDelegate::OnIoThreadClientReady(child_id, route_id);
 
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   InterceptNavigationDelegate::Associate(
       web_contents_.get(),
       make_scoped_ptr(new InterceptNavigationDelegate(
           env, intercept_navigation_delegate)));
+
+  // Finally, having setup the associations, release any deferred requests
+  int child_id = web_contents_->GetRenderProcessHost()->GetID();
+  int route_id = web_contents_->GetRoutingID();
+  AwResourceDispatcherHostDelegate::OnIoThreadClientReady(child_id, route_id);
 }
 
 void AwContents::SetSaveFormData(bool enabled) {
diff --git a/android_webview/native/aw_pdf_exporter.cc b/android_webview/native/aw_pdf_exporter.cc
index f8e6210..5699d2e 100644
--- a/android_webview/native/aw_pdf_exporter.cc
+++ b/android_webview/native/aw_pdf_exporter.cc
@@ -95,6 +95,7 @@
   margins.bottom =
       MilsToDots(Java_AwPdfExporter_getBottomMargin(env, obj), dpi);
   print_settings_->SetCustomMargins(margins);
+  print_settings_->should_print_backgrounds = true;
 }
 
 void AwPdfExporter::DidExportPdf(bool success) {
diff --git a/android_webview/native/aw_settings.cc b/android_webview/native/aw_settings.cc
index e4aa588..f227ddc 100644
--- a/android_webview/native/aw_settings.cc
+++ b/android_webview/native/aw_settings.cc
@@ -315,7 +315,9 @@
   web_prefs->report_screen_size_in_physical_pixels_quirk = support_quirks;
 
   web_prefs->password_echo_enabled =
-      Java_AwSettings_getPasswordEchoEnabled(env, obj);
+      Java_AwSettings_getPasswordEchoEnabledLocked(env, obj);
+  web_prefs->spatial_navigation_enabled =
+      Java_AwSettings_getSpatialNavigationLocked(env, obj);
 }
 
 static jint Init(JNIEnv* env,
diff --git a/android_webview/native/aw_web_contents_delegate.cc b/android_webview/native/aw_web_contents_delegate.cc
index 0acbbb4..e763637 100644
--- a/android_webview/native/aw_web_contents_delegate.cc
+++ b/android_webview/native/aw_web_contents_delegate.cc
@@ -7,6 +7,7 @@
 #include "android_webview/browser/aw_javascript_dialog_manager.h"
 #include "android_webview/browser/find_helper.h"
 #include "android_webview/native/aw_contents.h"
+#include "android_webview/native/aw_contents_io_thread_client_impl.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
@@ -158,6 +159,17 @@
   }
 }
 
+// Notifies the delegate about the creation of a new WebContents. This
+// typically happens when popups are created.
+void AwWebContentsDelegate::WebContentsCreated(WebContents* source_contents,
+                                               int64 source_frame_id,
+                                               const string16& frame_name,
+                                               const GURL& target_url,
+                                               content::WebContents* new_contents) {
+  AwContentsIoThreadClientImpl::RegisterPendingContents(new_contents);
+}
+
+
 void AwWebContentsDelegate::CloseContents(WebContents* source) {
   JNIEnv* env = AttachCurrentThread();
 
diff --git a/android_webview/native/aw_web_contents_delegate.h b/android_webview/native/aw_web_contents_delegate.h
index 8dc50ef..3a597bf 100644
--- a/android_webview/native/aw_web_contents_delegate.h
+++ b/android_webview/native/aw_web_contents_delegate.h
@@ -40,6 +40,14 @@
                               const gfx::Rect& initial_pos,
                               bool user_gesture,
                               bool* was_blocked) OVERRIDE;
+
+  virtual void WebContentsCreated(content::WebContents* source_contents,
+                                  int64 source_frame_id,
+                                  const string16& frame_name,
+                                  const GURL& target_url,
+                                  content::WebContents* new_contents) OVERRIDE;
+
+
   virtual void CloseContents(content::WebContents* source) OVERRIDE;
   virtual void ActivateContents(content::WebContents* contents) OVERRIDE;
 };
diff --git a/android_webview/native/cookie_manager.cc b/android_webview/native/cookie_manager.cc
index cb65d41..f688ce5 100644
--- a/android_webview/native/cookie_manager.cc
+++ b/android_webview/native/cookie_manager.cc
@@ -45,7 +45,7 @@
 // See issue http://crbug.com/157683
 
 // All functions on the CookieManager can be called from any thread, including
-// threads without a message loop. BrowserThread::FILE is used to call methods
+// threads without a message loop. BrowserThread::IO is used to call methods
 // on CookieMonster that needs to be called, and called back, on a chrome
 // thread.
 
@@ -265,7 +265,7 @@
   DCHECK(!cookie_monster_.get());
 
   cookie_monster_proxy_ =
-      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
+      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
   scoped_refptr<base::SequencedTaskRunner> background_task_runner =
       BrowserThread::GetBlockingPool()->GetSequencedTaskRunner(
           BrowserThread::GetBlockingPool()->GetSequenceToken());
diff --git a/android_webview/native/intercepted_request_data_impl.cc b/android_webview/native/intercepted_request_data_impl.cc
index dc5fe22..03b9f42 100644
--- a/android_webview/native/intercepted_request_data_impl.cc
+++ b/android_webview/native/intercepted_request_data_impl.cc
@@ -22,8 +22,8 @@
     public AndroidStreamReaderURLRequestJob::Delegate {
  public:
     StreamReaderJobDelegateImpl(
-        const InterceptedRequestDataImpl* intercepted_request_data)
-        : intercepted_request_data_impl_(intercepted_request_data) {
+        scoped_ptr<InterceptedRequestDataImpl> intercepted_request_data)
+        : intercepted_request_data_impl_(intercepted_request_data.Pass()) {
       DCHECK(intercepted_request_data_impl_);
     }
 
@@ -53,11 +53,31 @@
     }
 
  private:
-    const InterceptedRequestDataImpl* intercepted_request_data_impl_;
+    scoped_ptr<InterceptedRequestDataImpl> intercepted_request_data_impl_;
 };
 
 } // namespace
 
+// static
+net::URLRequestJob* InterceptedRequestData::CreateJobFor(
+    scoped_ptr<InterceptedRequestData> intercepted_request_data,
+    net::URLRequest* request,
+    net::NetworkDelegate* network_delegate) {
+  DCHECK(intercepted_request_data);
+  DCHECK(request);
+  DCHECK(network_delegate);
+
+  return new AndroidStreamReaderURLRequestJob(
+      request,
+      network_delegate,
+      scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate>(
+          new StreamReaderJobDelegateImpl(
+              // PassAs rightfully doesn't support downcasts.
+              scoped_ptr<InterceptedRequestDataImpl>(
+                  static_cast<InterceptedRequestDataImpl*>(
+                      intercepted_request_data.release())))));
+}
+
 InterceptedRequestDataImpl::InterceptedRequestDataImpl(
     const base::android::JavaRef<jobject>& obj)
   : java_object_(obj) {
@@ -99,13 +119,4 @@
   return RegisterNativesImpl(env);
 }
 
-net::URLRequestJob* InterceptedRequestDataImpl::CreateJobFor(
-    net::URLRequest* request,
-    net::NetworkDelegate* network_delegate) const {
-  scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate>
-      stream_reader_job_delegate_impl(new StreamReaderJobDelegateImpl(this));
-  return new AndroidStreamReaderURLRequestJob(
-      request, network_delegate, stream_reader_job_delegate_impl.Pass());
-}
-
 } // namespace android_webview
diff --git a/android_webview/native/intercepted_request_data_impl.h b/android_webview/native/intercepted_request_data_impl.h
index 457bda9..0246569 100644
--- a/android_webview/native/intercepted_request_data_impl.h
+++ b/android_webview/native/intercepted_request_data_impl.h
@@ -25,10 +25,6 @@
   virtual bool GetMimeType(JNIEnv* env, std::string* mime_type) const;
   virtual bool GetCharset(JNIEnv* env, std::string* charset) const;
 
-  virtual net::URLRequestJob* CreateJobFor(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const OVERRIDE;
-
  private:
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
 
diff --git a/android_webview/renderer/aw_content_renderer_client.cc b/android_webview/renderer/aw_content_renderer_client.cc
index 3369b02..1b69d1c 100644
--- a/android_webview/renderer/aw_content_renderer_client.cc
+++ b/android_webview/renderer/aw_content_renderer_client.cc
@@ -106,16 +106,17 @@
                           gurl.SchemeIs(chrome::kAboutScheme)))
     return false;
 
+  // use NavigationInterception throttle to handle the call as that can
+  // be deferred until after the java side has been constructed.
+  if (opener_id != MSG_ROUTING_NONE)
+    return false;
+
   bool ignore_navigation = false;
   base::string16 url =  request.url().string();
 
   int routing_id = view->GetRoutingID();
-  // When opener_id is valid (popup case), use opener id for routing.
-  if (opener_id != MSG_ROUTING_NONE)
-    routing_id = opener_id;
   RenderThread::Get()->Send(new AwViewHostMsg_ShouldOverrideUrlLoading(
       routing_id, url, &ignore_navigation));
-
   return ignore_navigation;
 }
 
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
index e3c5077..6090143 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
@@ -8,14 +8,18 @@
 import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.widget.FrameLayout;
-import android.util.Log;
 
 import org.chromium.android_webview.AwContents;
 import org.chromium.content.browser.ContentViewCore;
@@ -147,6 +151,32 @@
         super.onDraw(canvas);
     }
 
+    @Override
+    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+        AccessibilityNodeProvider provider =
+            mAwContents.getAccessibilityNodeProvider();
+        return provider == null ? super.getAccessibilityNodeProvider() : provider;
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setClassName(AwContents.class.getName());
+        mAwContents.onInitializeAccessibilityNodeInfo(info);
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setClassName(AwContents.class.getName());
+        mAwContents.onInitializeAccessibilityEvent(event);
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        return mAwContents.performAccessibilityAction(action, arguments);
+    }
+
     // TODO: AwContents could define a generic class that holds an implementation similar to
     // the one below.
     private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
diff --git a/base/logging.h b/base/logging.h
index 859f260..f2051fb 100644
--- a/base/logging.h
+++ b/base/logging.h
@@ -986,8 +986,8 @@
 #if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
 #define NOTIMPLEMENTED_POLICY 0
 #else
-// Select default policy: LOG(ERROR)
-#define NOTIMPLEMENTED_POLICY 4
+// WebView: Hide NOTIMPLEMENTED entirely in Android release branch.
+#define NOTIMPLEMENTED_POLICY 0
 #endif
 #endif
 
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index 889e2fb..c84ade3 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -41,6 +41,18 @@
   // This should be first so others can use it.
   layer_impl->UpdateTwinLayer();
 
+  if (layer_impl->bounds().IsEmpty()) {
+    // Update may not get called for an empty layer, so resize here instead.
+    // Using layer_impl because either bounds() or paint_properties().bounds
+    // may disagree and either one could have been pushed to layer_impl.
+    pile_->Resize(layer_impl->bounds());
+    pile_->UpdateRecordedRegion();
+  }
+
+  if (DrawsContent()) {
+    DCHECK(paint_properties().bounds == pile_->size());
+  }
+
   layer_impl->SetIsMask(is_mask_);
   layer_impl->CreateTilingSetIfNeeded();
   // Unlike other properties, invalidation must always be set on layer_impl.
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 22f5d7c..bda2429 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -66,10 +66,19 @@
 }
 
 void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) {
-  LayerImpl::PushPropertiesTo(base_layer);
-
   PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer);
 
+  // We have already synced the important bits from the the active layer, and
+  // we will soon swap out its tilings and use them for recycling. However,
+  // there are now tiles in this layer's tilings that were unref'd and replaced
+  // with new tiles (due to invalidation). This resets all active priorities on
+  // the to-be-recycled tiling to ensure replaced tiles don't linger and take
+  // memory (due to a stale 'active' priority).
+  if (layer_impl->tilings_)
+    layer_impl->tilings_->DidBecomeRecycled();
+
+  LayerImpl::PushPropertiesTo(base_layer);
+
   // When the pending tree pushes to the active tree, the pending twin
   // disappears.
   layer_impl->twin_layer_ = NULL;
@@ -602,7 +611,8 @@
   // get updated prior to drawing or activation.  If this tree does not
   // need update draw properties, then its transforms are up to date and
   // we can create tiles for this tiling immediately.
-  if (!layer_tree_impl()->needs_update_draw_properties())
+  if (!layer_tree_impl()->needs_update_draw_properties() &&
+      should_update_tile_priorities_)
     UpdateTilePriorities();
 }
 
diff --git a/cc/resources/picture_layer_tiling.cc b/cc/resources/picture_layer_tiling.cc
index 12bfb18..c0a10aa 100644
--- a/cc/resources/picture_layer_tiling.cc
+++ b/cc/resources/picture_layer_tiling.cc
@@ -648,6 +648,16 @@
   live_tiles_rect_ = new_live_tiles_rect;
 }
 
+void PictureLayerTiling::DidBecomeRecycled() {
+  // DidBecomeActive below will set the active priority for tiles that are
+  // still in the tree. Calling this first on an active tiling that is becoming
+  // recycled takes care of tiles that are no longer in the active tree (eg.
+  // due to a pending invalidation).
+  for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
+    it->second->SetPriority(ACTIVE_TREE, TilePriority());
+  }
+}
+
 void PictureLayerTiling::DidBecomeActive() {
   for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
     it->second->SetPriority(ACTIVE_TREE, it->second->priority(PENDING_TREE));
diff --git a/cc/resources/picture_layer_tiling.h b/cc/resources/picture_layer_tiling.h
index 973fa50..9be1d6d 100644
--- a/cc/resources/picture_layer_tiling.h
+++ b/cc/resources/picture_layer_tiling.h
@@ -147,6 +147,13 @@
   // also updates the pile on each tile to be the current client's pile.
   void DidBecomeActive();
 
+  // Resets the active priority for all tiles in a tiling, when an active
+  // tiling is becoming recycled. This may include some tiles which are
+  // not in the the pending tiling (due to invalidations). This must
+  // be called before DidBecomeActive, as it resets the active priority
+  // while DidBecomeActive promotes pending priority on a similar set of tiles.
+  void DidBecomeRecycled();
+
   void UpdateTilesToCurrentPile();
 
   bool NeedsUpdateForFrameAtTime(double frame_time_in_seconds) {
diff --git a/cc/resources/picture_layer_tiling_set.cc b/cc/resources/picture_layer_tiling_set.cc
index 1b0cb5f..5f76052 100644
--- a/cc/resources/picture_layer_tiling_set.cc
+++ b/cc/resources/picture_layer_tiling_set.cc
@@ -334,6 +334,11 @@
     tilings_[i]->DidBecomeActive();
 }
 
+void PictureLayerTilingSet::DidBecomeRecycled() {
+  for (size_t i = 0; i < tilings_.size(); ++i)
+    tilings_[i]->DidBecomeRecycled();
+}
+
 scoped_ptr<base::Value> PictureLayerTilingSet::AsValue() const {
   scoped_ptr<base::ListValue> state(new base::ListValue());
   for (size_t i = 0; i < tilings_.size(); ++i)
diff --git a/cc/resources/picture_layer_tiling_set.h b/cc/resources/picture_layer_tiling_set.h
index e498cb9..c43850a 100644
--- a/cc/resources/picture_layer_tiling_set.h
+++ b/cc/resources/picture_layer_tiling_set.h
@@ -68,6 +68,7 @@
       size_t max_tiles_for_interest_area);
 
   void DidBecomeActive();
+  void DidBecomeRecycled();
 
   // For a given rect, iterates through tiles that can fill it.  If no
   // set of tiles with resources can fill the rect, then it will iterate
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index 11eaab0..0727daf 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -86,6 +86,8 @@
       format(0),
       filter(0),
       image_id(0),
+      bound_image_id(0),
+      dirty_image(false),
       texture_pool(0),
       hint(TextureUsageAny),
       type(static_cast<ResourceType>(0)) {}
@@ -118,6 +120,8 @@
       format(format),
       filter(filter),
       image_id(0),
+      bound_image_id(0),
+      dirty_image(false),
       texture_pool(texture_pool),
       hint(hint),
       type(GLTexture) {}
@@ -143,6 +147,8 @@
       format(format),
       filter(filter),
       image_id(0),
+      bound_image_id(0),
+      dirty_image(false),
       texture_pool(0),
       hint(TextureUsageAny),
       type(Bitmap) {}
@@ -620,7 +626,6 @@
 }
 
 ResourceProvider::ScopedSamplerGL::~ScopedSamplerGL() {
-  resource_provider_->UnbindForSampling(resource_id_, target_, unit_);
 }
 
 ResourceProvider::ScopedWriteLockGL::ScopedWriteLockGL(
@@ -1116,34 +1121,20 @@
     resource->filter = filter;
   }
 
-  if (resource->image_id)
+  if (resource->image_id && resource->dirty_image) {
+    // Release image currently bound to texture.
+    if (resource->bound_image_id)
+      context3d->releaseTexImage2DCHROMIUM(target, resource->bound_image_id);
     context3d->bindTexImage2DCHROMIUM(target, resource->image_id);
+    resource->bound_image_id = resource->image_id;
+    resource->dirty_image = false;
+  }
 
   // Active unit being GL_TEXTURE0 is effectively the ground state.
   if (unit != GL_TEXTURE0)
     GLC(context3d, context3d->activeTexture(GL_TEXTURE0));
 }
 
-void ResourceProvider::UnbindForSampling(
-    ResourceProvider::ResourceId resource_id, GLenum target, GLenum unit) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  ResourceMap::iterator it = resources_.find(resource_id);
-  DCHECK(it != resources_.end());
-  Resource* resource = &it->second;
-
-  if (!resource->image_id)
-    return;
-
-  WebGraphicsContext3D* context3d = output_surface_->context3d();
-  DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(context3d));
-  if (unit != GL_TEXTURE0)
-    GLC(context3d, context3d->activeTexture(unit));
-  context3d->releaseTexImage2DCHROMIUM(target, resource->image_id);
-  // Active unit being GL_TEXTURE0 is effectively the ground state.
-  if (unit != GL_TEXTURE0)
-    GLC(context3d, context3d->activeTexture(GL_TEXTURE0));
-}
-
 void ResourceProvider::BeginSetPixels(ResourceId id) {
   DCHECK(thread_checker_.CalledOnValidThread());
   ResourceMap::iterator it = resources_.find(id);
@@ -1374,6 +1365,8 @@
   DCHECK(context3d);
   context3d->destroyImageCHROMIUM(resource->image_id);
   resource->image_id = 0;
+  resource->bound_image_id = 0;
+  resource->dirty_image = false;
   resource->allocated = false;
 }
 
@@ -1413,6 +1406,7 @@
     WebGraphicsContext3D* context3d = output_surface_->context3d();
     DCHECK(context3d);
     context3d->unmapImageCHROMIUM(resource->image_id);
+    resource->dirty_image = true;
   }
 }
 
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index b5dccc8..f1b5dad 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -368,6 +368,8 @@
     // TODO(skyostil): Use a separate sampler object for filter state.
     GLenum filter;
     unsigned image_id;
+    unsigned bound_image_id;
+    bool dirty_image;
     GLenum texture_pool;
     TextureUsageHint hint;
     ResourceType type;
@@ -417,9 +419,6 @@
                        GLenum target,
                        GLenum unit,
                        GLenum filter);
-  void UnbindForSampling(ResourceProvider::ResourceId resource_id,
-                         GLenum target,
-                         GLenum unit);
 
   OutputSurface* output_surface_;
   bool lost_output_surface_;
diff --git a/cc/resources/resource_provider_unittest.cc b/cc/resources/resource_provider_unittest.cc
index a23af0b..32c5cf6 100644
--- a/cc/resources/resource_provider_unittest.cc
+++ b/cc/resources/resource_provider_unittest.cc
@@ -1638,9 +1638,30 @@
   EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
       .Times(1)
       .RetiresOnSaturation();
+  {
+    ResourceProvider::ScopedSamplerGL lock_gl(
+        resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
+    EXPECT_EQ(kTextureId, lock_gl.texture_id());
+  }
+
+  EXPECT_CALL(*context, mapImageCHROMIUM(kImageId, GL_READ_WRITE))
+      .WillOnce(Return(dummy_mapped_buffer_address))
+      .RetiresOnSaturation();
+  resource_provider->MapImage(id);
+
+  EXPECT_CALL(*context, unmapImageCHROMIUM(kImageId))
+      .Times(1)
+      .RetiresOnSaturation();
+  resource_provider->UnmapImage(id);
+
+  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId)).Times(1)
+      .RetiresOnSaturation();
   EXPECT_CALL(*context, releaseTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
       .Times(1)
       .RetiresOnSaturation();
+  EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
+      .Times(1)
+      .RetiresOnSaturation();
   EXPECT_CALL(*context, deleteTexture(kTextureId))
       .Times(1)
       .RetiresOnSaturation();
diff --git a/cc/resources/tile_manager.cc b/cc/resources/tile_manager.cc
index e6850bc..526f90a 100644
--- a/cc/resources/tile_manager.cc
+++ b/cc/resources/tile_manager.cc
@@ -67,9 +67,15 @@
                                           TreePriority tree_priority,
                                           bool is_ready_to_draw,
                                           bool is_active) {
-  // The amount of time for which we want to have prepainting coverage.
+  // The amount of time/pixels for which we want to have prepainting coverage.
+  // Note: All very arbitrary constants: metric-based tuning is welcome!
   const float kPrepaintingWindowTimeSeconds = 1.0f;
   const float kBackflingGuardDistancePixels = 314.0f;
+  // Note: The max distances here assume that SOON_BIN will never help overcome
+  // raster being too slow (only caching in advance will do that), so we just
+  // need enough padding to handle some latency and per-tile variability.
+  const float kMaxPrepaintingDistancePixelsHighRes = 2000.0f;
+  const float kMaxPrepaintingDistancePixelsLowRes = 4000.0f;
 
   // Don't let low res tiles be in the now bin unless we're in a mode where
   // we're prioritizing checkerboard prevention.
@@ -86,8 +92,16 @@
   if (prio.resolution == NON_IDEAL_RESOLUTION)
     return is_active ? EVENTUALLY_AND_ACTIVE_BIN : EVENTUALLY_BIN;
 
+  float max_prepainting_distance_pixels =
+      (prio.resolution == HIGH_RESOLUTION)
+          ? kMaxPrepaintingDistancePixelsHighRes
+          : kMaxPrepaintingDistancePixelsLowRes;
+
+  // Soon bin if we are within backfling-guard, or under both the time window
+  // and the max distance window.
   if (prio.distance_to_visible_in_pixels < kBackflingGuardDistancePixels ||
-      prio.time_to_visible_in_seconds < kPrepaintingWindowTimeSeconds)
+      (prio.time_to_visible_in_seconds < kPrepaintingWindowTimeSeconds &&
+       prio.distance_to_visible_in_pixels <= max_prepainting_distance_pixels))
     return SOON_BIN;
 
   return is_active ? EVENTUALLY_AND_ACTIVE_BIN : EVENTUALLY_BIN;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java
index 94d2b53..60ab606 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java
@@ -13,6 +13,7 @@
 import org.chromium.content.browser.test.util.UiUtils;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.ui.ActivityWindowAndroid;
 import org.chromium.ui.ViewAndroidDelegate;
 import org.chromium.ui.WindowAndroid;
 import org.chromium.ui.autofill.AutofillPopup;
@@ -39,7 +40,7 @@
         waitForActiveShellToBeDoneLoading();
 
         mMockAutofillCallback = new MockAutofillCallback();
-        mWindowAndroid = new WindowAndroid(activity);
+        mWindowAndroid = new ActivityWindowAndroid(activity);
         final ViewAndroidDelegate viewDelegate =
                 activity.getActiveContentView().getContentViewCore().getViewAndroidDelegate();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
index 76af25c..0d005c5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
@@ -4,10 +4,7 @@
 
 package org.chromium.chrome.browser.input;
 
-import android.test.suitebuilder.annotation.LargeTest;
-
 import org.chromium.base.test.util.DisabledTest;
-import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.content.browser.ContentView;
 import org.chromium.content.browser.input.SelectPopupDialog;
@@ -18,10 +15,9 @@
 import org.chromium.content.browser.test.util.UiUtils;
 import org.chromium.chrome.browser.ContentViewUtil;
 import org.chromium.chrome.testshell.ChromiumTestShellTestBase;
+import org.chromium.ui.ActivityWindowAndroid;
 import org.chromium.ui.WindowAndroid;
 
-import java.util.concurrent.TimeUnit;
-
 public class SelectPopupOtherContentViewTest extends ChromiumTestShellTestBase {
     private static final int WAIT_TIMEOUT_SECONDS = 2;
     private static final String SELECT_URL = UrlUtils.encodeHtmlDataUri(
@@ -77,7 +73,7 @@
             @Override
             public void run() {
                 int nativeWebContents = ContentViewUtil.createNativeWebContents(false);
-                WindowAndroid windowAndroid = new WindowAndroid(getActivity());
+                WindowAndroid windowAndroid = new ActivityWindowAndroid(getActivity());
                 ContentView contentView = ContentView.newInstance(
                         getActivity(), nativeWebContents, windowAndroid);
                 contentView.destroy();
diff --git a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java
index b53c80d..f8b177a 100644
--- a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java
+++ b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java
@@ -23,6 +23,7 @@
 import org.chromium.content.browser.DeviceUtils;
 import org.chromium.content.common.CommandLine;
 import org.chromium.content.common.ProcessInitException;
+import org.chromium.ui.ActivityWindowAndroid;
 import org.chromium.ui.WindowAndroid;
 
 /**
@@ -91,7 +92,7 @@
             mTabManager.setStartupUrl(startupUrl);
         }
 
-        mWindow = new WindowAndroid(this);
+        mWindow = new ActivityWindowAndroid(this);
         mWindow.restoreInstanceState(savedInstanceState);
         mTabManager.setWindow(mWindow);
 
diff --git a/content/browser/android/browser_media_player_manager.cc b/content/browser/android/browser_media_player_manager.cc
index dbf0c8f..57c8f6d 100644
--- a/content/browser/android/browser_media_player_manager.cc
+++ b/content/browser/android/browser_media_player_manager.cc
@@ -383,9 +383,13 @@
   RemovePlayer(player_id);
 
   RenderProcessHost* host = render_view_host()->GetProcess();
+  ContentViewCoreImpl* content_view_core_impl =
+      static_cast<ContentViewCoreImpl*>(
+          ContentViewCore::FromWebContents(web_contents_));
   AddPlayer(media::MediaPlayerAndroid::Create(
       player_id, url, source_type, first_party_for_cookies,
-      host->GetBrowserContext()->IsOffTheRecord(), this));
+      host->GetBrowserContext()->IsOffTheRecord(), this,
+      !content_view_core_impl->ShouldBlockMediaRequest(url)));
 }
 
 void BrowserMediaPlayerManager::OnStart(int player_id) {
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc
index 31ab2d2..da33b25 100644
--- a/content/browser/android/content_view_core_impl.cc
+++ b/content/browser/android/content_view_core_impl.cc
@@ -487,6 +487,14 @@
   Java_ContentViewCore_unhandledFlingStartEvent(env, j_obj.obj());
 }
 
+void ContentViewCoreImpl::OnScrollUpdateGestureConsumed() {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
+  if (j_obj.is_null())
+    return;
+  Java_ContentViewCore_onScrollUpdateGestureConsumed(env, j_obj.obj());
+}
+
 void ContentViewCoreImpl::HasTouchEventHandlers(bool need_touch_events) {
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
@@ -635,6 +643,17 @@
   return Java_ContentViewCore_getContext(env, obj.obj());
 }
 
+bool ContentViewCoreImpl::ShouldBlockMediaRequest(const GURL& url) {
+  JNIEnv* env = AttachCurrentThread();
+
+  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+  if (obj.is_null())
+    return true;
+  ScopedJavaLocalRef<jstring> j_url = ConvertUTF8ToJavaString(env, url.spec());
+  return Java_ContentViewCore_shouldBlockMediaRequest(env, obj.obj(),
+                                                      j_url.obj());
+}
+
 gfx::Size ContentViewCoreImpl::GetPhysicalBackingSize() const {
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
@@ -808,12 +827,7 @@
 
 ScopedJavaLocalRef<jstring> ContentViewCoreImpl::GetURL(
     JNIEnv* env, jobject) const {
-  // The current users of the Java API expect to use the active entry
-  // rather than the visible entry, which is exposed by WebContents::GetURL.
-  content::NavigationEntry* entry =
-      web_contents_->GetController().GetActiveEntry();
-  GURL url = entry ? entry->GetVirtualURL() : GURL::EmptyGURL();
-  return ConvertUTF8ToJavaString(env, url.spec());
+  return ConvertUTF8ToJavaString(env, GetWebContents()->GetURL().spec());
 }
 
 ScopedJavaLocalRef<jstring> ContentViewCoreImpl::GetTitle(
@@ -1443,7 +1457,7 @@
 ScopedJavaLocalRef<jstring>
 ContentViewCoreImpl::GetOriginalUrlForActiveNavigationEntry(JNIEnv* env,
                                                             jobject obj) {
-  NavigationEntry* entry = web_contents_->GetController().GetActiveEntry();
+  NavigationEntry* entry = web_contents_->GetController().GetVisibleEntry();
   if (entry == NULL)
     return ScopedJavaLocalRef<jstring>(env, NULL);
   return ConvertUTF8ToJavaString(env, entry->GetOriginalRequestURL().spec());
@@ -1518,7 +1532,7 @@
 
 bool ContentViewCoreImpl::GetUseDesktopUserAgent(
     JNIEnv* env, jobject obj) {
-  NavigationEntry* entry = web_contents_->GetController().GetActiveEntry();
+  NavigationEntry* entry = web_contents_->GetController().GetVisibleEntry();
   return entry && entry->GetIsOverridingUserAgent();
 }
 
@@ -1567,7 +1581,7 @@
     return;
 
   // Make sure the navigation entry actually exists.
-  NavigationEntry* entry = web_contents_->GetController().GetActiveEntry();
+  NavigationEntry* entry = web_contents_->GetController().GetVisibleEntry();
   if (!entry)
     return;
 
diff --git a/content/browser/android/content_view_core_impl.h b/content/browser/android/content_view_core_impl.h
index a20365b..837a644 100644
--- a/content/browser/android/content_view_core_impl.h
+++ b/content/browser/android/content_view_core_impl.h
@@ -256,6 +256,7 @@
   bool HasFocus();
   void ConfirmTouchEvent(InputEventAckState ack_result);
   void UnhandledFlingStartEvent();
+  void OnScrollUpdateGestureConsumed();
   void HasTouchEventHandlers(bool need_touch_events);
   void OnSelectionChanged(const std::string& text);
   void OnSelectionBoundsChanged(
@@ -285,6 +286,9 @@
   // typically be an Activity context for an on screen view.
   base::android::ScopedJavaLocalRef<jobject> GetContext();
 
+  // Returns True if the given media should be blocked to load.
+  bool ShouldBlockMediaRequest(const GURL& url);
+
   // --------------------------------------------------------------------------
   // Methods called from native code
   // --------------------------------------------------------------------------
diff --git a/content/browser/android/in_process/synchronous_compositor_impl.cc b/content/browser/android/in_process/synchronous_compositor_impl.cc
index aa34ba4..c354085 100644
--- a/content/browser/android/in_process/synchronous_compositor_impl.cc
+++ b/content/browser/android/in_process/synchronous_compositor_impl.cc
@@ -250,7 +250,9 @@
 SynchronousCompositorFactoryImpl::TryCreateStreamTextureFactory() {
   scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
       context_provider;
-  if (CanCreateMainThreadContext() && offscreen_context_for_main_thread_) {
+  if (CanCreateMainThreadContext() &&
+      GetOffscreenContextProviderForMainThread()) {
+    DCHECK(offscreen_context_for_main_thread_);
     DCHECK(wrapped_gl_context_for_main_thread_);
     context_provider =
         new VideoContextProvider(offscreen_context_for_main_thread_,
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc
index 9eb5774..d6f355d 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -1630,6 +1630,9 @@
     uint16 mode) {
 
   DCHECK(connections_.has(connection));
+  DCHECK(transactions_.find(transaction_id) == transactions_.end());
+  if (transactions_.find(transaction_id) != transactions_.end())
+    return;
 
   scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
       transaction_id,
@@ -1637,7 +1640,6 @@
       std::set<int64>(object_store_ids.begin(), object_store_ids.end()),
       static_cast<indexed_db::TransactionMode>(mode),
       this);
-  DCHECK(transactions_.find(transaction_id) == transactions_.end());
   transactions_[transaction_id] = transaction;
 }
 
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host.cc b/content/browser/indexed_db/indexed_db_dispatcher_host.cc
index e926a90..0476867 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host.cc
@@ -463,6 +463,12 @@
 
   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
 
+  if (transaction_database_map_.find(host_transaction_id) !=
+      transaction_database_map_.end()) {
+    DLOG(ERROR) << "Duplicate host_transaction_id.";
+    return;
+  }
+
   connection->database()->CreateTransaction(
       host_transaction_id, connection, params.object_store_ids, params.mode);
   transaction_database_map_[host_transaction_id] = params.ipc_database_id;
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 8e88540..fde357b 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -932,6 +932,10 @@
 void RenderWidgetHostViewAndroid::GestureEventAck(
     int gesture_event_type,
     InputEventAckState ack_result) {
+  if (gesture_event_type == WebKit::WebInputEvent::GestureScrollUpdate &&
+      ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) {
+    content_view_core_->OnScrollUpdateGestureConsumed();
+  }
   if (gesture_event_type == WebKit::WebInputEvent::GestureFlingStart &&
       ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) {
     content_view_core_->UnhandledFlingStartEvent();
diff --git a/content/child/npapi/np_channel_base.cc b/content/child/npapi/np_channel_base.cc
index c8e32f3..6d12d7c 100644
--- a/content/child/npapi/np_channel_base.cc
+++ b/content/child/npapi/np_channel_base.cc
@@ -14,6 +14,7 @@
 #include "ipc/ipc_sync_message.h"
 
 #if defined(OS_POSIX)
+#include "base/file_util.h"
 #include "ipc/ipc_channel_posix.h"
 #endif
 
@@ -49,6 +50,14 @@
     const IPC::ChannelHandle& channel_handle, IPC::Channel::Mode mode,
     ChannelFactory factory, base::MessageLoopProxy* ipc_message_loop,
     bool create_pipe_now, base::WaitableEvent* shutdown_event) {
+#if defined(OS_POSIX)
+  // On POSIX the channel_handle conveys an FD (socket) which is duped by the
+  // kernel during the IPC message exchange (via the SCM_RIGHTS mechanism).
+  // Ensure we do not leak this FD.
+  int fd = channel_handle.socket.auto_close ? channel_handle.socket.fd : -1;
+  file_util::ScopedFD auto_close_fd(&fd);
+#endif
+
   scoped_refptr<NPChannelBase> channel;
   std::string channel_key = channel_handle.name;
   ChannelMap::const_iterator iter = GetChannelMap()->find(channel_key);
@@ -62,6 +71,9 @@
 
   if (!channel->channel_valid()) {
     channel->channel_handle_ = channel_handle;
+#if defined(OS_POSIX)
+    ignore_result(auto_close_fd.release());
+#endif
     if (mode & IPC::Channel::MODE_SERVER_FLAG) {
       channel->channel_handle_.name =
           IPC::Channel::GenerateVerifiedChannelID(channel_key);
diff --git a/content/common/gpu/stream_texture_manager_android.cc b/content/common/gpu/stream_texture_manager_android.cc
index 3462fe2..2752eae 100644
--- a/content/common/gpu/stream_texture_manager_android.cc
+++ b/content/common/gpu/stream_texture_manager_android.cc
@@ -27,10 +27,7 @@
 }
 
 void StreamTextureManagerAndroid::StreamTextureAndroid::Update() {
-  GLint texture_id = 0;
-  glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_id);
   surface_texture_bridge_->UpdateTexImage();
-  glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id);
   if (matrix_callback_.is_null())
     return;
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java
index c118350..8af9a2e 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java
@@ -173,4 +173,13 @@
     public ContentVideoViewClient getContentVideoViewClient() {
         return null;
     }
+
+    /**
+     * Called when BrowserMediaPlayerManager wants to load a media resource.
+     * @param url the URL of media resource to load.
+     * @return true to prevent the resource from being loaded.
+     */
+    public boolean shouldBlockMediaRequest(String url) {
+        return false;
+    }
 }
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index e90e7bb..3433c2f 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -176,8 +176,8 @@
     }
 
     /**
-     * An interface that allows the embedder to be notified when the pinch gesture starts and
-     * stops.
+     * An interface that allows the embedder to be notified of events and state changes related to
+     * gesture processing.
      */
     public interface GestureStateListener {
         /**
@@ -204,6 +204,14 @@
          * Called when a fling event was not handled by the renderer.
          */
         void onUnhandledFlingStartEvent();
+
+        /**
+         * Called to indicate that a scroll update gesture had been consumed by the page.
+         * This callback is called whenever any layer is scrolled (like a frame or div). It is
+         * not called when a JS touch handler consumes the event (preventDefault), it is not called
+         * for JS-initiated scrolling.
+         */
+        void onScrollUpdateGestureConsumed();
     }
 
     /**
@@ -366,6 +374,9 @@
 
     private Runnable mDeferredHandleFadeInRunnable;
 
+    private PositionObserver mPositionObserver;
+    private PositionObserver.Listener mPositionListener;
+
     // Size of the viewport in physical pixels as set from onSizeChanged.
     private int mViewportWidthPix;
     private int mViewportHeightPix;
@@ -706,6 +717,15 @@
         mHardwareAccelerated = hasHardwareAcceleration(mContext);
 
         mContainerView = containerView;
+        mPositionObserver = new ViewPositionObserver(mContainerView);
+        mPositionListener = new PositionObserver.Listener() {
+            @Override
+            public void onPositionChanged(int x, int y) {
+                if (isSelectionHandleShowing() || isInsertionHandleShowing()) {
+                    temporarilyHideTextHandles();
+                }
+            }
+        };
 
         int windowNativePointer = windowAndroid != null ? windowAndroid.getNativePointer() : 0;
 
@@ -1254,6 +1274,14 @@
         }
     }
 
+    @SuppressWarnings("unused")
+    @CalledByNative
+    private void onScrollUpdateGestureConsumed() {
+        if (mGestureStateListener != null) {
+            mGestureStateListener.onScrollUpdateGestureConsumed();
+        }
+    }
+
     @Override
     public boolean sendGesture(int type, long timeMs, int x, int y, boolean lastInputEventForVSync,
                                Bundle b) {
@@ -2003,7 +2031,8 @@
 
     private SelectionHandleController getSelectionHandleController() {
         if (mSelectionHandleController == null) {
-            mSelectionHandleController = new SelectionHandleController(getContainerView()) {
+            mSelectionHandleController = new SelectionHandleController(
+                    getContainerView(), mPositionObserver) {
                 @Override
                 public void selectBetweenCoordinates(int x1, int y1, int x2, int y2) {
                     if (mNativeContentViewCore != 0 && !(x1 == x2 && y1 == y2)) {
@@ -2029,7 +2058,8 @@
 
     private InsertionHandleController getInsertionHandleController() {
         if (mInsertionHandleController == null) {
-            mInsertionHandleController = new InsertionHandleController(getContainerView()) {
+            mInsertionHandleController = new InsertionHandleController(
+                    getContainerView(), mPositionObserver) {
                 private static final int AVERAGE_LINE_HEIGHT = 14;
 
                 @Override
@@ -2095,6 +2125,7 @@
         if (mInsertionHandleController != null) {
             mInsertionHandleController.hideAndDisallowAutomaticShowing();
         }
+        mPositionObserver.removeListener(mPositionListener);
     }
 
     private void showSelectActionBar() {
@@ -2214,10 +2245,10 @@
     // Makes the insertion/selection handles invisible. They will fade back in shortly after the
     // last call to scheduleTextHandleFadeIn (or temporarilyHideTextHandles).
     private void temporarilyHideTextHandles() {
-        if (isSelectionHandleShowing()) {
+        if (isSelectionHandleShowing() && !mSelectionHandleController.isDragging()) {
             mSelectionHandleController.setHandleVisibility(HandleView.INVISIBLE);
         }
-        if (isInsertionHandleShowing()) {
+        if (isInsertionHandleShowing() && !mInsertionHandleController.isDragging()) {
             mInsertionHandleController.setHandleVisibility(HandleView.INVISIBLE);
         }
         scheduleTextHandleFadeIn();
@@ -2468,6 +2499,9 @@
             }
             mHasSelection = false;
         }
+        if (isSelectionHandleShowing() || isInsertionHandleShowing()) {
+            mPositionObserver.addListener(mPositionListener);
+        }
     }
 
     @SuppressWarnings("unused")
@@ -2981,6 +3015,11 @@
         return mContentViewClient.getContentVideoViewClient();
     }
 
+    @CalledByNative
+    private boolean shouldBlockMediaRequest(String url) {
+        return mContentViewClient.shouldBlockMediaRequest(url);
+    }
+
     private native void nativeOnJavaContentViewCoreDestroyed(int nativeContentViewCoreImpl);
 
     private native void nativeLoadUrl(
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewGestureHandler.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewGestureHandler.java
index ddf433e..9710b01 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewGestureHandler.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewGestureHandler.java
@@ -954,7 +954,8 @@
         // "Last finger raised" could be an end to movement.  However,
         // give the mSimpleTouchDetector a chance to continue
         // scrolling with a fling.
-        if (event.getAction() == MotionEvent.ACTION_UP) {
+        if (event.getAction() == MotionEvent.ACTION_UP
+                || event.getAction() == MotionEvent.ACTION_CANCEL) {
             if (mTouchScrolling) {
                 possiblyEndMovement = true;
             }
diff --git a/content/public/android/java/src/org/chromium/content/browser/PositionObserver.java b/content/public/android/java/src/org/chromium/content/browser/PositionObserver.java
new file mode 100644
index 0000000..1c14d2f
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/PositionObserver.java
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser;
+
+/**
+ * Used to register listeners that can be notified of changes to the position of a view.
+ */
+public interface PositionObserver {
+    public interface Listener {
+        /**
+         * Called during predraw if the position of the underlying view has changed.
+         */
+        void onPositionChanged(int positionX, int positionY);
+    }
+
+    /**
+     * @return The current x position of the observed view.
+     */
+    int getPositionX();
+
+    /**
+     * @return The current y position of the observed view.
+     */
+    int getPositionY();
+
+    /**
+     * Register a listener to be called when the position of the underlying view changes.
+     */
+    void addListener(Listener listener);
+
+    /**
+     * Remove a previously installed listener.
+     */
+    void removeListener(Listener listener);
+}
diff --git a/content/public/android/java/src/org/chromium/content/browser/ViewPositionObserver.java b/content/public/android/java/src/org/chromium/content/browser/ViewPositionObserver.java
new file mode 100644
index 0000000..55de7c4
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/ViewPositionObserver.java
@@ -0,0 +1,99 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser;
+
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import java.util.ArrayList;
+
+/**
+ * Used to register listeners that can be notified of changes to the position of a view.
+ */
+public class ViewPositionObserver implements PositionObserver {
+    private View mView;
+    // Absolute position of the container view relative to its parent window.
+    private final int[] mPosition = new int[2];
+
+    private final ArrayList<Listener> mListeners;
+    private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
+
+    /**
+     * @param view The view to observe.
+     */
+    public ViewPositionObserver(View view) {
+        mView = view;
+        mListeners = new ArrayList<Listener>();
+        updatePosition();
+        mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
+            @Override
+            public boolean onPreDraw() {
+                updatePosition();
+                return true;
+            }
+        };
+    }
+
+    /**
+     * @return The current x position of the observed view.
+     */
+    public int getPositionX() {
+        // The stored position may be out-of-date. Get the real current position.
+        updatePosition();
+        return mPosition[0];
+    }
+
+    /**
+     * @return The current y position of the observed view.
+     */
+    public int getPositionY() {
+        // The stored position may be out-of-date. Get the real current position.
+        updatePosition();
+        return mPosition[1];
+    }
+
+    /**
+     * Register a listener to be called when the position of the underlying view changes.
+     */
+    public void addListener(Listener listener) {
+        if (mListeners.contains(listener)) return;
+
+        if (mListeners.isEmpty()) {
+            mView.getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
+            updatePosition();
+        }
+
+        mListeners.add(listener);
+    }
+
+    /**
+     * Remove a previously installed listener.
+     */
+    public void removeListener(Listener listener) {
+        if (!mListeners.contains(listener)) return;
+
+        mListeners.remove(listener);
+
+        if (mListeners.isEmpty()) {
+            mView.getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener);
+        }
+    }
+
+    private void notifyListeners() {
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).onPositionChanged(mPosition[0], mPosition[1]);
+        }
+    }
+
+    private void updatePosition() {
+        int previousPositionX = mPosition[0];
+        int previousPositionY = mPosition[1];
+        mView.getLocationInWindow(mPosition);
+        if (mPosition[0] != previousPositionX || mPosition[1] != previousPositionY) {
+            notifyListeners();
+        }
+    }
+}
+
diff --git a/content/public/android/java/src/org/chromium/content/browser/ZoomManager.java b/content/public/android/java/src/org/chromium/content/browser/ZoomManager.java
index 986d61e..7deb570 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ZoomManager.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ZoomManager.java
@@ -123,7 +123,10 @@
         try {
             boolean inGesture = isScaleGestureDetectionInProgress();
             boolean retVal = mMultiTouchDetector.onTouchEvent(event);
-            if (event.getActionMasked() == MotionEvent.ACTION_UP && !inGesture) return false;
+            if (!inGesture && (event.getActionMasked() == MotionEvent.ACTION_UP
+                    || event.getActionMasked() == MotionEvent.ACTION_CANCEL)) {
+                return false;
+            }
             return retVal;
         } catch (Exception e) {
             Log.e(TAG, "ScaleGestureDetector got into a bad state!", e);
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java b/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java
index 88a6dea..2310726 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java
@@ -24,27 +24,40 @@
 import android.widget.PopupWindow;
 import android.widget.TextView;
 
+import org.chromium.content.browser.PositionObserver;
+
 /**
  * View that displays a selection or insertion handle for text editing.
+ *
+ * While a HandleView is logically a child of some other view, it does not exist in that View's
+ * hierarchy.
+ *
  */
 public class HandleView extends View {
     private static final float FADE_DURATION = 200.f;
 
     private Drawable mDrawable;
     private final PopupWindow mContainer;
+
+    // The position of the handle relative to the parent view.
     private int mPositionX;
     private int mPositionY;
+
+    // The position of the parent relative to the application's root view.
+    private int mParentPositionX;
+    private int mParentPositionY;
+
+    // The offset from this handles position to the "tip" of the handle.
+    private float mHotspotX;
+    private float mHotspotY;
+
     private final CursorController mController;
     private boolean mIsDragging;
     private float mTouchToWindowOffsetX;
     private float mTouchToWindowOffsetY;
-    private float mHotspotX;
-    private float mHotspotY;
+
     private int mLineOffsetY;
-    private int mLastParentX;
-    private int mLastParentY;
     private float mDownPositionX, mDownPositionY;
-    private int mContainerPositionX, mContainerPositionY;
     private long mTouchTimer;
     private boolean mIsInsertionHandle = false;
     private float mAlpha;
@@ -61,13 +74,15 @@
     private Drawable mSelectHandleRight;
     private Drawable mSelectHandleCenter;
 
-    private final int[] mTempCoords = new int[2];
     private final Rect mTempRect = new Rect();
 
     static final int LEFT = 0;
     static final int CENTER = 1;
     static final int RIGHT = 2;
 
+    private PositionObserver mParentPositionObserver;
+    private PositionObserver.Listener mParentPositionListener;
+
     // Number of dips to subtract from the handle's y position to give a suitable
     // y coordinate for the corresponding text position. This is to compensate for the fact
     // that the handle position is at the base of the line of text.
@@ -79,7 +94,8 @@
         android.R.attr.textSelectHandleRight,
     };
 
-    HandleView(CursorController controller, int pos, View parent) {
+    HandleView(CursorController controller, int pos, View parent,
+            PositionObserver parentPositionObserver) {
         super(parent.getContext());
         Context context = parent.getContext();
         mParent = parent;
@@ -101,6 +117,14 @@
                 LINE_OFFSET_Y_DIP, context.getResources().getDisplayMetrics());
 
         mAlpha = 1.f;
+
+        mParentPositionListener = new PositionObserver.Listener() {
+            @Override
+            public void onPositionChanged(int x, int y) {
+                updateParentPosition(x, y);
+            }
+        };
+        mParentPositionObserver = parentPositionObserver;
     }
 
     void setOrientation(int pos) {
@@ -152,21 +176,46 @@
                 mDrawable.getIntrinsicHeight());
     }
 
-    private void updateContainerPosition() {
-        final int[] coords = mTempCoords;
-        mParent.getLocationInWindow(coords);
-        mContainerPositionX = coords[0] + mPositionX;
-        mContainerPositionY = coords[1] + mPositionY;
+    private void updateParentPosition(int parentPositionX, int parentPositionY) {
+        // Hide paste popup window as soon as a scroll occurs.
+        if (mPastePopupWindow != null) mPastePopupWindow.hide();
+
+        mTouchToWindowOffsetX += parentPositionX - mParentPositionX;
+        mTouchToWindowOffsetY += parentPositionY - mParentPositionY;
+        mParentPositionX = parentPositionX;
+        mParentPositionY = parentPositionY;
+        onPositionChanged();
+    }
+
+    private int getContainerPositionX() {
+        return mParentPositionX + mPositionX;
+    }
+
+    private int getContainerPositionY() {
+        return mParentPositionY + mPositionY;
+    }
+
+    private void onPositionChanged() {
+        mContainer.update(getContainerPositionX(), getContainerPositionY(),
+                getRight() - getLeft(), getBottom() - getTop());
+    }
+
+    private void showContainer() {
+        mContainer.showAtLocation(mParent, 0, getContainerPositionX(), getContainerPositionY());
     }
 
     void show() {
+        // While hidden, the parent position may have become stale. It must be updated before
+        // checking isPositionVisible().
+        updateParentPosition(mParentPositionObserver.getPositionX(),
+                mParentPositionObserver.getPositionY());
         if (!isPositionVisible()) {
             hide();
             return;
         }
+        mParentPositionObserver.addListener(mParentPositionListener);
         mContainer.setContentView(this);
-        updateContainerPosition();
-        mContainer.showAtLocation(mParent, 0, mContainerPositionX, mContainerPositionY);
+        showContainer();
 
         // Hide paste view when handle is moved on screen.
         if (mPastePopupWindow != null) {
@@ -177,6 +226,7 @@
     void hide() {
         mIsDragging = false;
         mContainer.dismiss();
+        mParentPositionObserver.removeListener(mParentPositionListener);
         if (mPastePopupWindow != null) {
             mPastePopupWindow.hide();
         }
@@ -203,10 +253,8 @@
             return false;
         }
 
-        final int[] coords = mTempCoords;
-        mParent.getLocationInWindow(coords);
-        final int posX = coords[0] + mPositionX + (int) mHotspotX;
-        final int posY = coords[1] + mPositionY + (int) mHotspotY;
+        final int posX = getContainerPositionX() + (int) mHotspotX;
+        final int posY = getContainerPositionY() + (int) mHotspotY;
 
         return posX >= clip.left && posX <= clip.right &&
                 posY >= clip.top && posY <= clip.bottom;
@@ -214,44 +262,24 @@
 
     // x and y are in physical pixels.
     void moveTo(int x, int y) {
+        int previousPositionX = mPositionX;
+        int previousPositionY = mPositionY;
+
         mPositionX = x;
         mPositionY = y;
         if (isPositionVisible()) {
-            int[] coords = null;
             if (mContainer.isShowing()) {
-                coords = mTempCoords;
-                mParent.getLocationInWindow(coords);
-                final int containerPositionX = coords[0] + mPositionX;
-                final int containerPositionY = coords[1] + mPositionY;
-
-                if (containerPositionX != mContainerPositionX ||
-                    containerPositionY != mContainerPositionY) {
-                    mContainerPositionX = containerPositionX;
-                    mContainerPositionY = containerPositionY;
-
-                    mContainer.update(mContainerPositionX, mContainerPositionY,
-                            getRight() - getLeft(), getBottom() - getTop());
-
-                    // Hide paste popup window as soon as a scroll occurs.
-                    if (mPastePopupWindow != null) {
-                        mPastePopupWindow.hide();
-                    }
+                onPositionChanged();
+                // Hide paste popup window as soon as the handle is dragged.
+                if (mPastePopupWindow != null &&
+                        (previousPositionX != mPositionX || previousPositionY != mPositionY)) {
+                    mPastePopupWindow.hide();
                 }
             } else {
                 show();
             }
 
             if (mIsDragging) {
-                if (coords == null) {
-                    coords = mTempCoords;
-                    mParent.getLocationInWindow(coords);
-                }
-                if (coords[0] != mLastParentX || coords[1] != mLastParentY) {
-                    mTouchToWindowOffsetX += coords[0] - mLastParentX;
-                    mTouchToWindowOffsetY += coords[1] - mLastParentY;
-                    mLastParentX = coords[0];
-                    mLastParentY = coords[1];
-                }
                 // Hide paste popup window as soon as the handle is dragged.
                 if (mPastePopupWindow != null) {
                     mPastePopupWindow.hide();
@@ -265,9 +293,6 @@
     @Override
     protected void onDraw(Canvas c) {
         updateAlpha();
-        updateContainerPosition();
-        mContainer.update(mContainerPositionX, mContainerPositionY,
-                getRight() - getLeft(), getBottom() - getTop());
         mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop());
         mDrawable.draw(c);
     }
@@ -280,10 +305,6 @@
                 mDownPositionY = ev.getRawY();
                 mTouchToWindowOffsetX = mDownPositionX - mPositionX;
                 mTouchToWindowOffsetY = mDownPositionY - mPositionY;
-                final int[] coords = mTempCoords;
-                mParent.getLocationInWindow(coords);
-                mLastParentX = coords[0];
-                mLastParentY = coords[1];
                 mIsDragging = true;
                 mController.beforeStartUpdatingPosition(this);
                 mTouchTimer = SystemClock.uptimeMillis();
@@ -347,17 +368,31 @@
 
     // x and y are in physical pixels.
     void positionAt(int x, int y) {
-        moveTo((int)(x - mHotspotX), (int)(y - mHotspotY));
+        moveTo(x - Math.round(mHotspotX), y - Math.round(mHotspotY));
     }
 
-    // Returns the x coordinate of the position that the handle appears to be pointing to.
+    // Returns the x coordinate of the position that the handle appears to be pointing to relative
+    // to the handles "parent" view.
     int getAdjustedPositionX() {
-        return (int) (mPositionX + mHotspotX);
+        return mPositionX + Math.round(mHotspotX);
     }
 
-    // Returns the y coordinate of the position that the handle appears to be pointing to.
+    // Returns the y coordinate of the position that the handle appears to be pointing to relative
+    // to the handles "parent" view.
     int getAdjustedPositionY() {
-        return (int) (mPositionY + mHotspotY);
+        return mPositionY + Math.round(mHotspotY);
+    }
+
+    // Returns the x coordinate of the postion that the handle appears to be pointing to relative to
+    // the root view of the application.
+    int getRootViewRelativePositionX() {
+        return getContainerPositionX() + Math.round(mHotspotX);
+    }
+
+    // Returns the y coordinate of the postion that the handle appears to be pointing to relative to
+    // the root view of the application.
+    int getRootViewRelativePositionY() {
+        return getContainerPositionY() + Math.round(mHotspotY);
     }
 
     // Returns a suitable y coordinate for the text position corresponding to the handle.
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/InsertionHandleController.java b/content/public/android/java/src/org/chromium/content/browser/input/InsertionHandleController.java
index f251edf..66ce430 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/InsertionHandleController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/InsertionHandleController.java
@@ -18,6 +18,8 @@
 
 import com.google.common.annotations.VisibleForTesting;
 
+import org.chromium.content.browser.PositionObserver;
+
 /**
  * CursorController for inserting text at the cursor position.
  */
@@ -37,9 +39,13 @@
 
     private Context mContext;
 
-    public InsertionHandleController(View parent) {
+    private PositionObserver mPositionObserver;
+
+    public InsertionHandleController(View parent, PositionObserver positionObserver) {
         mParent = parent;
+
         mContext = parent.getContext();
+        mPositionObserver = positionObserver;
     }
 
     /** Allows the handle to be shown automatically when cursor position changes */
@@ -72,6 +78,13 @@
         showPastePopup();
     }
 
+    /**
+     * @return whether the handle is being dragged.
+     */
+    public boolean isDragging() {
+        return mHandle != null && mHandle.isDragging();
+    }
+
     /** Shows the handle at the given coordinates, as long as automatic showing is allowed */
     public void onCursorPositionChanged() {
         if (mAllowAutomaticShowing) {
@@ -167,7 +180,9 @@
     }
 
     private void createHandleIfNeeded() {
-        if (mHandle == null) mHandle = new HandleView(this, HandleView.CENTER, mParent);
+        if (mHandle == null) {
+            mHandle = new HandleView(this, HandleView.CENTER, mParent, mPositionObserver);
+        }
     }
 
     private void showHandleIfNeeded() {
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/SelectPopupDialog.java b/content/public/android/java/src/org/chromium/content/browser/input/SelectPopupDialog.java
index 360f928..0de8a70 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/SelectPopupDialog.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/SelectPopupDialog.java
@@ -81,8 +81,10 @@
             if (mItemEnabled[position] != POPUP_ITEM_TYPE_ENABLED) {
                 if (mItemEnabled[position] == POPUP_ITEM_TYPE_GROUP) {
                     // Currently select_dialog_multichoice & select_dialog_multichoice use
-                    // CheckedTextViews. If that changes, the class cast will no longer be valid.
-                    ((CheckedTextView) convertView).setCheckMarkDrawable(null);
+                    // CheckedTextViews in chrome but not in WebView.
+                    if (convertView instanceof CheckedTextView) {
+                        ((CheckedTextView) convertView).setCheckMarkDrawable(null);
+                    }
                 } else {
                     // Draw the disabled element in a disabled state.
                     convertView.setEnabled(false);
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/SelectionHandleController.java b/content/public/android/java/src/org/chromium/content/browser/input/SelectionHandleController.java
index 289dfa2..d28286d 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/SelectionHandleController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/SelectionHandleController.java
@@ -8,6 +8,8 @@
 
 import com.google.common.annotations.VisibleForTesting;
 
+import org.chromium.content.browser.PositionObserver;
+
 /**
  * CursorController for selecting a range of text.
  */
@@ -33,8 +35,11 @@
     private int mFixedHandleX;
     private int mFixedHandleY;
 
-    public SelectionHandleController(View parent) {
+    private PositionObserver mPositionObserver;
+
+    public SelectionHandleController(View parent, PositionObserver positionObserver) {
         mParent = parent;
+        mPositionObserver = positionObserver;
     }
 
     /** Automatically show selection anchors when text is selected. */
@@ -190,11 +195,13 @@
     private void createHandlesIfNeeded(int startDir, int endDir) {
         if (mStartHandle == null) {
             mStartHandle = new HandleView(this,
-                startDir == TEXT_DIRECTION_RTL ? HandleView.RIGHT : HandleView.LEFT, mParent);
+                    startDir == TEXT_DIRECTION_RTL ? HandleView.RIGHT : HandleView.LEFT, mParent,
+                    mPositionObserver);
         }
         if (mEndHandle == null) {
             mEndHandle = new HandleView(this,
-                endDir == TEXT_DIRECTION_RTL ? HandleView.LEFT : HandleView.RIGHT, mParent);
+                    endDir == TEXT_DIRECTION_RTL ? HandleView.LEFT : HandleView.RIGHT, mParent,
+                    mPositionObserver);
         }
     }
 
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ClipboardTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ClipboardTest.java
new file mode 100644
index 0000000..644216d
--- /dev/null
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ClipboardTest.java
@@ -0,0 +1,97 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser;
+
+import android.content.ClipboardManager;
+import android.content.ClipData;
+import android.content.Context;
+import android.os.Build;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
+import android.view.KeyEvent;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.UrlUtils;
+import org.chromium.content.browser.input.ImeAdapter;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.content_shell_apk.ContentShellTestBase;
+
+import java.util.concurrent.Callable;
+
+public class ClipboardTest extends ContentShellTestBase {
+    private static final String TEST_PAGE_DATA_URL = UrlUtils.encodeHtmlDataUri(
+            "<html><body>Hello, <a href=\"http://www.example.com/\">world</a>, how <b> " +
+            "Chromium</b> doing today?</body></html>");
+
+    private static final String EXPECTED_TEXT_RESULT = "Hello, world, how Chromium doing today?";
+
+    // String to search for in the HTML representation on the clipboard.
+    private static final String EXPECTED_HTML_NEEDLE = "http://www.example.com/";
+
+    /**
+     * Tests that copying document fragments will put at least a plain-text representation
+     * of the contents on the clipboard. For Android JellyBean and higher, we also expect
+     * the HTML representation of the fragment to be available.
+     */
+    @LargeTest
+    @Feature({"Clipboard","TextInput"})
+    public void testCopyDocumentFragment() throws Throwable {
+        launchContentShellWithUrl(TEST_PAGE_DATA_URL);
+        assertTrue("Page failed to load", waitForActiveShellToBeDoneLoading());
+
+        final ClipboardManager clipboardManager = (ClipboardManager)
+                getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
+        assertNotNull(clipboardManager);
+
+        // Clear the clipboard to make sure we start with a clean state.
+        clipboardManager.setPrimaryClip(ClipData.newPlainText(null, ""));
+        assertFalse(hasPrimaryClip(clipboardManager));
+
+        getImeAdapter().selectAll();
+        getImeAdapter().copy();
+
+        // Waits until data has been made available on the Android clipboard.
+        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() {
+                    @Override
+                    public Boolean call() throws Exception {
+                        return hasPrimaryClip(clipboardManager);
+                    }
+                });
+            }
+        }));
+
+        // Verify that the data on the clipboard is what we expect it to be. For Android JB MR2
+        // and higher we expect HTML content, for other versions the plain-text representation.
+        final ClipData clip = clipboardManager.getPrimaryClip();
+        assertEquals(EXPECTED_TEXT_RESULT, clip.getItemAt(0).coerceToText(getActivity()));
+
+        // Android JellyBean and higher should have a HTML representation on the clipboard as well.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            String htmlText = clip.getItemAt(0).getHtmlText();
+
+            assertNotNull(htmlText);
+            assertTrue(htmlText.contains(EXPECTED_HTML_NEEDLE));
+        }
+    }
+
+    private ImeAdapter getImeAdapter() {
+        return getContentViewCore().getImeAdapterForTest();
+    }
+
+    // Returns whether there is a primary clip with content on the current clipboard.
+    private Boolean hasPrimaryClip(ClipboardManager clipboardManager) {
+        final ClipData clip = clipboardManager.getPrimaryClip();
+        if (clip != null && clip.getItemCount() > 0) {
+            return !TextUtils.isEmpty(clip.getItemAt(0).getText());
+        }
+
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewGestureHandlerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewGestureHandlerTest.java
index ce63def..ba66171 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewGestureHandlerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewGestureHandlerTest.java
@@ -578,6 +578,75 @@
     }
 
     /**
+     * Verify that for a normal scroll the following events are sent:
+     * - GESTURE_SCROLL_START
+     * - GESTURE_SCROLL_BY
+     * - GESTURE_SCROLL_END
+     * @throws Exception
+     */
+    @SmallTest
+    @Feature({"Gestures"})
+    public void testScrollEventActionUpSequence() throws Exception {
+        checkScrollEventSequenceForEndActionType(MotionEvent.ACTION_UP);
+    }
+
+    /**
+     * Verify that for a cancelled scroll the following events are sent:
+     * - GESTURE_SCROLL_START
+     * - GESTURE_SCROLL_BY
+     * - GESTURE_SCROLL_END
+     * @throws Exception
+     */
+    @SmallTest
+    @Feature({"Gestures"})
+    public void testScrollEventActionCancelSequence() throws Exception {
+        checkScrollEventSequenceForEndActionType(MotionEvent.ACTION_CANCEL);
+    }
+
+    private void checkScrollEventSequenceForEndActionType(int endActionType) throws Exception {
+        final long downTime = SystemClock.uptimeMillis();
+        final long eventTime = SystemClock.uptimeMillis();
+        final int scrollToX = FAKE_COORD_X + 100;
+        final int scrollToY = FAKE_COORD_Y + 100;
+
+        GestureRecordingMotionEventDelegate mockDelegate =
+                new GestureRecordingMotionEventDelegate();
+        mGestureHandler = new ContentViewGestureHandler(
+                getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager,
+                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
+
+        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
+
+        assertTrue(mGestureHandler.onTouchEvent(event));
+
+        event = MotionEvent.obtain(
+                downTime, eventTime + 1000, MotionEvent.ACTION_MOVE, scrollToX, scrollToY, 0);
+        assertTrue(mGestureHandler.onTouchEvent(event));
+        assertTrue(mGestureHandler.isNativeScrolling());
+        assertTrue("A scrollStart event should have been sent",
+                mockDelegate.mGestureTypeList.contains(
+                        ContentViewGestureHandler.GESTURE_SCROLL_START));
+        assertEquals("We should have started scrolling",
+                ContentViewGestureHandler.GESTURE_SCROLL_BY,
+                mockDelegate.mMostRecentGestureEvent.mType);
+        assertEquals("Only scrollBegin and scrollBy should have been sent",
+                2, mockDelegate.mGestureTypeList.size());
+
+        event = MotionEvent.obtain(
+                downTime, eventTime + 1000, endActionType, scrollToX, scrollToY, 0);
+        mGestureHandler.onTouchEvent(event);
+        assertFalse(mGestureHandler.isNativeScrolling());
+        assertTrue("A scrollEnd event should have been sent",
+                mockDelegate.mGestureTypeList.contains(
+                        ContentViewGestureHandler.GESTURE_SCROLL_END));
+        assertEquals("We should have stopped scrolling",
+                ContentViewGestureHandler.GESTURE_SCROLL_END,
+                mockDelegate.mMostRecentGestureEvent.mType);
+        assertEquals("Only scrollBegin and scrollBy and scrollEnd should have been sent",
+                3, mockDelegate.mGestureTypeList.size());
+    }
+
+    /**
      * Verify that for a normal fling (fling after scroll) the following events are sent:
      * - GESTURE_SCROLL_BEGIN
      * - GESTURE_FLING_START
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/SelectionHandleTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/SelectionHandleTest.java
index 162f478..04e9c92 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/SelectionHandleTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/SelectionHandleTest.java
@@ -392,16 +392,11 @@
         touchCommon.longPressView(getContentView(), centerX, centerY);
 
         assertWaitForHandlesShowingEquals(true);
+        assertWaitForHandleViewStopped(getStartHandle());
 
         // No words wrap in the sample text so handles should be at the same y
         // position.
         assertEquals(getStartHandle().getPositionY(), getEndHandle().getPositionY());
-
-        // In ContentShell, the handles are initially misplaced when they first appear. This is
-        // fixed after the first time they are dragged (or the page is scrolled).
-        // TODO(cjhopman): Fix this problem in ContentShell: http://crbug.com/243836
-        dragHandleTo(getStartHandle(), centerX - 40, centerY - 40, 1);
-        assertWaitForHandleViewStopped(getStartHandle());
     }
 
     private void clickToDismissHandles() throws Throwable {
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
index 3f7ceb0..1fee1bf 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
@@ -28,6 +28,7 @@
 import org.chromium.content.common.ProcessInitException;
 import org.chromium.content_shell.Shell;
 import org.chromium.content_shell.ShellManager;
+import org.chromium.ui.ActivityWindowAndroid;
 import org.chromium.ui.WindowAndroid;
 
 /**
@@ -84,7 +85,7 @@
 
             setContentView(R.layout.content_shell_activity);
             mShellManager = (ShellManager) findViewById(R.id.shell_container);
-            mWindowAndroid = new WindowAndroid(this);
+            mWindowAndroid = new ActivityWindowAndroid(this);
             mWindowAndroid.restoreInstanceState(savedInstanceState);
             mShellManager.setWindow(mWindowAndroid);
 
diff --git a/gpu/command_buffer/service/context_state.cc b/gpu/command_buffer/service/context_state.cc
index 43a2d89..e8912cf 100644
--- a/gpu/command_buffer/service/context_state.cc
+++ b/gpu/command_buffer/service/context_state.cc
@@ -87,16 +87,6 @@
 }
 
 void ContextState::RestoreRenderbufferBindings() const {
-  // === Begin android workaround for b/11061206
-  GLuint context_service_id  =
-      bound_renderbuffer.get() ? bound_renderbuffer->service_id() : 0;
-  GLint current_service_id = 0;
-  glGetIntegerv(GL_RENDERBUFFER_BINDING_EXT, &current_service_id);
-
-  if (context_service_id == static_cast<GLuint>(current_service_id))
-    return;
-  // === End android workaround for b/11061206
-
   // Restore Bindings
   glBindRenderbufferEXT(
       GL_RENDERBUFFER,
diff --git a/gpu/command_buffer/service/framebuffer_manager.cc b/gpu/command_buffer/service/framebuffer_manager.cc
index b468262..c118af2 100644
--- a/gpu/command_buffer/service/framebuffer_manager.cc
+++ b/gpu/command_buffer/service/framebuffer_manager.cc
@@ -84,7 +84,7 @@
     return true;
   }
 
-  virtual void DetachFromFramebuffer() const OVERRIDE {
+  virtual void DetachFromFramebuffer(Framebuffer* framebuffer) const OVERRIDE {
     // Nothing to do for renderbuffers.
   }
 
@@ -188,8 +188,10 @@
     return texture_ref_->texture()->CanRenderTo();
   }
 
-  virtual void DetachFromFramebuffer() const OVERRIDE {
+  virtual void DetachFromFramebuffer(Framebuffer* framebuffer)
+      const OVERRIDE {
     texture_ref_->texture()->DetachFromFramebuffer();
+    framebuffer->OnTextureRefDetached(texture_ref_.get());
   }
 
   virtual bool ValidForAttachmentType(
@@ -225,6 +227,10 @@
   DISALLOW_COPY_AND_ASSIGN(TextureAttachment);
 };
 
+FramebufferManager::TextureDetachObserver::TextureDetachObserver() {}
+
+FramebufferManager::TextureDetachObserver::~TextureDetachObserver() {}
+
 FramebufferManager::FramebufferManager(
     uint32 max_draw_buffers, uint32 max_color_attachments)
     : framebuffer_state_change_count_(1),
@@ -247,7 +253,7 @@
   deleted_ = true;
   while (!attachments_.empty()) {
     Attachment* attachment = attachments_.begin()->second.get();
-    attachment->DetachFromFramebuffer();
+    attachment->DetachFromFramebuffer(this);
     attachments_.erase(attachments_.begin());
   }
 }
@@ -532,7 +538,7 @@
     GLenum attachment, Renderbuffer* renderbuffer) {
   const Attachment* a = GetAttachment(attachment);
   if (a)
-    a->DetachFromFramebuffer();
+    a->DetachFromFramebuffer(this);
   if (renderbuffer) {
     attachments_[attachment] = scoped_refptr<Attachment>(
         new RenderbufferAttachment(renderbuffer));
@@ -547,7 +553,7 @@
     GLint level, GLsizei samples) {
   const Attachment* a = GetAttachment(attachment);
   if (a)
-    a->DetachFromFramebuffer();
+    a->DetachFromFramebuffer(this);
   if (texture_ref) {
     attachments_[attachment] = scoped_refptr<Attachment>(
         new TextureAttachment(texture_ref, target, level, samples));
@@ -568,6 +574,10 @@
   return NULL;
 }
 
+void Framebuffer::OnTextureRefDetached(TextureRef* texture) {
+  manager_->OnTextureRefDetached(texture);
+}
+
 bool FramebufferManager::GetClientId(
     GLuint service_id, GLuint* client_id) const {
   // This doesn't need to be fast. It's only used during slow queries.
@@ -605,6 +615,12 @@
       framebuffer_state_change_count_;
 }
 
+void FramebufferManager::OnTextureRefDetached(TextureRef* texture) {
+  FOR_EACH_OBSERVER(TextureDetachObserver,
+                    texture_detach_observers_,
+                    OnTextureRefDetachedFromFramebuffer(texture));
+}
+
 }  // namespace gles2
 }  // namespace gpu
 
diff --git a/gpu/command_buffer/service/framebuffer_manager.h b/gpu/command_buffer/service/framebuffer_manager.h
index 176e3e2..850e702 100644
--- a/gpu/command_buffer/service/framebuffer_manager.h
+++ b/gpu/command_buffer/service/framebuffer_manager.h
@@ -9,6 +9,7 @@
 #include "base/containers/hash_tables.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
 #include "gpu/command_buffer/service/gl_utils.h"
 #include "gpu/gpu_export.h"
 
@@ -41,7 +42,7 @@
     virtual bool IsRenderbuffer(
         Renderbuffer* renderbuffer) const = 0;
     virtual bool CanRenderTo() const = 0;
-    virtual void DetachFromFramebuffer() const = 0;
+    virtual void DetachFromFramebuffer(Framebuffer* framebuffer) const = 0;
     virtual bool ValidForAttachmentType(
         GLenum attachment_type, uint32 max_color_attachments) = 0;
     virtual void AddToSignature(
@@ -130,6 +131,8 @@
     return allow_framebuffer_combo_complete_map_;
   }
 
+  void OnTextureRefDetached(TextureRef* texture);
+
  private:
   friend class FramebufferManager;
   friend class base::RefCounted<Framebuffer>;
@@ -184,6 +187,17 @@
 // so we can correctly clear them.
 class GPU_EXPORT FramebufferManager {
  public:
+  class GPU_EXPORT TextureDetachObserver {
+   public:
+    TextureDetachObserver();
+    virtual ~TextureDetachObserver();
+
+    virtual void OnTextureRefDetachedFromFramebuffer(TextureRef* texture) = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(TextureDetachObserver);
+  };
+
   FramebufferManager(uint32 max_draw_buffers, uint32 max_color_attachments);
   ~FramebufferManager();
 
@@ -217,12 +231,22 @@
         (framebuffer_state_change_count_ + 1) | 0x80000000U;
   }
 
+  void AddObserver(TextureDetachObserver* observer) {
+    texture_detach_observers_.AddObserver(observer);
+  }
+
+  void RemoveObserver(TextureDetachObserver* observer) {
+    texture_detach_observers_.RemoveObserver(observer);
+  }
+
  private:
   friend class Framebuffer;
 
   void StartTracking(Framebuffer* framebuffer);
   void StopTracking(Framebuffer* framebuffer);
 
+  void OnTextureRefDetached(TextureRef* texture);
+
   // Info for each framebuffer in the system.
   typedef base::hash_map<GLuint, scoped_refptr<Framebuffer> >
       FramebufferMap;
@@ -241,6 +265,8 @@
   uint32 max_draw_buffers_;
   uint32 max_color_attachments_;
 
+  ObserverList<TextureDetachObserver> texture_detach_observers_;
+
   DISALLOW_COPY_AND_ASSIGN(FramebufferManager);
 };
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 44f7674..85ecf2c 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -524,7 +524,8 @@
 
 // This class implements GLES2Decoder so we don't have to expose all the GLES2
 // cmd stuff to outside this class.
-class GLES2DecoderImpl : public GLES2Decoder {
+class GLES2DecoderImpl : public GLES2Decoder,
+                         public FramebufferManager::TextureDetachObserver {
  public:
   // Used by PrepForSetUniformByLocation to validate types.
   struct BaseUniformInfo {
@@ -647,6 +648,10 @@
 
   virtual error::ContextLostReason GetContextLostReason() OVERRIDE;
 
+  // Overridden from FramebufferManager::TextureDetachObserver:
+  virtual void OnTextureRefDetachedFromFramebuffer(
+      TextureRef* texture) OVERRIDE;
+
  private:
   friend class ScopedFrameBufferBinder;
   friend class ScopedGLErrorSuppressor;
@@ -1403,12 +1408,17 @@
   void RestoreStateForAttrib(GLuint attrib);
 
   // If texture is a stream texture, this will update the stream to the newest
-  // buffer.
-  void UpdateStreamTextureIfNeeded(Texture* texture);
+  // buffer and bind the texture implicitly.
+  void UpdateStreamTextureIfNeeded(Texture* texture, GLuint texture_unit_index);
 
-  // Returns false if unrenderable textures were replaced.
+  // If an image is bound to texture, this will call Will/DidUseTexImage
+  // if needed.
+  void DoWillUseTexImageIfNeeded(Texture* texture, GLenum textarget);
+  void DoDidUseTexImageIfNeeded(Texture* texture, GLenum textarget);
+
+  // Returns false if textures were replaced.
   bool PrepareTexturesForRender();
-  void RestoreStateForNonRenderableTextures();
+  void RestoreStateForTextures();
 
   // Returns true if GL_FIXED attribs were simulated.
   bool SimulateFixedAttribs(
@@ -2537,6 +2547,8 @@
       AsyncPixelTransferManager::Create(context.get()));
   async_pixel_transfer_manager_->Initialize(texture_manager());
 
+  framebuffer_manager()->AddObserver(this);
+
   return true;
 }
 
@@ -3257,6 +3269,8 @@
   // by the context group.
   async_pixel_transfer_manager_.reset();
 
+  framebuffer_manager()->RemoveObserver(this);
+
   if (group_.get()) {
     group_->Destroy(this, have_context);
     group_ = NULL;
@@ -4924,6 +4938,9 @@
     return;
   }
 
+  if (texture_ref)
+    DoWillUseTexImageIfNeeded(texture_ref->texture(), textarget);
+
   LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(name);
   if (0 == samples) {
     glFramebufferTexture2DEXT(target, attachment, textarget, service_id, level);
@@ -4944,6 +4961,10 @@
   if (framebuffer == state_.bound_draw_framebuffer.get()) {
     clear_state_dirty_ = true;
   }
+
+  if (texture_ref)
+    DoDidUseTexImageIfNeeded(texture_ref->texture(), textarget);
+
   OnFboChanged();
 }
 
@@ -5650,21 +5671,62 @@
                      std::string("PERFORMANCE WARNING: ") + msg);
 }
 
-void GLES2DecoderImpl::UpdateStreamTextureIfNeeded(Texture* texture) {
+void GLES2DecoderImpl::UpdateStreamTextureIfNeeded(Texture* texture,
+                                                   GLuint texture_unit_index) {
   if (texture && texture->IsStreamTexture()) {
     DCHECK(stream_texture_manager());
     StreamTexture* stream_tex =
         stream_texture_manager()->LookupStreamTexture(texture->service_id());
-    if (stream_tex)
+    if (stream_tex) {
+      glActiveTexture(GL_TEXTURE0 + texture_unit_index);
       stream_tex->Update();
+    }
+  }
+}
+
+void GLES2DecoderImpl::DoWillUseTexImageIfNeeded(
+    Texture* texture, GLenum textarget) {
+  // This might be supported in the future.
+  if (textarget != GL_TEXTURE_2D)
+    return;
+  // Image is already in use if texture is attached to a framebuffer.
+  if (texture && !texture->IsAttachedToFramebuffer()) {
+    gfx::GLImage* image = texture->GetLevelImage(textarget, 0);
+    if (image) {
+      ScopedGLErrorSuppressor suppressor(
+          "GLES2DecoderImpl::DoWillUseTexImageIfNeeded",
+          this);
+      glBindTexture(textarget, texture->service_id());
+      image->WillUseTexImage();
+      RestoreCurrentTexture2DBindings();
+    }
+  }
+}
+
+void GLES2DecoderImpl::DoDidUseTexImageIfNeeded(
+    Texture* texture, GLenum textarget) {
+  // This might be supported in the future.
+  if (textarget != GL_TEXTURE_2D)
+    return;
+  // Image is still in use if texture is attached to a framebuffer.
+  if (texture && !texture->IsAttachedToFramebuffer()) {
+    gfx::GLImage* image = texture->GetLevelImage(textarget, 0);
+    if (image) {
+      ScopedGLErrorSuppressor suppressor(
+          "GLES2DecoderImpl::DoDidUseTexImageIfNeeded",
+          this);
+      glBindTexture(textarget, texture->service_id());
+      image->DidUseTexImage();
+      RestoreCurrentTexture2DBindings();
+    }
   }
 }
 
 bool GLES2DecoderImpl::PrepareTexturesForRender() {
   DCHECK(state_.current_program.get());
-  bool have_unrenderable_textures =
-      texture_manager()->HaveUnrenderableTextures();
-  if (!have_unrenderable_textures && !features().oes_egl_image_external) {
+  if (!texture_manager()->HaveUnrenderableTextures() &&
+      !texture_manager()->HaveImages() &&
+      !features().oes_egl_image_external) {
     return true;
   }
 
@@ -5679,16 +5741,14 @@
       GLuint texture_unit_index = uniform_info->texture_units[jj];
       if (texture_unit_index < state_.texture_units.size()) {
         TextureUnit& texture_unit = state_.texture_units[texture_unit_index];
-        TextureRef* texture =
+        TextureRef* texture_ref =
             texture_unit.GetInfoForSamplerType(uniform_info->type).get();
-        if (texture)
-          UpdateStreamTextureIfNeeded(texture->texture());
-        if (have_unrenderable_textures &&
-            (!texture || !texture_manager()->CanRender(texture))) {
+        GLenum textarget = GetBindTargetForSamplerType(uniform_info->type);
+        if (!texture_ref || !texture_manager()->CanRender(texture_ref)) {
           textures_set = true;
           glActiveTexture(GL_TEXTURE0 + texture_unit_index);
           glBindTexture(
-              GetBindTargetForSamplerType(uniform_info->type),
+              textarget,
               texture_manager()->black_texture_id(uniform_info->type));
           LOCAL_RENDER_WARNING(
               std::string("texture bound to texture unit ") +
@@ -5696,7 +5756,23 @@
               " is not renderable. It maybe non-power-of-2 and have"
               " incompatible texture filtering or is not"
               " 'texture complete'");
+          continue;
         }
+
+        Texture* texture = texture_ref->texture();
+        if (textarget == GL_TEXTURE_2D) {
+          gfx::GLImage* image = texture->GetLevelImage(textarget, 0);
+          if (image && !texture->IsAttachedToFramebuffer()) {
+            ScopedGLErrorSuppressor suppressor(
+                "GLES2DecoderImpl::PrepareTexturesForRender", this);
+            textures_set = true;
+            glActiveTexture(GL_TEXTURE0 + texture_unit_index);
+            image->WillUseTexImage();
+            continue;
+          }
+        }
+
+        UpdateStreamTextureIfNeeded(texture, texture_unit_index);
       }
       // else: should this be an error?
     }
@@ -5704,7 +5780,7 @@
   return !textures_set;
 }
 
-void GLES2DecoderImpl::RestoreStateForNonRenderableTextures() {
+void GLES2DecoderImpl::RestoreStateForTextures() {
   DCHECK(state_.current_program.get());
   const Program::SamplerIndices& sampler_indices =
       state_.current_program->sampler_indices();
@@ -5717,9 +5793,7 @@
       if (texture_unit_index < state_.texture_units.size()) {
         TextureUnit& texture_unit = state_.texture_units[texture_unit_index];
         TextureRef* texture_ref =
-            uniform_info->type == GL_SAMPLER_2D
-                ? texture_unit.bound_texture_2d.get()
-                : texture_unit.bound_texture_cube_map.get();
+            texture_unit.GetInfoForSamplerType(uniform_info->type).get();
         if (!texture_ref || !texture_manager()->CanRender(texture_ref)) {
           glActiveTexture(GL_TEXTURE0 + texture_unit_index);
           // Get the texture_ref info that was previously bound here.
@@ -5728,6 +5802,20 @@
                             : texture_unit.bound_texture_cube_map.get();
           glBindTexture(texture_unit.bind_target,
                         texture_ref ? texture_ref->service_id() : 0);
+          continue;
+        }
+
+        Texture* texture = texture_ref->texture();
+        if (texture_unit.bind_target == GL_TEXTURE_2D) {
+          gfx::GLImage* image = texture->GetLevelImage(
+              texture_unit.bind_target, 0);
+          if (image && !texture->IsAttachedToFramebuffer()) {
+            ScopedGLErrorSuppressor suppressor(
+                "GLES2DecoderImpl::RestoreStateForTextures", this);
+            glActiveTexture(GL_TEXTURE0 + texture_unit_index);
+            image->DidUseTexImage();
+            continue;
+          }
         }
       }
     }
@@ -6070,7 +6158,7 @@
       }
       ProcessPendingQueries();
       if (textures_set) {
-        RestoreStateForNonRenderableTextures();
+        RestoreStateForTextures();
       }
       if (simulated_fixed_attribs) {
         RestoreStateForSimulatedFixedAttribs();
@@ -6204,7 +6292,7 @@
 
       ProcessPendingQueries();
       if (textures_set) {
-        RestoreStateForNonRenderableTextures();
+        RestoreStateForTextures();
       }
       if (simulated_fixed_attribs) {
         RestoreStateForSimulatedFixedAttribs();
@@ -9786,6 +9874,8 @@
         dest_texture_ref, GL_TEXTURE_2D, level, true);
   }
 
+  DoWillUseTexImageIfNeeded(source_texture, source_texture->target());
+
   // GL_TEXTURE_EXTERNAL_OES texture requires apply a transform matrix
   // before presenting.
   if (source_texture->target() == GL_TEXTURE_EXTERNAL_OES) {
@@ -9818,6 +9908,8 @@
         unpack_premultiply_alpha_,
         unpack_unpremultiply_alpha_);
   }
+
+  DoDidUseTexImageIfNeeded(source_texture, source_texture->target());
 }
 
 static GLenum ExtractTypeFromStorageFormat(GLenum internalformat) {
@@ -10481,6 +10573,12 @@
   return error::kNoError;
 }
 
+void GLES2DecoderImpl::OnTextureRefDetachedFromFramebuffer(
+    TextureRef* texture_ref) {
+  Texture* texture = texture_ref->texture();
+  DoDidUseTexImageIfNeeded(texture, texture->target());
+}
+
 // Include the auto-generated part of this file. We split this because it means
 // we can easily edit the non-auto generated parts right here in this file
 // instead of having to edit some template or the code generator.
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index 906eb88..52caaa0 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -5759,6 +5759,9 @@
   EXPECT_CALL(*stream_texture_manager(), LookupStreamTexture(kServiceTextureId))
       .WillOnce(Return(&stream_texture))
       .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
+      .Times(1)
+      .RetiresOnSaturation();
   EXPECT_CALL(stream_texture, Update())
       .Times(1)
       .RetiresOnSaturation();
@@ -8048,6 +8051,146 @@
   EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 0) == NULL);
 }
 
+class MockGLImage : public gfx::GLImage {
+ public:
+  MockGLImage() {}
+
+  // Overridden from gfx::GLImage:
+  MOCK_METHOD0(Destroy, void());
+  MOCK_METHOD0(GetSize, gfx::Size());
+  MOCK_METHOD0(BindTexImage, bool());
+  MOCK_METHOD0(ReleaseTexImage, void());
+  MOCK_METHOD0(WillUseTexImage, void());
+  MOCK_METHOD0(DidUseTexImage, void());
+
+ protected:
+  virtual ~MockGLImage() {}
+};
+
+TEST_F(GLES2DecoderWithShaderTest, UseTexImage) {
+  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
+  DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+               kSharedMemoryId, kSharedMemoryOffset);
+
+  TextureRef* texture_ref = group().texture_manager()->GetTexture(
+      client_texture_id_);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
+  EXPECT_EQ(kServiceTextureId, texture->service_id());
+
+  const int32 kImageId = 1;
+  scoped_refptr<MockGLImage> image(new MockGLImage);
+  group().image_manager()->AddImage(image.get(), kImageId);
+
+  // Bind image to texture.
+  EXPECT_CALL(*image, BindTexImage())
+      .Times(1)
+      .WillOnce(Return(true))
+      .RetiresOnSaturation();
+  EXPECT_CALL(*image, GetSize())
+      .Times(1)
+      .WillOnce(Return(gfx::Size(1, 1)))
+      .RetiresOnSaturation();
+  // ScopedGLErrorSuppressor calls GetError on its constructor and destructor.
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  BindTexImage2DCHROMIUM bind_tex_image_2d_cmd;
+  bind_tex_image_2d_cmd.Init(GL_TEXTURE_2D, kImageId);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(bind_tex_image_2d_cmd));
+
+  AddExpectationsForSimulatedAttrib0(kNumVertices, 0);
+  SetupExpectationsForApplyingDefaultDirtyState();
+
+  // ScopedGLErrorSuppressor calls GetError on its constructor and destructor.
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
+      .Times(3)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*image, WillUseTexImage())
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*image, DidUseTexImage())
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
+      .Times(1)
+      .RetiresOnSaturation();
+  DrawArrays cmd;
+  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+  DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_,
+                    kServiceFramebufferId);
+  // ScopedGLErrorSuppressor calls GetError on its constructor and destructor.
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, kServiceTextureId))
+      .Times(2)
+      .RetiresOnSaturation();
+  // Image will be 'in use' as long as bound to a framebuffer.
+  EXPECT_CALL(*image, WillUseTexImage())
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, FramebufferTexture2DEXT(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+      kServiceTextureId, 0))
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  FramebufferTexture2D fbtex_cmd;
+  fbtex_cmd.Init(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, client_texture_id_,
+      0);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(fbtex_cmd));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+  // ScopedGLErrorSuppressor calls GetError on its constructor and destructor.
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
+      kServiceRenderbufferId))
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, kServiceTextureId))
+      .Times(2)
+      .RetiresOnSaturation();
+  // Image should no longer be 'in use' after being unbound from framebuffer.
+  EXPECT_CALL(*image, DidUseTexImage())
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  FramebufferRenderbuffer fbrb_cmd;
+  fbrb_cmd.Init(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
+      client_renderbuffer_id_);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(fbrb_cmd));
+}
+
 TEST_F(GLES2DecoderManualInitTest, GpuMemoryManagerCHROMIUM) {
   InitDecoder(
       "GL_ARB_texture_rectangle",  // extensions
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 486bdac..9f4c1d9 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -79,6 +79,7 @@
   DCHECK_EQ(0, num_unrenderable_textures_);
   DCHECK_EQ(0, num_unsafe_textures_);
   DCHECK_EQ(0, num_uncleared_mips_);
+  DCHECK_EQ(0, num_images_);
 }
 
 void TextureManager::Destroy(bool have_context) {
@@ -117,6 +118,7 @@
       framebuffer_attachment_count_(0),
       stream_texture_(false),
       immutable_(false),
+      has_images_(false),
       estimated_size_(0),
       can_render_condition_(CAN_RENDER_ALWAYS) {
 }
@@ -433,6 +435,29 @@
   can_render_condition_ = can_render_condition;
 }
 
+void Texture::UpdateHasImages() {
+  if (level_infos_.empty())
+    return;
+
+  bool has_images = false;
+  for (size_t ii = 0; ii < level_infos_.size(); ++ii) {
+    for (size_t jj = 0; jj < level_infos_[ii].size(); ++jj) {
+      const Texture::LevelInfo& info = level_infos_[ii][jj];
+      if (info.image.get() != NULL) {
+        has_images = true;
+        break;
+      }
+    }
+  }
+
+  if (has_images_ == has_images)
+    return;
+  has_images_ = has_images;
+  int delta = has_images ? +1 : -1;
+  for (RefSet::iterator it = refs_.begin(); it != refs_.end(); ++it)
+    (*it)->manager()->UpdateNumImages(delta);
+}
+
 void Texture::IncAllFramebufferStateChangeCount() {
   for (RefSet::iterator it = refs_.begin(); it != refs_.end(); ++it)
     (*it)->manager()->IncFramebufferStateChangeCount();
@@ -481,6 +506,7 @@
   Update(feature_info);
   UpdateCleared();
   UpdateCanRenderCondition();
+  UpdateHasImages();
   if (IsAttachedToFramebuffer()) {
     // TODO(gman): If textures tracked which framebuffers they were attached to
     // we could just mark those framebuffers as not complete.
@@ -782,10 +808,10 @@
   DCHECK_EQ(info.level, level);
   info.image = image;
   UpdateCanRenderCondition();
+  UpdateHasImages();
 }
 
-gfx::GLImage* Texture::GetLevelImage(
-  GLint target, GLint level) const {
+gfx::GLImage* Texture::GetLevelImage(GLint target, GLint level) const {
   size_t face_index = GLTargetToFaceIndex(target);
   if (level >= 0 && face_index < level_infos_.size() &&
       static_cast<size_t>(level) < level_infos_[face_index].size()) {
@@ -846,6 +872,7 @@
       num_unrenderable_textures_(0),
       num_unsafe_textures_(0),
       num_uncleared_mips_(0),
+      num_images_(0),
       texture_count_(0),
       have_context_(true) {
   for (int ii = 0; ii < kNumDefaultTextures; ++ii) {
@@ -1131,6 +1158,8 @@
     ++num_unsafe_textures_;
   if (!texture->CanRender(feature_info_.get()))
     ++num_unrenderable_textures_;
+  if (texture->HasImages())
+    ++num_images_;
 }
 
 void TextureManager::StopTracking(TextureRef* ref) {
@@ -1145,6 +1174,10 @@
   }
 
   --texture_count_;
+  if (texture->HasImages()) {
+    DCHECK_NE(0, num_images_);
+    --num_images_;
+  }
   if (!texture->CanRender(feature_info_.get())) {
     DCHECK_NE(0, num_unrenderable_textures_);
     --num_unrenderable_textures_;
@@ -1230,10 +1263,14 @@
     ++num_unrenderable_textures_;
 }
 
+void TextureManager::UpdateNumImages(int delta) {
+  num_images_ += delta;
+  DCHECK_GE(num_images_, 0);
+}
+
 void TextureManager::IncFramebufferStateChangeCount() {
   if (framebuffer_manager_)
     framebuffer_manager_->IncFramebufferStateChangeCount();
-
 }
 
 }  // namespace gles2
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index cbc659c..dea11a6 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -112,6 +112,10 @@
   // does not exist.
   gfx::GLImage* GetLevelImage(GLint target, GLint level) const;
 
+  bool HasImages() const {
+    return has_images_;
+  }
+
   // Returns true of the given dimensions are inside the dimensions of the
   // level and if the format and type match the level.
   bool ValidForTexture(
@@ -315,6 +319,10 @@
   // texture.
   void UpdateCanRenderCondition();
 
+  // Updates the images count in all the managers referencing this
+  // texture.
+  void UpdateHasImages();
+
   // Increment the framebuffer state change count in all the managers
   // referencing this texture.
   void IncAllFramebufferStateChangeCount();
@@ -376,6 +384,9 @@
   // or dimensions of the texture object can be made.
   bool immutable_;
 
+  // Whether or not this texture has images.
+  bool has_images_;
+
   // Size in bytes this texture is assumed to take in memory.
   uint32 estimated_size_;
 
@@ -629,6 +640,10 @@
     return num_uncleared_mips_ > 0;
   }
 
+  bool HaveImages() const {
+    return num_images_ > 0;
+  }
+
   GLuint black_texture_id(GLenum target) const {
     switch (target) {
       case GL_SAMPLER_2D:
@@ -687,6 +702,7 @@
   void UpdateUnclearedMips(int delta);
   void UpdateCanRenderCondition(Texture::CanRenderCondition old_condition,
                                 Texture::CanRenderCondition new_condition);
+  void UpdateNumImages(int delta);
   void IncFramebufferStateChangeCount();
 
   MemoryTypeTracker* GetMemTracker(GLenum texture_pool);
@@ -710,6 +726,7 @@
   int num_unrenderable_textures_;
   int num_unsafe_textures_;
   int num_uncleared_mips_;
+  int num_images_;
 
   // Counts the number of Textures allocated with 'this' as its manager.
   // Allows to check no Texture will outlive this.
diff --git a/gpu/command_buffer/service/texture_manager_unittest.cc b/gpu/command_buffer/service/texture_manager_unittest.cc
index 402cc8d..ec5c919 100644
--- a/gpu/command_buffer/service/texture_manager_unittest.cc
+++ b/gpu/command_buffer/service/texture_manager_unittest.cc
@@ -2349,5 +2349,65 @@
             memory_tracker2_->GetSize(MemoryTracker::kUnmanaged));
 }
 
+TEST_F(SharedTextureTest, Images) {
+  scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10);
+  scoped_refptr<TextureRef> ref2 =
+      texture_manager2_->Consume(20, ref1->texture());
+
+  texture_manager1_->SetTarget(ref1.get(), GL_TEXTURE_2D);
+  texture_manager1_->SetLevelInfo(ref1.get(),
+                                  GL_TEXTURE_2D,
+                                  1,
+                                  GL_RGBA,
+                                  2,
+                                  2,
+                                  1,
+                                  0,
+                                  GL_RGBA,
+                                  GL_UNSIGNED_BYTE,
+                                  true);
+  EXPECT_FALSE(ref1->texture()->HasImages());
+  EXPECT_FALSE(ref2->texture()->HasImages());
+  EXPECT_FALSE(texture_manager1_->HaveImages());
+  EXPECT_FALSE(texture_manager2_->HaveImages());
+  texture_manager1_->SetLevelImage(ref1.get(),
+                                   GL_TEXTURE_2D,
+                                   1,
+                                   gfx::GLImage::CreateGLImage(0).get());
+  EXPECT_TRUE(ref1->texture()->HasImages());
+  EXPECT_TRUE(ref2->texture()->HasImages());
+  EXPECT_TRUE(texture_manager1_->HaveImages());
+  EXPECT_TRUE(texture_manager2_->HaveImages());
+  texture_manager1_->SetLevelImage(ref1.get(),
+                                   GL_TEXTURE_2D,
+                                   1,
+                                   gfx::GLImage::CreateGLImage(0).get());
+  EXPECT_TRUE(ref1->texture()->HasImages());
+  EXPECT_TRUE(ref2->texture()->HasImages());
+  EXPECT_TRUE(texture_manager1_->HaveImages());
+  EXPECT_TRUE(texture_manager2_->HaveImages());
+  texture_manager1_->SetLevelInfo(ref1.get(),
+                                  GL_TEXTURE_2D,
+                                  1,
+                                  GL_RGBA,
+                                  2,
+                                  2,
+                                  1,
+                                  0,
+                                  GL_RGBA,
+                                  GL_UNSIGNED_BYTE,
+                                  true);
+  EXPECT_FALSE(ref1->texture()->HasImages());
+  EXPECT_FALSE(ref2->texture()->HasImages());
+  EXPECT_FALSE(texture_manager1_->HaveImages());
+  EXPECT_FALSE(texture_manager1_->HaveImages());
+
+  EXPECT_CALL(*gl_, DeleteTextures(1, _))
+      .Times(1)
+      .RetiresOnSaturation();
+  texture_manager1_->RemoveTexture(10);
+  texture_manager2_->RemoveTexture(20);
+}
+
 }  // namespace gles2
 }  // namespace gpu
diff --git a/media/base/android/media_player_android.h b/media/base/android/media_player_android.h
index f1c9c37..ae2eed2 100644
--- a/media/base/android/media_player_android.h
+++ b/media/base/android/media_player_android.h
@@ -47,12 +47,15 @@
   // before decoding the media stream. This allows |manager_| to track
   // unused resources and free them when needed. On the other hand, it needs
   // to call ReleaseMediaResources() when it is done with decoding.
+  // |load_media_resource| indcates whether the media resource should be
+  // initialized after the player created.
   static MediaPlayerAndroid* Create(int player_id,
                                     const GURL& url,
                                     SourceType source_type,
                                     const GURL& first_party_for_cookies,
                                     bool hide_url_log,
-                                    MediaPlayerManager* manager);
+                                    MediaPlayerManager* manager,
+                                    bool load_media_resource);
 
   // Passing an external java surface object to the player.
   virtual void SetVideoSurface(gfx::ScopedJavaSurface surface) = 0;
diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc
index 342ceaa..63d120f 100644
--- a/media/base/android/media_player_bridge.cc
+++ b/media/base/android/media_player_bridge.cc
@@ -37,7 +37,8 @@
     SourceType source_type,
     const GURL& first_party_for_cookies,
     bool hide_url_log,
-    MediaPlayerManager* manager) {
+    MediaPlayerManager* manager,
+    bool load_media_resource) {
   if (source_type == SOURCE_TYPE_URL) {
     MediaPlayerBridge* media_player_bridge = new MediaPlayerBridge(
         player_id,
@@ -45,7 +46,8 @@
         first_party_for_cookies,
         hide_url_log,
         manager);
-    media_player_bridge->Initialize();
+    if (load_media_resource)
+      media_player_bridge->Initialize();
     return media_player_bridge;
   } else {
     return new MediaSourcePlayer(
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index 2274f25..a8db569 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -520,6 +520,37 @@
   EXPECT_FALSE(handle_.socket());
 }
 
+TEST_P(HttpProxyClientSocketPoolTest, Tunnel1xxResponse) {
+  // Tests that 1xx responses are rejected for a CONNECT request.
+  if (GetParam().proxy_type == SPDY) {
+    // SPDY doesn't have 1xx responses.
+    return;
+  }
+
+  MockWrite writes[] = {
+    MockWrite(ASYNC, 0,
+              "CONNECT www.google.com:443 HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Proxy-Connection: keep-alive\r\n\r\n"),
+  };
+  MockRead reads[] = {
+    MockRead(ASYNC, 1, "HTTP/1.1 100 Continue\r\n\r\n"),
+    MockRead(ASYNC, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
+  };
+
+  Initialize(reads, arraysize(reads), writes, arraysize(writes),
+             NULL, 0, NULL, 0);
+
+  int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
+                        &pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle_.is_initialized());
+  EXPECT_FALSE(handle_.socket());
+
+  data_->RunFor(2);
+  EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback_.WaitForResult());
+}
+
 TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupError) {
   MockWrite writes[] = {
     MockWrite(ASYNC, 0,
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index e53aafd..853414a 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -79,7 +79,7 @@
 // Example:
 //
 // scoped_refptr<SeekableIOBuffer> buf = new SeekableIOBuffer(1024);
-// // capacity() == 1024. size() == BytesRemaining == BytesConsumed() == 0.
+// // capacity() == 1024. size() == BytesRemaining() == BytesConsumed() == 0.
 // // data() points to the beginning of the buffer.
 //
 // // Read() takes an IOBuffer.
@@ -94,7 +94,7 @@
 //   buf->DidConsume(bytes_written);
 // }
 // // BytesRemaining() == 0. BytesConsumed() == size().
-// // data() points to the end of the comsumed bytes (exclusive).
+// // data() points to the end of the consumed bytes (exclusive).
 //
 // // If you want to reuse the buffer, be sure to clear the buffer.
 // buf->Clear();
@@ -161,7 +161,7 @@
   }
 
   char* real_data_;
-  int capacity_;
+  const int capacity_;
   int size_;
   int used_;
 };
@@ -293,6 +293,7 @@
   DCHECK(io_state_ == STATE_REQUEST_SENT || io_state_ == STATE_DONE);
   DCHECK(callback_.is_null());
   DCHECK(!callback.is_null());
+  DCHECK_EQ(0, read_buf_unused_offset_);
 
   // This function can be called with io_state_ == STATE_DONE if the
   // connection is closed after seeing just a 1xx response code.
@@ -304,8 +305,8 @@
 
   if (read_buf_->offset() > 0) {
     // Simulate the state where the data was just read from the socket.
-    result = read_buf_->offset() - read_buf_unused_offset_;
-    read_buf_->set_offset(read_buf_unused_offset_);
+    result = read_buf_->offset();
+    read_buf_->set_offset(0);
   }
   if (result > 0)
     io_state_ = STATE_READ_HEADERS_COMPLETE;
@@ -517,6 +518,8 @@
 }
 
 int HttpStreamParser::DoReadHeadersComplete(int result) {
+  DCHECK_EQ(0, read_buf_unused_offset_);
+
   if (result == 0)
     result = ERR_CONNECTION_CLOSED;
 
@@ -580,41 +583,43 @@
   if (end_of_header_offset == -1) {
     io_state_ = STATE_READ_HEADERS;
     // Prevent growing the headers buffer indefinitely.
-    if (read_buf_->offset() - read_buf_unused_offset_ >= kMaxHeaderBufSize) {
+    if (read_buf_->offset() >= kMaxHeaderBufSize) {
       io_state_ = STATE_DONE;
       return ERR_RESPONSE_HEADERS_TOO_BIG;
     }
   } else {
+    CalculateResponseBodySize();
+    // If the body is zero length, the caller may not call ReadResponseBody,
+    // which is where any extra data is copied to read_buf_, so we move the
+    // data here.
+    if (response_body_length_ == 0) {
+      int extra_bytes = read_buf_->offset() - end_of_header_offset;
+      if (extra_bytes) {
+        CHECK_GT(extra_bytes, 0);
+        memmove(read_buf_->StartOfBuffer(),
+                read_buf_->StartOfBuffer() + end_of_header_offset,
+                extra_bytes);
+      }
+      read_buf_->SetCapacity(extra_bytes);
+      if (response_->headers->response_code() / 100 == 1) {
+        // After processing a 1xx response, the caller will ask for the next
+        // header, so reset state to support that. We don't completely ignore a
+        // 1xx response because it cannot be returned in reply to a CONNECT
+        // request so we return OK here, which lets the caller inspect the
+        // response and reject it in the event that we're setting up a CONNECT
+        // tunnel.
+        response_header_start_offset_ = -1;
+        response_body_length_ = -1;
+        io_state_ = STATE_REQUEST_SENT;
+      } else {
+        io_state_ = STATE_DONE;
+      }
+      return OK;
+    }
+
     // Note where the headers stop.
     read_buf_unused_offset_ = end_of_header_offset;
-
-    if (response_->headers->response_code() / 100 == 1) {
-      // After processing a 1xx response, the caller will ask for the next
-      // header, so reset state to support that.  We don't just skip these
-      // completely because 1xx codes aren't acceptable when establishing a
-      // tunnel.
-      io_state_ = STATE_REQUEST_SENT;
-      response_header_start_offset_ = -1;
-    } else {
-      io_state_ = STATE_BODY_PENDING;
-      CalculateResponseBodySize();
-      // If the body is 0, the caller may not call ReadResponseBody, which
-      // is where any extra data is copied to read_buf_, so we move the
-      // data here and transition to DONE.
-      if (response_body_length_ == 0) {
-        io_state_ = STATE_DONE;
-        int extra_bytes = read_buf_->offset() - read_buf_unused_offset_;
-        if (extra_bytes) {
-          CHECK_GT(extra_bytes, 0);
-          memmove(read_buf_->StartOfBuffer(),
-                  read_buf_->StartOfBuffer() + read_buf_unused_offset_,
-                  extra_bytes);
-        }
-        read_buf_->SetCapacity(extra_bytes);
-        read_buf_unused_offset_ = 0;
-        return OK;
-      }
-    }
+    io_state_ = STATE_BODY_PENDING;
   }
   return result;
 }
@@ -751,20 +756,19 @@
 
 int HttpStreamParser::ParseResponseHeaders() {
   int end_offset = -1;
+  DCHECK_EQ(0, read_buf_unused_offset_);
 
   // Look for the start of the status line, if it hasn't been found yet.
   if (response_header_start_offset_ < 0) {
     response_header_start_offset_ = HttpUtil::LocateStartOfStatusLine(
-        read_buf_->StartOfBuffer() + read_buf_unused_offset_,
-        read_buf_->offset() - read_buf_unused_offset_);
+        read_buf_->StartOfBuffer(), read_buf_->offset());
   }
 
   if (response_header_start_offset_ >= 0) {
-    end_offset = HttpUtil::LocateEndOfHeaders(
-        read_buf_->StartOfBuffer() + read_buf_unused_offset_,
-        read_buf_->offset() - read_buf_unused_offset_,
-        response_header_start_offset_);
-  } else if (read_buf_->offset() - read_buf_unused_offset_ >= 8) {
+    end_offset = HttpUtil::LocateEndOfHeaders(read_buf_->StartOfBuffer(),
+                                              read_buf_->offset(),
+                                              response_header_start_offset_);
+  } else if (read_buf_->offset() >= 8) {
     // Enough data to decide that this is an HTTP/0.9 response.
     // 8 bytes = (4 bytes of junk) + "http".length()
     end_offset = 0;
@@ -776,14 +780,16 @@
   int rv = DoParseResponseHeaders(end_offset);
   if (rv < 0)
     return rv;
-  return end_offset + read_buf_unused_offset_;
+  return end_offset;
 }
 
 int HttpStreamParser::DoParseResponseHeaders(int end_offset) {
   scoped_refptr<HttpResponseHeaders> headers;
+  DCHECK_EQ(0, read_buf_unused_offset_);
+
   if (response_header_start_offset_ >= 0) {
     headers = new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(
-        read_buf_->StartOfBuffer() + read_buf_unused_offset_, end_offset));
+        read_buf_->StartOfBuffer(), end_offset));
   } else {
     // Enough data was read -- there is no status line.
     headers = new HttpResponseHeaders(std::string("HTTP/0.9 200 OK"));
@@ -830,13 +836,16 @@
   // (informational), 204 (no content), and 304 (not modified) responses
   // MUST NOT include a message-body. All other responses do include a
   // message-body, although it MAY be of zero length.
-  switch (response_->headers->response_code()) {
-    // Note that 1xx was already handled earlier.
-    case 204:  // No Content
-    case 205:  // Reset Content
-    case 304:  // Not Modified
-      response_body_length_ = 0;
-      break;
+  if (response_->headers->response_code() / 100 == 1) {
+    response_body_length_ = 0;
+  } else {
+    switch (response_->headers->response_code()) {
+      case 204:  // No Content
+      case 205:  // Reset Content
+      case 304:  // Not Modified
+        response_body_length_ = 0;
+        break;
+    }
   }
   if (request_->method == "HEAD")
     response_body_length_ = 0;
diff --git a/net/http/http_stream_parser.h b/net/http/http_stream_parser.h
index a41e393..43e1514 100644
--- a/net/http/http_stream_parser.h
+++ b/net/http/http_stream_parser.h
@@ -178,7 +178,8 @@
   scoped_refptr<GrowableIOBuffer> read_buf_;
 
   // Offset of the first unused byte in |read_buf_|.  May be nonzero due to
-  // a 1xx header, or body data in the same packet as header data.
+  // body data in the same packet as header data but is zero when reading
+  // headers.
   int read_buf_unused_offset_;
 
   // The amount beyond |read_buf_unused_offset_| where the status line starts;
diff --git a/skia/ext/analysis_canvas.cc b/skia/ext/analysis_canvas.cc
index a2d397c..5476bda 100644
--- a/skia/ext/analysis_canvas.cc
+++ b/skia/ext/analysis_canvas.cc
@@ -182,6 +182,16 @@
   is_transparent_ = false;
 }
 
+void AnalysisDevice::drawRRect(const SkDraw& draw,
+                               const SkRRect& rr,
+                               const SkPaint& paint) {
+  // This should add the SkRRect to an SkPath, and call
+  // drawPath, but since drawPath ignores the SkPath, just
+  // do the same work here.
+  is_solid_color_ = false;
+  is_transparent_ = false;
+}
+
 void AnalysisDevice::drawPath(const SkDraw& draw,
                               const SkPath& path,
                               const SkPaint& paint,
diff --git a/skia/ext/analysis_canvas.h b/skia/ext/analysis_canvas.h
index 5db1540..1709251 100644
--- a/skia/ext/analysis_canvas.h
+++ b/skia/ext/analysis_canvas.h
@@ -81,6 +81,9 @@
   virtual void drawRect(const SkDraw& draw,
                         const SkRect& rect,
                         const SkPaint& paint) OVERRIDE;
+  virtual void drawRRect(const SkDraw& draw,
+                         const SkRRect& rr,
+                         const SkPaint& paint) OVERRIDE;
   virtual void drawOval(const SkDraw& draw,
                         const SkRect& oval,
                         const SkPaint& paint) OVERRIDE;
diff --git a/skia/ext/vector_platform_device_emf_win.cc b/skia/ext/vector_platform_device_emf_win.cc
index 958ff8f..97334d2 100644
--- a/skia/ext/vector_platform_device_emf_win.cc
+++ b/skia/ext/vector_platform_device_emf_win.cc
@@ -211,6 +211,13 @@
   Cleanup();
 }
 
+void VectorPlatformDeviceEmf::drawRRect(const SkDraw& draw, const SkRRect& rr,
+                                        const SkPaint& paint) {
+  SkPath path;
+  path.addRRect(rr);
+  this->drawPath(draw, path, paint, NULL, true);
+}
+
 void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw,
                                        const SkPath& path,
                                        const SkPaint& paint,
diff --git a/skia/ext/vector_platform_device_emf_win.h b/skia/ext/vector_platform_device_emf_win.h
index c0deeec..de09be5 100644
--- a/skia/ext/vector_platform_device_emf_win.h
+++ b/skia/ext/vector_platform_device_emf_win.h
@@ -40,6 +40,8 @@
                           const SkPaint& paint) OVERRIDE;
   virtual void drawRect(const SkDraw& draw, const SkRect& r,
                         const SkPaint& paint) OVERRIDE;
+  virtual void drawRRect(const SkDraw&, const SkRRect& rr,
+                         const SkPaint& paint) OVERRIDE;
   virtual void drawPath(const SkDraw& draw, const SkPath& path,
                         const SkPaint& paint,
                         const SkMatrix* prePathMatrix = NULL,
diff --git a/ui/android/java/src/org/chromium/ui/ActivityWindowAndroid.java b/ui/android/java/src/org/chromium/ui/ActivityWindowAndroid.java
new file mode 100644
index 0000000..8c228fe
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/ActivityWindowAndroid.java
@@ -0,0 +1,72 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.ui;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * The class provides the WindowAndroid's implementation which requires
+ * Activity Instance.
+ * Only instantiate this class when you need the implemented features.
+ */
+public class ActivityWindowAndroid extends WindowAndroid {
+    // Constants used for intent request code bounding.
+    private static final int REQUEST_CODE_PREFIX = 1000;
+    private static final int REQUEST_CODE_RANGE_SIZE = 100;
+    private static final String TAG = "ActivityWindowAndroid";
+
+    private Activity mActivity;
+    private int mNextRequestCode = 0;
+
+    public ActivityWindowAndroid(Activity activity) {
+        super(activity.getApplicationContext());
+        mActivity = activity;
+    }
+
+    @Override
+    public boolean showIntent(Intent intent, IntentCallback callback, int errorId) {
+        int requestCode = REQUEST_CODE_PREFIX + mNextRequestCode;
+        mNextRequestCode = (mNextRequestCode + 1) % REQUEST_CODE_RANGE_SIZE;
+
+        try {
+            mActivity.startActivityForResult(intent, requestCode);
+        } catch (ActivityNotFoundException e) {
+            return false;
+        }
+
+        mOutstandingIntents.put(requestCode, callback);
+        mIntentErrors.put(requestCode, mApplicationContext.getString(errorId));
+
+        return true;
+    }
+
+    @Override
+    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+        IntentCallback callback = mOutstandingIntents.get(requestCode);
+        mOutstandingIntents.delete(requestCode);
+        String errorMessage = mIntentErrors.remove(requestCode);
+
+        if (callback != null) {
+            callback.onIntentCompleted(this, resultCode,
+                    mApplicationContext.getContentResolver(), data);
+            return true;
+        } else {
+            if (errorMessage != null) {
+                showCallbackNonExistentError(errorMessage);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    @Deprecated
+    public Context getContext() {
+        return mActivity;
+    }
+}
diff --git a/ui/android/java/src/org/chromium/ui/Clipboard.java b/ui/android/java/src/org/chromium/ui/Clipboard.java
index a66fa3d..a7a3381 100644
--- a/ui/android/java/src/org/chromium/ui/Clipboard.java
+++ b/ui/android/java/src/org/chromium/ui/Clipboard.java
@@ -10,6 +10,7 @@
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.Context;
+import android.os.Build;
 import android.text.TextUtils;
 
 /**
@@ -76,6 +77,23 @@
     }
 
     /**
+     * Gets the HTML text of top item on the primary clip on the Android clipboard.
+     *
+     * @return a Java string with the html text if any, or null if there is no html
+     *         text or no entries on the primary clip.
+     */
+    @CalledByNative
+    private String getHtmlText() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            final ClipData clip = mClipboardManager.getPrimaryClip();
+            if (clip != null && clip.getItemCount() > 0) {
+                return clip.getItemAt(0).getHtmlText();
+            }
+        }
+        return null;
+    }
+
+    /**
      * Emulates the behavior of the now-deprecated
      * {@link android.text.ClipboardManager#setText(CharSequence)}, setting the
      * clipboard's current primary clip to a plain-text clip that consists of
@@ -90,22 +108,18 @@
     }
 
     /**
-     * Approximates the behavior of the now-deprecated
-     * {@link android.text.ClipboardManager#hasText()}, returning true if and
-     * only if the clipboard has a primary clip and that clip contains a plain
-     * non-empty text entry (without attempting coercion - URLs and intents
-     * will cause this method to return false).
+     * Writes HTML to the clipboard, together with a plain-text representation
+     * of that very data. This API is only available in Android JellyBean+ and
+     * will be a no-operation in older versions.
      *
-     * @return as described above
+     * @param html The HTML content to be pasted to the clipboard.
+     * @param text Plain-text representation of the HTML content.
      */
-    @SuppressWarnings("javadoc")
     @CalledByNative
-    private boolean hasPlainText() {
-        final ClipData clip = mClipboardManager.getPrimaryClip();
-        if (clip != null && clip.getItemCount() > 0) {
-            final CharSequence text = clip.getItemAt(0).getText();
-            return !TextUtils.isEmpty(text);
+    private void setHTMLText(final String html, final String text) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            mClipboardManager.setPrimaryClip(
+                    ClipData.newHtmlText(null, text, html));
         }
-        return false;
     }
 }
diff --git a/ui/android/java/src/org/chromium/ui/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/WindowAndroid.java
index 3fec600..3466a88 100644
--- a/ui/android/java/src/org/chromium/ui/WindowAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/WindowAndroid.java
@@ -4,12 +4,11 @@
 
 package org.chromium.ui;
 
-import android.app.Activity;
-import android.content.ActivityNotFoundException;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.util.Log;
 import android.util.SparseArray;
 import android.widget.Toast;
 
@@ -22,29 +21,26 @@
  */
 @JNINamespace("ui")
 public class WindowAndroid {
+    private static final String TAG = "WindowAndroid";
 
     // Native pointer to the c++ WindowAndroid object.
     private int mNativeWindowAndroid = 0;
 
-    // Constants used for intent request code bounding.
-    private static final int REQUEST_CODE_PREFIX = 1000;
-    private static final int REQUEST_CODE_RANGE_SIZE = 100;
     // A string used as a key to store intent errors in a bundle
     static final String WINDOW_CALLBACK_ERRORS = "window_callback_errors";
 
-    private int mNextRequestCode = 0;
-    protected Activity mActivity;
+    protected Context mApplicationContext;
     protected SparseArray<IntentCallback> mOutstandingIntents;
     protected HashMap<Integer, String> mIntentErrors;
 
     /**
-     * @param activity
+     * @param context, the application context..
      */
-    public WindowAndroid(Activity activity) {
-        mActivity = activity;
+    public WindowAndroid(Context context) {
+        assert context == context.getApplicationContext();
+        mApplicationContext = context;
         mOutstandingIntents = new SparseArray<IntentCallback>();
         mIntentErrors = new HashMap<Integer, String>();
-
     }
 
     /**
@@ -56,19 +52,8 @@
      * @return Whether the intent was shown.
      */
     public boolean showIntent(Intent intent, IntentCallback callback, int errorId) {
-        int requestCode = REQUEST_CODE_PREFIX + mNextRequestCode;
-        mNextRequestCode = (mNextRequestCode + 1) % REQUEST_CODE_RANGE_SIZE;
-
-        try {
-            mActivity.startActivityForResult(intent, requestCode);
-        } catch (ActivityNotFoundException e) {
-            return false;
-        }
-
-        mOutstandingIntents.put(requestCode, callback);
-        mIntentErrors.put(requestCode, mActivity.getString(errorId));
-
-        return true;
+        Log.d(TAG, "Can't show intent as context is not an Activity: " + intent);
+        return false;
     }
 
     /**
@@ -77,7 +62,7 @@
      */
     public void showError(String error) {
         if (error != null) {
-            Toast.makeText(mActivity, error, Toast.LENGTH_SHORT).show();
+            Toast.makeText(mApplicationContext, error, Toast.LENGTH_SHORT).show();
         }
     }
 
@@ -86,7 +71,7 @@
      * @param resId The error message string's resource id.
      */
     public void showError(int resId) {
-        showError(mActivity.getString(resId));
+        showError(mApplicationContext.getString(resId));
     }
 
     /**
@@ -101,16 +86,25 @@
      * Broadcasts the given intent to all interested BroadcastReceivers.
      */
     public void sendBroadcast(Intent intent) {
-        mActivity.sendBroadcast(intent);
+        mApplicationContext.sendBroadcast(intent);
     }
 
     /**
      * TODO(nileshagrawal): Stop returning Activity Context crbug.com/233440.
-     * @return Activity context.
+     * @return Activity context, it could be null. Note, in most cases, you probably
+     * just need Application Context returned by getApplicationContext().
+     * @see #getApplicationContext()
      */
     @Deprecated
     public Context getContext() {
-        return mActivity;
+        return null;
+    }
+
+    /**
+     * @return The application context for this activity.
+     */
+    public Context getApplicationContext() {
+        return mApplicationContext;
     }
 
     /**
@@ -146,20 +140,6 @@
      * @return Boolean value of whether the intent was started by the native window.
      */
     public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
-        IntentCallback callback = mOutstandingIntents.get(requestCode);
-        mOutstandingIntents.delete(requestCode);
-        String errorMessage = mIntentErrors.remove(requestCode);
-
-        if (callback != null) {
-            callback.onIntentCompleted(this, resultCode,
-                    mActivity.getContentResolver(), data);
-            return true;
-        } else {
-            if (errorMessage != null) {
-                showCallbackNonExistentError(errorMessage);
-                return true;
-            }
-        }
         return false;
     }
 
diff --git a/ui/base/clipboard/clipboard_android.cc b/ui/base/clipboard/clipboard_android.cc
index 4eb052a..ae3766e 100644
--- a/ui/base/clipboard/clipboard_android.cc
+++ b/ui/base/clipboard/clipboard_android.cc
@@ -97,10 +97,26 @@
 
   map_[format] = data;
   if (format == kPlainTextFormat) {
-    ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(
-        env, data.c_str());
-    DCHECK(str.obj() && !ClearException(env));
+    ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, data);
+    DCHECK(str.obj());
+
     Java_Clipboard_setText(env, clipboard_manager_.obj(), str.obj());
+  } else if (format == kHTMLFormat) {
+    // Android's API for storing HTML content on the clipboard requires a plain-
+    // text representation to be available as well. ScopedClipboardWriter has a
+    // stable order for setting clipboard data, ensuring that plain-text data
+    // is available first. Do not write to the clipboard when only HTML data is
+    // available, because otherwise others apps may not be able to paste it.
+    if (!ContainsKey(map_, kPlainTextFormat))
+      return;
+
+    ScopedJavaLocalRef<jstring> html = ConvertUTF8ToJavaString(env, data);
+    ScopedJavaLocalRef<jstring> text = ConvertUTF8ToJavaString(
+        env, map_[kPlainTextFormat].c_str());
+
+    DCHECK(html.obj() && text.obj());
+    Java_Clipboard_setHTMLText(
+        env, clipboard_manager_.obj(), html.obj(), text.obj());
   }
 }
 
@@ -113,39 +129,37 @@
 
 // If the internal map contains a plain-text entry and it does not match that
 // in the Android clipboard, clear the map and insert the Android text into it.
+// If there is an HTML entry in the Android clipboard it gets inserted in the
+// map.
 void ClipboardMap::SyncWithAndroidClipboard() {
   lock_.AssertAcquired();
   JNIEnv* env = AttachCurrentThread();
 
+  // Update the plain text clipboard entry
   std::map<std::string, std::string>::const_iterator it =
     map_.find(kPlainTextFormat);
-
-  if (!Java_Clipboard_hasPlainText(env, clipboard_manager_.obj())) {
-    if (it != map_.end())
-      // We have plain text on this side, but Android doesn't. Nuke ours.
-      map_.clear();
-    return;
-  }
-
-  ScopedJavaLocalRef<jstring> java_string =
+  ScopedJavaLocalRef<jstring> java_string_text =
       Java_Clipboard_getCoercedText(env, clipboard_manager_.obj());
-
-  if (!java_string.obj()) {
-    // Tolerate a null value from the Java side, even though that should not
-    // happen since hasPlainText has already returned true.
-    // Should only happen if someone is using the clipboard on multiple
-    // threads and clears it out after hasPlainText but before we get here...
-    if (it != map_.end())
+  if (java_string_text.obj()) {
+    std::string android_string = ConvertJavaStringToUTF8(java_string_text);
+    if (it == map_.end() || it->second != android_string) {
+      // There is a different string in the Android clipboard than we have.
+      // Clear the map on our side.
+      map_.clear();
+      map_[kPlainTextFormat] = android_string;
+    }
+  } else {
+    if (it != map_.end()) {
       // We have plain text on this side, but Android doesn't. Nuke ours.
       map_.clear();
-    return;
+    }
   }
 
-  // If Android text differs from ours (or we have none), then copy Android's.
-  std::string android_string = ConvertJavaStringToUTF8(java_string);
-  if (it == map_.end() || it->second != android_string) {
-    map_.clear();
-    map_[kPlainTextFormat] = android_string;
+  // Update the html clipboard entry
+  ScopedJavaLocalRef<jstring> java_string_html =
+      Java_Clipboard_getHtmlText(env, clipboard_manager_.obj());
+  if (java_string_html.obj()) {
+    map_[kHTMLFormat] = ConvertJavaStringToUTF8(java_string_html);
   }
 }
 
diff --git a/ui/gl/gl_image.cc b/ui/gl/gl_image.cc
index aaefb94..ff7eb56 100644
--- a/ui/gl/gl_image.cc
+++ b/ui/gl/gl_image.cc
@@ -19,6 +19,14 @@
   NOTIMPLEMENTED();
 }
 
+void GLImage::WillUseTexImage() {
+  NOTIMPLEMENTED();
+}
+
+void GLImage::DidUseTexImage() {
+  NOTIMPLEMENTED();
+}
+
 GLImage::~GLImage() {}
 
 }  // namespace gfx
diff --git a/ui/gl/gl_image.h b/ui/gl/gl_image.h
index f1d0537..36ba904 100644
--- a/ui/gl/gl_image.h
+++ b/ui/gl/gl_image.h
@@ -33,6 +33,12 @@
   // Release image from texture currently bound to GL_TEXTURE_2D target.
   virtual void ReleaseTexImage();
 
+  // Called before the texture is used for drawing.
+  virtual void WillUseTexImage();
+
+  // Called after the texture has been used for drawing.
+  virtual void DidUseTexImage();
+
   // Create a GL image for a window.
   static scoped_refptr<GLImage> CreateGLImage(gfx::PluginWindowHandle window);
 
diff --git a/ui/gl/gl_image_egl.cc b/ui/gl/gl_image_egl.cc
index 2571519..cb8a44c 100644
--- a/ui/gl/gl_image_egl.cc
+++ b/ui/gl/gl_image_egl.cc
@@ -7,11 +7,34 @@
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_surface_egl.h"
 
+// === START ANDROID WORKAROUND b/11392857
+#include <sys/system_properties.h>
+
+namespace {
+bool RequiresGrallocUnbind() {
+  static const char* kPropertyName = "ro.webview.gralloc_unbind";
+  char prop_value[PROP_VALUE_MAX];
+  int prop_value_length = __system_property_get(kPropertyName, prop_value);
+  if (prop_value_length == 1) {
+    if (strcmp(prop_value, "1") == 0) {
+      return true;
+    } else if (strcmp(prop_value, "0") == 0) {
+      return false;
+    }
+  }
+  LOG_IF(WARNING, prop_value_length > 0)
+      << "Unrecognized value for " << kPropertyName << ": " << prop_value;
+  return strcmp((char*)glGetString(GL_VENDOR), "NVIDIA Corporation") == 0;
+}
+}
+// === END   ANDROID WORKAROUND b/11392857
+
 namespace gfx {
 
 GLImageEGL::GLImageEGL(gfx::Size size)
     : egl_image_(EGL_NO_IMAGE_KHR),
-      size_(size) {
+      size_(size),
+      in_use_(false) {
 }
 
 GLImageEGL::~GLImageEGL() {
@@ -40,25 +63,6 @@
   return true;
 }
 
-bool GLImageEGL::BindTexImage() {
-  if (egl_image_ == EGL_NO_IMAGE_KHR) {
-    LOG(ERROR) << "NULL EGLImage in BindTexImage";
-    return false;
-  }
-
-  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
-
-  if (glGetError() != GL_NO_ERROR) {
-    return false;
-  }
-
-  return true;
-}
-
-gfx::Size GLImageEGL::GetSize() {
-  return size_;
-}
-
 void GLImageEGL::Destroy() {
   if (egl_image_ == EGL_NO_IMAGE_KHR)
     return;
@@ -74,12 +78,44 @@
   egl_image_ = EGL_NO_IMAGE_KHR;
 }
 
+gfx::Size GLImageEGL::GetSize() {
+  return size_;
+}
+
+bool GLImageEGL::BindTexImage() {
+  if (egl_image_ == EGL_NO_IMAGE_KHR) {
+    LOG(ERROR) << "NULL EGLImage in BindTexImage";
+    return false;
+  }
+
+  // Defer ImageTargetTexture2D if not currently in use.
+  if (!in_use_)
+    return true;
+
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
+  DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+  return true;
+}
+
 void GLImageEGL::ReleaseTexImage() {
-  // === START ANDROID WORKAROUND http://b/10205015 and http://b/10892941
-  static bool is_qcom = strcmp((char*)glGetString(GL_VENDOR), "Qualcomm") == 0;
-  static bool is_arm = strcmp((char*)glGetString(GL_VENDOR), "ARM") == 0;
-  if (is_qcom || is_arm) return;
-  // === END   ANDROID WORKAROUND http://b/10205015 and http://b/10892941
+  // Nothing to do here as image is released after each use.
+}
+
+void GLImageEGL::WillUseTexImage() {
+  DCHECK(egl_image_);
+  DCHECK(!in_use_);
+  in_use_ = true;
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
+  DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+}
+
+void GLImageEGL::DidUseTexImage() {
+  DCHECK(in_use_);
+  in_use_ = false;
+  // === START ANDROID WORKAROUND b/11392857
+  static bool requires_gralloc_unbind = RequiresGrallocUnbind();
+  if (!requires_gralloc_unbind) return;
+  // === END   ANDROID WORKAROUND b/11392857
   char zero[4] = { 0, };
   glTexImage2D(GL_TEXTURE_2D,
                0,
diff --git a/ui/gl/gl_image_egl.h b/ui/gl/gl_image_egl.h
index 1c66bb9..2c64f4d 100644
--- a/ui/gl/gl_image_egl.h
+++ b/ui/gl/gl_image_egl.h
@@ -21,6 +21,8 @@
   virtual gfx::Size GetSize() OVERRIDE;
   virtual bool BindTexImage() OVERRIDE;
   virtual void ReleaseTexImage() OVERRIDE;
+  virtual void WillUseTexImage() OVERRIDE;
+  virtual void DidUseTexImage() OVERRIDE;
 
  protected:
   virtual ~GLImageEGL();
@@ -28,6 +30,7 @@
  private:
   EGLImageKHR egl_image_;
   gfx::Size size_;
+  bool in_use_;
 
   DISALLOW_COPY_AND_ASSIGN(GLImageEGL);
 };
diff --git a/ui/gl/gl_image_glx.cc b/ui/gl/gl_image_glx.cc
index 85254f3..739b4d4 100644
--- a/ui/gl/gl_image_glx.cc
+++ b/ui/gl/gl_image_glx.cc
@@ -52,6 +52,10 @@
     glx_pixmap_(0) {
 }
 
+GLImageGLX::~GLImageGLX() {
+  Destroy();
+}
+
 bool GLImageGLX::Initialize() {
   if (!GLSurfaceGLX::IsTextureFromPixmapSupported()) {
     LOG(ERROR) << "GLX_EXT_texture_from_pixmap not supported.";
@@ -170,8 +174,10 @@
   glXReleaseTexImageEXT(display_, glx_pixmap_, GLX_FRONT_LEFT_EXT);
 }
 
-GLImageGLX::~GLImageGLX() {
-  Destroy();
+void GLImageGLX::WillUseTexImage() {
+}
+
+void GLImageGLX::DidUseTexImage() {
 }
 
 }  // namespace gfx
diff --git a/ui/gl/gl_image_glx.h b/ui/gl/gl_image_glx.h
index a3cec2d..8632934 100644
--- a/ui/gl/gl_image_glx.h
+++ b/ui/gl/gl_image_glx.h
@@ -23,6 +23,8 @@
   virtual gfx::Size GetSize() OVERRIDE;
   virtual bool BindTexImage() OVERRIDE;
   virtual void ReleaseTexImage() OVERRIDE;
+  virtual void WillUseTexImage() OVERRIDE;
+  virtual void DidUseTexImage() OVERRIDE;
 
  protected:
   virtual ~GLImageGLX();
diff --git a/ui/gl/gl_image_shm.cc b/ui/gl/gl_image_shm.cc
index 14ed836..ccee11b 100644
--- a/ui/gl/gl_image_shm.cc
+++ b/ui/gl/gl_image_shm.cc
@@ -36,6 +36,13 @@
   return true;
 }
 
+void GLImageShm::Destroy() {
+}
+
+gfx::Size GLImageShm::GetSize() {
+  return size_;
+}
+
 bool GLImageShm::BindTexImage() {
   TRACE_EVENT0("gpu", "GLImageShm::BindTexImage");
   DCHECK(shared_memory_);
@@ -63,14 +70,13 @@
   return true;
 }
 
-gfx::Size GLImageShm::GetSize() {
-  return size_;
-}
-
-void GLImageShm::Destroy() {
-}
-
 void GLImageShm::ReleaseTexImage() {
 }
 
+void GLImageShm::WillUseTexImage() {
+}
+
+void GLImageShm::DidUseTexImage() {
+}
+
 }  // namespace gfx
diff --git a/ui/gl/gl_image_shm.h b/ui/gl/gl_image_shm.h
index 70233f9..c94ca85 100644
--- a/ui/gl/gl_image_shm.h
+++ b/ui/gl/gl_image_shm.h
@@ -21,6 +21,8 @@
   virtual gfx::Size GetSize() OVERRIDE;
   virtual bool BindTexImage() OVERRIDE;
   virtual void ReleaseTexImage() OVERRIDE;
+  virtual void WillUseTexImage() OVERRIDE;
+  virtual void DidUseTexImage() OVERRIDE;
 
  protected:
   virtual ~GLImageShm();
diff --git a/ui/gl/gl_image_stub.cc b/ui/gl/gl_image_stub.cc
index 86efe6e..a1a8a85 100644
--- a/ui/gl/gl_image_stub.cc
+++ b/ui/gl/gl_image_stub.cc
@@ -6,6 +6,13 @@
 
 namespace gfx {
 
+GLImageStub::GLImageStub() {
+}
+
+GLImageStub::~GLImageStub() {
+  Destroy();
+}
+
 void GLImageStub::Destroy() {
 }
 
@@ -20,6 +27,10 @@
 void GLImageStub::ReleaseTexImage() {
 }
 
-GLImageStub::~GLImageStub() {}
+void GLImageStub::WillUseTexImage() {
+}
+
+void GLImageStub::DidUseTexImage() {
+}
 
 }  // namespace gfx
diff --git a/ui/gl/gl_image_stub.h b/ui/gl/gl_image_stub.h
index 8b47358..ec232fa 100644
--- a/ui/gl/gl_image_stub.h
+++ b/ui/gl/gl_image_stub.h
@@ -12,11 +12,15 @@
 // A GLImage that does nothing for unit tests.
 class GL_EXPORT GLImageStub : public GLImage {
  public:
+  GLImageStub();
+
   // Implement GLImage.
   virtual void Destroy() OVERRIDE;
   virtual gfx::Size GetSize() OVERRIDE;
   virtual bool BindTexImage() OVERRIDE;
   virtual void ReleaseTexImage() OVERRIDE;
+  virtual void WillUseTexImage() OVERRIDE;
+  virtual void DidUseTexImage() OVERRIDE;
 
  protected:
   virtual ~GLImageStub();