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, ¤t_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();